@moretea-labs/forge 1.6.0 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -0
- package/CHANGELOG.md +39 -0
- package/CLAUDE.md +4 -0
- package/README.md +2 -2
- package/README.zh-CN.md +1 -1
- package/SKILL.md +2 -2
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/agents/openai.yaml +1 -1
- package/assets/hooks/hook-input.sh +11 -8
- package/assets/hooks/lib/session-state.sh +10 -7
- package/assets/hooks/lib/workflow-state.sh +13 -177
- package/assets/hooks/post-tool-observer.sh +8 -2
- package/assets/hooks/prompt-guard.sh +5 -81
- package/assets/hooks/session-start-context.sh +56 -1
- package/assets/partials/05-workflow.partial.md +1 -1
- package/assets/partials-agents/02-operating-mode.partial.md +2 -0
- package/assets/partials-agents/03-orchestration.partial.md +1 -1
- package/assets/plugin-registry.v1.json +3 -3
- package/assets/reference-configs/document-generation.md +1 -1
- package/assets/reference-configs/harness-overview.md +1 -1
- package/assets/reference-configs/sprint-contracts.md +3 -3
- package/assets/skill-version.json +2 -2
- package/assets/templates/contract.template.md +2 -7
- package/assets/templates/helpers/capture-plan.sh +2 -2
- package/assets/templates/helpers/check-architecture-sync.sh +17 -0
- package/assets/templates/helpers/check-task-workflow.sh +4 -4
- package/assets/templates/helpers/contract-worktree.sh +1 -21
- package/assets/templates/helpers/ensure-task-workflow.sh +11 -36
- package/assets/templates/helpers/inspect-project-state.ts +8 -6
- package/assets/templates/helpers/migrate-project-template.sh +22 -45
- package/assets/templates/helpers/migrate-workflow-docs.ts +9 -7
- package/assets/templates/helpers/new-plan.sh +1 -1
- package/assets/templates/helpers/plan-to-todo.sh +12 -37
- package/assets/templates/helpers/ship-worktrees.sh +2 -29
- package/assets/templates/helpers/verify-contract.sh +1 -111
- package/assets/templates/helpers/verify-sprint.sh +3 -180
- package/assets/templates/helpers/workflow-contract.ts +5 -1
- package/assets/templates/plan.template.md +1 -1
- package/assets/templates/review.template.md +9 -34
- package/assets/workflow-contract.v1.json +10 -19
- package/docs/README.md +10 -10
- package/docs/architecture/CURRENT.md +178 -0
- package/docs/architecture/EVOLUTION.md +42 -0
- package/docs/architecture/current/README.md +4 -156
- package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
- package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
- package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
- package/docs/architecture/history/README.md +7 -0
- package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
- package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
- package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
- package/docs/architecture/history.md +3 -34
- package/docs/architecture/index.md +15 -98
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
- package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
- package/docs/architecture/versions/1.6.md +38 -0
- package/docs/architecture/versions/1.7.md +41 -0
- package/docs/forge-chatgpt-browser-engine.md +2 -2
- package/docs/forge-chatgpt-mcp-setup.md +4 -28
- package/docs/forge-chatgpt-supervised-automation.md +3 -3
- package/docs/forge-github-issue-launcher.md +1 -1
- package/docs/forge-plugin-management.md +1 -1
- package/docs/forge-runtime-self-healing-loop.md +1 -1
- package/docs/operations/controller-browser-plugin.md +26 -6
- package/docs/operations/controller-performance-and-502.md +143 -0
- package/docs/operations/controller-reliability-runbook.md +1 -1
- package/docs/operations/features.md +2 -2
- package/docs/operations/github-repository.md +1 -1
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/mcp-tool-exposure.md +5 -3
- package/docs/wiki/Architecture.md +6 -6
- package/docs/wiki/Implementation.md +2 -2
- package/docs/wiki/Integrations.md +2 -2
- package/docs/wiki/Work-Lifecycle.md +1 -1
- package/package.json +33 -11
- package/scripts/activate-source-baseline.ts +17 -13
- package/scripts/architecture-event.ts +2 -820
- package/scripts/architecture-queue.sh +2 -645
- package/scripts/archive-architecture-request.sh +2 -242
- package/scripts/archive-workflow.sh +1 -291
- package/scripts/benchmark-background-check-overlap.ts +228 -0
- package/scripts/benchmark-mcp-host.sh +117 -0
- package/scripts/benchmark-mcp-transport.ts +170 -0
- package/scripts/benchmark-quality-harness.ts +146 -0
- package/scripts/benchmark-route-session-concurrency.ts +207 -6
- package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
- package/scripts/benchmark-typescript-navigation.ts +186 -0
- package/scripts/benchmark-work-efficiency.ts +334 -0
- package/scripts/capability-config.ts +2 -392
- package/scripts/capability-resolver.ts +2 -503
- package/scripts/capture-plan.sh +1 -332
- package/scripts/check-agent-tooling.sh +2 -1558
- package/scripts/check-architecture-sync.sh +2 -371
- package/scripts/check-brain-manifest.sh +2 -253
- package/scripts/check-context-files.sh +2 -47
- package/scripts/check-deploy-sql-order.sh +2 -115
- package/scripts/check-mcp-compatibility.ts +14 -0
- package/scripts/check-runtime-architecture.mjs +349 -79
- package/scripts/check-skill-version.ts +3 -191
- package/scripts/check-source-duplication.mjs +61 -0
- package/scripts/check-stable-baseline.ts +92 -0
- package/scripts/check-task-sync.sh +2 -63
- package/scripts/check-task-workflow.sh +2 -1000
- package/scripts/codex-handoff-resume.sh +2 -267
- package/scripts/context-contract-sync.sh +2 -514
- package/scripts/contract-run.ts +2 -463
- package/scripts/contract-worktree.sh +1 -722
- package/scripts/ensure-task-workflow.sh +1 -1225
- package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
- package/scripts/inspect-project-state.ts +4 -279
- package/scripts/install-standalone-recovery.ts +24 -0
- package/scripts/lib/project-init-lib.sh +22 -46
- package/scripts/lib/run-forge-helper.sh +19 -0
- package/scripts/lib/run-forge-helper.ts +26 -0
- package/scripts/migrate-project-template.sh +1 -1189
- package/scripts/migrate-workflow-docs.ts +4 -412
- package/scripts/new-plan.sh +1 -180
- package/scripts/new-spec.sh +1 -47
- package/scripts/new-sprint.sh +1 -46
- package/scripts/plan-to-todo.sh +1 -796
- package/scripts/prepare-codex-handoff.sh +2 -201
- package/scripts/prepare-handoff.sh +1 -78
- package/scripts/refresh-current-status.sh +2 -467
- package/scripts/run-governed-gate.ts +5 -1
- package/scripts/select-agent-context-blocks.sh +2 -64
- package/scripts/ship-worktrees.sh +1 -558
- package/scripts/smoke-ios-physical-device.ts +4 -2
- package/scripts/smoke-runtime-recovery.ts +1 -35
- package/scripts/smoke-schedule-engine.ts +1 -20
- package/scripts/sprint-backlog.sh +1 -834
- package/scripts/stage-runtime-release.ts +2 -1
- package/scripts/summarize-failures.sh +2 -123
- package/scripts/switch-plan.sh +1 -150
- package/scripts/sync-brain-docs.sh +2 -379
- package/scripts/sync-generated-authority.mjs +175 -0
- package/scripts/verify-contract.sh +2 -703
- package/scripts/verify-forge-runtime.sh +0 -1
- package/scripts/verify-sprint.sh +1 -677
- package/scripts/workflow-contract.ts +4 -205
- package/scripts/workstream-sync.sh +2 -261
- package/src/cli/agent-jobs/executor-health.ts +1 -1
- package/src/cli/commands/init-hook.ts +3 -3
- package/src/cli/commands/mcp.ts +9 -5
- package/src/cli/commands/plugin.ts +12 -3
- package/src/cli/commands/recovery.ts +25 -3
- package/src/cli/commands/runtime.ts +55 -3
- package/src/cli/commands/security.ts +12 -1
- package/src/cli/commands/setup-profile.ts +31 -21
- package/src/cli/controller/check-runner.ts +113 -5
- package/src/cli/controller/composite-result.ts +1 -1
- package/src/cli/controller/context/focus.ts +51 -0
- package/src/cli/controller/context/instruction-context.ts +114 -0
- package/src/cli/controller/context/known-paths.ts +76 -0
- package/src/cli/controller/context/query-planning.ts +110 -0
- package/src/cli/controller/context/rule-resolution.ts +532 -0
- package/src/cli/controller/context/source-materializer.ts +278 -0
- package/src/cli/controller/context/structural.ts +16 -0
- package/src/cli/controller/context/types.ts +286 -0
- package/src/cli/controller/context-pack.ts +828 -487
- package/src/cli/controller/launch-agents.ts +77 -2
- package/src/cli/controller/operational-plan.ts +2 -2
- package/src/cli/controller/work-mode.ts +6 -4
- package/src/cli/editing/edit-session.ts +60 -7
- package/src/cli/github/contracts.ts +16 -0
- package/src/cli/github/github.ts +135 -16
- package/src/cli/github/session.ts +2 -1
- package/src/cli/installer/managed-entries.ts +15 -1
- package/src/cli/local-bridge/console-automations.ts +54 -78
- package/src/cli/local-bridge/console-view-models.ts +1 -35
- package/src/cli/local-bridge/facade-api.ts +37 -264
- package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
- package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +23 -13
- package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
- package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +6 -4
- package/src/cli/local-bridge/frontend/src/lib/api.ts +1 -6
- package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
- package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +2 -2
- package/src/cli/local-bridge/frontend/src/styles.css +1 -1
- package/src/cli/local-bridge/frontend/src/types.ts +5 -8
- package/src/cli/local-bridge/job-store.ts +1 -490
- package/src/cli/local-bridge/server.ts +2 -515
- package/src/cli/local-bridge/ui-dist/app.css +1 -1
- package/src/cli/local-bridge/ui-dist/app.js +3703 -3757
- package/src/cli/mcp/auth.ts +5 -5
- package/src/cli/mcp/instructions.ts +3 -2
- package/src/cli/mcp/legacy-tool-service.ts +10 -222
- package/src/cli/mcp/multi-repository.ts +16 -5
- package/src/cli/mcp/oauth.ts +175 -26
- package/src/cli/mcp/post-finalize-work-attribution.ts +150 -0
- package/src/cli/mcp/repository-tools.ts +282 -146
- package/src/cli/mcp/server.ts +586 -116
- package/src/cli/mcp/tool-capability-cache.ts +1 -1
- package/src/cli/mcp/tool-contract.ts +14 -0
- package/src/cli/mcp/toolset-selection.ts +1 -1
- package/src/cli/mcp/toolset.ts +44 -7
- package/src/cli/mcp/transports/http.ts +124 -14
- package/src/cli/mcp/transports/session-registry.ts +43 -5
- package/src/cli/mcp/types.ts +3 -3
- package/src/cli/repositories/command-classifier.ts +103 -21
- package/src/cli/repositories/command-executor.ts +78 -526
- package/src/cli/repositories/command-process.ts +108 -0
- package/src/cli/repositories/command-scope.ts +99 -41
- package/src/cli/repositories/controller-home.ts +7 -8
- package/src/cli/repositories/git-command-executor.ts +2 -0
- package/src/cli/repositories/locks.ts +53 -16
- package/src/cli/repositories/repository-snapshot.ts +124 -0
- package/src/cli/repositories/selected-path-actions.ts +17 -9
- package/src/cli/repositories/structured-git.ts +118 -6
- package/src/cli/repository/inspector.ts +563 -65
- package/src/cli/repository/session-cache.ts +58 -17
- package/src/cli/v81-entry.ts +1 -4
- package/src/effects/fs-transaction.ts +85 -17
- package/src/runtime/context/codegraph-read-provider.ts +233 -2
- package/src/runtime/context/codegraph-sidecar.cjs +74 -40
- package/src/runtime/context/generic-lsp-provider.ts +355 -0
- package/src/runtime/context/lsp-client.ts +283 -0
- package/src/runtime/context/semantic-navigation-contract.ts +61 -0
- package/src/runtime/context/semantic-navigation.ts +223 -0
- package/src/runtime/context/swift-navigation.ts +565 -0
- package/src/runtime/context/typescript-navigation.ts +226 -0
- package/src/runtime/control-plane/dispatch-priority.ts +28 -0
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +48 -16
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +106 -0
- package/src/runtime/control-plane/execution/retained-work-resume.ts +203 -0
- package/src/runtime/control-plane/execution/session-store.ts +18 -0
- package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
- package/src/runtime/control-plane/execution/work-completion-authority.ts +58 -0
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +200 -0
- package/src/runtime/control-plane/execution/work-execution-support.ts +386 -0
- package/src/runtime/control-plane/execution/work-finalization-service.ts +1913 -0
- package/src/runtime/control-plane/execution/work-handle-authority.ts +96 -0
- package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
- package/src/runtime/control-plane/execution/work-operation-service.ts +468 -0
- package/src/runtime/control-plane/execution/work-path-scope.ts +37 -0
- package/src/runtime/control-plane/execution/work-preparation-service.ts +479 -0
- package/src/runtime/control-plane/execution/work-process-evidence.ts +129 -0
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
- package/src/runtime/control-plane/execution/work-task-receipt.ts +22 -18
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +156 -11
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +22 -14
- package/src/runtime/control-plane/execution/work-verification-context.ts +56 -0
- package/src/runtime/control-plane/execution/work-verification-service.ts +388 -0
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +90 -9
- package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
- package/src/runtime/control-plane/facade/controller-round-relay.ts +1118 -0
- package/src/runtime/control-plane/facade/controller-session-store.ts +302 -12
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +17 -1
- package/src/runtime/control-plane/facade/goal-workloop.ts +928 -190
- package/src/runtime/control-plane/facade/index.ts +3 -0
- package/src/runtime/control-plane/facade/plan-contract-store.ts +275 -42
- package/src/runtime/control-plane/facade/repository-work-admission.ts +147 -0
- package/src/runtime/control-plane/facade/requirement-authority.ts +83 -0
- package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
- package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
- package/src/runtime/control-plane/facade/types.ts +90 -10
- package/src/runtime/control-plane/facade/work-contract-store.ts +157 -261
- package/src/runtime/control-plane/facade/work-state-machine.ts +250 -0
- package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
- package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
- package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +152 -0
- package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +278 -543
- package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
- package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
- package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
- package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
- package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
- package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +63 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +365 -117
- package/src/runtime/control-plane/launcher/launch-reservation-store.ts +89 -0
- package/src/runtime/control-plane/launcher/thin-launcher.ts +244 -11
- package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
- package/src/runtime/control-plane/persistence/requirement-store.ts +0 -12
- package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
- package/src/runtime/control-plane/release-retention.ts +62 -2
- package/src/runtime/control-plane/repo-actor/registry.ts +4 -2
- package/src/runtime/control-plane/routing/route-policy.ts +24 -21
- package/src/runtime/control-plane/routing/workspace-admission.ts +70 -0
- package/src/runtime/control-plane/runtime-status-client.ts +0 -2
- package/src/runtime/diagnostics/mcp-timing.ts +13 -0
- package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
- package/src/runtime/diagnostics/performance.ts +24 -1
- package/src/runtime/evidence/event-ledger.ts +1 -1
- package/src/runtime/evidence/process-check-execution.ts +18 -0
- package/src/runtime/execution/jobs/child-reference.ts +6 -2
- package/src/runtime/execution/jobs/store.ts +58 -4
- package/src/runtime/execution/jobs/types.ts +1 -1
- package/src/runtime/execution/managed-workspace.ts +203 -1
- package/src/runtime/execution/process-runtime/check-facade.ts +51 -4
- package/src/runtime/execution/process-runtime/check-result.ts +88 -0
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +7 -1
- package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +367 -40
- package/src/runtime/execution/process-runtime/gc.ts +38 -6
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +1201 -0
- package/src/runtime/execution/process-runtime/persisted-check.ts +51 -5
- package/src/runtime/execution/process-runtime/resource-claims.ts +10 -6
- package/src/runtime/execution/process-runtime/runtime.ts +53 -3
- package/src/runtime/execution/process-runtime/store.ts +1 -0
- package/src/runtime/execution/process-runtime/types.ts +4 -12
- package/src/runtime/execution/thin-harness/async-process.ts +12 -2
- package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
- package/src/runtime/execution/work-activity.ts +21 -0
- package/src/runtime/execution/workers/executor.ts +18 -61
- package/src/runtime/gateway/mcp/execution-tools.ts +35 -1852
- package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +61 -0
- package/src/runtime/gateway/mcp/process-tools.ts +83 -37
- package/src/runtime/gateway/mcp/router.ts +186 -84
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +565 -0
- package/src/runtime/gateway/mcp/runtime-tools.ts +2378 -1947
- package/src/runtime/plugins/app-store-connect-adapter.ts +216 -47
- package/src/runtime/plugins/browser-adapter.ts +2662 -829
- package/src/runtime/plugins/browser-automation-service.ts +10 -0
- package/src/runtime/plugins/browser-handoff-host.ts +2 -25
- package/src/runtime/plugins/browser-handoff.ts +110 -5
- package/src/runtime/plugins/browser-macos-bridge.ts +701 -63
- package/src/runtime/plugins/browser-manifest-surface.ts +603 -0
- package/src/runtime/plugins/browser-native-session-service.ts +222 -0
- package/src/runtime/plugins/browser-node-bridge.ts +6 -34
- package/src/runtime/plugins/browser-provider-registry.ts +109 -0
- package/src/runtime/plugins/browser-runtime-contract.ts +154 -0
- package/src/runtime/plugins/browser-runtime.ts +193 -0
- package/src/runtime/plugins/browser-session-authority.ts +332 -0
- package/src/runtime/plugins/browser-session-store.ts +121 -0
- package/src/runtime/plugins/browser-session-types.ts +98 -0
- package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +10 -4
- package/src/runtime/plugins/external-adapter.ts +358 -3
- package/src/runtime/plugins/external-unix-socket.ts +7 -1
- package/src/runtime/plugins/first-party-registry.ts +2 -0
- package/src/runtime/plugins/github-adapter.ts +125 -0
- package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
- package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
- package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
- package/src/runtime/plugins/ios-adapter.ts +106 -16
- package/src/runtime/plugins/ios-agent-device.ts +234 -64
- package/src/runtime/plugins/ios-physical-device.ts +1143 -392
- package/src/runtime/plugins/lightweight-action.ts +153 -0
- package/src/runtime/plugins/local-system-adapter.ts +123 -6
- package/src/runtime/plugins/macos-capability-broker.ts +47 -9
- package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
- package/src/runtime/plugins/plugin-management-adapter.ts +49 -8
- package/src/runtime/plugins/resend-adapter.ts +1 -1
- package/src/runtime/plugins/store.ts +330 -33
- package/src/runtime/plugins/types.ts +38 -3
- package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
- package/src/runtime/projections/git-status-sampler.ts +16 -16
- package/src/runtime/projections/materialized-view.ts +28 -10
- package/src/runtime/recovery/maintenance-executor.ts +426 -25
- package/src/runtime/root/mcp-transport.ts +180 -10
- package/src/runtime/root/package-connector-service.ts +230 -10
- package/src/runtime/root/package-runtime-release.ts +143 -17
- package/src/runtime/root/package-runtime-service.ts +461 -22
- package/src/runtime/root/release-manifest.ts +144 -18
- package/src/runtime/root/release-materialize.ts +125 -32
- package/src/runtime/root/release-store.ts +48 -11
- package/src/runtime/root/runtime.ts +23 -5
- package/src/runtime/root/service.ts +100 -29
- package/src/runtime/root/types.ts +12 -0
- package/src/runtime/safe-tooling/ios-development.ts +321 -63
- package/src/runtime/shared/trusted-executable.ts +62 -0
- package/src/runtime/shared/trusted-node-executable.ts +48 -0
- package/src/runtime/standalone-recovery/core.ts +1079 -240
- package/src/runtime/standalone-recovery/entry.ts +100 -24
- package/src/runtime/standalone-recovery/installer.ts +20 -4
- package/src/runtime/standalone-recovery/service-labels.ts +2 -0
- package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
- package/src/runtime/workflow/schedules/browser-probe.ts +81 -3
- package/src/runtime/workflow/schedules/engine.ts +364 -202
- package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
- package/src/runtime/workflow/schedules/settlement.ts +64 -17
- package/src/runtime/workflow/schedules/store.ts +209 -19
- package/src/runtime/workflow/schedules/types.ts +2 -4
- package/src/runtime/workflow/schedules/work-continuation.ts +26 -13
- package/src/testing/test-governance.ts +8 -40
- package/assets/reference-configs/heartbeat-triage.md +0 -57
- package/assets/templates/helpers/harness-trace-grade.sh +0 -150
- package/assets/templates/helpers/heartbeat-triage.sh +0 -361
- package/assets/templates/helpers/maintenance-triage.sh +0 -167
- package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
- package/docs/architecture/current/architecture-invariants.md +0 -331
- package/docs/architecture/current/automation-and-schedule-engine.md +0 -459
- package/docs/architecture/current/codegraph-context-provider.md +0 -238
- package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
- package/docs/architecture/current/controller-repository-decoupling.md +0 -148
- package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -373
- package/docs/architecture/current/entity-model.md +0 -612
- package/docs/architecture/current/external-plugin-governance.md +0 -396
- package/docs/architecture/current/failure-recovery.md +0 -447
- package/docs/architecture/current/governance.md +0 -213
- package/docs/architecture/current/human-interaction-plane.md +0 -87
- package/docs/architecture/current/implementation-status.md +0 -128
- package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
- package/docs/architecture/current/legacy-governance.md +0 -58
- package/docs/architecture/current/local-workspace-target-grants.md +0 -70
- package/docs/architecture/current/migration-roadmap.md +0 -105
- package/docs/architecture/current/multi-repository-execution.md +0 -371
- package/docs/architecture/current/operations-runbook.md +0 -45
- package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
- package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
- package/docs/architecture/current/repository-access-modes.md +0 -102
- package/docs/architecture/current/route-policy.md +0 -15
- package/docs/architecture/current/route-session-concurrency.md +0 -85
- package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
- package/docs/architecture/current/runtime-directory-map.md +0 -49
- package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
- package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
- package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
- package/docs/architecture/current/system-overview.md +0 -169
- package/docs/architecture/current/target-requirements-traceability.md +0 -202
- package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
- package/docs/architecture/current/thin-harness-v1.md +0 -321
- package/docs/architecture/current/verification-and-release-gates.md +0 -391
- package/docs/forge-autonomous-goal-loop.md +0 -9
- package/docs/forge-provider-configuration.md +0 -7
- package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -93
- package/docs/operations/gmail-assistant-routines.md +0 -186
- package/docs/operations/personal-assistant-operator-guide.md +0 -61
- package/docs/operations/provider-configuration.md +0 -31
- package/scripts/harness-trace-grade.sh +0 -150
- package/scripts/heartbeat-triage.sh +0 -361
- package/scripts/maintenance-triage.sh +0 -167
- package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
- package/src/cli/repositories/goal-registry.ts +0 -239
- package/src/runtime/assistant/action-proposals.ts +0 -279
- package/src/runtime/assistant/intent.ts +0 -395
- package/src/runtime/assistant/model-provider.ts +0 -460
- package/src/runtime/assistant/openapi.ts +0 -223
- package/src/runtime/assistant/policy.ts +0 -89
- package/src/runtime/assistant/readiness.ts +0 -258
- package/src/runtime/assistant/routine-runtime.ts +0 -477
- package/src/runtime/assistant/schedule-binding.ts +0 -171
- package/src/runtime/assistant/standing-grants.ts +0 -375
- package/src/runtime/assistant/store.ts +0 -243
- package/src/runtime/assistant/types.ts +0 -135
- package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
- package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
- package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
- package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
- package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
- package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
- package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
- package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
- package/src/runtime/control-plane/goal-loop/index.ts +0 -13
- package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
- package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
- package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
- package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
- package/src/runtime/control-plane/goal-loop/types.ts +0 -378
- package/src/runtime/control-plane/startup-recovery.ts +0 -198
- package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
- package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
- package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
- package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
- package/src/runtime/workflow/findings/store.ts +0 -204
- package/src/runtime/workflow/findings/types.ts +0 -58
- package/src/runtime/workflow/portfolio/engine.ts +0 -20
- package/src/runtime/workflow/portfolio/store.ts +0 -185
- package/src/runtime/workflow/portfolio/types.ts +0 -29
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { statSync } from 'fs';
|
|
2
|
+
import { dirname, relative, resolve } from 'path';
|
|
3
|
+
import * as ts from 'typescript';
|
|
4
|
+
|
|
5
|
+
export type TypeScriptNavigationKind = 'definition' | 'references' | 'implementations';
|
|
6
|
+
|
|
7
|
+
export interface TypeScriptNavigationRequest {
|
|
8
|
+
navigation: TypeScriptNavigationKind;
|
|
9
|
+
path: string;
|
|
10
|
+
line: number;
|
|
11
|
+
column: number;
|
|
12
|
+
tsconfigPath?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface TypeScriptNavigationLocation {
|
|
16
|
+
path: string;
|
|
17
|
+
line: number;
|
|
18
|
+
column: number;
|
|
19
|
+
name?: string;
|
|
20
|
+
kind?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface TypeScriptNavigationResult {
|
|
24
|
+
navigation: TypeScriptNavigationKind;
|
|
25
|
+
target: { path: string; line: number; column: number };
|
|
26
|
+
locations: TypeScriptNavigationLocation[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface TypeScriptNavigationAccess {
|
|
30
|
+
/** Stable identity for one read-policy scope. Restricted and unrestricted projects must never share a Language Service. */
|
|
31
|
+
cacheScope: string;
|
|
32
|
+
/** Source identity observed by the caller. A different identity must not reuse project membership. */
|
|
33
|
+
sourceIdentity?: string;
|
|
34
|
+
/** Return true only for repository-relative paths that this navigation call may read. */
|
|
35
|
+
allowRepositoryPath(relativePath: string): boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface CachedProject {
|
|
39
|
+
repoRoot: string;
|
|
40
|
+
configPath: string;
|
|
41
|
+
service: ts.LanguageService;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const projects = new Map<string, CachedProject>();
|
|
45
|
+
const MAX_CACHED_TYPESCRIPT_PROJECTS = 32;
|
|
46
|
+
|
|
47
|
+
function normalizePath(path: string): string {
|
|
48
|
+
return path.replace(/\\/g, '/');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function scriptVersion(path: string): string {
|
|
52
|
+
try {
|
|
53
|
+
const stat = statSync(path);
|
|
54
|
+
return `${stat.mtimeMs}:${stat.size}`;
|
|
55
|
+
} catch {
|
|
56
|
+
return 'missing';
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function repositoryRelativePath(repoRoot: string, fileName: string): string | undefined {
|
|
61
|
+
const normalized = normalizePath(relative(repoRoot, resolve(fileName)));
|
|
62
|
+
if (!normalized || normalized === '.' || normalized === '..' || normalized.startsWith('../')) return undefined;
|
|
63
|
+
return normalized;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function loadProject(repoRoot: string, tsconfigPath = 'tsconfig.json', access?: TypeScriptNavigationAccess): CachedProject {
|
|
67
|
+
const root = resolve(repoRoot);
|
|
68
|
+
const configPath = resolve(root, tsconfigPath);
|
|
69
|
+
const configRelative = repositoryRelativePath(root, configPath);
|
|
70
|
+
if (!configRelative) throw new Error('TypeScript navigation tsconfig must be inside the repository.');
|
|
71
|
+
if (access && !access.allowRepositoryPath(configRelative)) {
|
|
72
|
+
throw new Error(`TypeScript navigation tsconfig is denied by read policy: ${configRelative}.`);
|
|
73
|
+
}
|
|
74
|
+
const cacheKey = `${root}\0${configPath}\0${scriptVersion(configPath)}\0${access?.cacheScope ?? 'unrestricted'}\0${access?.sourceIdentity ?? 'unbound-source'}`;
|
|
75
|
+
const cached = projects.get(cacheKey);
|
|
76
|
+
if (cached) {
|
|
77
|
+
projects.delete(cacheKey);
|
|
78
|
+
projects.set(cacheKey, cached);
|
|
79
|
+
return cached;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const canReadAbsolute = (fileName: string): boolean => {
|
|
83
|
+
if (!access) return true;
|
|
84
|
+
const repoRelative = repositoryRelativePath(root, fileName);
|
|
85
|
+
return repoRelative === undefined || access.allowRepositoryPath(repoRelative);
|
|
86
|
+
};
|
|
87
|
+
const readAllowedFile = (fileName: string): string | undefined => canReadAbsolute(fileName) ? ts.sys.readFile(fileName) : undefined;
|
|
88
|
+
const fileExistsAllowed = (fileName: string): boolean => canReadAbsolute(fileName) && ts.sys.fileExists(fileName);
|
|
89
|
+
const readDirectoryAllowed: typeof ts.sys.readDirectory = (path, extensions, exclude, include, depth) =>
|
|
90
|
+
ts.sys.readDirectory(path, extensions, exclude, include, depth).filter(canReadAbsolute);
|
|
91
|
+
|
|
92
|
+
const config = ts.readConfigFile(configPath, readAllowedFile);
|
|
93
|
+
if (config.error) {
|
|
94
|
+
throw new Error(ts.flattenDiagnosticMessageText(config.error.messageText, '\n'));
|
|
95
|
+
}
|
|
96
|
+
const parseHost: ts.ParseConfigHost = {
|
|
97
|
+
useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
|
|
98
|
+
readDirectory: readDirectoryAllowed,
|
|
99
|
+
fileExists: fileExistsAllowed,
|
|
100
|
+
readFile: readAllowedFile,
|
|
101
|
+
};
|
|
102
|
+
const parsed = ts.parseJsonConfigFileContent(config.config, parseHost, dirname(configPath), undefined, configPath);
|
|
103
|
+
if (parsed.errors.length > 0) {
|
|
104
|
+
throw new Error(parsed.errors.map((entry) => ts.flattenDiagnosticMessageText(entry.messageText, '\n')).join('\n'));
|
|
105
|
+
}
|
|
106
|
+
const scriptFileNames = parsed.fileNames.filter(canReadAbsolute);
|
|
107
|
+
|
|
108
|
+
const host: ts.LanguageServiceHost = {
|
|
109
|
+
getCompilationSettings: () => parsed.options,
|
|
110
|
+
getScriptFileNames: () => scriptFileNames,
|
|
111
|
+
getScriptVersion: scriptVersion,
|
|
112
|
+
getScriptSnapshot: (fileName) => {
|
|
113
|
+
if (!fileExistsAllowed(fileName)) return undefined;
|
|
114
|
+
const content = readAllowedFile(fileName);
|
|
115
|
+
return content === undefined ? undefined : ts.ScriptSnapshot.fromString(content);
|
|
116
|
+
},
|
|
117
|
+
getCurrentDirectory: () => root,
|
|
118
|
+
getDefaultLibFileName: (options) => ts.getDefaultLibFilePath(options),
|
|
119
|
+
fileExists: fileExistsAllowed,
|
|
120
|
+
readFile: readAllowedFile,
|
|
121
|
+
readDirectory: readDirectoryAllowed,
|
|
122
|
+
directoryExists: ts.sys.directoryExists,
|
|
123
|
+
getDirectories: ts.sys.getDirectories,
|
|
124
|
+
realpath: ts.sys.realpath,
|
|
125
|
+
useCaseSensitiveFileNames: () => ts.sys.useCaseSensitiveFileNames,
|
|
126
|
+
getNewLine: () => ts.sys.newLine,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const project = {
|
|
130
|
+
repoRoot: root,
|
|
131
|
+
configPath,
|
|
132
|
+
service: ts.createLanguageService(host, ts.createDocumentRegistry()),
|
|
133
|
+
};
|
|
134
|
+
projects.set(cacheKey, project);
|
|
135
|
+
while (projects.size > MAX_CACHED_TYPESCRIPT_PROJECTS) {
|
|
136
|
+
const oldestKey = projects.keys().next().value as string | undefined;
|
|
137
|
+
if (!oldestKey) break;
|
|
138
|
+
const oldest = projects.get(oldestKey);
|
|
139
|
+
projects.delete(oldestKey);
|
|
140
|
+
oldest?.service.dispose();
|
|
141
|
+
}
|
|
142
|
+
return project;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function sourcePosition(project: CachedProject, program: ts.Program, fileName: string, line: number, column: number): number {
|
|
146
|
+
if (!Number.isInteger(line) || line < 1 || !Number.isInteger(column) || column < 1) {
|
|
147
|
+
throw new Error('TypeScript navigation line and column must be positive 1-based integers.');
|
|
148
|
+
}
|
|
149
|
+
const source = program.getSourceFile(fileName);
|
|
150
|
+
if (!source) throw new Error(`TypeScript project does not include ${normalizePath(relative(project.repoRoot, fileName))}.`);
|
|
151
|
+
if (line > source.getLineAndCharacterOfPosition(source.getEnd()).line + 1) {
|
|
152
|
+
throw new Error(`TypeScript navigation line ${line} is outside ${normalizePath(relative(project.repoRoot, fileName))}.`);
|
|
153
|
+
}
|
|
154
|
+
return source.getPositionOfLineAndCharacter(line - 1, column - 1);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function location(
|
|
158
|
+
project: CachedProject,
|
|
159
|
+
program: ts.Program,
|
|
160
|
+
fileName: string,
|
|
161
|
+
textSpan: ts.TextSpan,
|
|
162
|
+
name?: string,
|
|
163
|
+
kind?: ts.ScriptElementKind,
|
|
164
|
+
): TypeScriptNavigationLocation {
|
|
165
|
+
const source = program.getSourceFile(fileName);
|
|
166
|
+
if (!source) {
|
|
167
|
+
return { path: normalizePath(relative(project.repoRoot, fileName)), line: 1, column: 1, ...(name ? { name } : {}), ...(kind ? { kind } : {}) };
|
|
168
|
+
}
|
|
169
|
+
const point = source.getLineAndCharacterOfPosition(textSpan.start);
|
|
170
|
+
return {
|
|
171
|
+
path: normalizePath(relative(project.repoRoot, fileName)),
|
|
172
|
+
line: point.line + 1,
|
|
173
|
+
column: point.character + 1,
|
|
174
|
+
...(name ? { name } : {}),
|
|
175
|
+
...(kind ? { kind } : {}),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function dedupe(locations: TypeScriptNavigationLocation[]): TypeScriptNavigationLocation[] {
|
|
180
|
+
const seen = new Set<string>();
|
|
181
|
+
return locations.filter((entry) => {
|
|
182
|
+
const key = `${entry.path}:${entry.line}:${entry.column}:${entry.name ?? ''}:${entry.kind ?? ''}`;
|
|
183
|
+
if (seen.has(key)) return false;
|
|
184
|
+
seen.add(key);
|
|
185
|
+
return true;
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function navigateTypeScriptSymbol(
|
|
190
|
+
repoRoot: string,
|
|
191
|
+
request: TypeScriptNavigationRequest,
|
|
192
|
+
access?: TypeScriptNavigationAccess,
|
|
193
|
+
): TypeScriptNavigationResult {
|
|
194
|
+
const project = loadProject(repoRoot, request.tsconfigPath, access);
|
|
195
|
+
const fileName = resolve(project.repoRoot, request.path);
|
|
196
|
+
// A references response may materialize hundreds of locations. Acquire the
|
|
197
|
+
// Language Service Program once per navigation so that location formatting
|
|
198
|
+
// does not repeatedly re-enter the service while the result is built.
|
|
199
|
+
const program = project.service.getProgram();
|
|
200
|
+
if (!program) throw new Error('TypeScript Language Service did not produce a Program.');
|
|
201
|
+
const position = sourcePosition(project, program, fileName, request.line, request.column);
|
|
202
|
+
let locations: TypeScriptNavigationLocation[] = [];
|
|
203
|
+
|
|
204
|
+
if (request.navigation === 'definition') {
|
|
205
|
+
locations = (project.service.getDefinitionAtPosition(fileName, position) ?? [])
|
|
206
|
+
.map((entry) => location(project, program, entry.fileName, entry.textSpan, entry.name, entry.kind));
|
|
207
|
+
} else if (request.navigation === 'implementations') {
|
|
208
|
+
locations = (project.service.getImplementationAtPosition(fileName, position) ?? [])
|
|
209
|
+
.map((entry) => location(project, program, entry.fileName, entry.textSpan, undefined, entry.kind));
|
|
210
|
+
} else {
|
|
211
|
+
locations = (project.service.findReferences(fileName, position) ?? []).flatMap((group) =>
|
|
212
|
+
group.references.map((entry) => location(project, program, entry.fileName, entry.textSpan, group.definition.name, group.definition.kind)),
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
navigation: request.navigation,
|
|
218
|
+
target: { path: normalizePath(request.path), line: request.line, column: request.column },
|
|
219
|
+
locations: dedupe(locations),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function clearTypeScriptNavigationCache(): void {
|
|
224
|
+
for (const project of projects.values()) project.service.dispose();
|
|
225
|
+
projects.clear();
|
|
226
|
+
}
|
|
@@ -65,3 +65,31 @@ export function compareExecutionJobDispatchRanks(
|
|
|
65
65
|
|| left.queuedAtMs - right.queuedAtMs
|
|
66
66
|
|| left.jobId.localeCompare(right.jobId);
|
|
67
67
|
}
|
|
68
|
+
|
|
69
|
+
export function selectExecutionJobDispatchRepositories(
|
|
70
|
+
activeJobs: readonly ExecutionJob[],
|
|
71
|
+
scheduleNow: number,
|
|
72
|
+
lastRepoDispatch: ReadonlyMap<string, number>,
|
|
73
|
+
): string[] {
|
|
74
|
+
const waiting = activeJobs.filter(isExecutionJobDispatchCandidate);
|
|
75
|
+
const rankByJobId = new Map(
|
|
76
|
+
waiting.map((job) => [job.jobId, rankExecutionJobForDispatch(job, scheduleNow)] as const),
|
|
77
|
+
);
|
|
78
|
+
const compareWaiting = (left: ExecutionJob, right: ExecutionJob): number =>
|
|
79
|
+
compareExecutionJobDispatchRanks(rankByJobId.get(left.jobId)!, rankByJobId.get(right.jobId)!);
|
|
80
|
+
const topByRepo = new Map<string, ExecutionJob>();
|
|
81
|
+
for (const job of waiting.slice().sort(compareWaiting)) {
|
|
82
|
+
if (!topByRepo.has(job.repoId)) topByRepo.set(job.repoId, job);
|
|
83
|
+
}
|
|
84
|
+
return [...topByRepo.keys()].sort((left, right) => {
|
|
85
|
+
const leftRank = rankByJobId.get(topByRepo.get(left)!.jobId)!;
|
|
86
|
+
const rightRank = rankByJobId.get(topByRepo.get(right)!.jobId)!;
|
|
87
|
+
const priority = leftRank.effectivePriority - rightRank.effectivePriority;
|
|
88
|
+
if (priority !== 0) return priority;
|
|
89
|
+
const fairness = (lastRepoDispatch.get(left) ?? 0) - (lastRepoDispatch.get(right) ?? 0);
|
|
90
|
+
return fairness
|
|
91
|
+
|| leftRank.queuedAtMs - rightRank.queuedAtMs
|
|
92
|
+
|| leftRank.jobId.localeCompare(rightRank.jobId)
|
|
93
|
+
|| left.localeCompare(right);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { createHash } from 'crypto';
|
|
2
2
|
import { getEditSession, listEditSessions, type EditSession } from '../../../cli/editing/edit-session';
|
|
3
|
-
import { globMatches } from '../../../cli/mcp/paths';
|
|
4
3
|
import { runProcess } from '../../../effects/process-runner';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { getWorkContract, updateWorkContract } from '../facade/work-contract-store';
|
|
5
|
+
import { completeWorkWithReceipt } from './work-completion-authority';
|
|
7
6
|
import { isDirectEditWorkCompletionReceipt, isTerminalWorkContractStatus, type DirectEditWorkCompletionReceipt, type WorkReconciliationRecord } from '../facade/types';
|
|
8
|
-
import {
|
|
7
|
+
import { historicalVerificationEvidenceAtRevision } from './verification-evidence';
|
|
8
|
+
import { readWorkHandle } from './work-handle-store';
|
|
9
|
+
import { assertWorkPathsWithinScope, findWorkPathScopeViolation } from './work-path-scope';
|
|
9
10
|
|
|
10
11
|
export interface DirectEditWorkCompletionReconciliation {
|
|
11
12
|
completedWorkIds: string[];
|
|
@@ -101,6 +102,15 @@ export function reconcileFinalizedDirectEditWorksAfterCommit(input: {
|
|
|
101
102
|
skipped.push({ sessionId: session.sessionId, workId: session.workId, reason: 'configured_checks_not_passed' });
|
|
102
103
|
continue;
|
|
103
104
|
}
|
|
105
|
+
const scopeViolation = findWorkPathScopeViolation(work, paths);
|
|
106
|
+
if (scopeViolation) {
|
|
107
|
+
skipped.push({
|
|
108
|
+
sessionId: session.sessionId,
|
|
109
|
+
workId: session.workId,
|
|
110
|
+
reason: `${scopeViolation.kind === 'forbidden' ? 'work_scope_forbidden' : 'work_scope_out_of_scope'}:${scopeViolation.path}`,
|
|
111
|
+
});
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
104
114
|
const status = git(input.repoRoot, ['status', '--porcelain=v1', '--', ...paths]);
|
|
105
115
|
if (!status.ok || status.stdout.trim()) {
|
|
106
116
|
skipped.push({ sessionId: session.sessionId, workId: session.workId, reason: 'owned_paths_not_clean' });
|
|
@@ -140,7 +150,7 @@ export function reconcileFinalizedDirectEditWorksAfterCommit(input: {
|
|
|
140
150
|
verifiedAt: session.verifiedAt ?? session.finalizedAt ?? recordedAt,
|
|
141
151
|
recordedAt,
|
|
142
152
|
};
|
|
143
|
-
|
|
153
|
+
completeWorkWithReceipt(
|
|
144
154
|
{ controllerHome: input.controllerHome, repoId: input.repoId },
|
|
145
155
|
session.workId,
|
|
146
156
|
receipt,
|
|
@@ -209,7 +219,16 @@ export function acceptReviewedDirectEditWorkReconciliation(input: ReviewedDirect
|
|
|
209
219
|
}
|
|
210
220
|
throw new Error(`DIRECT_EDIT_WORK_RECONCILIATION_WORK_TERMINAL: ${input.workId}`);
|
|
211
221
|
}
|
|
212
|
-
|
|
222
|
+
const failedHandle = work.status === 'failed'
|
|
223
|
+
? readWorkHandle(input.controllerHome, input.repoId, input.workId)
|
|
224
|
+
: undefined;
|
|
225
|
+
const failedReviewedRecovery = work.workKind === 'repository_change'
|
|
226
|
+
&& work.status === 'failed'
|
|
227
|
+
&& failedHandle?.managedWorktree === false
|
|
228
|
+
&& failedHandle.state === 'failed'
|
|
229
|
+
&& failedHandle.finalization.validation === 'failed'
|
|
230
|
+
&& String(failedHandle.finalization.lastError ?? failedHandle.failureReason ?? '').includes('WORK_HANDLE_HEAD_CHANGED');
|
|
231
|
+
if ((isTerminalWorkContractStatus(work.status) && !failedReviewedRecovery) || work.workKind !== 'repository_change') {
|
|
213
232
|
throw new Error(`DIRECT_EDIT_WORK_RECONCILIATION_WORK_NOT_ELIGIBLE: ${input.workId}`);
|
|
214
233
|
}
|
|
215
234
|
if (!work.baseRevision?.trim()) throw new Error('DIRECT_EDIT_WORK_RECONCILIATION_BASE_REVISION_MISSING');
|
|
@@ -226,25 +245,39 @@ export function acceptReviewedDirectEditWorkReconciliation(input: ReviewedDirect
|
|
|
226
245
|
}
|
|
227
246
|
|
|
228
247
|
const comparedPaths = normalizedComparedPaths(input.comparedPaths);
|
|
229
|
-
|
|
248
|
+
let comparisonBaseRevision = baseRevision;
|
|
249
|
+
if (failedReviewedRecovery) {
|
|
250
|
+
const targetParent = exactCommit(input.repoRoot, `${targetRevision}^`, 'TARGET_PARENT_REVISION');
|
|
251
|
+
if (!git(input.repoRoot, ['merge-base', '--is-ancestor', baseRevision, targetParent]).ok) {
|
|
252
|
+
throw new Error('DIRECT_EDIT_WORK_RECONCILIATION_TARGET_PARENT_NOT_DESCENDANT');
|
|
253
|
+
}
|
|
254
|
+
comparisonBaseRevision = targetParent;
|
|
255
|
+
}
|
|
256
|
+
const actualPaths = comparedRevisionPaths(input.repoRoot, comparisonBaseRevision, targetRevision);
|
|
230
257
|
if (comparedPaths.length !== actualPaths.length || comparedPaths.some((path, index) => path !== actualPaths[index])) {
|
|
231
258
|
throw new Error('DIRECT_EDIT_WORK_RECONCILIATION_PATH_COMPARISON_MISMATCH');
|
|
232
259
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
260
|
+
assertWorkPathsWithinScope(work, comparedPaths, {
|
|
261
|
+
forbidden: 'DIRECT_EDIT_WORK_RECONCILIATION_FORBIDDEN_PATH',
|
|
262
|
+
outOfScope: 'DIRECT_EDIT_WORK_RECONCILIATION_PATH_OUT_OF_SCOPE',
|
|
263
|
+
});
|
|
237
264
|
const dirty = git(input.repoRoot, ['status', '--porcelain=v1', '--', ...comparedPaths]);
|
|
238
265
|
if (!dirty.ok || dirty.stdout.trim()) throw new Error('DIRECT_EDIT_WORK_RECONCILIATION_OWNED_PATHS_DIRTY');
|
|
239
266
|
|
|
240
267
|
let verifiedAt = new Date().toISOString();
|
|
241
268
|
for (const checkId of work.checks) {
|
|
242
|
-
const
|
|
269
|
+
const candidates = historicalVerificationEvidenceAtRevision(work.checkRefs, {
|
|
243
270
|
sourceRevision: targetRevision,
|
|
271
|
+
repoId: input.repoId,
|
|
272
|
+
workId: work.workId,
|
|
273
|
+
checkoutId: work.checkoutId,
|
|
244
274
|
checkId,
|
|
245
275
|
requestedChecks: work.checks,
|
|
246
|
-
}).
|
|
247
|
-
|
|
276
|
+
}).filter((entry) => entry.current && entry.record.outcome === 'valid_pass');
|
|
277
|
+
const distinctReceipts = new Map(candidates.map((entry) => [entry.record.receipt!.receiptId, entry]));
|
|
278
|
+
if (distinctReceipts.size === 0) throw new Error(`DIRECT_EDIT_WORK_RECONCILIATION_CHECK_EVIDENCE_STALE: ${checkId}`);
|
|
279
|
+
if (distinctReceipts.size > 1) throw new Error(`DIRECT_EDIT_WORK_RECONCILIATION_CHECK_EVIDENCE_AMBIGUOUS: ${checkId}`);
|
|
280
|
+
const current = [...distinctReceipts.values()][0]!;
|
|
248
281
|
verifiedAt = current.record.completedAt ?? current.record.recordedAt ?? verifiedAt;
|
|
249
282
|
}
|
|
250
283
|
|
|
@@ -294,13 +327,12 @@ export function acceptReviewedDirectEditWorkReconciliation(input: ReviewedDirect
|
|
|
294
327
|
verifiedAt,
|
|
295
328
|
recordedAt,
|
|
296
329
|
};
|
|
297
|
-
|
|
330
|
+
completeWorkWithReceipt(
|
|
298
331
|
{ controllerHome: input.controllerHome, repoId: input.repoId },
|
|
299
332
|
input.workId,
|
|
300
333
|
receipt,
|
|
301
334
|
'completed_changed',
|
|
302
335
|
'repository_change',
|
|
303
336
|
);
|
|
304
|
-
if (recorded.requirementId) completeRequirementFromWork({ controllerHome: input.controllerHome }, { requirementId: recorded.requirementId, work: recorded });
|
|
305
337
|
return { workId: input.workId, reconciliation, receipt };
|
|
306
338
|
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
listControllerChecks,
|
|
10
10
|
} from '../../../cli/controller/check-runner';
|
|
11
11
|
import type { RepositoryRecord } from '../../../cli/repositories/types';
|
|
12
|
+
import { selectRepositoryCheckout } from '../../../cli/repositories/registry';
|
|
12
13
|
import { executionIdentityForRepository } from './execution-identity';
|
|
13
14
|
import {
|
|
14
15
|
claimsForCheck,
|
|
@@ -266,6 +267,57 @@ function failedRun(
|
|
|
266
267
|
return coordinatorResult(controllerHome, failed);
|
|
267
268
|
}
|
|
268
269
|
|
|
270
|
+
export function resolveEditValidationRepository(
|
|
271
|
+
repository: RepositoryRecord,
|
|
272
|
+
editSessionId: string,
|
|
273
|
+
): { repository: RepositoryRecord; session: EditSession } {
|
|
274
|
+
const checkoutIds = [...new Set([
|
|
275
|
+
repository.activeCheckoutId,
|
|
276
|
+
...repository.checkouts.map((checkout) => checkout.checkoutId),
|
|
277
|
+
].filter(Boolean))];
|
|
278
|
+
for (const checkoutId of checkoutIds) {
|
|
279
|
+
let candidate: RepositoryRecord;
|
|
280
|
+
try {
|
|
281
|
+
candidate = selectRepositoryCheckout(repository, checkoutId);
|
|
282
|
+
} catch {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
let session: EditSession;
|
|
286
|
+
try {
|
|
287
|
+
session = getEditSession(candidate.canonicalRoot, editSessionId);
|
|
288
|
+
} catch (error) {
|
|
289
|
+
if (error instanceof Error && error.message === `edit session not found: ${editSessionId}`) continue;
|
|
290
|
+
throw error;
|
|
291
|
+
}
|
|
292
|
+
if (session.repoId && session.repoId !== candidate.repoId) {
|
|
293
|
+
throw new Error(`EDIT_VALIDATION_SESSION_IDENTITY_MISMATCH: repoId ${session.repoId} != ${candidate.repoId}`);
|
|
294
|
+
}
|
|
295
|
+
if (session.checkoutId && session.checkoutId !== candidate.activeCheckoutId) {
|
|
296
|
+
throw new Error(`EDIT_VALIDATION_SESSION_IDENTITY_MISMATCH: checkoutId ${session.checkoutId} != ${candidate.activeCheckoutId}`);
|
|
297
|
+
}
|
|
298
|
+
return { repository: candidate, session };
|
|
299
|
+
}
|
|
300
|
+
throw new Error(`edit session not found: ${editSessionId}`);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function validationRunRepository(
|
|
304
|
+
repository: RepositoryRecord,
|
|
305
|
+
run: EditValidationRunState,
|
|
306
|
+
): RepositoryRecord {
|
|
307
|
+
if (run.repositoryId !== repository.repoId) {
|
|
308
|
+
throw new Error(`EDIT_VALIDATION_REPOSITORY_MISMATCH: ${run.repositoryId} != ${repository.repoId}`);
|
|
309
|
+
}
|
|
310
|
+
const candidate = selectRepositoryCheckout(repository, run.checkoutId);
|
|
311
|
+
const session = getEditSession(candidate.canonicalRoot, run.editSessionId);
|
|
312
|
+
if (session.repoId && session.repoId !== run.repositoryId) {
|
|
313
|
+
throw new Error(`EDIT_VALIDATION_SESSION_IDENTITY_MISMATCH: repoId ${session.repoId} != ${run.repositoryId}`);
|
|
314
|
+
}
|
|
315
|
+
if (session.checkoutId && session.checkoutId !== run.checkoutId) {
|
|
316
|
+
throw new Error(`EDIT_VALIDATION_SESSION_IDENTITY_MISMATCH: checkoutId ${session.checkoutId} != ${run.checkoutId}`);
|
|
317
|
+
}
|
|
318
|
+
return candidate;
|
|
319
|
+
}
|
|
320
|
+
|
|
269
321
|
function receiptFor(
|
|
270
322
|
controllerHome: string,
|
|
271
323
|
repository: RepositoryRecord,
|
|
@@ -312,10 +364,18 @@ export async function reconcileEditValidationRun(
|
|
|
312
364
|
): Promise<EditValidationCoordinatorResult> {
|
|
313
365
|
let run = readRun(controllerHome, repository.repoId, runInput.validationId) ?? runInput;
|
|
314
366
|
if (run.status !== 'running') return coordinatorResult(controllerHome, run);
|
|
315
|
-
|
|
316
|
-
|
|
367
|
+
let validationRepository: RepositoryRecord;
|
|
368
|
+
try {
|
|
369
|
+
validationRepository = validationRunRepository(repository, run);
|
|
370
|
+
} catch (error) {
|
|
371
|
+
return failedRun(
|
|
372
|
+
controllerHome,
|
|
373
|
+
run,
|
|
374
|
+
'EDIT_VALIDATION_CHECKOUT_UNAVAILABLE',
|
|
375
|
+
error instanceof Error ? error.message : String(error),
|
|
376
|
+
);
|
|
317
377
|
}
|
|
318
|
-
const session = getEditSession(
|
|
378
|
+
const session = getEditSession(validationRepository.canonicalRoot, run.editSessionId);
|
|
319
379
|
if (session.currentRevision !== run.editRevision) {
|
|
320
380
|
return failedRun(controllerHome, run, 'EDIT_VALIDATION_STALE_REVISION', `Validation targets revision ${run.editRevision}, current edit revision is ${session.currentRevision}.`);
|
|
321
381
|
}
|
|
@@ -334,10 +394,10 @@ export async function reconcileEditValidationRun(
|
|
|
334
394
|
const checkRequestId = `${run.requestId}:check:${index + 1}:${createHash('sha256').update(checkId).digest('hex').slice(0, 12)}`;
|
|
335
395
|
const facade = await runPersistedCheckViaProcessRuntime({
|
|
336
396
|
controllerHome,
|
|
337
|
-
repoId:
|
|
338
|
-
checkoutId:
|
|
339
|
-
repoRoot:
|
|
340
|
-
executionIdentity: executionIdentityForRepository(
|
|
397
|
+
repoId: validationRepository.repoId,
|
|
398
|
+
checkoutId: validationRepository.activeCheckoutId,
|
|
399
|
+
repoRoot: validationRepository.canonicalRoot,
|
|
400
|
+
executionIdentity: executionIdentityForRepository(validationRepository),
|
|
341
401
|
checkId,
|
|
342
402
|
timeoutMs: run.timeoutMs,
|
|
343
403
|
interactiveWaitMs: 0,
|
|
@@ -384,10 +444,10 @@ export async function reconcileEditValidationRun(
|
|
|
384
444
|
}
|
|
385
445
|
|
|
386
446
|
try {
|
|
387
|
-
const receipts = run.checkIds.map((checkId) => receiptFor(controllerHome,
|
|
388
|
-
const checked = recordEditSessionProcessCheckReceipts(
|
|
389
|
-
repoId:
|
|
390
|
-
checkoutId:
|
|
447
|
+
const receipts = run.checkIds.map((checkId) => receiptFor(controllerHome, validationRepository, run, checkId));
|
|
448
|
+
const checked = recordEditSessionProcessCheckReceipts(validationRepository.canonicalRoot, run.editSessionId, {
|
|
449
|
+
repoId: validationRepository.repoId,
|
|
450
|
+
checkoutId: validationRepository.activeCheckoutId,
|
|
391
451
|
receipts,
|
|
392
452
|
reviewer: run.reviewer,
|
|
393
453
|
note: run.note,
|
|
@@ -415,7 +475,9 @@ export async function startOrJoinEditValidation(
|
|
|
415
475
|
repository: RepositoryRecord,
|
|
416
476
|
input: EditValidationStartInput,
|
|
417
477
|
): Promise<EditValidationCoordinatorResult> {
|
|
418
|
-
const
|
|
478
|
+
const resolved = resolveEditValidationRepository(repository, input.editSessionId);
|
|
479
|
+
const validationRepository = resolved.repository;
|
|
480
|
+
const session = resolved.session;
|
|
419
481
|
const checkIds = normalized(input.checkIds ?? session.requestedChecks);
|
|
420
482
|
const requestId = input.validationRequestId?.trim()
|
|
421
483
|
|| input.requestId?.trim()
|
|
@@ -426,14 +488,14 @@ export async function startOrJoinEditValidation(
|
|
|
426
488
|
if (existing.checkIds.join('\n') !== checkIds.join('\n')) {
|
|
427
489
|
return failedRun(controllerHome, existing, 'EDIT_VALIDATION_REQUEST_CONFLICT', 'The validation request id is already bound to a different check set.');
|
|
428
490
|
}
|
|
429
|
-
return reconcileEditValidationRun(controllerHome,
|
|
491
|
+
return reconcileEditValidationRun(controllerHome, validationRepository, existing);
|
|
430
492
|
}
|
|
431
493
|
|
|
432
494
|
if (checkIds.length === 0) {
|
|
433
495
|
try {
|
|
434
|
-
const checked = recordEditSessionProcessCheckReceipts(
|
|
435
|
-
repoId:
|
|
436
|
-
checkoutId:
|
|
496
|
+
const checked = recordEditSessionProcessCheckReceipts(validationRepository.canonicalRoot, session.sessionId, {
|
|
497
|
+
repoId: validationRepository.repoId,
|
|
498
|
+
checkoutId: validationRepository.activeCheckoutId,
|
|
437
499
|
receipts: [],
|
|
438
500
|
reviewer: input.reviewer,
|
|
439
501
|
note: input.note,
|
|
@@ -441,8 +503,8 @@ export async function startOrJoinEditValidation(
|
|
|
441
503
|
const completed = saveRun(controllerHome, {
|
|
442
504
|
schemaVersion: 1,
|
|
443
505
|
validationId: id,
|
|
444
|
-
repositoryId:
|
|
445
|
-
checkoutId:
|
|
506
|
+
repositoryId: validationRepository.repoId,
|
|
507
|
+
checkoutId: validationRepository.activeCheckoutId,
|
|
446
508
|
editSessionId: session.sessionId,
|
|
447
509
|
editRevision: session.currentRevision,
|
|
448
510
|
requestId,
|
|
@@ -464,8 +526,8 @@ export async function startOrJoinEditValidation(
|
|
|
464
526
|
const run: EditValidationRunState = {
|
|
465
527
|
schemaVersion: 1,
|
|
466
528
|
validationId: id,
|
|
467
|
-
repositoryId:
|
|
468
|
-
checkoutId:
|
|
529
|
+
repositoryId: validationRepository.repoId,
|
|
530
|
+
checkoutId: validationRepository.activeCheckoutId,
|
|
469
531
|
editSessionId: session.sessionId,
|
|
470
532
|
editRevision: session.currentRevision,
|
|
471
533
|
requestId,
|
|
@@ -487,13 +549,13 @@ export async function startOrJoinEditValidation(
|
|
|
487
549
|
const run = saveRun(controllerHome, {
|
|
488
550
|
schemaVersion: 1,
|
|
489
551
|
validationId: id,
|
|
490
|
-
repositoryId:
|
|
491
|
-
checkoutId:
|
|
552
|
+
repositoryId: validationRepository.repoId,
|
|
553
|
+
checkoutId: validationRepository.activeCheckoutId,
|
|
492
554
|
editSessionId: session.sessionId,
|
|
493
555
|
editRevision: session.currentRevision,
|
|
494
556
|
requestId,
|
|
495
557
|
checkIds,
|
|
496
|
-
lanes: checkLanes(
|
|
558
|
+
lanes: checkLanes(validationRepository, checkIds),
|
|
497
559
|
processes: {},
|
|
498
560
|
reviewer: input.reviewer,
|
|
499
561
|
note: input.note,
|
|
@@ -505,7 +567,7 @@ export async function startOrJoinEditValidation(
|
|
|
505
567
|
createdAt: now(),
|
|
506
568
|
updatedAt: now(),
|
|
507
569
|
});
|
|
508
|
-
return reconcileEditValidationRun(controllerHome,
|
|
570
|
+
return reconcileEditValidationRun(controllerHome, validationRepository, run);
|
|
509
571
|
}
|
|
510
572
|
|
|
511
573
|
export interface PendingEditValidationSummary {
|