specrails-desktop 2.57.0 → 2.58.0
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 +1 -1
- package/cli/dist/args.js +10 -4
- package/cli/dist/help.js +1 -1
- package/client/dist/assets/{ActivityFeedPage-DdMNmVBk.js → ActivityFeedPage-BQGppIR0.js} +1 -1
- package/client/dist/assets/{AgentBrowserCapture-CeerUf--.js → AgentBrowserCapture-PI-cxyxi.js} +1 -1
- package/client/dist/assets/{AgentModeAnalyticsPane-BjGb-8rh.js → AgentModeAnalyticsPane-mQsOqdXx.js} +2 -2
- package/client/dist/assets/{AgentModeCodePane-8XRkQEo_.js → AgentModeCodePane-BL-vpmlY.js} +2 -2
- package/client/dist/assets/{AgentModeJobsPane-BN3dgJOA.js → AgentModeJobsPane-DR_06oqH.js} +1 -1
- package/client/dist/assets/AgentsPage-BKIKjm_1.js +118 -0
- package/client/dist/assets/{AnalyticsPage-CyfhYiBn.js → AnalyticsPage-069npRVo.js} +1 -1
- package/client/dist/assets/{CodePage-C2Nf-Q5W.js → CodePage-Alitorrv.js} +2 -2
- package/client/dist/assets/DesktopAnalyticsPage-qtWAPM47.js +1 -0
- package/client/dist/assets/{DocsDialog-Br3bczCp.js → DocsDialog-DwL6rgcT.js} +1 -1
- package/client/dist/assets/{DocsPage-BcDr2cBm.js → DocsPage-BA1hiJZ-.js} +1 -1
- package/client/dist/assets/{ExportDropdown-BVGKEgkd.js → ExportDropdown-BcMpvV89.js} +1 -1
- package/client/dist/assets/InteractiveJobComposer-B3H1YHr6.js +19 -0
- package/client/dist/assets/JobDetailModal-XZQ9eAAy.js +1 -0
- package/client/dist/assets/{JobDetailPage-vBbNeRuz.js → JobDetailPage-B-SIaq_q.js} +1 -1
- package/client/dist/assets/{JobsPage-CJCtPhIz.js → JobsPage-iQXAv2mx.js} +1 -1
- package/client/dist/assets/LoopBuilderPage-B_gbFAHn.js +1 -0
- package/client/dist/assets/LoopPreviewModal-je5pSCf-.js +1 -0
- package/client/dist/assets/LoopsPage-BZwcY5cu.js +2 -0
- package/client/dist/assets/{MinimizedChatsContext-D3QzgV0B.js → MinimizedChatsContext-CNx_0bZu.js} +1 -1
- package/client/dist/assets/{PluginsPage-DOpTV75p.js → PluginsPage-DFocnoHX.js} +2 -2
- package/client/dist/assets/{ProjectSettingsDialog-Dkwrn0z1.js → ProjectSettingsDialog-Bh3S6EBB.js} +1 -1
- package/client/dist/assets/{RepositoryDeliveries-Du2UCf1a.js → RepositoryDeliveries-BiwJuS8r.js} +1 -1
- package/client/dist/assets/{RepositoryScopeSelector-hPGZNRBm.js → RepositoryScopeSelector-C2mvWBQ6.js} +1 -1
- package/client/dist/assets/{ReviewPacketPage-DcOq-UTA.js → ReviewPacketPage-CrAf-WDy.js} +2 -2
- package/client/dist/assets/TemplatePreviewModal-C7DordpN.js +1 -0
- package/client/dist/assets/{TicketDetailModalContext-DAK-NoK6.js → TicketDetailModalContext-B-DySpKL.js} +1 -1
- package/client/dist/assets/{Trans-DEbgkuAY.js → Trans-D3AfDylf.js} +1 -1
- package/client/dist/assets/agentRuntime-BeUb3g9g.js +1 -0
- package/client/dist/assets/agentRuntime-Bg9tbmqm.js +1 -0
- package/client/dist/assets/agentRuntime-D-GJVGkj.js +1 -0
- package/client/dist/assets/agentRuntime-D2__CVI_.js +1 -0
- package/client/dist/assets/agentRuntime-D35dGVOP.js +1 -0
- package/client/dist/assets/agentRuntime-DGz_IqKW.js +1 -0
- package/client/dist/assets/agentRuntime-O1liDt6G.js +1 -0
- package/client/dist/assets/agentRuntime-hInH9Fzj.js +1 -0
- package/client/dist/assets/agentstudio-B2CTtQgZ.js +1 -0
- package/client/dist/assets/agentstudio-BWAKeGgB.js +1 -0
- package/client/dist/assets/{agentstudio-DR7doNBV.js → agentstudio-Cja8Sjcp.js} +1 -1
- package/client/dist/assets/agentstudio-DtfPcr99.js +1 -0
- package/client/dist/assets/agentstudio-QdAJIDef.js +1 -0
- package/client/dist/assets/agentstudio-X70BKf6u.js +1 -0
- package/client/dist/assets/agentstudio-fLXpRGIi.js +1 -0
- package/client/dist/assets/agentstudio-qXKqVnWS.js +1 -0
- package/client/dist/assets/{analytics-jzCkOec3.js → analytics-B1DIQd4m.js} +1 -1
- package/client/dist/assets/{analytics-CoEzWPx4.js → analytics-BAIZ9vE_.js} +1 -1
- package/client/dist/assets/{analytics-DjLlE7jS.js → analytics-BTqgmgBd.js} +1 -1
- package/client/dist/assets/analytics-BWynXPEm.js +1 -0
- package/client/dist/assets/{analytics-nZIjjtCc.js → analytics-DGcUsQ44.js} +1 -1
- package/client/dist/assets/{analytics-BNgZ1Nj7.js → analytics-GnSnpJ4W.js} +1 -1
- package/client/dist/assets/{analytics-DFh5lSOl.js → analytics-TWgYaoO0.js} +1 -1
- package/client/dist/assets/{analytics-DZp-DS2U.js → analytics-kJy6Tuko.js} +1 -1
- package/client/dist/assets/builder-C0FyvM83.js +1 -0
- package/client/dist/assets/builder-C1neIYGR.js +1 -0
- package/client/dist/assets/builder-CvnFm0im.js +1 -0
- package/client/dist/assets/builder-CzW5ovyI.js +1 -0
- package/client/dist/assets/builder-D06L7iPy.js +1 -0
- package/client/dist/assets/builder-DGETbtZj.js +1 -0
- package/client/dist/assets/builder-DXEJUlC0.js +1 -0
- package/client/dist/assets/builder-DyQperXU.js +1 -0
- package/client/dist/assets/common-BCK7c9Pv.js +1 -0
- package/client/dist/assets/common-C0BOdeNi.js +1 -0
- package/client/dist/assets/common-C4Yvvhp6.js +1 -0
- package/client/dist/assets/{common-BrlwEnL7.js → common-DmsclwjP.js} +1 -1
- package/client/dist/assets/common-DpLWwgI1.js +1 -0
- package/client/dist/assets/common-OCvl3c8t.js +1 -0
- package/client/dist/assets/common-c0aujEZC.js +1 -0
- package/client/dist/assets/common-od8WThlf.js +1 -0
- package/client/dist/assets/dashboard-B1ndRHuC.js +1 -0
- package/client/dist/assets/dashboard-BX7KXiqA.js +1 -0
- package/client/dist/assets/dashboard-B_BSNxcN.js +1 -0
- package/client/dist/assets/dashboard-CCeFN7-V.js +1 -0
- package/client/dist/assets/dashboard-CRl0WtFJ.js +1 -0
- package/client/dist/assets/dashboard-XGdQ5mjw.js +1 -0
- package/client/dist/assets/dashboard-_oltA6Jd.js +1 -0
- package/client/dist/assets/dashboard-bAuf5KVk.js +1 -0
- package/client/dist/assets/{formatDistanceToNow-Cc2u5FwQ.js → formatDistanceToNow-aBss1Nai.js} +1 -1
- package/client/dist/assets/{getTimezoneOffsetInMilliseconds-BHm2U-w1.js → getTimezoneOffsetInMilliseconds-DOGj1Mfz.js} +1 -1
- package/client/dist/assets/index-BS7KCWkV.css +2 -0
- package/client/dist/assets/{index-Cxb288Rj.js → index-C4Mv6tV6.js} +32 -32
- package/client/dist/assets/{jira-api-DrGxiD9p.js → jira-api-BYbEPlkr.js} +1 -1
- package/client/dist/assets/{jobs-DSp9DoD_.js → jobs-6_-r75yC.js} +1 -1
- package/client/dist/assets/{jobs-C6qmvUAi.js → jobs-B1yqyId5.js} +1 -1
- package/client/dist/assets/{jobs-DoFaEl9g.js → jobs-BFuV7ttH.js} +1 -1
- package/client/dist/assets/{jobs-DTQO08nJ.js → jobs-BKa8LuwA.js} +1 -1
- package/client/dist/assets/{jobs-DrF8Do8s.js → jobs-BKiK-ZWh.js} +1 -1
- package/client/dist/assets/{jobs-DLHdDkHc.js → jobs-DmKV7b7d.js} +1 -1
- package/client/dist/assets/{jobs-C9j2PuwW.js → jobs-KG4Sn12X.js} +1 -1
- package/client/dist/assets/{jobs-Dpw6AEOP.js → jobs-pPv4KWjb.js} +1 -1
- package/client/dist/assets/loop-layout-BF8xMiNv.js +7 -0
- package/client/dist/assets/loops-B4_D-nbg.js +1 -0
- package/client/dist/assets/loops-BJDY9FTh.js +1 -0
- package/client/dist/assets/loops-BO3V8244.js +1 -0
- package/client/dist/assets/loops-C-hZK8pf.js +1 -0
- package/client/dist/assets/loops-CDOWZ0ci.js +1 -0
- package/client/dist/assets/loops-Dv849ckF.js +1 -0
- package/client/dist/assets/loops-Q9LIgGkq.js +1 -0
- package/client/dist/assets/loops-eUnCfLjC.js +1 -0
- package/client/dist/assets/{project-repositories-9Wzs0coQ.js → project-repositories-CSnfzUpg.js} +1 -1
- package/client/dist/assets/settings-3o4Ntzi5.js +1 -0
- package/client/dist/assets/settings-B97jlkwD.js +1 -0
- package/client/dist/assets/settings-BTaT_bsS.js +1 -0
- package/client/dist/assets/settings-CT8w7WiC.js +1 -0
- package/client/dist/assets/settings-Cvr8_tvh.js +1 -0
- package/client/dist/assets/settings-Dn1qgAC8.js +1 -0
- package/client/dist/assets/settings-EShDdg4J.js +1 -0
- package/client/dist/assets/settings-tfLhQZRN.js +1 -0
- package/client/dist/assets/{setup-Dwzt5nO1.js → setup-CBMu3tqH.js} +1 -1
- package/client/dist/assets/{setup-B5FHuxvB.js → setup-CPh-e6J2.js} +1 -1
- package/client/dist/assets/{setup-DpqzaEEA.js → setup-CRKLp_ke.js} +1 -1
- package/client/dist/assets/{setup-CROyqD5N.js → setup-Cy302v4J.js} +1 -1
- package/client/dist/assets/{setup-C9Fy9PKQ.js → setup-D681vTPf.js} +1 -1
- package/client/dist/assets/{setup-BihzXadx.js → setup-DYK0VBkE.js} +1 -1
- package/client/dist/assets/{setup-BtC6Hg4K.js → setup-KAuiIiVs.js} +1 -1
- package/client/dist/assets/{setup-zLEDnG7N.js → setup-OGX-oCfI.js} +1 -1
- package/client/dist/assets/{spending-T5GM7wpn.js → spending-HBSk-6jv.js} +1 -1
- package/client/dist/assets/{useDesktop-QogiN0zR.js → useDesktop-0unHDBJJ.js} +1 -1
- package/client/dist/assets/useRuntimeRuns-BN1rhyEm.js +1 -0
- package/client/dist/assets/{useSharedWebSocket-Dz-HI6De.js → useSharedWebSocket-BZMrX4jr.js} +2 -2
- package/client/dist/index.html +25 -25
- package/docs/agent-live-steering.md +16 -0
- package/docs/cli.md +7 -7
- package/docs/codex.md +2 -2
- package/docs/customizing.md +1 -1
- package/docs/gemini.md +1 -1
- package/docs/getting-started.md +1 -1
- package/docs/guide/de/integrations/7-local-engines.md +1 -1
- package/docs/guide/de/pipeline/1-rails-and-jobs.md +8 -6
- package/docs/guide/de/pipeline/2-the-job-detail-view.md +11 -2
- package/docs/guide/de/pipeline/4-picking-an-engine-per-rail.md +2 -2
- package/docs/guide/de/pipeline/5-the-loop-builder.md +24 -5
- package/docs/guide/en/integrations/7-local-engines.md +1 -1
- package/docs/guide/en/pipeline/1-rails-and-jobs.md +8 -6
- package/docs/guide/en/pipeline/2-the-job-detail-view.md +11 -2
- package/docs/guide/en/pipeline/4-picking-an-engine-per-rail.md +2 -2
- package/docs/guide/en/pipeline/5-the-loop-builder.md +34 -5
- package/docs/guide/en/settings/3-pipeline-telemetry-and-diagnostics.md +1 -1
- package/docs/guide/es/integrations/7-local-engines.md +1 -1
- package/docs/guide/es/pipeline/1-rails-and-jobs.md +8 -6
- package/docs/guide/es/pipeline/2-the-job-detail-view.md +11 -2
- package/docs/guide/es/pipeline/4-picking-an-engine-per-rail.md +2 -2
- package/docs/guide/es/pipeline/5-the-loop-builder.md +30 -5
- package/docs/guide/es/settings/3-pipeline-telemetry-and-diagnostics.md +1 -2
- package/docs/guide/fr/integrations/7-local-engines.md +1 -1
- package/docs/guide/fr/pipeline/1-rails-and-jobs.md +8 -6
- package/docs/guide/fr/pipeline/2-the-job-detail-view.md +11 -2
- package/docs/guide/fr/pipeline/4-picking-an-engine-per-rail.md +2 -2
- package/docs/guide/fr/pipeline/5-the-loop-builder.md +24 -5
- package/docs/guide/it/integrations/7-local-engines.md +1 -1
- package/docs/guide/it/pipeline/1-rails-and-jobs.md +8 -6
- package/docs/guide/it/pipeline/2-the-job-detail-view.md +11 -2
- package/docs/guide/it/pipeline/4-picking-an-engine-per-rail.md +2 -2
- package/docs/guide/it/pipeline/5-the-loop-builder.md +24 -5
- package/docs/guide/ja/integrations/7-local-engines.md +1 -1
- package/docs/guide/ja/pipeline/1-rails-and-jobs.md +8 -6
- package/docs/guide/ja/pipeline/2-the-job-detail-view.md +11 -2
- package/docs/guide/ja/pipeline/4-picking-an-engine-per-rail.md +2 -2
- package/docs/guide/ja/pipeline/5-the-loop-builder.md +24 -5
- package/docs/guide/pt/integrations/7-local-engines.md +1 -1
- package/docs/guide/pt/pipeline/1-rails-and-jobs.md +8 -6
- package/docs/guide/pt/pipeline/2-the-job-detail-view.md +11 -2
- package/docs/guide/pt/pipeline/4-picking-an-engine-per-rail.md +2 -2
- package/docs/guide/pt/pipeline/5-the-loop-builder.md +24 -5
- package/docs/guide/zh/integrations/7-local-engines.md +1 -1
- package/docs/guide/zh/pipeline/1-rails-and-jobs.md +8 -6
- package/docs/guide/zh/pipeline/2-the-job-detail-view.md +11 -2
- package/docs/guide/zh/pipeline/4-picking-an-engine-per-rail.md +2 -2
- package/docs/guide/zh/pipeline/5-the-loop-builder.md +24 -5
- package/docs/internals/api-reference.md +2 -2
- package/docs/internals/ci-performance.md +71 -0
- package/docs/internals/companion-rails-as-loops-contract.md +4 -3
- package/docs/internals/configuration.md +3 -3
- package/docs/internals/core-runtime-updates.md +3 -2
- package/docs/internals/interactive-jobs.md +2 -2
- package/docs/internals/loop-step-log-explorer.md +42 -0
- package/docs/internals/mission-rail-cards.md +1 -1
- package/docs/internals/operations-runbook.md +22 -0
- package/docs/internals/programmatic-agent-runtime.md +130 -7
- package/docs/internals/project-builder.md +35 -16
- package/docs/internals/source-map.md +48 -2
- package/docs/kimi.md +2 -2
- package/docs/local-providers.md +1 -1
- package/docs/running-pipelines.md +26 -18
- package/docs/tracking-cost.md +10 -1
- package/package.json +2 -1
- package/server/dist/core-compat.js +8 -3
- package/server/dist/core-execution.js +49 -3
- package/server/dist/db/migrations.js +101 -0
- package/server/dist/desktop-db.js +28 -0
- package/server/dist/desktop-router.js +2 -0
- package/server/dist/mcp/guide.js +6 -3
- package/server/dist/mcp/tools/jobs.js +14 -1
- package/server/dist/mcp/tools/loops.js +17 -8
- package/server/dist/mcp/tools/rails.js +3 -3
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-bridge.js +313 -9
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-controls-router.js +34 -1
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-controls.js +203 -21
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-effective-config.js +46 -1
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-engines.js +10 -0
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-loader.js +68 -3
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-metrics.js +16 -8
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-package-gc.js +81 -0
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-package-lock.js +37 -0
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-package.js +30 -24
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-retention-host.js +87 -0
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-retention-quarantine.js +231 -0
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-retention-records.js +32 -0
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-retention.js +102 -0
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-settings-router.js +12 -7
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-settings.js +57 -13
- package/server/dist/modules/agents/runtime/agent-catalog.js +32 -0
- package/server/dist/modules/agents/runtime/agent-refine-manager.js +12 -0
- package/server/dist/modules/agents/runtime/agent-role-descriptor.js +64 -0
- package/server/dist/modules/agents/runtime/profiles-router.js +26 -34
- package/server/dist/modules/builder/runtime/milestone-chain.js +121 -73
- package/server/dist/modules/delivery/runtime/definition-fork.js +158 -0
- package/server/dist/modules/delivery/runtime/delivery-evidence.js +56 -10
- package/server/dist/modules/delivery/runtime/isolated-settlement-reconstruction.js +99 -0
- package/server/dist/modules/delivery/runtime/isolated-settlement-store.js +53 -0
- package/server/dist/modules/delivery/runtime/multi-repo-execution-store.js +3 -1
- package/server/dist/modules/delivery/runtime/rail-isolated-launch.js +388 -203
- package/server/dist/modules/delivery/runtime/rail-launch-parser.js +3 -2
- package/server/dist/modules/delivery/runtime/rail-pr-store.js +6 -1
- package/server/dist/modules/delivery/runtime/rails-router.js +66 -10
- package/server/dist/modules/execution/runtime/queue-manager.js +11 -2
- package/server/dist/modules/loops/runtime/builtin-loops.js +70 -0
- package/server/dist/modules/loops/runtime/definition-cancellation.js +31 -0
- package/server/dist/modules/loops/runtime/legacy-launch-telemetry.js +78 -0
- package/server/dist/modules/loops/runtime/loop-command-catalog.js +13 -12
- package/server/dist/modules/loops/runtime/loop-compat.js +219 -0
- package/server/dist/modules/loops/runtime/loop-core-factory.js +42 -0
- package/server/dist/modules/loops/runtime/loop-definition-controls.js +48 -0
- package/server/dist/modules/loops/runtime/loop-definition-events.js +145 -0
- package/server/dist/modules/loops/runtime/loop-definition-recovery.js +106 -0
- package/server/dist/modules/loops/runtime/loop-definition-run.js +36 -0
- package/server/dist/modules/loops/runtime/loop-definition.js +246 -0
- package/server/dist/modules/loops/runtime/loop-effect.js +10 -1
- package/server/dist/modules/loops/runtime/loop-executors.js +63 -3
- package/server/dist/modules/loops/runtime/loop-factory.js +35 -15
- package/server/dist/modules/loops/runtime/loop-graph.js +84 -8
- package/server/dist/modules/loops/runtime/loop-migration.js +36 -0
- package/server/dist/modules/loops/runtime/loop-preview.js +10 -0
- package/server/dist/modules/loops/runtime/loop-run-manager.js +206 -13
- package/server/dist/modules/loops/runtime/loop-runs-store.js +271 -7
- package/server/dist/modules/loops/runtime/loop-templates.js +205 -45
- package/server/dist/modules/loops/runtime/loops-router.js +246 -29
- package/server/dist/modules/loops/runtime/loops-store.js +140 -20
- package/server/dist/modules/missions/runtime/agent-operator-prompt.js +14 -11
- package/server/dist/modules/specs/runtime/spec-addenda.js +28 -0
- package/server/dist/project-registry.js +87 -51
- package/server/dist/project-router-jobs.js +17 -6
- package/server/dist/project-router-loop-runs.js +200 -0
- package/server/dist/project-router-spending.js +17 -0
- package/server/dist/project-router.js +8 -6
- package/server/dist/schemas/agent-runtime.schema.json +93 -1
- package/server/dist/schemas/workflow-definition.schema.json +312 -0
- package/server/dist/vitest-setup.js +22 -0
- package/server/dist/worktree-overlay.js +1 -1
- package/client/dist/assets/AgentsPage-DfHWgP_x.js +0 -87
- package/client/dist/assets/DesktopAnalyticsPage-tc0p4jJW.js +0 -1
- package/client/dist/assets/InteractiveJobComposer-DFPpdg2D.js +0 -19
- package/client/dist/assets/JobDetailModal-BdOv-l-M.js +0 -1
- package/client/dist/assets/LoopBuilderPage-BemvUOxy.js +0 -7
- package/client/dist/assets/LoopPreviewModal-C6hNAeYN.js +0 -1
- package/client/dist/assets/LoopsPage-BZr0quvh.js +0 -1
- package/client/dist/assets/TemplatePreviewModal-Cejcquah.js +0 -1
- package/client/dist/assets/agentRuntime-BUimiX__.js +0 -1
- package/client/dist/assets/agentRuntime-BcN97sAB.js +0 -1
- package/client/dist/assets/agentRuntime-Ckyg-ahT.js +0 -1
- package/client/dist/assets/agentRuntime-DJCwyX4t.js +0 -1
- package/client/dist/assets/agentRuntime-DVzPJOl0.js +0 -1
- package/client/dist/assets/agentRuntime-V9AZTG3c.js +0 -1
- package/client/dist/assets/agentRuntime-bAAjw1vw.js +0 -1
- package/client/dist/assets/agentRuntime-v4qKAYH-.js +0 -1
- package/client/dist/assets/agentstudio-BNUth0uj.js +0 -1
- package/client/dist/assets/agentstudio-BNdB5hkr.js +0 -1
- package/client/dist/assets/agentstudio-C7-bt8FO.js +0 -1
- package/client/dist/assets/agentstudio-C8uNchpV.js +0 -1
- package/client/dist/assets/agentstudio-CKabyMlg.js +0 -1
- package/client/dist/assets/agentstudio-CN2pokQu.js +0 -1
- package/client/dist/assets/agentstudio-y2dVDXeM.js +0 -1
- package/client/dist/assets/analytics-BlypLLR6.js +0 -1
- package/client/dist/assets/builder-765NeYTp.js +0 -1
- package/client/dist/assets/builder-8MG3V6ea.js +0 -1
- package/client/dist/assets/builder-B-1UBs5K.js +0 -1
- package/client/dist/assets/builder-BTiGnPT0.js +0 -1
- package/client/dist/assets/builder-Cx9Rz-7u.js +0 -1
- package/client/dist/assets/builder-DKFMELBl.js +0 -1
- package/client/dist/assets/builder-OcmKrC0h.js +0 -1
- package/client/dist/assets/builder-P9qVBXFZ.js +0 -1
- package/client/dist/assets/common-B5AZNYMH.js +0 -1
- package/client/dist/assets/common-BFZ0c_vN.js +0 -1
- package/client/dist/assets/common-BttQx3z4.js +0 -1
- package/client/dist/assets/common-CM65k_-e.js +0 -1
- package/client/dist/assets/common-D4mvFFlu.js +0 -1
- package/client/dist/assets/common-TZ9Ny96S.js +0 -1
- package/client/dist/assets/common-nFpSF4GC.js +0 -1
- package/client/dist/assets/dashboard-B9uWNGO2.js +0 -1
- package/client/dist/assets/dashboard-BIwmO9wc.js +0 -1
- package/client/dist/assets/dashboard-BV4vOZkR.js +0 -1
- package/client/dist/assets/dashboard-Bf6MPVsM.js +0 -1
- package/client/dist/assets/dashboard-C8klWVvZ.js +0 -1
- package/client/dist/assets/dashboard-D1ORhPF0.js +0 -1
- package/client/dist/assets/dashboard-DSubvs9x.js +0 -1
- package/client/dist/assets/dashboard-px_vlRx_.js +0 -1
- package/client/dist/assets/index-C51x61Bz.css +0 -2
- package/client/dist/assets/loops-B7YpGizO.js +0 -1
- package/client/dist/assets/loops-BomD2J-h.js +0 -1
- package/client/dist/assets/loops-BrQHSQw3.js +0 -1
- package/client/dist/assets/loops-BvBwvTPA.js +0 -1
- package/client/dist/assets/loops-CODk6Slz.js +0 -1
- package/client/dist/assets/loops-DC6k-Ym-.js +0 -1
- package/client/dist/assets/loops-DSEg8-uY.js +0 -1
- package/client/dist/assets/loops-DhCoXD_v.js +0 -1
- package/client/dist/assets/settings-BsehKpty.js +0 -1
- package/client/dist/assets/settings-CQWob3SB.js +0 -1
- package/client/dist/assets/settings-CgCl0hzy.js +0 -1
- package/client/dist/assets/settings-DLRLd22L.js +0 -1
- package/client/dist/assets/settings-Dn5prAFl.js +0 -1
- package/client/dist/assets/settings-HZSu-K4l.js +0 -1
- package/client/dist/assets/settings-p4OVIyRh.js +0 -1
- package/client/dist/assets/settings-rddaldUp.js +0 -1
- package/client/dist/assets/useRuntimeRuns-Dqmz9fS9.js +0 -1
- package/docs/guide/de/pipeline/3-batch-implement-and-multi-feature.md +0 -84
- package/docs/guide/en/pipeline/3-batch-implement-and-multi-feature.md +0 -126
- package/docs/guide/es/pipeline/3-batch-implement-and-multi-feature.md +0 -114
- package/docs/guide/fr/pipeline/3-batch-implement-and-multi-feature.md +0 -84
- package/docs/guide/it/pipeline/3-batch-implement-and-multi-feature.md +0 -84
- package/docs/guide/ja/pipeline/3-batch-implement-and-multi-feature.md +0 -84
- package/docs/guide/pt/pipeline/3-batch-implement-and-multi-feature.md +0 -84
- package/docs/guide/zh/pipeline/3-batch-implement-and-multi-feature.md +0 -84
- package/server/dist/modules/loops/runtime/loop-templates-ported.js +0 -675
- /package/client/dist/assets/{LoopBuilderPage-DtxX27Jz.css → loop-layout-DtxX27Jz.css} +0 -0
|
@@ -36,7 +36,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.PrContinuationIsolationError = void 0;
|
|
37
37
|
exports.buildSpecSnapshot = buildSpecSnapshot;
|
|
38
38
|
exports.launchIsolatedRail = launchIsolatedRail;
|
|
39
|
+
exports.reattachIsolatedSettlement = reattachIsolatedSettlement;
|
|
39
40
|
exports.reconcileRailWorktrees = reconcileRailWorktrees;
|
|
41
|
+
const isolated_settlement_reconstruction_1 = require("./isolated-settlement-reconstruction");
|
|
42
|
+
const legacy_launch_telemetry_1 = require("../../loops/runtime/legacy-launch-telemetry");
|
|
40
43
|
/**
|
|
41
44
|
* Isolated (worktree-per-ticket) rail launch — the live wiring that turns the
|
|
42
45
|
* tested building blocks (worktree-manager, merge-manager, rail-merge-orchestrator,
|
|
@@ -73,6 +76,9 @@ const pr_naming_1 = require("./pr-naming");
|
|
|
73
76
|
const jira_db_1 = require("../../../jira/jira-db");
|
|
74
77
|
const db_1 = require("../../../db");
|
|
75
78
|
const delivery_evidence_1 = require("./delivery-evidence");
|
|
79
|
+
const isolated_settlement_store_1 = require("./isolated-settlement-store");
|
|
80
|
+
const loop_runs_store_1 = require("../../loops/runtime/loop-runs-store");
|
|
81
|
+
const loop_definition_recovery_1 = require("../../loops/runtime/loop-definition-recovery");
|
|
76
82
|
const integration_branch_1 = require("../../../integration-branch");
|
|
77
83
|
const repo_lock_1 = require("../../../repo-lock");
|
|
78
84
|
const project_repositories_1 = require("../../../project-repositories");
|
|
@@ -359,7 +365,7 @@ async function captureSettlementIgnoredPaths(git, worktreePath, overlayExcludes)
|
|
|
359
365
|
}
|
|
360
366
|
}
|
|
361
367
|
function branchRecords(results) {
|
|
362
|
-
return results.flatMap((result) => result.run.ticketIds.map((ticketId) => ({
|
|
368
|
+
return results.flatMap((result) => (result.run.ticketIds.length ? result.run.ticketIds : [result.run.ticketId]).map((ticketId) => ({
|
|
363
369
|
ticketId,
|
|
364
370
|
branch: result.run.handle.branch,
|
|
365
371
|
// Legacy consumers interpret this as delivery eligibility, not engine truth.
|
|
@@ -392,6 +398,209 @@ function getRailName(ctx, railIndex) {
|
|
|
392
398
|
return null;
|
|
393
399
|
}
|
|
394
400
|
}
|
|
401
|
+
/** Shared by fresh execution and durable reattachment; one owner of Git settlement. */
|
|
402
|
+
function createIsolatedRunSettlement(ports) {
|
|
403
|
+
const { git, baseRepo, overlaySourceRoot, overlayFallbackRoots, overlayProviderDir, overlayInstructions, isAborted, onFinished, recordRunProvenance, markWorktree, hasRuntimeRequest, commitMessage } = ports;
|
|
404
|
+
return async (a, enginePromise) => {
|
|
405
|
+
let actualOutcome = 'failed';
|
|
406
|
+
let engineFailure;
|
|
407
|
+
let stallReason;
|
|
408
|
+
try {
|
|
409
|
+
const result = await enginePromise;
|
|
410
|
+
actualOutcome = result.outcome;
|
|
411
|
+
stallReason = result.stallReason;
|
|
412
|
+
}
|
|
413
|
+
catch (err) {
|
|
414
|
+
engineFailure = errorDetail(err);
|
|
415
|
+
console.error(`[rail-isolated] loop run ${a.runId} rejected: ${engineFailure}`);
|
|
416
|
+
}
|
|
417
|
+
if (ports.ownsRun && !ports.ownsRun(a.runId))
|
|
418
|
+
throw new Error('Fork owns the original settlement');
|
|
419
|
+
if (isAborted?.()) {
|
|
420
|
+
// The coordinator never started. Do not commit incidental edits or
|
|
421
|
+
// release a mount borrowed from a previous delivery while rolling back.
|
|
422
|
+
const finalSha = await readHeadSha(git, a.handle.worktreePath);
|
|
423
|
+
if (a.worktreeOwnership !== 'created')
|
|
424
|
+
markWorktree(a, 'needs-review');
|
|
425
|
+
return {
|
|
426
|
+
run: a, implementationOutcome: 'failed', deliveryOutcome: 'not_started',
|
|
427
|
+
initialSha: a.initialSha, finalSha, changed: Boolean(finalSha && a.initialSha !== finalSha),
|
|
428
|
+
safeToRelease: a.worktreeOwnership === 'created' && finalSha !== null && finalSha === a.initialSha,
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
const implementationOutcome = actualOutcome === 'success' ? 'succeeded' : 'failed';
|
|
432
|
+
recordRunProvenance(a);
|
|
433
|
+
let callbackFailure;
|
|
434
|
+
try {
|
|
435
|
+
onFinished(a.runId, actualOutcome, stallReason);
|
|
436
|
+
}
|
|
437
|
+
catch (err) {
|
|
438
|
+
callbackFailure = errorDetail(err);
|
|
439
|
+
console.error(`[rail-isolated] terminal callback failed for ${a.runId}: ${callbackFailure}`);
|
|
440
|
+
}
|
|
441
|
+
// A paused/interrupted programmatic workflow owns unfinished changes and
|
|
442
|
+
// verification evidence in this exact mount. Keep them available for
|
|
443
|
+
// explicit Core recovery, including a currently clean worktree.
|
|
444
|
+
if (implementationOutcome === 'failed' && hasRuntimeRequest(a.runId)) {
|
|
445
|
+
markWorktree(a, 'needs-review');
|
|
446
|
+
return {
|
|
447
|
+
run: a, implementationOutcome, deliveryOutcome: 'blocked',
|
|
448
|
+
initialSha: a.initialSha, finalSha: await readHeadSha(git, a.handle.worktreePath),
|
|
449
|
+
failureCode: 'settlement_interrupted',
|
|
450
|
+
failureDetail: 'Programmatic workflow needs attention. Resume it from Agent Runtime settings in its original worktree.',
|
|
451
|
+
safeToRelease: false,
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
// The engine may have edited a copied overlay file. Re-authenticate only
|
|
455
|
+
// automatic-cleanup authority. Allocation-time paths remain conservative
|
|
456
|
+
// NEVER-COMMIT exclusions, while a modified copy is preserved in the
|
|
457
|
+
// worktree instead of being silently staged or deleted.
|
|
458
|
+
if (a.overlayCleanupEvidence.length > 0) {
|
|
459
|
+
a.overlayCleanupEvidence = (0, worktree_overlay_1.revalidateOverlayCleanupEvidence)({
|
|
460
|
+
worktreePath: a.handle.worktreePath,
|
|
461
|
+
sourceRoot: overlaySourceRoot,
|
|
462
|
+
fallbackSourceRoots: overlayFallbackRoots,
|
|
463
|
+
providerDir: overlayProviderDir,
|
|
464
|
+
instructionsFilename: overlayInstructions,
|
|
465
|
+
}, a.overlayCleanupEvidence);
|
|
466
|
+
}
|
|
467
|
+
// Warm-dependency links are re-proven against the base repo (their source),
|
|
468
|
+
// not the framework overlay source. A link the engine replaced with a real
|
|
469
|
+
// install loses its authorization here and correctly preserves the worktree.
|
|
470
|
+
if (a.warmLinkEvidence.length > 0) {
|
|
471
|
+
a.warmLinkEvidence = (0, worktree_node_modules_1.authenticateWarmNodeModulesLinks)(baseRepo, a.handle.worktreePath)
|
|
472
|
+
.filter((live) => a.warmLinkEvidence.some((prior) => prior.path === live.path && prior.kind === live.kind && prior.digest === live.digest));
|
|
473
|
+
}
|
|
474
|
+
let commit;
|
|
475
|
+
try {
|
|
476
|
+
commit = await (0, worktree_manager_1.commitWorktreeAndVerify)(git, a.handle.worktreePath, commitMessage(a, implementationOutcome === 'failed'), a.overlayExcludes);
|
|
477
|
+
}
|
|
478
|
+
catch (err) {
|
|
479
|
+
commit = { staged: false, committed: false, clean: false, dirty: [], error: errorDetail(err) };
|
|
480
|
+
}
|
|
481
|
+
if (!commit.clean) {
|
|
482
|
+
const detail = commitFailureSummary(commit);
|
|
483
|
+
console.error(`[rail-isolated] run ${a.runId} left an unsafe worktree: ${detail}`);
|
|
484
|
+
markWorktree(a, 'needs-review');
|
|
485
|
+
return {
|
|
486
|
+
run: a,
|
|
487
|
+
implementationOutcome,
|
|
488
|
+
deliveryOutcome: 'blocked',
|
|
489
|
+
initialSha: a.initialSha,
|
|
490
|
+
finalSha: await readHeadSha(git, a.handle.worktreePath),
|
|
491
|
+
failureCode: 'commit_failed',
|
|
492
|
+
failureDetail: detail,
|
|
493
|
+
safeToRelease: false,
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
// The worktree is proven clean THIS instant — freeze the ignored-path set
|
|
497
|
+
// as the settlement snapshot. Everything ignored right now is run residue
|
|
498
|
+
// by construction (the worktree is app-created and deliverables are
|
|
499
|
+
// committed); anything ignored that appears LATER preserves the worktree.
|
|
500
|
+
a.settlementIgnoredPaths = a.automaticRelease === false ? null : await captureSettlementIgnoredPaths(git, a.handle.worktreePath, a.overlayExcludes);
|
|
501
|
+
let finalSha = null;
|
|
502
|
+
let verificationFailure;
|
|
503
|
+
if (a.continuationTarget) {
|
|
504
|
+
try {
|
|
505
|
+
finalSha = await verifyContinuationWorktree(git, baseRepo, a.handle, a.continuationTarget);
|
|
506
|
+
}
|
|
507
|
+
catch (err) {
|
|
508
|
+
verificationFailure = errorDetail(err);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
else {
|
|
512
|
+
finalSha = await readHeadSha(git, a.handle.worktreePath);
|
|
513
|
+
}
|
|
514
|
+
const changed = commit.committed || Boolean(a.initialSha && finalSha && a.initialSha !== finalSha);
|
|
515
|
+
// A successful commit without a provable object is not retryable or safe to
|
|
516
|
+
// detach. A continuation additionally requires HEAD == refs/heads/<branch>.
|
|
517
|
+
if (verificationFailure || !finalSha || (!commit.committed && a.branchOwnership === 'created' && !a.initialSha)) {
|
|
518
|
+
const detail = verificationFailure
|
|
519
|
+
?? (!finalSha
|
|
520
|
+
? 'the final HEAD object could not be verified'
|
|
521
|
+
: 'the initial HEAD object was not captured, so a no-change result cannot be proven');
|
|
522
|
+
console.error(`[rail-isolated] run ${a.runId} finished on an unverified ref: ${detail}`);
|
|
523
|
+
markWorktree(a, 'needs-review');
|
|
524
|
+
return {
|
|
525
|
+
run: a,
|
|
526
|
+
implementationOutcome,
|
|
527
|
+
deliveryOutcome: 'blocked',
|
|
528
|
+
initialSha: a.initialSha,
|
|
529
|
+
finalSha,
|
|
530
|
+
changed,
|
|
531
|
+
failureCode: 'branch_verification_failed',
|
|
532
|
+
failureDetail: detail,
|
|
533
|
+
safeToRelease: false,
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
// Failed clean units remain `building` until aggregate cleanup removes the
|
|
537
|
+
// mount and atomically terminalizes the ledger as failed. Marking them
|
|
538
|
+
// terminal first would make idempotent cleanup skip a still-mounted path.
|
|
539
|
+
const ledgerFailure = implementationOutcome === 'succeeded'
|
|
540
|
+
? (markWorktree(a, 'built') ?? undefined)
|
|
541
|
+
: undefined;
|
|
542
|
+
const settlementFailure = callbackFailure ?? ledgerFailure;
|
|
543
|
+
if (settlementFailure) {
|
|
544
|
+
markWorktree(a, 'needs-review');
|
|
545
|
+
return {
|
|
546
|
+
run: a,
|
|
547
|
+
implementationOutcome,
|
|
548
|
+
deliveryOutcome: 'blocked',
|
|
549
|
+
initialSha: a.initialSha,
|
|
550
|
+
finalSha,
|
|
551
|
+
changed,
|
|
552
|
+
failureCode: 'settlement_interrupted',
|
|
553
|
+
failureDetail: settlementFailure,
|
|
554
|
+
safeToRelease: false,
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
if (implementationOutcome === 'failed') {
|
|
558
|
+
return {
|
|
559
|
+
run: a,
|
|
560
|
+
implementationOutcome,
|
|
561
|
+
deliveryOutcome: 'not_started',
|
|
562
|
+
initialSha: a.initialSha,
|
|
563
|
+
finalSha,
|
|
564
|
+
changed,
|
|
565
|
+
...(engineFailure ? { failureDetail: engineFailure } : {}),
|
|
566
|
+
safeToRelease: a.automaticRelease !== false,
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
// A resumed non-PR branch may already contain reviewable commits from an
|
|
570
|
+
// earlier attempt, even when this iteration itself did not add one.
|
|
571
|
+
let resumedBranchDelta = null;
|
|
572
|
+
if (!changed && a.continuationTarget === null && a.branchOwnership === 'preexisting') {
|
|
573
|
+
resumedBranchDelta = await branchHasDelta(git, a.handle.worktreePath, a.baseRef);
|
|
574
|
+
if (resumedBranchDelta === null) {
|
|
575
|
+
const detail = `cannot prove whether resumed branch ${a.handle.branch} has commits ahead of ${a.baseRef}`;
|
|
576
|
+
markWorktree(a, 'needs-review');
|
|
577
|
+
return {
|
|
578
|
+
run: a,
|
|
579
|
+
implementationOutcome,
|
|
580
|
+
deliveryOutcome: 'blocked',
|
|
581
|
+
initialSha: a.initialSha,
|
|
582
|
+
finalSha,
|
|
583
|
+
changed,
|
|
584
|
+
failureCode: 'branch_verification_failed',
|
|
585
|
+
failureDetail: detail,
|
|
586
|
+
safeToRelease: false,
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
const noChanges = !changed && Boolean(a.initialSha && finalSha && a.initialSha === finalSha && (a.continuationTarget !== null ||
|
|
591
|
+
a.branchOwnership === 'created' ||
|
|
592
|
+
resumedBranchDelta === false));
|
|
593
|
+
return {
|
|
594
|
+
run: a,
|
|
595
|
+
implementationOutcome,
|
|
596
|
+
deliveryOutcome: noChanges ? 'no_changes' : 'ready',
|
|
597
|
+
initialSha: a.initialSha,
|
|
598
|
+
finalSha,
|
|
599
|
+
changed,
|
|
600
|
+
safeToRelease: a.automaticRelease !== false,
|
|
601
|
+
};
|
|
602
|
+
};
|
|
603
|
+
}
|
|
395
604
|
async function launchIsolatedRail(input, io = {}) {
|
|
396
605
|
const { ctx, railIndex, ticketIds, loopId, loopName, loopGraph, provider, model, effort, deciderEngine } = input;
|
|
397
606
|
let expectedRepositoryBaseSha = input.repositoryExecution?.expectedBaseSha;
|
|
@@ -936,6 +1145,19 @@ async function launchIsolatedRail(input, io = {}) {
|
|
|
936
1145
|
throw err;
|
|
937
1146
|
}
|
|
938
1147
|
}
|
|
1148
|
+
// Durable admission is part of allocation: if any snapshot cannot commit,
|
|
1149
|
+
// roll back the entire batch before releasing the repository lock or
|
|
1150
|
+
// starting Core. The same ownership-aware cleanup handles that failure.
|
|
1151
|
+
if (prDeliveryId && (0, loop_graph_1.isDefinitionGraph)(loopGraph))
|
|
1152
|
+
ctx.db.transaction(() => {
|
|
1153
|
+
for (const run of allocated)
|
|
1154
|
+
(0, isolated_settlement_store_1.saveIsolatedSettlementSnapshot)(ctx.db, {
|
|
1155
|
+
version: 1, projectId: ctx.project.id, deliveryId: prDeliveryId, baseRepo,
|
|
1156
|
+
overlaySourceRoot, overlayFallbackRoots, overlayProviderDir, overlayInstructions,
|
|
1157
|
+
commitMessage: worktreeCommitMessage(ctx, run.ticketId, run.runId),
|
|
1158
|
+
partialCommitMessage: worktreeCommitMessage(ctx, run.ticketId, run.runId, true), run,
|
|
1159
|
+
});
|
|
1160
|
+
})();
|
|
939
1161
|
}
|
|
940
1162
|
catch (err) {
|
|
941
1163
|
for (const a of allocated) {
|
|
@@ -997,7 +1219,7 @@ async function launchIsolatedRail(input, io = {}) {
|
|
|
997
1219
|
return;
|
|
998
1220
|
provenanceRecorded.add(a.runId);
|
|
999
1221
|
try {
|
|
1000
|
-
recordProvenance({
|
|
1222
|
+
const record = () => recordProvenance({
|
|
1001
1223
|
db: ctx.db,
|
|
1002
1224
|
projectId: ctx.project.id,
|
|
1003
1225
|
runId: a.runId,
|
|
@@ -1007,6 +1229,10 @@ async function launchIsolatedRail(input, io = {}) {
|
|
|
1007
1229
|
snapshot: a.provenanceSnapshot,
|
|
1008
1230
|
broadcast: (msg) => ctx.broadcast(msg),
|
|
1009
1231
|
});
|
|
1232
|
+
if (prDeliveryId && (0, loop_graph_1.isDefinitionGraph)(loopGraph))
|
|
1233
|
+
(0, isolated_settlement_store_1.recordIsolatedProvenanceOnce)(ctx.db, prDeliveryId, a.runId, record);
|
|
1234
|
+
else
|
|
1235
|
+
record();
|
|
1010
1236
|
}
|
|
1011
1237
|
catch (err) {
|
|
1012
1238
|
console.warn(`[rail-isolated] provenance recording failed for ${a.runId}: ${errorDetail(err)}`);
|
|
@@ -1022,206 +1248,15 @@ async function launchIsolatedRail(input, io = {}) {
|
|
|
1022
1248
|
return errorDetail(err);
|
|
1023
1249
|
}
|
|
1024
1250
|
};
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
actualOutcome = result.outcome;
|
|
1035
|
-
stallReason = result.stallReason;
|
|
1036
|
-
}
|
|
1037
|
-
catch (err) {
|
|
1038
|
-
engineFailure = errorDetail(err);
|
|
1039
|
-
console.error(`[rail-isolated] loop run ${a.runId} rejected: ${engineFailure}`);
|
|
1040
|
-
}
|
|
1041
|
-
if (input.repositoryExecution?.isAborted?.()) {
|
|
1042
|
-
// The coordinator never started. Do not commit incidental edits or
|
|
1043
|
-
// release a mount borrowed from a previous delivery while rolling back.
|
|
1044
|
-
const finalSha = await readHeadSha(git, a.handle.worktreePath);
|
|
1045
|
-
if (a.worktreeOwnership !== 'created')
|
|
1046
|
-
markWorktree(a, 'needs-review');
|
|
1047
|
-
return {
|
|
1048
|
-
run: a, implementationOutcome: 'failed', deliveryOutcome: 'not_started',
|
|
1049
|
-
initialSha: a.initialSha, finalSha, changed: Boolean(finalSha && a.initialSha !== finalSha),
|
|
1050
|
-
safeToRelease: a.worktreeOwnership === 'created' && finalSha !== null && finalSha === a.initialSha,
|
|
1051
|
-
};
|
|
1052
|
-
}
|
|
1053
|
-
const implementationOutcome = actualOutcome === 'success' ? 'succeeded' : 'failed';
|
|
1054
|
-
recordRunProvenance(a);
|
|
1055
|
-
let callbackFailure;
|
|
1056
|
-
try {
|
|
1057
|
-
ctx.onLoopRunFinished(a.runId, actualOutcome, { ...runFinishedOpts, ...(stallReason ? { stallReason } : {}) });
|
|
1058
|
-
}
|
|
1059
|
-
catch (err) {
|
|
1060
|
-
callbackFailure = errorDetail(err);
|
|
1061
|
-
console.error(`[rail-isolated] terminal callback failed for ${a.runId}: ${callbackFailure}`);
|
|
1062
|
-
}
|
|
1063
|
-
// A paused/interrupted programmatic workflow owns unfinished changes and
|
|
1064
|
-
// verification evidence in this exact mount. Keep them available for
|
|
1065
|
-
// explicit Core recovery, including a currently clean worktree.
|
|
1066
|
-
if (implementationOutcome === 'failed' && (0, agent_runtime_paths_1.hasAgentRuntimeRequest)(input.runtimeStateProject ?? ctx.project, a.runId)) {
|
|
1067
|
-
markWorktree(a, 'needs-review');
|
|
1068
|
-
return {
|
|
1069
|
-
run: a, implementationOutcome, deliveryOutcome: 'blocked',
|
|
1070
|
-
initialSha: a.initialSha, finalSha: await readHeadSha(git, a.handle.worktreePath),
|
|
1071
|
-
failureCode: 'settlement_interrupted',
|
|
1072
|
-
failureDetail: 'Programmatic workflow needs attention. Resume it from Agent Runtime settings in its original worktree.',
|
|
1073
|
-
safeToRelease: false,
|
|
1074
|
-
};
|
|
1075
|
-
}
|
|
1076
|
-
// The engine may have edited a copied overlay file. Re-authenticate only
|
|
1077
|
-
// automatic-cleanup authority. Allocation-time paths remain conservative
|
|
1078
|
-
// NEVER-COMMIT exclusions, while a modified copy is preserved in the
|
|
1079
|
-
// worktree instead of being silently staged or deleted.
|
|
1080
|
-
if (a.overlayCleanupEvidence.length > 0) {
|
|
1081
|
-
a.overlayCleanupEvidence = (0, worktree_overlay_1.revalidateOverlayCleanupEvidence)({
|
|
1082
|
-
worktreePath: a.handle.worktreePath,
|
|
1083
|
-
sourceRoot: overlaySourceRoot,
|
|
1084
|
-
fallbackSourceRoots: overlayFallbackRoots,
|
|
1085
|
-
providerDir: overlayProviderDir,
|
|
1086
|
-
instructionsFilename: overlayInstructions,
|
|
1087
|
-
}, a.overlayCleanupEvidence);
|
|
1088
|
-
}
|
|
1089
|
-
// Warm-dependency links are re-proven against the base repo (their source),
|
|
1090
|
-
// not the framework overlay source. A link the engine replaced with a real
|
|
1091
|
-
// install loses its authorization here and correctly preserves the worktree.
|
|
1092
|
-
if (a.warmLinkEvidence.length > 0) {
|
|
1093
|
-
a.warmLinkEvidence = (0, worktree_node_modules_1.authenticateWarmNodeModulesLinks)(baseRepo, a.handle.worktreePath)
|
|
1094
|
-
.filter((live) => a.warmLinkEvidence.some((prior) => prior.path === live.path && prior.kind === live.kind && prior.digest === live.digest));
|
|
1095
|
-
}
|
|
1096
|
-
let commit;
|
|
1097
|
-
try {
|
|
1098
|
-
commit = await (0, worktree_manager_1.commitWorktreeAndVerify)(git, a.handle.worktreePath, worktreeCommitMessage(ctx, a.ticketId, a.runId, implementationOutcome === 'failed'), a.overlayExcludes);
|
|
1099
|
-
}
|
|
1100
|
-
catch (err) {
|
|
1101
|
-
commit = { staged: false, committed: false, clean: false, dirty: [], error: errorDetail(err) };
|
|
1102
|
-
}
|
|
1103
|
-
if (!commit.clean) {
|
|
1104
|
-
const detail = commitFailureSummary(commit);
|
|
1105
|
-
console.error(`[rail-isolated] run ${a.runId} left an unsafe worktree: ${detail}`);
|
|
1106
|
-
markWorktree(a, 'needs-review');
|
|
1107
|
-
return {
|
|
1108
|
-
run: a,
|
|
1109
|
-
implementationOutcome,
|
|
1110
|
-
deliveryOutcome: 'blocked',
|
|
1111
|
-
initialSha: a.initialSha,
|
|
1112
|
-
finalSha: await readHeadSha(git, a.handle.worktreePath),
|
|
1113
|
-
failureCode: 'commit_failed',
|
|
1114
|
-
failureDetail: detail,
|
|
1115
|
-
safeToRelease: false,
|
|
1116
|
-
};
|
|
1117
|
-
}
|
|
1118
|
-
// The worktree is proven clean THIS instant — freeze the ignored-path set
|
|
1119
|
-
// as the settlement snapshot. Everything ignored right now is run residue
|
|
1120
|
-
// by construction (the worktree is app-created and deliverables are
|
|
1121
|
-
// committed); anything ignored that appears LATER preserves the worktree.
|
|
1122
|
-
a.settlementIgnoredPaths = await captureSettlementIgnoredPaths(git, a.handle.worktreePath, a.overlayExcludes);
|
|
1123
|
-
let finalSha = null;
|
|
1124
|
-
let verificationFailure;
|
|
1125
|
-
if (a.continuationTarget) {
|
|
1126
|
-
try {
|
|
1127
|
-
finalSha = await verifyContinuationWorktree(git, baseRepo, a.handle, a.continuationTarget);
|
|
1128
|
-
}
|
|
1129
|
-
catch (err) {
|
|
1130
|
-
verificationFailure = errorDetail(err);
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
else {
|
|
1134
|
-
finalSha = await readHeadSha(git, a.handle.worktreePath);
|
|
1135
|
-
}
|
|
1136
|
-
const changed = commit.committed || Boolean(a.initialSha && finalSha && a.initialSha !== finalSha);
|
|
1137
|
-
// A successful commit without a provable object is not retryable or safe to
|
|
1138
|
-
// detach. A continuation additionally requires HEAD == refs/heads/<branch>.
|
|
1139
|
-
if (verificationFailure || !finalSha || (!commit.committed && a.branchOwnership === 'created' && !a.initialSha)) {
|
|
1140
|
-
const detail = verificationFailure
|
|
1141
|
-
?? (!finalSha
|
|
1142
|
-
? 'the final HEAD object could not be verified'
|
|
1143
|
-
: 'the initial HEAD object was not captured, so a no-change result cannot be proven');
|
|
1144
|
-
console.error(`[rail-isolated] run ${a.runId} finished on an unverified ref: ${detail}`);
|
|
1145
|
-
markWorktree(a, 'needs-review');
|
|
1146
|
-
return {
|
|
1147
|
-
run: a,
|
|
1148
|
-
implementationOutcome,
|
|
1149
|
-
deliveryOutcome: 'blocked',
|
|
1150
|
-
initialSha: a.initialSha,
|
|
1151
|
-
finalSha,
|
|
1152
|
-
changed,
|
|
1153
|
-
failureCode: 'branch_verification_failed',
|
|
1154
|
-
failureDetail: detail,
|
|
1155
|
-
safeToRelease: false,
|
|
1156
|
-
};
|
|
1157
|
-
}
|
|
1158
|
-
// Failed clean units remain `building` until aggregate cleanup removes the
|
|
1159
|
-
// mount and atomically terminalizes the ledger as failed. Marking them
|
|
1160
|
-
// terminal first would make idempotent cleanup skip a still-mounted path.
|
|
1161
|
-
const ledgerFailure = implementationOutcome === 'succeeded'
|
|
1162
|
-
? (markWorktree(a, 'built') ?? undefined)
|
|
1163
|
-
: undefined;
|
|
1164
|
-
const settlementFailure = callbackFailure ?? ledgerFailure;
|
|
1165
|
-
if (settlementFailure) {
|
|
1166
|
-
markWorktree(a, 'needs-review');
|
|
1167
|
-
return {
|
|
1168
|
-
run: a,
|
|
1169
|
-
implementationOutcome,
|
|
1170
|
-
deliveryOutcome: 'blocked',
|
|
1171
|
-
initialSha: a.initialSha,
|
|
1172
|
-
finalSha,
|
|
1173
|
-
changed,
|
|
1174
|
-
failureCode: 'settlement_interrupted',
|
|
1175
|
-
failureDetail: settlementFailure,
|
|
1176
|
-
safeToRelease: false,
|
|
1177
|
-
};
|
|
1178
|
-
}
|
|
1179
|
-
if (implementationOutcome === 'failed') {
|
|
1180
|
-
return {
|
|
1181
|
-
run: a,
|
|
1182
|
-
implementationOutcome,
|
|
1183
|
-
deliveryOutcome: 'not_started',
|
|
1184
|
-
initialSha: a.initialSha,
|
|
1185
|
-
finalSha,
|
|
1186
|
-
changed,
|
|
1187
|
-
...(engineFailure ? { failureDetail: engineFailure } : {}),
|
|
1188
|
-
safeToRelease: true,
|
|
1189
|
-
};
|
|
1190
|
-
}
|
|
1191
|
-
// A resumed non-PR branch may already contain reviewable commits from an
|
|
1192
|
-
// earlier attempt, even when this iteration itself did not add one.
|
|
1193
|
-
let resumedBranchDelta = null;
|
|
1194
|
-
if (!changed && a.continuationTarget === null && a.branchOwnership === 'preexisting') {
|
|
1195
|
-
resumedBranchDelta = await branchHasDelta(git, a.handle.worktreePath, a.baseRef);
|
|
1196
|
-
if (resumedBranchDelta === null) {
|
|
1197
|
-
const detail = `cannot prove whether resumed branch ${a.handle.branch} has commits ahead of ${a.baseRef}`;
|
|
1198
|
-
markWorktree(a, 'needs-review');
|
|
1199
|
-
return {
|
|
1200
|
-
run: a,
|
|
1201
|
-
implementationOutcome,
|
|
1202
|
-
deliveryOutcome: 'blocked',
|
|
1203
|
-
initialSha: a.initialSha,
|
|
1204
|
-
finalSha,
|
|
1205
|
-
changed,
|
|
1206
|
-
failureCode: 'branch_verification_failed',
|
|
1207
|
-
failureDetail: detail,
|
|
1208
|
-
safeToRelease: false,
|
|
1209
|
-
};
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
const noChanges = !changed && Boolean(a.initialSha && finalSha && a.initialSha === finalSha && (a.continuationTarget !== null ||
|
|
1213
|
-
a.branchOwnership === 'created' ||
|
|
1214
|
-
resumedBranchDelta === false));
|
|
1215
|
-
return {
|
|
1216
|
-
run: a,
|
|
1217
|
-
implementationOutcome,
|
|
1218
|
-
deliveryOutcome: noChanges ? 'no_changes' : 'ready',
|
|
1219
|
-
initialSha: a.initialSha,
|
|
1220
|
-
finalSha,
|
|
1221
|
-
changed,
|
|
1222
|
-
safeToRelease: true,
|
|
1223
|
-
};
|
|
1224
|
-
};
|
|
1251
|
+
const settleAllocatedRun = createIsolatedRunSettlement({
|
|
1252
|
+
git, baseRepo, overlaySourceRoot, overlayFallbackRoots, overlayProviderDir, overlayInstructions,
|
|
1253
|
+
isAborted: input.repositoryExecution?.isAborted,
|
|
1254
|
+
ownsRun: runId => !(0, loop_runs_store_1.readDefinitionForkTarget)(ctx.db, runId),
|
|
1255
|
+
onFinished: (runId, outcome, stallReason) => ctx.onLoopRunFinished(runId, outcome, { ...runFinishedOpts, ...(stallReason ? { stallReason } : {}) }),
|
|
1256
|
+
recordRunProvenance, markWorktree,
|
|
1257
|
+
hasRuntimeRequest: runId => (0, agent_runtime_paths_1.hasAgentRuntimeRequest)(input.runtimeStateProject ?? ctx.project, runId),
|
|
1258
|
+
commitMessage: (run, partial) => worktreeCommitMessage(ctx, run.ticketId, run.runId, partial),
|
|
1259
|
+
});
|
|
1225
1260
|
const runPromises = [];
|
|
1226
1261
|
for (const a of allocated) {
|
|
1227
1262
|
ctx.railLoopRuns.set(a.runId, {
|
|
@@ -1259,7 +1294,14 @@ async function launchIsolatedRail(input, io = {}) {
|
|
|
1259
1294
|
...(input.followUp ? { followUp: { id: input.followUp.id, version: input.followUp.version, hash: input.followUp.hash, openspecChangeName: input.followUp.openspecChangeName ?? null, briefing: (0, pr_follow_up_1.renderFollowUpBriefing)(input.followUp, { prNumber: launchContinuation?.prNumber ?? input.explicitPrTarget?.prNumber ?? null, ticketIds }) } } : {}),
|
|
1260
1295
|
...(claimedAddenda.briefing ? { addenda: { ids: claimedAddenda.snapshot.map((e) => e.id), briefing: claimedAddenda.briefing } } : {}),
|
|
1261
1296
|
});
|
|
1262
|
-
runPromises.push(settleAllocatedRun(a, enginePromise))
|
|
1297
|
+
runPromises.push(settleAllocatedRun(a, enginePromise).then(async (result) => {
|
|
1298
|
+
if (prDeliveryId && (0, loop_graph_1.isDefinitionGraph)(loopGraph))
|
|
1299
|
+
(0, isolated_settlement_store_1.saveIsolatedSettlementResult)(ctx.db, prDeliveryId, result);
|
|
1300
|
+
if (prDeliveryId && (0, loop_graph_1.isDefinitionGraph)(loopGraph) && allocated.some(unit => (0, loop_runs_store_1.readDefinitionForkTarget)(ctx.db, unit.runId))) {
|
|
1301
|
+
await reattachIsolatedSettlement(ctx, prDeliveryId, a.runId).catch(error => console.error('[rail-isolated] fork sibling settlement remains pending:', error));
|
|
1302
|
+
}
|
|
1303
|
+
return result;
|
|
1304
|
+
}));
|
|
1263
1305
|
try {
|
|
1264
1306
|
ctx.jiraSyncManager.onRailLaunch(a.ticketIds, a.runId);
|
|
1265
1307
|
}
|
|
@@ -1282,6 +1324,8 @@ async function launchIsolatedRail(input, io = {}) {
|
|
|
1282
1324
|
// the draft PR is only created when the user clicks [Create PR] on the
|
|
1283
1325
|
// pr-decision endpoint (ask-first, safe-pr-review-flow).
|
|
1284
1326
|
const guardedRunPromises = runPromises.map((promise, index) => promise.catch(async (err) => {
|
|
1327
|
+
if ((0, loop_runs_store_1.readDefinitionForkTarget)(ctx.db, allocated[index].runId))
|
|
1328
|
+
return { run: allocated[index], implementationOutcome: 'failed', deliveryOutcome: 'not_started', initialSha: allocated[index].initialSha, finalSha: null, safeToRelease: false };
|
|
1285
1329
|
const run = allocated[index];
|
|
1286
1330
|
const detail = errorDetail(err);
|
|
1287
1331
|
console.error(`[rail-isolated] unexpected settlement rejection for ${run.runId}: ${detail}`);
|
|
@@ -1300,6 +1344,8 @@ async function launchIsolatedRail(input, io = {}) {
|
|
|
1300
1344
|
};
|
|
1301
1345
|
}));
|
|
1302
1346
|
void Promise.all(guardedRunPromises).then(async (settledResults) => {
|
|
1347
|
+
if (allocated.some(unit => (0, loop_runs_store_1.readDefinitionForkTarget)(ctx.db, unit.runId)))
|
|
1348
|
+
return;
|
|
1303
1349
|
// Cardinality is invariant: one structured result per allocated unit. The
|
|
1304
1350
|
// guarded promises above synthesize a blocked record for any rejection.
|
|
1305
1351
|
let results = settledResults;
|
|
@@ -1590,9 +1636,12 @@ async function launchIsolatedRail(input, io = {}) {
|
|
|
1590
1636
|
const harvest = io.harvestEvidence ?? delivery_evidence_1.harvestDeliveryEvidence;
|
|
1591
1637
|
let settleEvidence;
|
|
1592
1638
|
try {
|
|
1639
|
+
const definitionEvidence = await (0, loop_definition_recovery_1.probeDefinitionRuns)({ db: ctx.db, cwd: ctx.project.path, env: process.env }, results.filter(result => (0, loop_runs_store_1.getLoopRun)(ctx.db, result.run.runId)?.engine_version === 2).map(result => result.run.runId), true);
|
|
1593
1640
|
settleEvidence = harvest({ readEvents: (runId) => (0, db_1.getJobEvents)(ctx.db, runId) }, results.map((result) => ({
|
|
1594
1641
|
ticketId: result.run.ticketId,
|
|
1595
1642
|
runId: result.run.runId,
|
|
1643
|
+
definitionStatus: definitionEvidence.get(result.run.runId),
|
|
1644
|
+
reviewerStepId: (0, loop_runs_store_1.readDefinitionRun)(ctx.db, result.run.runId)?.request.graph.config.reviewerStepId,
|
|
1596
1645
|
worktreePath: result.run.handle.worktreePath,
|
|
1597
1646
|
loopId,
|
|
1598
1647
|
// Programmatic-runtime evidence (host-run verification, reviewer
|
|
@@ -1674,6 +1723,8 @@ async function launchIsolatedRail(input, io = {}) {
|
|
|
1674
1723
|
succeeded: result.deliveryOutcome === 'ready',
|
|
1675
1724
|
})));
|
|
1676
1725
|
try {
|
|
1726
|
+
if (branches.length)
|
|
1727
|
+
(0, legacy_launch_telemetry_1.recordLegacyLaunch)(ctx.db, { kind: 'merge_back', projectId: ctx.project.id, runId: allocated[0]?.runId });
|
|
1677
1728
|
const outcomes = await (0, rail_merge_orchestrator_1.runMergeBack)({
|
|
1678
1729
|
git, executor: (0, loop_executors_1.createLoopExecutors)(), baseDir: baseRepo,
|
|
1679
1730
|
provider, model, effort, ...(deciderEngine ? { deciderEngine } : {}), constants, branches,
|
|
@@ -1723,6 +1774,8 @@ async function launchIsolatedRail(input, io = {}) {
|
|
|
1723
1774
|
console.error('[rail-isolated] merge-back failed:', err);
|
|
1724
1775
|
}
|
|
1725
1776
|
}).catch(async (err) => {
|
|
1777
|
+
if (allocated.some(unit => (0, loop_runs_store_1.readDefinitionForkTarget)(ctx.db, unit.runId)))
|
|
1778
|
+
return;
|
|
1726
1779
|
const detail = errorDetail(err);
|
|
1727
1780
|
console.error(`[rail-isolated] aggregate settlement failed: ${detail}`);
|
|
1728
1781
|
if (!prMode || !prDeliveryId || (0, rail_pr_store_1.getPrDelivery)(ctx.db, prDeliveryId)?.decision !== 'building')
|
|
@@ -1750,6 +1803,138 @@ async function launchIsolatedRail(input, io = {}) {
|
|
|
1750
1803
|
}).finally(() => { input.repositoryExecution?.onSettled(prDeliveryId); });
|
|
1751
1804
|
return allocated.map((a) => a.runId);
|
|
1752
1805
|
}
|
|
1806
|
+
/** Git and Node can spell the same Windows directory differently. Compare its
|
|
1807
|
+
* filesystem identity instead of case-folding paths (which could admit another
|
|
1808
|
+
* repository on a case-sensitive volume). */
|
|
1809
|
+
function sameRecoveryDirectory(observed, expected) {
|
|
1810
|
+
const left = fs.statSync(observed, { bigint: true });
|
|
1811
|
+
const right = fs.statSync(expected, { bigint: true });
|
|
1812
|
+
if (!left.isDirectory() || !right.isDirectory())
|
|
1813
|
+
return false;
|
|
1814
|
+
if (left.ino !== 0n && right.ino !== 0n)
|
|
1815
|
+
return left.dev === right.dev && left.ino === right.ino;
|
|
1816
|
+
return fs.realpathSync.native(observed) === fs.realpathSync.native(expected);
|
|
1817
|
+
}
|
|
1818
|
+
/** Reattach the original allocation after Core finishes; no new generation or remote effects. */
|
|
1819
|
+
async function reattachIsolatedSettlement(ctx, deliveryId, runId, io = {}) {
|
|
1820
|
+
const frozen = (0, loop_runs_store_1.readDefinitionRun)(ctx.db, runId);
|
|
1821
|
+
if (!frozen || frozen.row.project_id !== ctx.project.id || frozen.row.status !== 'completed')
|
|
1822
|
+
throw new Error('Recovered workflow must finish before delivery settlement');
|
|
1823
|
+
(0, isolated_settlement_reconstruction_1.ensureIsolatedSettlementSnapshot)(ctx.db, ctx.project.id, runId);
|
|
1824
|
+
const ids = ctx.db.prepare('SELECT delivery_id FROM definition_delivery_settlements WHERE project_id=? AND run_id=? ORDER BY delivery_id')
|
|
1825
|
+
.all(ctx.project.id, runId);
|
|
1826
|
+
const rows = ids.map(({ delivery_id }) => (0, rail_pr_store_1.getPrDelivery)(ctx.db, delivery_id));
|
|
1827
|
+
if (!rows.length || rows.some(row => !row) || !rows.some(row => row.id === deliveryId || row.parent_delivery_id === deliveryId))
|
|
1828
|
+
throw new Error('Original isolated settlement is unavailable');
|
|
1829
|
+
const deliveries = rows.filter((row) => !!row);
|
|
1830
|
+
if (deliveries.some(row => ['discarded', 'superseded', 'merged', 'completed'].includes(row.decision)))
|
|
1831
|
+
throw new Error('Delivery ownership has already changed');
|
|
1832
|
+
const parents = [...new Set(deliveries.map(row => row.parent_delivery_id).filter((id) => !!id))];
|
|
1833
|
+
const allRows = [...deliveries, ...parents.map(id => (0, rail_pr_store_1.getPrDelivery)(ctx.db, id)).filter((row) => !!row)];
|
|
1834
|
+
if (allRows.some(row => ['discarded', 'superseded', 'merged', 'completed'].includes(row.decision)))
|
|
1835
|
+
throw new Error('Delivery group ownership has already changed');
|
|
1836
|
+
const pending = deliveries.filter(row => !['on_review', 'no_changes', 'pr_draft', 'pr_ready'].includes(row.decision));
|
|
1837
|
+
if (!pending.length) {
|
|
1838
|
+
ctx.onLoopRunFinished(runId, frozen.row.final_outcome ?? 'failed', { ticketCompletionStatus: 'on_review' });
|
|
1839
|
+
return;
|
|
1840
|
+
}
|
|
1841
|
+
// Terminal admission must not depend on optional, potentially large evidence
|
|
1842
|
+
// outputs. A failed full-status harvest stays visible in the review packet.
|
|
1843
|
+
const probe = await (0, loop_definition_recovery_1.probeDefinitionRun)({ db: ctx.db, cwd: ctx.project.path, env: process.env }, runId);
|
|
1844
|
+
if (probe.status === 'unavailable' || probe.lease?.active || ['paused', 'running'].includes(probe.status))
|
|
1845
|
+
throw new Error('Core has not provided an inactive terminal result');
|
|
1846
|
+
const policy = frozen.metadata.definition?.delivery;
|
|
1847
|
+
const accepted = probe.status === 'succeeded' && probe.completion?.ok === true && (policy?.requiresVerified === false || probe.completion.verified);
|
|
1848
|
+
const outcome = accepted ? 'success' : probe.status === 'cancelled' ? 'stopped' : 'blocked';
|
|
1849
|
+
const token = (0, ids_1.newId)();
|
|
1850
|
+
const execution = (0, loop_runs_store_1.claimDefinitionExecution)(ctx.db, runId, { owner: token, repositoryMounts: (0, loop_runs_store_1.definitionRepositoryMounts)(frozen.request) });
|
|
1851
|
+
if (!execution.ok)
|
|
1852
|
+
throw new Error(`runtime_run_active: ${execution.conflictingRunId}`);
|
|
1853
|
+
const claimed = [];
|
|
1854
|
+
const git = io.git ?? worktree_manager_1.defaultGitRunner;
|
|
1855
|
+
try {
|
|
1856
|
+
ctx.db.transaction(() => {
|
|
1857
|
+
for (const row of allRows) {
|
|
1858
|
+
if (!(0, rail_pr_store_1.claimPrDeliveryOperation)(ctx.db, row.id, row.decision, 'recover-and-retry', token))
|
|
1859
|
+
throw new Error('Delivery is busy; retry settlement');
|
|
1860
|
+
claimed.push(row);
|
|
1861
|
+
}
|
|
1862
|
+
})();
|
|
1863
|
+
for (const row of pending) {
|
|
1864
|
+
const records = (0, isolated_settlement_store_1.readIsolatedSettlementRecords)(ctx.db, ctx.project.id, row.id);
|
|
1865
|
+
const record = records.find(item => item.snapshot.run.runId === runId);
|
|
1866
|
+
if (!record)
|
|
1867
|
+
throw new Error('Original isolated allocation is missing');
|
|
1868
|
+
const snapshot = record.snapshot, run = snapshot.run;
|
|
1869
|
+
const ledger = (0, rail_worktrees_store_1.getRailWorktree)(ctx.db, run.ledgerId);
|
|
1870
|
+
if (!ledger || ledger.run_id !== runId || ledger.branch !== run.handle.branch || ledger.worktree_path !== run.handle.worktreePath)
|
|
1871
|
+
throw new Error('Worktree ownership changed before recovery');
|
|
1872
|
+
const context = frozen.metadata.context ?? (frozen.metadata.contextPath ? JSON.parse(fs.readFileSync(frozen.metadata.contextPath, 'utf8')) : null);
|
|
1873
|
+
if (!context || context.runId !== runId || !Array.isArray(context.repositories) || !context.repositories.some((repo) => repo.path && fs.realpathSync(repo.path) === fs.realpathSync(run.handle.worktreePath)))
|
|
1874
|
+
throw new Error('Recovered mount is outside the frozen Core context');
|
|
1875
|
+
if (snapshot.reconstructedFrom) {
|
|
1876
|
+
const repository = frozen.request.executionManifest?.repositories.find(item => item.worktreeId === run.ledgerId);
|
|
1877
|
+
const common = await git.run(['rev-parse', '--path-format=absolute', '--git-common-dir'], run.handle.worktreePath);
|
|
1878
|
+
if (!repository || common.code !== 0 || !common.stdout.trim() ||
|
|
1879
|
+
!sameRecoveryDirectory(common.stdout.trim(), repository.gitCommonDir))
|
|
1880
|
+
throw new Error('Recovered worktree belongs to another Git repository');
|
|
1881
|
+
}
|
|
1882
|
+
const branch = await git.run(['branch', '--show-current'], run.handle.worktreePath);
|
|
1883
|
+
if (branch.code !== 0 || branch.stdout.trim() !== run.handle.branch)
|
|
1884
|
+
throw new Error('Recovered worktree is on another branch');
|
|
1885
|
+
const settle = createIsolatedRunSettlement({
|
|
1886
|
+
...snapshot, git,
|
|
1887
|
+
onFinished: () => { },
|
|
1888
|
+
hasRuntimeRequest: () => true,
|
|
1889
|
+
commitMessage: (_run, partial) => partial ? snapshot.partialCommitMessage : snapshot.commitMessage,
|
|
1890
|
+
markWorktree: (unit, state) => (0, rail_worktrees_store_1.updateRailWorktreeState)(ctx.db, unit.ledgerId, state) ? null : 'Worktree ledger disappeared',
|
|
1891
|
+
recordRunProvenance: unit => (0, isolated_settlement_store_1.recordIsolatedProvenanceOnce)(ctx.db, row.id, runId, () => (io.recordProvenance ?? file_story_1.recordLoopRunProvenance)({
|
|
1892
|
+
db: ctx.db, projectId: ctx.project.id, runId, ticketId: unit.ticketId, repoDir: unit.handle.worktreePath,
|
|
1893
|
+
repositoryId: row.repository_id ?? undefined, snapshot: unit.provenanceSnapshot, broadcast: message => ctx.broadcast(message),
|
|
1894
|
+
})),
|
|
1895
|
+
});
|
|
1896
|
+
const result = await settle(run, Promise.resolve({ runId, outcome }));
|
|
1897
|
+
(0, isolated_settlement_store_1.saveIsolatedSettlementResult)(ctx.db, row.id, result);
|
|
1898
|
+
const latest = (0, isolated_settlement_store_1.readIsolatedSettlementRecords)(ctx.db, ctx.project.id, row.id);
|
|
1899
|
+
const results = latest.flatMap(item => item.result ? [item.result] : []);
|
|
1900
|
+
const complete = results.length === latest.length;
|
|
1901
|
+
const successes = results.filter(item => item.implementationOutcome === 'succeeded');
|
|
1902
|
+
const ready = results.filter(item => item.deliveryOutcome === 'ready');
|
|
1903
|
+
const noChanges = complete && results.every(item => item.deliveryOutcome === 'no_changes');
|
|
1904
|
+
const partial = complete && ready.length > 0 && results.some(item => item.implementationOutcome !== 'succeeded' || !['ready', 'no_changes'].includes(item.deliveryOutcome));
|
|
1905
|
+
const next = !complete ? 'building' : !successes.length ? 'implementation_failed' : row.pr_url ? 'pr_failed' : ready.length ? 'on_review' : noChanges ? 'no_changes' : 'pr_failed';
|
|
1906
|
+
const definitionEvidence = await (0, loop_definition_recovery_1.probeDefinitionRuns)({ db: ctx.db, cwd: ctx.project.path, env: process.env }, results.map(item => item.run.runId), true);
|
|
1907
|
+
const evidence = (io.harvestEvidence ?? delivery_evidence_1.harvestDeliveryEvidence)({ readEvents: id => (0, db_1.getJobEvents)(ctx.db, id) }, results.map(item => ({
|
|
1908
|
+
ticketId: item.run.ticketId, runId: item.run.runId, worktreePath: item.run.handle.worktreePath,
|
|
1909
|
+
definitionStatus: definitionEvidence.get(item.run.runId),
|
|
1910
|
+
reviewerStepId: (0, loop_runs_store_1.readDefinitionRun)(ctx.db, item.run.runId)?.request.graph.config.reviewerStepId,
|
|
1911
|
+
})));
|
|
1912
|
+
if (!(0, rail_pr_store_1.transitionClaimedDecision)(ctx.db, row.id, row.decision, next, token, {
|
|
1913
|
+
branches: branchRecords(results), runIds: latest.map(item => item.snapshot.run.runId), worktreeIds: latest.map(item => item.snapshot.run.ledgerId),
|
|
1914
|
+
implementationOutcome: !complete ? 'unknown' : successes.length === results.length ? 'succeeded' : successes.length ? 'partially_succeeded' : 'failed',
|
|
1915
|
+
deliveryOutcome: !complete ? 'not_started' : row.pr_url && ready.length ? 'retryable_failure' : ready.length ? (partial ? 'partial' : 'ready') : noChanges ? 'no_changes' : 'blocked',
|
|
1916
|
+
statusCode: !complete ? 'restart_pending' : row.pr_url && ready.length ? 'settlement_interrupted' : ready.length ? (partial ? 'partial_success' : 'ready_for_review') : noChanges ? 'no_changes' : 'implementation_failed',
|
|
1917
|
+
statusDetail: row.pr_url && ready.length ? 'Recovered local work is ready. Retry delivery to update the existing PR.' : null,
|
|
1918
|
+
...(results.length === 1 ? { deliverySha: results[0].finalSha } : {}), settleEvidence: evidence,
|
|
1919
|
+
}))
|
|
1920
|
+
throw new Error('Delivery changed during recovered settlement');
|
|
1921
|
+
}
|
|
1922
|
+
const { refreshRepositoryDeliveryGroup } = await Promise.resolve().then(() => __importStar(require('./multi-repo-execution')));
|
|
1923
|
+
for (const parent of parents)
|
|
1924
|
+
refreshRepositoryDeliveryGroup(ctx.db, parent);
|
|
1925
|
+
ctx.onLoopRunFinished(runId, outcome, { ticketCompletionStatus: 'on_review' });
|
|
1926
|
+
}
|
|
1927
|
+
finally {
|
|
1928
|
+
for (const row of claimed)
|
|
1929
|
+
(0, rail_pr_store_1.releasePrDeliveryOperation)(ctx.db, row.id, token);
|
|
1930
|
+
execution.release();
|
|
1931
|
+
}
|
|
1932
|
+
for (const row of allRows) {
|
|
1933
|
+
const latest = (0, rail_pr_store_1.getPrDelivery)(ctx.db, row.id);
|
|
1934
|
+
if (latest)
|
|
1935
|
+
ctx.broadcast((0, rail_pr_store_1.toRailPrStateMessage)(ctx.project.id, (0, rail_pr_store_1.toPrDeliverySnapshot)(latest)));
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1753
1938
|
function recoveryOverlayExcludes(repoDir, worktreePath) {
|
|
1754
1939
|
try {
|
|
1755
1940
|
const parsed = JSON.parse(fs.readFileSync(path.join(worktreePath, worktree_overlay_1.OVERLAY_MANIFEST), 'utf8'));
|