@moretea-labs/forge 1.8.0 → 1.8.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 +70 -7
- package/CHANGELOG.md +14 -0
- package/CLAUDE.md +17 -2
- package/CONTRIBUTING.md +3 -2
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/adapters/chatgpt/browser-delivery-host.ts +7 -1
- package/adapters/chatgpt/browser-delivery-runtime.ts +529 -56
- package/adapters/chatgpt/controller-binding-store.ts +101 -22
- package/adapters/chatgpt/controller-round-host.ts +15 -25
- package/adapters/chatgpt/controller-round-settlement-store.ts +58 -4
- package/adapters/chatgpt/provider-delivery.ts +228 -1
- package/adapters/chatgpt/work-conversation-binding-store.ts +79 -53
- package/adapters/computer/chatgpt-extension-target.ts +463 -0
- package/adapters/computer/chatgpt-target-router.ts +103 -0
- package/adapters/computer/chrome-extension/background.js +385 -0
- package/adapters/computer/chrome-extension/content.js +378 -0
- package/adapters/computer/chrome-extension/core.js +24 -0
- package/adapters/computer/chrome-extension/manifest.json +30 -0
- package/adapters/computer/desktop-operator-discovery.ts +11 -31
- package/adapters/computer/interaction-target-authority.ts +142 -18
- package/adapters/mcp/auth.ts +0 -1
- package/adapters/mcp/instructions.ts +5 -4
- package/adapters/mcp/multi-repository.ts +3 -19
- package/adapters/mcp/oauth.ts +73 -0
- package/adapters/mcp/paths.ts +4 -1
- package/adapters/mcp/runtime-gateway/automation-receipt-adapter.ts +87 -0
- package/adapters/mcp/runtime-gateway/bounded-result-runtime.ts +62 -0
- package/adapters/mcp/runtime-gateway/cognition-settlement.ts +201 -0
- package/adapters/mcp/runtime-gateway/context-adapter.ts +411 -73
- package/adapters/mcp/runtime-gateway/context-pack-sidecar.ts +91 -0
- package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +20 -5
- package/adapters/mcp/runtime-gateway/core-capability-adapter.ts +258 -0
- package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +10 -27
- package/adapters/mcp/runtime-gateway/engineering-tool-contract.ts +15 -0
- package/adapters/mcp/runtime-gateway/execution-tools.ts +8 -42
- package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +2 -1
- package/adapters/mcp/runtime-gateway/plugin-adapter.ts +117 -45
- package/adapters/mcp/runtime-gateway/process-tools.ts +190 -22
- package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +16 -4
- package/adapters/mcp/runtime-gateway/recovery-adapter.ts +69 -52
- package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +4 -35
- package/adapters/mcp/runtime-gateway/result-adapter.ts +10 -4
- package/adapters/mcp/runtime-gateway/router.ts +18 -504
- package/adapters/mcp/runtime-gateway/routing-policy.ts +499 -0
- package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +14 -65
- package/adapters/mcp/runtime-gateway/runtime-readiness-observation.ts +218 -0
- package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +113 -173
- package/adapters/mcp/runtime-gateway/runtime-tools.ts +15 -5
- package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +1 -0
- package/adapters/mcp/runtime-gateway/shared-adapter.ts +20 -0
- package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +358 -266
- package/adapters/mcp/runtime-gateway/typescript-navigation-sidecar.ts +136 -0
- package/adapters/mcp/runtime-gateway/work-adapter.ts +261 -3122
- package/adapters/mcp/runtime-gateway/work-controller-operations.ts +257 -0
- package/adapters/mcp/runtime-gateway/work-detail-projection.ts +60 -0
- package/adapters/mcp/runtime-gateway/work-input-wire-migration.ts +246 -0
- package/adapters/mcp/runtime-gateway/work-learning-operations.ts +145 -0
- package/adapters/mcp/runtime-gateway/work-list-projection.ts +38 -0
- package/adapters/mcp/runtime-gateway/work-plan-operations.ts +382 -0
- package/adapters/mcp/runtime-gateway/work-repair-adapter.ts +308 -0
- package/adapters/mcp/runtime-gateway/work-requirement-operations.ts +148 -0
- package/adapters/mcp/runtime-gateway/work-semantic-operations.ts +263 -0
- package/adapters/mcp/runtime-gateway/work-workflow-operations.ts +113 -0
- package/adapters/mcp/runtime-gateway/workflow-supervisor-adapter.ts +156 -0
- package/adapters/mcp/server.ts +67 -23
- package/adapters/mcp/setup.ts +1 -3
- package/adapters/mcp/tool-mapping/legacy-tool-service.ts +44 -170
- package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +4 -9
- package/adapters/mcp/tool-mapping/repository-tools.ts +133 -142
- package/adapters/mcp/toolset-names.ts +5 -0
- package/adapters/mcp/toolset.ts +6 -5
- package/adapters/mcp/transports/http-observation.ts +365 -0
- package/adapters/mcp/transports/http.ts +154 -677
- package/adapters/mcp/transports/oauth-http.ts +322 -0
- package/adapters/mcp/transports/session-registry.ts +6 -0
- package/adapters/mcp/tunnels/openai-secure-tunnel.ts +89 -5
- package/assets/plugin-registry.v1.json +6 -4
- package/assets/reference-configs/agentic-development-flow.md +25 -0
- package/assets/reference-configs/global-working-rules.md +19 -0
- package/assets/skill-version.json +2 -2
- package/docs/architecture/CURRENT.md +111 -41
- package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +14 -5
- package/docs/architecture/decisions/20260823-immediate-controller-relay.md +6 -3
- package/docs/architecture/decisions/20260907-assistant-learning-loop.md +2 -0
- package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +19 -4
- package/docs/architecture/decisions/20260916-workflow-supervisor-authority.md +249 -0
- package/docs/architecture/decisions/20260917-cognitive-memory-foundation.md +82 -0
- package/docs/architecture/decisions/20260918-stable-candidate-release-lifecycle.md +221 -0
- package/docs/architecture/decisions/20260919-autonomous-work-liveness.md +62 -0
- package/docs/architecture/decisions/20260924-thin-semantic-working-context.md +72 -0
- package/docs/architecture/decisions/20261001-supervisor-turn-authority.md +237 -0
- package/docs/architecture/decisions/20261005-forge-v3-desktop-client-boundary.md +77 -0
- package/docs/architecture/domains/verification.md +1 -0
- package/docs/architecture/domains/workflow-engine.md +6 -0
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
- package/docs/forge-plugin-management.md +2 -2
- package/docs/operations/controller-performance-and-502.md +17 -0
- package/docs/operations/controller-reliability-runbook.md +2 -2
- package/docs/operations/features.md +1 -1
- package/docs/operations/features.zh-CN.md +1 -1
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/mcp-tool-exposure.md +2 -0
- package/docs/operations/releasing.md +2 -0
- package/docs/operations/standalone-disaster-recovery.md +14 -6
- package/docs/wiki/Implementation.md +1 -1
- package/package.json +25 -14
- package/packages/kernel/cognition/api/index.ts +6 -0
- package/packages/kernel/cognition/application/activation-service.ts +451 -0
- package/packages/kernel/cognition/application/consolidation-service.ts +185 -0
- package/packages/kernel/cognition/application/legacy-experience-adapter.ts +38 -0
- package/packages/kernel/cognition/application/memory-service.ts +64 -0
- package/packages/kernel/cognition/domain/learning.ts +106 -0
- package/packages/kernel/cognition/domain/memory.ts +254 -0
- package/packages/kernel/controller/api/index.ts +1 -0
- package/packages/kernel/controller/application/continuation-service.ts +281 -0
- package/packages/kernel/controller/application/controller-round-service.ts +12 -0
- package/packages/kernel/controller/domain/controller-round-transition-policy.ts +242 -21
- package/packages/kernel/controller/domain/controller-round.ts +4 -1
- package/packages/kernel/controller/domain/execution-quality.ts +3 -1
- package/packages/kernel/controller/infrastructure/controller-round-store.ts +689 -103
- package/packages/kernel/controller/infrastructure/controller-session-store.ts +297 -71
- package/packages/kernel/controller/ports/controller-host.ts +2 -0
- package/packages/kernel/identity/api/index.ts +10 -0
- package/packages/kernel/identity/domain/activity-projection.ts +43 -0
- package/packages/kernel/identity/domain/capability-broker.ts +60 -0
- package/packages/kernel/identity/domain/grant.ts +72 -0
- package/packages/kernel/identity/domain/owned-resource.ts +116 -0
- package/packages/kernel/identity/domain/user-request.ts +93 -0
- package/packages/kernel/identity/infrastructure/activity-projection-store.ts +77 -0
- package/packages/kernel/identity/infrastructure/capability-broker.ts +81 -0
- package/packages/kernel/identity/infrastructure/grant-store.ts +284 -0
- package/packages/kernel/identity/infrastructure/owned-resource-store.ts +259 -0
- package/packages/kernel/identity/infrastructure/user-request-store.ts +182 -0
- package/packages/kernel/scheduler/api/index.ts +0 -2
- package/packages/kernel/scheduler/application/schedule-service.ts +3 -0
- package/packages/kernel/scheduler/application/settlement.ts +0 -15
- package/packages/kernel/scheduler/domain/schedule.ts +87 -0
- package/packages/kernel/scheduler/infrastructure/schedule-store.ts +153 -133
- package/packages/kernel/work/api/index.ts +3 -2
- package/packages/kernel/work/application/work-service.ts +17 -42
- package/packages/kernel/work/domain/current-task-lineage.ts +104 -0
- package/packages/kernel/work/domain/engineering-design.ts +10 -5
- package/packages/kernel/work/domain/engineering-profile.ts +8 -12
- package/packages/kernel/work/domain/evaluation-promotion-receipt.ts +186 -0
- package/packages/kernel/work/domain/execution-concurrency.ts +19 -26
- package/packages/kernel/work/domain/execution-snapshot.ts +0 -31
- package/packages/kernel/work/domain/implementation-review.ts +14 -331
- package/packages/kernel/work/domain/objective-graph.ts +119 -0
- package/packages/kernel/work/domain/project-engineering-contract.ts +23 -1
- package/packages/kernel/work/domain/repository-completion-receipt.ts +1 -1
- package/packages/kernel/work/domain/state-machine.ts +25 -63
- package/packages/kernel/work/domain/types.ts +57 -51
- package/packages/kernel/work/infrastructure/work-contract-store.ts +819 -728
- package/packages/kernel/work/ports/work-contract-store.ts +5 -2
- package/packages/plugin-runtime/browser/session-authority.ts +2 -1
- package/packages/plugin-runtime/computer/chatgpt-identity.ts +24 -0
- package/packages/plugin-runtime/computer/conversation-target.ts +143 -0
- package/packages/plugin-runtime/computer/index.ts +2 -0
- package/packages/plugin-runtime/computer/target-authority.ts +26 -1
- package/packages/plugin-runtime/external/unix-jsonl-transport.ts +142 -18
- package/packages/protocols/computer/contract.ts +27 -1
- package/scripts/activate-source-baseline.ts +7 -9
- package/scripts/canary-controller-semantic-wait.ts +12 -24
- package/scripts/check-mcp-compatibility.ts +48 -136
- package/scripts/check-runtime-architecture.mjs +622 -217
- package/scripts/forge-local-recovery-helper.mjs +93 -15
- package/scripts/lib/project-init-lib.sh +3 -0
- package/scripts/open-source-audit-allowlist.txt +1 -2
- package/scripts/run-bounded-command.mjs +5 -0
- package/scripts/run-governed-gate.ts +0 -1
- package/scripts/run-mobile-monitor-bridge.ts +0 -1
- package/scripts/smoke-control-plane-failure-domain.ts +94 -0
- package/scripts/smoke-mcp-http-runtime.ts +5 -0
- package/scripts/smoke-runtime-recovery.ts +12 -43
- package/scripts/smoke-schedule-engine.ts +23 -15
- package/scripts/smoke-unattended-transport-recovery.ts +223 -0
- package/scripts/smoke-user-request-continuation-restart.ts +232 -0
- package/scripts/smoke-workflow-supervisor-chrome-adapter.ts +92 -0
- package/scripts/smoke-workflow-supervisor-process-restart.ts +276 -0
- package/scripts/smoke-workflow-supervisor-restart-recovery.ts +150 -0
- package/scripts/smoke-workflow-supervisor.ts +103 -0
- package/scripts/verify-forge-runtime.sh +2 -1
- package/src/cli/agent-jobs/executor-health.ts +11 -34
- package/src/cli/agent-jobs/integration.ts +18 -49
- package/src/cli/agent-jobs/job-manager.ts +7 -3
- package/src/cli/commands/brain-assistant.ts +27 -13
- package/src/cli/commands/brain.ts +13 -15
- package/src/cli/commands/chatgpt.ts +1 -163
- package/src/cli/commands/recovery.ts +135 -121
- package/src/cli/commands/runtime.ts +3 -3
- package/src/cli/commands/setup-profile.ts +8 -0
- package/src/cli/controller/check-runner.ts +13 -2
- package/src/cli/controller/completion-backlog.ts +3 -3
- package/src/cli/controller/completion-orchestrator.ts +4 -43
- package/src/cli/controller/completion-target.ts +3 -72
- package/src/cli/controller/context/source-materializer.ts +81 -100
- package/src/cli/controller/context-pack.ts +10 -8
- package/src/cli/controller/execution-completion.ts +4 -7
- package/src/cli/controller/execution-policy.ts +36 -153
- package/src/cli/controller/governance.ts +2 -3
- package/src/cli/controller/issue-store.ts +4 -99
- package/src/cli/controller/launch-agents.ts +59 -18
- package/src/cli/controller/operational-plan.ts +24 -51
- package/src/cli/controller/runtime-config.ts +5 -1
- package/src/cli/controller/stuck-state-migration.ts +2 -3
- package/src/cli/controller/systemd-user.ts +39 -1
- package/src/cli/editing/edit-session.ts +1 -7
- package/src/cli/local-bridge/console-automations.ts +18 -13
- package/src/cli/local-bridge/console-view-models.ts +3 -12
- package/src/cli/local-bridge/facade-api.ts +64 -191
- package/src/cli/local-bridge/server.ts +24 -147
- package/src/cli/local-bridge/types.ts +0 -1
- package/src/cli/mcp/oauth-client-retirement.ts +74 -0
- package/src/cli/repositories/command-executor.ts +29 -23
- package/src/cli/repositories/command-scope.ts +35 -16
- package/src/cli/repositories/controller-home.ts +48 -0
- package/src/cli/repositories/ephemeral-workspace.ts +20 -30
- package/src/cli/repositories/git-command-executor.ts +2 -2
- package/src/cli/repositories/registry.ts +77 -11
- package/src/cli/repositories/structured-git.ts +62 -2
- package/src/cli/repository/session-cache.ts +92 -6
- package/src/runtime/context/assistant-context.ts +20 -7
- package/src/runtime/context/assistant-work-context.ts +89 -27
- package/src/runtime/context/automatic-learning.ts +565 -0
- package/src/runtime/context/cognitive-attention.ts +173 -0
- package/src/runtime/context/cognitive-skill-canary.ts +158 -0
- package/src/runtime/context/context-closure.ts +46 -9
- package/src/runtime/context/context-pack-process.ts +103 -0
- package/src/runtime/context/context-plane.ts +1 -9
- package/src/runtime/context/direct-controller-learning.ts +298 -0
- package/src/runtime/context/semantic-navigation-contract.ts +9 -0
- package/src/runtime/context/semantic-navigation.ts +6 -34
- package/src/runtime/context/typescript-navigation-process.ts +145 -0
- package/src/runtime/context/typescript-navigation.ts +89 -3
- package/src/runtime/control-plane/cleanup-artifact-retention.ts +212 -9
- package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +9 -47
- package/src/runtime/control-plane/execution/controller-authority-recovery.ts +34 -4
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +29 -20
- package/src/runtime/control-plane/execution/execution-identity.ts +23 -3
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +7 -30
- package/src/runtime/control-plane/execution/retained-work-resume.ts +206 -199
- package/src/runtime/control-plane/execution/session-store.ts +31 -1
- package/src/runtime/control-plane/execution/validation.ts +6 -8
- package/src/runtime/control-plane/execution/work-authority-reconciler.ts +13 -42
- package/src/runtime/control-plane/execution/work-check-evidence.ts +137 -0
- package/src/runtime/control-plane/execution/work-completion-authority.ts +30 -85
- package/src/runtime/control-plane/execution/work-delivery-service.ts +497 -0
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +12 -50
- package/src/runtime/control-plane/execution/work-execution-support.ts +50 -149
- package/src/runtime/control-plane/execution/work-handle-authority.ts +188 -109
- package/src/runtime/control-plane/execution/work-handle-store.ts +19 -43
- package/src/runtime/control-plane/execution/work-head-settlement.ts +53 -20
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +52 -29
- package/src/runtime/control-plane/execution/work-operation-service.ts +43 -27
- package/src/runtime/control-plane/execution/work-preparation-service.ts +126 -266
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +2 -3
- package/src/runtime/control-plane/execution/work-revision-diff.ts +39 -0
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +173 -186
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +28 -84
- package/src/runtime/control-plane/execution/work-verification-service.ts +406 -50
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +5 -1
- package/src/runtime/control-plane/facade/capability-registry.ts +362 -37
- package/src/runtime/control-plane/facade/check-normalization.ts +3 -9
- package/src/runtime/control-plane/facade/codex-delegation.ts +10 -11
- package/src/runtime/control-plane/facade/controller-connection.ts +135 -0
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +1 -20
- package/src/runtime/control-plane/facade/goal-workloop.ts +281 -1753
- package/src/runtime/control-plane/facade/handoff-inbox-application.ts +12 -25
- package/src/runtime/control-plane/facade/handoff-inbox-store.ts +256 -52
- package/src/runtime/control-plane/facade/index.ts +0 -1
- package/src/runtime/control-plane/facade/operation-digest.ts +12 -15
- package/src/runtime/control-plane/facade/plan-contract-store.ts +451 -991
- package/src/runtime/control-plane/facade/policy-gate.ts +0 -23
- package/src/runtime/control-plane/facade/repository-work-admission.ts +12 -22
- package/src/runtime/control-plane/facade/requirement-authority.ts +102 -199
- package/src/runtime/control-plane/facade/requirement-board.ts +3 -3
- package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +51 -14
- package/src/runtime/control-plane/facade/self-healing-loop.ts +18 -53
- package/src/runtime/control-plane/facade/suggested-actions.ts +15 -18
- package/src/runtime/control-plane/facade/types.ts +49 -108
- package/src/runtime/control-plane/facade/work-continuation.ts +8 -8
- package/src/runtime/control-plane/generated-cache-retention.ts +97 -1
- package/src/runtime/control-plane/global-scheduler/autonomous-continuation.ts +248 -0
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +1 -1
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +239 -112
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +2 -1
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +66 -40
- package/src/runtime/control-plane/global-scheduler/wake-signal.ts +42 -2
- package/src/runtime/control-plane/governance/access-policy.ts +19 -2
- package/src/runtime/control-plane/governance/authorization.ts +20 -3
- package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +63 -300
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +28 -300
- package/src/runtime/control-plane/launcher/local-conversation-controller.ts +238 -0
- package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +5 -2
- package/src/runtime/control-plane/launcher/thin-launcher.ts +96 -240
- package/src/runtime/control-plane/persistence/cognition-store.ts +820 -0
- package/src/runtime/control-plane/persistence/experience-store.ts +162 -29
- package/src/runtime/control-plane/persistence/requirement-store.ts +216 -74
- package/src/runtime/control-plane/persistence/sqlite-store.ts +113 -6
- package/src/runtime/control-plane/release-retention.ts +135 -47
- package/src/runtime/control-plane/repository-namespace-retention.ts +2 -2
- package/src/runtime/control-plane/runtime-cleanup.ts +290 -21
- package/src/runtime/control-plane/workspace/project-onboarding.ts +80 -0
- package/src/runtime/control-plane/workspace/semantic-sync-service.ts +19 -5
- package/src/runtime/control-plane/workspace/workspace-store.ts +169 -0
- package/src/runtime/diagnostics/incident-repair.ts +313 -146
- package/src/runtime/diagnostics/mcp-timing.ts +114 -0
- package/src/runtime/evidence/artifact-store.ts +158 -40
- package/src/runtime/evidence/evidence-store.ts +112 -8
- package/src/runtime/evidence/output-handle-index.ts +225 -0
- package/src/runtime/evidence/result-store.ts +158 -44
- package/src/runtime/execution/jobs/store.ts +3 -4
- package/src/runtime/execution/managed-workspace.ts +57 -0
- package/src/runtime/execution/process-runtime/check-facade.ts +1 -1
- package/src/runtime/execution/process-runtime/check-result-retention.ts +2 -2
- package/src/runtime/execution/process-runtime/check-result.ts +105 -1
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +18 -8
- package/src/runtime/execution/process-runtime/command-facade.ts +79 -27
- package/src/runtime/execution/process-runtime/gc.ts +15 -2
- package/src/runtime/execution/process-runtime/handle-index.ts +176 -0
- package/src/runtime/execution/process-runtime/host-command-lane.ts +139 -0
- package/src/runtime/execution/process-runtime/index.ts +3 -0
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +251 -72
- package/src/runtime/execution/process-runtime/persisted-check.ts +3 -3
- package/src/runtime/execution/process-runtime/process-runner-entry.ts +47 -5
- package/src/runtime/execution/process-runtime/process-scope.ts +160 -0
- package/src/runtime/execution/process-runtime/resource-claims.ts +65 -61
- package/src/runtime/execution/process-runtime/runtime.ts +35 -2
- package/src/runtime/execution/process-runtime/store.ts +30 -2
- package/src/runtime/execution/process-runtime/types.ts +6 -1
- package/src/runtime/execution/workers/executor.ts +2 -6
- package/src/runtime/platform/service-inventory.ts +130 -0
- package/src/runtime/platform/service-manager.ts +8 -0
- package/src/runtime/plugins/action-application.ts +85 -28
- package/src/runtime/plugins/app-store-connect-adapter.ts +38 -14
- package/src/runtime/plugins/browser-adapter.ts +757 -34
- package/src/runtime/plugins/browser-automation-service.ts +23 -3
- package/src/runtime/plugins/browser-macos-bridge.ts +321 -29
- package/src/runtime/plugins/browser-manifest-surface.ts +45 -11
- package/src/runtime/plugins/browser-native-session-service.ts +63 -13
- package/src/runtime/plugins/browser-registration.ts +16 -0
- package/src/runtime/plugins/browser-runtime.ts +14 -1
- package/src/runtime/plugins/browser-session-authority.ts +2 -2
- package/src/runtime/plugins/browser-session-store.ts +46 -76
- package/src/runtime/plugins/capability-authorization-grants.ts +258 -62
- package/src/runtime/plugins/computer-chatgpt-browser-adapter-installer.ts +219 -0
- package/src/runtime/plugins/computer-chatgpt-macos-target.ts +891 -0
- package/src/runtime/plugins/computer-registration.ts +222 -11
- package/src/runtime/plugins/desktop-operator-external-policy.ts +36 -4
- package/src/runtime/plugins/desktop-operator-registration.ts +12 -4
- package/src/runtime/plugins/external-adapter.ts +42 -6
- package/src/runtime/plugins/external-registration.ts +21 -1
- package/src/runtime/plugins/gmail-adapter.ts +23 -2
- package/src/runtime/plugins/google-calendar-adapter.ts +2 -2
- package/src/runtime/plugins/google-shared.ts +14 -7
- package/src/runtime/plugins/google-tasks-adapter.ts +2 -2
- package/src/runtime/plugins/ios-adapter.ts +1 -1
- package/src/runtime/plugins/lightweight-action.ts +208 -2
- package/src/runtime/plugins/local-system-adapter.ts +12 -2
- package/src/runtime/plugins/managed-process-adapter.ts +12 -5
- package/src/runtime/plugins/plugin-action-sidecar.ts +17 -13
- package/src/runtime/plugins/plugin-management-adapter.ts +1 -1
- package/src/runtime/plugins/store.ts +602 -428
- package/src/runtime/plugins/types.ts +20 -0
- package/src/runtime/plugins/xiaohongshu-publish.ts +16 -0
- package/src/runtime/projections/invalidation.ts +7 -3
- package/src/runtime/projections/materialized-view.ts +80 -36
- package/src/runtime/recovery/actions.ts +3 -3
- package/src/runtime/recovery/capability-action-application.ts +46 -45
- package/src/runtime/recovery/lifecycle-inventory.ts +29 -29
- package/src/runtime/recovery/maintenance-executor.ts +374 -123
- package/src/runtime/recovery/store.ts +29 -11
- package/src/runtime/release/release-coordinator.ts +296 -0
- package/src/runtime/release/release-session.ts +716 -0
- package/src/runtime/release/release-state-migration.ts +25 -0
- package/src/runtime/resources/leases/store.ts +56 -25
- package/src/runtime/root/computer-composition.ts +191 -6
- package/src/runtime/root/controller-progression-composition.ts +192 -0
- package/src/runtime/root/controller-round-composition.ts +20 -1
- package/src/runtime/root/deployment-topology.ts +106 -0
- package/src/runtime/root/entry.ts +4 -3
- package/src/runtime/root/known-good-recovery.ts +184 -0
- package/src/runtime/root/local-bridge.ts +0 -1
- package/src/runtime/root/mcp-transport.ts +111 -18
- package/src/runtime/root/ownership.ts +261 -9
- package/src/runtime/root/package-connector-service.ts +105 -19
- package/src/runtime/root/package-runtime-release.ts +117 -18
- package/src/runtime/root/package-runtime-service.ts +17 -11
- package/src/runtime/root/release-execution-canary.ts +44 -12
- package/src/runtime/root/release-loader.ts +90 -8
- package/src/runtime/root/release-manifest.ts +80 -6
- package/src/runtime/root/release-materialize.ts +372 -28
- package/src/runtime/root/release-store.ts +395 -36
- package/src/runtime/root/runtime-lane.ts +191 -0
- package/src/runtime/root/runtime.ts +159 -27
- package/src/runtime/root/scheduled-controller-composition.ts +28 -3
- package/src/runtime/root/service-runner.ts +2 -2
- package/src/runtime/root/service.ts +36 -33
- package/src/runtime/root/status.ts +48 -0
- package/src/runtime/root/types.ts +32 -3
- package/src/runtime/root/workflow-supervisor-composition.ts +493 -0
- package/src/runtime/root/workflow-supervisor-runtime.ts +209 -0
- package/src/runtime/root/write-fence.ts +50 -2
- package/src/runtime/safe-tooling/google-credential-store.ts +76 -21
- package/src/runtime/safe-tooling/google-oauth-broker.ts +25 -7
- package/src/runtime/safe-tooling/workspace-auth.ts +1 -1
- package/src/runtime/shared/bounded-child-supervisor.ts +9 -1
- package/src/runtime/shared/process-environment.ts +20 -9
- package/src/runtime/shared/response-budget.ts +1 -3
- package/src/runtime/standalone-recovery/controller-home-migration.ts +16 -24
- package/src/runtime/standalone-recovery/core.ts +3702 -342
- package/src/runtime/standalone-recovery/entry.ts +469 -39
- package/src/runtime/standalone-recovery/installer.ts +262 -148
- package/src/runtime/standalone-recovery/mcp-server.ts +29 -193
- package/src/runtime/standalone-recovery/operation-lock.ts +1 -1
- package/src/runtime/standalone-recovery/performance.ts +54 -16
- package/src/runtime/standalone-recovery/release.ts +53 -5
- package/src/runtime/standalone-recovery/service-labels.ts +5 -2
- package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +5 -2
- package/src/runtime/workflow/schedules/browser-probe.ts +17 -6
- package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +10 -3
- package/src/runtime/workflow/schedules/engine.ts +138 -172
- package/src/runtime/workflow/schedules/work-continuation.ts +44 -5
- package/src/runtime/workflows/runtime.ts +15 -19
- package/src/testing/test-governance.ts +5 -1
- package/supervisor/chatgpt-conversation.ts +23 -0
- package/supervisor/client.ts +201 -0
- package/supervisor/control-plane.ts +868 -0
- package/supervisor/entry.ts +15 -0
- package/supervisor/forge-validators.ts +80 -0
- package/supervisor/index.ts +5 -0
- package/supervisor/native-browser-adapter.ts +786 -0
- package/supervisor/native-messaging/host.ts +39 -0
- package/supervisor/native-messaging/manifest.ts +26 -0
- package/supervisor/native-messaging/protocol.ts +27 -0
- package/supervisor/paths.ts +18 -0
- package/supervisor/protocol.ts +202 -0
- package/supervisor/server.ts +404 -0
- package/supervisor/store.ts +1451 -0
- package/supervisor/tsconfig.json +8 -0
- package/supervisor/types.ts +255 -0
- package/adapters/chatgpt/controller-host.ts +0 -119
- package/adapters/mcp/controller-round-compatibility.ts +0 -184
- package/adapters/mcp/frozen-client-semantic-compatibility.ts +0 -292
- package/adapters/mcp/runtime-gateway/work-compat-adapter.ts +0 -502
- package/packages/kernel/progression/api/index.ts +0 -2
- package/packages/kernel/progression/application/projector.ts +0 -211
- package/packages/kernel/progression/domain/types.ts +0 -116
- package/packages/kernel/progression/index.ts +0 -1
- package/packages/kernel/scheduler/application/continuation-service.ts +0 -212
- package/packages/kernel/scheduler/domain/continuation.ts +0 -38
- package/packages/kernel/scheduler/infrastructure/continuation-dispatch-store.ts +0 -38
- package/packages/kernel/work/domain/admission-policy.ts +0 -34
- package/scripts/benchmark-background-check-overlap.ts +0 -228
- package/scripts/benchmark-computer-hotpath.ts +0 -255
- package/scripts/benchmark-controller-hotpath.ts +0 -212
- package/scripts/benchmark-controller-round2.ts +0 -191
- package/scripts/benchmark-ios-agent-device.ts +0 -511
- package/scripts/benchmark-mcp-host.sh +0 -117
- package/scripts/benchmark-mcp-transport.ts +0 -169
- package/scripts/benchmark-operational-memory-activation.ts +0 -235
- package/scripts/benchmark-operational-shadow.ts +0 -28
- package/scripts/benchmark-quality-harness.ts +0 -146
- package/scripts/benchmark-route-session-concurrency.ts +0 -915
- package/scripts/benchmark-thin-harness-gateway-ab.ts +0 -632
- package/scripts/benchmark-thin-harness-mcp-e2e.ts +0 -498
- package/scripts/benchmark-thin-harness.ts +0 -251
- package/scripts/benchmark-typescript-navigation.ts +0 -186
- package/scripts/benchmark-work-efficiency.ts +0 -334
- package/scripts/build-controller-ui.ts +0 -28
- package/scripts/check-npm-release.sh +0 -32
- package/scripts/check-public-release-surface.sh +0 -19
- package/scripts/check-release-published.sh +0 -57
- package/scripts/check-release-readiness.sh +0 -37
- package/scripts/check-release-version.mjs +0 -74
- package/scripts/check-tarball-install-smoke.sh +0 -150
- package/scripts/package-source-archive.sh +0 -147
- package/scripts/public-release-files.txt +0 -39
- package/scripts/public-release.ts +0 -174
- package/scripts/publish-release-tarball.sh +0 -27
- package/scripts/route-nl-vs-ts-eval.ts +0 -679
- package/scripts/stage-runtime-release.ts +0 -44
- package/src/cli/controller/progress.ts +0 -656
- package/src/cli/controller/work-mode.ts +0 -181
- package/src/cli/local-bridge/console-assets.ts +0 -17
- package/src/cli/local-bridge/dashboard.ts +0 -17
- package/src/cli/local-bridge/frontend/src/App.tsx +0 -10
- package/src/cli/local-bridge/frontend/src/components/Button.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/DetailPane.tsx +0 -3
- package/src/cli/local-bridge/frontend/src/components/SearchField.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/SectionHeader.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/Segmented.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/StatusText.tsx +0 -3
- package/src/cli/local-bridge/frontend/src/design/icons.tsx +0 -11
- package/src/cli/local-bridge/frontend/src/design/tokens.css +0 -8
- package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +0 -153
- package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +0 -37
- package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +0 -183
- package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +0 -2
- package/src/cli/local-bridge/frontend/src/features/system/SystemView.tsx +0 -2
- package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +0 -4
- package/src/cli/local-bridge/frontend/src/lib/api.ts +0 -30
- package/src/cli/local-bridge/frontend/src/lib/format.ts +0 -4
- package/src/cli/local-bridge/frontend/src/lib/routes.ts +0 -7
- package/src/cli/local-bridge/frontend/src/main.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/shell/AppShell.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +0 -2
- package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +0 -4
- package/src/cli/local-bridge/frontend/src/styles.css +0 -28
- package/src/cli/local-bridge/frontend/src/types.ts +0 -15
- package/src/cli/local-bridge/frontend/src/vite-env.d.ts +0 -1
- package/src/cli/local-bridge/frontend/vite.config.ts +0 -27
- package/src/cli/local-bridge/ui-dist/app.css +0 -1
- package/src/cli/local-bridge/ui-dist/app.js +0 -11228
- package/src/runtime/control-plane/execution/content-equivalent-commit-authority.ts +0 -169
- package/src/runtime/control-plane/execution/direct-canonical-work-reconciliation.ts +0 -85
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +0 -655
- package/src/runtime/control-plane/execution/work-finalization-service.ts +0 -2850
- package/src/runtime/control-plane/execution/work-path-scope.ts +0 -37
- package/src/runtime/control-plane/execution/work-task-receipt.ts +0 -468
- package/src/runtime/control-plane/facade/controller-round-relay.ts +0 -3
- package/src/runtime/control-plane/facade/work-admission-policy.ts +0 -142
- package/src/runtime/control-plane/facade/work-implementation-review.ts +0 -2
- package/src/runtime/control-plane/facade/work-state-machine.ts +0 -2
- package/src/runtime/control-plane/facade/work-task-projection.ts +0 -66
- package/src/runtime/control-plane/routing/index.ts +0 -1
- package/src/runtime/control-plane/routing/route-policy.ts +0 -401
- package/src/runtime/control-plane/routing/workspace-admission.ts +0 -70
|
@@ -33,11 +33,34 @@ function nearestExistingPath(candidate: string): string | undefined {
|
|
|
33
33
|
return current;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Minimal command execution target. Repository identity stays optional: an
|
|
38
|
+
* arbitrary workspace target carries only its scope key and root, so command
|
|
39
|
+
* policy/audit/cwd checks need no fabricated repository record.
|
|
40
|
+
*/
|
|
41
|
+
export interface RepositoryCommandScopeTarget {
|
|
42
|
+
canonicalRoot: string;
|
|
43
|
+
/** Registered repository id (`RepositoryRecord.repoId`); absent for an arbitrary workspace target. */
|
|
44
|
+
repoId?: string;
|
|
45
|
+
/** Workspace scope key; present only when the target is not a repository. */
|
|
46
|
+
workspaceScopeKey?: string;
|
|
47
|
+
activeCheckoutId?: string;
|
|
48
|
+
defaultBranch?: string;
|
|
49
|
+
enabled?: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Canonical scope key for command policy/audit/approval storage. */
|
|
53
|
+
export function commandExecutionScopeKey(target: RepositoryCommandScopeTarget): string {
|
|
54
|
+
const key = target.repoId?.trim() || target.workspaceScopeKey?.trim();
|
|
55
|
+
if (!key) throw new Error('COMMAND_TARGET_SCOPE_REQUIRED: command target requires a repository id or workspace scope key');
|
|
56
|
+
return key;
|
|
57
|
+
}
|
|
58
|
+
|
|
36
59
|
export function resolveRepositoryCommandCwd(
|
|
37
|
-
repository:
|
|
60
|
+
repository: RepositoryCommandScopeTarget,
|
|
38
61
|
requestedCwd: string | undefined,
|
|
39
62
|
): { root: string; cwd: string; relativeCwd: string } {
|
|
40
|
-
if (
|
|
63
|
+
if (repository.enabled === false) throw new Error(`REPOSITORY_DISABLED: ${commandExecutionScopeKey(repository)}`);
|
|
41
64
|
const root = realpathSync(repository.canonicalRoot);
|
|
42
65
|
const raw = requestedCwd?.trim() || '.';
|
|
43
66
|
if (raw.includes('\0')) throw new Error('COMMAND_SCOPE_DENIED: cwd contains a null byte');
|
|
@@ -258,7 +281,7 @@ interface RepositoryCommandPathPolicyOptions {
|
|
|
258
281
|
}
|
|
259
282
|
|
|
260
283
|
interface SourceControllerRoundCompatibilityScope {
|
|
261
|
-
operation: 'round-
|
|
284
|
+
operation: 'round-close';
|
|
262
285
|
controllerHome: string;
|
|
263
286
|
workId: string;
|
|
264
287
|
controllerAuthorityId: string;
|
|
@@ -274,11 +297,9 @@ function sourceControllerRoundCompatibilityScope(
|
|
|
274
297
|
const args = command.args ?? [];
|
|
275
298
|
if (executable !== 'bun' || args[0] !== 'src/cli/index.ts' || args[1] !== 'chatgpt') return undefined;
|
|
276
299
|
const operation = args[2];
|
|
277
|
-
if (operation !== 'round-
|
|
300
|
+
if (operation !== 'round-close') return undefined;
|
|
278
301
|
|
|
279
|
-
const allowedFlags =
|
|
280
|
-
? new Set(['--controller-home', '--repo-id', '--work-id', '--controller-authority-id', '--relay-scope-id', '--reason', '--timeout-ms'])
|
|
281
|
-
: new Set(['--controller-home', '--repo-id', '--work-id', '--controller-authority-id', '--relay-scope-id', '--disposition', '--handoff-id', '--reason']);
|
|
302
|
+
const allowedFlags = new Set(['--controller-home', '--repo-id', '--work-id', '--controller-authority-id', '--relay-scope-id', '--disposition', '--handoff-id', '--reason']);
|
|
282
303
|
const values = new Map<string, string>();
|
|
283
304
|
for (let index = 3; index < args.length; index += 2) {
|
|
284
305
|
const flag = args[index];
|
|
@@ -300,14 +321,12 @@ function sourceControllerRoundCompatibilityScope(
|
|
|
300
321
|
if (requestedControllerHome !== configuredControllerHome) {
|
|
301
322
|
throw new Error(`SOURCE_CONTROLLER_ROUND_COMMAND_CONTROLLER_HOME_MISMATCH:${requestedControllerHome}`);
|
|
302
323
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
throw new Error('SOURCE_CONTROLLER_ROUND_COMMAND_HANDOFF_REQUIRED');
|
|
310
|
-
}
|
|
324
|
+
const disposition = values.get('--disposition');
|
|
325
|
+
if (!disposition || !['wait', 'wait_for_user', 'goal_complete'].includes(disposition)) {
|
|
326
|
+
throw new Error(`SOURCE_CONTROLLER_ROUND_COMMAND_DISPOSITION_INVALID:${disposition ?? 'missing'}`);
|
|
327
|
+
}
|
|
328
|
+
if (disposition === 'wait_for_user' && !values.get('--handoff-id')?.trim()) {
|
|
329
|
+
throw new Error('SOURCE_CONTROLLER_ROUND_COMMAND_HANDOFF_REQUIRED');
|
|
311
330
|
}
|
|
312
331
|
return {
|
|
313
332
|
operation,
|
|
@@ -328,7 +347,7 @@ function grantCoversPath(grant: ExternalFilesystemGrant, canonicalPath: string,
|
|
|
328
347
|
}
|
|
329
348
|
|
|
330
349
|
function operationForToken(words: string[], index: number, external: string, cwd: string, root: string): RepositoryCommandExternalPathOperation | 'external_write' | 'unsupported' {
|
|
331
|
-
const program = words[0]?.toLowerCase();
|
|
350
|
+
const program = words[0]?.split(/[\\/]/).at(-1)?.toLowerCase();
|
|
332
351
|
if (!program) return 'unsupported';
|
|
333
352
|
if (program === 'cp' || program === 'install') {
|
|
334
353
|
const operands = words.slice(1).filter((word) => word && !word.startsWith('-'));
|
|
@@ -253,7 +253,10 @@ export function ensureRepoPreferredControllerHome(repoRoot?: string, explicit?:
|
|
|
253
253
|
return ensureControllerHome(resolveRepoPreferredControllerHome(repoRoot, explicit));
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
+
/** Legacy adapter-only sentinel. Never use this as a canonical storage or resource scope. */
|
|
256
257
|
export const CONTROLLER_SCOPE_REPO_ID = '__controller__';
|
|
258
|
+
/** Canonical mechanical scope for state owned by the serving ForgeInstance itself. */
|
|
259
|
+
export const FORGE_INSTANCE_SCOPE_KEY = 'forge-instance';
|
|
257
260
|
|
|
258
261
|
/** Human-editable user Workflow Asset content. Machine workflow state stays in control-plane persistence. */
|
|
259
262
|
export function controllerWorkflowContentRoot(controllerHome: string): string {
|
|
@@ -283,6 +286,51 @@ export function repositoryControllerRoot(controllerHome: string, repoId: string)
|
|
|
283
286
|
: join(resolveControllerHome(durableHome), 'repositories', repoId);
|
|
284
287
|
}
|
|
285
288
|
|
|
289
|
+
/** Reserved prefix for arbitrary workspace scopes; registered repository ids are `repo_*`. */
|
|
290
|
+
export const WORKSPACE_SCOPE_PREFIX = 'workspace_';
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Reserved scope key for portable semantic facts (Requirement/Plan/Work) that
|
|
294
|
+
* belong to the ForgeInstance rather than to one repository. Repository and
|
|
295
|
+
* project placement stays optional provenance on those records.
|
|
296
|
+
*/
|
|
297
|
+
export const SEMANTIC_SCOPE_KEY = 'semantic';
|
|
298
|
+
|
|
299
|
+
export function isWorkspaceScopeKey(scopeKey: string): boolean {
|
|
300
|
+
return scopeKey.trim().startsWith(WORKSPACE_SCOPE_PREFIX);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export function isSemanticScopeKey(scopeKey: string): boolean {
|
|
304
|
+
return scopeKey.trim() === SEMANTIC_SCOPE_KEY;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Controller-Home root for an arbitrary workspace scope. A workspace has no
|
|
309
|
+
* repository semantics, so its operation facts own a sibling partition instead
|
|
310
|
+
* of a repository partition.
|
|
311
|
+
*/
|
|
312
|
+
export function workspaceScopeRoot(controllerHome: string, scopeKey: string): string {
|
|
313
|
+
const key = scopeKey.trim();
|
|
314
|
+
if (!isWorkspaceScopeKey(key)) throw new Error(`WORKSPACE_SCOPE_KEY_REQUIRED: ${scopeKey}`);
|
|
315
|
+
return join(resolveControllerHome(durableControllerHome(controllerHome)), 'workspaces', key);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Controller-Home root for one operation scope key. Repository scopes (including
|
|
320
|
+
* the controller-system sentinel) keep their established root; workspace scopes
|
|
321
|
+
* own the workspace partition.
|
|
322
|
+
*/
|
|
323
|
+
export function scopedOperationRoot(controllerHome: string, scopeKey: string): string {
|
|
324
|
+
const key = scopeKey.trim();
|
|
325
|
+
return key === FORGE_INSTANCE_SCOPE_KEY
|
|
326
|
+
? controllerSystemRoot(controllerHome)
|
|
327
|
+
: isWorkspaceScopeKey(key)
|
|
328
|
+
? workspaceScopeRoot(controllerHome, key)
|
|
329
|
+
: isSemanticScopeKey(key)
|
|
330
|
+
? join(resolveControllerHome(durableControllerHome(controllerHome)), SEMANTIC_SCOPE_KEY)
|
|
331
|
+
: repositoryControllerRoot(controllerHome, key);
|
|
332
|
+
}
|
|
333
|
+
|
|
286
334
|
export function ensureRepositoryControllerLayout(controllerHome: string, repoId: string): string {
|
|
287
335
|
const root = repositoryControllerRoot(controllerHome, repoId);
|
|
288
336
|
for (const child of [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'crypto';
|
|
2
2
|
import { existsSync, realpathSync, statSync } from 'fs';
|
|
3
|
-
import { isAbsolute
|
|
4
|
-
import type {
|
|
3
|
+
import { isAbsolute } from 'path';
|
|
4
|
+
import type { RepositoryCommandScopeTarget } from './command-scope';
|
|
5
5
|
|
|
6
6
|
export interface EphemeralWorkspaceCoordinates {
|
|
7
7
|
workspaceId: string;
|
|
@@ -10,7 +10,11 @@ export interface EphemeralWorkspaceCoordinates {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export interface EphemeralWorkspaceTarget extends EphemeralWorkspaceCoordinates {
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Typed command target for an arbitrary workspace: it carries the workspace
|
|
15
|
+
* scope key and root, and no repository identity at all.
|
|
16
|
+
*/
|
|
17
|
+
commandTarget: RepositoryCommandScopeTarget;
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
export function ephemeralWorkspaceCoordinates(rootInput: string): EphemeralWorkspaceCoordinates {
|
|
@@ -29,34 +33,20 @@ export function ephemeralWorkspaceCoordinates(rootInput: string): EphemeralWorks
|
|
|
29
33
|
};
|
|
30
34
|
}
|
|
31
35
|
|
|
32
|
-
|
|
36
|
+
/**
|
|
37
|
+
* `controllerHome` is retained for call-site compatibility; an arbitrary
|
|
38
|
+
* workspace owns no controller-scoped repository record.
|
|
39
|
+
*/
|
|
40
|
+
export function resolveEphemeralWorkspaceTarget(rootInput: string, _controllerHome?: string): EphemeralWorkspaceTarget {
|
|
33
41
|
const coordinates = ephemeralWorkspaceCoordinates(rootInput);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
repoId: coordinates.workspaceId,
|
|
38
|
-
displayName: `Ephemeral Workspace (${coordinates.canonicalRoot.split(/[\\/]/).at(-1) || 'root'})`,
|
|
39
|
-
localRoot: coordinates.canonicalRoot,
|
|
40
|
-
canonicalRoot: coordinates.canonicalRoot,
|
|
41
|
-
activeCheckoutId: coordinates.checkoutId,
|
|
42
|
-
checkouts: [{
|
|
43
|
-
checkoutId: coordinates.checkoutId,
|
|
44
|
-
localRoot: coordinates.canonicalRoot,
|
|
42
|
+
return {
|
|
43
|
+
...coordinates,
|
|
44
|
+
commandTarget: {
|
|
45
45
|
canonicalRoot: coordinates.canonicalRoot,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
lifecycle: 'active',
|
|
52
|
-
}],
|
|
53
|
-
repositoryType: 'unknown',
|
|
54
|
-
enabled: true,
|
|
55
|
-
createdAt: now,
|
|
56
|
-
updatedAt: now,
|
|
57
|
-
lastSeenAt: now,
|
|
58
|
-
configurationPath: join(controllerHome, 'ephemeral-workspaces', `${coordinates.workspaceId}.json`),
|
|
59
|
-
stateStorageStrategy: 'controller-home',
|
|
46
|
+
workspaceScopeKey: coordinates.workspaceId,
|
|
47
|
+
// The workspace's own checkout coordinate, never a repository checkout id.
|
|
48
|
+
activeCheckoutId: coordinates.checkoutId,
|
|
49
|
+
enabled: true,
|
|
50
|
+
},
|
|
60
51
|
};
|
|
61
|
-
return { ...coordinates, repository };
|
|
62
52
|
}
|
|
@@ -3,7 +3,7 @@ import { spawnSync } from 'child_process';
|
|
|
3
3
|
import { appendFileSync, mkdirSync, realpathSync } from 'fs';
|
|
4
4
|
import { dirname, join, relative } from 'path';
|
|
5
5
|
import { capProcessOutput, redactProcessOutput } from '../../effects/process-runner';
|
|
6
|
-
import {
|
|
6
|
+
import { scopedOperationRoot } from './controller-home';
|
|
7
7
|
import { classifyRepositoryCommand, type RepositoryCommandAuthorization, type RepositoryCommandClassification } from './command-classifier';
|
|
8
8
|
import { resolveRepositoryCommandCwd } from './command-scope';
|
|
9
9
|
import type { RepositoryRecord } from './types';
|
|
@@ -209,7 +209,7 @@ function snapshotChanged(before: RepositoryGitSnapshot, after: RepositoryGitSnap
|
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
function auditExecution(controllerHome: string, repository: RepositoryRecord, execution: RepositoryGitExecution): void {
|
|
212
|
-
const path = join(
|
|
212
|
+
const path = join(scopedOperationRoot(controllerHome, repository.repoId), 'audit', 'git-commands.jsonl');
|
|
213
213
|
mkdirSync(dirname(path), { recursive: true });
|
|
214
214
|
appendFileSync(path, `${JSON.stringify({
|
|
215
215
|
timestamp: new Date().toISOString(),
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
realpathSync,
|
|
7
7
|
renameSync,
|
|
8
8
|
rmSync,
|
|
9
|
+
statSync,
|
|
9
10
|
writeFileSync,
|
|
10
11
|
} from 'fs';
|
|
11
12
|
import { dirname, join, relative, resolve } from 'path';
|
|
@@ -291,6 +292,51 @@ function readRegistryFile(path: string, strict: boolean): RepositoryRegistry | u
|
|
|
291
292
|
}
|
|
292
293
|
}
|
|
293
294
|
|
|
295
|
+
interface RepositoryRegistryReadSnapshot {
|
|
296
|
+
fileIdentity: string;
|
|
297
|
+
registry: RepositoryRegistry;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const repositoryRegistryReadSnapshots = new Map<string, RepositoryRegistryReadSnapshot>();
|
|
301
|
+
|
|
302
|
+
function registryFileIdentity(path: string): string | undefined {
|
|
303
|
+
try {
|
|
304
|
+
const stat = statSync(path, { bigint: true });
|
|
305
|
+
return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
|
|
306
|
+
} catch {
|
|
307
|
+
return undefined;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function cloneRepositoryRecord(record: RepositoryRecord): RepositoryRecord {
|
|
312
|
+
return {
|
|
313
|
+
...record,
|
|
314
|
+
checkouts: record.checkouts.map((checkout) => ({ ...checkout })),
|
|
315
|
+
github: record.github
|
|
316
|
+
? { ...record.github, labels: record.github.labels ? [...record.github.labels] : undefined }
|
|
317
|
+
: undefined,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function loadRepositoryRegistryReadSnapshot(controllerHome?: string): RepositoryRegistry {
|
|
322
|
+
const path = registryPath(controllerHome);
|
|
323
|
+
const beforeIdentity = registryFileIdentity(path);
|
|
324
|
+
const cached = beforeIdentity ? repositoryRegistryReadSnapshots.get(path) : undefined;
|
|
325
|
+
if (cached && cached.fileIdentity === beforeIdentity) return cached.registry;
|
|
326
|
+
|
|
327
|
+
let registry = readRegistryFile(path, true) ?? defaultRegistry();
|
|
328
|
+
let afterIdentity = registryFileIdentity(path);
|
|
329
|
+
if (beforeIdentity && afterIdentity && beforeIdentity !== afterIdentity) {
|
|
330
|
+
// A cross-process atomic replacement raced this read. Re-read once from the
|
|
331
|
+
// new physical authority instead of caching a mixed-generation snapshot.
|
|
332
|
+
registry = readRegistryFile(path, true) ?? defaultRegistry();
|
|
333
|
+
afterIdentity = registryFileIdentity(path);
|
|
334
|
+
}
|
|
335
|
+
if (afterIdentity) repositoryRegistryReadSnapshots.set(path, { fileIdentity: afterIdentity, registry });
|
|
336
|
+
else repositoryRegistryReadSnapshots.delete(path);
|
|
337
|
+
return registry;
|
|
338
|
+
}
|
|
339
|
+
|
|
294
340
|
function timestampValue(value: string | undefined): number {
|
|
295
341
|
const parsed = value ? Date.parse(value) : Number.NaN;
|
|
296
342
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
@@ -555,17 +601,18 @@ export function repositorySummary(record: RepositoryRecord): RepositorySummary {
|
|
|
555
601
|
}
|
|
556
602
|
|
|
557
603
|
export function listRepositories(controllerHome?: string, options: { includeRemoved?: boolean } = {}): RepositoryRecord[] {
|
|
558
|
-
return
|
|
604
|
+
return loadRepositoryRegistryReadSnapshot(controllerHome).repositories
|
|
559
605
|
.filter((record) => options.includeRemoved === true || !record.removedAt)
|
|
560
|
-
.sort((a, b) => a.displayName.localeCompare(b.displayName) || a.repoId.localeCompare(b.repoId))
|
|
606
|
+
.sort((a, b) => a.displayName.localeCompare(b.displayName) || a.repoId.localeCompare(b.repoId))
|
|
607
|
+
.map(cloneRepositoryRecord);
|
|
561
608
|
}
|
|
562
609
|
|
|
563
610
|
export function getRepository(repoId: string, controllerHome?: string, options: { includeRemoved?: boolean } = {}): RepositoryRecord {
|
|
564
|
-
const record =
|
|
611
|
+
const record = loadRepositoryRegistryReadSnapshot(controllerHome).repositories.find((candidate) => candidate.repoId === repoId);
|
|
565
612
|
if (!record || (record.removedAt && options.includeRemoved !== true)) {
|
|
566
613
|
throw new Error(`repository not found: ${repoId}`);
|
|
567
614
|
}
|
|
568
|
-
return record;
|
|
615
|
+
return cloneRepositoryRecord(record);
|
|
569
616
|
}
|
|
570
617
|
|
|
571
618
|
export type RepositoryCheckoutSelectionErrorCode = 'CHECKOUT_NOT_FOUND' | 'CHECKOUT_NOT_ACTIVE';
|
|
@@ -635,13 +682,29 @@ export function registerRepository(input: RegisterRepositoryInput): RepositoryRe
|
|
|
635
682
|
? repositoryByGitCommonDirectory(registry.repositories, canonicalRoot)
|
|
636
683
|
: undefined;
|
|
637
684
|
const existingIdentity = existingByRoot ?? existingByCommonDirectory;
|
|
638
|
-
const
|
|
685
|
+
const explicitRepoId = input.repoIdOverride?.trim();
|
|
686
|
+
const derivedRepoId = explicitRepoId
|
|
639
687
|
|| existingIdentity?.repoId
|
|
640
688
|
|| localIdentity.repoId
|
|
641
689
|
|| (canonicalRemote ? stableRemoteRepoId(canonicalRemote) : newLocalRepoId());
|
|
642
|
-
const
|
|
690
|
+
const existingByDerivedId = registry.repositories.find((record) => record.repoId === derivedRepoId);
|
|
691
|
+
// Repository identity is rooted in one Git checkout family, not merely one
|
|
692
|
+
// remote URL. Two independent clones of the same remote have different Git
|
|
693
|
+
// common directories and must remain separate Repository records. The one
|
|
694
|
+
// intentional exception is an explicit registration replacement; legacy
|
|
695
|
+
// repo-local identity may also restore a record whose old canonical root no
|
|
696
|
+
// longer exists after a filesystem move.
|
|
697
|
+
const explicitReplacement = Boolean(explicitRepoId && existingByDerivedId);
|
|
698
|
+
const legacyMovedRoot = Boolean(
|
|
699
|
+
!explicitRepoId
|
|
700
|
+
&& localIdentity.repoId
|
|
701
|
+
&& existingByDerivedId
|
|
702
|
+
&& !existsSync(existingByDerivedId.canonicalRoot),
|
|
703
|
+
);
|
|
704
|
+
const existing = existingIdentity ?? (explicitReplacement || legacyMovedRoot ? existingByDerivedId : undefined);
|
|
705
|
+
const repoId = existing?.repoId
|
|
706
|
+
?? (existingByDerivedId && !existingIdentity ? newLocalRepoId() : derivedRepoId);
|
|
643
707
|
const checkoutId = stableCheckoutId(repoId, canonicalRoot);
|
|
644
|
-
const existing = existingIdentity ?? registry.repositories.find((record) => record.repoId === repoId);
|
|
645
708
|
const checkout: RepositoryCheckout = {
|
|
646
709
|
checkoutId,
|
|
647
710
|
localRoot: canonicalRoot,
|
|
@@ -659,8 +722,8 @@ export function registerRepository(input: RegisterRepositoryInput): RepositoryRe
|
|
|
659
722
|
// A repository-scoped command may start from a managed checkout (for example
|
|
660
723
|
// immutable release construction). Registering that path must add/update the
|
|
661
724
|
// checkout only; it must never promote the ephemeral worktree to repository
|
|
662
|
-
// canonical authority.
|
|
663
|
-
//
|
|
725
|
+
// canonical authority. Active checkout selection is separate authority held
|
|
726
|
+
// by activeCheckoutId; canonicalRoot remains the repository registration root.
|
|
664
727
|
if (existing && worktree && existing.canonicalRemote && canonicalRemote !== existing.canonicalRemote) {
|
|
665
728
|
throw new Error(`CHECKOUT_REPOSITORY_MISMATCH: ${canonicalRoot}`);
|
|
666
729
|
}
|
|
@@ -776,6 +839,9 @@ export function addRepositoryCheckout(input: AddRepositoryCheckoutInput): Reposi
|
|
|
776
839
|
if (current.canonicalRemote && canonicalRemote !== current.canonicalRemote) {
|
|
777
840
|
throw new Error(`CHECKOUT_REPOSITORY_MISMATCH: ${canonicalRoot}`);
|
|
778
841
|
}
|
|
842
|
+
if (!repositoryCheckoutRootMatches(current, canonicalRoot)) {
|
|
843
|
+
throw new Error(`CHECKOUT_REPOSITORY_MISMATCH: ${canonicalRoot}`);
|
|
844
|
+
}
|
|
779
845
|
const timestamp = now();
|
|
780
846
|
const checkoutId = stableCheckoutId(current.repoId, canonicalRoot);
|
|
781
847
|
const checkout: RepositoryCheckout = {
|
|
@@ -792,14 +858,14 @@ export function addRepositoryCheckout(input: AddRepositoryCheckoutInput): Reposi
|
|
|
792
858
|
const activate = input.activate === true;
|
|
793
859
|
const next: RepositoryRecord = {
|
|
794
860
|
...current,
|
|
795
|
-
...(activate ? {
|
|
861
|
+
...(activate ? { activeCheckoutId: checkoutId } : {}),
|
|
796
862
|
checkouts: [...current.checkouts.filter((value) => value.checkoutId !== checkoutId), checkout],
|
|
797
863
|
updatedAt: timestamp,
|
|
798
864
|
lastSeenAt: timestamp,
|
|
799
865
|
};
|
|
800
866
|
registry.repositories[index] = next;
|
|
801
867
|
saveRepositoryRegistry(registry, home);
|
|
802
|
-
return next;
|
|
868
|
+
return activate ? selectRepositoryCheckout(next, checkoutId) : next;
|
|
803
869
|
}
|
|
804
870
|
|
|
805
871
|
export function setRepositoryCheckoutLifecycle(input: SetRepositoryCheckoutLifecycleInput): RepositoryRecord {
|
|
@@ -98,6 +98,16 @@ function gitText(repository: RepositoryRecord, args: string[]): string | null {
|
|
|
98
98
|
return result.ok && result.stdout.trim() ? result.stdout.trim() : null;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
function stashSelectorForOid(repository: RepositoryRecord, oid: string): string | null {
|
|
102
|
+
const result = runGit(repository, ['stash', 'list', '--format=%H%x00%gd'], 64 * 1024);
|
|
103
|
+
if (!result.ok) return null;
|
|
104
|
+
for (const line of result.stdout.split(/\r?\n/)) {
|
|
105
|
+
const [candidateOid, selector] = line.split('\0');
|
|
106
|
+
if (candidateOid === oid && selector) return selector;
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
101
111
|
function assertSafeBranchName(raw: unknown): string {
|
|
102
112
|
try {
|
|
103
113
|
return validateBranchName(raw, { purpose: 'GIT_BRANCH' });
|
|
@@ -414,6 +424,7 @@ export function repositoryGitFinishWorkflow(controllerHome: string, repository:
|
|
|
414
424
|
const requestedPreservedDirtyPaths = normalizePaths(input.preserveDirtyTargetPaths);
|
|
415
425
|
const currentDirtyPaths = [...new Set([...before.staged, ...before.unstaged, ...before.untracked])].sort();
|
|
416
426
|
const preserveDirtyTargetInPlace = alreadyOnTarget && requestedPreservedDirtyPaths.length > 0;
|
|
427
|
+
let equivalentOverlappingDirtyPaths: string[] = [];
|
|
417
428
|
if (preserveDirtyTargetInPlace) {
|
|
418
429
|
const requested = new Set(requestedPreservedDirtyPaths);
|
|
419
430
|
if (requested.size !== currentDirtyPaths.length || currentDirtyPaths.some((path) => !requested.has(path))) {
|
|
@@ -426,7 +437,19 @@ export function repositoryGitFinishWorkflow(controllerHome: string, repository:
|
|
|
426
437
|
const candidatePaths = new Set(candidateDiff.stdout.split(/\r?\n/).map((path) => path.trim()).filter(Boolean));
|
|
427
438
|
const overlapping = currentDirtyPaths.filter((path) => candidatePaths.has(path));
|
|
428
439
|
if (overlapping.length > 0) {
|
|
429
|
-
|
|
440
|
+
const staged = new Set(before.staged);
|
|
441
|
+
const unstaged = new Set(before.unstaged);
|
|
442
|
+
const untracked = new Set(before.untracked);
|
|
443
|
+
const equivalent = overlapping.every((path) => {
|
|
444
|
+
if (untracked.has(path) || (!staged.has(path) && !unstaged.has(path))) return false;
|
|
445
|
+
if (staged.has(path) && !runGit(repository, ['diff', '--cached', '--quiet', featureBranch, '--', path], 8 * 1024).ok) return false;
|
|
446
|
+
if (unstaged.has(path) && !runGit(repository, ['diff', '--quiet', featureBranch, '--', path], 8 * 1024).ok) return false;
|
|
447
|
+
return true;
|
|
448
|
+
});
|
|
449
|
+
if (!equivalent) {
|
|
450
|
+
return { repoId: repository.repoId, checkoutId: repository.activeCheckoutId, featureBranch, targetBranch, before, steps, after: before, completed: false, error: { code: 'GIT_DIRTY_TARGET_PATH_CONFLICT', message: `Work-owned dirty target path(s) overlap the integration candidate: ${overlapping.join(', ')}` } };
|
|
451
|
+
}
|
|
452
|
+
equivalentOverlappingDirtyPaths = overlapping;
|
|
430
453
|
}
|
|
431
454
|
}
|
|
432
455
|
if (!before.clean && !alreadyOnTarget) {
|
|
@@ -442,6 +465,30 @@ export function repositoryGitFinishWorkflow(controllerHome: string, repository:
|
|
|
442
465
|
}
|
|
443
466
|
const protectTrackedChanges = alreadyOnTarget && !preserveDirtyTargetInPlace && (before.staged.length > 0 || before.unstaged.length > 0);
|
|
444
467
|
const finishAuthorization = 'explicit_user_request' as const;
|
|
468
|
+
let equivalentOverlapStashRef: string | null = null;
|
|
469
|
+
if (equivalentOverlappingDirtyPaths.length > 0) {
|
|
470
|
+
const stash = executeRepositoryGitCommand(controllerHome, repository, { args: ['stash', 'push', '-m', `forge-finish-equivalent:${input.workId ?? featureBranch}`, '--', ...equivalentOverlappingDirtyPaths], authorization: finishAuthorization, ...input });
|
|
471
|
+
steps.push({ name: 'stash_equivalent_target_overlap', execution: stash });
|
|
472
|
+
if (stash.status !== 'executed' || stash.ok !== true) {
|
|
473
|
+
return { repoId: repository.repoId, checkoutId: repository.activeCheckoutId, featureBranch, targetBranch, before, steps, after: repositoryGitStatus(repository), completed: false, error: { code: 'GIT_EQUIVALENT_TARGET_STASH_FAILED', message: stash.stderr || 'Unable to preserve candidate-equivalent dirty target paths before integration.' } };
|
|
474
|
+
}
|
|
475
|
+
equivalentOverlapStashRef = gitText(repository, ['rev-parse', 'refs/stash']);
|
|
476
|
+
if (!equivalentOverlapStashRef) {
|
|
477
|
+
return { repoId: repository.repoId, checkoutId: repository.activeCheckoutId, featureBranch, targetBranch, before, steps, after: repositoryGitStatus(repository), completed: false, error: { code: 'GIT_EQUIVALENT_TARGET_STASH_REFERENCE_MISSING', message: 'Candidate-equivalent target paths were stashed but the recovery stash reference could not be resolved.' } };
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
const restoreEquivalentOverlap = (name: string): RepositoryGitExecution | undefined => {
|
|
481
|
+
if (!equivalentOverlapStashRef) return undefined;
|
|
482
|
+
const execution = executeRepositoryGitCommand(controllerHome, repository, { args: ['stash', 'apply', '--index', equivalentOverlapStashRef], authorization: finishAuthorization, ...input });
|
|
483
|
+
steps.push({ name, execution });
|
|
484
|
+
return execution;
|
|
485
|
+
};
|
|
486
|
+
const dropEquivalentOverlapStash = (): void => {
|
|
487
|
+
if (!equivalentOverlapStashRef) return;
|
|
488
|
+
const selector = stashSelectorForOid(repository, equivalentOverlapStashRef) ?? equivalentOverlapStashRef;
|
|
489
|
+
const execution = executeRepositoryGitCommand(controllerHome, repository, { args: ['stash', 'drop', selector], authorization: finishAuthorization, ...input });
|
|
490
|
+
steps.push({ name: 'drop_equivalent_target_stash', execution });
|
|
491
|
+
};
|
|
445
492
|
let stashRef: string | null = null;
|
|
446
493
|
if (protectTrackedChanges) {
|
|
447
494
|
const stash = executeRepositoryGitCommand(controllerHome, repository, { args: ['stash', 'push', '-m', `forge-finish:${input.workId ?? featureBranch}`], authorization: finishAuthorization, ...input });
|
|
@@ -468,17 +515,30 @@ export function repositoryGitFinishWorkflow(controllerHome: string, repository:
|
|
|
468
515
|
};
|
|
469
516
|
const dropTrackedStash = (): void => {
|
|
470
517
|
if (!stashRef) return;
|
|
471
|
-
const
|
|
518
|
+
const selector = stashSelectorForOid(repository, stashRef) ?? stashRef;
|
|
519
|
+
const execution = executeRepositoryGitCommand(controllerHome, repository, { args: ['stash', 'drop', selector], authorization: finishAuthorization, ...input });
|
|
472
520
|
steps.push({ name: 'drop_target_stash', execution });
|
|
473
521
|
};
|
|
474
522
|
const merge = executeRepositoryGitCommand(controllerHome, repository, { args: ['merge', ...(input.noFf === true ? ['--no-ff'] : ['--ff-only']), featureBranch], authorization: finishAuthorization, ...input });
|
|
475
523
|
steps.push({ name: 'merge_feature', execution: merge });
|
|
476
524
|
if (merge.status !== 'executed' || merge.ok !== true) {
|
|
525
|
+
const restoreEquivalent = restoreEquivalentOverlap('restore_equivalent_target_overlap_after_merge_failure');
|
|
526
|
+
if (restoreEquivalent && (restoreEquivalent.status !== 'executed' || restoreEquivalent.ok !== true)) return { repoId: repository.repoId, checkoutId: repository.activeCheckoutId, featureBranch, targetBranch, before, steps, after: repositoryGitStatus(repository), completed: false, error: { code: 'GIT_EQUIVALENT_TARGET_RESTORE_FAILED', message: restoreEquivalent.stderr || 'Candidate-equivalent target paths could not be restored after merge failure; recovery stash was retained.' } };
|
|
527
|
+
dropEquivalentOverlapStash();
|
|
477
528
|
const restore = applyTrackedStash('restore_target_changes_after_merge_failure');
|
|
478
529
|
if (restore && (restore.status !== 'executed' || restore.ok !== true)) return { repoId: repository.repoId, checkoutId: repository.activeCheckoutId, featureBranch, targetBranch, before, steps, after: repositoryGitStatus(repository), completed: false, error: { code: 'GIT_LOCAL_CHANGES_RESTORE_FAILED', message: restore.stderr || 'git stash apply failed while restoring the pre-merge target state' } };
|
|
479
530
|
dropTrackedStash();
|
|
480
531
|
return { repoId: repository.repoId, checkoutId: repository.activeCheckoutId, featureBranch, targetBranch, before, steps, after: repositoryGitStatus(repository), completed: false, error: { code: 'GIT_MERGE_FAILED', message: merge.stderr || 'git merge failed' } };
|
|
481
532
|
}
|
|
533
|
+
if (equivalentOverlappingDirtyPaths.length > 0) {
|
|
534
|
+
const afterMerge = repositoryGitStatus(repository);
|
|
535
|
+
const remainingDirty = new Set([...afterMerge.staged, ...afterMerge.unstaged, ...afterMerge.untracked]);
|
|
536
|
+
const contentMatchesCandidate = runGit(repository, ['diff', '--quiet', featureBranch, '--', ...equivalentOverlappingDirtyPaths], 32 * 1024).ok;
|
|
537
|
+
if (!contentMatchesCandidate || equivalentOverlappingDirtyPaths.some((path) => remainingDirty.has(path))) {
|
|
538
|
+
return { repoId: repository.repoId, checkoutId: repository.activeCheckoutId, featureBranch, targetBranch, before, steps, after: afterMerge, completed: false, error: { code: 'GIT_EQUIVALENT_TARGET_POSTCONDITION_FAILED', message: 'Integrated target did not retain the exact candidate content for a reconciled dirty overlap; recovery stash was retained.' } };
|
|
539
|
+
}
|
|
540
|
+
dropEquivalentOverlapStash();
|
|
541
|
+
}
|
|
482
542
|
const reapply = applyTrackedStash('reapply_target_changes');
|
|
483
543
|
if (reapply && (reapply.status !== 'executed' || reapply.ok !== true)) return { repoId: repository.repoId, checkoutId: repository.activeCheckoutId, featureBranch, targetBranch, before, steps, after: repositoryGitStatus(repository), completed: false, error: { code: 'GIT_LOCAL_CHANGES_RESTORE_FAILED', message: reapply.stderr || 'Tracked target changes passed merge-tree preflight but could not be reapplied after merge; recovery stash was retained.' } };
|
|
484
544
|
dropTrackedStash();
|