@moretea-labs/forge 1.6.0 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -0
- package/CHANGELOG.md +39 -0
- package/CLAUDE.md +4 -0
- package/README.md +2 -2
- package/README.zh-CN.md +1 -1
- package/SKILL.md +2 -2
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/agents/openai.yaml +1 -1
- package/assets/hooks/hook-input.sh +11 -8
- package/assets/hooks/lib/session-state.sh +10 -7
- package/assets/hooks/lib/workflow-state.sh +13 -177
- package/assets/hooks/post-tool-observer.sh +8 -2
- package/assets/hooks/prompt-guard.sh +5 -81
- package/assets/hooks/session-start-context.sh +56 -1
- package/assets/partials/05-workflow.partial.md +1 -1
- package/assets/partials-agents/02-operating-mode.partial.md +2 -0
- package/assets/partials-agents/03-orchestration.partial.md +1 -1
- package/assets/plugin-registry.v1.json +3 -3
- package/assets/reference-configs/document-generation.md +1 -1
- package/assets/reference-configs/harness-overview.md +1 -1
- package/assets/reference-configs/sprint-contracts.md +3 -3
- package/assets/skill-version.json +2 -2
- package/assets/templates/contract.template.md +2 -7
- package/assets/templates/helpers/capture-plan.sh +2 -2
- package/assets/templates/helpers/check-architecture-sync.sh +17 -0
- package/assets/templates/helpers/check-task-workflow.sh +4 -4
- package/assets/templates/helpers/contract-worktree.sh +1 -21
- package/assets/templates/helpers/ensure-task-workflow.sh +11 -36
- package/assets/templates/helpers/inspect-project-state.ts +8 -6
- package/assets/templates/helpers/migrate-project-template.sh +22 -45
- package/assets/templates/helpers/migrate-workflow-docs.ts +9 -7
- package/assets/templates/helpers/new-plan.sh +1 -1
- package/assets/templates/helpers/plan-to-todo.sh +12 -37
- package/assets/templates/helpers/ship-worktrees.sh +2 -29
- package/assets/templates/helpers/verify-contract.sh +1 -111
- package/assets/templates/helpers/verify-sprint.sh +3 -180
- package/assets/templates/helpers/workflow-contract.ts +5 -1
- package/assets/templates/plan.template.md +1 -1
- package/assets/templates/review.template.md +9 -34
- package/assets/workflow-contract.v1.json +10 -19
- package/docs/README.md +10 -10
- package/docs/architecture/CURRENT.md +178 -0
- package/docs/architecture/EVOLUTION.md +42 -0
- package/docs/architecture/current/README.md +4 -156
- package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
- package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
- package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
- package/docs/architecture/history/README.md +7 -0
- package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
- package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
- package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
- package/docs/architecture/history.md +3 -34
- package/docs/architecture/index.md +15 -98
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
- package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
- package/docs/architecture/versions/1.6.md +38 -0
- package/docs/architecture/versions/1.7.md +41 -0
- package/docs/forge-chatgpt-browser-engine.md +2 -2
- package/docs/forge-chatgpt-mcp-setup.md +4 -28
- package/docs/forge-chatgpt-supervised-automation.md +3 -3
- package/docs/forge-github-issue-launcher.md +1 -1
- package/docs/forge-plugin-management.md +1 -1
- package/docs/forge-runtime-self-healing-loop.md +1 -1
- package/docs/operations/controller-browser-plugin.md +26 -6
- package/docs/operations/controller-performance-and-502.md +143 -0
- package/docs/operations/controller-reliability-runbook.md +1 -1
- package/docs/operations/features.md +2 -2
- package/docs/operations/github-repository.md +1 -1
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/mcp-tool-exposure.md +5 -3
- package/docs/wiki/Architecture.md +6 -6
- package/docs/wiki/Implementation.md +2 -2
- package/docs/wiki/Integrations.md +2 -2
- package/docs/wiki/Work-Lifecycle.md +1 -1
- package/package.json +33 -11
- package/scripts/activate-source-baseline.ts +17 -13
- package/scripts/architecture-event.ts +2 -820
- package/scripts/architecture-queue.sh +2 -645
- package/scripts/archive-architecture-request.sh +2 -242
- package/scripts/archive-workflow.sh +1 -291
- package/scripts/benchmark-background-check-overlap.ts +228 -0
- package/scripts/benchmark-mcp-host.sh +117 -0
- package/scripts/benchmark-mcp-transport.ts +170 -0
- package/scripts/benchmark-quality-harness.ts +146 -0
- package/scripts/benchmark-route-session-concurrency.ts +207 -6
- package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
- package/scripts/benchmark-typescript-navigation.ts +186 -0
- package/scripts/benchmark-work-efficiency.ts +334 -0
- package/scripts/capability-config.ts +2 -392
- package/scripts/capability-resolver.ts +2 -503
- package/scripts/capture-plan.sh +1 -332
- package/scripts/check-agent-tooling.sh +2 -1558
- package/scripts/check-architecture-sync.sh +2 -371
- package/scripts/check-brain-manifest.sh +2 -253
- package/scripts/check-context-files.sh +2 -47
- package/scripts/check-deploy-sql-order.sh +2 -115
- package/scripts/check-mcp-compatibility.ts +14 -0
- package/scripts/check-runtime-architecture.mjs +349 -79
- package/scripts/check-skill-version.ts +3 -191
- package/scripts/check-source-duplication.mjs +61 -0
- package/scripts/check-stable-baseline.ts +92 -0
- package/scripts/check-task-sync.sh +2 -63
- package/scripts/check-task-workflow.sh +2 -1000
- package/scripts/codex-handoff-resume.sh +2 -267
- package/scripts/context-contract-sync.sh +2 -514
- package/scripts/contract-run.ts +2 -463
- package/scripts/contract-worktree.sh +1 -722
- package/scripts/ensure-task-workflow.sh +1 -1225
- package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
- package/scripts/inspect-project-state.ts +4 -279
- package/scripts/install-standalone-recovery.ts +24 -0
- package/scripts/lib/project-init-lib.sh +22 -46
- package/scripts/lib/run-forge-helper.sh +19 -0
- package/scripts/lib/run-forge-helper.ts +26 -0
- package/scripts/migrate-project-template.sh +1 -1189
- package/scripts/migrate-workflow-docs.ts +4 -412
- package/scripts/new-plan.sh +1 -180
- package/scripts/new-spec.sh +1 -47
- package/scripts/new-sprint.sh +1 -46
- package/scripts/plan-to-todo.sh +1 -796
- package/scripts/prepare-codex-handoff.sh +2 -201
- package/scripts/prepare-handoff.sh +1 -78
- package/scripts/refresh-current-status.sh +2 -467
- package/scripts/run-governed-gate.ts +5 -1
- package/scripts/select-agent-context-blocks.sh +2 -64
- package/scripts/ship-worktrees.sh +1 -558
- package/scripts/smoke-ios-physical-device.ts +4 -2
- package/scripts/smoke-runtime-recovery.ts +1 -35
- package/scripts/smoke-schedule-engine.ts +1 -20
- package/scripts/sprint-backlog.sh +1 -834
- package/scripts/stage-runtime-release.ts +2 -1
- package/scripts/summarize-failures.sh +2 -123
- package/scripts/switch-plan.sh +1 -150
- package/scripts/sync-brain-docs.sh +2 -379
- package/scripts/sync-generated-authority.mjs +175 -0
- package/scripts/verify-contract.sh +2 -703
- package/scripts/verify-forge-runtime.sh +0 -1
- package/scripts/verify-sprint.sh +1 -677
- package/scripts/workflow-contract.ts +4 -205
- package/scripts/workstream-sync.sh +2 -261
- package/src/cli/agent-jobs/executor-health.ts +1 -1
- package/src/cli/commands/init-hook.ts +3 -3
- package/src/cli/commands/mcp.ts +9 -5
- package/src/cli/commands/plugin.ts +12 -3
- package/src/cli/commands/recovery.ts +25 -3
- package/src/cli/commands/runtime.ts +55 -3
- package/src/cli/commands/security.ts +12 -1
- package/src/cli/commands/setup-profile.ts +31 -21
- package/src/cli/controller/check-runner.ts +113 -5
- package/src/cli/controller/composite-result.ts +1 -1
- package/src/cli/controller/context/focus.ts +51 -0
- package/src/cli/controller/context/instruction-context.ts +114 -0
- package/src/cli/controller/context/known-paths.ts +76 -0
- package/src/cli/controller/context/query-planning.ts +110 -0
- package/src/cli/controller/context/rule-resolution.ts +532 -0
- package/src/cli/controller/context/source-materializer.ts +278 -0
- package/src/cli/controller/context/structural.ts +16 -0
- package/src/cli/controller/context/types.ts +286 -0
- package/src/cli/controller/context-pack.ts +828 -487
- package/src/cli/controller/launch-agents.ts +77 -2
- package/src/cli/controller/operational-plan.ts +2 -2
- package/src/cli/controller/work-mode.ts +6 -4
- package/src/cli/editing/edit-session.ts +60 -7
- package/src/cli/github/contracts.ts +16 -0
- package/src/cli/github/github.ts +135 -16
- package/src/cli/github/session.ts +2 -1
- package/src/cli/installer/managed-entries.ts +15 -1
- package/src/cli/local-bridge/console-automations.ts +54 -78
- package/src/cli/local-bridge/console-view-models.ts +1 -35
- package/src/cli/local-bridge/facade-api.ts +37 -264
- package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
- package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +23 -13
- package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
- package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +6 -4
- package/src/cli/local-bridge/frontend/src/lib/api.ts +1 -6
- package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
- package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +2 -2
- package/src/cli/local-bridge/frontend/src/styles.css +1 -1
- package/src/cli/local-bridge/frontend/src/types.ts +5 -8
- package/src/cli/local-bridge/job-store.ts +1 -490
- package/src/cli/local-bridge/server.ts +2 -515
- package/src/cli/local-bridge/ui-dist/app.css +1 -1
- package/src/cli/local-bridge/ui-dist/app.js +3703 -3757
- package/src/cli/mcp/auth.ts +5 -5
- package/src/cli/mcp/instructions.ts +3 -2
- package/src/cli/mcp/legacy-tool-service.ts +10 -222
- package/src/cli/mcp/multi-repository.ts +16 -5
- package/src/cli/mcp/oauth.ts +175 -26
- package/src/cli/mcp/post-finalize-work-attribution.ts +150 -0
- package/src/cli/mcp/repository-tools.ts +282 -146
- package/src/cli/mcp/server.ts +586 -116
- package/src/cli/mcp/tool-capability-cache.ts +1 -1
- package/src/cli/mcp/tool-contract.ts +14 -0
- package/src/cli/mcp/toolset-selection.ts +1 -1
- package/src/cli/mcp/toolset.ts +44 -7
- package/src/cli/mcp/transports/http.ts +124 -14
- package/src/cli/mcp/transports/session-registry.ts +43 -5
- package/src/cli/mcp/types.ts +3 -3
- package/src/cli/repositories/command-classifier.ts +103 -21
- package/src/cli/repositories/command-executor.ts +78 -526
- package/src/cli/repositories/command-process.ts +108 -0
- package/src/cli/repositories/command-scope.ts +99 -41
- package/src/cli/repositories/controller-home.ts +7 -8
- package/src/cli/repositories/git-command-executor.ts +2 -0
- package/src/cli/repositories/locks.ts +53 -16
- package/src/cli/repositories/repository-snapshot.ts +124 -0
- package/src/cli/repositories/selected-path-actions.ts +17 -9
- package/src/cli/repositories/structured-git.ts +118 -6
- package/src/cli/repository/inspector.ts +563 -65
- package/src/cli/repository/session-cache.ts +58 -17
- package/src/cli/v81-entry.ts +1 -4
- package/src/effects/fs-transaction.ts +85 -17
- package/src/runtime/context/codegraph-read-provider.ts +233 -2
- package/src/runtime/context/codegraph-sidecar.cjs +74 -40
- package/src/runtime/context/generic-lsp-provider.ts +355 -0
- package/src/runtime/context/lsp-client.ts +283 -0
- package/src/runtime/context/semantic-navigation-contract.ts +61 -0
- package/src/runtime/context/semantic-navigation.ts +223 -0
- package/src/runtime/context/swift-navigation.ts +565 -0
- package/src/runtime/context/typescript-navigation.ts +226 -0
- package/src/runtime/control-plane/dispatch-priority.ts +28 -0
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +48 -16
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +106 -0
- package/src/runtime/control-plane/execution/retained-work-resume.ts +203 -0
- package/src/runtime/control-plane/execution/session-store.ts +18 -0
- package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
- package/src/runtime/control-plane/execution/work-completion-authority.ts +58 -0
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +200 -0
- package/src/runtime/control-plane/execution/work-execution-support.ts +386 -0
- package/src/runtime/control-plane/execution/work-finalization-service.ts +1913 -0
- package/src/runtime/control-plane/execution/work-handle-authority.ts +96 -0
- package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
- package/src/runtime/control-plane/execution/work-operation-service.ts +468 -0
- package/src/runtime/control-plane/execution/work-path-scope.ts +37 -0
- package/src/runtime/control-plane/execution/work-preparation-service.ts +479 -0
- package/src/runtime/control-plane/execution/work-process-evidence.ts +129 -0
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
- package/src/runtime/control-plane/execution/work-task-receipt.ts +22 -18
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +156 -11
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +22 -14
- package/src/runtime/control-plane/execution/work-verification-context.ts +56 -0
- package/src/runtime/control-plane/execution/work-verification-service.ts +388 -0
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +90 -9
- package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
- package/src/runtime/control-plane/facade/controller-round-relay.ts +1118 -0
- package/src/runtime/control-plane/facade/controller-session-store.ts +302 -12
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +17 -1
- package/src/runtime/control-plane/facade/goal-workloop.ts +928 -190
- package/src/runtime/control-plane/facade/index.ts +3 -0
- package/src/runtime/control-plane/facade/plan-contract-store.ts +275 -42
- package/src/runtime/control-plane/facade/repository-work-admission.ts +147 -0
- package/src/runtime/control-plane/facade/requirement-authority.ts +83 -0
- package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
- package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
- package/src/runtime/control-plane/facade/types.ts +90 -10
- package/src/runtime/control-plane/facade/work-contract-store.ts +157 -261
- package/src/runtime/control-plane/facade/work-state-machine.ts +250 -0
- package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
- package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
- package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +152 -0
- package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +278 -543
- package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
- package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
- package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
- package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
- package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
- package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +63 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +365 -117
- package/src/runtime/control-plane/launcher/launch-reservation-store.ts +89 -0
- package/src/runtime/control-plane/launcher/thin-launcher.ts +244 -11
- package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
- package/src/runtime/control-plane/persistence/requirement-store.ts +0 -12
- package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
- package/src/runtime/control-plane/release-retention.ts +62 -2
- package/src/runtime/control-plane/repo-actor/registry.ts +4 -2
- package/src/runtime/control-plane/routing/route-policy.ts +24 -21
- package/src/runtime/control-plane/routing/workspace-admission.ts +70 -0
- package/src/runtime/control-plane/runtime-status-client.ts +0 -2
- package/src/runtime/diagnostics/mcp-timing.ts +13 -0
- package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
- package/src/runtime/diagnostics/performance.ts +24 -1
- package/src/runtime/evidence/event-ledger.ts +1 -1
- package/src/runtime/evidence/process-check-execution.ts +18 -0
- package/src/runtime/execution/jobs/child-reference.ts +6 -2
- package/src/runtime/execution/jobs/store.ts +58 -4
- package/src/runtime/execution/jobs/types.ts +1 -1
- package/src/runtime/execution/managed-workspace.ts +203 -1
- package/src/runtime/execution/process-runtime/check-facade.ts +51 -4
- package/src/runtime/execution/process-runtime/check-result.ts +88 -0
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +7 -1
- package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +367 -40
- package/src/runtime/execution/process-runtime/gc.ts +38 -6
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +1201 -0
- package/src/runtime/execution/process-runtime/persisted-check.ts +51 -5
- package/src/runtime/execution/process-runtime/resource-claims.ts +10 -6
- package/src/runtime/execution/process-runtime/runtime.ts +53 -3
- package/src/runtime/execution/process-runtime/store.ts +1 -0
- package/src/runtime/execution/process-runtime/types.ts +4 -12
- package/src/runtime/execution/thin-harness/async-process.ts +12 -2
- package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
- package/src/runtime/execution/work-activity.ts +21 -0
- package/src/runtime/execution/workers/executor.ts +18 -61
- package/src/runtime/gateway/mcp/execution-tools.ts +35 -1852
- package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +61 -0
- package/src/runtime/gateway/mcp/process-tools.ts +83 -37
- package/src/runtime/gateway/mcp/router.ts +186 -84
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +565 -0
- package/src/runtime/gateway/mcp/runtime-tools.ts +2378 -1947
- package/src/runtime/plugins/app-store-connect-adapter.ts +216 -47
- package/src/runtime/plugins/browser-adapter.ts +2662 -829
- package/src/runtime/plugins/browser-automation-service.ts +10 -0
- package/src/runtime/plugins/browser-handoff-host.ts +2 -25
- package/src/runtime/plugins/browser-handoff.ts +110 -5
- package/src/runtime/plugins/browser-macos-bridge.ts +701 -63
- package/src/runtime/plugins/browser-manifest-surface.ts +603 -0
- package/src/runtime/plugins/browser-native-session-service.ts +222 -0
- package/src/runtime/plugins/browser-node-bridge.ts +6 -34
- package/src/runtime/plugins/browser-provider-registry.ts +109 -0
- package/src/runtime/plugins/browser-runtime-contract.ts +154 -0
- package/src/runtime/plugins/browser-runtime.ts +193 -0
- package/src/runtime/plugins/browser-session-authority.ts +332 -0
- package/src/runtime/plugins/browser-session-store.ts +121 -0
- package/src/runtime/plugins/browser-session-types.ts +98 -0
- package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +10 -4
- package/src/runtime/plugins/external-adapter.ts +358 -3
- package/src/runtime/plugins/external-unix-socket.ts +7 -1
- package/src/runtime/plugins/first-party-registry.ts +2 -0
- package/src/runtime/plugins/github-adapter.ts +125 -0
- package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
- package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
- package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
- package/src/runtime/plugins/ios-adapter.ts +106 -16
- package/src/runtime/plugins/ios-agent-device.ts +234 -64
- package/src/runtime/plugins/ios-physical-device.ts +1143 -392
- package/src/runtime/plugins/lightweight-action.ts +153 -0
- package/src/runtime/plugins/local-system-adapter.ts +123 -6
- package/src/runtime/plugins/macos-capability-broker.ts +47 -9
- package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
- package/src/runtime/plugins/plugin-management-adapter.ts +49 -8
- package/src/runtime/plugins/resend-adapter.ts +1 -1
- package/src/runtime/plugins/store.ts +330 -33
- package/src/runtime/plugins/types.ts +38 -3
- package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
- package/src/runtime/projections/git-status-sampler.ts +16 -16
- package/src/runtime/projections/materialized-view.ts +28 -10
- package/src/runtime/recovery/maintenance-executor.ts +426 -25
- package/src/runtime/root/mcp-transport.ts +180 -10
- package/src/runtime/root/package-connector-service.ts +230 -10
- package/src/runtime/root/package-runtime-release.ts +143 -17
- package/src/runtime/root/package-runtime-service.ts +461 -22
- package/src/runtime/root/release-manifest.ts +144 -18
- package/src/runtime/root/release-materialize.ts +125 -32
- package/src/runtime/root/release-store.ts +48 -11
- package/src/runtime/root/runtime.ts +23 -5
- package/src/runtime/root/service.ts +100 -29
- package/src/runtime/root/types.ts +12 -0
- package/src/runtime/safe-tooling/ios-development.ts +321 -63
- package/src/runtime/shared/trusted-executable.ts +62 -0
- package/src/runtime/shared/trusted-node-executable.ts +48 -0
- package/src/runtime/standalone-recovery/core.ts +1079 -240
- package/src/runtime/standalone-recovery/entry.ts +100 -24
- package/src/runtime/standalone-recovery/installer.ts +20 -4
- package/src/runtime/standalone-recovery/service-labels.ts +2 -0
- package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
- package/src/runtime/workflow/schedules/browser-probe.ts +81 -3
- package/src/runtime/workflow/schedules/engine.ts +364 -202
- package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
- package/src/runtime/workflow/schedules/settlement.ts +64 -17
- package/src/runtime/workflow/schedules/store.ts +209 -19
- package/src/runtime/workflow/schedules/types.ts +2 -4
- package/src/runtime/workflow/schedules/work-continuation.ts +26 -13
- package/src/testing/test-governance.ts +8 -40
- package/assets/reference-configs/heartbeat-triage.md +0 -57
- package/assets/templates/helpers/harness-trace-grade.sh +0 -150
- package/assets/templates/helpers/heartbeat-triage.sh +0 -361
- package/assets/templates/helpers/maintenance-triage.sh +0 -167
- package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
- package/docs/architecture/current/architecture-invariants.md +0 -331
- package/docs/architecture/current/automation-and-schedule-engine.md +0 -459
- package/docs/architecture/current/codegraph-context-provider.md +0 -238
- package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
- package/docs/architecture/current/controller-repository-decoupling.md +0 -148
- package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -373
- package/docs/architecture/current/entity-model.md +0 -612
- package/docs/architecture/current/external-plugin-governance.md +0 -396
- package/docs/architecture/current/failure-recovery.md +0 -447
- package/docs/architecture/current/governance.md +0 -213
- package/docs/architecture/current/human-interaction-plane.md +0 -87
- package/docs/architecture/current/implementation-status.md +0 -128
- package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
- package/docs/architecture/current/legacy-governance.md +0 -58
- package/docs/architecture/current/local-workspace-target-grants.md +0 -70
- package/docs/architecture/current/migration-roadmap.md +0 -105
- package/docs/architecture/current/multi-repository-execution.md +0 -371
- package/docs/architecture/current/operations-runbook.md +0 -45
- package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
- package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
- package/docs/architecture/current/repository-access-modes.md +0 -102
- package/docs/architecture/current/route-policy.md +0 -15
- package/docs/architecture/current/route-session-concurrency.md +0 -85
- package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
- package/docs/architecture/current/runtime-directory-map.md +0 -49
- package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
- package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
- package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
- package/docs/architecture/current/system-overview.md +0 -169
- package/docs/architecture/current/target-requirements-traceability.md +0 -202
- package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
- package/docs/architecture/current/thin-harness-v1.md +0 -321
- package/docs/architecture/current/verification-and-release-gates.md +0 -391
- package/docs/forge-autonomous-goal-loop.md +0 -9
- package/docs/forge-provider-configuration.md +0 -7
- package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -93
- package/docs/operations/gmail-assistant-routines.md +0 -186
- package/docs/operations/personal-assistant-operator-guide.md +0 -61
- package/docs/operations/provider-configuration.md +0 -31
- package/scripts/harness-trace-grade.sh +0 -150
- package/scripts/heartbeat-triage.sh +0 -361
- package/scripts/maintenance-triage.sh +0 -167
- package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
- package/src/cli/repositories/goal-registry.ts +0 -239
- package/src/runtime/assistant/action-proposals.ts +0 -279
- package/src/runtime/assistant/intent.ts +0 -395
- package/src/runtime/assistant/model-provider.ts +0 -460
- package/src/runtime/assistant/openapi.ts +0 -223
- package/src/runtime/assistant/policy.ts +0 -89
- package/src/runtime/assistant/readiness.ts +0 -258
- package/src/runtime/assistant/routine-runtime.ts +0 -477
- package/src/runtime/assistant/schedule-binding.ts +0 -171
- package/src/runtime/assistant/standing-grants.ts +0 -375
- package/src/runtime/assistant/store.ts +0 -243
- package/src/runtime/assistant/types.ts +0 -135
- package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
- package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
- package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
- package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
- package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
- package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
- package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
- package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
- package/src/runtime/control-plane/goal-loop/index.ts +0 -13
- package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
- package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
- package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
- package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
- package/src/runtime/control-plane/goal-loop/types.ts +0 -378
- package/src/runtime/control-plane/startup-recovery.ts +0 -198
- package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
- package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
- package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
- package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
- package/src/runtime/workflow/findings/store.ts +0 -204
- package/src/runtime/workflow/findings/types.ts +0 -58
- package/src/runtime/workflow/portfolio/engine.ts +0 -20
- package/src/runtime/workflow/portfolio/store.ts +0 -185
- package/src/runtime/workflow/portfolio/types.ts +0 -29
|
@@ -1,14 +1,16 @@
|
|
|
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 { getControllerSession } from '../control-plane/facade/controller-session-store';
|
|
10
|
+
import { getControllerSession, withControllerSessionTerminalizationFence } from '../control-plane/facade/controller-session-store';
|
|
10
11
|
import { listPlanContracts } from '../control-plane/facade/plan-contract-store';
|
|
11
12
|
import { readRequirement } from '../control-plane/persistence/requirement-store';
|
|
13
|
+
import { listControlPlaneRecords, type ControlPlaneRecord } from '../control-plane/persistence/sqlite-store';
|
|
12
14
|
import { isTerminalWorkContractStatus, type WorkContract } from '../control-plane/facade/types';
|
|
13
15
|
import { listSchedules } from '../workflow/schedules/store';
|
|
14
16
|
import {
|
|
@@ -40,11 +42,14 @@ export type RuntimeMaintenanceCandidateKind =
|
|
|
40
42
|
| 'runtime_storage_warning'
|
|
41
43
|
| 'stale_runtime_temp_entry'
|
|
42
44
|
| 'stale_work_contract'
|
|
43
|
-
| 'stale_edit_session'
|
|
45
|
+
| 'stale_edit_session'
|
|
46
|
+
| 'retained_migrated_work'
|
|
47
|
+
| 'unowned_managed_worktree';
|
|
44
48
|
|
|
45
49
|
export interface RuntimeMaintenanceRepository {
|
|
46
50
|
repoId: string;
|
|
47
51
|
canonicalRoot: string;
|
|
52
|
+
defaultBranch?: string;
|
|
48
53
|
/** Test-only or embedded-runtime override. Production defaults to system temp roots. */
|
|
49
54
|
runtimeTempRoots?: string[];
|
|
50
55
|
}
|
|
@@ -83,6 +88,11 @@ export interface RuntimeMaintenanceCandidate {
|
|
|
83
88
|
deadlineAt?: string;
|
|
84
89
|
suggestedAction: RuntimeMaintenanceActionId;
|
|
85
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' | 'semantic_completion_required';
|
|
95
|
+
sourceState?: 'no_managed_source' | 'clean_integrated' | 'dirty_worktree' | 'unintegrated_commits' | 'source_state_unknown';
|
|
86
96
|
}
|
|
87
97
|
|
|
88
98
|
export interface RuntimeMaintenanceSummary {
|
|
@@ -97,6 +107,8 @@ export interface RuntimeMaintenanceSummary {
|
|
|
97
107
|
staleRuntimeTempEntries: number;
|
|
98
108
|
staleWorkContracts: number;
|
|
99
109
|
staleEditSessions: number;
|
|
110
|
+
retainedMigratedWork: number;
|
|
111
|
+
unownedManagedWorktrees: number;
|
|
100
112
|
}
|
|
101
113
|
|
|
102
114
|
export interface RuntimeMaintenanceStatus {
|
|
@@ -227,6 +239,8 @@ function summarize(candidates: RuntimeMaintenanceCandidate[]): RuntimeMaintenanc
|
|
|
227
239
|
staleRuntimeTempEntries: candidates.filter((candidate) => candidate.kind === 'stale_runtime_temp_entry').length,
|
|
228
240
|
staleWorkContracts: candidates.filter((candidate) => candidate.kind === 'stale_work_contract').length,
|
|
229
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,
|
|
230
244
|
};
|
|
231
245
|
}
|
|
232
246
|
|
|
@@ -393,6 +407,292 @@ function scanRuntimeTempCandidates(
|
|
|
393
407
|
}));
|
|
394
408
|
}
|
|
395
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 staleWorkSemanticTerminalizationReady(contract: WorkContract): boolean {
|
|
588
|
+
if (contract.phase !== 'cleanup') return false;
|
|
589
|
+
return (['implementation', 'verification', 'delivery'] as const).every((phase) =>
|
|
590
|
+
['satisfied', 'skipped'].includes(contract.phaseEvidence[phase].state));
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function staleWorkCandidateReason(source: StaleWorkSourceInspection, semanticReady: boolean): string {
|
|
594
|
+
if (!source.safeToCancel) {
|
|
595
|
+
return `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.`;
|
|
596
|
+
}
|
|
597
|
+
if (!semanticReady) {
|
|
598
|
+
return `Protected stale Work: repository source has no unique live changes, but the Work semantic lifecycle has not reached cleanup with implementation, verification, and delivery explicitly satisfied or skipped. ${source.detail} Maintenance may clean source debt, but it cannot infer that the objective or acceptance criteria are complete.`;
|
|
599
|
+
}
|
|
600
|
+
return `Cleanup-ready WorkContract exceeded the explicit maintenance age threshold and has no active Plan, Requirement, Schedule, or Controller authority. ${source.detail} Explicit full maintenance may finish cleanup while retaining durable evidence.`;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function scanRetainedWorktreeCandidates(
|
|
604
|
+
repository: RuntimeMaintenanceRepository,
|
|
605
|
+
controllerHome: string,
|
|
606
|
+
maxCandidates: number,
|
|
607
|
+
): RuntimeMaintenanceCandidate[] {
|
|
608
|
+
const worktreePaths = gitWorktreePaths(repository.canonicalRoot);
|
|
609
|
+
if (worktreePaths.length === 0) return [];
|
|
610
|
+
const normalizedInventory = new Set(worktreePaths.map((path) => resolve(path)));
|
|
611
|
+
const currentContracts = readWorkContractStore({ controllerHome, repoId: repository.repoId }).contracts;
|
|
612
|
+
const currentOwnedPaths = new Set(
|
|
613
|
+
currentContracts
|
|
614
|
+
.map((contract) => contract.worktreeRef?.trim())
|
|
615
|
+
.filter((path): path is string => Boolean(path))
|
|
616
|
+
.map((path) => resolve(path)),
|
|
617
|
+
);
|
|
618
|
+
const candidates: RuntimeMaintenanceCandidate[] = [];
|
|
619
|
+
const retained = listRetainedMigratedWork(controllerHome, repository.repoId);
|
|
620
|
+
const retainedByPath = new Map(retained.map((entry) => [resolve(entry.work.worktreeRef!), entry]));
|
|
621
|
+
|
|
622
|
+
for (const path of worktreePaths) {
|
|
623
|
+
const normalized = resolve(path);
|
|
624
|
+
if (normalized === resolve(repository.canonicalRoot) || currentOwnedPaths.has(normalized)) continue;
|
|
625
|
+
const migrated = retainedByPath.get(normalized);
|
|
626
|
+
if (migrated) {
|
|
627
|
+
const completed = migrated.work.status === 'completed';
|
|
628
|
+
candidates.push({
|
|
629
|
+
kind: 'retained_migrated_work',
|
|
630
|
+
id: migrated.work.workId,
|
|
631
|
+
path: normalized,
|
|
632
|
+
status: migrated.work.status,
|
|
633
|
+
safe: false,
|
|
634
|
+
reason: completed
|
|
635
|
+
? '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.'
|
|
636
|
+
: '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.',
|
|
637
|
+
suggestedAction: 'full_maintenance_pass',
|
|
638
|
+
ownershipStatus: 'explicit',
|
|
639
|
+
sourceControllerHome: migrated.sourceHome,
|
|
640
|
+
objective: migrated.work.objective,
|
|
641
|
+
continuationPrompt: migrated.work.continuationPrompt,
|
|
642
|
+
disposition: completed ? 'completion_reconciliation' : 'resume_or_supersede_review',
|
|
643
|
+
});
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
if (!looksLikeForgeManagedWorktree(normalized)) continue;
|
|
647
|
+
candidates.push({
|
|
648
|
+
kind: 'unowned_managed_worktree',
|
|
649
|
+
id: `managed-worktree:${basename(normalized)}`,
|
|
650
|
+
path: normalized,
|
|
651
|
+
safe: false,
|
|
652
|
+
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.',
|
|
653
|
+
suggestedAction: 'full_maintenance_pass',
|
|
654
|
+
ownershipStatus: 'unknown',
|
|
655
|
+
disposition: 'ownership_reconciliation',
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// A retained migrated Work whose referenced path still exists but is no longer
|
|
660
|
+
// in Git inventory remains preservation debt rather than disappearing silently.
|
|
661
|
+
for (const migrated of retained) {
|
|
662
|
+
const normalized = resolve(migrated.work.worktreeRef!);
|
|
663
|
+
if (normalizedInventory.has(normalized) || currentOwnedPaths.has(normalized)) continue;
|
|
664
|
+
// Legacy WorkContract records sometimes stored the repository checkout/root
|
|
665
|
+
// in worktreeRef even though it was not a Controller-managed worktree. Such
|
|
666
|
+
// historical references remain audit evidence, but must not turn every old
|
|
667
|
+
// repository root into current lifecycle debt. Only known Forge/RepoHarness
|
|
668
|
+
// managed-worktree storage can represent preserved implementation here.
|
|
669
|
+
if (!looksLikeForgeManagedWorktree(normalized)) continue;
|
|
670
|
+
// A removed legacy campaign may leave an empty managed-storage directory
|
|
671
|
+
// behind after its Git metadata and contents are already gone. That is not
|
|
672
|
+
// preserved implementation and must not block lifecycle health. A retained
|
|
673
|
+
// path outside current Git inventory only remains lifecycle debt when its
|
|
674
|
+
// worktree .git marker still exists for manual reconciliation.
|
|
675
|
+
if (!existsSync(join(normalized, '.git'))) continue;
|
|
676
|
+
const completed = migrated.work.status === 'completed';
|
|
677
|
+
candidates.push({
|
|
678
|
+
kind: 'retained_migrated_work',
|
|
679
|
+
id: migrated.work.workId,
|
|
680
|
+
path: normalized,
|
|
681
|
+
status: migrated.work.status,
|
|
682
|
+
safe: false,
|
|
683
|
+
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.',
|
|
684
|
+
suggestedAction: 'full_maintenance_pass',
|
|
685
|
+
ownershipStatus: 'explicit',
|
|
686
|
+
sourceControllerHome: migrated.sourceHome,
|
|
687
|
+
objective: migrated.work.objective,
|
|
688
|
+
continuationPrompt: migrated.work.continuationPrompt,
|
|
689
|
+
disposition: completed ? 'completion_reconciliation' : 'resume_or_supersede_review',
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
return candidates.slice(0, maxCandidates);
|
|
694
|
+
}
|
|
695
|
+
|
|
396
696
|
function normalizedOptions(options: RuntimeMaintenanceOptions = {}): Required<RuntimeMaintenanceOptions> {
|
|
397
697
|
return {
|
|
398
698
|
minAgeMinutes: clampNumber(options.minAgeMinutes, DEFAULT_MIN_AGE_MINUTES, 0, 7 * 24 * 60),
|
|
@@ -438,25 +738,129 @@ function scanStaleWorkContractCandidates(
|
|
|
438
738
|
return { contract, ageMinutes };
|
|
439
739
|
})
|
|
440
740
|
.filter(({ ageMinutes }) => ageMinutes >= options.minAgeMinutes)
|
|
741
|
+
.flatMap(({ contract, ageMinutes }) => {
|
|
742
|
+
const authorityRefs = activeWorkAuthorityRefs(repository, controllerHome, contract);
|
|
743
|
+
// A live Plan/Requirement/Schedule/Controller reference is lifecycle authority,
|
|
744
|
+
// not maintenance debt. Keep the Work fenced until that authority disappears;
|
|
745
|
+
// if it later becomes unowned, the next scan will surface it as stale.
|
|
746
|
+
if (authorityRefs.length > 0) return [];
|
|
747
|
+
return [{ contract, ageMinutes, source: inspectStaleWorkRepositorySource(repository, contract) }];
|
|
748
|
+
})
|
|
441
749
|
.sort((left, right) => right.ageMinutes - left.ageMinutes)
|
|
442
750
|
.slice(0, options.maxCandidates)
|
|
443
|
-
.map(({ contract, ageMinutes }) => {
|
|
444
|
-
const
|
|
751
|
+
.map(({ contract, ageMinutes, source }) => {
|
|
752
|
+
const semanticReady = staleWorkSemanticTerminalizationReady(contract);
|
|
445
753
|
return {
|
|
446
754
|
kind: 'stale_work_contract' as const,
|
|
447
755
|
id: contract.workId,
|
|
756
|
+
path: source.path,
|
|
448
757
|
status: contract.status,
|
|
449
|
-
safe:
|
|
450
|
-
reason:
|
|
451
|
-
? `Nonterminal WorkContract is old but still has active lifecycle authority (${authorityRefs.join(', ')}); maintenance must not cancel it from age alone.`
|
|
452
|
-
: 'Nonterminal WorkContract exceeded the explicit maintenance age threshold and has no active Plan, Requirement, Schedule, or Controller authority; explicit full maintenance may cancel it while retaining evidence.',
|
|
758
|
+
safe: source.safeToCancel && semanticReady,
|
|
759
|
+
reason: staleWorkCandidateReason(source, semanticReady),
|
|
453
760
|
ageMinutes,
|
|
454
761
|
suggestedAction: 'full_maintenance_pass' as const,
|
|
455
762
|
ownershipStatus: 'explicit' as const,
|
|
763
|
+
sourceState: source.state,
|
|
764
|
+
disposition: !source.safeToCancel
|
|
765
|
+
? ('source_preservation_required' as const)
|
|
766
|
+
: !semanticReady
|
|
767
|
+
? ('semantic_completion_required' as const)
|
|
768
|
+
: undefined,
|
|
456
769
|
};
|
|
457
770
|
});
|
|
458
771
|
}
|
|
459
772
|
|
|
773
|
+
export function applyStaleWorkContractMaintenanceCandidate(
|
|
774
|
+
repository: RuntimeMaintenanceRepository,
|
|
775
|
+
controllerHome: string,
|
|
776
|
+
candidate: RuntimeMaintenanceCandidate,
|
|
777
|
+
): RuntimeMaintenanceCandidate & { applied: boolean; result: string } {
|
|
778
|
+
const work = getWorkContract({ controllerHome, repoId: repository.repoId }, candidate.id);
|
|
779
|
+
if (!work || isTerminalWorkContractStatus(work.status)) {
|
|
780
|
+
return { ...candidate, applied: false, result: 'already_terminal' };
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// The stale scan is discovery only. Re-evaluate every non-Controller authority
|
|
784
|
+
// while holding the canonical ControllerSession lock, then perform the Work
|
|
785
|
+
// transition before releasing it. A newer controller_claim therefore either
|
|
786
|
+
// wins the lock first and fences this mutation, or starts only after an already
|
|
787
|
+
// completed maintenance terminalization; there is no check-then-cancel gap.
|
|
788
|
+
const fenced = withControllerSessionTerminalizationFence(
|
|
789
|
+
{ controllerHome, repoId: repository.repoId },
|
|
790
|
+
{ workId: work.workId, actor: `runtime-maintenance-terminalize:${work.workId}` },
|
|
791
|
+
() => {
|
|
792
|
+
const current = getWorkContract({ controllerHome, repoId: repository.repoId }, work.workId);
|
|
793
|
+
if (!current || isTerminalWorkContractStatus(current.status)) {
|
|
794
|
+
return { ...candidate, applied: false, result: 'already_terminal' };
|
|
795
|
+
}
|
|
796
|
+
const authorityRefs = activeWorkAuthorityRefs(repository, controllerHome, current);
|
|
797
|
+
if (authorityRefs.length > 0) {
|
|
798
|
+
return {
|
|
799
|
+
...candidate,
|
|
800
|
+
applied: false,
|
|
801
|
+
result: `work_authority_became_active:${authorityRefs.join(',')}`,
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
// Discovery and mutation are separated by an arbitrary Controller/MCP delay.
|
|
805
|
+
// Re-probe the live worktree while the canonical ControllerSession fence is
|
|
806
|
+
// held so neither a newer Controller claim nor a late source write can be
|
|
807
|
+
// raced by stale maintenance terminalization.
|
|
808
|
+
const source = inspectStaleWorkRepositorySource(repository, current);
|
|
809
|
+
if (!source.safeToCancel) {
|
|
810
|
+
return {
|
|
811
|
+
...candidate,
|
|
812
|
+
path: source.path ?? candidate.path,
|
|
813
|
+
safe: false,
|
|
814
|
+
reason: staleWorkCandidateReason(source, staleWorkSemanticTerminalizationReady(current)),
|
|
815
|
+
sourceState: source.state,
|
|
816
|
+
disposition: 'source_preservation_required' as const,
|
|
817
|
+
applied: false,
|
|
818
|
+
result: `work_source_preserved:${source.state}`,
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
const semanticReady = staleWorkSemanticTerminalizationReady(current);
|
|
822
|
+
if (!semanticReady) {
|
|
823
|
+
return {
|
|
824
|
+
...candidate,
|
|
825
|
+
path: source.path ?? candidate.path,
|
|
826
|
+
safe: false,
|
|
827
|
+
reason: staleWorkCandidateReason(source, false),
|
|
828
|
+
sourceState: source.state,
|
|
829
|
+
disposition: 'semantic_completion_required' as const,
|
|
830
|
+
applied: false,
|
|
831
|
+
result: 'work_semantic_completion_required',
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
transitionWorkContractPhase({ controllerHome, repoId: repository.repoId }, current.workId, {
|
|
835
|
+
phase: 'cleanup',
|
|
836
|
+
status: 'cancelled',
|
|
837
|
+
state: 'skipped',
|
|
838
|
+
summary: 'Cancelled by explicit full maintenance after the Work had already reached cleanup with prior semantic phases satisfied and no unique live repository source remained; durable evidence retained.',
|
|
839
|
+
evidenceRefs: current.evidenceRefs,
|
|
840
|
+
});
|
|
841
|
+
return {
|
|
842
|
+
...candidate,
|
|
843
|
+
path: source.path ?? candidate.path,
|
|
844
|
+
safe: true,
|
|
845
|
+
reason: staleWorkCandidateReason(source, true),
|
|
846
|
+
sourceState: source.state,
|
|
847
|
+
disposition: undefined,
|
|
848
|
+
applied: true,
|
|
849
|
+
result: 'work_contract_cancelled_evidence_retained',
|
|
850
|
+
};
|
|
851
|
+
},
|
|
852
|
+
);
|
|
853
|
+
if (!fenced.allowed) {
|
|
854
|
+
const generation = fenced.owner?.claimGeneration;
|
|
855
|
+
return {
|
|
856
|
+
...candidate,
|
|
857
|
+
applied: false,
|
|
858
|
+
result: `work_terminalization_fenced:${fenced.reason}${typeof generation === 'number' ? `:claim_generation=${generation}` : ''}`,
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
return fenced.value;
|
|
862
|
+
}
|
|
863
|
+
|
|
460
864
|
function scanStaleEditSessionCandidates(
|
|
461
865
|
repository: RuntimeMaintenanceRepository,
|
|
462
866
|
controllerHome: string,
|
|
@@ -482,14 +886,20 @@ function scanStaleEditSessionCandidates(
|
|
|
482
886
|
.filter(({ ageMinutes }) => ageMinutes >= options.minAgeMinutes)
|
|
483
887
|
.sort((left, right) => right.ageMinutes - left.ageMinutes)
|
|
484
888
|
.slice(0, options.maxCandidates)
|
|
485
|
-
.
|
|
889
|
+
.flatMap(({ session, ageMinutes }) => {
|
|
486
890
|
const work = session.workId
|
|
487
891
|
? getWorkContract({ controllerHome, repoId: repository.repoId }, session.workId)
|
|
488
892
|
: undefined;
|
|
489
893
|
const terminalWork = Boolean(work && isTerminalWorkContractStatus(work.status));
|
|
894
|
+
if (work && !terminalWork && activeWorkAuthorityRefs(repository, controllerHome, work).length > 0) {
|
|
895
|
+
// The Edit Session inherits the live Work lifecycle authority. Reporting it
|
|
896
|
+
// as stale maintenance debt while that authority is active would duplicate
|
|
897
|
+
// ownership and can permanently block release readiness for valid long work.
|
|
898
|
+
return [];
|
|
899
|
+
}
|
|
490
900
|
const contractFreeDirect = !session.workId;
|
|
491
901
|
const safeToReconcile = terminalWork || contractFreeDirect;
|
|
492
|
-
return {
|
|
902
|
+
return [{
|
|
493
903
|
kind: 'stale_edit_session' as const,
|
|
494
904
|
id: session.sessionId,
|
|
495
905
|
status: session.status,
|
|
@@ -498,11 +908,11 @@ function scanStaleEditSessionCandidates(
|
|
|
498
908
|
? 'Edit Session belongs to a terminal WorkContract; cleanup may reconcile metadata only and must refuse unique uncommitted source changes.'
|
|
499
909
|
: contractFreeDirect
|
|
500
910
|
? 'Contract-free Direct Edit Session has no WorkContract by design; cleanup may reconcile metadata only and must refuse unique uncommitted source changes.'
|
|
501
|
-
: 'Edit Session WorkContract is missing or still nonterminal; active-session ownership must remain fenced.',
|
|
911
|
+
: 'Edit Session WorkContract is missing or still nonterminal without live lifecycle authority; active-session ownership must remain fenced.',
|
|
502
912
|
ageMinutes,
|
|
503
913
|
suggestedAction: 'full_maintenance_pass' as const,
|
|
504
914
|
ownershipStatus: safeToReconcile ? 'explicit' as const : 'unknown' as const,
|
|
505
|
-
};
|
|
915
|
+
}];
|
|
506
916
|
});
|
|
507
917
|
}
|
|
508
918
|
|
|
@@ -552,7 +962,8 @@ export function buildRuntimeMaintenanceStatus(
|
|
|
552
962
|
const tempCandidates = scanRuntimeTempCandidates(repository, normalized.maxCandidates);
|
|
553
963
|
const staleWorkCandidates = scanStaleWorkContractCandidates(repository, controllerHome, normalized);
|
|
554
964
|
const staleEditCandidates = scanStaleEditSessionCandidates(repository, controllerHome, normalized);
|
|
555
|
-
const
|
|
965
|
+
const retainedWorktreeCandidates = scanRetainedWorktreeCandidates(repository, controllerHome, normalized.maxCandidates);
|
|
966
|
+
const candidates = [...localJobCandidates, ...storageCandidates, ...staleWorkCandidates, ...staleEditCandidates, ...retainedWorktreeCandidates, ...tempCandidates].slice(0, normalized.maxCandidates);
|
|
556
967
|
const summary = summarize(candidates);
|
|
557
968
|
const blockingCandidates = candidates.filter((candidate) => candidate.kind !== 'stale_runtime_temp_entry');
|
|
558
969
|
const readyForExecution = storage.report?.readyForExecution === true
|
|
@@ -588,6 +999,7 @@ export function buildRuntimeMaintenanceStatus(
|
|
|
588
999
|
'Pending approvals are not cancelled unless cancel_pending_approvals is explicitly enabled.',
|
|
589
1000
|
'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.',
|
|
590
1001
|
'Runtime lifecycle changes and source repair are outside the runtime maintenance executor.',
|
|
1002
|
+
'Retained or unowned managed worktrees are review-only lifecycle blockers. Runtime maintenance never deletes or modifies their preserved implementation state.',
|
|
591
1003
|
],
|
|
592
1004
|
};
|
|
593
1005
|
}
|
|
@@ -806,18 +1218,7 @@ export function applyRuntimeMaintenance(
|
|
|
806
1218
|
};
|
|
807
1219
|
}
|
|
808
1220
|
if (candidate.kind === 'stale_work_contract') {
|
|
809
|
-
|
|
810
|
-
if (!work || isTerminalWorkContractStatus(work.status)) {
|
|
811
|
-
return { ...candidate, applied: false, result: 'already_terminal' };
|
|
812
|
-
}
|
|
813
|
-
transitionWorkContractPhase({ controllerHome, repoId: repository.repoId }, work.workId, {
|
|
814
|
-
phase: 'cleanup',
|
|
815
|
-
status: 'cancelled',
|
|
816
|
-
state: 'skipped',
|
|
817
|
-
summary: 'Cancelled by explicit full maintenance stale Work cleanup; durable evidence retained.',
|
|
818
|
-
evidenceRefs: work.evidenceRefs,
|
|
819
|
-
});
|
|
820
|
-
return { ...candidate, applied: true, result: 'work_contract_cancelled_evidence_retained' };
|
|
1221
|
+
return applyStaleWorkContractMaintenanceCandidate(repository, controllerHome, candidate);
|
|
821
1222
|
}
|
|
822
1223
|
if (candidate.kind === 'stale_edit_session') {
|
|
823
1224
|
const cleaned = cleanupEditSession(repository.canonicalRoot, candidate.id, {
|