specrails-desktop 2.55.0 → 2.55.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/cli/dist/args.js +80 -0
- package/cli/dist/desktop-client.js +125 -0
- package/cli/dist/desktop-control.js +339 -0
- package/cli/dist/format.js +45 -0
- package/cli/dist/help.js +68 -0
- package/cli/dist/output.js +47 -0
- package/cli/dist/run-command.js +340 -0
- package/cli/dist/specrails-desktop.js +41 -1107
- package/cli/dist/status.js +111 -0
- package/client/dist/assets/{ActivityFeedPage-ILXMZXcb.js → ActivityFeedPage-DmJcQpNe.js} +1 -1
- package/client/dist/assets/{AgentBrowserCapture-UcsupCy7.js → AgentBrowserCapture-NxBHrU-V.js} +1 -1
- package/client/dist/assets/{AgentModeAnalyticsPane-CQuxYh8B.js → AgentModeAnalyticsPane-CGAKTJfU.js} +2 -2
- package/client/dist/assets/{AgentModeCodePane-zPWbW7ei.js → AgentModeCodePane-DNSs5_oe.js} +2 -2
- package/client/dist/assets/{AgentModeJobsPane-CInByIdl.js → AgentModeJobsPane-DLoVYDnG.js} +1 -1
- package/client/dist/assets/{AgentsPage-BWvgxaBv.js → AgentsPage-CAzMn9Gg.js} +1 -1
- package/client/dist/assets/{AnalyticsPage-DP3jakv-.js → AnalyticsPage-C06KT4Y0.js} +1 -1
- package/client/dist/assets/{CodePage-BKXiwE5a.js → CodePage-aX1mxGyT.js} +1 -1
- package/client/dist/assets/{DesktopAnalyticsPage-DryJOaMz.js → DesktopAnalyticsPage-BtscfiQD.js} +1 -1
- package/client/dist/assets/{DocsDialog-BsCG9ksG.js → DocsDialog-CNOcXhp-.js} +1 -1
- package/client/dist/assets/{DocsPage-Cc-GeKYl.js → DocsPage-C2BugxaI.js} +1 -1
- package/client/dist/assets/{ExportDropdown-ClahJdDn.js → ExportDropdown-BVGKEgkd.js} +1 -1
- package/client/dist/assets/{InteractiveJobComposer-DfLF6N0f.js → InteractiveJobComposer-CCncBqNu.js} +1 -1
- package/client/dist/assets/{JobDetailModal-DHeur9YK.js → JobDetailModal-Cn_k3-RM.js} +1 -1
- package/client/dist/assets/{JobDetailPage-CraDKzeG.js → JobDetailPage-BogJFBsk.js} +1 -1
- package/client/dist/assets/{JobsPage-CcbuSwj7.js → JobsPage-DL4E8ZyU.js} +1 -1
- package/client/dist/assets/{LoopBuilderPage-BKtNp494.js → LoopBuilderPage-9L6URQLY.js} +1 -1
- package/client/dist/assets/{LoopPreviewModal-DbZd9isy.js → LoopPreviewModal-CAPqTIgL.js} +1 -1
- package/client/dist/assets/{LoopsPage-DEg6sIkG.js → LoopsPage-aU8dYw7L.js} +1 -1
- package/client/dist/assets/{MinimizedChatsContext-BGtEjWsc.js → MinimizedChatsContext-D3QzgV0B.js} +1 -1
- package/client/dist/assets/{PluginsPage-BVqSsPzL.js → PluginsPage-D_tsW9B5.js} +1 -1
- package/client/dist/assets/{ProjectSettingsDialog-CAjLgJAZ.js → ProjectSettingsDialog-_ZbNuspP.js} +1 -1
- package/client/dist/assets/{RepositoryDeliveries-DrcrxLe0.js → RepositoryDeliveries-Du2UCf1a.js} +1 -1
- package/client/dist/assets/{RepositoryScopeSelector-CAGY3xW6.js → RepositoryScopeSelector-hPGZNRBm.js} +1 -1
- package/client/dist/assets/ReviewPacketPage-CG-Dv0El.js +4 -0
- package/client/dist/assets/{TemplatePreviewModal-Bqs-9fhn.js → TemplatePreviewModal-C19H2b2o.js} +1 -1
- package/client/dist/assets/{TicketDetailModalContext-C0jQ5yOk.js → TicketDetailModalContext-DAK-NoK6.js} +1 -1
- package/client/dist/assets/{Trans-DtWlCJN9.js → Trans-DEbgkuAY.js} +1 -1
- package/client/dist/assets/{dashboard-CTfEtkqX.js → dashboard-B9uWNGO2.js} +1 -1
- package/client/dist/assets/{dashboard-DMklepmz.js → dashboard-BIwmO9wc.js} +1 -1
- package/client/dist/assets/{dashboard-DBAP2_SQ.js → dashboard-BV4vOZkR.js} +1 -1
- package/client/dist/assets/{dashboard-D40HP2ks.js → dashboard-Bf6MPVsM.js} +1 -1
- package/client/dist/assets/{dashboard-Csf1yGSN.js → dashboard-C8klWVvZ.js} +1 -1
- package/client/dist/assets/{dashboard-C29Qwa-L.js → dashboard-D1ORhPF0.js} +1 -1
- package/client/dist/assets/{dashboard-Cr4D8oMn.js → dashboard-DSubvs9x.js} +1 -1
- package/client/dist/assets/{dashboard-CSJzf5OT.js → dashboard-px_vlRx_.js} +1 -1
- package/client/dist/assets/{formatDistanceToNow-DYmvOA3R.js → formatDistanceToNow-Cc2u5FwQ.js} +1 -1
- package/client/dist/assets/{getTimezoneOffsetInMilliseconds-BAhW5E6V.js → getTimezoneOffsetInMilliseconds-BHm2U-w1.js} +1 -1
- package/client/dist/assets/{index-BfAZg5DG.js → index-BcligJ3-.js} +29 -29
- package/client/dist/assets/index-C51x61Bz.css +2 -0
- package/client/dist/assets/{jira-api-B27Za5FP.js → jira-api-DrGxiD9p.js} +1 -1
- package/client/dist/assets/packet-8nn9RsU7.js +1 -0
- package/client/dist/assets/packet-B0H15elv.js +1 -0
- package/client/dist/assets/packet-BQvBgrwh.js +1 -0
- package/client/dist/assets/packet-BR7mO0-a.js +1 -0
- package/client/dist/assets/packet-BgcQ6q_K.js +1 -0
- package/client/dist/assets/packet-D2s92Jnn.js +1 -0
- package/client/dist/assets/packet-YbtcD1HS.js +1 -0
- package/client/dist/assets/packet-c0Qj_9eX.js +1 -0
- package/client/dist/assets/{project-repositories-DixIZHTA.js → project-repositories-9Wzs0coQ.js} +1 -1
- package/client/dist/assets/{spending-BGmZBX5B.js → spending-T5GM7wpn.js} +1 -1
- package/client/dist/assets/{useDesktop-CPB4cnwI.js → useDesktop-QogiN0zR.js} +1 -1
- package/client/dist/assets/{useRuntimeRuns-DEQ9HdAM.js → useRuntimeRuns-Dqmz9fS9.js} +1 -1
- package/client/dist/assets/{useSharedWebSocket-DcPRKR8R.js → useSharedWebSocket-Dz-HI6De.js} +2 -2
- package/client/dist/index.html +17 -17
- package/docs/codex.md +5 -5
- package/docs/customizing.md +1 -1
- package/docs/gemini-cli-provider-study.md +14 -14
- package/docs/gemini-core-support-evaluation.md +1 -1
- package/docs/gemini.md +5 -5
- package/docs/internals/README.md +6 -2
- package/docs/internals/adding-a-provider.md +15 -15
- package/docs/internals/agent-runtime-framework-evaluation.md +8 -8
- package/docs/internals/api-reference.md +2 -2
- package/docs/internals/architecture.md +9 -9
- package/docs/internals/browser-capture-performance.md +3 -3
- package/docs/internals/code-explorer-story.md +2 -2
- package/docs/internals/companion-rails-as-loops-contract.md +2 -2
- package/docs/internals/embedded-browser-native-webview-evaluation.md +5 -5
- package/docs/internals/gemini-mcp-registration.md +2 -2
- package/docs/internals/global-artifacts-alignment-contract.md +1 -1
- package/docs/internals/global-artifacts-relocation-evaluation.md +1 -1
- package/docs/internals/interactive-jobs.md +8 -8
- package/docs/internals/legacy-implementation-notes.md +722 -0
- package/docs/internals/loop-step-log-explorer.md +8 -8
- package/docs/internals/mission-rail-cards.md +14 -14
- package/docs/internals/modular-architecture.md +107 -0
- package/docs/internals/profiles.md +1 -1
- package/docs/internals/project-builder.md +18 -18
- package/docs/internals/review-packet.md +8 -8
- package/docs/internals/safe-pr-review-flow.md +11 -11
- package/docs/internals/source-architecture.md +131 -0
- package/docs/internals/source-map.md +1354 -0
- package/docs/internals/spec-addenda.md +65 -9
- package/docs/internals/switch-to-agent-mode-design.md +11 -11
- package/docs/tracking-cost.md +1 -1
- package/package.json +5 -2
- package/server/dist/agent-mcp-config.js +1 -1
- package/server/dist/attachment-manager.js +1 -1
- package/server/dist/build-dirs.js +2 -2
- package/server/dist/config.js +0 -3
- package/server/dist/core-completion.js +1 -1
- package/server/dist/core-execution.js +1 -1
- package/server/dist/db/activity.js +39 -0
- package/server/dist/db/connection.js +41 -0
- package/server/dist/db/conversations.js +48 -0
- package/server/dist/db/jobs.js +251 -0
- package/server/dist/db/migrations.js +1521 -0
- package/server/dist/db/proposals.js +50 -0
- package/server/dist/db/settings.js +15 -0
- package/server/dist/db/stats.js +61 -0
- package/server/dist/db/telemetry.js +56 -0
- package/server/dist/db/templates.js +37 -0
- package/server/dist/db/types.js +2 -0
- package/server/dist/db.js +66 -2224
- package/server/dist/desktop-db.js +3 -3
- package/server/dist/desktop-router.js +8 -8
- package/server/dist/git-diagnostics.js +1 -1
- package/server/dist/index.js +11 -11
- package/server/dist/integration-branch.js +3 -14
- package/server/dist/jira/jira-issue-fields.js +0 -1
- package/server/dist/jira/jira-materializer.js +1 -1
- package/server/dist/jira/jira-sync-manager.js +1 -1
- package/server/dist/mcp/mcp-admin-router.js +1 -1
- package/server/dist/mcp/mcp-server.js +1 -1
- package/server/dist/mcp/tools/jobs.js +1 -1
- package/server/dist/mcp/tools/rails.js +2 -2
- package/server/dist/mcp/tools/specs.js +2 -2
- package/server/dist/mcp/tools/types.js +3 -3
- package/server/dist/mobile/mobile-gateway.js +1 -1
- package/server/dist/mobile/mobile-missions.js +1 -1
- package/server/dist/{desktop-analytics.js → modules/accounting/runtime/desktop-analytics.js} +2 -2
- package/server/dist/{pricing.js → modules/accounting/runtime/pricing.js} +1 -1
- package/server/dist/{result-event.js → modules/accounting/runtime/result-event.js} +3 -3
- package/server/dist/{spending.js → modules/accounting/runtime/spending.js} +0 -3
- package/server/dist/{telemetry-compactor.js → modules/accounting/runtime/telemetry-compactor.js} +1 -1
- package/server/dist/{telemetry-receiver.js → modules/accounting/runtime/telemetry-receiver.js} +2 -2
- package/server/dist/{agent-runtime-bridge.js → modules/agent-runtime/runtime/agent-runtime-bridge.js} +2 -2
- package/server/dist/{agent-runtime-controls.js → modules/agent-runtime/runtime/agent-runtime-controls.js} +7 -7
- package/server/dist/{agent-runtime-effective-config.js → modules/agent-runtime/runtime/agent-runtime-effective-config.js} +1 -1
- package/server/dist/{agent-runtime-loader.js → modules/agent-runtime/runtime/agent-runtime-loader.js} +3 -3
- package/server/dist/{agent-runtime-paths.js → modules/agent-runtime/runtime/agent-runtime-paths.js} +1 -1
- package/server/dist/{agent-runtime-settings-router.js → modules/agent-runtime/runtime/agent-runtime-settings-router.js} +2 -2
- package/server/dist/{agent-runtime-settings.js → modules/agent-runtime/runtime/agent-runtime-settings.js} +3 -3
- package/server/dist/{agent-runtime-settlement.js → modules/agent-runtime/runtime/agent-runtime-settlement.js} +6 -6
- package/server/dist/{agent-defaults.js → modules/agents/runtime/agent-defaults.js} +3 -3
- package/server/dist/{agent-generator.js → modules/agents/runtime/agent-generator.js} +7 -7
- package/server/dist/{agent-refine-manager.js → modules/agents/runtime/agent-refine-manager.js} +5 -5
- package/server/dist/{agent-store.js → modules/agents/runtime/agent-store.js} +1 -1
- package/server/dist/{profile-manager.js → modules/agents/runtime/profile-manager.js} +2 -2
- package/server/dist/{profiles-router.js → modules/agents/runtime/profiles-router.js} +5 -5
- package/server/dist/{browser-capture-manager.js → modules/browser/runtime/browser-capture-manager.js} +2 -4
- package/server/dist/{browser-playwright.js → modules/browser/runtime/browser-playwright.js} +1 -1
- package/server/dist/{blueprint-chat-manager.js → modules/builder/runtime/blueprint-chat-manager.js} +7 -7
- package/server/dist/{blueprint-commit.js → modules/builder/runtime/blueprint-commit.js} +8 -8
- package/server/dist/{blueprint-draft-parser.js → modules/builder/runtime/blueprint-draft-parser.js} +1 -1
- package/server/dist/{blueprint-generation.js → modules/builder/runtime/blueprint-generation.js} +1 -1
- package/server/dist/{blueprint-operator-prompt.js → modules/builder/runtime/blueprint-operator-prompt.js} +1 -1
- package/server/dist/{blueprint-router.js → modules/builder/runtime/blueprint-router.js} +3 -3
- package/server/dist/{blueprint-spec-quality.js → modules/builder/runtime/blueprint-spec-quality.js} +1 -1
- package/server/dist/{milestone-chain.js → modules/builder/runtime/milestone-chain.js} +1 -1
- package/server/dist/{milestone-progress.js → modules/builder/runtime/milestone-progress.js} +6 -6
- package/server/dist/{code-activity.js → modules/code/runtime/code-activity.js} +1 -1
- package/server/dist/{code-explorer-router.js → modules/code/runtime/code-explorer-router.js} +5 -5
- package/server/dist/{file-provenance.js → modules/code/runtime/file-provenance.js} +1 -1
- package/server/dist/{file-story-manager.js → modules/code/runtime/file-story-manager.js} +2 -2
- package/server/dist/{file-story.js → modules/code/runtime/file-story.js} +2 -2
- package/server/dist/{file-summary-generator.js → modules/code/runtime/file-summary-generator.js} +3 -3
- package/server/dist/{file-summary-manager.js → modules/code/runtime/file-summary-manager.js} +3 -3
- package/server/dist/{project-code-discovery.js → modules/code/runtime/project-code-discovery.js} +2 -2
- package/server/dist/modules/conversations/domain/draft-stream.js +91 -0
- package/server/dist/modules/conversations/domain/recovery-context.js +101 -0
- package/server/dist/modules/conversations/index.js +10 -0
- package/server/dist/{chat-manager.js → modules/conversations/runtime/chat-manager.js} +32 -216
- package/server/dist/{context-budget.js → modules/conversations/runtime/context-budget.js} +1 -1
- package/server/dist/{explore-cwd-manager.js → modules/conversations/runtime/explore-cwd-manager.js} +1 -1
- package/server/dist/{explore-smash.js → modules/conversations/runtime/explore-smash.js} +1 -1
- package/server/dist/{explore-stdin-session.js → modules/conversations/runtime/explore-stdin-session.js} +1 -1
- package/server/dist/modules/delivery/adapters/decisions/chains.js +138 -0
- package/server/dist/modules/delivery/adapters/decisions/contracts.js +2 -0
- package/server/dist/modules/delivery/adapters/decisions/create.js +234 -0
- package/server/dist/modules/delivery/adapters/decisions/discard.js +251 -0
- package/server/dist/modules/delivery/adapters/decisions/dispatch.js +107 -0
- package/server/dist/modules/delivery/adapters/decisions/evidence.js +205 -0
- package/server/dist/modules/delivery/adapters/decisions/merge-local.js +338 -0
- package/server/dist/modules/delivery/adapters/decisions/metadata.js +109 -0
- package/server/dist/modules/delivery/adapters/decisions/observations.js +260 -0
- package/server/dist/modules/delivery/adapters/decisions/ownership.js +83 -0
- package/server/dist/modules/delivery/adapters/decisions/publish.js +242 -0
- package/server/dist/modules/delivery/adapters/decisions/recovery.js +425 -0
- package/server/dist/modules/delivery/adapters/decisions/retry.js +112 -0
- package/server/dist/modules/delivery/adapters/decisions/transitions.js +94 -0
- package/server/dist/modules/delivery/domain/decision-policy.js +50 -0
- package/server/dist/modules/delivery/domain/state.js +2 -0
- package/server/dist/modules/delivery/index.js +7 -0
- package/server/dist/{active-pr-continuation.js → modules/delivery/runtime/active-pr-continuation.js} +2 -2
- package/server/dist/{delivery-evidence.js → modules/delivery/runtime/delivery-evidence.js} +3 -3
- package/server/dist/{multi-repo-bases.js → modules/delivery/runtime/multi-repo-bases.js} +1 -1
- package/server/dist/{multi-repo-checkout.js → modules/delivery/runtime/multi-repo-checkout.js} +4 -4
- package/server/dist/{multi-repo-delivery.js → modules/delivery/runtime/multi-repo-delivery.js} +4 -4
- package/server/dist/{multi-repo-execution.js → modules/delivery/runtime/multi-repo-execution.js} +8 -8
- package/server/dist/{pr-body.js → modules/delivery/runtime/pr-body.js} +1 -1
- package/server/dist/{pr-follow-up-scope.js → modules/delivery/runtime/pr-follow-up-scope.js} +2 -2
- package/server/dist/{pr-follow-up.js → modules/delivery/runtime/pr-follow-up.js} +1 -1
- package/server/dist/{pr-naming.js → modules/delivery/runtime/pr-naming.js} +1 -1
- package/server/dist/{pr-publisher.js → modules/delivery/runtime/pr-publisher.js} +2 -2
- package/server/dist/{rail-isolated-launch.js → modules/delivery/runtime/rail-isolated-launch.js} +29 -27
- package/server/dist/{rail-launch-parser.js → modules/delivery/runtime/rail-launch-parser.js} +2 -2
- package/server/dist/{rail-merge-orchestrator.js → modules/delivery/runtime/rail-merge-orchestrator.js} +5 -5
- package/server/dist/modules/delivery/runtime/rail-pr-decision.js +12 -0
- package/server/dist/{rail-pr-delivery.js → modules/delivery/runtime/rail-pr-delivery.js} +1 -1
- package/server/dist/{rail-pr-store.js → modules/delivery/runtime/rail-pr-store.js} +2 -2
- package/server/dist/{rail-pr-ticket-effects.js → modules/delivery/runtime/rail-pr-ticket-effects.js} +3 -3
- package/server/dist/{rail-worktree-release.js → modules/delivery/runtime/rail-worktree-release.js} +4 -4
- package/server/dist/{rails-router.js → modules/delivery/runtime/rails-router.js} +66 -67
- package/server/dist/{review-packet.js → modules/delivery/runtime/review-packet.js} +3 -3
- package/server/dist/modules/execution/adapters/budget-storage.js +21 -0
- package/server/dist/modules/execution/adapters/usage-reader.js +38 -0
- package/server/dist/modules/execution/application/enforce-budget.js +14 -0
- package/server/dist/modules/execution/application/record-job-invocations.js +64 -0
- package/server/dist/modules/execution/application/recover-job-usage.js +200 -0
- package/server/dist/modules/execution/domain/job-accounting.js +2 -0
- package/server/dist/modules/execution/domain/scheduling.js +24 -0
- package/server/dist/modules/execution/domain/usage.js +21 -0
- package/server/dist/modules/execution/index.js +14 -0
- package/server/dist/modules/execution/ports.js +2 -0
- package/server/dist/{accept-ladder.js → modules/execution/runtime/accept-ladder.js} +1 -1
- package/server/dist/{interactive-job-session.js → modules/execution/runtime/interactive-job-session.js} +11 -11
- package/server/dist/{job-phase-breakdown.js → modules/execution/runtime/job-phase-breakdown.js} +1 -1
- package/server/dist/{queue-manager.js → modules/execution/runtime/queue-manager.js} +74 -491
- package/server/dist/{revision-seed.js → modules/execution/runtime/revision-seed.js} +1 -1
- package/server/dist/{spawn-lifecycle.js → modules/execution/runtime/spawn-lifecycle.js} +3 -3
- package/server/dist/{stuck-run-detector.js → modules/execution/runtime/stuck-run-detector.js} +1 -1
- package/server/dist/{loop-command-catalog.js → modules/loops/runtime/loop-command-catalog.js} +1 -1
- package/server/dist/{loop-executors.js → modules/loops/runtime/loop-executors.js} +17 -17
- package/server/dist/{loop-factory.js → modules/loops/runtime/loop-factory.js} +2 -21
- package/server/dist/{loop-graph.js → modules/loops/runtime/loop-graph.js} +25 -0
- package/server/dist/{loop-role-engines.js → modules/loops/runtime/loop-role-engines.js} +4 -4
- package/server/dist/{loop-run-manager.js → modules/loops/runtime/loop-run-manager.js} +101 -26
- package/server/dist/{loop-runs-store.js → modules/loops/runtime/loop-runs-store.js} +1 -1
- package/server/dist/{loop-shell-invocation.js → modules/loops/runtime/loop-shell-invocation.js} +2 -2
- package/server/dist/{loop-step-idle.js → modules/loops/runtime/loop-step-idle.js} +1 -1
- package/server/dist/{loop-templates.js → modules/loops/runtime/loop-templates.js} +24 -29
- package/server/dist/{loops-router.js → modules/loops/runtime/loops-router.js} +2 -2
- package/server/dist/{agent-chat-manager.js → modules/missions/runtime/agent-chat-manager.js} +19 -19
- package/server/dist/{agent-chat-router.js → modules/missions/runtime/agent-chat-router.js} +6 -6
- package/server/dist/{agent-context-resolver.js → modules/missions/runtime/agent-context-resolver.js} +8 -8
- package/server/dist/{agent-fence-promotion.js → modules/missions/runtime/agent-fence-promotion.js} +1 -1
- package/server/dist/{agent-input-store.js → modules/missions/runtime/agent-input-store.js} +1 -1
- package/server/dist/{agent-operator-prompt.js → modules/missions/runtime/agent-operator-prompt.js} +17 -7
- package/server/dist/{agent-spec-framing.js → modules/missions/runtime/agent-spec-framing.js} +4 -4
- package/server/dist/{agent-steering.js → modules/missions/runtime/agent-steering.js} +1 -1
- package/server/dist/{mission-run-notify.js → modules/missions/runtime/mission-run-notify.js} +1 -1
- package/server/dist/modules/project-settings/adapters/http.js +24 -0
- package/server/dist/modules/project-settings/adapters/sqlite.js +120 -0
- package/server/dist/modules/project-settings/application.js +11 -0
- package/server/dist/modules/project-settings/domain.js +95 -0
- package/server/dist/modules/project-settings/index.js +11 -0
- package/server/dist/modules/project-settings/ports.js +2 -0
- package/server/dist/{contract-refine-runner.js → modules/specs/runtime/contract-refine-runner.js} +12 -12
- package/server/dist/{proposal-manager.js → modules/specs/runtime/proposal-manager.js} +8 -8
- package/server/dist/{smash-runner.js → modules/specs/runtime/smash-runner.js} +26 -58
- package/server/dist/{spec-addenda-core.js → modules/specs/runtime/spec-addenda-core.js} +2 -2
- package/server/dist/{spec-addenda.js → modules/specs/runtime/spec-addenda.js} +3 -3
- package/server/dist/{spec-launcher-manager.js → modules/specs/runtime/spec-launcher-manager.js} +8 -8
- package/server/dist/{spec-models.js → modules/specs/runtime/spec-models.js} +1 -1
- package/server/dist/{ticket-watcher.js → modules/specs/runtime/ticket-watcher.js} +1 -1
- package/server/dist/{background-process-control.js → modules/terminals/runtime/background-process-control.js} +1 -1
- package/server/dist/{background-process-service.js → modules/terminals/runtime/background-process-service.js} +3 -3
- package/server/dist/{background-process-store.js → modules/terminals/runtime/background-process-store.js} +1 -1
- package/server/dist/{background-windows-bootstrap.js → modules/terminals/runtime/background-windows-bootstrap.js} +3 -3
- package/server/dist/{terminal-manager.js → modules/terminals/runtime/terminal-manager.js} +3 -3
- package/server/dist/project-git.js +1 -1
- package/server/dist/project-profile-support.js +32 -0
- package/server/dist/project-registry.js +34 -40
- package/server/dist/project-router-background-processes.js +1 -1
- package/server/dist/project-router-chat.js +6 -6
- package/server/dist/project-router-git.js +1 -1
- package/server/dist/project-router-helpers.js +2 -2
- package/server/dist/project-router-jobs.js +14 -12
- package/server/dist/project-router-loop-runs.js +12 -12
- package/server/dist/project-router-settings.js +15 -75
- package/server/dist/project-router-setup.js +2 -2
- package/server/dist/project-router-spending.js +4 -4
- package/server/dist/project-router-terminals.js +4 -4
- package/server/dist/project-router-tickets.js +14 -14
- package/server/dist/project-router.js +13 -13
- package/server/dist/providers/codex-adapter.js +3 -3
- package/server/dist/providers/gemini-adapter.js +1 -1
- package/server/dist/providers/telemetry-env.js +55 -0
- package/server/dist/runtime-role-prompts-router.js +2 -2
- package/server/dist/setup-manager.js +2 -48
- package/server/dist/shared/git-branch-name.js +16 -0
- package/server/dist/transient-children.js +3 -3
- package/server/dist/util/distribute-int.js +33 -0
- package/client/dist/assets/ReviewPacketPage-D-UjTef3.js +0 -4
- package/client/dist/assets/index-BAtqmAZR.css +0 -2
- package/client/dist/assets/packet-CjS_478C.js +0 -1
- package/client/dist/assets/packet-D69dKsHN.js +0 -1
- package/client/dist/assets/packet-D9rU6Oxo.js +0 -1
- package/client/dist/assets/packet-DMDFoamg.js +0 -1
- package/client/dist/assets/packet-Dd2RMs1h.js +0 -1
- package/client/dist/assets/packet-DjC9OibC.js +0 -1
- package/client/dist/assets/packet-DwPtJoDz.js +0 -1
- package/client/dist/assets/packet-ceahDg8_.js +0 -1
- package/server/dist/rail-pr-decision.js +0 -2377
- package/server/dist/ticket-broadcast.js +0 -47
- /package/server/dist/{ai-invocations.js → modules/accounting/runtime/ai-invocations.js} +0 -0
- /package/server/dist/{codex-otel-bridge.js → modules/accounting/runtime/codex-otel-bridge.js} +0 -0
- /package/server/dist/{metrics.js → modules/accounting/runtime/metrics.js} +0 -0
- /package/server/dist/{telemetry-export.js → modules/accounting/runtime/telemetry-export.js} +0 -0
- /package/server/dist/{agent-runtime-accounting.js → modules/agent-runtime/runtime/agent-runtime-accounting.js} +0 -0
- /package/server/dist/{agent-runtime-controls-router.js → modules/agent-runtime/runtime/agent-runtime-controls-router.js} +0 -0
- /package/server/dist/{agent-runtime-events.js → modules/agent-runtime/runtime/agent-runtime-events.js} +0 -0
- /package/server/dist/{agent-runtime-history.js → modules/agent-runtime/runtime/agent-runtime-history.js} +0 -0
- /package/server/dist/{agent-runtime-metrics.js → modules/agent-runtime/runtime/agent-runtime-metrics.js} +0 -0
- /package/server/dist/{agent-runtime-package.js → modules/agent-runtime/runtime/agent-runtime-package.js} +0 -0
- /package/server/dist/{agent-runtime-repositories.js → modules/agent-runtime/runtime/agent-runtime-repositories.js} +0 -0
- /package/server/dist/{agent-runtime-verification-suggestions.js → modules/agent-runtime/runtime/agent-runtime-verification-suggestions.js} +0 -0
- /package/server/dist/{agent-refine-db.js → modules/agents/runtime/agent-refine-db.js} +0 -0
- /package/server/dist/{browser-capture-types.js → modules/browser/runtime/browser-capture-types.js} +0 -0
- /package/server/dist/{browser-context-pool.js → modules/browser/runtime/browser-context-pool.js} +0 -0
- /package/server/dist/{browser-network.js → modules/browser/runtime/browser-network.js} +0 -0
- /package/server/dist/{browser-viewport.js → modules/browser/runtime/browser-viewport.js} +0 -0
- /package/server/dist/{blueprint-render.js → modules/builder/runtime/blueprint-render.js} +0 -0
- /package/server/dist/{blueprint-spec-fixtures.js → modules/builder/runtime/blueprint-spec-fixtures.js} +0 -0
- /package/server/dist/{blueprint-store.js → modules/builder/runtime/blueprint-store.js} +0 -0
- /package/server/dist/{blueprint-types.js → modules/builder/runtime/blueprint-types.js} +0 -0
- /package/server/dist/{builder-cwd-manager.js → modules/builder/runtime/builder-cwd-manager.js} +0 -0
- /package/server/dist/{milestone-chain-store.js → modules/builder/runtime/milestone-chain-store.js} +0 -0
- /package/server/dist/{changes-reader.js → modules/code/runtime/changes-reader.js} +0 -0
- /package/server/dist/{context-scope.js → modules/conversations/runtime/context-scope.js} +0 -0
- /package/server/dist/{explore-contract-refine.js → modules/conversations/runtime/explore-contract-refine.js} +0 -0
- /package/server/dist/{explore-draft-title.js → modules/conversations/runtime/explore-draft-title.js} +0 -0
- /package/server/dist/{merge-manager.js → modules/delivery/runtime/merge-manager.js} +0 -0
- /package/server/dist/{multi-repo-execution-store.js → modules/delivery/runtime/multi-repo-execution-store.js} +0 -0
- /package/server/dist/{pr-lifecycle.js → modules/delivery/runtime/pr-lifecycle.js} +0 -0
- /package/server/dist/{rail-isolation.js → modules/delivery/runtime/rail-isolation.js} +0 -0
- /package/server/dist/{rail-pr-recovery-git.js → modules/delivery/runtime/rail-pr-recovery-git.js} +0 -0
- /package/server/dist/{rail-worktrees-store.js → modules/delivery/runtime/rail-worktrees-store.js} +0 -0
- /package/server/dist/{rails-store.js → modules/delivery/runtime/rails-store.js} +0 -0
- /package/server/dist/{job-listing.js → modules/execution/runtime/job-listing.js} +0 -0
- /package/server/dist/{job-spawn-idempotency.js → modules/execution/runtime/job-spawn-idempotency.js} +0 -0
- /package/server/dist/{run-duration-stats.js → modules/execution/runtime/run-duration-stats.js} +0 -0
- /package/server/dist/{verification-sentinel.js → modules/execution/runtime/verification-sentinel.js} +0 -0
- /package/server/dist/{loop-constants.js → modules/loops/runtime/loop-constants.js} +0 -0
- /package/server/dist/{loop-decider.js → modules/loops/runtime/loop-decider.js} +0 -0
- /package/server/dist/{loop-effect.js → modules/loops/runtime/loop-effect.js} +0 -0
- /package/server/dist/{loop-preview.js → modules/loops/runtime/loop-preview.js} +0 -0
- /package/server/dist/{loop-templates-ported.js → modules/loops/runtime/loop-templates-ported.js} +0 -0
- /package/server/dist/{loops-store.js → modules/loops/runtime/loops-store.js} +0 -0
- /package/server/dist/{agent-chat-registry.js → modules/missions/runtime/agent-chat-registry.js} +0 -0
- /package/server/dist/{agent-cwd-manager.js → modules/missions/runtime/agent-cwd-manager.js} +0 -0
- /package/server/dist/{agent-failure-briefing.js → modules/missions/runtime/agent-failure-briefing.js} +0 -0
- /package/server/dist/{agent-tier.js → modules/missions/runtime/agent-tier.js} +0 -0
- /package/server/dist/{spec-contract-prompt.js → modules/specs/runtime/spec-contract-prompt.js} +0 -0
- /package/server/dist/{spec-draft-parser.js → modules/specs/runtime/spec-draft-parser.js} +0 -0
- /package/server/dist/{ticket-store.js → modules/specs/runtime/ticket-store.js} +0 -0
- /package/server/dist/{terminal-marks-store.js → modules/terminals/runtime/terminal-marks-store.js} +0 -0
- /package/server/dist/{terminal-osc-parser.js → modules/terminals/runtime/terminal-osc-parser.js} +0 -0
- /package/server/dist/{terminal-settings.js → modules/terminals/runtime/terminal-settings.js} +0 -0
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.runRecoverAndRetry = runRecoverAndRetry;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const worktree_manager_1 = require("../../../../worktree-manager");
|
|
39
|
+
const rail_pr_store_1 = require("../../runtime/rail-pr-store");
|
|
40
|
+
const rail_worktrees_store_1 = require("../../runtime/rail-worktrees-store");
|
|
41
|
+
const pr_lifecycle_1 = require("../../runtime/pr-lifecycle");
|
|
42
|
+
const rail_pr_recovery_git_1 = require("../../runtime/rail-pr-recovery-git");
|
|
43
|
+
const transitions_1 = require("./transitions");
|
|
44
|
+
const observations_1 = require("./observations");
|
|
45
|
+
const evidence_1 = require("./evidence");
|
|
46
|
+
const retry_1 = require("./retry");
|
|
47
|
+
/**
|
|
48
|
+
* Explicit recovery for a legacy successful continuation whose automatic
|
|
49
|
+
* causal scan could not freeze a commit. Unlike Checkout, this operates only
|
|
50
|
+
* on the delivery-owned isolated worktree/branch and never changes the user's
|
|
51
|
+
* main checkout. The user's confirmation authorizes adopting already-committed
|
|
52
|
+
* progress on that exact branch, but only as a non-force fast-forward of the
|
|
53
|
+
* live PR head.
|
|
54
|
+
*/
|
|
55
|
+
async function runRecoverAndRetry(deps, row) {
|
|
56
|
+
if (!row.pr_url || !row.branch || !row.operation_token)
|
|
57
|
+
return (0, transitions_1.illegalAction)(row.decision);
|
|
58
|
+
const snap = (0, rail_pr_store_1.toPrDeliverySnapshot)(row);
|
|
59
|
+
const runIds = [...new Set(snap.runIds.filter((runId) => typeof runId === 'string' && runId.length > 0))];
|
|
60
|
+
if (runIds.length !== 1) {
|
|
61
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, `Commit & retry push requires exactly one recorded implementation run; found ${runIds.length}. No local evidence was changed.`);
|
|
62
|
+
}
|
|
63
|
+
const runId = runIds[0];
|
|
64
|
+
const matchingUnitIndexes = snap.branches
|
|
65
|
+
.map((branch, index) => branch.branch === row.branch && branch.runId === runId ? index : -1)
|
|
66
|
+
.filter((index) => index >= 0);
|
|
67
|
+
if (matchingUnitIndexes.length === 0) {
|
|
68
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, 'Commit & retry push could not prove an exact recorded unit for this run and PR branch. No local evidence was changed.');
|
|
69
|
+
}
|
|
70
|
+
const matchingUnits = matchingUnitIndexes.map((index) => snap.branches[index]);
|
|
71
|
+
const recordedFinalValues = matchingUnits.map((unit) => unit.finalSha);
|
|
72
|
+
const hasAnyRecordedFinal = recordedFinalValues.some((sha) => sha != null);
|
|
73
|
+
const recordedFinalShas = [...new Set(recordedFinalValues
|
|
74
|
+
.filter((sha) => typeof sha === 'string' && evidence_1.COMMIT_SHA_RE.test(sha))
|
|
75
|
+
.map((sha) => sha.toLowerCase()))];
|
|
76
|
+
const recordedFinalEvidenceUnsafe = hasAnyRecordedFinal && (recordedFinalValues.some((sha) => typeof sha !== 'string' || !evidence_1.COMMIT_SHA_RE.test(sha)) ||
|
|
77
|
+
recordedFinalShas.length !== 1);
|
|
78
|
+
// Durable final evidence is one sufficient ownership proof, not a veto over
|
|
79
|
+
// the independent exact run-marker proof. Incomplete/disagreeing migrated
|
|
80
|
+
// unit fields are ignored here; a unique causal commit may still recover the
|
|
81
|
+
// result without adopting any of those disputed SHAs.
|
|
82
|
+
const recordedFinalSha = recordedFinalEvidenceUnsafe ? null : recordedFinalShas[0] ?? null;
|
|
83
|
+
let discovery = null;
|
|
84
|
+
const discoverCandidate = async () => {
|
|
85
|
+
if (!discovery) {
|
|
86
|
+
discovery = await (0, rail_pr_recovery_git_1.discoverRunMarkedCommit)(deps.git, deps.project.path, runId);
|
|
87
|
+
}
|
|
88
|
+
return discovery;
|
|
89
|
+
};
|
|
90
|
+
const ownedWorktrees = snap.worktreeIds
|
|
91
|
+
.map((worktreeId) => (0, rail_worktrees_store_1.getRailWorktree)(deps.db, worktreeId))
|
|
92
|
+
.filter((worktree) => Boolean(worktree && worktree.branch === row.branch && worktree.run_id === runId));
|
|
93
|
+
const presentWorktrees = ownedWorktrees.filter((worktree) => {
|
|
94
|
+
try {
|
|
95
|
+
fs.lstatSync(worktree.worktree_path);
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
const worktree = presentWorktrees[0] ?? null;
|
|
103
|
+
let preprotectedCandidateSha = null;
|
|
104
|
+
let preprotectedCandidateSource = null;
|
|
105
|
+
// Discover and pin an exact object before any network dependency, even when
|
|
106
|
+
// a ledger path still exists. A recreated/unsafe/dirty path must not hide an
|
|
107
|
+
// older run-owned orphan or leave it exposed to Git GC.
|
|
108
|
+
{
|
|
109
|
+
if (recordedFinalSha && await (0, evidence_1.commitObjectExists)(deps, recordedFinalSha)) {
|
|
110
|
+
preprotectedCandidateSha = recordedFinalSha;
|
|
111
|
+
preprotectedCandidateSource = 'recorded-final';
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
const found = await discoverCandidate();
|
|
115
|
+
if (found.kind === 'ambiguous') {
|
|
116
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, `Git found ${found.count} different run-marked commits. No ambiguous object, branch, or worktree was changed.`);
|
|
117
|
+
}
|
|
118
|
+
if (found.kind === 'scan_failed') {
|
|
119
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, `${found.detail}. No branch, worktree, or object was changed.`);
|
|
120
|
+
}
|
|
121
|
+
if (found.kind === 'unique') {
|
|
122
|
+
preprotectedCandidateSha = found.sha;
|
|
123
|
+
preprotectedCandidateSource = 'run-marker';
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (preprotectedCandidateSha) {
|
|
127
|
+
const protection = await (0, rail_pr_recovery_git_1.protectRecoveryCommit)(deps.git, deps.project.path, row.id, preprotectedCandidateSha);
|
|
128
|
+
if (!protection.ok) {
|
|
129
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, `${protection.detail}. The candidate object and every user-visible branch were left unchanged.`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (presentWorktrees.length > 1) {
|
|
134
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, 'Multiple live worktrees claim this run and branch, so Specrails refused to choose one. Every local result remains intact.', preprotectedCandidateSha);
|
|
135
|
+
}
|
|
136
|
+
const observed = await (0, pr_lifecycle_1.observePrLifecycle)(deps.exec, deps.project.path, row.pr_url, preprotectedCandidateSha);
|
|
137
|
+
if (!observed.ok) {
|
|
138
|
+
if (preprotectedCandidateSha) {
|
|
139
|
+
if (worktree) {
|
|
140
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, `Protected an exact run-owned commit, but a live worktree also exists and the PR could not be observed: ${observed.detail}. Both local results were preserved and neither was selected or pushed.`, preprotectedCandidateSha);
|
|
141
|
+
}
|
|
142
|
+
const durablyProvenNoChange = row.implementation_outcome === 'succeeded' &&
|
|
143
|
+
matchingUnitIndexes.length === snap.branches.length &&
|
|
144
|
+
matchingUnits.every((unit) => (unit.changed === false &&
|
|
145
|
+
unit.initialSha?.toLowerCase() === preprotectedCandidateSha.toLowerCase() &&
|
|
146
|
+
unit.finalSha?.toLowerCase() === preprotectedCandidateSha.toLowerCase()));
|
|
147
|
+
const detail = `Recovered and protected the exact run-owned commit, but the recorded PR could not be observed: ${observed.detail}; retry will revalidate before any push.`;
|
|
148
|
+
if (durablyProvenNoChange) {
|
|
149
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, detail, preprotectedCandidateSha);
|
|
150
|
+
}
|
|
151
|
+
return (0, observations_1.persistProtectedRecoveryRetry)(deps, row, snap, matchingUnitIndexes, runId, preprotectedCandidateSha, detail);
|
|
152
|
+
}
|
|
153
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, `The existing PR could not be verified before local recovery: ${observed.detail}. No local evidence was changed.`);
|
|
154
|
+
}
|
|
155
|
+
if (!(0, pr_lifecycle_1.isExactOpenPr)(observed, row.branch, row.base_branch) || !observed.headRefOid) {
|
|
156
|
+
if (preprotectedCandidateSha) {
|
|
157
|
+
if (worktree) {
|
|
158
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, 'A protected run-owned commit and a live worktree both exist while the recorded PR lifecycle changed. Specrails preserved both and refused to choose or push either automatically.', preprotectedCandidateSha);
|
|
159
|
+
}
|
|
160
|
+
const frozen = (0, observations_1.persistProtectedRecoveryRetry)(deps, row, snap, matchingUnitIndexes, runId, preprotectedCandidateSha, 'Protected the exact run-owned commit; revalidating the changed PR lifecycle before any push.');
|
|
161
|
+
if (frozen.status !== 200 || frozen.body.decision !== 'pr_failed')
|
|
162
|
+
return frozen;
|
|
163
|
+
const recoveredRow = (0, rail_pr_store_1.getPrDelivery)(deps.db, row.id);
|
|
164
|
+
if (!recoveredRow || recoveredRow.operation_token !== row.operation_token) {
|
|
165
|
+
return (0, transitions_1.staleDecision)(recoveredRow?.decision ?? row.decision);
|
|
166
|
+
}
|
|
167
|
+
return (0, retry_1.retryExistingPrFollowupPush)(deps, recoveredRow);
|
|
168
|
+
}
|
|
169
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, 'The recorded PR is no longer an exact open head/base recovery target. No local evidence was changed.');
|
|
170
|
+
}
|
|
171
|
+
const baselineSha = observed.headRefOid;
|
|
172
|
+
// Ensure the live remote baseline object is available for the ancestry proof
|
|
173
|
+
// without advancing any local branch or touching the main checkout.
|
|
174
|
+
if (!(await (0, evidence_1.commitObjectExists)(deps, baselineSha))) {
|
|
175
|
+
try {
|
|
176
|
+
await deps.git.run(['fetch', 'origin', `refs/heads/${row.branch}`], deps.project.path);
|
|
177
|
+
}
|
|
178
|
+
catch { /* the exact object check below remains authoritative */ }
|
|
179
|
+
}
|
|
180
|
+
if (!(await (0, evidence_1.commitObjectExists)(deps, baselineSha))) {
|
|
181
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, 'The live PR head object is unavailable locally, so fast-forward recovery cannot be proven. No local evidence was changed.', preprotectedCandidateSha);
|
|
182
|
+
}
|
|
183
|
+
let candidateSha = preprotectedCandidateSha;
|
|
184
|
+
let candidateOwned = preprotectedCandidateSha !== null;
|
|
185
|
+
let candidateSource = preprotectedCandidateSource ?? 'baseline';
|
|
186
|
+
let committed = false;
|
|
187
|
+
if (worktree) {
|
|
188
|
+
const authenticated = await (0, evidence_1.authenticateRecoveryWorktree)(deps, worktree.worktree_path, row.branch);
|
|
189
|
+
if (!authenticated.ok) {
|
|
190
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, `The preserved path at ${worktree.worktree_path} could not be authenticated safely: ${authenticated.detail}. Nothing was staged, pushed, or removed.`, preprotectedCandidateSha);
|
|
191
|
+
}
|
|
192
|
+
const authenticatedPath = authenticated.realPath;
|
|
193
|
+
const [actualBranch, worktreeHead, branchHead] = await Promise.all([
|
|
194
|
+
deps.git.run(['rev-parse', '--abbrev-ref', 'HEAD'], authenticatedPath).catch(() => ({ code: 1, stdout: '', stderr: '' })),
|
|
195
|
+
(0, evidence_1.exactRefSha)(deps, authenticatedPath, 'HEAD'),
|
|
196
|
+
(0, evidence_1.exactRefSha)(deps, deps.project.path, `refs/heads/${row.branch}`),
|
|
197
|
+
]);
|
|
198
|
+
if (actualBranch.code !== 0 || actualBranch.stdout.trim() !== row.branch ||
|
|
199
|
+
!worktreeHead || !branchHead || worktreeHead !== branchHead ||
|
|
200
|
+
authenticated.head.toLowerCase() !== worktreeHead.toLowerCase()) {
|
|
201
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, `The preserved worktree at ${worktree.worktree_path} is not on the exact recorded branch/HEAD. It was left untouched.`, preprotectedCandidateSha);
|
|
202
|
+
}
|
|
203
|
+
if (preprotectedCandidateSha &&
|
|
204
|
+
worktreeHead.toLowerCase() !== preprotectedCandidateSha.toLowerCase()) {
|
|
205
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, `A protected run-owned commit and a different live worktree result both exist. Specrails preserved both and refused to choose or bundle them automatically. Inspect ${worktree.worktree_path} before retrying or discarding either result.`, preprotectedCandidateSha);
|
|
206
|
+
}
|
|
207
|
+
if (!(await (0, evidence_1.isFastForwardCandidate)(deps, baselineSha, worktreeHead))) {
|
|
208
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, `The preserved branch diverges from the live PR head. The worktree at ${worktree.worktree_path} was left untouched.`, preprotectedCandidateSha);
|
|
209
|
+
}
|
|
210
|
+
let skipWorktreeCommit = false;
|
|
211
|
+
if (worktreeHead === baselineSha) {
|
|
212
|
+
candidateOwned = true;
|
|
213
|
+
candidateSource = 'baseline';
|
|
214
|
+
}
|
|
215
|
+
else if (recordedFinalSha && worktreeHead.toLowerCase() === recordedFinalSha) {
|
|
216
|
+
candidateOwned = true;
|
|
217
|
+
candidateSource = 'recorded-final';
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
const found = await discoverCandidate();
|
|
221
|
+
if (found.kind === 'unique' && found.sha === worktreeHead.toLowerCase()) {
|
|
222
|
+
candidateOwned = true;
|
|
223
|
+
candidateSource = 'run-marker';
|
|
224
|
+
}
|
|
225
|
+
else if (found.kind === 'unique') {
|
|
226
|
+
// A later/unrelated checked-out tip must not be bundled into the
|
|
227
|
+
// recovered result. Preserve it untouched and use only the exact
|
|
228
|
+
// run-owned object discovered independently.
|
|
229
|
+
candidateSha = found.sha;
|
|
230
|
+
candidateOwned = true;
|
|
231
|
+
candidateSource = 'run-marker';
|
|
232
|
+
skipWorktreeCommit = true;
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
const reason = found.kind === 'ambiguous'
|
|
236
|
+
? `Git found ${found.count} run-marked commits, so no unique worktree baseline could be chosen.`
|
|
237
|
+
: found.kind === 'scan_failed'
|
|
238
|
+
? `${found.detail}.`
|
|
239
|
+
: 'The preserved worktree branch advanced beyond every delivery-owned commit.';
|
|
240
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, `${reason} The worktree at ${worktree.worktree_path} was left untouched.`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (!skipWorktreeCommit) {
|
|
244
|
+
deps.assertAdmission?.();
|
|
245
|
+
const reauthenticated = await (0, evidence_1.authenticateRecoveryWorktree)(deps, worktree.worktree_path, row.branch, worktreeHead);
|
|
246
|
+
if (!reauthenticated.ok || reauthenticated.realPath !== authenticatedPath) {
|
|
247
|
+
const detail = reauthenticated.ok
|
|
248
|
+
? 'the canonical worktree path changed before staging'
|
|
249
|
+
: reauthenticated.detail;
|
|
250
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, `The preserved path at ${worktree.worktree_path} failed final authentication: ${detail}. Nothing was staged, pushed, or removed.`);
|
|
251
|
+
}
|
|
252
|
+
deps.assertAdmission?.();
|
|
253
|
+
const overlayExcludes = [...new Set(matchingUnits.flatMap((unit) => unit.overlayExcludes ?? []))];
|
|
254
|
+
const commit = await (0, worktree_manager_1.commitWorktreeAndVerify)(deps.git, reauthenticated.realPath, `specrails: recovered follow-up (run ${runId})`, overlayExcludes);
|
|
255
|
+
if (!commit.clean) {
|
|
256
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, `The preserved worktree could not be committed safely: ${commit.error ?? (commit.dirty.join(', ') || 'deliverable changes remain')}. It was left intact at ${worktree.worktree_path}.`);
|
|
257
|
+
}
|
|
258
|
+
committed = commit.committed;
|
|
259
|
+
const [afterBranch, afterHead, afterBranchHead] = await Promise.all([
|
|
260
|
+
deps.git.run(['rev-parse', '--abbrev-ref', 'HEAD'], reauthenticated.realPath).catch(() => ({ code: 1, stdout: '', stderr: '' })),
|
|
261
|
+
(0, evidence_1.exactRefSha)(deps, reauthenticated.realPath, 'HEAD'),
|
|
262
|
+
(0, evidence_1.exactRefSha)(deps, deps.project.path, `refs/heads/${row.branch}`),
|
|
263
|
+
]);
|
|
264
|
+
if (afterBranch.code !== 0 || afterBranch.stdout.trim() !== row.branch ||
|
|
265
|
+
!afterHead || afterHead !== afterBranchHead) {
|
|
266
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, `The recovery commit exists, but final branch verification failed. It remains preserved at ${worktree.worktree_path}.`);
|
|
267
|
+
}
|
|
268
|
+
candidateSha = afterHead;
|
|
269
|
+
if (committed) {
|
|
270
|
+
candidateOwned = true;
|
|
271
|
+
candidateSource = 'worktree';
|
|
272
|
+
}
|
|
273
|
+
if (candidateSource === 'baseline' && recordedFinalSha && afterHead.toLowerCase() === recordedFinalSha) {
|
|
274
|
+
candidateSource = 'recorded-final';
|
|
275
|
+
}
|
|
276
|
+
candidateOwned = candidateOwned || committed || candidateSource !== 'baseline' || afterHead === baselineSha;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
candidateSha ??= await (0, evidence_1.exactRefSha)(deps, deps.project.path, `refs/heads/${row.branch}`);
|
|
281
|
+
}
|
|
282
|
+
// A consistent durable final SHA outranks a mutable branch name. This also
|
|
283
|
+
// lets another checkout recover an object whose branch was later reset.
|
|
284
|
+
if (!committed && recordedFinalSha &&
|
|
285
|
+
await (0, evidence_1.commitObjectExists)(deps, recordedFinalSha) &&
|
|
286
|
+
(!candidateOwned || candidateSha === baselineSha)) {
|
|
287
|
+
candidateSha = recordedFinalSha;
|
|
288
|
+
candidateOwned = true;
|
|
289
|
+
candidateSource = 'recorded-final';
|
|
290
|
+
}
|
|
291
|
+
// Before claiming absence, inspect the same complete causal surface as
|
|
292
|
+
// startup. A unique run-marked object may survive after both the worktree and
|
|
293
|
+
// visible branch were removed.
|
|
294
|
+
const baselineHasDurableFinalProof = candidateSha === baselineSha && recordedFinalSha === baselineSha;
|
|
295
|
+
if (!candidateSha || (candidateSha === baselineSha && !baselineHasDurableFinalProof) || !candidateOwned) {
|
|
296
|
+
const found = await discoverCandidate();
|
|
297
|
+
if (found.kind === 'ambiguous') {
|
|
298
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, `Git found ${found.count} different run-marked commits. No ambiguous object, branch, or worktree was changed.`);
|
|
299
|
+
}
|
|
300
|
+
if (found.kind === 'scan_failed') {
|
|
301
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, `${found.detail}. No branch, worktree, or object was changed.`);
|
|
302
|
+
}
|
|
303
|
+
if (found.kind === 'unique') {
|
|
304
|
+
candidateSha = found.sha;
|
|
305
|
+
candidateOwned = true;
|
|
306
|
+
candidateSource = 'run-marker';
|
|
307
|
+
}
|
|
308
|
+
else if (candidateSha && candidateSha !== baselineSha && !candidateOwned) {
|
|
309
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, 'The recorded local branch advanced beyond the run-owned result. The later branch tip was preserved and was not committed or pushed.');
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (!candidateSha || !(await (0, evidence_1.commitObjectExists)(deps, candidateSha))) {
|
|
313
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, 'No delivery-owned commit is available in this clone’s refs, reflogs, worktrees, or unreachable objects. The result may still exist on the computer where the run executed; nothing here was changed or removed.');
|
|
314
|
+
}
|
|
315
|
+
if (candidateSha === baselineSha) {
|
|
316
|
+
const provesNoChanges = row.implementation_outcome === 'succeeded' &&
|
|
317
|
+
matchingUnitIndexes.length === snap.branches.length &&
|
|
318
|
+
matchingUnits.every((unit) => (unit.changed === false &&
|
|
319
|
+
unit.initialSha?.toLowerCase() === baselineSha.toLowerCase() &&
|
|
320
|
+
unit.finalSha?.toLowerCase() === baselineSha.toLowerCase()));
|
|
321
|
+
if (provesNoChanges) {
|
|
322
|
+
const noChangeIndexes = new Set(matchingUnitIndexes);
|
|
323
|
+
const noChangeBranches = snap.branches.map((unit, index) => noChangeIndexes.has(index)
|
|
324
|
+
? {
|
|
325
|
+
...unit,
|
|
326
|
+
succeeded: true,
|
|
327
|
+
implementationOutcome: 'succeeded',
|
|
328
|
+
deliveryOutcome: 'no_changes',
|
|
329
|
+
initialSha: baselineSha,
|
|
330
|
+
finalSha: baselineSha,
|
|
331
|
+
changed: false,
|
|
332
|
+
failureCode: null,
|
|
333
|
+
}
|
|
334
|
+
: unit);
|
|
335
|
+
const conflict = (0, transitions_1.casTransition)(deps, row, 'no_changes', {
|
|
336
|
+
branches: noChangeBranches,
|
|
337
|
+
deliverySha: baselineSha,
|
|
338
|
+
deliveryOutcome: 'no_changes',
|
|
339
|
+
statusCode: 'no_changes',
|
|
340
|
+
statusDetail: null,
|
|
341
|
+
isContinuation: true,
|
|
342
|
+
});
|
|
343
|
+
if (conflict)
|
|
344
|
+
return conflict;
|
|
345
|
+
await (0, evidence_1.releaseDeliveredRecoveryLineage)(deps, row, baselineSha);
|
|
346
|
+
(0, transitions_1.finalizeTransition)(deps, row.id);
|
|
347
|
+
return {
|
|
348
|
+
status: 200,
|
|
349
|
+
body: {
|
|
350
|
+
ok: true,
|
|
351
|
+
decision: 'no_changes',
|
|
352
|
+
prUrl: row.pr_url,
|
|
353
|
+
noChanges: true,
|
|
354
|
+
deliveryVerified: true,
|
|
355
|
+
verifiedSha: baselineSha,
|
|
356
|
+
pushed: false,
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
const alreadyDelivered = recordedFinalSha === baselineSha || candidateSource === 'run-marker';
|
|
361
|
+
if (!alreadyDelivered) {
|
|
362
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, 'This clone contains only the current PR head and no additional run-owned result. The original execution computer may still retain its worktree or orphan commit; nothing here was changed or removed.');
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
const protection = preprotectedCandidateSha &&
|
|
367
|
+
preprotectedCandidateSha.toLowerCase() !== candidateSha.toLowerCase()
|
|
368
|
+
? await (0, rail_pr_recovery_git_1.advanceRecoveryCommitProtection)(deps.git, deps.project.path, row.id, preprotectedCandidateSha, candidateSha)
|
|
369
|
+
: await (0, rail_pr_recovery_git_1.protectRecoveryCommit)(deps.git, deps.project.path, row.id, candidateSha);
|
|
370
|
+
if (!protection.ok) {
|
|
371
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, `${protection.detail}. The candidate object and every user-visible branch were left unchanged.`, preprotectedCandidateSha);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (!(await (0, evidence_1.isFastForwardCandidate)(deps, baselineSha, candidateSha))) {
|
|
375
|
+
return (0, observations_1.persistRecoveryBlocked)(deps, row, 'The exact run-owned recovery commit is not a fast-forward of the live PR head. It remains protected locally and nothing was pushed or removed.', candidateSha);
|
|
376
|
+
}
|
|
377
|
+
const recoveredIndexes = new Set(matchingUnitIndexes);
|
|
378
|
+
const recoveredBranches = snap.branches.map((unit, index) => recoveredIndexes.has(index)
|
|
379
|
+
? {
|
|
380
|
+
...unit,
|
|
381
|
+
runId,
|
|
382
|
+
branch: row.branch,
|
|
383
|
+
succeeded: true,
|
|
384
|
+
implementationOutcome: 'succeeded',
|
|
385
|
+
deliveryOutcome: 'ready',
|
|
386
|
+
// Equality with the live PR head can mean the exact run commit was
|
|
387
|
+
// already delivered; it is not no-change evidence. Preserve a durable
|
|
388
|
+
// initial SHA when available and infer the observed baseline only for
|
|
389
|
+
// a still-missing descendant. The proven no-change branch returned
|
|
390
|
+
// above before reaching this mapping.
|
|
391
|
+
initialSha: unit.initialSha ?? (candidateSha !== baselineSha ? baselineSha : null),
|
|
392
|
+
finalSha: candidateSha,
|
|
393
|
+
changed: true,
|
|
394
|
+
failureCode: null,
|
|
395
|
+
branchOwnership: 'borrowed-pr',
|
|
396
|
+
...(worktree ? { worktreePath: worktree.worktree_path } : {}),
|
|
397
|
+
}
|
|
398
|
+
: unit);
|
|
399
|
+
// Freeze before the network mutation. A crash after this write leaves a
|
|
400
|
+
// normal immutable Retry push card; a crash before it leaves the run-marked
|
|
401
|
+
// commit discoverable by startup recovery.
|
|
402
|
+
if (!(0, rail_pr_store_1.transitionClaimedDecision)(deps.db, row.id, row.decision, 'pr_failed', row.operation_token, {
|
|
403
|
+
branches: recoveredBranches,
|
|
404
|
+
deliverySha: candidateSha,
|
|
405
|
+
deliveryOutcome: 'retryable_failure',
|
|
406
|
+
statusCode: 'settlement_interrupted',
|
|
407
|
+
statusDetail: candidateSource === 'worktree'
|
|
408
|
+
? `Committed the preserved worktree as ${candidateSha.slice(0, 8)}; validating the exact PR before push.`
|
|
409
|
+
: candidateSource === 'run-marker'
|
|
410
|
+
? `Protected the unique run-owned commit ${candidateSha.slice(0, 8)}; validating the exact PR before push.`
|
|
411
|
+
: `Recovered the durable final commit ${candidateSha.slice(0, 8)}; validating the exact PR before push.`,
|
|
412
|
+
isContinuation: true,
|
|
413
|
+
})) {
|
|
414
|
+
const current = (0, rail_pr_store_1.getPrDelivery)(deps.db, row.id);
|
|
415
|
+
return (0, transitions_1.staleDecision)(current?.decision ?? row.decision);
|
|
416
|
+
}
|
|
417
|
+
if (worktree)
|
|
418
|
+
(0, rail_worktrees_store_1.updateRailWorktreeState)(deps.db, worktree.id, 'built');
|
|
419
|
+
const recoveredRow = (0, rail_pr_store_1.getPrDelivery)(deps.db, row.id);
|
|
420
|
+
if (!recoveredRow || recoveredRow.operation_token !== row.operation_token) {
|
|
421
|
+
return (0, transitions_1.staleDecision)(recoveredRow?.decision ?? row.decision);
|
|
422
|
+
}
|
|
423
|
+
deps.assertAdmission?.();
|
|
424
|
+
return (0, retry_1.retryExistingPrFollowupPush)(deps, recoveredRow);
|
|
425
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.retryExistingPrFollowupPush = retryExistingPrFollowupPush;
|
|
4
|
+
const pr_publisher_1 = require("../../runtime/pr-publisher");
|
|
5
|
+
const pr_lifecycle_1 = require("../../runtime/pr-lifecycle");
|
|
6
|
+
const transitions_1 = require("./transitions");
|
|
7
|
+
const observations_1 = require("./observations");
|
|
8
|
+
const evidence_1 = require("./evidence");
|
|
9
|
+
async function retryExistingPrFollowupPush(deps, row) {
|
|
10
|
+
if (!row.branch || !row.pr_url)
|
|
11
|
+
return { status: 502, body: { error: 'push_failed', detail: 'missing PR branch/url' } };
|
|
12
|
+
if (!row.delivery_sha) {
|
|
13
|
+
const detail = 'missing verified delivery SHA; refusing to resolve a mutable branch for retry';
|
|
14
|
+
const conflict = (0, transitions_1.casTransition)(deps, row, 'pr_failed', {
|
|
15
|
+
deliveryOutcome: 'blocked',
|
|
16
|
+
statusCode: 'branch_verification_failed',
|
|
17
|
+
statusDetail: detail,
|
|
18
|
+
});
|
|
19
|
+
if (conflict)
|
|
20
|
+
return conflict;
|
|
21
|
+
(0, transitions_1.finalizeTransition)(deps, row.id);
|
|
22
|
+
return { status: 409, body: { error: 'missing_verified_sha', decision: 'pr_failed', prUrl: row.pr_url, detail } };
|
|
23
|
+
}
|
|
24
|
+
const beforePush = await (0, pr_lifecycle_1.observePrLifecycle)(deps.exec, deps.project.path, row.pr_url, row.delivery_sha);
|
|
25
|
+
if (!beforePush.ok) {
|
|
26
|
+
return (0, observations_1.persistRetryablePrObservationFailure)(deps, row, `could not confirm the existing PR is open before retry: ${beforePush.detail}`);
|
|
27
|
+
}
|
|
28
|
+
if (!(0, pr_lifecycle_1.isExactOpenPr)(beforePush, row.branch, row.base_branch)) {
|
|
29
|
+
const identityMatches = (0, pr_lifecycle_1.matchesRecordedPrIdentity)(beforePush, row.branch, row.base_branch);
|
|
30
|
+
if (beforePush.state === 'MERGED' && identityMatches && beforePush.includesExpectedSha === true) {
|
|
31
|
+
return (0, observations_1.settleMergedPr)(deps, row);
|
|
32
|
+
}
|
|
33
|
+
if (beforePush.state === 'MERGED') {
|
|
34
|
+
return (0, observations_1.rerouteFromStalePr)(deps, row, identityMatches ? (0, observations_1.mergedEvidenceDetail)(beforePush) : 'the previous PR merged after its recorded head/base identity changed', false);
|
|
35
|
+
}
|
|
36
|
+
if (beforePush.state === 'CLOSED') {
|
|
37
|
+
if (identityMatches && beforePush.includesExpectedSha === true) {
|
|
38
|
+
return (0, observations_1.settleObservedClosedPr)(deps, row, beforePush);
|
|
39
|
+
}
|
|
40
|
+
return (0, observations_1.rerouteFromStalePr)(deps, row, 'the previous PR closed before this implementation was delivered', false);
|
|
41
|
+
}
|
|
42
|
+
return (0, observations_1.rerouteFromStalePr)(deps, row, 'the existing open PR no longer matches its recorded head/base identity', false);
|
|
43
|
+
}
|
|
44
|
+
if (beforePush.includesExpectedSha === true) {
|
|
45
|
+
return (0, observations_1.settleObservedExistingPr)(deps, row, beforePush, false);
|
|
46
|
+
}
|
|
47
|
+
if (!beforePush.headRefOid) {
|
|
48
|
+
return (0, observations_1.persistRetryablePrObservationFailure)(deps, row, 'the open PR did not expose a verifiable head commit; Retry push will revalidate before mutation');
|
|
49
|
+
}
|
|
50
|
+
if (!(await (0, evidence_1.commitObjectExists)(deps, beforePush.headRefOid))) {
|
|
51
|
+
try {
|
|
52
|
+
await deps.git.run(['fetch', 'origin', `refs/heads/${row.branch}`], deps.project.path);
|
|
53
|
+
}
|
|
54
|
+
catch { /* the immutable object check below remains authoritative */ }
|
|
55
|
+
}
|
|
56
|
+
if (!(await (0, evidence_1.commitObjectExists)(deps, beforePush.headRefOid))) {
|
|
57
|
+
return (0, observations_1.persistRetryablePrObservationFailure)(deps, row, 'the live PR head object is unavailable locally; Retry push will revalidate before mutation');
|
|
58
|
+
}
|
|
59
|
+
if (!(await (0, evidence_1.isFastForwardCandidate)(deps, beforePush.headRefOid, row.delivery_sha))) {
|
|
60
|
+
const detail = 'the exact delivery commit is not a fast-forward of the live PR head; it was not pushed';
|
|
61
|
+
return row.is_continuation === 1
|
|
62
|
+
? (0, observations_1.persistRecoveryBlocked)(deps, row, `${detail} and remains protected`, row.delivery_sha)
|
|
63
|
+
: (0, observations_1.persistMovedPrHead)(deps, row, detail);
|
|
64
|
+
}
|
|
65
|
+
const remote = await (0, pr_lifecycle_1.verifyPushRemoteForPr)(deps.exec, deps.project.path, row.pr_url);
|
|
66
|
+
if (!remote.ok) {
|
|
67
|
+
return (0, observations_1.persistRetryablePrObservationFailure)(deps, row, `refusing to push until the PR repository and origin are proven identical: ${remote.detail}`);
|
|
68
|
+
}
|
|
69
|
+
const pushed = await (0, pr_publisher_1.pushBranch)(deps.exec, {
|
|
70
|
+
repoDir: deps.project.path,
|
|
71
|
+
branch: row.branch,
|
|
72
|
+
baseBranch: row.base_branch,
|
|
73
|
+
remote: remote.pushTarget,
|
|
74
|
+
sourceSha: row.delivery_sha,
|
|
75
|
+
});
|
|
76
|
+
if (pushed.state === 'local-only') {
|
|
77
|
+
const conflict = (0, transitions_1.casTransition)(deps, row, 'pr_failed', {
|
|
78
|
+
prState: 'local-only',
|
|
79
|
+
deliveryOutcome: 'retryable_failure',
|
|
80
|
+
statusCode: 'push_failed',
|
|
81
|
+
statusDetail: pushed.reason,
|
|
82
|
+
});
|
|
83
|
+
if (conflict)
|
|
84
|
+
return conflict;
|
|
85
|
+
(0, transitions_1.finalizeTransition)(deps, row.id);
|
|
86
|
+
return { status: 200, body: { ok: true, decision: 'pr_failed', prUrl: row.pr_url, detail: pushed.reason } };
|
|
87
|
+
}
|
|
88
|
+
const afterPush = await (0, pr_lifecycle_1.observePrLifecycle)(deps.exec, deps.project.path, row.pr_url, row.delivery_sha);
|
|
89
|
+
if (!afterPush.ok) {
|
|
90
|
+
return (0, observations_1.persistRetryablePrObservationFailure)(deps, row, `exact commit was pushed, but the PR lifecycle could not be confirmed: ${afterPush.detail}`);
|
|
91
|
+
}
|
|
92
|
+
if ((0, pr_lifecycle_1.isExactOpenPr)(afterPush, row.branch, row.base_branch)) {
|
|
93
|
+
if (afterPush.includesExpectedSha !== true) {
|
|
94
|
+
return (0, observations_1.persistRetryablePrObservationFailure)(deps, row, 'the exact push completed, but the PR does not yet expose the verified commit as its head; Retry push remains safe and uses the preserved SHA');
|
|
95
|
+
}
|
|
96
|
+
return (0, observations_1.settleObservedExistingPr)(deps, row, afterPush, true);
|
|
97
|
+
}
|
|
98
|
+
const identityMatches = (0, pr_lifecycle_1.matchesRecordedPrIdentity)(afterPush, row.branch, row.base_branch);
|
|
99
|
+
if (afterPush.state === 'MERGED' && identityMatches && afterPush.includesExpectedSha === true) {
|
|
100
|
+
return (0, observations_1.settleMergedPr)(deps, row);
|
|
101
|
+
}
|
|
102
|
+
if (afterPush.state === 'MERGED') {
|
|
103
|
+
return (0, observations_1.rerouteFromStalePr)(deps, row, identityMatches ? (0, observations_1.mergedEvidenceDetail)(afterPush) : 'the previous PR merged after its recorded head/base identity changed', true);
|
|
104
|
+
}
|
|
105
|
+
if (afterPush.state === 'CLOSED') {
|
|
106
|
+
if (identityMatches && afterPush.includesExpectedSha === true) {
|
|
107
|
+
return (0, observations_1.settleObservedClosedPr)(deps, row, afterPush);
|
|
108
|
+
}
|
|
109
|
+
return (0, observations_1.rerouteFromStalePr)(deps, row, 'the previous PR closed before this implementation was delivered', true);
|
|
110
|
+
}
|
|
111
|
+
return (0, observations_1.rerouteFromStalePr)(deps, row, 'the existing open PR no longer matches its recorded head/base identity after the exact push', true);
|
|
112
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deferredFinalizations = void 0;
|
|
4
|
+
exports.staleDecision = staleDecision;
|
|
5
|
+
exports.illegalAction = illegalAction;
|
|
6
|
+
exports.ghFailed = ghFailed;
|
|
7
|
+
exports.safetyArchiveRecorder = safetyArchiveRecorder;
|
|
8
|
+
exports.casTransition = casTransition;
|
|
9
|
+
exports.casTransitionWithTicketEffect = casTransitionWithTicketEffect;
|
|
10
|
+
exports.applyTerminalTicketEffect = applyTerminalTicketEffect;
|
|
11
|
+
exports.finalizeTransition = finalizeTransition;
|
|
12
|
+
const rail_pr_store_1 = require("../../runtime/rail-pr-store");
|
|
13
|
+
const agent_chat_registry_1 = require("../../../missions/runtime/agent-chat-registry");
|
|
14
|
+
const rail_pr_ticket_effects_1 = require("../../runtime/rail-pr-ticket-effects");
|
|
15
|
+
// Action handlers keep using one finalization helper, but the operation lease
|
|
16
|
+
// must be absent from every emitted snapshot. Calls made while a lease is held
|
|
17
|
+
// are deferred until executePrDecision's finally releases that exact token.
|
|
18
|
+
exports.deferredFinalizations = new Set();
|
|
19
|
+
function staleDecision(current) {
|
|
20
|
+
return { status: 409, body: { error: 'stale_decision', current } };
|
|
21
|
+
}
|
|
22
|
+
function illegalAction(current) {
|
|
23
|
+
return { status: 409, body: { error: 'stale_decision', current, reason: 'illegal_action' } };
|
|
24
|
+
}
|
|
25
|
+
function ghFailed(r) {
|
|
26
|
+
const detail = (r.stderr.trim() || r.stdout.trim()).split('\n')[0] || `exit ${r.code}`;
|
|
27
|
+
return { status: 502, body: { error: 'gh_failed', detail } };
|
|
28
|
+
}
|
|
29
|
+
function safetyArchiveRecorder(deps, row) {
|
|
30
|
+
return (archive) => {
|
|
31
|
+
if (!(0, rail_pr_store_1.appendPrDeliverySafetyArchive)(deps.db, row.id, archive)) {
|
|
32
|
+
throw new Error(`delivery ${row.id} disappeared while recording safety archive ${archive}`);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Atomic CAS transition; a `false` return means a concurrent mutation raced us
|
|
38
|
+
* between the pre-check and here — surface the same 409 with the fresh state.
|
|
39
|
+
*/
|
|
40
|
+
function casTransition(deps, row, next, patch = {}) {
|
|
41
|
+
if (row.operation_token && (0, rail_pr_store_1.transitionClaimedDecision)(deps.db, row.id, row.decision, next, row.operation_token, patch))
|
|
42
|
+
return null;
|
|
43
|
+
const current = (0, rail_pr_store_1.getPrDelivery)(deps.db, row.id);
|
|
44
|
+
return staleDecision(current?.decision ?? row.decision);
|
|
45
|
+
}
|
|
46
|
+
function casTransitionWithTicketEffect(deps, row, next, patch, effect) {
|
|
47
|
+
if (deps.repositoryChildOf)
|
|
48
|
+
return casTransition(deps, row, next, patch);
|
|
49
|
+
if (row.operation_token &&
|
|
50
|
+
(0, rail_pr_ticket_effects_1.transitionClaimedDecisionWithTicketEffect)(deps.db, row.id, row.decision, next, row.operation_token, patch, effect))
|
|
51
|
+
return null;
|
|
52
|
+
const current = (0, rail_pr_store_1.getPrDelivery)(deps.db, row.id);
|
|
53
|
+
return staleDecision(current?.decision ?? row.decision);
|
|
54
|
+
}
|
|
55
|
+
function applyTerminalTicketEffect(deps, row, terminalDecision, cleanupWarnings) {
|
|
56
|
+
if (deps.repositoryChildOf)
|
|
57
|
+
return;
|
|
58
|
+
const result = (0, rail_pr_ticket_effects_1.applyRailPrTicketEffect)(deps, row.id);
|
|
59
|
+
if (result.ok)
|
|
60
|
+
return;
|
|
61
|
+
cleanupWarnings.push(`ticket status update pending: ${result.error ?? 'unknown ticket-store error'}`);
|
|
62
|
+
if (!row.operation_token)
|
|
63
|
+
return;
|
|
64
|
+
(0, rail_pr_store_1.transitionClaimedDecision)(deps.db, row.id, terminalDecision, terminalDecision, row.operation_token, {
|
|
65
|
+
statusCode: 'cleanup_incomplete',
|
|
66
|
+
cleanupWarnings,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Post-transition fan-out: re-broadcast the durable rail.pr_state snapshot
|
|
71
|
+
* (both surfaces converge on it) and, for agent-chat-originated launches,
|
|
72
|
+
* update the persisted inline card in place.
|
|
73
|
+
*/
|
|
74
|
+
function finalizeTransition(deps, id) {
|
|
75
|
+
const row = (0, rail_pr_store_1.getPrDelivery)(deps.db, id);
|
|
76
|
+
if (!row)
|
|
77
|
+
return undefined;
|
|
78
|
+
const snap = (0, rail_pr_store_1.toPrDeliverySnapshot)(row);
|
|
79
|
+
if (row.operation_token) {
|
|
80
|
+
exports.deferredFinalizations.add(id);
|
|
81
|
+
return snap;
|
|
82
|
+
}
|
|
83
|
+
if (deps.repositoryChildOf)
|
|
84
|
+
return snap;
|
|
85
|
+
deps.broadcast((0, rail_pr_store_1.toRailPrStateMessage)(deps.project.id, snap));
|
|
86
|
+
if (row.origin_conversation_id) {
|
|
87
|
+
const agent = (deps.agentChat ?? agent_chat_registry_1.getAgentChatManager)();
|
|
88
|
+
// Shared mapper (rail-pr-store.toPrDecisionCardEnvelope) — the SAME shape
|
|
89
|
+
// rail-isolated-launch's origin-card sync posts, so the two card-writing
|
|
90
|
+
// sites (incl. the runIds chip data) can never drift.
|
|
91
|
+
agent?.updatePrDecisionCard(row.origin_conversation_id, (0, rail_pr_store_1.toPrDecisionCardEnvelope)(deps.project.id, snap));
|
|
92
|
+
}
|
|
93
|
+
return snap;
|
|
94
|
+
}
|