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
|
@@ -14,6 +14,7 @@ const loop_runs_store_1 = require("./loop-runs-store");
|
|
|
14
14
|
const providers_1 = require("./providers");
|
|
15
15
|
const runtime_1 = require("./providers/runtime");
|
|
16
16
|
const agent_defaults_1 = require("./agent-defaults");
|
|
17
|
+
const profile_manager_1 = require("./profile-manager");
|
|
17
18
|
const spec_models_1 = require("./spec-models");
|
|
18
19
|
const workspace_resolution_1 = require("./workspace-resolution");
|
|
19
20
|
const loop_factory_1 = require("./loop-factory");
|
|
@@ -29,9 +30,15 @@ const rail_pr_decision_1 = require("./rail-pr-decision");
|
|
|
29
30
|
const active_pr_continuation_1 = require("./active-pr-continuation");
|
|
30
31
|
const rail_isolated_launch_1 = require("./rail-isolated-launch");
|
|
31
32
|
const worktree_manager_1 = require("./worktree-manager");
|
|
33
|
+
const integration_branch_1 = require("./integration-branch");
|
|
32
34
|
const rail_worktree_release_1 = require("./rail-worktree-release");
|
|
33
35
|
const project_git_1 = require("./project-git");
|
|
34
36
|
const pr_publisher_1 = require("./pr-publisher");
|
|
37
|
+
const project_repositories_1 = require("./project-repositories");
|
|
38
|
+
const multi_repo_checkout_1 = require("./multi-repo-checkout");
|
|
39
|
+
const multi_repo_bases_1 = require("./multi-repo-bases");
|
|
40
|
+
const multi_repo_execution_1 = require("./multi-repo-execution");
|
|
41
|
+
const multi_repo_execution_store_1 = require("./multi-repo-execution-store");
|
|
35
42
|
const ids_1 = require("./ids");
|
|
36
43
|
const repo_lock_1 = require("./repo-lock");
|
|
37
44
|
const agent_chat_registry_1 = require("./agent-chat-registry");
|
|
@@ -40,7 +47,12 @@ const VALID_MODES = new Set(['implement', 'batch-implement', 'freestyle', 'loop'
|
|
|
40
47
|
function prDeliveryContinuesTickets(delivery, ticketIds) {
|
|
41
48
|
if (delivery.decision !== 'pr_draft' && delivery.decision !== 'pr_ready')
|
|
42
49
|
return false;
|
|
43
|
-
if (
|
|
50
|
+
if (delivery.executionManifest && delivery.repositoryDeliveries?.length) {
|
|
51
|
+
const active = delivery.repositoryDeliveries.filter((repository) => !['merged', 'completed', 'no_changes'].includes(repository.decision));
|
|
52
|
+
if (!active.length || active.some((repository) => !repository.prUrl || !repository.branch || !repository.deliverySha))
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
else if (!delivery.prUrl || !delivery.branch || !delivery.deliverySha || delivery.prState !== 'pr-created')
|
|
44
56
|
return false;
|
|
45
57
|
const covered = new Set(delivery.ticketIds);
|
|
46
58
|
const requested = new Set(ticketIds);
|
|
@@ -62,11 +74,30 @@ function prDeliveryRevisionAllowed(delivery, revisionOfDeliveryId, ticketIds) {
|
|
|
62
74
|
const requested = new Set(ticketIds);
|
|
63
75
|
return requested.size > 0 && requested.size === covered.size && [...requested].every((id) => covered.has(id));
|
|
64
76
|
}
|
|
77
|
+
function prDeliveryCheckoutTarget(delivery) {
|
|
78
|
+
if (delivery.branch && delivery.deliverySha)
|
|
79
|
+
return { branch: delivery.branch, sha: delivery.deliverySha };
|
|
80
|
+
// Before Create PR, a single settled unit already has an exact local result.
|
|
81
|
+
// A batch must first be assembled: checking out its first branch would hide
|
|
82
|
+
// the rest of the implementation.
|
|
83
|
+
const units = delivery.branches;
|
|
84
|
+
if (!delivery.branch && !delivery.deliverySha && units.length === 1 && units[0].succeeded && units[0].finalSha &&
|
|
85
|
+
units[0].deliveryOutcome !== 'blocked' && units[0].deliveryOutcome !== 'no_changes' &&
|
|
86
|
+
units[0].implementationOutcome !== 'failed' && units[0].changed !== false) {
|
|
87
|
+
return { branch: units[0].branch, sha: units[0].finalSha };
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
65
91
|
function prDeliveryCheckoutBlock(delivery) {
|
|
92
|
+
if ((0, rail_pr_store_1.isTerminalPrDecision)(delivery.decision) || delivery.decision === 'building' ||
|
|
93
|
+
delivery.decision === 'implementation_failed' || delivery.decision === 'no_changes') {
|
|
94
|
+
return 'delivery is not awaiting implementation review';
|
|
95
|
+
}
|
|
66
96
|
if (delivery.deliveryOutcome === 'blocked') {
|
|
67
97
|
return 'delivery is blocked and has no safely checkoutable result';
|
|
68
98
|
}
|
|
69
|
-
|
|
99
|
+
const target = prDeliveryCheckoutTarget(delivery);
|
|
100
|
+
if (!target || !/^[0-9a-f]{40,64}$/i.test(target.sha)) {
|
|
70
101
|
return 'delivery has no verified commit available for checkout';
|
|
71
102
|
}
|
|
72
103
|
return null;
|
|
@@ -402,7 +433,7 @@ function createRailsRouter() {
|
|
|
402
433
|
return;
|
|
403
434
|
}
|
|
404
435
|
let { mode = 'implement' } = req.body ?? {};
|
|
405
|
-
const { profileName, aiEngine, model, loopId: rawLoopId, reasoning_effort, originConversationId, originSurface, targetPrNumber, revisionOfDeliveryId, revisionNote } = req.body ?? {};
|
|
436
|
+
const { repositoryIds: rawRepositoryIds, baseDeliveryIds, profileName, aiEngine, model, loopId: rawLoopId, reasoning_effort, originConversationId, originSurface, targetPrNumber, revisionOfDeliveryId, revisionNote, baseBranch: rawBaseBranch } = req.body ?? {};
|
|
406
437
|
// Revision launch (nontech-review-experience Wave 3): the user asked for a
|
|
407
438
|
// change to a delivery that is already awaiting their decision. Shape is
|
|
408
439
|
// validated here; the narrow guard exemption is enforced below.
|
|
@@ -425,6 +456,21 @@ function createRailsRouter() {
|
|
|
425
456
|
return;
|
|
426
457
|
}
|
|
427
458
|
}
|
|
459
|
+
// Explicit base branch (premium-milestone-progress): a milestone chain
|
|
460
|
+
// stacks chunk k+1 on chunk k's delivered branch. Shape-validated here;
|
|
461
|
+
// resolved locally and isolation-gated below. Never silently ignored.
|
|
462
|
+
let baseBranch = null;
|
|
463
|
+
if (rawBaseBranch !== undefined && rawBaseBranch !== null) {
|
|
464
|
+
if (typeof rawBaseBranch !== 'string' || !(0, integration_branch_1.isValidBranchName)(rawBaseBranch)) {
|
|
465
|
+
res.status(400).json({ error: 'invalid_base_branch', detail: 'baseBranch must be a valid local branch name' });
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
baseBranch = rawBaseBranch;
|
|
469
|
+
}
|
|
470
|
+
if (baseBranch && !(0, feature_flags_1.isLoopsEnabled)()) {
|
|
471
|
+
res.status(400).json({ error: 'base_branch_requires_isolation', detail: 'baseBranch requires an isolated (worktree) loop launch; loops are disabled' });
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
428
474
|
let loopId = rawLoopId;
|
|
429
475
|
// Origin link (safe-pr-review-flow): an agent-chat/MCP launch tags itself so
|
|
430
476
|
// the PR decision can later be posted back into the launching conversation.
|
|
@@ -517,6 +563,38 @@ function createRailsRouter() {
|
|
|
517
563
|
res.status(400).json({ error: 'Rail has no tickets assigned' });
|
|
518
564
|
return;
|
|
519
565
|
}
|
|
566
|
+
let repositoryIds;
|
|
567
|
+
const primaryRepository = (0, project_repositories_1.getProjectRepositories)(c.project).find((repository) => repository.isPrimary);
|
|
568
|
+
try {
|
|
569
|
+
const requiredRepositoryIds = [...new Set(rail.ticketIds.flatMap((id) => (0, project_repositories_1.validateTicketRepositoryIds)(c.project, c.getTicketSpec?.(id)?.repositoryIds) ?? [primaryRepository.id]))];
|
|
570
|
+
repositoryIds = (0, project_repositories_1.validateTicketRepositoryIds)(c.project, rawRepositoryIds) ?? requiredRepositoryIds;
|
|
571
|
+
const missing = requiredRepositoryIds.filter((id) => !repositoryIds.includes(id));
|
|
572
|
+
if (missing.length) {
|
|
573
|
+
res.status(400).json({ error: 'repository_scope_incomplete', missingRepositoryIds: missing,
|
|
574
|
+
detail: 'The launch must include every repository required by its specs. Edit the spec scope first if its implementation targets have changed.' });
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
catch (error) {
|
|
579
|
+
res.status(error instanceof project_repositories_1.RepositoryValidationError ? error.status : 400).json({ error: error instanceof Error ? error.message : 'Invalid repository selection' });
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
let repositoryBases;
|
|
583
|
+
if (baseDeliveryIds !== undefined) {
|
|
584
|
+
try {
|
|
585
|
+
repositoryBases = await (0, multi_repo_bases_1.resolveRepositoryDeliveryBases)(c.db, c.project, repositoryIds, baseDeliveryIds, worktree_manager_1.defaultGitRunner);
|
|
586
|
+
baseBranch = repositoryBases.repositoryBaseBranches[primaryRepository.id] ?? baseBranch;
|
|
587
|
+
}
|
|
588
|
+
catch (error) {
|
|
589
|
+
res.status(409).json({ error: 'invalid_repository_base', detail: error instanceof Error ? error.message : String(error) });
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
const requiresRepositoryIsolation = repositoryIds.length !== 1 || repositoryIds[0] !== primaryRepository.id;
|
|
594
|
+
if (requiresRepositoryIsolation && !(0, feature_flags_1.isLoopsEnabled)()) {
|
|
595
|
+
res.status(409).json({ error: 'repository_isolation_required' });
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
520
598
|
// Per-rail spec cap: a single launch may carry at most
|
|
521
599
|
// MAX_TICKETS_PER_RAIL_LAUNCH specs — bigger batches must be chunked
|
|
522
600
|
// across rails (the milestone launcher does this automatically).
|
|
@@ -583,7 +661,7 @@ function createRailsRouter() {
|
|
|
583
661
|
// Freestyle runs no agent pipeline, so profiles do not apply.
|
|
584
662
|
resolvedProfile = null;
|
|
585
663
|
}
|
|
586
|
-
else if (
|
|
664
|
+
else if ((0, providers_1.getAdapter)(engineCheck.provider).capabilities.profiles !== true) {
|
|
587
665
|
resolvedProfile = null;
|
|
588
666
|
}
|
|
589
667
|
else if (profileName === null) {
|
|
@@ -628,6 +706,11 @@ function createRailsRouter() {
|
|
|
628
706
|
res.status(400).json({ error: 'Loop must be published before it can run on a rail' });
|
|
629
707
|
return;
|
|
630
708
|
}
|
|
709
|
+
const validation = (0, loop_graph_1.validateLoopGraph)(loop.graph);
|
|
710
|
+
if (!validation.valid) {
|
|
711
|
+
res.status(422).json({ error: 'Loop graph is invalid', errors: validation.errors });
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
631
714
|
// A standalone loop (no {{spec.*}} and no ticket command) ignores the
|
|
632
715
|
// rail's spec — it would just re-run once per ticket. Those belong to
|
|
633
716
|
// the Loops page "Run" action; reject them here as a defence-in-depth
|
|
@@ -661,9 +744,20 @@ function createRailsRouter() {
|
|
|
661
744
|
// dashboard factory launches send neither. Read at launch time, so a
|
|
662
745
|
// settings change applies to the next launch without restart.
|
|
663
746
|
const globalAgentDefaults = (0, agent_defaults_1.resolveAgentDefaults)(c.desktopDb, loopProvider);
|
|
747
|
+
let profileModel;
|
|
748
|
+
if (resolvedProfile) {
|
|
749
|
+
try {
|
|
750
|
+
const execution = (0, workspace_resolution_1.resolveProjectExecution)({ slug: c.project.slug, path: c.project.path });
|
|
751
|
+
profileModel = (0, profile_manager_1.resolveProfile)(execution.cwd, resolvedProfile, loopProvider)?.profile.orchestrator.model;
|
|
752
|
+
}
|
|
753
|
+
catch (error) {
|
|
754
|
+
res.status(400).json({ error: 'invalid_profile', detail: error instanceof Error ? error.message : String(error) });
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
664
758
|
const loopModel = typeof model === 'string' && model
|
|
665
759
|
? model
|
|
666
|
-
: (globalAgentDefaults?.pipelineModel ?? loopAdapter.defaultModel());
|
|
760
|
+
: (profileModel ?? globalAgentDefaults?.pipelineModel ?? loopAdapter.defaultModel());
|
|
667
761
|
let effort;
|
|
668
762
|
if (reasoning_effort !== undefined && reasoning_effort !== null) {
|
|
669
763
|
const allowed = (0, providers_1.reasoningEffortsForModel)(loopAdapter, loopModel);
|
|
@@ -693,14 +787,10 @@ function createRailsRouter() {
|
|
|
693
787
|
const scope = (0, loop_command_catalog_1.dominantTicketScope)(promptsText);
|
|
694
788
|
// Parallel isolation (default-on; disable with SPECRAILS_RAIL_WORKTREES=0):
|
|
695
789
|
// a per-ticket rail on a repo-mutating loop runs each ticket in its own git
|
|
696
|
-
// worktree, then merges the branches back.
|
|
697
|
-
//
|
|
698
|
-
//
|
|
790
|
+
// worktree, then merges the branches back. A non-git repo or an unborn
|
|
791
|
+
// HEAD uses the explicit shared-cwd fallback; an operational allocation
|
|
792
|
+
// failure aborts rather than moving execution into the active checkout.
|
|
699
793
|
let isolationUnavailable;
|
|
700
|
-
// Human-readable failure detail for the 'error' case — surfaced to the
|
|
701
|
-
// client so a silent fallback (no delivery row → no implementation
|
|
702
|
-
// cards) is diagnosable from the UI, not just the server log.
|
|
703
|
-
let isolationUnavailableDetail;
|
|
704
794
|
let continuablePrDelivery = null;
|
|
705
795
|
let revisionRequest = null;
|
|
706
796
|
// Read-only vs mutating is DERIVED from the loop's nodes (see loop-effect),
|
|
@@ -747,15 +837,30 @@ function createRailsRouter() {
|
|
|
747
837
|
res.status(409).json({ error: 'pr_decision_pending', prDeliveryId: pendingSnapshot.id });
|
|
748
838
|
return;
|
|
749
839
|
}
|
|
840
|
+
if (pendingSnapshot?.executionManifest) {
|
|
841
|
+
const previousIds = pendingSnapshot.executionManifest.selectedRepositoryIds;
|
|
842
|
+
if (previousIds.length !== repositoryIds.length || previousIds.some((id) => !repositoryIds.includes(id))) {
|
|
843
|
+
res.status(409).json({ error: 'delivery_repository_scope_changed', detail: 'A follow-up must retain the delivered repository scope.' });
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
750
847
|
continuablePrDelivery = pendingSnapshot;
|
|
751
848
|
}
|
|
752
849
|
const useIsolation = (0, rail_isolation_1.isolationApplies)({
|
|
753
850
|
loopsEnabled: (0, feature_flags_1.isLoopsEnabled)(), scope, ticketCount: rail.ticketIds.length, readOnly: loopReadOnly,
|
|
754
851
|
});
|
|
852
|
+
if (requiresRepositoryIsolation && !useIsolation) {
|
|
853
|
+
res.status(409).json({ error: 'repository_isolation_required', detail: 'Selected repositories require worktree isolation for this loop.' });
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
755
856
|
if (typeof targetPrNumber === 'number' && !useIsolation) {
|
|
756
857
|
res.status(400).json({ error: 'target_pr_requires_pr_mode', detail: 'targetPrNumber requires an isolated (worktree) launch; this launch would run in the shared checkout' });
|
|
757
858
|
return;
|
|
758
859
|
}
|
|
860
|
+
if (baseBranch && !useIsolation) {
|
|
861
|
+
res.status(400).json({ error: 'base_branch_requires_isolation', detail: 'baseBranch requires an isolated (worktree) launch; this launch would run in the shared checkout' });
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
759
864
|
if (continuablePrDelivery && !loopReadOnly && !useIsolation) {
|
|
760
865
|
res.status(409).json({
|
|
761
866
|
error: 'pr_continuation_isolation_required',
|
|
@@ -767,8 +872,12 @@ function createRailsRouter() {
|
|
|
767
872
|
if (useIsolation) {
|
|
768
873
|
// Worktree isolation needs a git repo WITH at least one commit (an
|
|
769
874
|
// unborn HEAD can't be branched). Fall back (with a message) otherwise.
|
|
770
|
-
const status = await (0, worktree_manager_1.repoIsolationStatus)(worktree_manager_1.defaultGitRunner, c.project.path);
|
|
875
|
+
const status = requiresRepositoryIsolation ? 'ok' : await (0, worktree_manager_1.repoIsolationStatus)(worktree_manager_1.defaultGitRunner, c.project.path);
|
|
771
876
|
if (status !== 'ok') {
|
|
877
|
+
if (baseBranch) {
|
|
878
|
+
res.status(400).json({ error: 'base_branch_requires_isolation', detail: `baseBranch requires worktree isolation, which is unavailable (${status})` });
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
772
881
|
if (typeof targetPrNumber === 'number') {
|
|
773
882
|
res.status(400).json({
|
|
774
883
|
error: 'target_pr_requires_pr_mode',
|
|
@@ -788,13 +897,24 @@ function createRailsRouter() {
|
|
|
788
897
|
console.warn(`[rails-router] worktree isolation unavailable (${status}); running shared cwd`);
|
|
789
898
|
}
|
|
790
899
|
else {
|
|
900
|
+
if (baseBranch) {
|
|
901
|
+
// Must resolve as a LOCAL branch — a chain never stacks on a guess.
|
|
902
|
+
const verify = await worktree_manager_1.defaultGitRunner.run(['rev-parse', '--verify', '--quiet', `refs/heads/${baseBranch}`], c.project.path);
|
|
903
|
+
if (verify.code !== 0) {
|
|
904
|
+
res.status(400).json({ error: 'invalid_base_branch', detail: `branch ${baseBranch} does not resolve locally` });
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
791
908
|
try {
|
|
792
909
|
const ids = await (0, rail_isolated_launch_1.launchIsolatedRail)({
|
|
793
|
-
ctx: c, railIndex, ticketIds: [...rail.ticketIds], loopId, loopName, loopGraph,
|
|
910
|
+
ctx: c, railIndex, ticketIds: [...rail.ticketIds], repositoryIds, ...repositoryBases, loopId, loopName, loopGraph,
|
|
794
911
|
provider: loopProvider, model: loopModel, effort, scope,
|
|
912
|
+
profileName: resolvedProfile,
|
|
795
913
|
originSurface: originSurface ?? 'dashboard',
|
|
796
914
|
originConversationId: originConversationId ?? null,
|
|
797
|
-
...(
|
|
915
|
+
...(baseBranch ? { baseBranch } : {}),
|
|
916
|
+
...(continuablePrDelivery?.executionManifest && !revisionRequest ? { repositoryContinuation: { deliveryId: continuablePrDelivery.id, decision: continuablePrDelivery.decision } } : {}),
|
|
917
|
+
...(continuablePrDelivery && !continuablePrDelivery.executionManifest ? {
|
|
798
918
|
requiredPrContinuation: {
|
|
799
919
|
deliveryId: continuablePrDelivery.id,
|
|
800
920
|
decision: continuablePrDelivery.decision,
|
|
@@ -845,9 +965,13 @@ function createRailsRouter() {
|
|
|
845
965
|
});
|
|
846
966
|
return;
|
|
847
967
|
}
|
|
848
|
-
console.error('[rails-router] isolated launch failed
|
|
849
|
-
|
|
850
|
-
|
|
968
|
+
console.error('[rails-router] isolated launch failed:', err);
|
|
969
|
+
res.status(409).json({
|
|
970
|
+
error: 'isolation_failed',
|
|
971
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
972
|
+
action: 'Resolve the worktree allocation error and retry the rail launch.',
|
|
973
|
+
});
|
|
974
|
+
return;
|
|
851
975
|
}
|
|
852
976
|
}
|
|
853
977
|
}
|
|
@@ -900,16 +1024,26 @@ function createRailsRouter() {
|
|
|
900
1024
|
repoDir: loopExec.relocated ? loopExec.repoDir : undefined,
|
|
901
1025
|
railIndex,
|
|
902
1026
|
ticketId: ticketIds[0],
|
|
903
|
-
spec:
|
|
1027
|
+
spec: {
|
|
1028
|
+
...spec,
|
|
1029
|
+
ticketIds,
|
|
1030
|
+
...(ticketIds.length > 1 ? {
|
|
1031
|
+
tickets: ticketIds.map((id) => {
|
|
1032
|
+
const ticket = c.getTicketSpec(id);
|
|
1033
|
+
return { id, title: ticket?.title, description: ticket?.description };
|
|
1034
|
+
}),
|
|
1035
|
+
} : {}),
|
|
1036
|
+
},
|
|
904
1037
|
ticketCompletionStatus: loopTicketCompletionStatus,
|
|
905
1038
|
constants: (0, loop_constants_1.loadConstantMap)(c.desktopDb),
|
|
906
1039
|
provider: loopProvider,
|
|
907
1040
|
model: loopModel,
|
|
908
1041
|
effort,
|
|
1042
|
+
profileName: resolvedProfile,
|
|
909
1043
|
})
|
|
910
1044
|
.then((r) => {
|
|
911
1045
|
recordRunProvenance();
|
|
912
|
-
c.onLoopRunFinished(r.runId, r.outcome);
|
|
1046
|
+
c.onLoopRunFinished(r.runId, r.outcome, r.stallReason ? { stallReason: r.stallReason } : undefined);
|
|
913
1047
|
return r.outcome === 'success';
|
|
914
1048
|
})
|
|
915
1049
|
.catch((err) => {
|
|
@@ -939,7 +1073,6 @@ function createRailsRouter() {
|
|
|
939
1073
|
res.status(202).json({
|
|
940
1074
|
loopRunIds, railIndex, mode,
|
|
941
1075
|
...(isolationUnavailable ? { isolationUnavailable } : {}),
|
|
942
|
-
...(isolationUnavailableDetail ? { isolationUnavailableDetail } : {}),
|
|
943
1076
|
});
|
|
944
1077
|
return;
|
|
945
1078
|
}
|
|
@@ -1103,17 +1236,40 @@ function createRailsRouter() {
|
|
|
1103
1236
|
return;
|
|
1104
1237
|
}
|
|
1105
1238
|
const c = ctx(req);
|
|
1106
|
-
|
|
1239
|
+
let row = (0, rail_pr_store_1.getPrDelivery)(c.db, req.params.prDeliveryId);
|
|
1107
1240
|
if (!row) {
|
|
1108
1241
|
res.status(404).json({ error: 'Delivery not found' });
|
|
1109
1242
|
return;
|
|
1110
1243
|
}
|
|
1111
1244
|
try {
|
|
1112
|
-
const
|
|
1245
|
+
const parent = row.parent_delivery_id ? (0, rail_pr_store_1.getPrDelivery)(c.db, row.parent_delivery_id) : row;
|
|
1246
|
+
if (!parent) {
|
|
1247
|
+
res.status(404).json({ error: 'Delivery group not found' });
|
|
1248
|
+
return;
|
|
1249
|
+
}
|
|
1250
|
+
const repositoryId = req.query.repositoryId;
|
|
1251
|
+
if (repositoryId !== undefined && (typeof repositoryId !== 'string' || !repositoryId)) {
|
|
1252
|
+
res.status(400).json({ error: 'Invalid repositoryId' });
|
|
1253
|
+
return;
|
|
1254
|
+
}
|
|
1255
|
+
if (repositoryId) {
|
|
1256
|
+
row = (0, multi_repo_execution_1.listRepositoryDeliveries)(c.db, parent.id).find((child) => child.repository_id === repositoryId);
|
|
1257
|
+
if (!row) {
|
|
1258
|
+
res.status(404).json({ error: 'Repository not in this delivery' });
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
const manifest = (0, multi_repo_execution_store_1.readExecutionManifest)(parent.execution_manifest);
|
|
1263
|
+
const frozenRepository = row.repository_id ? manifest?.repositories.find((repository) => repository.repositoryId === row.repository_id && repository.sourcePath === row.repository_path) : undefined;
|
|
1264
|
+
if (row.parent_delivery_id && !frozenRepository) {
|
|
1265
|
+
res.status(409).json({ error: 'Repository delivery has no frozen source path' });
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
const packet = (0, review_packet_1.composeReviewPacket)({ db: c.db, row, ...(frozenRepository ? { repositoryId: frozenRepository.repositoryId, includeLegacyProvenance: frozenRepository.repositoryId === manifest?.primaryRepositoryId } : {}) });
|
|
1113
1269
|
// Capability probes are read-only and offline; a failure degrades to the
|
|
1114
1270
|
// confirm-gated local path rather than blocking the packet.
|
|
1115
|
-
const acceptCapability = await (0, accept_ladder_1.resolveAcceptCapability)({ repoDir: c.project.path, exec: pr_publisher_1.defaultExec });
|
|
1116
|
-
res.json({ packet, acceptCapability, snapshot: (0, rail_pr_store_1.toPrDeliverySnapshot)(
|
|
1271
|
+
const acceptCapability = await (0, accept_ladder_1.resolveAcceptCapability)({ repoDir: frozenRepository?.sourcePath ?? c.project.path, exec: pr_publisher_1.defaultExec });
|
|
1272
|
+
res.json({ packet, acceptCapability, snapshot: (0, rail_pr_store_1.toPrDeliverySnapshot)(parent), repositoryId: frozenRepository?.repositoryId ?? null });
|
|
1117
1273
|
}
|
|
1118
1274
|
catch (err) {
|
|
1119
1275
|
console.error('[rails-router] packet composition failed:', err);
|
|
@@ -1130,7 +1286,11 @@ function createRailsRouter() {
|
|
|
1130
1286
|
// specrails never merges — the engineer owns the merge.
|
|
1131
1287
|
router.post('/pr-decision', async (req, res) => {
|
|
1132
1288
|
const c = ctx(req);
|
|
1133
|
-
const { prDeliveryId, action, expectedDecision } = req.body ?? {};
|
|
1289
|
+
const { prDeliveryId, action, expectedDecision, repositoryId } = req.body ?? {};
|
|
1290
|
+
if (repositoryId !== undefined && (typeof repositoryId !== 'string' || !repositoryId)) {
|
|
1291
|
+
res.status(400).json({ error: 'repositoryId must be a nonempty string' });
|
|
1292
|
+
return;
|
|
1293
|
+
}
|
|
1134
1294
|
if (typeof prDeliveryId !== 'string' || !prDeliveryId) {
|
|
1135
1295
|
res.status(400).json({ error: 'prDeliveryId is required' });
|
|
1136
1296
|
return;
|
|
@@ -1144,6 +1304,11 @@ function createRailsRouter() {
|
|
|
1144
1304
|
return;
|
|
1145
1305
|
}
|
|
1146
1306
|
try {
|
|
1307
|
+
const targetDelivery = (0, rail_pr_store_1.getPrDelivery)(c.db, prDeliveryId);
|
|
1308
|
+
if (repositoryId && targetDelivery && !targetDelivery.execution_manifest && !(0, project_repositories_1.resolveProjectRepository)(c.project, repositoryId).isPrimary) {
|
|
1309
|
+
res.status(400).json({ error: 'Repository is not part of this delivery' });
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1147
1312
|
const admission = (0, process_admission_1.captureProcessAdmission)(c.project.id);
|
|
1148
1313
|
const execute = () => {
|
|
1149
1314
|
admission.assertCurrent();
|
|
@@ -1155,13 +1320,13 @@ function createRailsRouter() {
|
|
|
1155
1320
|
broadcast: c.broadcast,
|
|
1156
1321
|
jiraSyncManager: c.jiraSyncManager,
|
|
1157
1322
|
assertAdmission: () => admission.assertCurrent(),
|
|
1158
|
-
}, { prDeliveryId, action, expectedDecision });
|
|
1323
|
+
}, { prDeliveryId, action, expectedDecision, repositoryId });
|
|
1159
1324
|
};
|
|
1160
1325
|
// Every decision that can touch refs/worktrees shares the same project
|
|
1161
1326
|
// mutex as startup reconciliation, launch allocation and checkout. Local
|
|
1162
1327
|
// merge already acquires it internally to keep its guarded read/advance
|
|
1163
1328
|
// sequence atomic, so avoid nesting that non-reentrant lock.
|
|
1164
|
-
const result = action === 'merge-local'
|
|
1329
|
+
const result = action === 'merge-local' || !!(0, rail_pr_store_1.getPrDelivery)(c.db, prDeliveryId)?.execution_manifest
|
|
1165
1330
|
? await execute()
|
|
1166
1331
|
: await (0, repo_lock_1.withRepoLock)(c.project.path, execute);
|
|
1167
1332
|
// The durable row is authoritative. Return its post-action snapshot even
|
|
@@ -1178,6 +1343,10 @@ function createRailsRouter() {
|
|
|
1178
1343
|
res.status(409).json({ error: 'project_recovery_in_progress' });
|
|
1179
1344
|
return;
|
|
1180
1345
|
}
|
|
1346
|
+
if (err instanceof project_repositories_1.RepositoryValidationError) {
|
|
1347
|
+
res.status(err.status).json({ error: err.message });
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1181
1350
|
console.error('[rails-router] pr-decision error:', err);
|
|
1182
1351
|
res.status(500).json({ error: 'pr-decision failed', detail: err.message });
|
|
1183
1352
|
}
|
|
@@ -1210,6 +1379,18 @@ function createRailsRouter() {
|
|
|
1210
1379
|
res.status(404).json({ error: 'Unknown prDeliveryId' });
|
|
1211
1380
|
return;
|
|
1212
1381
|
}
|
|
1382
|
+
if (!row.execution_manifest && req.body?.repositoryId !== undefined) {
|
|
1383
|
+
try {
|
|
1384
|
+
if (typeof req.body.repositoryId !== 'string' || !(0, project_repositories_1.resolveProjectRepository)(c.project, req.body.repositoryId).isPrimary) {
|
|
1385
|
+
res.status(400).json({ error: 'Repository is not part of this delivery' });
|
|
1386
|
+
return;
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
catch (error) {
|
|
1390
|
+
res.status(400).json({ error: error instanceof Error ? error.message : 'Invalid repositoryId' });
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1213
1394
|
const active = (0, rail_pr_store_1.getActivePrDeliveryByRail)(c.db, row.rail_index);
|
|
1214
1395
|
if (!active || active.id !== row.id) {
|
|
1215
1396
|
res.status(409).json({
|
|
@@ -1219,16 +1400,34 @@ function createRailsRouter() {
|
|
|
1219
1400
|
});
|
|
1220
1401
|
return;
|
|
1221
1402
|
}
|
|
1403
|
+
if (row.execution_manifest) {
|
|
1404
|
+
const repositoryId = req.body?.repositoryId;
|
|
1405
|
+
if (repositoryId !== undefined && (typeof repositoryId !== 'string' || !repositoryId)) {
|
|
1406
|
+
res.status(400).json({ error: 'repositoryId must be a nonempty string' });
|
|
1407
|
+
return;
|
|
1408
|
+
}
|
|
1409
|
+
try {
|
|
1410
|
+
const outcome = await (0, multi_repo_checkout_1.checkoutRepositoryDelivery)({
|
|
1411
|
+
db: c.db, project: c.project, git: worktree_manager_1.defaultGitRunner, exec: pr_publisher_1.defaultExec,
|
|
1412
|
+
broadcast: c.broadcast, jiraSyncManager: c.jiraSyncManager,
|
|
1413
|
+
assertAdmission: () => admission.assertCurrent(),
|
|
1414
|
+
}, { prDeliveryId, repositoryId });
|
|
1415
|
+
res.status(outcome.status).json(outcome.body);
|
|
1416
|
+
}
|
|
1417
|
+
catch (error) {
|
|
1418
|
+
res.status(error instanceof process_admission_1.ProcessAdmissionClosedError ? 409 : 500).json({
|
|
1419
|
+
error: error instanceof process_admission_1.ProcessAdmissionClosedError ? 'project_recovery_in_progress' : 'pr-checkout failed',
|
|
1420
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
return;
|
|
1424
|
+
}
|
|
1222
1425
|
const snap = (0, rail_pr_store_1.toPrDeliverySnapshot)(row);
|
|
1223
1426
|
const checkoutBlock = prDeliveryCheckoutBlock(snap);
|
|
1224
1427
|
if (checkoutBlock) {
|
|
1225
1428
|
res.status(409).json({ error: 'checkout_not_deliverable', detail: checkoutBlock });
|
|
1226
1429
|
return;
|
|
1227
1430
|
}
|
|
1228
|
-
if (!snap.branch || !snap.prUrl) {
|
|
1229
|
-
res.status(409).json({ error: 'checkout_unavailable', detail: 'delivery has no PR branch' });
|
|
1230
|
-
return;
|
|
1231
|
-
}
|
|
1232
1431
|
try {
|
|
1233
1432
|
const outcome = await (0, repo_lock_1.withRepoLock)(c.project.path, async () => {
|
|
1234
1433
|
admission.assertCurrent();
|
|
@@ -1251,12 +1450,13 @@ function createRailsRouter() {
|
|
|
1251
1450
|
detail: currentCheckoutBlock,
|
|
1252
1451
|
};
|
|
1253
1452
|
}
|
|
1254
|
-
|
|
1453
|
+
const target = prDeliveryCheckoutTarget(currentSnap);
|
|
1454
|
+
if (!target) {
|
|
1255
1455
|
return {
|
|
1256
1456
|
ok: false,
|
|
1257
1457
|
status: 409,
|
|
1258
1458
|
error: 'checkout_not_deliverable',
|
|
1259
|
-
detail: 'The current delivery no longer has
|
|
1459
|
+
detail: 'The current delivery no longer has a verified local branch.',
|
|
1260
1460
|
};
|
|
1261
1461
|
}
|
|
1262
1462
|
const cleanliness = await (0, project_git_1.inspectProjectCheckoutCleanliness)(c.project.path);
|
|
@@ -1268,9 +1468,8 @@ function createRailsRouter() {
|
|
|
1268
1468
|
detail: cleanliness.detail,
|
|
1269
1469
|
};
|
|
1270
1470
|
}
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
}
|
|
1471
|
+
// Compatible local edits are preserved by Git's checkout transaction;
|
|
1472
|
+
// only unreadable status prevents attempting the handoff here.
|
|
1274
1473
|
const info = await (0, project_git_1.getProjectGitInfo)(c.project.path);
|
|
1275
1474
|
if (!info.git) {
|
|
1276
1475
|
return { ok: false, status: 409, error: 'checkout_unavailable', detail: 'project is not a git repository' };
|
|
@@ -1301,30 +1500,25 @@ function createRailsRouter() {
|
|
|
1301
1500
|
}
|
|
1302
1501
|
}
|
|
1303
1502
|
}
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
error: 'checkout_unavailable',
|
|
1309
|
-
detail: cleanupWarnings[0],
|
|
1310
|
-
};
|
|
1311
|
-
}
|
|
1312
|
-
// Cleanup just completed with ZERO warnings — clear any stale
|
|
1313
|
-
// cleanup_incomplete evidence persisted by earlier failed release
|
|
1314
|
-
// attempts, so the card stops claiming "Cleanup is incomplete" after
|
|
1315
|
-
// the condition has been resolved. Same-state patch + live broadcast.
|
|
1316
|
-
if (currentSnap.cleanupWarnings.length > 0 || currentSnap.statusCode === 'cleanup_incomplete') {
|
|
1503
|
+
// A preserved unit worktree need not hold the assembled review branch.
|
|
1504
|
+
// Keep its warnings visible, but let Git decide whether the requested
|
|
1505
|
+
// branch is actually occupied instead of blocking every batch checkout.
|
|
1506
|
+
if (cleanupWarnings.length > 0 || currentSnap.cleanupWarnings.length > 0 || currentSnap.statusCode === 'cleanup_incomplete') {
|
|
1317
1507
|
(0, rail_pr_store_1.transitionDecision)(c.db, row.id, currentSnap.decision, currentSnap.decision, {
|
|
1318
|
-
cleanupWarnings
|
|
1319
|
-
...(
|
|
1508
|
+
cleanupWarnings,
|
|
1509
|
+
...(cleanupWarnings.length > 0 ? { statusCode: 'cleanup_incomplete' }
|
|
1510
|
+
: currentSnap.statusCode === 'cleanup_incomplete' ? { statusCode: null } : {}),
|
|
1320
1511
|
});
|
|
1321
1512
|
emitPrDeliveryUpdate(c, row.id);
|
|
1322
1513
|
}
|
|
1323
|
-
const checkedOut = await (0, project_git_1.checkoutProjectReviewBranch)(c.project.path,
|
|
1514
|
+
const checkedOut = await (0, project_git_1.checkoutProjectReviewBranch)(c.project.path, target.branch, target.sha);
|
|
1324
1515
|
if (!checkedOut.ok) {
|
|
1325
|
-
return {
|
|
1516
|
+
return {
|
|
1517
|
+
ok: false, status: 409, error: 'checkout_failed',
|
|
1518
|
+
detail: [checkedOut.error, ...cleanupWarnings].join('\n'),
|
|
1519
|
+
};
|
|
1326
1520
|
}
|
|
1327
|
-
return { ok: true, git: await (0, project_git_1.getProjectGitInfo)(c.project.path) };
|
|
1521
|
+
return { ok: true, branch: target.branch, git: await (0, project_git_1.getProjectGitInfo)(c.project.path), cleanupWarnings };
|
|
1328
1522
|
});
|
|
1329
1523
|
if (!outcome.ok) {
|
|
1330
1524
|
res.status(outcome.status).json({ error: outcome.error, detail: outcome.detail });
|