specrails-desktop 2.36.0 → 2.42.2
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/README.md +94 -247
- package/client/dist/assets/ActivityFeedPage-DoEMumQS.js +1 -0
- package/client/dist/assets/AgentBrowserCapture-DIh4K0gh.js +1 -0
- package/client/dist/assets/AgentModeAnalyticsPane-mbq4WEoO.js +2 -0
- package/client/dist/assets/AgentModeCodePane-CKh7zuA4.js +2 -0
- package/client/dist/assets/AgentModeJobsPane-BDokPiLJ.js +1 -0
- package/client/dist/assets/AgentsPage-BFVAZbBj.js +87 -0
- package/client/dist/assets/AnalyticsPage-3Y9LuWwz.js +1 -0
- package/client/dist/assets/CodePage-C9KyuV_C.js +3 -0
- package/client/dist/assets/DesktopAnalyticsPage-Bhi54bXQ.js +1 -0
- package/client/dist/assets/{DocsDialog-C76VdBJC.js → DocsDialog-Dj6mt4ga.js} +2 -2
- package/client/dist/assets/{DocsPage-ziMlj6B6.js → DocsPage-24yWglNI.js} +1 -1
- package/client/dist/assets/ExportDropdown-ophapHrT.js +1 -0
- package/client/dist/assets/InteractiveJobComposer-C1ucmxQh.js +19 -0
- package/client/dist/assets/JobDetailModal-DNWmgVHD.js +1 -0
- package/client/dist/assets/JobDetailPage-BcajZyuu.js +1 -0
- package/client/dist/assets/JobsPage-B9nTZw6e.js +1 -0
- package/client/dist/assets/LoopBuilderPage-Oc48iOFW.js +7 -0
- package/client/dist/assets/LoopPreviewModal-ae0Fr6SU.js +1 -0
- package/client/dist/assets/LoopsPage-CUEqzUBI.js +1 -0
- package/client/dist/assets/MinimizedChatsContext-Deg39peL.js +1 -0
- package/client/dist/assets/PluginsPage-C1xtARIx.js +2 -0
- package/client/dist/assets/ProjectSettingsDialog-JZzrXFBN.js +1 -0
- package/client/dist/assets/RepositoryDeliveries-CSDsiVUz.js +5 -0
- package/client/dist/assets/RepositoryScopeSelector-D-RCaknl.js +1 -0
- package/client/dist/assets/ReviewPacketPage-x5EfGTiL.js +1 -0
- package/client/dist/assets/TemplatePreviewModal-BSyuZxGq.js +1 -0
- package/client/dist/assets/TicketDetailModalContext-DqyF0so-.js +14 -0
- package/client/dist/assets/Trans-SnMX4MhW.js +1 -0
- package/client/dist/assets/agent-BdT0SB1D.js +1 -0
- package/client/dist/assets/agent-Bh9K7iEt.js +1 -0
- package/client/dist/assets/agent-C4ibdFk9.js +1 -0
- package/client/dist/assets/agent-CSFY22Zo.js +1 -0
- package/client/dist/assets/agent-DAnqxUn8.js +1 -0
- package/client/dist/assets/agent-DTQiu0Mi.js +1 -0
- package/client/dist/assets/agent-T54WuR_Y.js +1 -0
- package/client/dist/assets/agent-i75WDZOA.js +1 -0
- package/client/dist/assets/browser-05vRDg_y.js +1 -0
- package/client/dist/assets/browser-Bulb-cB1.js +1 -0
- package/client/dist/assets/browser-C7f-Jsg1.js +1 -0
- package/client/dist/assets/browser-CG0PE1j_.js +1 -0
- package/client/dist/assets/browser-CMzaqAjo.js +1 -0
- package/client/dist/assets/browser-DMajYdNX.js +1 -0
- package/client/dist/assets/browser-DxcOHrdP.js +1 -0
- package/client/dist/assets/browser-ObE7xVo3.js +1 -0
- package/client/dist/assets/builder-765NeYTp.js +1 -0
- package/client/dist/assets/builder-8MG3V6ea.js +1 -0
- package/client/dist/assets/builder-B-1UBs5K.js +1 -0
- package/client/dist/assets/builder-BTiGnPT0.js +1 -0
- package/client/dist/assets/builder-Cx9Rz-7u.js +1 -0
- package/client/dist/assets/builder-DKFMELBl.js +1 -0
- package/client/dist/assets/builder-OcmKrC0h.js +1 -0
- package/client/dist/assets/builder-P9qVBXFZ.js +1 -0
- package/client/dist/assets/{button-CJqFnUOL.js → button-DCBqEaZl.js} +1 -1
- package/client/dist/assets/{code-CkUuW4m9.js → code-B37GRg9C.js} +1 -1
- package/client/dist/assets/{code-B0O8-p8k.js → code-BBOEZbf9.js} +1 -1
- package/client/dist/assets/{code-B1VLZDhS.js → code-BRm-jcyx.js} +1 -1
- package/client/dist/assets/{code-Bpj4Ddml.js → code-CF7w62CR.js} +1 -1
- package/client/dist/assets/{code-BykBZ5-e.js → code-CLuvJt2r.js} +1 -1
- package/client/dist/assets/{code-B86IMAfz.js → code-D0RCm59f.js} +1 -1
- package/client/dist/assets/code-DaZ-ZwCT.js +1 -0
- package/client/dist/assets/{code-cAqaqMTx.js → code-iIHkGaCL.js} +1 -1
- package/client/dist/assets/commands-0XjAftla.js +1 -0
- package/client/dist/assets/commands-BAWSYHjQ.js +1 -0
- package/client/dist/assets/commands-BBwowgiE.js +1 -0
- package/client/dist/assets/commands-BacjIZLN.js +1 -0
- package/client/dist/assets/commands-Bs62IAZ3.js +1 -0
- package/client/dist/assets/commands-CLPdh-Hh.js +1 -0
- package/client/dist/assets/commands-DlbFzgrp.js +1 -0
- package/client/dist/assets/commands-LjPSsi5F.js +1 -0
- package/client/dist/assets/common-B5AZNYMH.js +1 -0
- package/client/dist/assets/common-BFZ0c_vN.js +1 -0
- package/client/dist/assets/common-BrlwEnL7.js +1 -0
- package/client/dist/assets/common-BttQx3z4.js +1 -0
- package/client/dist/assets/common-CM65k_-e.js +1 -0
- package/client/dist/assets/common-D4mvFFlu.js +1 -0
- package/client/dist/assets/common-TZ9Ny96S.js +1 -0
- package/client/dist/assets/common-nFpSF4GC.js +1 -0
- package/client/dist/assets/dashboard-B14uYXkG.js +1 -0
- package/client/dist/assets/dashboard-BK_mez-z.js +1 -0
- package/client/dist/assets/dashboard-BfBir_6o.js +1 -0
- package/client/dist/assets/dashboard-Bt0cCzeG.js +1 -0
- package/client/dist/assets/dashboard-BwkjxI6H.js +1 -0
- package/client/dist/assets/{dashboard-C8WOEiPu.js → dashboard-C3bDKf7s.js} +1 -1
- package/client/dist/assets/dashboard-CdvjGfog.js +1 -0
- package/client/dist/assets/dashboard-fRrP0Kv0.js +1 -0
- package/client/dist/assets/dialog-SRw01_QU.js +45 -0
- package/client/dist/assets/dist-js-CJ_2XX1Y.js +1 -0
- package/client/dist/assets/formatDistanceToNow-DSj5DprF.js +1 -0
- package/client/dist/assets/getTimezoneOffsetInMilliseconds-YhD8rIEs.js +1 -0
- package/client/dist/assets/index-DwAmL7m5.css +2 -0
- package/client/dist/assets/index-EzrkUb8B.js +76 -0
- package/client/dist/assets/{jira-api-B1-MUehg.js → jira-api-CSCqGr-Q.js} +1 -1
- package/client/dist/assets/{jobs-CN_zv4Gr.js → jobs-B8rrzfyZ.js} +1 -1
- package/client/dist/assets/{jobs-DUlWSYq2.js → jobs-BS6RTurf.js} +1 -1
- package/client/dist/assets/{jobs-Bpks6doU.js → jobs-BTT0Bsks.js} +1 -1
- package/client/dist/assets/{jobs-Bgq-PZkC.js → jobs-CezU69AE.js} +1 -1
- package/client/dist/assets/{jobs-SwYS2M-B.js → jobs-ClFJae0s.js} +1 -1
- package/client/dist/assets/{jobs-BAls3bWq.js → jobs-DJSFIFVy.js} +1 -1
- package/client/dist/assets/{jobs-DhxKgWFq.js → jobs-DhgGr8vm.js} +1 -1
- package/client/dist/assets/{jobs-B_TeSQxb.js → jobs-DzMjUcaN.js} +1 -1
- package/client/dist/assets/mcp-6rMoZfEO.js +1 -0
- package/client/dist/assets/mcp-BD8sL5mP.js +1 -0
- package/client/dist/assets/mcp-BkcA3I5s.js +1 -0
- package/client/dist/assets/mcp-CCpSelOh.js +1 -0
- package/client/dist/assets/mcp-CUAN6ORK.js +1 -0
- package/client/dist/assets/mcp-CqbRxRTE.js +1 -0
- package/client/dist/assets/mcp-DP2YfVhO.js +1 -0
- package/client/dist/assets/mcp-DUVQamDz.js +1 -0
- package/client/dist/assets/narration-B-ynWTpn.js +1 -0
- package/client/dist/assets/narration-Bj_laZwg.js +1 -0
- package/client/dist/assets/narration-BxHpmMv_.js +1 -0
- package/client/dist/assets/narration-C1Px0-0j.js +1 -0
- package/client/dist/assets/narration-CAXhGbzM.js +1 -0
- package/client/dist/assets/narration-DQ1l_Kq6.js +1 -0
- package/client/dist/assets/narration-XISXwqZd.js +1 -0
- package/client/dist/assets/narration-kwzfglQo.js +1 -0
- package/client/dist/assets/nav-1-Hh2kOz.js +1 -0
- package/client/dist/assets/nav-B34QkESm.js +1 -0
- package/client/dist/assets/nav-Bap_UXAq.js +1 -0
- package/client/dist/assets/{nav-pYuXq7sf.js → nav-BwxXIB24.js} +1 -1
- package/client/dist/assets/nav-Ch-zHBG5.js +1 -0
- package/client/dist/assets/nav-CygC_WN-.js +1 -0
- package/client/dist/assets/{nav-iwjCg5FR.js → nav-KnnLQtOQ.js} +1 -1
- package/client/dist/assets/nav-m00uxYUG.js +1 -0
- package/client/dist/assets/packet-BQ2tXfJI.js +1 -0
- package/client/dist/assets/packet-BdpOJ1Cy.js +1 -0
- package/client/dist/assets/packet-C3Ievb9R.js +1 -0
- package/client/dist/assets/packet-C4zJIG0Y.js +1 -0
- package/client/dist/assets/packet-Cx-zRuWn.js +1 -0
- package/client/dist/assets/packet-DUTGX52M.js +1 -0
- package/client/dist/assets/packet-DhsKtUPB.js +1 -0
- package/client/dist/assets/packet-z3LJ8Zyy.js +1 -0
- package/client/dist/assets/project-repositories-DYhTvwQ1.js +1 -0
- package/client/dist/assets/provider-capabilities-B-wOHfLP.js +1 -0
- package/client/dist/assets/settings-BBt2xtDQ.js +1 -0
- package/client/dist/assets/settings-BJB9RKJj.js +1 -0
- package/client/dist/assets/settings-CHwQR2rB.js +1 -0
- package/client/dist/assets/settings-CrMi3kSi.js +1 -0
- package/client/dist/assets/settings-DKyhDGD1.js +1 -0
- package/client/dist/assets/settings-Deupji9c.js +1 -0
- package/client/dist/assets/settings-DqRMmwAM.js +1 -0
- package/client/dist/assets/settings-J4rj6vo-.js +1 -0
- package/client/dist/assets/setup-2xGgu4Sk.js +1 -0
- package/client/dist/assets/setup-Ba11l2JA.js +1 -0
- package/client/dist/assets/setup-BzW4kMok.js +1 -0
- package/client/dist/assets/setup-COzwYhkJ.js +1 -0
- package/client/dist/assets/setup-CRVULhtF.js +1 -0
- package/client/dist/assets/setup-DgvlxwpE.js +1 -0
- package/client/dist/assets/setup-Dw_wYeEY.js +1 -0
- package/client/dist/assets/setup-ZPtEeJPW.js +1 -0
- package/client/dist/assets/spending-BqbdPm_4.js +1 -0
- package/client/dist/assets/square-Ci-PFcFe.js +1 -0
- package/client/dist/assets/{terminal-BFRZnMPP.js → terminal-BAzpHtpY.js} +1 -1
- package/client/dist/assets/{terminal-IahE4LW9.js → terminal-C7dHeR3x.js} +1 -1
- package/client/dist/assets/{terminal-tITa81iz.js → terminal-CMla5Qsz.js} +1 -1
- package/client/dist/assets/{terminal-BZiIu3rf.js → terminal-CdNXH-n9.js} +1 -1
- package/client/dist/assets/{terminal-ScjyXNxQ.js → terminal-Cw5bZsmV.js} +1 -1
- package/client/dist/assets/{terminal-C2NAd7BW.js → terminal-DFUP4eNv.js} +1 -1
- package/client/dist/assets/{terminal-d7GadXbN.js → terminal-DGqljeK_.js} +1 -1
- package/client/dist/assets/{terminal-QpXZU2Bn.js → terminal-cU70NodO.js} +1 -1
- package/client/dist/assets/useDesktop-RSMTyy7V.js +1 -0
- package/client/dist/assets/useSharedWebSocket-BOdS5_Iz.js +2 -0
- package/client/dist/index.html +34 -35
- package/docs/README.md +3 -0
- package/docs/agent-live-steering.md +57 -0
- package/docs/ci-cd.md +57 -0
- package/docs/code-explorer.md +48 -0
- package/docs/codex.md +12 -1
- package/docs/features/detachable-mission-windows.md +59 -0
- package/docs/guide/de/integrations/5-mcp-server.md +13 -9
- package/docs/guide/de/integrations/6-agent-chat.md +1 -0
- package/docs/guide/en/integrations/5-mcp-server.md +13 -9
- package/docs/guide/en/integrations/6-agent-chat.md +1 -0
- package/docs/guide/es/integrations/5-mcp-server.md +13 -9
- package/docs/guide/es/integrations/6-agent-chat.md +1 -0
- package/docs/guide/fr/integrations/5-mcp-server.md +13 -9
- package/docs/guide/fr/integrations/6-agent-chat.md +1 -0
- package/docs/guide/it/integrations/5-mcp-server.md +13 -9
- package/docs/guide/it/integrations/6-agent-chat.md +1 -0
- package/docs/guide/ja/integrations/5-mcp-server.md +13 -9
- package/docs/guide/ja/integrations/6-agent-chat.md +1 -0
- package/docs/guide/pt/integrations/5-mcp-server.md +13 -9
- package/docs/guide/pt/integrations/6-agent-chat.md +1 -0
- package/docs/guide/zh/integrations/5-mcp-server.md +13 -9
- package/docs/guide/zh/integrations/6-agent-chat.md +1 -0
- package/docs/internals/README.md +1 -0
- package/docs/internals/browser-capture-performance.md +55 -3
- package/docs/internals/browser-login-popups.md +45 -0
- package/docs/internals/browser-native-retina-audit.md +39 -0
- package/docs/internals/ci-cd-audit.md +39 -0
- package/docs/internals/core-runtime-updates.md +68 -0
- package/docs/internals/embedded-browser-native-webview-evaluation.md +2 -0
- package/docs/internals/implementation-reliability-audit.md +93 -0
- package/docs/internals/interactive-jobs.md +110 -6
- package/docs/internals/mcp-mission-audit.md +76 -0
- package/docs/internals/project-builder.md +404 -42
- package/docs/internals/safe-pr-review-flow.md +52 -10
- package/docs/internals/startup-recovery-audit.md +58 -0
- package/docs/mcp.md +74 -41
- package/docs/mission-processes.md +59 -0
- package/docs/multi-repo-projects.md +51 -0
- package/docs/platforms/windows-parity.md +70 -0
- package/docs/platforms/windows.md +13 -11
- package/docs/running-pipelines.md +14 -0
- package/mcp-bridge/dist/specrails-mcp.js +7186 -0
- package/package.json +9 -4
- package/server/dist/agent-chat-manager.js +537 -68
- package/server/dist/agent-chat-router.js +87 -6
- package/server/dist/agent-context-resolver.js +129 -25
- package/server/dist/agent-defaults.js +19 -11
- package/server/dist/agent-input-store.js +206 -0
- package/server/dist/agent-mcp-config.js +54 -15
- package/server/dist/agent-operator-prompt.js +255 -65
- package/server/dist/agent-spec-framing.js +202 -0
- package/server/dist/agent-steering.js +218 -0
- package/server/dist/agent-store.js +153 -0
- package/server/dist/api-not-found.js +8 -0
- package/server/dist/background-process-control.js +164 -0
- package/server/dist/background-process-service.js +61 -0
- package/server/dist/background-process-store.js +242 -0
- package/server/dist/background-windows-bootstrap.js +238 -0
- package/server/dist/blueprint-chat-manager.js +476 -62
- package/server/dist/blueprint-commit.js +21 -1
- package/server/dist/blueprint-draft-parser.js +122 -17
- package/server/dist/blueprint-generation.js +238 -0
- package/server/dist/blueprint-operator-prompt.js +91 -63
- package/server/dist/blueprint-router.js +55 -3
- package/server/dist/blueprint-spec-fixtures.js +85 -0
- package/server/dist/blueprint-spec-quality.js +57 -16
- package/server/dist/blueprint-store.js +92 -1
- package/server/dist/browser-capture-manager.js +86 -30
- package/server/dist/browser-playwright.js +168 -45
- package/server/dist/browser-viewport.js +26 -0
- package/server/dist/chat-manager.js +23 -15
- package/server/dist/chromium-archive.cjs +83 -0
- package/server/dist/chromium-resolver.js +72 -64
- package/server/dist/code-activity.js +145 -0
- package/server/dist/code-explorer-router.js +714 -114
- package/server/dist/core-compat.js +28 -1
- package/server/dist/core-execution.js +111 -0
- package/server/dist/core-package.js +4 -16
- package/server/dist/core-runtime.js +202 -0
- package/server/dist/core-update-manager.js +161 -60
- package/server/dist/core-update-state.js +29 -0
- package/server/dist/db.js +66 -35
- package/server/dist/delivery-evidence.js +142 -39
- package/server/dist/desktop-db.js +239 -43
- package/server/dist/desktop-router.js +109 -9
- package/server/dist/dev-ports.js +8 -3
- package/server/dist/file-provenance.js +34 -22
- package/server/dist/file-story-manager.js +62 -64
- package/server/dist/file-story.js +118 -35
- package/server/dist/file-summary-generator.js +46 -5
- package/server/dist/file-summary-manager.js +355 -106
- package/server/dist/framework-manager.js +98 -110
- package/server/dist/framework-reseed.js +76 -10
- package/server/dist/git-diagnostics.js +2 -2
- package/server/dist/headroom-manager.js +38 -3
- package/server/dist/host-control.js +52 -0
- package/server/dist/index.js +43 -2
- package/server/dist/interactive-job-session.js +149 -16
- package/server/dist/internal-api.js +48 -0
- package/server/dist/jira/jira-materializer.js +1 -0
- package/server/dist/json-tolerant.js +174 -0
- package/server/dist/legacy-migration.js +4 -2
- package/server/dist/loop-command-catalog.js +83 -21
- package/server/dist/loop-constants.js +6 -1
- package/server/dist/loop-decider.js +11 -1
- package/server/dist/loop-executors.js +194 -45
- package/server/dist/loop-factory.js +11 -4
- package/server/dist/loop-graph.js +90 -4
- package/server/dist/loop-run-manager.js +301 -69
- package/server/dist/loop-shell-invocation.js +21 -0
- package/server/dist/loop-step-idle.js +55 -0
- package/server/dist/loop-templates.js +31 -7
- package/server/dist/mcp/agent-capability.js +37 -4
- package/server/dist/mcp/guide.js +149 -36
- package/server/dist/mcp/mcp-admin-router.js +20 -4
- package/server/dist/mcp/mcp-server.js +89 -43
- package/server/dist/mcp/mcp-tiers.js +6 -5
- package/server/dist/mcp/mcp-token.js +3 -1
- package/server/dist/mcp/resources.js +5 -4
- package/server/dist/mcp/tools/catalog.js +4 -0
- package/server/dist/mcp/tools/code.js +125 -14
- package/server/dist/mcp/tools/context.js +153 -0
- package/server/dist/mcp/tools/git.js +15 -5
- package/server/dist/mcp/tools/jobs.js +84 -71
- package/server/dist/mcp/tools/loops.js +17 -6
- package/server/dist/mcp/tools/meta.js +74 -27
- package/server/dist/mcp/tools/mission.js +29 -0
- package/server/dist/mcp/tools/projects.js +22 -6
- package/server/dist/mcp/tools/rails.js +48 -11
- package/server/dist/mcp/tools/specs.js +86 -14
- package/server/dist/mcp/tools/support.js +1 -1
- package/server/dist/mcp/tools/types.js +117 -43
- package/server/dist/mcp/tools/watch.js +144 -51
- package/server/dist/milestone-chain-store.js +164 -0
- package/server/dist/milestone-chain.js +487 -0
- package/server/dist/milestone-progress.js +348 -0
- package/server/dist/mobile/mobile-devices.js +10 -7
- package/server/dist/mobile/mobile-gateway.js +2 -0
- package/server/dist/mobile/mobile-missions.js +214 -0
- package/server/dist/mobile/mobile-redact.js +17 -3
- package/server/dist/mobile/mobile-router.js +46 -4
- package/server/dist/mobile/mobile-ws.js +49 -4
- package/server/dist/multi-repo-bases.js +56 -0
- package/server/dist/multi-repo-checkout.js +101 -0
- package/server/dist/multi-repo-delivery.js +100 -0
- package/server/dist/multi-repo-execution-store.js +65 -0
- package/server/dist/multi-repo-execution.js +453 -0
- package/server/dist/offline-assemble.js +10 -8
- package/server/dist/openspec-runtime-plugin-commands.json +18 -0
- package/server/dist/openspec-runtime-plugin.js +120 -0
- package/server/dist/plugins/codex-spawn.js +49 -0
- package/server/dist/plugins/serena/manifest.js +2 -2
- package/server/dist/plugins/serena/templates/instructions.md +16 -0
- package/server/dist/pr-publisher.js +1 -1
- package/server/dist/profiles-router.js +3 -18
- package/server/dist/project-code-discovery.js +142 -0
- package/server/dist/project-git.js +19 -21
- package/server/dist/project-registry.js +272 -20
- package/server/dist/project-repositories.js +157 -0
- package/server/dist/project-repository-provenance.js +31 -0
- package/server/dist/project-router-background-processes.js +105 -0
- package/server/dist/project-router-git.js +75 -16
- package/server/dist/project-router-jobs.js +2 -126
- package/server/dist/project-router-loop-runs.js +72 -2
- package/server/dist/project-router-tickets.js +53 -8
- package/server/dist/project-router.js +239 -38
- package/server/dist/provider-detection.js +18 -6
- package/server/dist/provider-limit.js +64 -0
- package/server/dist/providers/claude-adapter.js +26 -1
- package/server/dist/providers/claude-background-tasks.js +18 -0
- package/server/dist/providers/claude-live-session.js +383 -0
- package/server/dist/providers/codex-adapter.js +19 -0
- package/server/dist/providers/codex-live-session.js +545 -0
- package/server/dist/providers/gemini-adapter.js +1 -0
- package/server/dist/providers/live-session-types.js +12 -0
- package/server/dist/providers/live-session.js +13 -0
- package/server/dist/providers/terminal-result.js +24 -0
- package/server/dist/queue-manager.js +2 -0
- package/server/dist/rail-isolated-launch.js +141 -16
- package/server/dist/rail-pr-decision.js +364 -99
- package/server/dist/rail-pr-store.js +24 -10
- package/server/dist/rail-worktree-release.js +34 -2
- package/server/dist/rail-worktrees-store.js +4 -4
- package/server/dist/rails-router.js +249 -55
- package/server/dist/repo-lock.js +92 -17
- package/server/dist/review-packet.js +23 -13
- package/server/dist/schemas/file-summary.v1.json +59 -59
- package/server/dist/schemas/profile.v1.json +137 -137
- package/server/dist/setup-manager.js +52 -8
- package/server/dist/shell-integration/bash-shim.bash +45 -0
- package/server/dist/shell-integration/fish-shim.fish +36 -0
- package/server/dist/shell-integration/powershell-shim.ps1 +48 -0
- package/server/dist/shell-integration/zsh-shim.zsh +47 -0
- package/server/dist/smash-runner.js +1 -0
- package/server/dist/spawn-lifecycle.js +44 -27
- package/server/dist/spec-contract-prompt.js +143 -0
- package/server/dist/spec-draft-parser.js +6 -1
- package/server/dist/spec-models.js +1 -0
- package/server/dist/stuck-run-detector.js +1 -0
- package/server/dist/terminal-manager.js +21 -1
- package/server/dist/terminal-shell-integration.js +4 -1
- package/server/dist/transient-children.js +619 -230
- package/server/dist/util/cli-prompt.js +78 -5
- package/server/dist/util/sqlite-migrations.js +20 -0
- package/server/dist/util/win-spawn.js +18 -3
- package/server/dist/util/windows-provider-shell.js +29 -0
- package/server/dist/verification-sentinel.js +14 -0
- package/server/dist/vitest-setup.js +2 -0
- package/server/dist/windows-job-supervisor.js +133 -0
- package/server/dist/workspace-manager.js +9 -30
- package/server/dist/worktree-manager.js +17 -2
- package/server/dist/worktree-overlay.js +3 -1
- package/client/dist/assets/ActivityFeedPage-tAQcWs1R.js +0 -1
- package/client/dist/assets/AgentBrowserCapture-BX8BNae5.js +0 -1
- package/client/dist/assets/AgentModeAnalyticsPane-BVAqCL0K.js +0 -2
- package/client/dist/assets/AgentModeCodePane-9XfqQc9c.js +0 -2
- package/client/dist/assets/AgentModeJobsPane-BCjLsPhc.js +0 -1
- package/client/dist/assets/AgentsPage-C8J_42sm.js +0 -87
- package/client/dist/assets/AnalyticsPage-BtQuyfJm.js +0 -1
- package/client/dist/assets/CodePage-BZ2MwdCu.js +0 -2
- package/client/dist/assets/DesktopAnalyticsPage-Bh_29V4g.js +0 -1
- package/client/dist/assets/ExportDropdown-BrtfWzEG.js +0 -1
- package/client/dist/assets/InteractiveJobComposer-BCZiSfzc.js +0 -19
- package/client/dist/assets/JobDetailModal-NAJmuYIT.js +0 -1
- package/client/dist/assets/JobDetailPage-fE0ZycUX.js +0 -1
- package/client/dist/assets/JobsPage-z5M_HVJG.js +0 -1
- package/client/dist/assets/LoopBuilderPage-j1FQS47c.js +0 -7
- package/client/dist/assets/LoopPreviewModal-DhDJjdCe.js +0 -1
- package/client/dist/assets/LoopsPage-Ll7zORZb.js +0 -1
- package/client/dist/assets/MinimizedChatsContext-Bd3OqR_S.js +0 -1
- package/client/dist/assets/PluginsPage-DN4kGvJt.js +0 -2
- package/client/dist/assets/ProjectSettingsDialog-OgsgxjzF.js +0 -1
- package/client/dist/assets/ReviewPacketPage-DFog4sXn.js +0 -1
- package/client/dist/assets/TemplatePreviewModal-L2XxqiOD.js +0 -1
- package/client/dist/assets/TicketDetailModal-CsF-AWZX.js +0 -14
- package/client/dist/assets/Trans-Bsz9_gyq.js +0 -1
- package/client/dist/assets/agent-BQgKv_2F.js +0 -1
- package/client/dist/assets/agent-C1NRDJqL.js +0 -1
- package/client/dist/assets/agent-CLNleiy6.js +0 -1
- package/client/dist/assets/agent-CmceArkQ.js +0 -1
- package/client/dist/assets/agent-D3UxQ3SR.js +0 -1
- package/client/dist/assets/agent-DB1JeE64.js +0 -1
- package/client/dist/assets/agent-DXkHcQ-D.js +0 -1
- package/client/dist/assets/agent-DjJJJ2M8.js +0 -1
- package/client/dist/assets/auth-D_5bQv5W.js +0 -1
- package/client/dist/assets/ban-E4EaZe3a.js +0 -1
- package/client/dist/assets/bot-DzuuuC5R.js +0 -1
- package/client/dist/assets/browser-BgqHDMXP.js +0 -1
- package/client/dist/assets/browser-CsKJtrpv.js +0 -1
- package/client/dist/assets/browser-Cu58EpZU.js +0 -1
- package/client/dist/assets/browser-CvXOJm-j.js +0 -1
- package/client/dist/assets/browser-DOrFh1c7.js +0 -1
- package/client/dist/assets/browser-DQv_Jo7x.js +0 -1
- package/client/dist/assets/browser-LPgxvJu2.js +0 -1
- package/client/dist/assets/browser-eL-3MIEI.js +0 -1
- package/client/dist/assets/builder-0IWtosUm.js +0 -1
- package/client/dist/assets/builder-Bf2N6XWn.js +0 -1
- package/client/dist/assets/builder-BnqaaBkT.js +0 -1
- package/client/dist/assets/builder-BxxfGgAO.js +0 -1
- package/client/dist/assets/builder-C5IIgWsq.js +0 -1
- package/client/dist/assets/builder-CHuahItu.js +0 -1
- package/client/dist/assets/builder-CjWosXL_.js +0 -1
- package/client/dist/assets/builder-CpCIOoKj.js +0 -1
- package/client/dist/assets/code-CHPkOkDS.js +0 -1
- package/client/dist/assets/commands-6tYQih5m.js +0 -1
- package/client/dist/assets/commands-BK2QinXN.js +0 -1
- package/client/dist/assets/commands-C2Nca76h.js +0 -1
- package/client/dist/assets/commands-CKdLihNX.js +0 -1
- package/client/dist/assets/commands-CYIBTAlc.js +0 -1
- package/client/dist/assets/commands-CjUtYJIM.js +0 -1
- package/client/dist/assets/commands-Cq_u_8y9.js +0 -1
- package/client/dist/assets/commands-D_ZHYQug.js +0 -1
- package/client/dist/assets/common-6MLbD-jM.js +0 -1
- package/client/dist/assets/common-BKGEu4rm.js +0 -1
- package/client/dist/assets/common-BNbnv8cE.js +0 -1
- package/client/dist/assets/common-BqGWtDbt.js +0 -1
- package/client/dist/assets/common-DBtTzeYm.js +0 -1
- package/client/dist/assets/common-DEW3Nr8n.js +0 -1
- package/client/dist/assets/common-DHsv9N5d.js +0 -1
- package/client/dist/assets/common-mkrEcW-Y.js +0 -1
- package/client/dist/assets/dashboard-B9GhC5ku.js +0 -1
- package/client/dist/assets/dashboard-CBnv6zOI.js +0 -1
- package/client/dist/assets/dashboard-CaP0fU87.js +0 -1
- package/client/dist/assets/dashboard-D4ZZ4x84.js +0 -1
- package/client/dist/assets/dashboard-DmuTQBX8.js +0 -1
- package/client/dist/assets/dashboard-DynLYj3d.js +0 -1
- package/client/dist/assets/dashboard-zj4gGFJ1.js +0 -1
- package/client/dist/assets/dialog-CdWCb02j.js +0 -45
- package/client/dist/assets/dist-bVqfYmby.js +0 -1
- package/client/dist/assets/dist-js-CWeUdf5Z.js +0 -1
- package/client/dist/assets/formatDistanceToNow-DQCvMgUc.js +0 -1
- package/client/dist/assets/getTimezoneOffsetInMilliseconds-C16cxSe1.js +0 -1
- package/client/dist/assets/git-refresh-wTW0rO0Z.js +0 -1
- package/client/dist/assets/i18n-hnX2DPmo.js +0 -2
- package/client/dist/assets/index-DIhMEAVw.js +0 -66
- package/client/dist/assets/index-DwygcU7s.css +0 -2
- package/client/dist/assets/mcp-B8X8wBLu.js +0 -1
- package/client/dist/assets/mcp-C2AojZZN.js +0 -1
- package/client/dist/assets/mcp-D8BT78_L.js +0 -1
- package/client/dist/assets/mcp-Df5RWBNk.js +0 -1
- package/client/dist/assets/mcp-Dj7BgMQW.js +0 -1
- package/client/dist/assets/mcp-Q7iT7rcz.js +0 -1
- package/client/dist/assets/mcp-YlvNvJbW.js +0 -1
- package/client/dist/assets/mcp-mKFpTSRb.js +0 -1
- package/client/dist/assets/narration-4Z2VUhZw.js +0 -1
- package/client/dist/assets/narration-BGuiEDj3.js +0 -1
- package/client/dist/assets/narration-BuSoqRmv.js +0 -1
- package/client/dist/assets/narration-CB9t29fy.js +0 -1
- package/client/dist/assets/narration-CU3x8FHD.js +0 -1
- package/client/dist/assets/narration-CWeXhfP5.js +0 -1
- package/client/dist/assets/narration-DRaDnhT5.js +0 -1
- package/client/dist/assets/narration-DtEbQgCw.js +0 -1
- package/client/dist/assets/nav-ApJVg3xu.js +0 -1
- package/client/dist/assets/nav-BKNky1Bg.js +0 -1
- package/client/dist/assets/nav-C6vWWKYx.js +0 -1
- package/client/dist/assets/nav-CXJ6Aw89.js +0 -1
- package/client/dist/assets/nav-CsmFBIDf.js +0 -1
- package/client/dist/assets/nav-DjkmQJjd.js +0 -1
- package/client/dist/assets/packet-B1IxaL5f.js +0 -1
- package/client/dist/assets/packet-Bh5kkbiB.js +0 -1
- package/client/dist/assets/packet-By8fqYxW.js +0 -1
- package/client/dist/assets/packet-CZdvA44E.js +0 -1
- package/client/dist/assets/packet-CrjHRQCP.js +0 -1
- package/client/dist/assets/packet-Dwi2pTtx.js +0 -1
- package/client/dist/assets/packet-_-ZIa0IR.js +0 -1
- package/client/dist/assets/packet-n0W6E1Hn.js +0 -1
- package/client/dist/assets/play-DNlrawbS.js +0 -1
- package/client/dist/assets/provider-capabilities-Doej9lfa.js +0 -1
- package/client/dist/assets/settings-Bd0x4TBa.js +0 -1
- package/client/dist/assets/settings-C1-7cXzC.js +0 -1
- package/client/dist/assets/settings-CAFaT38b.js +0 -1
- package/client/dist/assets/settings-Ct3v1nW2.js +0 -1
- package/client/dist/assets/settings-DHh7_IF5.js +0 -1
- package/client/dist/assets/settings-DQXAmdcv.js +0 -1
- package/client/dist/assets/settings-guzlhpup.js +0 -1
- package/client/dist/assets/settings-sZfTPMEH.js +0 -1
- package/client/dist/assets/setup-3jWaoVUO.js +0 -1
- package/client/dist/assets/setup-BjgRneb2.js +0 -1
- package/client/dist/assets/setup-BzFIjv-f.js +0 -1
- package/client/dist/assets/setup-C4VfQsxE.js +0 -1
- package/client/dist/assets/setup-DfUkpDbN.js +0 -1
- package/client/dist/assets/setup-ECe90Pmu.js +0 -1
- package/client/dist/assets/setup-dlqC4Jmm.js +0 -1
- package/client/dist/assets/setup-jwfLMiM2.js +0 -1
- package/client/dist/assets/spending-EC9xqio-.js +0 -1
- package/client/dist/assets/useDesktop-BQVt8lFd.js +0 -1
|
@@ -35,7 +35,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.PR_DECISION_ACTIONS = void 0;
|
|
37
37
|
exports.isPrDecisionAction = isPrDecisionAction;
|
|
38
|
+
exports.actionAllowed = actionAllowed;
|
|
38
39
|
exports.executePrDecision = executePrDecision;
|
|
40
|
+
exports.sweepMergedChainAncestors = sweepMergedChainAncestors;
|
|
39
41
|
/**
|
|
40
42
|
* Execute a user's ask-first PR decision (safe-pr-review-flow) against its
|
|
41
43
|
* authoritative `rail_pr_deliveries` row: the actions both decision
|
|
@@ -52,9 +54,9 @@ exports.executePrDecision = executePrDecision;
|
|
|
52
54
|
* the on_review tickets to todo.
|
|
53
55
|
* poll-merge → `gh pr view` — MERGED promotes the tickets to done.
|
|
54
56
|
* merge-local→ the REMOTE-LESS acceptance path: merge the delivered branches
|
|
55
|
-
* into the integration branch
|
|
56
|
-
*
|
|
57
|
-
*
|
|
57
|
+
* into the integration branch after isolated assembly. Compatible
|
|
58
|
+
* local changes survive; another current branch stays untouched.
|
|
59
|
+
* Without it, a repo with no GitHub remote wedges
|
|
58
60
|
* forever in retry(local-only)/discard — no way to ACCEPT work.
|
|
59
61
|
*
|
|
60
62
|
* Every mutation rides a lease-owned compare-and-set (a raced concurrent
|
|
@@ -82,9 +84,12 @@ const workspace_resolution_1 = require("./workspace-resolution");
|
|
|
82
84
|
const agent_chat_registry_1 = require("./agent-chat-registry");
|
|
83
85
|
const ids_1 = require("./ids");
|
|
84
86
|
const repo_lock_1 = require("./repo-lock");
|
|
87
|
+
const integration_branch_1 = require("./integration-branch");
|
|
85
88
|
const rail_pr_ticket_effects_1 = require("./rail-pr-ticket-effects");
|
|
86
89
|
const pr_lifecycle_1 = require("./pr-lifecycle");
|
|
87
90
|
const rail_pr_recovery_git_1 = require("./rail-pr-recovery-git");
|
|
91
|
+
const milestone_chain_store_1 = require("./milestone-chain-store");
|
|
92
|
+
const rail_pr_store_2 = require("./rail-pr-store");
|
|
88
93
|
exports.PR_DECISION_ACTIONS = ['create-pr', 'publish', 'discard', 'dismiss', 'poll-merge', 'reopen', 'merge-local', 'acknowledge-no-changes', 'recover-and-retry'];
|
|
89
94
|
function safetyArchiveRecorder(deps, row) {
|
|
90
95
|
return (archive) => {
|
|
@@ -155,6 +160,12 @@ async function executePrDecision(deps, input) {
|
|
|
155
160
|
const row = (0, rail_pr_store_1.getPrDelivery)(deps.db, input.prDeliveryId);
|
|
156
161
|
if (!row)
|
|
157
162
|
return { status: 404, body: { error: 'Unknown prDeliveryId' } };
|
|
163
|
+
if (row.parent_delivery_id && deps.repositoryChildOf !== row.parent_delivery_id)
|
|
164
|
+
return { status: 409, body: { error: 'repository_delivery_requires_parent', parentDeliveryId: row.parent_delivery_id } };
|
|
165
|
+
if (!row.parent_delivery_id && row.execution_manifest) {
|
|
166
|
+
const { executeRepositoryGroupDecision } = await Promise.resolve().then(() => __importStar(require('./multi-repo-delivery')));
|
|
167
|
+
return executeRepositoryGroupDecision(deps, input);
|
|
168
|
+
}
|
|
158
169
|
// Compare-and-set pre-check: the caller decided against a snapshot — if the
|
|
159
170
|
// row moved on (the other surface answered first), it must reconcile, not act.
|
|
160
171
|
if (row.decision !== input.expectedDecision)
|
|
@@ -212,6 +223,8 @@ function casTransition(deps, row, next, patch = {}) {
|
|
|
212
223
|
return staleDecision(current?.decision ?? row.decision);
|
|
213
224
|
}
|
|
214
225
|
function casTransitionWithTicketEffect(deps, row, next, patch, effect) {
|
|
226
|
+
if (deps.repositoryChildOf)
|
|
227
|
+
return casTransition(deps, row, next, patch);
|
|
215
228
|
if (row.operation_token &&
|
|
216
229
|
(0, rail_pr_ticket_effects_1.transitionClaimedDecisionWithTicketEffect)(deps.db, row.id, row.decision, next, row.operation_token, patch, effect))
|
|
217
230
|
return null;
|
|
@@ -219,6 +232,8 @@ function casTransitionWithTicketEffect(deps, row, next, patch, effect) {
|
|
|
219
232
|
return staleDecision(current?.decision ?? row.decision);
|
|
220
233
|
}
|
|
221
234
|
function applyTerminalTicketEffect(deps, row, terminalDecision, cleanupWarnings) {
|
|
235
|
+
if (deps.repositoryChildOf)
|
|
236
|
+
return;
|
|
222
237
|
const result = (0, rail_pr_ticket_effects_1.applyRailPrTicketEffect)(deps, row.id);
|
|
223
238
|
if (result.ok)
|
|
224
239
|
return;
|
|
@@ -244,6 +259,8 @@ function finalizeTransition(deps, id) {
|
|
|
244
259
|
deferredFinalizations.add(id);
|
|
245
260
|
return snap;
|
|
246
261
|
}
|
|
262
|
+
if (deps.repositoryChildOf)
|
|
263
|
+
return snap;
|
|
247
264
|
deps.broadcast((0, rail_pr_store_1.toRailPrStateMessage)(deps.project.id, snap));
|
|
248
265
|
if (row.origin_conversation_id) {
|
|
249
266
|
const agent = (deps.agentChat ?? agent_chat_registry_1.getAgentChatManager)();
|
|
@@ -1616,6 +1633,7 @@ async function runDiscard(deps, row, opts = { removeNeedsReview: true }) {
|
|
|
1616
1633
|
});
|
|
1617
1634
|
if (conflict)
|
|
1618
1635
|
return conflict;
|
|
1636
|
+
pauseChainsOnDiscard(deps, row);
|
|
1619
1637
|
if (!preserveExternalReview)
|
|
1620
1638
|
applyTerminalTicketEffect(deps, row, 'discarded', cleanupWarnings);
|
|
1621
1639
|
finalizeTransition(deps, row.id);
|
|
@@ -1720,6 +1738,7 @@ async function settleMergedPr(deps, row) {
|
|
|
1720
1738
|
await releaseDeliveredRecoveryLineage(deps, row, row.delivery_sha);
|
|
1721
1739
|
applyTerminalTicketEffect(deps, row, 'merged', cleanupWarnings);
|
|
1722
1740
|
finalizeTransition(deps, row.id);
|
|
1741
|
+
await sweepMergedChainAncestors(deps, row);
|
|
1723
1742
|
return {
|
|
1724
1743
|
status: 200,
|
|
1725
1744
|
body: {
|
|
@@ -1908,38 +1927,102 @@ async function runMergeLocal(deps, row) {
|
|
|
1908
1927
|
return (0, repo_lock_1.withRepoLock)(deps.project.path, () => runMergeLocalLocked(deps, row));
|
|
1909
1928
|
}
|
|
1910
1929
|
async function runMergeLocalLocked(deps, row) {
|
|
1930
|
+
// A stacked milestone delivery targets the chain integration branch, not
|
|
1931
|
+
// the previous feature branch recorded as its PR base. Freeze that target
|
|
1932
|
+
// for the whole locked operation and every pre/post-advance check.
|
|
1933
|
+
const integrationTarget = mergeLocalTargetBranch(deps, row);
|
|
1911
1934
|
deps.assertAdmission?.();
|
|
1935
|
+
if (!(0, integration_branch_1.isValidBranchName)(integrationTarget) || integrationTarget === 'HEAD' || integrationTarget.startsWith('refs/')) {
|
|
1936
|
+
return {
|
|
1937
|
+
status: 409,
|
|
1938
|
+
body: { error: 'merge_local_blocked', reason: 'unresolved_head', base: integrationTarget,
|
|
1939
|
+
detail: 'Configure a local integration branch before accepting this delivery.' },
|
|
1940
|
+
};
|
|
1941
|
+
}
|
|
1942
|
+
const head = await deps.git.run(['symbolic-ref', '--quiet', 'HEAD'], deps.project.path);
|
|
1943
|
+
if (head.code !== 0 && head.code !== 1) {
|
|
1944
|
+
return { status: 409, body: { error: 'merge_local_blocked', reason: 'unresolved_head', base: integrationTarget } };
|
|
1945
|
+
}
|
|
1946
|
+
if (head.stdout.trim() === `refs/heads/${integrationTarget}`)
|
|
1947
|
+
return runMergeLocalIntoCheckout(deps, row, deps.project.path, integrationTarget);
|
|
1948
|
+
// Claim the designated base in an ordinary linked checkout. Git refuses if
|
|
1949
|
+
// another worktree already holds it; never update-ref a checked-out branch
|
|
1950
|
+
// behind its owner's back or switch/stash the user's current work.
|
|
1951
|
+
const safeId = row.id.replace(/[^A-Za-z0-9-]/g, '').slice(0, 64) || 'delivery';
|
|
1952
|
+
const target = path.join(deps.assemblyRoot ?? batchWorktreeRoot(deps.project.slug), `local-base-${safeId}-${(0, ids_1.newId)()}`);
|
|
1953
|
+
const added = await deps.git.run(['worktree', 'add', target, integrationTarget], deps.project.path);
|
|
1954
|
+
if (added.code !== 0) {
|
|
1955
|
+
return {
|
|
1956
|
+
status: 409,
|
|
1957
|
+
body: {
|
|
1958
|
+
error: 'merge_local_blocked', reason: 'integration_branch_busy', base: integrationTarget,
|
|
1959
|
+
detail: added.stderr.trim() || added.stdout.trim() || 'The integration branch could not be opened safely.',
|
|
1960
|
+
},
|
|
1961
|
+
};
|
|
1962
|
+
}
|
|
1963
|
+
let result;
|
|
1964
|
+
try {
|
|
1965
|
+
result = await runMergeLocalIntoCheckout(deps, row, target, integrationTarget);
|
|
1966
|
+
return result;
|
|
1967
|
+
}
|
|
1968
|
+
finally {
|
|
1969
|
+
// Even failed assembly must release the temporary base checkout. Non-force
|
|
1970
|
+
// removal preserves files if an external process wrote there during merge.
|
|
1971
|
+
let warning = null;
|
|
1972
|
+
try {
|
|
1973
|
+
const removed = await deps.git.run(['worktree', 'remove', target], deps.project.path);
|
|
1974
|
+
if (removed.code !== 0)
|
|
1975
|
+
warning = `integration worktree ${target}: ${removed.stderr.trim() || removed.stdout.trim() || `exit ${removed.code}`}`;
|
|
1976
|
+
}
|
|
1977
|
+
catch (err) {
|
|
1978
|
+
warning = `integration worktree ${target}: ${err instanceof Error ? err.message : String(err)}`;
|
|
1979
|
+
}
|
|
1980
|
+
if (warning) {
|
|
1981
|
+
safetyArchiveRecorder(deps, row)(target);
|
|
1982
|
+
const current = (0, rail_pr_store_1.getPrDelivery)(deps.db, row.id);
|
|
1983
|
+
if (current?.operation_token === row.operation_token) {
|
|
1984
|
+
const warnings = [...(0, rail_pr_store_1.toPrDeliverySnapshot)(current).cleanupWarnings, warning];
|
|
1985
|
+
casTransition(deps, current, current.decision, { cleanupWarnings: warnings, statusCode: 'cleanup_incomplete' });
|
|
1986
|
+
finalizeTransition(deps, row.id);
|
|
1987
|
+
if (result)
|
|
1988
|
+
result.body.cleanupWarnings = warnings;
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
async function runMergeLocalIntoCheckout(deps, row, targetRepo, integrationTarget) {
|
|
1912
1994
|
const snap = (0, rail_pr_store_1.toPrDeliverySnapshot)(row);
|
|
1913
1995
|
const cleanupWarnings = [];
|
|
1914
|
-
// Snapshot
|
|
1915
|
-
//
|
|
1916
|
-
|
|
1917
|
-
const
|
|
1918
|
-
|
|
1996
|
+
// Snapshot branch/HEAD before assembly. Local edits are allowed: the final
|
|
1997
|
+
// non-forced fast-forward leaves unrelated work intact and Git atomically
|
|
1998
|
+
// refuses any overwrite, including ignored files.
|
|
1999
|
+
const head = await deps.git.run(['symbolic-ref', '--quiet', 'HEAD'], targetRepo);
|
|
2000
|
+
const currentBranch = head.code === 0 ? head.stdout.trim().replace(/^refs\/heads\//, '') : '';
|
|
2001
|
+
if (currentBranch !== integrationTarget) {
|
|
1919
2002
|
return {
|
|
1920
2003
|
status: 409,
|
|
1921
|
-
body: { error: 'merge_local_blocked', reason: 'wrong_branch', current: currentBranch || null, base:
|
|
2004
|
+
body: { error: 'merge_local_blocked', reason: 'wrong_branch', current: currentBranch || null, base: integrationTarget },
|
|
1922
2005
|
};
|
|
1923
2006
|
}
|
|
1924
|
-
const status = await deps.git.run(['status', '--porcelain'],
|
|
1925
|
-
if (status.code !== 0
|
|
1926
|
-
return { status: 409, body: { error: 'merge_local_blocked', reason: '
|
|
2007
|
+
const status = await deps.git.run(['status', '--porcelain'], targetRepo);
|
|
2008
|
+
if (status.code !== 0) {
|
|
2009
|
+
return { status: 409, body: { error: 'merge_local_blocked', reason: 'status_unavailable', base: integrationTarget } };
|
|
1927
2010
|
}
|
|
1928
|
-
const startingHead = await deps.git.run(['rev-parse', '--verify', 'HEAD'],
|
|
2011
|
+
const startingHead = await deps.git.run(['rev-parse', '--verify', 'HEAD'], targetRepo);
|
|
1929
2012
|
const startingSha = startingHead.code === 0 ? startingHead.stdout.trim() : '';
|
|
1930
2013
|
if (!/^[0-9a-f]{40,64}$/i.test(startingSha)) {
|
|
1931
|
-
return { status: 409, body: { error: 'merge_local_blocked', reason: 'unresolved_head', base:
|
|
2014
|
+
return { status: 409, body: { error: 'merge_local_blocked', reason: 'unresolved_head', base: integrationTarget } };
|
|
1932
2015
|
}
|
|
1933
2016
|
// What to merge: immutable object ids only. The assembled head carries every
|
|
1934
2017
|
// unit already; without one, merge each settled unit's exact final SHA.
|
|
1935
2018
|
let toMerge;
|
|
1936
2019
|
let deliveryHeadSha = null;
|
|
1937
|
-
if (row.delivery_sha
|
|
2020
|
+
if (row.delivery_sha) {
|
|
2021
|
+
if (!COMMIT_SHA_RE.test(row.delivery_sha) || !(await commitObjectExists(deps, row.delivery_sha))) {
|
|
2022
|
+
return { status: 502, body: { error: 'merge_failed', detail: 'The verified delivery commit is unavailable; no branch fallback was merged.' } };
|
|
2023
|
+
}
|
|
1938
2024
|
deliveryHeadSha = row.delivery_sha;
|
|
1939
2025
|
}
|
|
1940
|
-
else if (row.branch) {
|
|
1941
|
-
deliveryHeadSha = await captureBranchSha(deps, row.branch);
|
|
1942
|
-
}
|
|
1943
2026
|
if (deliveryHeadSha) {
|
|
1944
2027
|
toMerge = [deliveryHeadSha];
|
|
1945
2028
|
}
|
|
@@ -1951,9 +2034,6 @@ async function runMergeLocalLocked(deps, row) {
|
|
|
1951
2034
|
if (!(await commitObjectExists(deps, sourceSha)))
|
|
1952
2035
|
sourceSha = null;
|
|
1953
2036
|
}
|
|
1954
|
-
else {
|
|
1955
|
-
sourceSha = await captureBranchSha(deps, unit.branch);
|
|
1956
|
-
}
|
|
1957
2037
|
if (!sourceSha) {
|
|
1958
2038
|
return {
|
|
1959
2039
|
status: 502,
|
|
@@ -1975,15 +2055,21 @@ async function runMergeLocalLocked(deps, row) {
|
|
|
1975
2055
|
const detail = (added.stderr.trim() || added.stdout.trim()).split('\n')[0] || `exit ${added.code}`;
|
|
1976
2056
|
return { status: 502, body: { error: 'merge_failed', detail: `creating isolated assembly: ${detail}` } };
|
|
1977
2057
|
}
|
|
2058
|
+
let assemblyRemovalAttempted = false;
|
|
1978
2059
|
const removeAssembly = async () => {
|
|
2060
|
+
if (assemblyRemovalAttempted)
|
|
2061
|
+
return;
|
|
2062
|
+
assemblyRemovalAttempted = true;
|
|
1979
2063
|
try {
|
|
1980
|
-
const removed = await deps.git.run(['worktree', 'remove',
|
|
2064
|
+
const removed = await deps.git.run(['worktree', 'remove', assemblyPath], deps.project.path);
|
|
1981
2065
|
if (removed.code !== 0) {
|
|
1982
2066
|
cleanupWarnings.push(`assembly worktree ${assemblyPath}: ${(removed.stderr.trim() || removed.stdout.trim()).split('\n')[0] || `exit ${removed.code}`}`);
|
|
2067
|
+
safetyArchiveRecorder(deps, row)(assemblyPath);
|
|
1983
2068
|
}
|
|
1984
2069
|
}
|
|
1985
2070
|
catch (err) {
|
|
1986
2071
|
cleanupWarnings.push(`assembly worktree ${assemblyPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
2072
|
+
safetyArchiveRecorder(deps, row)(assemblyPath);
|
|
1987
2073
|
}
|
|
1988
2074
|
};
|
|
1989
2075
|
const persistAssemblyFailure = (detail) => {
|
|
@@ -1995,97 +2081,136 @@ async function runMergeLocalLocked(deps, row) {
|
|
|
1995
2081
|
finalizeTransition(deps, row.id);
|
|
1996
2082
|
return null;
|
|
1997
2083
|
};
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2084
|
+
try {
|
|
2085
|
+
for (const branch of toMerge) {
|
|
2086
|
+
const r = await deps.git.run(['merge', '--no-ff', '--no-edit', branch], assemblyPath);
|
|
2087
|
+
if (r.code !== 0) {
|
|
2088
|
+
try {
|
|
2089
|
+
const aborted = await deps.git.run(['merge', '--abort'], assemblyPath);
|
|
2090
|
+
if (aborted.code !== 0)
|
|
2091
|
+
cleanupWarnings.push(`assembly abort ${branch}: exit ${aborted.code}`);
|
|
2092
|
+
}
|
|
2093
|
+
catch (err) {
|
|
2094
|
+
cleanupWarnings.push(`assembly abort ${branch}: ${err instanceof Error ? err.message : String(err)}`);
|
|
2095
|
+
}
|
|
2096
|
+
await removeAssembly();
|
|
2097
|
+
const detail = (r.stderr.trim() || r.stdout.trim()).split('\n')[0] || `exit ${r.code}`;
|
|
2098
|
+
const conflict = persistAssemblyFailure(`merging '${branch}': ${detail}`);
|
|
2099
|
+
if (conflict)
|
|
2100
|
+
return conflict;
|
|
2101
|
+
return { status: 502, body: { error: 'merge_failed', detail: `merging '${branch}': ${detail}` } };
|
|
2005
2102
|
}
|
|
2006
|
-
|
|
2007
|
-
|
|
2103
|
+
}
|
|
2104
|
+
const assembled = await deps.git.run(['rev-parse', '--verify', 'HEAD'], assemblyPath);
|
|
2105
|
+
const assembledSha = assembled.code === 0 ? assembled.stdout.trim() : '';
|
|
2106
|
+
if (!/^[0-9a-f]{40,64}$/i.test(assembledSha)) {
|
|
2107
|
+
await removeAssembly();
|
|
2108
|
+
const detail = 'isolated assembly produced no verifiable HEAD';
|
|
2109
|
+
const conflict = persistAssemblyFailure(detail);
|
|
2110
|
+
if (conflict)
|
|
2111
|
+
return conflict;
|
|
2112
|
+
return { status: 502, body: { error: 'merge_failed', detail } };
|
|
2113
|
+
}
|
|
2114
|
+
const [finalBranch, finalStatus, finalHead] = await Promise.all([
|
|
2115
|
+
deps.git.run(['symbolic-ref', '--quiet', 'HEAD'], targetRepo),
|
|
2116
|
+
deps.git.run(['status', '--porcelain'], targetRepo),
|
|
2117
|
+
deps.git.run(['rev-parse', '--verify', 'HEAD'], targetRepo),
|
|
2118
|
+
]);
|
|
2119
|
+
const changedReason = finalBranch.code !== 0 || finalBranch.stdout.trim() !== `refs/heads/${integrationTarget}`
|
|
2120
|
+
? 'wrong_branch'
|
|
2121
|
+
: finalStatus.code !== 0
|
|
2122
|
+
? 'status_unavailable'
|
|
2123
|
+
: finalHead.code !== 0 || finalHead.stdout.trim() !== startingSha
|
|
2124
|
+
? 'head_changed'
|
|
2125
|
+
: null;
|
|
2126
|
+
if (changedReason) {
|
|
2127
|
+
await removeAssembly();
|
|
2128
|
+
if (cleanupWarnings.length > 0) {
|
|
2129
|
+
const conflict = casTransition(deps, row, row.decision, { cleanupWarnings });
|
|
2130
|
+
if (conflict)
|
|
2131
|
+
return conflict;
|
|
2132
|
+
finalizeTransition(deps, row.id);
|
|
2008
2133
|
}
|
|
2134
|
+
return { status: 409, body: { error: 'merge_local_blocked', reason: changedReason, base: integrationTarget } };
|
|
2135
|
+
}
|
|
2136
|
+
const advanced = await deps.git.run(['merge', '--ff-only', '--no-overwrite-ignore', assembledSha], targetRepo);
|
|
2137
|
+
if (advanced.code !== 0) {
|
|
2009
2138
|
await removeAssembly();
|
|
2010
|
-
|
|
2011
|
-
|
|
2139
|
+
if (/would be overwritten|local changes|untracked working tree/i.test(`${advanced.stderr}\n${advanced.stdout}`)) {
|
|
2140
|
+
return {
|
|
2141
|
+
status: 409,
|
|
2142
|
+
body: {
|
|
2143
|
+
error: 'merge_local_blocked', reason: 'dirty', base: integrationTarget,
|
|
2144
|
+
detail: (advanced.stderr.trim() || advanced.stdout.trim()).slice(0, 1200),
|
|
2145
|
+
},
|
|
2146
|
+
};
|
|
2147
|
+
}
|
|
2148
|
+
const detail = (advanced.stderr.trim() || advanced.stdout.trim()).split('\n')[0] || `exit ${advanced.code}`;
|
|
2149
|
+
const conflict = persistAssemblyFailure(`advancing '${integrationTarget}': ${detail}`);
|
|
2012
2150
|
if (conflict)
|
|
2013
2151
|
return conflict;
|
|
2014
|
-
return { status: 502, body: { error: 'merge_failed', detail: `
|
|
2152
|
+
return { status: 502, body: { error: 'merge_failed', detail: `advancing '${integrationTarget}': ${detail}` } };
|
|
2015
2153
|
}
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
? 'wrong_branch'
|
|
2034
|
-
: finalStatus.code !== 0 || finalStatus.stdout.trim() !== ''
|
|
2035
|
-
? 'dirty'
|
|
2036
|
-
: finalHead.code !== 0 || finalHead.stdout.trim() !== startingSha
|
|
2037
|
-
? 'head_changed'
|
|
2038
|
-
: null;
|
|
2039
|
-
if (changedReason) {
|
|
2040
|
-
await removeAssembly();
|
|
2041
|
-
if (cleanupWarnings.length > 0) {
|
|
2042
|
-
const conflict = casTransition(deps, row, row.decision, { cleanupWarnings });
|
|
2154
|
+
const [acceptedBranch, acceptedHead, acceptedBase] = await Promise.all([
|
|
2155
|
+
deps.git.run(['symbolic-ref', '--quiet', 'HEAD'], targetRepo),
|
|
2156
|
+
deps.git.run(['rev-parse', '--verify', 'HEAD'], targetRepo),
|
|
2157
|
+
deps.git.run(['rev-parse', '--verify', `refs/heads/${integrationTarget}`], deps.project.path),
|
|
2158
|
+
]);
|
|
2159
|
+
if (acceptedBranch.code !== 0 || acceptedBranch.stdout.trim() !== `refs/heads/${integrationTarget}` ||
|
|
2160
|
+
acceptedHead.code !== 0 || acceptedHead.stdout.trim().toLowerCase() !== assembledSha.toLowerCase() ||
|
|
2161
|
+
acceptedBase.code !== 0 || acceptedBase.stdout.trim().toLowerCase() !== assembledSha.toLowerCase()) {
|
|
2162
|
+
// An external checkout/ref edit can race the final preflight. A successful
|
|
2163
|
+
// Git exit is insufficient evidence that the authorized base was updated.
|
|
2164
|
+
// Preserve the assembled object and all source worktrees/branches for a
|
|
2165
|
+
// retry; never roll back the external edit or mark tickets done.
|
|
2166
|
+
assemblyRemovalAttempted = true;
|
|
2167
|
+
safetyArchiveRecorder(deps, row)(assemblyPath);
|
|
2168
|
+
const detail = `The integration checkout changed during the final advance. The assembled result is preserved at ${assemblyPath}.`;
|
|
2169
|
+
cleanupWarnings.push(detail);
|
|
2170
|
+
const conflict = casTransition(deps, row, row.decision, { cleanupWarnings, statusCode: 'delivery_failed', statusDetail: detail });
|
|
2043
2171
|
if (conflict)
|
|
2044
2172
|
return conflict;
|
|
2045
2173
|
finalizeTransition(deps, row.id);
|
|
2174
|
+
return { status: 409, body: { error: 'merge_local_blocked', reason: 'head_changed', base: integrationTarget, detail } };
|
|
2046
2175
|
}
|
|
2047
|
-
return { status: 409, body: { error: 'merge_local_blocked', reason: changedReason, base: row.base_branch } };
|
|
2048
|
-
}
|
|
2049
|
-
const advanced = await deps.git.run(['merge', '--ff-only', assembledSha], deps.project.path);
|
|
2050
|
-
if (advanced.code !== 0) {
|
|
2051
2176
|
await removeAssembly();
|
|
2052
|
-
|
|
2053
|
-
|
|
2177
|
+
// Post-advance sweep: the work now lives on
|
|
2178
|
+
// the integration branch, so the launch's worktrees + branches are spent.
|
|
2179
|
+
cleanupWarnings.push(...await (0, rail_worktree_release_1.releaseRailWorktrees)({
|
|
2180
|
+
db: deps.db,
|
|
2181
|
+
git: deps.git,
|
|
2182
|
+
repoDir: deps.project.path,
|
|
2183
|
+
worktreeIds: releasableWorktreeIds(deps, snap),
|
|
2184
|
+
state: 'merged',
|
|
2185
|
+
expectedHeadByBranch: (0, rail_worktree_release_1.durableBranchHeads)(snap.branches),
|
|
2186
|
+
overlayEvidenceByBranch: (0, rail_worktree_release_1.durableOverlayCleanupEvidence)(snap.branches),
|
|
2187
|
+
settlementIgnoredByBranch: (0, rail_worktree_release_1.durableSettlementIgnoredPaths)(snap.branches),
|
|
2188
|
+
onSafetyArchive: safetyArchiveRecorder(deps, row),
|
|
2189
|
+
}));
|
|
2190
|
+
await deleteOwnedBranchesIfUnchanged(deps, row, snap, cleanupWarnings);
|
|
2191
|
+
const conflict = casTransitionWithTicketEffect(deps, row, 'merged', {
|
|
2192
|
+
deliveryOutcome: 'delivered',
|
|
2193
|
+
statusCode: cleanupWarnings.length > 0 ? 'cleanup_incomplete' : 'merged',
|
|
2194
|
+
cleanupWarnings,
|
|
2195
|
+
}, {
|
|
2196
|
+
deliveryId: row.id,
|
|
2197
|
+
ticketIds: snap.ticketIds,
|
|
2198
|
+
targetStatus: 'done',
|
|
2199
|
+
jiraAction: 'merged',
|
|
2200
|
+
prUrl: null,
|
|
2201
|
+
});
|
|
2054
2202
|
if (conflict)
|
|
2055
2203
|
return conflict;
|
|
2056
|
-
|
|
2204
|
+
applyTerminalTicketEffect(deps, row, 'merged', cleanupWarnings);
|
|
2205
|
+
finalizeTransition(deps, row.id);
|
|
2206
|
+
await sweepMergedChainAncestors(deps, row);
|
|
2207
|
+
return { status: 200, body: { ok: true, decision: 'merged', merged: true, local: true } };
|
|
2208
|
+
}
|
|
2209
|
+
finally {
|
|
2210
|
+
// Runner exceptions (spawn failures/timeouts) must release the temporary
|
|
2211
|
+
// assembly as well. A failed non-force removal leaves a recorded archive.
|
|
2212
|
+
await removeAssembly();
|
|
2057
2213
|
}
|
|
2058
|
-
await removeAssembly();
|
|
2059
|
-
// Post-advance sweep: the work now lives on
|
|
2060
|
-
// the integration branch, so the launch's worktrees + branches are spent.
|
|
2061
|
-
cleanupWarnings.push(...await (0, rail_worktree_release_1.releaseRailWorktrees)({
|
|
2062
|
-
db: deps.db,
|
|
2063
|
-
git: deps.git,
|
|
2064
|
-
repoDir: deps.project.path,
|
|
2065
|
-
worktreeIds: releasableWorktreeIds(deps, snap),
|
|
2066
|
-
state: 'merged',
|
|
2067
|
-
expectedHeadByBranch: (0, rail_worktree_release_1.durableBranchHeads)(snap.branches),
|
|
2068
|
-
overlayEvidenceByBranch: (0, rail_worktree_release_1.durableOverlayCleanupEvidence)(snap.branches),
|
|
2069
|
-
settlementIgnoredByBranch: (0, rail_worktree_release_1.durableSettlementIgnoredPaths)(snap.branches),
|
|
2070
|
-
onSafetyArchive: safetyArchiveRecorder(deps, row),
|
|
2071
|
-
}));
|
|
2072
|
-
await deleteOwnedBranchesIfUnchanged(deps, row, snap, cleanupWarnings);
|
|
2073
|
-
const conflict = casTransitionWithTicketEffect(deps, row, 'merged', {
|
|
2074
|
-
deliveryOutcome: 'delivered',
|
|
2075
|
-
statusCode: cleanupWarnings.length > 0 ? 'cleanup_incomplete' : 'merged',
|
|
2076
|
-
cleanupWarnings,
|
|
2077
|
-
}, {
|
|
2078
|
-
deliveryId: row.id,
|
|
2079
|
-
ticketIds: snap.ticketIds,
|
|
2080
|
-
targetStatus: 'done',
|
|
2081
|
-
jiraAction: 'merged',
|
|
2082
|
-
prUrl: null,
|
|
2083
|
-
});
|
|
2084
|
-
if (conflict)
|
|
2085
|
-
return conflict;
|
|
2086
|
-
applyTerminalTicketEffect(deps, row, 'merged', cleanupWarnings);
|
|
2087
|
-
finalizeTransition(deps, row.id);
|
|
2088
|
-
return { status: 200, body: { ok: true, decision: 'merged', merged: true, local: true } };
|
|
2089
2214
|
}
|
|
2090
2215
|
function resolveTicketFile(deps) {
|
|
2091
2216
|
if (deps.ticketFile)
|
|
@@ -2093,3 +2218,143 @@ function resolveTicketFile(deps) {
|
|
|
2093
2218
|
const exec = (0, workspace_resolution_1.resolveProjectExecution)({ slug: deps.project.slug, path: deps.project.path });
|
|
2094
2219
|
return exec.relocated ? exec.ticketsPath : (0, ticket_store_1.resolveTicketStoragePath)(deps.project.path);
|
|
2095
2220
|
}
|
|
2221
|
+
// ─── Milestone chain hooks (premium-milestone-progress D4) ───────────────────
|
|
2222
|
+
/** Decisions a stacked sibling can sit at while its head is already merged. */
|
|
2223
|
+
const SWEEPABLE_DECISIONS = new Set(['on_review', 'pr_draft', 'pr_ready', 'pr_closed']);
|
|
2224
|
+
/**
|
|
2225
|
+
* Merging a STACKED delivery also lands every earlier chunk it was built on.
|
|
2226
|
+
* Walk the chain(s) this delivery belongs to and settle each still-undecided
|
|
2227
|
+
* sibling whose delivered head commit is PROVABLY an ancestor of the chain's
|
|
2228
|
+
* integration branch (`git merge-base --is-ancestor`) as `merged` — the same
|
|
2229
|
+
* CAS + ticket effect (`done`) + Jira hook a direct merge runs. Chain-local by
|
|
2230
|
+
* design; siblings whose head is not an ancestor are left untouched. Returns
|
|
2231
|
+
* the swept delivery ids. Never throws.
|
|
2232
|
+
*/
|
|
2233
|
+
async function sweepMergedChainAncestors(deps, mergedRow) {
|
|
2234
|
+
const swept = [];
|
|
2235
|
+
if (deps.repositoryChildOf)
|
|
2236
|
+
return swept;
|
|
2237
|
+
let chains;
|
|
2238
|
+
try {
|
|
2239
|
+
chains = (0, milestone_chain_store_1.listChainsTouchingDelivery)(deps.db, mergedRow.id);
|
|
2240
|
+
}
|
|
2241
|
+
catch {
|
|
2242
|
+
return swept;
|
|
2243
|
+
}
|
|
2244
|
+
for (const chain of chains) {
|
|
2245
|
+
const integration = chain.integration_branch;
|
|
2246
|
+
if (!integration)
|
|
2247
|
+
continue;
|
|
2248
|
+
for (const entry of (0, milestone_chain_store_1.parseLaunched)(chain)) {
|
|
2249
|
+
if (!entry.deliveryId || entry.deliveryId === mergedRow.id || swept.includes(entry.deliveryId))
|
|
2250
|
+
continue;
|
|
2251
|
+
const other = (0, rail_pr_store_2.getPrDelivery)(deps.db, entry.deliveryId);
|
|
2252
|
+
if (!other || (0, rail_pr_store_2.isTerminalPrDecision)(other.decision) || !SWEEPABLE_DECISIONS.has(other.decision))
|
|
2253
|
+
continue;
|
|
2254
|
+
const snap = (0, rail_pr_store_2.toPrDeliverySnapshot)(other);
|
|
2255
|
+
// An assembled delivery SHA proves the whole chunk. Without that head,
|
|
2256
|
+
// every unit needs its own immutable proof; accepting just the first
|
|
2257
|
+
// ancestor could mark unmerged tickets done and delete their branches.
|
|
2258
|
+
const requiredShas = snap.deliverySha ? [snap.deliverySha] : snap.units.map((unit) => unit.finalSha);
|
|
2259
|
+
const validShas = requiredShas.filter((sha) => typeof sha === 'string' && COMMIT_SHA_RE.test(sha));
|
|
2260
|
+
if (requiredShas.length === 0 || validShas.length !== requiredShas.length)
|
|
2261
|
+
continue;
|
|
2262
|
+
let allAncestors = true;
|
|
2263
|
+
try {
|
|
2264
|
+
for (const sha of new Set(validShas)) {
|
|
2265
|
+
const r = await deps.git.run(['merge-base', '--is-ancestor', sha, integration], deps.project.path);
|
|
2266
|
+
if (r.code !== 0) {
|
|
2267
|
+
allAncestors = false;
|
|
2268
|
+
break;
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
catch {
|
|
2273
|
+
allAncestors = false;
|
|
2274
|
+
}
|
|
2275
|
+
if (!allAncestors)
|
|
2276
|
+
continue;
|
|
2277
|
+
const token = (0, ids_1.newId)();
|
|
2278
|
+
if (!(0, rail_pr_store_2.claimPrDeliveryOperation)(deps.db, other.id, other.decision, 'poll-merge', token))
|
|
2279
|
+
continue;
|
|
2280
|
+
let released = false;
|
|
2281
|
+
try {
|
|
2282
|
+
const claimed = { ...other, operation_token: token };
|
|
2283
|
+
const cleanupWarnings = await (0, rail_worktree_release_1.releaseRailWorktrees)({
|
|
2284
|
+
db: deps.db, git: deps.git, repoDir: deps.project.path,
|
|
2285
|
+
worktreeIds: releasableWorktreeIds(deps, snap), state: 'merged',
|
|
2286
|
+
expectedHeadByBranch: (0, rail_worktree_release_1.durableBranchHeads)(snap.branches),
|
|
2287
|
+
overlayEvidenceByBranch: (0, rail_worktree_release_1.durableOverlayCleanupEvidence)(snap.branches),
|
|
2288
|
+
settlementIgnoredByBranch: (0, rail_worktree_release_1.durableSettlementIgnoredPaths)(snap.branches),
|
|
2289
|
+
onSafetyArchive: safetyArchiveRecorder(deps, claimed),
|
|
2290
|
+
});
|
|
2291
|
+
await deleteOwnedBranchesIfUnchanged(deps, claimed, snap, cleanupWarnings);
|
|
2292
|
+
const conflict = casTransitionWithTicketEffect(deps, claimed, 'merged', {
|
|
2293
|
+
deliveryOutcome: 'delivered',
|
|
2294
|
+
statusCode: cleanupWarnings.length > 0 ? 'cleanup_incomplete' : 'merged',
|
|
2295
|
+
statusDetail: `merged as part of ${mergedRow.rail_key}`,
|
|
2296
|
+
cleanupWarnings,
|
|
2297
|
+
}, {
|
|
2298
|
+
deliveryId: claimed.id,
|
|
2299
|
+
ticketIds: snap.ticketIds,
|
|
2300
|
+
targetStatus: 'done',
|
|
2301
|
+
jiraAction: 'merged',
|
|
2302
|
+
prUrl: claimed.pr_url,
|
|
2303
|
+
});
|
|
2304
|
+
if (conflict)
|
|
2305
|
+
continue;
|
|
2306
|
+
applyTerminalTicketEffect(deps, claimed, 'merged', cleanupWarnings);
|
|
2307
|
+
// Release BEFORE finalizing: a row still holding a lease defers its
|
|
2308
|
+
// broadcast to the lease owner's finally, which here would never come.
|
|
2309
|
+
(0, rail_pr_store_2.releasePrDeliveryOperation)(deps.db, other.id, token);
|
|
2310
|
+
released = true;
|
|
2311
|
+
finalizeTransition(deps, other.id);
|
|
2312
|
+
swept.push(other.id);
|
|
2313
|
+
}
|
|
2314
|
+
catch (err) {
|
|
2315
|
+
console.error(`[milestone-chain] ancestor sweep failed for ${other.id}:`, err);
|
|
2316
|
+
}
|
|
2317
|
+
finally {
|
|
2318
|
+
if (!released)
|
|
2319
|
+
(0, rail_pr_store_2.releasePrDeliveryOperation)(deps.db, other.id, token);
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
return swept;
|
|
2324
|
+
}
|
|
2325
|
+
/** A discarded delivery that a chain's later chunks build on pauses those
|
|
2326
|
+
* chains (`head_discarded`, head rewound to the previous chunk's branch). */
|
|
2327
|
+
function pauseChainsOnDiscard(deps, row) {
|
|
2328
|
+
if (deps.repositoryChildOf)
|
|
2329
|
+
return;
|
|
2330
|
+
try {
|
|
2331
|
+
const branchOf = (id) => {
|
|
2332
|
+
const other = (0, rail_pr_store_2.getPrDelivery)(deps.db, id);
|
|
2333
|
+
if (!other)
|
|
2334
|
+
return null;
|
|
2335
|
+
const s = (0, rail_pr_store_2.toPrDeliverySnapshot)(other);
|
|
2336
|
+
return s.branch ?? s.units.find((u) => u.succeeded && u.branch)?.branch ?? null;
|
|
2337
|
+
};
|
|
2338
|
+
const paused = (0, milestone_chain_store_1.pauseChainsForDiscardedHead)(deps.db, row.id, branchOf);
|
|
2339
|
+
for (const chain of paused) {
|
|
2340
|
+
deps.broadcast({ type: 'milestone.chain_changed', projectId: deps.project.id, chain: (0, milestone_chain_store_1.toChainSnapshot)(chain), timestamp: new Date().toISOString() });
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
catch (err) {
|
|
2344
|
+
console.error('[milestone-chain] discard hook failed:', err);
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
/** Local-integration target: a chain-launched (stacked) delivery integrates
|
|
2348
|
+
* into the chain's integration branch, never into its feature base. */
|
|
2349
|
+
function mergeLocalTargetBranch(deps, row) {
|
|
2350
|
+
if (deps.repositoryChildOf)
|
|
2351
|
+
return deps.repositoryIntegrationBranch ?? row.base_branch;
|
|
2352
|
+
try {
|
|
2353
|
+
for (const chain of (0, milestone_chain_store_1.listChainsTouchingDelivery)(deps.db, row.id)) {
|
|
2354
|
+
if (chain.integration_branch)
|
|
2355
|
+
return chain.integration_branch;
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
catch { /* fall through */ }
|
|
2359
|
+
return row.base_branch;
|
|
2360
|
+
}
|