@overdeck/core 0.45.10 → 0.45.11
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/dist/{agent-state-KVbJEG5f.js → agent-state-RLhS7ckO.js} +1 -1
- package/dist/{agents-pp8FO7Uj.js → agents-By3u_fQ8.js} +1 -1
- package/dist/{agents-CQHK0CSA.js → agents-CgVr77Qq.js} +12 -12
- package/dist/{agents-CQHK0CSA.js.map → agents-CgVr77Qq.js.map} +1 -1
- package/dist/{backlog-auto-trigger-CyuoS0nl.js → backlog-auto-trigger-B_-1tk4e.js} +5 -5
- package/dist/{backlog-auto-trigger-CyuoS0nl.js.map → backlog-auto-trigger-B_-1tk4e.js.map} +1 -1
- package/dist/cli/index.js +45 -45
- package/dist/{cli-Cewlu3yU.js → cli-DGeSW85B.js} +2 -2
- package/dist/{cli-Cewlu3yU.js.map → cli-DGeSW85B.js.map} +1 -1
- package/dist/cli-MTB9IOoR.js +3 -0
- package/dist/{close-issue-BJGX9_z7.js → close-issue-Djajb6Sv.js} +2 -2
- package/dist/{close-issue-BJGX9_z7.js.map → close-issue-Djajb6Sv.js.map} +1 -1
- package/dist/{concurrency-Ca-fC7tA.js → concurrency-4rzjHAQA.js} +1 -1
- package/dist/{conversation-compaction-B_6s9EvM.js → conversation-compaction-BQvZh09I.js} +4 -4
- package/dist/{conversation-compaction-B_6s9EvM.js.map → conversation-compaction-BQvZh09I.js.map} +1 -1
- package/dist/dashboard/{advancing-selfheal-CMlGznKv.js → advancing-selfheal-BezNfM3P.js} +3 -3
- package/dist/dashboard/{advancing-selfheal-CMlGznKv.js.map → advancing-selfheal-BezNfM3P.js.map} +1 -1
- package/dist/dashboard/{agent-state-xk_G8t90.js → agent-state-BUANcaA7.js} +1 -1
- package/dist/dashboard/{agent-state-service-C1iUF9Ft.js → agent-state-service-DmFpIMbP.js} +1 -1
- package/dist/dashboard/{agent-state-service-5ge9xxhx.js → agent-state-service-tXUdp6UF.js} +2 -2
- package/dist/dashboard/{agent-state-service-5ge9xxhx.js.map → agent-state-service-tXUdp6UF.js.map} +1 -1
- package/dist/dashboard/{agents-BIggLvi_.js → agents-C-vqmKDi.js} +1 -1
- package/dist/dashboard/{agents-DHvsjsuP.js → agents-C7V4odQ6.js} +11 -11
- package/dist/dashboard/{agents-DHvsjsuP.js.map → agents-C7V4odQ6.js.map} +1 -1
- package/dist/dashboard/{backlog-auto-trigger-COm_R96V.js → backlog-auto-trigger-Cb8PR-5w.js} +2 -2
- package/dist/dashboard/{backlog-auto-trigger-COm_R96V.js.map → backlog-auto-trigger-Cb8PR-5w.js.map} +1 -1
- package/dist/dashboard/{cli-CvK9xuSS.js → cli-BgzbB6h3.js} +2 -2
- package/dist/dashboard/{cli-CvK9xuSS.js.map → cli-BgzbB6h3.js.map} +1 -1
- package/dist/dashboard/cli-DCqy_wFa.js +3 -0
- package/dist/dashboard/{close-issue-BnpErnD1.js → close-issue-BWf3pxn4.js} +2 -2
- package/dist/dashboard/{close-issue-BnpErnD1.js.map → close-issue-BWf3pxn4.js.map} +1 -1
- package/dist/dashboard/{closed-issue-reaper-DRqK5k39.js → closed-issue-reaper-D7XPq5pY.js} +4 -4
- package/dist/dashboard/{closed-issue-reaper-DRqK5k39.js.map → closed-issue-reaper-D7XPq5pY.js.map} +1 -1
- package/dist/dashboard/{closed-issue-reaper-BLCQwTpF.js → closed-issue-reaper-VSTSTROK.js} +1 -1
- package/dist/dashboard/{conflict-gate-JdFuhKuF.js → conflict-gate-DwOslTij.js} +1 -1
- package/dist/dashboard/{conflict-gate-BhKvrPxn.js → conflict-gate-FFEvTDVA.js} +3 -3
- package/dist/dashboard/{conflict-gate-BhKvrPxn.js.map → conflict-gate-FFEvTDVA.js.map} +1 -1
- package/dist/dashboard/{conversation-compaction-C5o_eKEm.js → conversation-compaction-CCqN4hWk.js} +4 -4
- package/dist/dashboard/{conversation-compaction-C5o_eKEm.js.map → conversation-compaction-CCqN4hWk.js.map} +1 -1
- package/dist/dashboard/{conversation-compaction-Ba5aZdrb.js → conversation-compaction-CL5Ti4j4.js} +1 -1
- package/dist/dashboard/{deacon-j3qSZ7--.js → deacon-CGHl90eC.js} +107 -50
- package/dist/dashboard/{deacon-j3qSZ7--.js.map → deacon-CGHl90eC.js.map} +1 -1
- package/dist/dashboard/{deacon-Cv2RMfHs.js → deacon-DMPh40Im.js} +2 -2
- package/dist/dashboard/deacon-swarm-CVTiNL0N.js +2 -0
- package/dist/dashboard/{deacon-swarm-DadWNi5J.js → deacon-swarm-aMMJTq6t.js} +4 -4
- package/dist/dashboard/{deacon-swarm-DadWNi5J.js.map → deacon-swarm-aMMJTq6t.js.map} +1 -1
- package/dist/dashboard/deacon.js +4 -4
- package/dist/dashboard/{feedback-target-oE4-Esxl.js → feedback-target-dJjFfITP.js} +4 -4
- package/dist/dashboard/{feedback-target-oE4-Esxl.js.map → feedback-target-dJjFfITP.js.map} +1 -1
- package/dist/dashboard/{feedback-target-C4DaDSZ8.js → feedback-target-nQSM6ygL.js} +1 -1
- package/dist/dashboard/{flywheel-ClMjGW_u.js → flywheel-BLoVNtJV.js} +6 -6
- package/dist/dashboard/{flywheel-ClMjGW_u.js.map → flywheel-BLoVNtJV.js.map} +1 -1
- package/dist/dashboard/{flywheel-DefHRzY4.js → flywheel-Cqf8vu7N.js} +6 -6
- package/dist/dashboard/{flywheel-DefHRzY4.js.map → flywheel-Cqf8vu7N.js.map} +1 -1
- package/dist/dashboard/{flywheel-BqDdGvrQ.js → flywheel-PJsbrXNY.js} +1 -1
- package/dist/dashboard/{flywheel-merge-order-CuDcqTRJ.js → flywheel-merge-order-B7BH8mZ3.js} +2 -2
- package/dist/dashboard/{flywheel-merge-order-CuDcqTRJ.js.map → flywheel-merge-order-B7BH8mZ3.js.map} +1 -1
- package/dist/dashboard/{flywheel-merge-order-B6YDSElh.js → flywheel-merge-order-CCwF60b-.js} +1 -1
- package/dist/dashboard/{harness-resolve-DKeSVsJL.js → harness-resolve-B3f9G1Kp.js} +2 -2
- package/dist/dashboard/{harness-resolve-DKeSVsJL.js.map → harness-resolve-B3f9G1Kp.js.map} +1 -1
- package/dist/dashboard/{inspect-agent-BKKc0XnB.js → inspect-agent-CKF5-SJr.js} +3 -3
- package/dist/dashboard/{inspect-agent-BKKc0XnB.js.map → inspect-agent-CKF5-SJr.js.map} +1 -1
- package/dist/dashboard/inspect-agent-CqpEWGWy.js +2 -0
- package/dist/dashboard/{issue-lifecycle-DnhQgddB.js → issue-lifecycle-5LbOcATP.js} +7 -7
- package/dist/dashboard/{issue-lifecycle-DnhQgddB.js.map → issue-lifecycle-5LbOcATP.js.map} +1 -1
- package/dist/dashboard/{issue-lifecycle-nY4esZn8.js → issue-lifecycle-CjYqQ669.js} +1 -1
- package/dist/dashboard/{issue-service-singleton-Bnqv1uaa.js → issue-service-singleton-B8iu_TKV.js} +1 -1
- package/dist/dashboard/{issue-service-singleton-_AquSBTl.js → issue-service-singleton-CDGt__oQ.js} +2 -2
- package/dist/dashboard/{issue-service-singleton-_AquSBTl.js.map → issue-service-singleton-CDGt__oQ.js.map} +1 -1
- package/dist/dashboard/{lifecycle-CGKm7doE.js → lifecycle-CEg4sHZH.js} +4 -4
- package/dist/dashboard/{lifecycle-CGKm7doE.js.map → lifecycle-CEg4sHZH.js.map} +1 -1
- package/dist/dashboard/{merge-agent-Dh8Y9PDO.js → merge-agent-7nBiCytp.js} +1 -1
- package/dist/dashboard/{merge-agent-BcgAYStl.js → merge-agent-C81xPIGd.js} +6 -6
- package/dist/dashboard/{merge-agent-BcgAYStl.js.map → merge-agent-C81xPIGd.js.map} +1 -1
- package/dist/dashboard/merge-ops-DE_6CnmZ.js +2 -0
- package/dist/dashboard/{merge-train-DmxlYbEo.js → merge-train-D3qzRccL.js} +2 -2
- package/dist/dashboard/{merge-train-DmxlYbEo.js.map → merge-train-D3qzRccL.js.map} +1 -1
- package/dist/dashboard/{merge-train-deps-DBHsOWt9.js → merge-train-deps-Be5ElC_P.js} +2 -2
- package/dist/dashboard/{merge-train-deps-DBHsOWt9.js.map → merge-train-deps-Be5ElC_P.js.map} +1 -1
- package/dist/dashboard/{orphan-proposed-reconciler-Db8tUhce.js → orphan-proposed-reconciler-Boq_XaRe.js} +3 -3
- package/dist/dashboard/{orphan-proposed-reconciler-Db8tUhce.js.map → orphan-proposed-reconciler-Boq_XaRe.js.map} +1 -1
- package/dist/dashboard/{orphan-proposed-reconciler-mXV--tcB.js → orphan-proposed-reconciler-C0sPnDq_.js} +1 -1
- package/dist/dashboard/{reconstruct-cache-_tDZ2D72.js → reconstruct-cache-C2pCltOq.js} +3 -3
- package/dist/dashboard/{reconstruct-cache-_tDZ2D72.js.map → reconstruct-cache-C2pCltOq.js.map} +1 -1
- package/dist/dashboard/{remote-completion-Bklh6loJ.js → remote-completion-BmxD5UMQ.js} +3 -3
- package/dist/dashboard/{remote-completion-Bklh6loJ.js.map → remote-completion-BmxD5UMQ.js.map} +1 -1
- package/dist/dashboard/resume-C-nxu9Y4.js +3 -0
- package/dist/dashboard/{review-agent-phzqNqIs.js → review-agent-D6P6d3pz.js} +26 -17
- package/dist/dashboard/review-agent-D6P6d3pz.js.map +1 -0
- package/dist/dashboard/review-agent-DTINEffB.js +3 -0
- package/dist/dashboard/{review-context-CCq56O_Y.js → review-context-Dyo0tsBa.js} +2 -2
- package/dist/dashboard/{review-context-CCq56O_Y.js.map → review-context-Dyo0tsBa.js.map} +1 -1
- package/dist/dashboard/{review-status-BQjqQXZG.js → review-status-B5ZASOo4.js} +18 -5
- package/dist/dashboard/review-status-B5ZASOo4.js.map +1 -0
- package/dist/dashboard/{review-status-bBkqUj5i.js → review-status-h7BXIA4z.js} +1 -1
- package/dist/dashboard/{review-verdict-feedback-wqnikhMK.js → review-verdict-feedback-B70VbfRL.js} +4 -4
- package/dist/dashboard/{review-verdict-feedback-wqnikhMK.js.map → review-verdict-feedback-B70VbfRL.js.map} +1 -1
- package/dist/dashboard/{review-verdict-feedback-cZh9Wi9G.js → review-verdict-feedback-CorgH49f.js} +1 -1
- package/dist/dashboard/{sequence-io-B2GmilSI.js → sequence-io-6YP8fVpH.js} +2 -2
- package/dist/dashboard/{sequence-io-B2GmilSI.js.map → sequence-io-6YP8fVpH.js.map} +1 -1
- package/dist/dashboard/{sequencer-agent-D8pW2aU_.js → sequencer-agent-ja-nkEc1.js} +5 -5
- package/dist/dashboard/{sequencer-agent-D8pW2aU_.js.map → sequencer-agent-ja-nkEc1.js.map} +1 -1
- package/dist/dashboard/server.js +66 -55
- package/dist/dashboard/server.js.map +1 -1
- package/dist/dashboard/{service-CILd-uOt.js → service-HKDrIy0h.js} +24 -24
- package/dist/dashboard/{service-CILd-uOt.js.map → service-HKDrIy0h.js.map} +1 -1
- package/dist/dashboard/{smart-compaction-Bgp_gwcA.js → smart-compaction-L0LSnF0_.js} +1 -1
- package/dist/dashboard/{smart-compaction-CrP90OFE.js → smart-compaction-uke1npUz.js} +2 -2
- package/dist/dashboard/{smart-compaction-CrP90OFE.js.map → smart-compaction-uke1npUz.js.map} +1 -1
- package/dist/dashboard/{spawn-planning-session-D15KbPOy.js → spawn-planning-session-CSMWco8B.js} +1 -1
- package/dist/dashboard/{spawn-planning-session-BmJWHSCu.js → spawn-planning-session-CXc0NkwV.js} +3 -3
- package/dist/dashboard/{spawn-planning-session-BmJWHSCu.js.map → spawn-planning-session-CXc0NkwV.js.map} +1 -1
- package/dist/dashboard/{specialist-context-CwKkcXL9.js → specialist-context-BGCJ24LH.js} +3 -3
- package/dist/dashboard/{specialist-context-CwKkcXL9.js.map → specialist-context-BGCJ24LH.js.map} +1 -1
- package/dist/dashboard/{specialist-logs-CvDxquV_.js → specialist-logs--Uz-r8ia.js} +2 -2
- package/dist/dashboard/{specialist-logs-CvDxquV_.js.map → specialist-logs--Uz-r8ia.js.map} +1 -1
- package/dist/dashboard/{specialists-BCjcG4uE.js → specialists-Ca9XSBCS.js} +1 -1
- package/dist/dashboard/{specialists-D07ybnam.js → specialists-DEfrAbWs.js} +36 -36
- package/dist/dashboard/{specialists-D07ybnam.js.map → specialists-DEfrAbWs.js.map} +1 -1
- package/dist/dashboard/{specialists-F2uMO4QS.js → specialists-DeZx9xom.js} +1 -1
- package/dist/dashboard/{specialists-BYi0k8CZ.js → specialists-nZ0mRwpZ.js} +12 -12
- package/dist/dashboard/{specialists-BYi0k8CZ.js.map → specialists-nZ0mRwpZ.js.map} +1 -1
- package/dist/dashboard/{summary-fork-DedqguZb.js → summary-fork-2a1L2DIU.js} +2 -2
- package/dist/dashboard/{summary-fork-EjvOkEPl.js → summary-fork-Xn0QUNmb.js} +3 -3
- package/dist/dashboard/{summary-fork-EjvOkEPl.js.map → summary-fork-Xn0QUNmb.js.map} +1 -1
- package/dist/dashboard/{teardown-workspace-5LCVbjfq.js → teardown-workspace-DhA6oAIK.js} +2 -2
- package/dist/dashboard/{teardown-workspace-5LCVbjfq.js.map → teardown-workspace-DhA6oAIK.js.map} +1 -1
- package/dist/dashboard/{test-agent-queue-qfsq6uaC.js → test-agent-queue-B_ukPyIK.js} +3 -3
- package/dist/dashboard/{test-agent-queue-qfsq6uaC.js.map → test-agent-queue-B_ukPyIK.js.map} +1 -1
- package/dist/dashboard/{uat-train-CtNGgXxj.js → uat-train-BLBAbJ8Y.js} +10 -10
- package/dist/dashboard/{uat-train-CtNGgXxj.js.map → uat-train-BLBAbJ8Y.js.map} +1 -1
- package/dist/dashboard/{uat-train-C3hWXtip.js → uat-train-DPfGR9_I.js} +1 -1
- package/dist/dashboard/{verification-runner-DuZRHjPU.js → verification-runner-KTGTNugM.js} +1 -1
- package/dist/dashboard/{verification-runner-BPAhPWR7.js → verification-runner-R2-4XgMO.js} +4 -4
- package/dist/dashboard/{verification-runner-BPAhPWR7.js.map → verification-runner-R2-4XgMO.js.map} +1 -1
- package/dist/dashboard/{workflows-H-COJ84X.js → workflows-CSKX5MhC.js} +7 -7
- package/dist/dashboard/{workflows-H-COJ84X.js.map → workflows-CSKX5MhC.js.map} +1 -1
- package/dist/dashboard/workflows-DVwXauef.js +2 -0
- package/dist/dashboard/{workspaces-CRU9_vRn.js → workspaces-DXLDgXo2.js} +32 -32
- package/dist/dashboard/{workspaces-CRU9_vRn.js.map → workspaces-DXLDgXo2.js.map} +1 -1
- package/dist/docs-index.sqlite +0 -0
- package/dist/{feedback-target-DTGAhFkM.js → feedback-target-BsKN4Tr_.js} +4 -4
- package/dist/{feedback-target-DTGAhFkM.js.map → feedback-target-BsKN4Tr_.js.map} +1 -1
- package/dist/{feedback-target-DutF088V.js → feedback-target-DM1WzOKy.js} +1 -1
- package/dist/{harness-resolve-Bz1prk6B.js → harness-resolve-B-YDNwNW.js} +2 -2
- package/dist/{harness-resolve-Bz1prk6B.js.map → harness-resolve-B-YDNwNW.js.map} +1 -1
- package/dist/{issue-lifecycle-fVqvCdKM.js → issue-lifecycle-Bc1l3U9f.js} +7 -7
- package/dist/{issue-lifecycle-fVqvCdKM.js.map → issue-lifecycle-Bc1l3U9f.js.map} +1 -1
- package/dist/{issue-service-singleton-CZm8p6Rj.js → issue-service-singleton-D-8Q6tdT.js} +1 -1
- package/dist/{issue-service-singleton-Cr73JAaR.js → issue-service-singleton-D1433I7n.js} +2 -2
- package/dist/{issue-service-singleton-Cr73JAaR.js.map → issue-service-singleton-D1433I7n.js.map} +1 -1
- package/dist/{lifecycle-CR6tZfl1.js → lifecycle-SmrqysDf.js} +7 -7
- package/dist/{lifecycle-CR6tZfl1.js.map → lifecycle-SmrqysDf.js.map} +1 -1
- package/dist/{lifecycle-D_EWiUjk.js → lifecycle-mRMyiIXs.js} +2 -2
- package/dist/{merge-agent-LM4avOtA.js → merge-agent-B9hIZ6Z0.js} +6 -6
- package/dist/{merge-agent-LM4avOtA.js.map → merge-agent-B9hIZ6Z0.js.map} +1 -1
- package/dist/{merge-agent-Bj046HE1.js → merge-agent-DUj22cXL.js} +1 -1
- package/dist/{merge-fulfillment-DrnC1Kmg.js → merge-fulfillment-__P4WE-T.js} +3 -3
- package/dist/{merge-fulfillment-DrnC1Kmg.js.map → merge-fulfillment-__P4WE-T.js.map} +1 -1
- package/dist/{reconstruct-cache-NPqoAUg-.js → reconstruct-cache-9SWp82SQ.js} +3 -3
- package/dist/{reconstruct-cache-NPqoAUg-.js.map → reconstruct-cache-9SWp82SQ.js.map} +1 -1
- package/dist/{records-BIkN1LQ4.js → records-CTmvuRzz.js} +14 -1
- package/dist/{records-BIkN1LQ4.js.map → records-CTmvuRzz.js.map} +1 -1
- package/dist/records-backfill-BQcmTmwF.js +2 -0
- package/dist/{records-backfill-HM0Pln6N.js → records-backfill-BrNsv_zT.js} +2 -2
- package/dist/{records-backfill-HM0Pln6N.js.map → records-backfill-BrNsv_zT.js.map} +1 -1
- package/dist/resume-Bn4kDk9x.js +3 -0
- package/dist/{review-agent-DFosQzCG.js → review-agent-PhqOhpx3.js} +24 -16
- package/dist/{review-agent-DFosQzCG.js.map → review-agent-PhqOhpx3.js.map} +1 -1
- package/dist/review-agent-hLbRRxZF.js +3 -0
- package/dist/{review-status-iJwfEoNX.js → review-status-B_XXomV0.js} +6 -6
- package/dist/{review-status-iJwfEoNX.js.map → review-status-B_XXomV0.js.map} +1 -1
- package/dist/{review-status-BbNVlXh1.js → review-status-Bo-EFVKh.js} +1 -1
- package/dist/{review-verdict-feedback-BGPw3z1X.js → review-verdict-feedback-CXHkoiKZ.js} +1 -1
- package/dist/{review-verdict-feedback-CCZ05atV.js → review-verdict-feedback-DGV13LDb.js} +4 -4
- package/dist/{review-verdict-feedback-CCZ05atV.js.map → review-verdict-feedback-DGV13LDb.js.map} +1 -1
- package/dist/{sequence-io-CcxZNHcD.js → sequence-io-Cg4NHAco.js} +1 -1
- package/dist/{sequence-io-Cx1usxG3.js → sequence-io-CyxvNHfA.js} +2 -2
- package/dist/{sequence-io-Cx1usxG3.js.map → sequence-io-CyxvNHfA.js.map} +1 -1
- package/dist/{smart-compaction-ByUEgQkF.js → smart-compaction-ByKXTQNy.js} +2 -2
- package/dist/{smart-compaction-ByUEgQkF.js.map → smart-compaction-ByKXTQNy.js.map} +1 -1
- package/dist/{smart-compaction-DfLe_CtH.js → smart-compaction-U6e_uB1E.js} +1 -1
- package/dist/{specialist-context-McuGkaeR.js → specialist-context-BUTadO-U.js} +3 -3
- package/dist/{specialist-context-McuGkaeR.js.map → specialist-context-BUTadO-U.js.map} +1 -1
- package/dist/{specialist-logs-CUUJ4eiB.js → specialist-logs-pEMCTtGZ.js} +2 -2
- package/dist/{specialist-logs-CUUJ4eiB.js.map → specialist-logs-pEMCTtGZ.js.map} +1 -1
- package/dist/{specialists-BSrylD1S.js → specialists-BGFnrQ8n.js} +1 -1
- package/dist/{specialists-B0elgInc.js → specialists-DeTkkp8b.js} +12 -12
- package/dist/{specialists-B0elgInc.js.map → specialists-DeTkkp8b.js.map} +1 -1
- package/dist/{summary-fork-DskMRnyb.js → summary-fork-CgzThE2-.js} +2 -2
- package/dist/{summary-fork-CPUTKcAT.js → summary-fork-Dx1RMuvU.js} +3 -3
- package/dist/{summary-fork-CPUTKcAT.js.map → summary-fork-Dx1RMuvU.js.map} +1 -1
- package/dist/{sync-BiNVQHBf.js → sync-Bsww9tuj.js} +2 -2
- package/dist/{sync-BiNVQHBf.js.map → sync-Bsww9tuj.js.map} +1 -1
- package/dist/sync-CylE_SjB.js +2 -0
- package/dist/{verdict-restore-DQiV7_fe.js → verdict-restore-DGpX0ZOJ.js} +5 -5
- package/dist/{verdict-restore-DQiV7_fe.js.map → verdict-restore-DGpX0ZOJ.js.map} +1 -1
- package/dist/{webhook-handlers-DHBSEDEm.js → webhook-handlers-BjsfIL7u.js} +3 -3
- package/dist/{webhook-handlers-DHBSEDEm.js.map → webhook-handlers-BjsfIL7u.js.map} +1 -1
- package/dist/{workspace-migrate-duUsWNpd.js → workspace-migrate-BBQ8nAnP.js} +2 -2
- package/dist/{workspace-migrate-duUsWNpd.js.map → workspace-migrate-BBQ8nAnP.js.map} +1 -1
- package/dist/{workspace-reap-DLCIoJmn.js → workspace-reap-12O9354k.js} +2 -2
- package/dist/{workspace-reap-DLCIoJmn.js.map → workspace-reap-12O9354k.js.map} +1 -1
- package/package.json +1 -1
- package/scripts/file-size-baseline.txt +1 -1
- package/dist/cli-DBWG12XK.js +0 -3
- package/dist/dashboard/cli-CJDWXE_Y.js +0 -3
- package/dist/dashboard/deacon-swarm-D1iAUX-P.js +0 -2
- package/dist/dashboard/inspect-agent-vm88mLYr.js +0 -2
- package/dist/dashboard/merge-ops-QdBiQ8XB.js +0 -2
- package/dist/dashboard/resume-DnIsmIgC.js +0 -3
- package/dist/dashboard/review-agent-DgySDb-G.js +0 -3
- package/dist/dashboard/review-agent-phzqNqIs.js.map +0 -1
- package/dist/dashboard/review-status-BQjqQXZG.js.map +0 -1
- package/dist/dashboard/workflows-Cz8OtiB4.js +0 -2
- package/dist/records-backfill-DEgtRyKL.js +0 -2
- package/dist/resume-DGymuXZM.js +0 -3
- package/dist/review-agent-DjVadURa.js +0 -3
- package/dist/sync-4jqukkxz.js +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge-agent-BcgAYStl.js","names":[],"sources":["../../src/lib/overdeck/git-activity.ts","../../src/lib/git-activity.ts","../../src/lib/git/operations.ts","../../src/lib/cloister/merge-agent.ts"],"sourcesContent":["/**\n * Overdeck door for git_operations — persistent log of git push/fetch/merge\n * events. Replaces database/git-operations-db.ts.\n *\n * Uses the hand-maintained overdeck migration as the schema source of truth.\n */\n\nimport { getOverdeckDatabaseSync } from './infra.js';\n\n// ─── Schema bootstrap ─────────────────────────────────────────────────────────\n\nfunction overdeckDb() {\n return getOverdeckDatabaseSync();\n}\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type GitOperationType =\n | 'push'\n | 'force_push'\n | 'fetch'\n | 'merge'\n | 'rev_parse'\n | 'main_diverged'\n | 'push_attempt'\n | 'fetch_attempt'\n | 'push_rejected'\n | 'non_ff'\n | 'force_push_cmd'\n | 'retry'\n | 'remote_rejected'\n | 'push_noop';\n\nexport type GitOperationStatus = 'success' | 'failure' | 'aborted';\n\nexport interface GitOperation {\n id?: number;\n operation: GitOperationType;\n branch?: string;\n issueId?: string;\n beforeSha?: string;\n afterSha?: string;\n remoteSha?: string;\n status: GitOperationStatus;\n error?: string;\n ts: string;\n}\n\nexport interface GitOperationFilter {\n issueId?: string;\n operation?: GitOperationType;\n status?: GitOperationStatus;\n since?: string;\n limit?: number;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\nexport function appendGitOperationSync(op: Omit<GitOperation, 'id'>): number {\n const result = overdeckDb().prepare(`\n INSERT INTO git_operations (\n operation, branch, issue_id,\n before_sha, after_sha, remote_sha,\n status, error, ts\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `).run(\n op.operation,\n op.branch ?? null,\n op.issueId ?? null,\n op.beforeSha ?? null,\n op.afterSha ?? null,\n op.remoteSha ?? null,\n op.status,\n op.error ?? null,\n new Date(op.ts).getTime(),\n );\n return result.lastInsertRowid as number;\n}\n\nexport function listGitOperationsSync(filter: GitOperationFilter = {}): GitOperation[] {\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (filter.issueId) { conditions.push('issue_id = ?'); params.push(filter.issueId); }\n if (filter.operation) { conditions.push('operation = ?'); params.push(filter.operation); }\n if (filter.status) { conditions.push('status = ?'); params.push(filter.status); }\n if (filter.since) { conditions.push('ts >= ?'); params.push(new Date(filter.since).getTime()); }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n const limit = filter.limit ? `LIMIT ${filter.limit}` : 'LIMIT 500';\n\n const rows = overdeckDb().prepare(`\n SELECT id, operation, branch, issue_id, before_sha, after_sha, remote_sha, status, error, ts\n FROM git_operations ${where} ORDER BY ts DESC ${limit}\n `).all(...params) as Array<{\n id: number;\n operation: string;\n branch: string | null;\n issue_id: string | null;\n before_sha: string | null;\n after_sha: string | null;\n remote_sha: string | null;\n status: string;\n error: string | null;\n ts: number;\n }>;\n\n return rows.map((row) => ({\n id: row.id,\n operation: row.operation as GitOperationType,\n branch: row.branch ?? undefined,\n issueId: row.issue_id ?? undefined,\n beforeSha: row.before_sha ?? undefined,\n afterSha: row.after_sha ?? undefined,\n remoteSha: row.remote_sha ?? undefined,\n status: row.status as GitOperationStatus,\n error: row.error ?? undefined,\n ts: new Date(row.ts).toISOString(),\n }));\n}\n","/**\n * Git Activity Service (PAN-653)\n *\n * Write path and read path for git operation events persisted to the\n * git_operations SQLite table. Survives dashboard restart — unlike the\n * in-memory activityLog array which is capped at 100 and wiped on restart.\n *\n * All functions use the async-safe getDatabase() which runs under Node 22.\n * NEVER use execSync/readFileSync here — this runs in the dashboard server.\n */\n\nimport { Effect, Data } from 'effect';\nimport {\n appendGitOperationSync,\n listGitOperationsSync,\n} from './overdeck/git-activity.js';\nimport type {\n GitOperation,\n GitOperationFilter,\n GitOperationType,\n GitOperationStatus,\n} from './overdeck/git-activity.js';\n\nexport type { GitOperation, GitOperationFilter, GitOperationType, GitOperationStatus };\nexport { appendGitOperationSync, listGitOperationsSync };\n\n/** A database operation against the git_operations table failed. */\nexport class GitActivityDbError extends Data.TaggedError('GitActivityDbError')<{\n readonly operation: string;\n readonly cause: unknown;\n}> {}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n\n/**\n * Effect-native appendGitOperation — typed-error variant of the SQLite write.\n * Fails with GitActivityDbError if the insert throws (e.g. DB locked).\n */\nexport const appendGitOperation = (\n op: Omit<GitOperation, 'id'>,\n): Effect.Effect<number, GitActivityDbError> =>\n Effect.try({\n try: () => appendGitOperationSync(op),\n catch: (cause) => new GitActivityDbError({ operation: 'appendGitOperation', cause }),\n });\n\n/**\n * Effect-native listGitOperations — typed-error variant of the SQLite read.\n * Fails with GitActivityDbError on query failure.\n */\nexport const listGitOperations = (\n filter: GitOperationFilter = {},\n): Effect.Effect<readonly GitOperation[], GitActivityDbError> =>\n Effect.try({\n try: () => listGitOperationsSync(filter),\n catch: (cause) => new GitActivityDbError({ operation: 'listGitOperations', cause }),\n });\n","/**\n * Git Operations Helpers (PAN-653)\n *\n * Thin wrappers over raw git commands that:\n * 1. Use execAsync (never execSync) — safe for dashboard server use\n * 2. Record before/after/remote SHAs for each operation\n * 3. Emit structured git.* events to the git_operations SQLite table\n * 4. For gitPush: perform a pre-push fetch + ancestor check and throw\n * MainDivergedError if origin/main has advanced past the local ancestor\n *\n * Usage: import { gitPush, gitFetch, gitForcePush, gitMerge, gitRevParse }\n * from 'src/lib/git/operations.js'\n *\n * NEVER add execSync, readFileSync, writeFileSync here — this module is\n * imported by dashboard server code (CLAUDE.md rule).\n */\n\nimport { execFile } from 'child_process';\nimport { promisify } from 'util';\nimport { Effect } from 'effect';\nimport { appendGitOperationSync } from '../git-activity.js';\nimport { GitError, VcsError } from '../errors.js';\n\nconst execFileAsync = promisify(execFile);\n\n// ============== Error types ==============\n\n/**\n * Thrown by gitPush when origin/main has advanced beyond the local ancestor.\n * The caller must NOT push — it should call markWorkspaceStuck instead.\n */\nexport class MainDivergedError extends Error {\n readonly localSha: string;\n readonly remoteSha: string;\n\n constructor(localSha: string, remoteSha: string) {\n super(\n `Main diverged: remote HEAD (${remoteSha.slice(0, 7)}) is not an ancestor of local HEAD (${localSha.slice(0, 7)}). Aborting push to protect the hotfix commit.`\n );\n this.name = 'MainDivergedError';\n this.localSha = localSha;\n this.remoteSha = remoteSha;\n }\n}async function gitRevParsePromise(cwd: string, ref: string): Promise<string | null> {\n const ts = new Date().toISOString();\n try {\n const { stdout } = await execFileAsync('git', ['rev-parse', ref], { cwd, encoding: 'utf-8' });\n const sha = stdout.trim();\n appendGitOperationSync({ operation: 'rev_parse', branch: ref, issueId: undefined, afterSha: sha, status: 'success', ts });\n return sha || null;\n } catch {\n return null;\n }\n}async function gitFetchPromise(\n cwd: string,\n remote = 'origin',\n branch?: string,\n opts: { issueId?: string } = {},\n): Promise<void> {\n const ts = new Date().toISOString();\n const args = branch ? ['fetch', remote, branch] : ['fetch', remote];\n try {\n await execFileAsync('git', args, { cwd, encoding: 'utf-8', timeout: 30000 });\n appendGitOperationSync({\n operation: 'fetch',\n branch: branch ?? remote,\n issueId: opts.issueId,\n status: 'success',\n ts,\n });\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n appendGitOperationSync({\n operation: 'fetch',\n branch: branch ?? remote,\n issueId: opts.issueId,\n status: 'failure',\n error: msg,\n ts,\n });\n throw err;\n }\n}async function gitPushPromise(\n cwd: string,\n remote = 'origin',\n branch = 'main',\n opts: { issueId?: string } = {},\n): Promise<void> {\n const ts = new Date().toISOString();\n\n // Step 1: record the local HEAD before the push\n const localSha = await Effect.runPromise(gitRevParse(cwd, 'HEAD')) ?? 'unknown';\n\n // Step 2: fetch latest remote state\n await Effect.runPromise(gitFetch(cwd, remote, branch, opts));\n\n // Step 3: read the remote tracking SHA\n const remoteSha = await Effect.runPromise(gitRevParse(cwd, `${remote}/${branch}`)) ?? '';\n\n // Step 4: ancestor check — is remoteSha an ancestor of localSha?\n if (remoteSha) {\n try {\n // git merge-base --is-ancestor <commit> <commit> exits 0 if true, 1 if false\n await execFileAsync('git', ['merge-base', '--is-ancestor', remoteSha, localSha], {\n cwd,\n encoding: 'utf-8',\n timeout: 10000,\n });\n } catch (err: unknown) {\n // exit code 1 = \"not an ancestor\" (true divergence); any other code is a real git error\n if ((err as { code?: number }).code !== 1) throw err;\n appendGitOperationSync({\n operation: 'main_diverged',\n branch,\n issueId: opts.issueId,\n beforeSha: localSha,\n remoteSha,\n status: 'aborted',\n error: `origin/${branch} (${remoteSha.slice(0, 7)}) is not ancestor of local HEAD (${localSha.slice(0, 7)})`,\n ts,\n });\n throw new MainDivergedError(localSha, remoteSha);\n }\n }\n\n // Step 5: push\n try {\n await execFileAsync('git', ['push', remote, branch], { cwd, encoding: 'utf-8', timeout: 60000 });\n const afterSha = await Effect.runPromise(gitRevParse(cwd, 'HEAD')) ?? localSha;\n appendGitOperationSync({\n operation: 'push',\n branch,\n issueId: opts.issueId,\n beforeSha: localSha,\n afterSha,\n remoteSha,\n status: 'success',\n ts,\n });\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n appendGitOperationSync({\n operation: 'push',\n branch,\n issueId: opts.issueId,\n beforeSha: localSha,\n remoteSha,\n status: 'failure',\n error: msg,\n ts,\n });\n throw err;\n }\n}async function gitForcePushPromise(\n cwd: string,\n remote = 'origin',\n branch = 'main',\n opts: { issueId?: string; reason?: string } = {},\n): Promise<void> {\n const ts = new Date().toISOString();\n const localSha = await Effect.runPromise(gitRevParse(cwd, 'HEAD')) ?? 'unknown';\n const remoteSha = await Effect.runPromise(gitRevParse(cwd, `${remote}/${branch}`)) ?? undefined;\n\n try {\n await execFileAsync('git', ['push', '--force-with-lease', remote, branch], {\n cwd,\n encoding: 'utf-8',\n timeout: 60000,\n });\n const afterSha = await Effect.runPromise(gitRevParse(cwd, 'HEAD')) ?? localSha;\n appendGitOperationSync({\n operation: 'force_push',\n branch,\n issueId: opts.issueId,\n beforeSha: localSha,\n afterSha,\n remoteSha,\n status: 'success',\n error: opts.reason,\n ts,\n });\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n appendGitOperationSync({\n operation: 'force_push',\n branch,\n issueId: opts.issueId,\n beforeSha: localSha,\n remoteSha,\n status: 'failure',\n error: msg,\n ts,\n });\n throw err;\n }\n}async function gitMergePromise(\n cwd: string,\n branch: string,\n opts: { issueId?: string; noFf?: boolean } = {},\n): Promise<void> {\n const ts = new Date().toISOString();\n const beforeSha = await Effect.runPromise(gitRevParse(cwd, 'HEAD')) ?? 'unknown';\n const args = opts.noFf ? ['merge', '--no-ff', branch] : ['merge', branch];\n try {\n await execFileAsync('git', args, { cwd, encoding: 'utf-8', timeout: 60000 });\n const afterSha = await Effect.runPromise(gitRevParse(cwd, 'HEAD')) ?? beforeSha;\n appendGitOperationSync({\n operation: 'merge',\n branch,\n issueId: opts.issueId,\n beforeSha,\n afterSha,\n status: 'success',\n ts,\n });\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n appendGitOperationSync({\n operation: 'merge',\n branch,\n issueId: opts.issueId,\n beforeSha,\n status: 'failure',\n error: msg,\n ts,\n });\n throw err;\n }\n}\n\n// ─── Effect variants (PAN-1249, additive) ────────────────────────────────────\n//\n// These wrap the existing Promise-based functions with typed Effect error\n// channels (GitError / VcsError). The underlying impl is unchanged; this is\n// an additive surface for callers that already speak Effect. MainDivergedError\n// is thrown by the underlying gitPush; the Effect variant surfaces it\n// preserved as the cause inside a VcsError tagged 'main-diverged'.\n\n/** Resolve a git ref to its SHA. Returns null if the ref does not exist. */\nexport function gitRevParse(\n cwd: string,\n ref: string,\n): Effect.Effect<string | null> {\n return Effect.promise(() => gitRevParsePromise(cwd, ref));\n}\n\n/** Fetch from a remote. */\nexport function gitFetch(\n cwd: string,\n remote = 'origin',\n branch?: string,\n opts: { issueId?: string } = {},\n): Effect.Effect<void, GitError> {\n return Effect.tryPromise({\n try: () => gitFetchPromise(cwd, remote, branch, opts),\n catch: (cause) =>\n new GitError({\n command: branch ? ['git', 'fetch', remote, branch] : ['git', 'fetch', remote],\n stderr: cause instanceof Error ? cause.message : String(cause),\n exitCode: -1,\n cause,\n }),\n });\n}\n\n/**\n * Push a branch to a remote with divergence guard.\n * Fails with VcsError tagged 'main-diverged' when MainDivergedError is thrown.\n */\nexport function gitPush(\n cwd: string,\n remote = 'origin',\n branch = 'main',\n opts: { issueId?: string } = {},\n): Effect.Effect<void, VcsError | GitError> {\n return Effect.tryPromise({\n try: () => gitPushPromise(cwd, remote, branch, opts),\n catch: (cause) => {\n if (cause instanceof MainDivergedError) {\n return new VcsError({\n operation: 'main-diverged',\n message: cause.message,\n cause,\n });\n }\n return new GitError({\n command: ['git', 'push', remote, branch],\n stderr: cause instanceof Error ? cause.message : String(cause),\n exitCode: -1,\n cause,\n });\n },\n });\n}\n\n/** Force-push a branch (--force-with-lease). */\nexport function gitForcePush(\n cwd: string,\n remote = 'origin',\n branch = 'main',\n opts: { issueId?: string; reason?: string } = {},\n): Effect.Effect<void, GitError> {\n return Effect.tryPromise({\n try: () => gitForcePushPromise(cwd, remote, branch, opts),\n catch: (cause) =>\n new GitError({\n command: ['git', 'push', '--force-with-lease', remote, branch],\n stderr: cause instanceof Error ? cause.message : String(cause),\n exitCode: -1,\n cause,\n }),\n });\n}\n\n/** Merge a branch into the current branch. */\nexport function gitMerge(\n cwd: string,\n branch: string,\n opts: { issueId?: string; noFf?: boolean } = {},\n): Effect.Effect<void, GitError> {\n return Effect.tryPromise({\n try: () => gitMergePromise(cwd, branch, opts),\n catch: (cause) =>\n new GitError({\n command: opts.noFf ? ['git', 'merge', '--no-ff', branch] : ['git', 'merge', branch],\n stderr: cause instanceof Error ? cause.message : String(cause),\n exitCode: -1,\n cause,\n }),\n });\n}\n","/**\n * Merge Agent - Automatic merge conflict resolution using Claude Code\n */\n\nimport { existsSync, mkdirSync, appendFileSync } from 'fs';\nimport { writeFile } from 'fs/promises';\nimport { join, dirname, basename, relative } from 'path';\nimport { fileURLToPath } from 'url';\nimport { spawn, exec } from 'child_process';\nimport { promisify } from 'util';\nimport { Effect } from 'effect';\nimport { capturePane, killSession, listSessionNames, sendKeys, sessionExists } from '../tmux.js';\nimport { emitActivityEntrySync, emitActivityTtsSync } from '../activity-logger.js';\nimport { loadConfigSync } from '../config-yaml.js';\n\nconst execAsync = promisify(exec);\n\n/**\n * Paths that must never enter a pipeline auto-commit, regardless of gitignore\n * state. These are workspace-local or machine-local state files and sync-target\n * directories; committing them pollutes feature branches and main.\n */\nexport const AUTO_COMMIT_EXCLUDED_PATHS = [\n '.pan/kickoff.md',\n '.pan/continue.json',\n '.pan/handoff-*.md',\n '.pan/spec.vbrief.json',\n '.claude/rules/',\n '.claude/skills/',\n // PAN-1899: machine-local Overdeck config copied into every workspace by\n // copyOverdeckSettingsToWorkspaceSync (config.yaml, projects.yaml,\n // settings.json). It diverges from main constantly and must never enter a\n // feature branch — that was the source of the recurring projects.yaml sync\n // conflict.\n '.overdeck/',\n];\n\nconst SYNC_MAIN_MAIN_PREFERRED_PATHS = [\n '.pan/continues',\n '.pan/specs',\n '.beads',\n];\n\nexport function isSyncMainMainPreferredPath(relativePath: string): boolean {\n const normalized = relativePath.replace(/\\\\/g, '/');\n return SYNC_MAIN_MAIN_PREFERRED_PATHS.some(\n (prefix) => normalized === prefix || normalized.startsWith(`${prefix}/`),\n );\n}\n\nexport function isAutoCommitExcludedPath(relativePath: string): boolean {\n const normalized = relativePath.replace(/\\\\/g, '/');\n for (const pattern of AUTO_COMMIT_EXCLUDED_PATHS) {\n if (pattern.endsWith('/')) {\n if (normalized.startsWith(pattern) || normalized === pattern.slice(0, -1)) {\n return true;\n }\n } else if (pattern.includes('*')) {\n const regex = new RegExp(\n '^' + pattern.replace(/\\./g, '\\\\.').replace(/\\*/g, '[^/]*') + '$'\n );\n if (regex.test(normalized)) return true;\n } else if (normalized === pattern) {\n return true;\n }\n }\n return false;\n}\n\nfunction parseStatusPath(line: string): string {\n // git status --porcelain lines are \"XY PATH\" or \"XY ORIG -> DEST\" for renames.\n const body = line.slice(3);\n if (line[0] === 'R' || line[1] === 'R') {\n const parts = body.split(' -> ');\n return parts[parts.length - 1];\n }\n return body;\n}\n\n/**\n * Auto-commit non-excluded workspace changes before a sync-main merge.\n * Respects .gitignore (no -f), unstages excluded paths, and leaves excluded\n * paths dirty so the sync can proceed. Returns success=false on git errors.\n */\nexport async function autoCommitWorkspaceChangesBeforeSync(\n projectPath: string,\n): Promise<{ success: boolean; committed: boolean; reason?: string }> {\n try {\n const { stdout: statusOut } = await execAsync('git status --porcelain', {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n if (!statusOut.trim()) {\n return { success: true, committed: false, reason: 'no uncommitted changes' };\n }\n\n // PAN-1819: plain `git add -A` respects .gitignore; never use -f.\n await execAsync('git add -A', { cwd: projectPath, encoding: 'utf-8' });\n\n // Belt-and-suspenders: unstage excluded paths regardless of ignore state.\n const resetPaths = AUTO_COMMIT_EXCLUDED_PATHS.map((p) =>\n p.endsWith('/') ? p.slice(0, -1) : p\n ).join(' ');\n await execAsync(`git reset HEAD -- ${resetPaths}`, {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n\n const { stdout: diffStat } = await execAsync('git diff --cached --stat', {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n if (!diffStat.trim()) {\n return { success: true, committed: false, reason: 'only excluded/ignored changes remain' };\n }\n\n await execAsync('git commit -m \"chore: auto-commit before sync with main\"', {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n return { success: true, committed: true };\n } catch (error: any) {\n return { success: false, committed: false, reason: `Failed to auto-commit: ${error.message}` };\n }\n}\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nimport {\n OVERDECK_HOME,\n} from '../paths.js';\nimport { resolveGitHubIssueSync } from '../tracker-utils.js';\n\nimport { restoreTrackedBeadsExport } from '../beads-restore.js';\nimport { runQualityGates } from './validation.js';\nimport { loadProjectsConfigSync } from '../projects.js';\nimport { cleanupStaleLocks } from '../git-utils.js';\nimport { gitPush, MainDivergedError } from '../git/operations.js';\nimport { markWorkspaceStuck, setReviewStatusSync } from '../review-status.js';\nimport { appendGitOperationSync, type GitOperationType } from '../git-activity.js';\nimport { recordFeatureRegistryLifecycle } from '../registry/feature-registry-population.js';\nimport { verifyMergedBeforeLifecycle, type PostMergeLifecycleOptions } from './merge-verification.js';\n\nconst SPECIALISTS_DIR = join(OVERDECK_HOME, 'specialists');\nconst MERGE_HISTORY_DIR = join(SPECIALISTS_DIR, 'merge-agent');\nconst MERGE_HISTORY_FILE = join(MERGE_HISTORY_DIR, 'history.jsonl');\n\n/**\n * Context for a merge conflict resolution request\n */\nexport interface MergeConflictContext {\n projectPath: string;\n sourceBranch: string;\n targetBranch: string;\n conflictFiles: string[];\n issueId: string;\n testCommand?: string;\n}\n\n/**\n * Result of merge agent execution\n */\nexport interface MergeResult {\n success: boolean;\n resolvedFiles?: string[];\n failedFiles?: string[];\n testsStatus?: 'PASS' | 'FAIL' | 'SKIP';\n validationStatus?: 'PASS' | 'FAIL' | 'NOT_RUN';\n reason?: string;\n notes?: string;\n output?: string;\n}\n\n/**\n * Merge history entry\n */\ninterface MergeHistoryEntry {\n timestamp: string;\n issueId: string;\n sourceBranch: string;\n targetBranch: string;\n conflictFiles: string[];\n result: MergeResult;\n sessionId?: string;\n}\n\n/**\n * Timeout for merge agent in milliseconds (15 minutes)\n */\n/**\n * Notify TLDR daemon to reindex changed files after merge\n */\nexport async function notifyTldrDaemon(projectPath: string, _sourceBranch: string): Promise<void> {\n try {\n console.log(`[merge-agent] Notifying TLDR daemon to reindex changed files...`);\n\n // Check if TLDR daemon is available\n const venvPath = join(projectPath, '.venv');\n if (!existsSync(venvPath)) {\n console.log(`[merge-agent] No .venv found, skipping TLDR notification`);\n return;\n }\n\n // Get changed files from the merge\n const { stdout } = await execAsync(`git diff --name-only HEAD~1 HEAD`, {\n cwd: projectPath,\n encoding: 'utf-8'\n });\n\n const changedFiles = stdout\n .trim()\n .split('\\n')\n .filter(f => f.trim().length > 0)\n .filter(f => {\n // Only include source code files (skip docs, configs, etc)\n const ext = f.split('.').pop()?.toLowerCase();\n return ext && ['ts', 'js', 'tsx', 'jsx', 'py', 'java', 'go', 'rs', 'cpp', 'c', 'h'].includes(ext);\n });\n\n if (changedFiles.length === 0) {\n console.log(`[merge-agent] No source files changed, skipping TLDR notification`);\n return;\n }\n\n console.log(`[merge-agent] Found ${changedFiles.length} changed source files to reindex`);\n\n // Get TLDR daemon service\n const { getTldrDaemonServiceSync } = await import('../tldr-daemon.js');\n const tldrService = getTldrDaemonServiceSync(projectPath, venvPath);\n\n // Check if daemon is running\n const status = await tldrService.getStatus();\n if (!status.running) {\n console.log(`[merge-agent] TLDR daemon not running, skipping notification`);\n return;\n }\n\n // Trigger warm to reindex (this will update the index incrementally)\n console.log(`[merge-agent] Triggering TLDR index warm...`);\n await tldrService.warm(true); // background mode\n\n console.log(`[merge-agent] ✓ TLDR daemon notified to reindex`);\n logActivity('tldr_notified', `Notified TLDR daemon to reindex ${changedFiles.length} files`);\n } catch (error: any) {\n // Non-fatal - log warning and continue\n console.warn(`[merge-agent] Failed to notify TLDR daemon: ${error.message}`);\n logActivity('tldr_notify_error', `TLDR notification failed: ${error.message}`);\n }\n}\n\n/**\n * Post-merge handoff: mark merged work as verifying on main and free runtime resources.\n *\n * Leaves the issue, workspace, vBRIEF, branches, and agent state dirs intact.\n * The explicit close-out ceremony performs final archival and destructive cleanup.\n *\n * IDEMPOTENT: Safe to call multiple times for the same issueId. Tracks completed\n * issues and returns immediately on re-entry. This is defense-in-depth against\n * the infinite loop that burned 24,626 Linear API calls (PAN-328).\n */\n\n// Defense-in-depth: track issues that have completed postMergeLifecycle.\n// Prevents re-execution even if caller guards fail. Persists for server lifetime.\nconst _completedPostMerge = new Set<string>();\nconst _postMergeInFlight = new Map<string, Promise<void>>();\n\n// PAN-1531: dropLingeringPreMergeStashes removed. The pre-merge stash kind\n// is no longer created by Overdeck, so there's nothing for the post-merge\n// lifecycle to clean up. Pre-existing pre-merge:* residue in refs/stash is\n// expected and inert.\n\nfunction shellQuote(value: string): string {\n return `'${value.replace(/'/g, `'\"'\"'`)}'`;\n}\n\nexport async function postMergeLifecycle(\n issueId: string,\n projectPath: string,\n sourceBranch?: string,\n options?: PostMergeLifecycleOptions,\n): Promise<void> {\n // PAN-1517: the per-slot swarm runtime is gone. Slot branches no longer exist\n // — parallelism is an in-context concern owned by the work agent (see\n // roles/work.md \"Parallel work via subagents\"). postMergeLifecycle fires only\n // for the issue's main feature branch merging to `main`.\n\n // Guard 1: skip if already completed (defense-in-depth against infinite loops)\n if (_completedPostMerge.has(issueId)) {\n console.log(`[merge-agent] postMergeLifecycle already completed for ${issueId}, skipping`);\n return;\n }\n\n const inFlight = _postMergeInFlight.get(issueId);\n if (inFlight) {\n console.log(`[merge-agent] postMergeLifecycle already running for ${issueId}, joining in-flight run`);\n return inFlight;\n }\n\n const run = (async () => {\n // Guard 2: closed-out is TERMINAL. Close-out flips the spec on main to\n // completed/cancelled, clears review status, and closes the tracker issue.\n // Re-running the handoff after that resurrects the review row and REOPENS\n // the closed issue — observed live on PAN-1190 (2026-06-11): the deacon's\n // stale-mergeStatus sweep saw the cleared row as \"stale\" 47 minutes after\n // close-out and the handoff reopened it into verifying-on-main forever.\n try {\n const { findSpecByIssue } = await import('../pan-dir/specs.js');\n const spec = await Effect.runPromise(findSpecByIssue(projectPath, issueId));\n if (spec && (spec.status === 'completed' || spec.status === 'cancelled')) {\n console.log(`[merge-agent] ${issueId} is closed out (spec ${spec.status}) — skipping post-merge lifecycle`);\n _completedPostMerge.add(issueId);\n return;\n }\n } catch {\n // Spec unreadable — proceed; the guard is best-effort.\n }\n\n const mergeVerification = await verifyMergedBeforeLifecycle(issueId, projectPath, sourceBranch, options);\n if (!mergeVerification.merged) {\n console.warn(`[merge-agent] Refusing post-merge lifecycle for ${issueId}: ${mergeVerification.reason}`);\n return;\n }\n console.log(`[merge-agent] Verified merge before lifecycle for ${issueId}: ${mergeVerification.reason}`);\n\n // Set mergeStatus='merged' after verifying the branch or PR actually landed.\n try {\n setReviewStatusSync(issueId, {\n mergeStatus: 'merged',\n readyForMerge: false,\n ...(options?.markReviewPassed ? { reviewStatus: 'passed' as const } : {}),\n });\n console.log(`[merge-agent] ✓ mergeStatus set to 'merged' for ${issueId}`);\n } catch (err: any) {\n console.warn(`[merge-agent] Could not set mergeStatus: ${err.message}`);\n }\n\n // Step 0: Write pending lifecycle file and spawn detached deploy script.\n // The deploy script rebuilds dist/, kills this server, and starts a fresh process.\n // The fresh process reads the pending file on startup and runs the lifecycle steps\n // with correct module chunk references (no ERR_MODULE_NOT_FOUND after merge).\n //\n // Skip this step when we ARE the fresh process (called from processPendingLifecycle) —\n // dynamic imports already resolve correctly and spawning again would create an infinite loop.\n if (!options?.skipDeploy) {\n const pendingFile = join(OVERDECK_HOME, 'pending-post-merge.json');\n let repoRoot = __dirname.includes('/src/')\n ? __dirname.replace(/\\/src\\/.*$/, '')\n : __dirname.replace(/\\/dist\\/.*$/, '').replace(/\\/lib\\/.*$/, '');\n // If running from a workspace (workspaces/feature-*/), resolve to the main repo root.\n // Without this, the deploy script builds and npm-links from the workspace, hijacking\n // the global `pan` CLI to point at stale workspace code.\n const wsMatch = repoRoot.match(/^(.+)\\/workspaces\\/feature-[^/]+$/);\n if (wsMatch) {\n repoRoot = wsMatch[1];\n console.log(`[merge-agent] Resolved workspace repoRoot to main repo: ${repoRoot}`);\n }\n const deployScript = join(repoRoot, 'scripts', 'post-merge-deploy.sh');\n\n try {\n const pendingData = JSON.stringify({\n issueId,\n projectPath,\n sourceBranch: sourceBranch ?? '',\n timestamp: Date.now(),\n reason: 'post-merge',\n trigger: 'merge-agent',\n });\n await writeFile(pendingFile, pendingData, 'utf-8');\n console.log(`[merge-agent] Wrote pending lifecycle file: ${pendingFile}`);\n\n // Pass 'post-merge' as the reason to the deploy script so it writes the\n // restart marker. We spawn detached and return immediately — the deploy script\n // kills this server. The new server reads the pending file on boot,\n // emits lifecycle_started, and after processing emits lifecycle_complete/failed.\n const child = spawn(deployScript, [repoRoot, issueId, projectPath, sourceBranch ?? '', 'post-merge'], {\n detached: true,\n stdio: 'ignore',\n });\n child.unref();\n console.log(`[merge-agent] Spawned detached deploy script (pid ${child.pid}) — server will restart with new build`);\n return;\n } catch (err: any) {\n console.warn(`[merge-agent] Failed to spawn deploy script: ${err.message}. Falling through to in-process lifecycle (may fail on stale chunks).`);\n }\n }\n\n console.log(`[merge-agent] Running post-merge verify handoff for ${issueId}`);\n\n // 1. Clean up stale workflow labels and keep the legacy merged marker for history.\n // verifying-on-main is applied next and takes precedence in canonical state mapping.\n try {\n const { cleanupMergedLabels } = await import('../lifecycle/label-cleanup.js');\n const ghResolved = resolveGitHubIssueSync(issueId);\n const labelCtx = ghResolved.isGitHub\n ? { issueId, projectPath, github: { owner: ghResolved.owner, repo: ghResolved.repo, number: ghResolved.number } }\n : { issueId, projectPath };\n // PAN-1249: cleanupMergedLabels returns Effect<StepResult>; bridge to Promise.\n const labelResult = await Effect.runPromise(cleanupMergedLabels(labelCtx));\n if (labelResult.success && !labelResult.skipped) {\n console.log(`[merge-agent] ✓ ${labelResult.details?.join('; ')}`);\n logActivity('labels_cleaned', labelResult.details?.join('; ') || 'Labels cleaned');\n } else if (labelResult.skipped) {\n console.log(`[merge-agent] Label cleanup skipped: ${labelResult.details?.join('; ')}`);\n } else {\n console.warn(`[merge-agent] Label cleanup failed (non-fatal): ${labelResult.error}`);\n }\n } catch (err) {\n console.warn(`[merge-agent] Could not clean labels: ${err}`);\n }\n\n try {\n await transitionIssueToVerifyingOnMain(issueId, projectPath);\n void recordFeatureRegistryLifecycle({ issueId, status: 'merged' });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.warn(`[merge-agent] Could not transition issue to verifying_on_main: ${message}`);\n try {\n setReviewStatusSync(issueId, {\n mergeStatus: 'failed',\n readyForMerge: false,\n mergeNotes: `Post-merge verifying_on_main transition failed: ${message}`,\n });\n } catch (statusErr: any) {\n console.warn(`[merge-agent] Could not persist verifying_on_main transition failure: ${statusErr?.message ?? statusErr}`);\n }\n announceMerge('failed', issueId, `Post-merge verifying_on_main transition failed: ${message}`);\n logActivity('merge_failed', `Post-merge verifying_on_main transition failed for ${issueId}: ${message}`);\n throw err;\n }\n\n // 2. Compact old beads (via lifecycle module)\n try {\n const { compactBeads } = await import('../lifecycle/compact-beads.js');\n // PAN-1249: compactBeads returns Effect<StepResult>; bridge to Promise.\n const beadsResult = await Effect.runPromise(compactBeads({ issueId, projectPath }));\n if (beadsResult.success && !beadsResult.skipped) {\n console.log(`[merge-agent] ✓ ${beadsResult.details?.join('; ')}`);\n logActivity('beads_compaction_complete', beadsResult.details?.join('; ') || 'Beads compacted');\n }\n } catch (err) {\n console.warn(`[merge-agent] Beads compaction failed: ${err}`);\n }\n\n // 3. Pause work/planning agents and kill their tmux panes to free resources.\n try {\n const { setAgentPaused, getAgentState } = await import('../agents.js');\n const { killSession, sessionExists } = await import('../tmux.js');\n const issueLower = issueId.toLowerCase();\n const reason = 'awaiting close-out (verify on main)';\n for (const agentId of [`agent-${issueLower}`, `planning-${issueLower}`]) {\n // Pause, then VERIFY the gate actually persisted to state.json. A server\n // restart mid-lifecycle (the PAN-1723 deploy re-runs this from\n // pending-post-merge.json) or a concurrent deacon read-modify-write on\n // state.json can silently drop the pause. An unpaused merged work agent\n // sits idle at its prompt yet still counts against the PAN-1665 work\n // ceiling, throttling dispatch for every live issue (PAN-1726). Assert\n // paused=true after the write; retry once, then fail loudly.\n const initial = await Effect.runPromise(setAgentPaused(agentId, reason, true));\n if (initial === null) {\n // No state.json for this agent — nothing to pause (e.g. planning never ran).\n continue;\n }\n let verify = await Effect.runPromise(getAgentState(agentId));\n if (verify?.paused !== true) {\n await Effect.runPromise(setAgentPaused(agentId, reason, true));\n verify = await Effect.runPromise(getAgentState(agentId));\n }\n if (verify?.paused === true) {\n console.log(`[merge-agent] ✓ Paused ${agentId}: ${reason}`);\n } else {\n console.error(\n `[merge-agent] ✗ FAILED to persist pause for ${agentId} after merge — state.json paused=${verify?.paused}. ` +\n `Idle merged work agent may hold a PAN-1665 work slot and throttle dispatch (PAN-1726).`,\n );\n logActivity('agent_pause_failed', `Could not persist pause for ${agentId} after merge — may throttle dispatch (PAN-1726)`);\n }\n if (await Effect.runPromise(sessionExists(agentId))) {\n await Effect.runPromise(killSession(agentId));\n console.log(`[merge-agent] ✓ Killed ${agentId} tmux session to free resources`);\n logActivity('agent_session_killed', `Freed resources: killed tmux session for ${agentId}`);\n }\n }\n } catch (err) {\n console.warn(`[merge-agent] Could not pause or kill agent sessions: ${err}`);\n }\n\n // 5a. Kill canonical reviewer/synthesis sessions (PAN-915).\n // Sessions persist across review rounds to preserve reviewer context, so the\n // merge is the right moment to tear them down. Issue is done — context value\n // is zero, RSS leak risk is non-zero. Resolve projectKey from the project\n // path so we don't depend on caller-supplied config.\n try {\n const { killAllReviewerSessions } = await import('./review-agent.js');\n const { resolveProjectFromIssueSync } = await import('../projects.js');\n const resolved = resolveProjectFromIssueSync(issueId);\n const projectKey = resolved?.projectKey;\n if (projectKey) {\n const { killed } = await Effect.runPromise(killAllReviewerSessions(projectKey, issueId));\n if (killed.length > 0) {\n console.log(`[merge-agent] ✓ Killed ${killed.length} canonical reviewer session(s) for ${issueId}`);\n logActivity('reviewer_sessions_killed', `Killed ${killed.length} reviewer session(s) for ${issueId} on merge`);\n }\n }\n } catch (err) {\n console.warn(`[merge-agent] Could not kill canonical reviewer sessions: ${err}`);\n }\n\n await killPostMergeRoleSessions(issueId);\n\n // 3c. Create a workspace-scoped memory reset marker so old review-blocker noise stays archived but out of retrieval.\n try {\n const { createResetMarker } = await import('../memory/cli.js');\n const timestamp = new Date().toISOString();\n const marker = await createResetMarker({\n projectId: basename(projectPath),\n scope: 'workspace',\n scopeId: `feature-${issueId.toLowerCase()}`,\n reason: 'post-merge cleanup',\n fromTimestamp: timestamp,\n createdAt: timestamp,\n });\n console.log(`[merge-agent] ✓ Created memory reset marker ${marker.id} for ${marker.scope}:${marker.scopeId}`);\n } catch (err) {\n console.warn(`[merge-agent] Memory reset marker creation failed (non-fatal): ${err}`);\n }\n\n // 4. Stop Docker containers + networks to prevent network pool exhaustion (non-fatal)\n // Orphaned Docker networks accumulate when workspaces are merged but containers are never\n // torn down, eventually exhausting Docker's address pool and blocking new workspace creation.\n try {\n const { findWorkspacePath } = await import('../lifecycle/archive-planning.js');\n const { stopWorkspaceDocker } = await import('../workspace-manager.js');\n const issueLower = issueId.toLowerCase();\n const workspacePath = findWorkspacePath(projectPath, issueLower);\n if (workspacePath) {\n const dockerResult = await Effect.runPromise(stopWorkspaceDocker(workspacePath, issueLower));\n if (dockerResult.containersFound) {\n console.log(`[merge-agent] ✓ Stopped Docker containers: ${dockerResult.steps.join('; ')}`);\n logActivity('docker_cleanup', `Stopped Docker for ${issueId}: ${dockerResult.steps.join('; ')}`);\n }\n }\n } catch (err) {\n console.warn(`[merge-agent] Docker cleanup failed (non-fatal): ${err}`);\n }\n\n await notifyTldrDaemon(projectPath, sourceBranch ?? '');\n await maybeSpawnPostMergeKnowledgeRetro(issueId, projectPath);\n\n // Mark completed BEFORE logging — prevents re-entry even if the log line triggers something\n _completedPostMerge.add(issueId);\n\n console.log(`[merge-agent] Post-merge handoff completed for ${issueId}. Awaiting close-out (verify on main).`);\n announceMerge('completed', issueId);\n logActivity('merge_complete', `Merged ${issueId}. Awaiting close-out (verify on main).`);\n })().finally(() => {\n _postMergeInFlight.delete(issueId);\n });\n _postMergeInFlight.set(issueId, run);\n return run;\n}\n\nasync function transitionIssueToVerifyingOnMain(issueId: string, projectPath: string): Promise<void> {\n const [effectModule, issueLifecycleModule, githubModule, linearModule, rallyModule, errorsModule] = await Promise.all([\n import('effect'),\n import('../../dashboard/server/services/issue-lifecycle.js'),\n import('../../dashboard/server/services/github-client.js'),\n import('../../dashboard/server/services/linear-client.js'),\n import('../../dashboard/server/services/rally-client.js'),\n import('../../dashboard/server/services/typed-errors.js'),\n ]);\n const { Effect, Layer } = effectModule;\n const { IssueLifecycle, IssueLifecycleLive } = issueLifecycleModule;\n const { GitHubClient } = githubModule;\n const { LinearClientOptionalLive } = linearModule;\n const { RallyClientOptionalLive } = rallyModule;\n const { IssueNotFound } = errorsModule;\n const gitHubRepo = (owner: string, repo: string) => shellQuote(`${owner}/${repo}`);\n const githubLayer = Layer.succeed(GitHubClient, {\n getIssue: (_owner: string, _repo: string, number: number) => Effect.fail(new IssueNotFound({ id: String(number) })),\n closeIssue: (owner: string, repo: string, number: number) => Effect.promise(() => execAsync(`gh issue close ${number} --repo ${gitHubRepo(owner, repo)}`, { cwd: projectPath, encoding: 'utf-8' }).then(() => undefined)),\n reopenIssue: (owner: string, repo: string, number: number) => Effect.promise(() => execAsync(`gh issue reopen ${number} --repo ${gitHubRepo(owner, repo)} 2>/dev/null || true`, { cwd: projectPath, encoding: 'utf-8' }).then(() => undefined)),\n addLabel: (owner: string, repo: string, number: number, label: string) => Effect.promise(async () => {\n if (label === 'verifying-on-main') {\n await execAsync(`gh label create ${shellQuote(label)} --repo ${gitHubRepo(owner, repo)} --color \"fbca04\" --description \"Merged — awaiting verification on main\" --force 2>/dev/null || true`, { cwd: projectPath, encoding: 'utf-8' });\n }\n await execAsync(`gh issue edit ${number} --repo ${gitHubRepo(owner, repo)} --add-label ${shellQuote(label)}`, { cwd: projectPath, encoding: 'utf-8' });\n }),\n removeLabel: (owner: string, repo: string, number: number, label: string) => Effect.promise(() => execAsync(`gh issue edit ${number} --repo ${gitHubRepo(owner, repo)} --remove-label ${shellQuote(label)} 2>/dev/null || true`, { cwd: projectPath, encoding: 'utf-8' }).then(() => undefined)),\n ensureLabel: (owner: string, repo: string, label: string, color = '0075ca', description = '') => Effect.promise(async () => {\n await execAsync(`gh label create ${shellQuote(label)} --repo ${gitHubRepo(owner, repo)} --color ${shellQuote(color)} --description ${shellQuote(description)} --force 2>/dev/null || true`, { cwd: projectPath, encoding: 'utf-8' });\n return { id: 0, name: label, color };\n }),\n addComment: () => Effect.void,\n getComments: () => Effect.succeed([]),\n });\n const layer = IssueLifecycleLive.pipe(\n Layer.provide(LinearClientOptionalLive),\n Layer.provide(githubLayer),\n Layer.provide(RallyClientOptionalLive),\n );\n await Effect.runPromise(\n Effect.gen(function* () {\n const lifecycle = yield* IssueLifecycle;\n yield* lifecycle.transitionTo(issueId, 'verifying_on_main');\n }).pipe(Effect.provide(layer)),\n );\n console.log(`[merge-agent] ✓ Transitioned ${issueId} to verifying_on_main`);\n}\n\nfunction isPostMergeRoleSession(sessionName: string, issueLower: string): boolean {\n if ([`agent-${issueLower}-test`, `agent-${issueLower}-ship`, `agent-${issueLower}-merge`].includes(sessionName)) {\n return true;\n }\n if (sessionName.startsWith(`agent-${issueLower}-review-`)) {\n return true;\n }\n return sessionName.startsWith('specialist-')\n && sessionName.includes(`-${issueLower}-`)\n && /-(review|test|merge|ship)(?:-|$)/.test(sessionName);\n}\n\nasync function killPostMergeRoleSessions(issueId: string): Promise<void> {\n try {\n const issueLower = issueId.toLowerCase();\n const sessions = await Effect.runPromise(listSessionNames());\n const targets = sessions.filter((session) => isPostMergeRoleSession(session, issueLower));\n for (const session of targets) {\n await Effect.runPromise(killSession(session));\n }\n if (targets.length > 0) {\n console.log(`[merge-agent] ✓ Killed ${targets.length} review/test/ship session(s) for ${issueId}`);\n logActivity('role_sessions_killed', `Killed ${targets.length} review/test/ship session(s) for ${issueId} on merge`);\n }\n } catch (err) {\n console.warn(`[merge-agent] Could not kill role sessions for ${issueId}: ${err}`);\n }\n}\n\nfunction isPostMergeKnowledgeRetroEnabled(): boolean {\n try {\n return loadConfigSync().config.knowledge?.postMergeAutoRetro === true;\n } catch (err) {\n console.warn(`[merge-agent] Could not read knowledge post-merge retro config: ${err}`);\n return false;\n }\n}\n\nasync function maybeSpawnPostMergeKnowledgeRetro(issueId: string, projectPath: string): Promise<void> {\n if (!isPostMergeKnowledgeRetroEnabled()) return;\n\n try {\n const child = spawn('pan', ['knowledge', issueId, '--retro'], {\n cwd: projectPath,\n detached: true,\n stdio: 'ignore',\n });\n child.once?.('error', (err) => {\n const message = err instanceof Error ? err.message : String(err);\n console.warn(`[merge-agent] Post-merge knowledge retro spawn failed for ${issueId}: ${message}`);\n logActivity('knowledge_retro_error', `Post-merge knowledge retro spawn failed for ${issueId}: ${message}`, issueId);\n });\n child.unref();\n console.log(`[merge-agent] Spawned post-merge knowledge retro for ${issueId} (pid ${child.pid})`);\n logActivity('knowledge_retro_spawned', `Spawned post-merge knowledge retro for ${issueId}`, issueId);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.warn(`[merge-agent] Post-merge knowledge retro spawn failed for ${issueId}: ${message}`);\n logActivity('knowledge_retro_error', `Post-merge knowledge retro spawn failed for ${issueId}: ${message}`, issueId);\n }\n}\n\n/**\n * Reset postMergeLifecycle completion tracking for an issue (used by reopen).\n */\nexport function resetPostMergeState(issueId: string): void {\n _completedPostMerge.delete(issueId);\n _postMergeInFlight.delete(issueId);\n}\n\n/**\n * Parse result markers from agent output\n */\nexport function parseAgentOutput(output: string): MergeResult {\n const lines = output.split('\\n');\n\n let mergeResult: 'SUCCESS' | 'FAILURE' | null = null;\n let resolvedFiles: string[] = [];\n let failedFiles: string[] = [];\n let testsStatus: 'PASS' | 'FAIL' | 'SKIP' | null = null;\n let validationStatus: 'PASS' | 'FAIL' | null = null;\n let reason = '';\n let notes = '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n // Match MERGE_RESULT\n if (trimmed.startsWith('MERGE_RESULT:')) {\n const value = trimmed.substring('MERGE_RESULT:'.length).trim();\n if (value === 'SUCCESS' || value === 'FAILURE') {\n mergeResult = value;\n }\n }\n\n // Match RESOLVED_FILES\n if (trimmed.startsWith('RESOLVED_FILES:')) {\n const value = trimmed.substring('RESOLVED_FILES:'.length).trim();\n resolvedFiles = value\n .split(',')\n .map((f) => f.trim())\n .filter((f) => f.length > 0);\n }\n\n // Match FAILED_FILES\n if (trimmed.startsWith('FAILED_FILES:')) {\n const value = trimmed.substring('FAILED_FILES:'.length).trim();\n failedFiles = value\n .split(',')\n .map((f) => f.trim())\n .filter((f) => f.length > 0);\n }\n\n // Match TESTS\n if (trimmed.startsWith('TESTS:')) {\n const value = trimmed.substring('TESTS:'.length).trim();\n if (value === 'PASS' || value === 'FAIL' || value === 'SKIP') {\n testsStatus = value;\n }\n }\n\n // Match VALIDATION\n if (trimmed.startsWith('VALIDATION:')) {\n const value = trimmed.substring('VALIDATION:'.length).trim();\n if (value === 'PASS' || value === 'FAIL') {\n validationStatus = value;\n }\n }\n\n // Match REASON\n if (trimmed.startsWith('REASON:')) {\n reason = trimmed.substring('REASON:'.length).trim();\n }\n\n // Match NOTES\n if (trimmed.startsWith('NOTES:')) {\n notes = trimmed.substring('NOTES:'.length).trim();\n }\n }\n\n // Build result\n if (mergeResult === 'SUCCESS') {\n return {\n success: true,\n resolvedFiles,\n testsStatus: testsStatus || 'SKIP',\n validationStatus: validationStatus || 'NOT_RUN',\n notes,\n output,\n };\n } else if (mergeResult === 'FAILURE') {\n return {\n success: false,\n failedFiles,\n validationStatus: validationStatus || 'NOT_RUN',\n reason,\n notes,\n output,\n };\n } else {\n // No structured result markers found - try to detect human-readable format\n // Agents sometimes output \"MERGE TASK COMPLETE\" instead of \"MERGE_RESULT: SUCCESS\"\n const lowerOutput = output.toLowerCase();\n\n // Check for success indicators\n const successIndicators = [\n 'merge task complete',\n 'successfully merged',\n 'merge complete',\n 'pushed merge commit',\n 'successfully merged and pushed',\n ];\n\n const failureIndicators = [\n 'merge failed',\n 'merge task failed',\n 'could not merge',\n 'conflict not resolved',\n ];\n\n const hasSuccessIndicator = successIndicators.some(i => lowerOutput.includes(i));\n const hasFailureIndicator = failureIndicators.some(i => lowerOutput.includes(i));\n\n if (hasSuccessIndicator && !hasFailureIndicator) {\n // Extract test status from output if mentioned\n let detectedTestStatus: 'PASS' | 'FAIL' | 'SKIP' = 'SKIP';\n if (lowerOutput.includes('tests: pass') || lowerOutput.includes('tests passed') ||\n output.match(/\\d+ passed/)) {\n detectedTestStatus = 'PASS';\n } else if (lowerOutput.includes('tests: fail') || lowerOutput.includes('tests failed')) {\n detectedTestStatus = 'FAIL';\n }\n\n console.log('[merge-agent] Detected success from human-readable output');\n return {\n success: true,\n testsStatus: detectedTestStatus,\n validationStatus: 'PASS',\n notes: 'Detected from human-readable output (agent did not use structured format)',\n output,\n };\n }\n\n if (hasFailureIndicator) {\n console.log('[merge-agent] Detected failure from human-readable output');\n return {\n success: false,\n validationStatus: 'NOT_RUN',\n reason: 'Detected merge failure from agent output',\n output,\n };\n }\n\n // Truly unrecognized output\n return {\n success: false,\n validationStatus: 'NOT_RUN',\n reason: 'Agent did not report result in expected format',\n output,\n };\n }\n}\n\n/**\n * Get conflict files from git status (async)\n */\nasync function getConflictFiles(projectPath: string): Promise<string[]> {\n try {\n const { stdout: status } = await execAsync('git diff --name-only --diff-filter=U', {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n\n return status\n .split('\\n')\n .map((line) => line.trim())\n .filter((line) => line.length > 0);\n } catch (error) {\n console.error('Failed to get conflict files:', error);\n return [];\n }\n}\n\nasync function resolveMainPreferredSyncConflicts(\n projectPath: string,\n conflictFiles: string[],\n): Promise<{ success: boolean; reason?: string }> {\n if (conflictFiles.length === 0 || !conflictFiles.every(isSyncMainMainPreferredPath)) {\n return { success: false, reason: 'conflicts include non-pipeline-owned files' };\n }\n\n try {\n for (const path of SYNC_MAIN_MAIN_PREFERRED_PATHS) {\n await execAsync(`git rm -r --quiet --ignore-unmatch -- ${path}`, {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n await execAsync(`git checkout origin/main -- ${path}`, {\n cwd: projectPath,\n encoding: 'utf-8',\n }).catch(() => {\n // The path may not exist on origin/main. In that case, the preceding\n // git rm records main's deletion for this pipeline-owned path.\n });\n await execAsync(`git add -A -- ${path}`, {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n }\n\n const remainingConflicts = await getConflictFiles(projectPath);\n if (remainingConflicts.length > 0) {\n return {\n success: false,\n reason: `Unresolved conflicts remain: ${remainingConflicts.join(', ')}`,\n };\n }\n\n await execAsync('git commit --no-edit', {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n return { success: true };\n } catch (error: any) {\n return {\n success: false,\n reason: `Failed to auto-resolve pipeline-owned conflicts: ${error.message}`,\n };\n }\n}\n\n/**\n * Log merge to history\n */\nexport function logMergeHistory(context: MergeConflictContext, result: MergeResult, sessionId?: string): void {\n // Ensure history directory exists\n if (!existsSync(MERGE_HISTORY_DIR)) {\n mkdirSync(MERGE_HISTORY_DIR, { recursive: true });\n }\n\n const entry: MergeHistoryEntry = {\n timestamp: new Date().toISOString(),\n issueId: context.issueId,\n sourceBranch: context.sourceBranch,\n targetBranch: context.targetBranch,\n conflictFiles: context.conflictFiles,\n result: {\n ...result,\n output: undefined, // Don't store full output in history\n },\n sessionId,\n };\n\n appendFileSync(MERGE_HISTORY_FILE, JSON.stringify(entry) + '\\n', 'utf-8');\n}\n\n/**\n * Log activity to the dashboard activity log (event-sourced via emitActivityEntry)\n */\nfunction logActivity(action: string, details: string, issueId?: string): void {\n emitActivityEntrySync({\n source: 'ship',\n level: action.includes('fail') || action.includes('error') ? 'error' : action.includes('warn') ? 'warn' : 'success',\n message: details,\n issueId,\n });\n}\n\n/**\n * Voice-worthy merge milestone announcement. Messages start with one of three\n * distinctive prefixes (\"Merge started\", \"Merge completed\", \"Merge failed\") so\n * pan-tts can filter merge-agent chatter and speak only these three events.\n *\n * Do not change these prefixes without updating the pan-tts filter\n * (~/Projects/pan-tts/src/pan_tts/__main__.py — ALLOWED_MERGE_PREFIXES).\n */\nfunction announceMerge(\n status: 'started' | 'completed' | 'failed',\n issueId: string,\n extra?: string,\n): void {\n const prefix = status === 'started'\n ? 'Merge started'\n : status === 'completed'\n ? 'Merge completed'\n : 'Merge failed';\n const tail = extra ? `. ${extra}` : '';\n emitActivityEntrySync({\n source: 'ship',\n level: status === 'failed' ? 'error' : 'success',\n message: `${prefix} for ${issueId}${tail}`,\n issueId,\n });\n // Upleveled TTS utterance — short, speakable, no issue prefix noise\n const ttsUtterance = status === 'started'\n ? `Starting merge for ${issueId}`\n : status === 'completed'\n ? `${issueId} merged to main`\n : `Merge failed for ${issueId}`;\n emitActivityTtsSync({\n utterance: ttsUtterance,\n priority: status === 'failed' ? 0 : 1,\n issueId,\n source: 'merge-agent',\n eventType: `mergeStatus.${status === 'completed' ? 'merged' : status === 'started' ? 'merging' : 'failed'}`,\n });\n}\n\n/**\n * Capture tmux output and look for result markers (async)\n */\nexport async function captureTmuxOutput(sessionName: string): Promise<string> {\n try {\n return await Effect.runPromise(capturePane(sessionName));\n } catch {\n return '';\n }\n}\n\n/** Patterns to match in tmux capture-pane output (git push/fetch lines) */\nexport const GIT_PATTERNS: Array<{ re: RegExp; operation: GitOperationType; level: 'info' | 'warn' | 'error' }> = [\n { re: /force-with-lease/i, operation: 'force_push_cmd', level: 'warn' },\n { re: /git push/i, operation: 'push_attempt', level: 'info' },\n { re: /git fetch/i, operation: 'fetch_attempt', level: 'info' },\n { re: /\\[rejected\\]/i, operation: 'push_rejected', level: 'error' },\n { re: /non-fast-forward/i, operation: 'non_ff', level: 'error' },\n { re: /retrying/i, operation: 'retry', level: 'warn' },\n { re: /\\[remote rejected\\]/i, operation: 'remote_rejected', level: 'error' },\n { re: /Everything up-to-date/i, operation: 'push_noop', level: 'info' },\n];\n\n/**\n * Scan tmux capture-pane output for git push/fetch patterns and emit each\n * as a git_operations row. Uses seenLineHashes to dedupe within a session.\n */\nexport function scanGitPatterns(\n output: string,\n seenLineHashes: Set<string>,\n issueId: string,\n branch?: string,\n): void {\n const lines = output.split('\\n');\n const ts = new Date().toISOString();\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n // Simple hash: first 120 chars (avoids hashing megabytes)\n const hash = trimmed.slice(0, 120);\n if (seenLineHashes.has(hash)) continue;\n\n for (const { re, operation, level } of GIT_PATTERNS) {\n if (re.test(trimmed)) {\n seenLineHashes.add(hash);\n appendGitOperationSync({\n operation,\n branch,\n issueId,\n status: level === 'error' ? 'failure' : 'success',\n error: level !== 'info' ? trimmed.slice(0, 200) : undefined,\n ts,\n });\n emitActivityEntrySync({\n source: 'ship',\n level,\n message: `[git] ${trimmed.slice(0, 100)}`,\n issueId,\n });\n break; // only match one pattern per line\n }\n }\n }\n}\n\n/**\n * Check if specialist-merge-agent tmux session is running (async)\n */\nexport async function isMergeAgentRunning(): Promise<boolean> {\n return Effect.runPromise(sessionExists('specialist-merge-agent'));\n}\n\n/**\n * Send a message to an agent's tmux session (async)\n */\nexport async function sendMessageToAgent(issueId: string, message: string): Promise<boolean> {\n // Agent sessions are typically named agent-{issueId} (lowercase)\n const sessionName = `agent-${issueId.toLowerCase()}`;\n\n try {\n // Check if session exists\n if (!await Effect.runPromise(sessionExists(sessionName))) {\n console.log(`[merge-agent] Could not send message to ${sessionName} (session does not exist)`);\n return false;\n }\n\n // Send the message using centralized sendKeys\n await Effect.runPromise(sendKeys(sessionName, message));\n\n console.log(`[merge-agent] Sent message to ${sessionName}`);\n logActivity('agent_message', `Sent to ${sessionName}: ${message.slice(0, 100)}...`);\n return true;\n } catch {\n console.log(`[merge-agent] Could not send message to ${sessionName} (session may not exist)`);\n return false;\n }\n}\n\n// PAN-1531: ship-role machinery (buildShipPreparationPrompt, buildShipSyncMainPrompt,\n// spawnShipRoleForTask, spawnMergeAgentForBranches, spawnRebaseAgentForBranch,\n// defaultWorkspaceForIssue) removed. Rebase is now performed in-process via\n// rebaseFeatureBranch() in src/lib/cloister/merge-rebase.ts. See docs/MERGE-WORKFLOW.md.\n\nexport async function salvageStrandedMerge(\n projectPath: string,\n targetBranch: string,\n headBefore: string,\n issueId: string,\n logActivity: (action: string, detail: string) => void,\n): Promise<{ success: boolean; reason?: string } | null> {\n try {\n const { stdout: currentHeadRaw } = await execAsync('git rev-parse HEAD', {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n const currentHead = currentHeadRaw.trim();\n\n if (currentHead === headBefore) {\n // No local merge happened — nothing to salvage\n return null;\n }\n\n // Local HEAD changed — check if it's ahead of remote\n await execAsync(`git fetch origin ${targetBranch}`, {\n cwd: projectPath,\n encoding: 'utf-8',\n timeout: 10000,\n }).catch(() => {});\n\n const { stdout: remoteHeadRaw } = await execAsync(`git rev-parse origin/${targetBranch}`, {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n\n if (remoteHeadRaw.trim() === currentHead) {\n // Already pushed (maybe by another process)\n console.log(`[merge-agent] Salvage check: merge already pushed`);\n return { success: true };\n }\n\n // Stranded merge detected — push it (with divergence guard to protect hotfixes)\n console.log(`[merge-agent] SALVAGING stranded merge for ${issueId}: local HEAD ${currentHead.slice(0, 8)} != remote ${remoteHeadRaw.trim().slice(0, 8)}`);\n logActivity('merge_salvage', `Pushing stranded merge commit ${currentHead.slice(0, 8)} for ${issueId}`);\n\n try {\n await Effect.runPromise(gitPush(projectPath, 'origin', targetBranch, { issueId }));\n } catch (pushErr: unknown) {\n if (pushErr instanceof MainDivergedError) {\n // origin has advanced past our local ancestor — a hotfix landed.\n // Mark stuck so Deacon won't re-trigger, then let the caller handle it.\n markWorkspaceStuck(issueId, 'main_diverged', {\n localSha: pushErr.localSha,\n remoteSha: pushErr.remoteSha,\n });\n logActivity('merge_salvage_diverged', `Salvage aborted: origin/${targetBranch} diverged (remote ${pushErr.remoteSha.slice(0, 7)} not ancestor of local ${pushErr.localSha.slice(0, 7)})`);\n return { success: false, reason: pushErr.message };\n }\n throw pushErr;\n }\n\n console.log(`[merge-agent] Salvage push successful for ${issueId}`);\n logActivity('merge_salvage_success', `Stranded merge pushed successfully`);\n return { success: true };\n } catch (error: any) {\n console.error(`[merge-agent] Salvage failed: ${error.message}`);\n logActivity('merge_salvage_failed', `Salvage push failed: ${error.message}`);\n return null;\n }\n}\n\n/**\n * Result of syncing main into a workspace branch\n */\nexport interface SyncMainResult {\n success: boolean;\n alreadyUpToDate?: boolean;\n commitCount?: number;\n changedFiles?: string[];\n conflictFiles?: string[];\n reason?: string;\n}\n\n/**\n * Scan workspace for leftover git conflict markers (async)\n */\nexport async function scanForConflictMarkers(projectPath: string): Promise<string[]> {\n try {\n // git diff --check exits non-zero and prints filenames when conflict markers exist\n const { stdout } = await execAsync('git diff --check 2>&1 || true', {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n const files = stdout\n .split('\\n')\n .filter(line => line.includes('leftover conflict marker'))\n .map(line => line.split(':')[0].trim())\n .filter(f => f.length > 0);\n return [...new Set(files)];\n } catch {\n return [];\n }\n}\n\n/**\n * Sync the latest main branch into a workspace's feature branch.\n *\n * This performs a `git merge origin/main` in the workspace. If the merge is clean\n * it returns immediately. If conflicts arise, the conflict details are surfaced\n * for manual workspace resolution. The merge is never pushed — this is a local\n * workspace operation.\n *\n * Auto-commits any uncommitted changes before merging (with safety verification).\n */\nexport async function syncMainIntoWorkspace(\n projectPath: string,\n issueId: string,\n): Promise<SyncMainResult> {\n console.log(`[sync-main] Starting sync of main into workspace for ${issueId}`);\n logActivity('sync_main_start', `Starting sync for ${issueId}`);\n\n // PAN-1158 safety net: a workspace bd dolt DB that briefly went empty can\n // leave `.beads/issues.jsonl` reported as deleted by `git status`. The\n // auto-commit below would then propagate that deletion onto the feature\n // branch. Restore the tracked export first so the auto-commit only sees\n // intentional changes.\n await Effect.runPromise(restoreTrackedBeadsExport(projectPath));\n\n // Pre-flight: auto-commit uncommitted changes before merge\n console.log(`[sync-main] Checking for uncommitted changes...`);\n logActivity('sync_main_auto_commit', `Auto-committing uncommitted changes before sync`);\n const autoCommit = await autoCommitWorkspaceChangesBeforeSync(projectPath);\n if (!autoCommit.success) {\n const message = autoCommit.reason || 'Failed to auto-commit uncommitted changes';\n console.error(`[sync-main] ${message}`);\n logActivity('sync_main_blocked', message);\n return { success: false, reason: message };\n }\n if (autoCommit.committed) {\n console.log(`[sync-main] Auto-commit successful`);\n }\n\n // Verify no non-excluded uncommitted changes remain.\n try {\n const { stdout: postCommitStatus } = await execAsync('git status --porcelain', {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n if (postCommitStatus.trim()) {\n const remainingNonExcluded = postCommitStatus\n .trim()\n .split('\\n')\n .filter((line) => !isAutoCommitExcludedPath(parseStatusPath(line)));\n if (remainingNonExcluded.length > 0) {\n const message = 'Uncommitted changes remain after auto-commit — aborting sync';\n console.error(`[sync-main] ${message}`);\n logActivity('sync_main_blocked', message);\n return { success: false, reason: message };\n }\n }\n } catch (error: any) {\n return { success: false, reason: `Failed to check git status: ${error.message}` };\n }\n\n // Clean up stale git lock files\n try {\n const lockCleanup = await Effect.runPromise(cleanupStaleLocks(projectPath));\n if (lockCleanup.found.length > 0) {\n console.log(`[sync-main] Found ${lockCleanup.found.length} lock file(s)`);\n if (lockCleanup.removed.length > 0) {\n console.log(`[sync-main] Cleaned up ${lockCleanup.removed.length} stale lock file(s)`);\n logActivity('git_lock_cleanup', `Removed ${lockCleanup.removed.length} stale lock file(s)`);\n }\n if (lockCleanup.errors.some((e: { file: string; error: string }) => e.error.includes('Git processes are running'))) {\n const message = 'Git processes are still running — cannot safely start sync';\n console.error(`[sync-main] ${message}`);\n logActivity('sync_main_blocked', message);\n return { success: false, reason: message };\n }\n }\n } catch (lockErr: any) {\n console.warn(`[sync-main] Lock cleanup warning: ${lockErr.message} (continuing)`);\n }\n\n // Fetch latest main\n try {\n console.log(`[sync-main] Fetching origin/main...`);\n await execAsync('git fetch origin main', { cwd: projectPath, encoding: 'utf-8' });\n } catch (error: any) {\n return { success: false, reason: `Failed to fetch origin/main: ${error.message}` };\n }\n\n // Attempt the merge\n let mergeOutput = '';\n let hasConflicts = false;\n try {\n const result = await execAsync('git merge origin/main', { cwd: projectPath, encoding: 'utf-8' });\n mergeOutput = (result.stdout || '') + (result.stderr || '');\n } catch (error: any) {\n mergeOutput = (error.stdout || '') + (error.stderr || '');\n hasConflicts = true;\n }\n\n // Already up to date?\n if (mergeOutput.includes('Already up to date') || mergeOutput.includes('Already up-to-date')) {\n console.log(`[sync-main] Already up to date`);\n logActivity('sync_main_noop', `${issueId} already up to date with main`);\n return { success: true, alreadyUpToDate: true };\n }\n\n if (!hasConflicts) {\n // Clean merge — collect stats\n console.log(`[sync-main] Clean merge completed`);\n logActivity('sync_main_success', `Clean merge of main into ${issueId}`);\n\n let changedFiles: string[] = [];\n let commitCount = 0;\n try {\n const { stdout: diffFiles } = await execAsync(\n 'git diff --name-only ORIG_HEAD HEAD 2>/dev/null || git diff --name-only HEAD~1 HEAD',\n { cwd: projectPath, encoding: 'utf-8' },\n );\n changedFiles = diffFiles.trim().split('\\n').filter(f => f.length > 0);\n } catch { /* non-fatal */ }\n try {\n const { stdout: logOut } = await execAsync(\n 'git log ORIG_HEAD..HEAD --oneline 2>/dev/null || echo \"\"',\n { cwd: projectPath, encoding: 'utf-8' },\n );\n commitCount = logOut.trim().split('\\n').filter(l => l.length > 0).length;\n } catch { /* non-fatal */ }\n\n return { success: true, commitCount, changedFiles };\n }\n\n // PAN-1531: sync-main conflict case — surface to operator instead of\n // delegating to an LLM ship role. Abort the merge so the working tree is\n // clean, then return the conflict files for the caller to display.\n const conflictFiles = await getConflictFiles(projectPath);\n const mainPreferredResolution = await resolveMainPreferredSyncConflicts(projectPath, conflictFiles);\n if (mainPreferredResolution.success) {\n console.log(`[sync-main] Auto-resolved ${conflictFiles.length} pipeline-owned conflict(s) with origin/main`);\n logActivity('sync_main_auto_resolved_conflicts', `Auto-resolved ${conflictFiles.length} pipeline-owned conflict(s) in ${issueId} with origin/main`);\n\n let changedFiles: string[] = [];\n let commitCount = 0;\n try {\n const { stdout: diffFiles } = await execAsync(\n 'git diff --name-only ORIG_HEAD HEAD 2>/dev/null || git diff --name-only HEAD~1 HEAD',\n { cwd: projectPath, encoding: 'utf-8' },\n );\n changedFiles = diffFiles.trim().split('\\n').filter(f => f.length > 0);\n } catch { /* non-fatal */ }\n try {\n const { stdout: logOut } = await execAsync(\n 'git log ORIG_HEAD..HEAD --oneline 2>/dev/null || echo \"\"',\n { cwd: projectPath, encoding: 'utf-8' },\n );\n commitCount = logOut.trim().split('\\n').filter(l => l.length > 0).length;\n } catch { /* non-fatal */ }\n\n return { success: true, commitCount, changedFiles };\n }\n\n console.log(`[sync-main] ${conflictFiles.length} conflict(s); aborting merge for manual resolution`);\n logActivity('sync_main_conflicts', `${conflictFiles.length} conflict(s) in ${issueId}: ${conflictFiles.join(', ')}`);\n\n try { await execAsync('git merge --abort', { cwd: projectPath, encoding: 'utf-8' }); } catch { /* non-fatal */ }\n\n return {\n success: false,\n conflictFiles,\n reason: `Sync-main produced ${conflictFiles.length} conflict(s) in ${issueId}: ${conflictFiles.join(', ')}. Resolve manually in the workspace, then re-run sync-main.`,\n };\n\n}\n\n/**\n * Look up and run quality gates for the project at projectPath.\n * Returns empty array if no quality gates are configured.\n *\n * In polyrepo mode (projectPath is a sub-repo of project.path), only gates\n * whose `path` field matches the relative sub-repo path are run. Gates with\n * no `path` field are skipped in polyrepo context.\n */\nexport async function runProjectQualityGates(\n projectPath: string,\n phase: 'pre_push' | 'post_push'\n): Promise<import('./validation.js').QualityGateResult[]> {\n try {\n const config = loadProjectsConfigSync();\n // Find the project whose path matches\n const project = Object.values(config.projects).find(p => projectPath.startsWith(p.path));\n if (!project?.quality_gates || Object.keys(project.quality_gates).length === 0) {\n console.log(`[merge-agent] No quality gates configured for ${projectPath}`);\n return [];\n }\n\n // Detect polyrepo context: if projectPath is a subdirectory of project.path,\n // repoRelPath is non-empty (e.g., 'frontend' or 'backend').\n const repoRelPath = relative(project.path, projectPath);\n const matchedRepo = project.workspace?.repos?.find(repo => (\n repoRelPath === repo.path ||\n projectPath === join(project.path, repo.path)\n ));\n const repoIdentifiers = matchedRepo\n ? new Set([matchedRepo.path, matchedRepo.name])\n : new Set(repoRelPath && !repoRelPath.startsWith('..') ? [repoRelPath] : []);\n\n let gatesToRun = project.quality_gates;\n if (repoRelPath && !repoRelPath.startsWith('..')) {\n // Polyrepo: gates can target either the repo path (\"frontend\") or the\n // configured repo key/alias (\"fe\"). Both map to the same sub-repo.\n const filtered = Object.entries(project.quality_gates).filter(\n ([, gate]) => gate.path && repoIdentifiers.has(gate.path)\n );\n if (filtered.length === 0) {\n console.log(`[merge-agent] No quality gates configured for repo path \"${repoRelPath}\"`);\n return [];\n }\n gatesToRun = Object.fromEntries(filtered);\n console.log(\n `[merge-agent] Polyrepo: running ${Object.keys(gatesToRun).length} gate(s) for path \"${repoRelPath}\" (${[...repoIdentifiers].join(', ')})`\n );\n }\n\n console.log(`[merge-agent] Running ${phase} quality gates for project \"${project.name}\"`);\n return await Effect.runPromise(runQualityGates(gatesToRun, projectPath, phase));\n } catch (error: any) {\n console.error(`[merge-agent] Failed to load quality gates: ${error.message}`);\n return [];\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"merge-agent-C81xPIGd.js","names":[],"sources":["../../src/lib/overdeck/git-activity.ts","../../src/lib/git-activity.ts","../../src/lib/git/operations.ts","../../src/lib/cloister/merge-agent.ts"],"sourcesContent":["/**\n * Overdeck door for git_operations — persistent log of git push/fetch/merge\n * events. Replaces database/git-operations-db.ts.\n *\n * Uses the hand-maintained overdeck migration as the schema source of truth.\n */\n\nimport { getOverdeckDatabaseSync } from './infra.js';\n\n// ─── Schema bootstrap ─────────────────────────────────────────────────────────\n\nfunction overdeckDb() {\n return getOverdeckDatabaseSync();\n}\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type GitOperationType =\n | 'push'\n | 'force_push'\n | 'fetch'\n | 'merge'\n | 'rev_parse'\n | 'main_diverged'\n | 'push_attempt'\n | 'fetch_attempt'\n | 'push_rejected'\n | 'non_ff'\n | 'force_push_cmd'\n | 'retry'\n | 'remote_rejected'\n | 'push_noop';\n\nexport type GitOperationStatus = 'success' | 'failure' | 'aborted';\n\nexport interface GitOperation {\n id?: number;\n operation: GitOperationType;\n branch?: string;\n issueId?: string;\n beforeSha?: string;\n afterSha?: string;\n remoteSha?: string;\n status: GitOperationStatus;\n error?: string;\n ts: string;\n}\n\nexport interface GitOperationFilter {\n issueId?: string;\n operation?: GitOperationType;\n status?: GitOperationStatus;\n since?: string;\n limit?: number;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\nexport function appendGitOperationSync(op: Omit<GitOperation, 'id'>): number {\n const result = overdeckDb().prepare(`\n INSERT INTO git_operations (\n operation, branch, issue_id,\n before_sha, after_sha, remote_sha,\n status, error, ts\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `).run(\n op.operation,\n op.branch ?? null,\n op.issueId ?? null,\n op.beforeSha ?? null,\n op.afterSha ?? null,\n op.remoteSha ?? null,\n op.status,\n op.error ?? null,\n new Date(op.ts).getTime(),\n );\n return result.lastInsertRowid as number;\n}\n\nexport function listGitOperationsSync(filter: GitOperationFilter = {}): GitOperation[] {\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (filter.issueId) { conditions.push('issue_id = ?'); params.push(filter.issueId); }\n if (filter.operation) { conditions.push('operation = ?'); params.push(filter.operation); }\n if (filter.status) { conditions.push('status = ?'); params.push(filter.status); }\n if (filter.since) { conditions.push('ts >= ?'); params.push(new Date(filter.since).getTime()); }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n const limit = filter.limit ? `LIMIT ${filter.limit}` : 'LIMIT 500';\n\n const rows = overdeckDb().prepare(`\n SELECT id, operation, branch, issue_id, before_sha, after_sha, remote_sha, status, error, ts\n FROM git_operations ${where} ORDER BY ts DESC ${limit}\n `).all(...params) as Array<{\n id: number;\n operation: string;\n branch: string | null;\n issue_id: string | null;\n before_sha: string | null;\n after_sha: string | null;\n remote_sha: string | null;\n status: string;\n error: string | null;\n ts: number;\n }>;\n\n return rows.map((row) => ({\n id: row.id,\n operation: row.operation as GitOperationType,\n branch: row.branch ?? undefined,\n issueId: row.issue_id ?? undefined,\n beforeSha: row.before_sha ?? undefined,\n afterSha: row.after_sha ?? undefined,\n remoteSha: row.remote_sha ?? undefined,\n status: row.status as GitOperationStatus,\n error: row.error ?? undefined,\n ts: new Date(row.ts).toISOString(),\n }));\n}\n","/**\n * Git Activity Service (PAN-653)\n *\n * Write path and read path for git operation events persisted to the\n * git_operations SQLite table. Survives dashboard restart — unlike the\n * in-memory activityLog array which is capped at 100 and wiped on restart.\n *\n * All functions use the async-safe getDatabase() which runs under Node 22.\n * NEVER use execSync/readFileSync here — this runs in the dashboard server.\n */\n\nimport { Effect, Data } from 'effect';\nimport {\n appendGitOperationSync,\n listGitOperationsSync,\n} from './overdeck/git-activity.js';\nimport type {\n GitOperation,\n GitOperationFilter,\n GitOperationType,\n GitOperationStatus,\n} from './overdeck/git-activity.js';\n\nexport type { GitOperation, GitOperationFilter, GitOperationType, GitOperationStatus };\nexport { appendGitOperationSync, listGitOperationsSync };\n\n/** A database operation against the git_operations table failed. */\nexport class GitActivityDbError extends Data.TaggedError('GitActivityDbError')<{\n readonly operation: string;\n readonly cause: unknown;\n}> {}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n\n/**\n * Effect-native appendGitOperation — typed-error variant of the SQLite write.\n * Fails with GitActivityDbError if the insert throws (e.g. DB locked).\n */\nexport const appendGitOperation = (\n op: Omit<GitOperation, 'id'>,\n): Effect.Effect<number, GitActivityDbError> =>\n Effect.try({\n try: () => appendGitOperationSync(op),\n catch: (cause) => new GitActivityDbError({ operation: 'appendGitOperation', cause }),\n });\n\n/**\n * Effect-native listGitOperations — typed-error variant of the SQLite read.\n * Fails with GitActivityDbError on query failure.\n */\nexport const listGitOperations = (\n filter: GitOperationFilter = {},\n): Effect.Effect<readonly GitOperation[], GitActivityDbError> =>\n Effect.try({\n try: () => listGitOperationsSync(filter),\n catch: (cause) => new GitActivityDbError({ operation: 'listGitOperations', cause }),\n });\n","/**\n * Git Operations Helpers (PAN-653)\n *\n * Thin wrappers over raw git commands that:\n * 1. Use execAsync (never execSync) — safe for dashboard server use\n * 2. Record before/after/remote SHAs for each operation\n * 3. Emit structured git.* events to the git_operations SQLite table\n * 4. For gitPush: perform a pre-push fetch + ancestor check and throw\n * MainDivergedError if origin/main has advanced past the local ancestor\n *\n * Usage: import { gitPush, gitFetch, gitForcePush, gitMerge, gitRevParse }\n * from 'src/lib/git/operations.js'\n *\n * NEVER add execSync, readFileSync, writeFileSync here — this module is\n * imported by dashboard server code (CLAUDE.md rule).\n */\n\nimport { execFile } from 'child_process';\nimport { promisify } from 'util';\nimport { Effect } from 'effect';\nimport { appendGitOperationSync } from '../git-activity.js';\nimport { GitError, VcsError } from '../errors.js';\n\nconst execFileAsync = promisify(execFile);\n\n// ============== Error types ==============\n\n/**\n * Thrown by gitPush when origin/main has advanced beyond the local ancestor.\n * The caller must NOT push — it should call markWorkspaceStuck instead.\n */\nexport class MainDivergedError extends Error {\n readonly localSha: string;\n readonly remoteSha: string;\n\n constructor(localSha: string, remoteSha: string) {\n super(\n `Main diverged: remote HEAD (${remoteSha.slice(0, 7)}) is not an ancestor of local HEAD (${localSha.slice(0, 7)}). Aborting push to protect the hotfix commit.`\n );\n this.name = 'MainDivergedError';\n this.localSha = localSha;\n this.remoteSha = remoteSha;\n }\n}async function gitRevParsePromise(cwd: string, ref: string): Promise<string | null> {\n const ts = new Date().toISOString();\n try {\n const { stdout } = await execFileAsync('git', ['rev-parse', ref], { cwd, encoding: 'utf-8' });\n const sha = stdout.trim();\n appendGitOperationSync({ operation: 'rev_parse', branch: ref, issueId: undefined, afterSha: sha, status: 'success', ts });\n return sha || null;\n } catch {\n return null;\n }\n}async function gitFetchPromise(\n cwd: string,\n remote = 'origin',\n branch?: string,\n opts: { issueId?: string } = {},\n): Promise<void> {\n const ts = new Date().toISOString();\n const args = branch ? ['fetch', remote, branch] : ['fetch', remote];\n try {\n await execFileAsync('git', args, { cwd, encoding: 'utf-8', timeout: 30000 });\n appendGitOperationSync({\n operation: 'fetch',\n branch: branch ?? remote,\n issueId: opts.issueId,\n status: 'success',\n ts,\n });\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n appendGitOperationSync({\n operation: 'fetch',\n branch: branch ?? remote,\n issueId: opts.issueId,\n status: 'failure',\n error: msg,\n ts,\n });\n throw err;\n }\n}async function gitPushPromise(\n cwd: string,\n remote = 'origin',\n branch = 'main',\n opts: { issueId?: string } = {},\n): Promise<void> {\n const ts = new Date().toISOString();\n\n // Step 1: record the local HEAD before the push\n const localSha = await Effect.runPromise(gitRevParse(cwd, 'HEAD')) ?? 'unknown';\n\n // Step 2: fetch latest remote state\n await Effect.runPromise(gitFetch(cwd, remote, branch, opts));\n\n // Step 3: read the remote tracking SHA\n const remoteSha = await Effect.runPromise(gitRevParse(cwd, `${remote}/${branch}`)) ?? '';\n\n // Step 4: ancestor check — is remoteSha an ancestor of localSha?\n if (remoteSha) {\n try {\n // git merge-base --is-ancestor <commit> <commit> exits 0 if true, 1 if false\n await execFileAsync('git', ['merge-base', '--is-ancestor', remoteSha, localSha], {\n cwd,\n encoding: 'utf-8',\n timeout: 10000,\n });\n } catch (err: unknown) {\n // exit code 1 = \"not an ancestor\" (true divergence); any other code is a real git error\n if ((err as { code?: number }).code !== 1) throw err;\n appendGitOperationSync({\n operation: 'main_diverged',\n branch,\n issueId: opts.issueId,\n beforeSha: localSha,\n remoteSha,\n status: 'aborted',\n error: `origin/${branch} (${remoteSha.slice(0, 7)}) is not ancestor of local HEAD (${localSha.slice(0, 7)})`,\n ts,\n });\n throw new MainDivergedError(localSha, remoteSha);\n }\n }\n\n // Step 5: push\n try {\n await execFileAsync('git', ['push', remote, branch], { cwd, encoding: 'utf-8', timeout: 60000 });\n const afterSha = await Effect.runPromise(gitRevParse(cwd, 'HEAD')) ?? localSha;\n appendGitOperationSync({\n operation: 'push',\n branch,\n issueId: opts.issueId,\n beforeSha: localSha,\n afterSha,\n remoteSha,\n status: 'success',\n ts,\n });\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n appendGitOperationSync({\n operation: 'push',\n branch,\n issueId: opts.issueId,\n beforeSha: localSha,\n remoteSha,\n status: 'failure',\n error: msg,\n ts,\n });\n throw err;\n }\n}async function gitForcePushPromise(\n cwd: string,\n remote = 'origin',\n branch = 'main',\n opts: { issueId?: string; reason?: string } = {},\n): Promise<void> {\n const ts = new Date().toISOString();\n const localSha = await Effect.runPromise(gitRevParse(cwd, 'HEAD')) ?? 'unknown';\n const remoteSha = await Effect.runPromise(gitRevParse(cwd, `${remote}/${branch}`)) ?? undefined;\n\n try {\n await execFileAsync('git', ['push', '--force-with-lease', remote, branch], {\n cwd,\n encoding: 'utf-8',\n timeout: 60000,\n });\n const afterSha = await Effect.runPromise(gitRevParse(cwd, 'HEAD')) ?? localSha;\n appendGitOperationSync({\n operation: 'force_push',\n branch,\n issueId: opts.issueId,\n beforeSha: localSha,\n afterSha,\n remoteSha,\n status: 'success',\n error: opts.reason,\n ts,\n });\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n appendGitOperationSync({\n operation: 'force_push',\n branch,\n issueId: opts.issueId,\n beforeSha: localSha,\n remoteSha,\n status: 'failure',\n error: msg,\n ts,\n });\n throw err;\n }\n}async function gitMergePromise(\n cwd: string,\n branch: string,\n opts: { issueId?: string; noFf?: boolean } = {},\n): Promise<void> {\n const ts = new Date().toISOString();\n const beforeSha = await Effect.runPromise(gitRevParse(cwd, 'HEAD')) ?? 'unknown';\n const args = opts.noFf ? ['merge', '--no-ff', branch] : ['merge', branch];\n try {\n await execFileAsync('git', args, { cwd, encoding: 'utf-8', timeout: 60000 });\n const afterSha = await Effect.runPromise(gitRevParse(cwd, 'HEAD')) ?? beforeSha;\n appendGitOperationSync({\n operation: 'merge',\n branch,\n issueId: opts.issueId,\n beforeSha,\n afterSha,\n status: 'success',\n ts,\n });\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n appendGitOperationSync({\n operation: 'merge',\n branch,\n issueId: opts.issueId,\n beforeSha,\n status: 'failure',\n error: msg,\n ts,\n });\n throw err;\n }\n}\n\n// ─── Effect variants (PAN-1249, additive) ────────────────────────────────────\n//\n// These wrap the existing Promise-based functions with typed Effect error\n// channels (GitError / VcsError). The underlying impl is unchanged; this is\n// an additive surface for callers that already speak Effect. MainDivergedError\n// is thrown by the underlying gitPush; the Effect variant surfaces it\n// preserved as the cause inside a VcsError tagged 'main-diverged'.\n\n/** Resolve a git ref to its SHA. Returns null if the ref does not exist. */\nexport function gitRevParse(\n cwd: string,\n ref: string,\n): Effect.Effect<string | null> {\n return Effect.promise(() => gitRevParsePromise(cwd, ref));\n}\n\n/** Fetch from a remote. */\nexport function gitFetch(\n cwd: string,\n remote = 'origin',\n branch?: string,\n opts: { issueId?: string } = {},\n): Effect.Effect<void, GitError> {\n return Effect.tryPromise({\n try: () => gitFetchPromise(cwd, remote, branch, opts),\n catch: (cause) =>\n new GitError({\n command: branch ? ['git', 'fetch', remote, branch] : ['git', 'fetch', remote],\n stderr: cause instanceof Error ? cause.message : String(cause),\n exitCode: -1,\n cause,\n }),\n });\n}\n\n/**\n * Push a branch to a remote with divergence guard.\n * Fails with VcsError tagged 'main-diverged' when MainDivergedError is thrown.\n */\nexport function gitPush(\n cwd: string,\n remote = 'origin',\n branch = 'main',\n opts: { issueId?: string } = {},\n): Effect.Effect<void, VcsError | GitError> {\n return Effect.tryPromise({\n try: () => gitPushPromise(cwd, remote, branch, opts),\n catch: (cause) => {\n if (cause instanceof MainDivergedError) {\n return new VcsError({\n operation: 'main-diverged',\n message: cause.message,\n cause,\n });\n }\n return new GitError({\n command: ['git', 'push', remote, branch],\n stderr: cause instanceof Error ? cause.message : String(cause),\n exitCode: -1,\n cause,\n });\n },\n });\n}\n\n/** Force-push a branch (--force-with-lease). */\nexport function gitForcePush(\n cwd: string,\n remote = 'origin',\n branch = 'main',\n opts: { issueId?: string; reason?: string } = {},\n): Effect.Effect<void, GitError> {\n return Effect.tryPromise({\n try: () => gitForcePushPromise(cwd, remote, branch, opts),\n catch: (cause) =>\n new GitError({\n command: ['git', 'push', '--force-with-lease', remote, branch],\n stderr: cause instanceof Error ? cause.message : String(cause),\n exitCode: -1,\n cause,\n }),\n });\n}\n\n/** Merge a branch into the current branch. */\nexport function gitMerge(\n cwd: string,\n branch: string,\n opts: { issueId?: string; noFf?: boolean } = {},\n): Effect.Effect<void, GitError> {\n return Effect.tryPromise({\n try: () => gitMergePromise(cwd, branch, opts),\n catch: (cause) =>\n new GitError({\n command: opts.noFf ? ['git', 'merge', '--no-ff', branch] : ['git', 'merge', branch],\n stderr: cause instanceof Error ? cause.message : String(cause),\n exitCode: -1,\n cause,\n }),\n });\n}\n","/**\n * Merge Agent - Automatic merge conflict resolution using Claude Code\n */\n\nimport { existsSync, mkdirSync, appendFileSync } from 'fs';\nimport { writeFile } from 'fs/promises';\nimport { join, dirname, basename, relative } from 'path';\nimport { fileURLToPath } from 'url';\nimport { spawn, exec } from 'child_process';\nimport { promisify } from 'util';\nimport { Effect } from 'effect';\nimport { capturePane, killSession, listSessionNames, sendKeys, sessionExists } from '../tmux.js';\nimport { emitActivityEntrySync, emitActivityTtsSync } from '../activity-logger.js';\nimport { loadConfigSync } from '../config-yaml.js';\n\nconst execAsync = promisify(exec);\n\n/**\n * Paths that must never enter a pipeline auto-commit, regardless of gitignore\n * state. These are workspace-local or machine-local state files and sync-target\n * directories; committing them pollutes feature branches and main.\n */\nexport const AUTO_COMMIT_EXCLUDED_PATHS = [\n '.pan/kickoff.md',\n '.pan/continue.json',\n '.pan/handoff-*.md',\n '.pan/spec.vbrief.json',\n '.claude/rules/',\n '.claude/skills/',\n // PAN-1899: machine-local Overdeck config copied into every workspace by\n // copyOverdeckSettingsToWorkspaceSync (config.yaml, projects.yaml,\n // settings.json). It diverges from main constantly and must never enter a\n // feature branch — that was the source of the recurring projects.yaml sync\n // conflict.\n '.overdeck/',\n];\n\nconst SYNC_MAIN_MAIN_PREFERRED_PATHS = [\n '.pan/continues',\n '.pan/specs',\n '.beads',\n];\n\nexport function isSyncMainMainPreferredPath(relativePath: string): boolean {\n const normalized = relativePath.replace(/\\\\/g, '/');\n return SYNC_MAIN_MAIN_PREFERRED_PATHS.some(\n (prefix) => normalized === prefix || normalized.startsWith(`${prefix}/`),\n );\n}\n\nexport function isAutoCommitExcludedPath(relativePath: string): boolean {\n const normalized = relativePath.replace(/\\\\/g, '/');\n for (const pattern of AUTO_COMMIT_EXCLUDED_PATHS) {\n if (pattern.endsWith('/')) {\n if (normalized.startsWith(pattern) || normalized === pattern.slice(0, -1)) {\n return true;\n }\n } else if (pattern.includes('*')) {\n const regex = new RegExp(\n '^' + pattern.replace(/\\./g, '\\\\.').replace(/\\*/g, '[^/]*') + '$'\n );\n if (regex.test(normalized)) return true;\n } else if (normalized === pattern) {\n return true;\n }\n }\n return false;\n}\n\nfunction parseStatusPath(line: string): string {\n // git status --porcelain lines are \"XY PATH\" or \"XY ORIG -> DEST\" for renames.\n const body = line.slice(3);\n if (line[0] === 'R' || line[1] === 'R') {\n const parts = body.split(' -> ');\n return parts[parts.length - 1];\n }\n return body;\n}\n\n/**\n * Auto-commit non-excluded workspace changes before a sync-main merge.\n * Respects .gitignore (no -f), unstages excluded paths, and leaves excluded\n * paths dirty so the sync can proceed. Returns success=false on git errors.\n */\nexport async function autoCommitWorkspaceChangesBeforeSync(\n projectPath: string,\n): Promise<{ success: boolean; committed: boolean; reason?: string }> {\n try {\n const { stdout: statusOut } = await execAsync('git status --porcelain', {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n if (!statusOut.trim()) {\n return { success: true, committed: false, reason: 'no uncommitted changes' };\n }\n\n // PAN-1819: plain `git add -A` respects .gitignore; never use -f.\n await execAsync('git add -A', { cwd: projectPath, encoding: 'utf-8' });\n\n // Belt-and-suspenders: unstage excluded paths regardless of ignore state.\n const resetPaths = AUTO_COMMIT_EXCLUDED_PATHS.map((p) =>\n p.endsWith('/') ? p.slice(0, -1) : p\n ).join(' ');\n await execAsync(`git reset HEAD -- ${resetPaths}`, {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n\n const { stdout: diffStat } = await execAsync('git diff --cached --stat', {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n if (!diffStat.trim()) {\n return { success: true, committed: false, reason: 'only excluded/ignored changes remain' };\n }\n\n await execAsync('git commit -m \"chore: auto-commit before sync with main\"', {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n return { success: true, committed: true };\n } catch (error: any) {\n return { success: false, committed: false, reason: `Failed to auto-commit: ${error.message}` };\n }\n}\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nimport {\n OVERDECK_HOME,\n} from '../paths.js';\nimport { resolveGitHubIssueSync } from '../tracker-utils.js';\n\nimport { restoreTrackedBeadsExport } from '../beads-restore.js';\nimport { runQualityGates } from './validation.js';\nimport { loadProjectsConfigSync } from '../projects.js';\nimport { cleanupStaleLocks } from '../git-utils.js';\nimport { gitPush, MainDivergedError } from '../git/operations.js';\nimport { markWorkspaceStuck, setReviewStatusSync } from '../review-status.js';\nimport { appendGitOperationSync, type GitOperationType } from '../git-activity.js';\nimport { recordFeatureRegistryLifecycle } from '../registry/feature-registry-population.js';\nimport { verifyMergedBeforeLifecycle, type PostMergeLifecycleOptions } from './merge-verification.js';\n\nconst SPECIALISTS_DIR = join(OVERDECK_HOME, 'specialists');\nconst MERGE_HISTORY_DIR = join(SPECIALISTS_DIR, 'merge-agent');\nconst MERGE_HISTORY_FILE = join(MERGE_HISTORY_DIR, 'history.jsonl');\n\n/**\n * Context for a merge conflict resolution request\n */\nexport interface MergeConflictContext {\n projectPath: string;\n sourceBranch: string;\n targetBranch: string;\n conflictFiles: string[];\n issueId: string;\n testCommand?: string;\n}\n\n/**\n * Result of merge agent execution\n */\nexport interface MergeResult {\n success: boolean;\n resolvedFiles?: string[];\n failedFiles?: string[];\n testsStatus?: 'PASS' | 'FAIL' | 'SKIP';\n validationStatus?: 'PASS' | 'FAIL' | 'NOT_RUN';\n reason?: string;\n notes?: string;\n output?: string;\n}\n\n/**\n * Merge history entry\n */\ninterface MergeHistoryEntry {\n timestamp: string;\n issueId: string;\n sourceBranch: string;\n targetBranch: string;\n conflictFiles: string[];\n result: MergeResult;\n sessionId?: string;\n}\n\n/**\n * Timeout for merge agent in milliseconds (15 minutes)\n */\n/**\n * Notify TLDR daemon to reindex changed files after merge\n */\nexport async function notifyTldrDaemon(projectPath: string, _sourceBranch: string): Promise<void> {\n try {\n console.log(`[merge-agent] Notifying TLDR daemon to reindex changed files...`);\n\n // Check if TLDR daemon is available\n const venvPath = join(projectPath, '.venv');\n if (!existsSync(venvPath)) {\n console.log(`[merge-agent] No .venv found, skipping TLDR notification`);\n return;\n }\n\n // Get changed files from the merge\n const { stdout } = await execAsync(`git diff --name-only HEAD~1 HEAD`, {\n cwd: projectPath,\n encoding: 'utf-8'\n });\n\n const changedFiles = stdout\n .trim()\n .split('\\n')\n .filter(f => f.trim().length > 0)\n .filter(f => {\n // Only include source code files (skip docs, configs, etc)\n const ext = f.split('.').pop()?.toLowerCase();\n return ext && ['ts', 'js', 'tsx', 'jsx', 'py', 'java', 'go', 'rs', 'cpp', 'c', 'h'].includes(ext);\n });\n\n if (changedFiles.length === 0) {\n console.log(`[merge-agent] No source files changed, skipping TLDR notification`);\n return;\n }\n\n console.log(`[merge-agent] Found ${changedFiles.length} changed source files to reindex`);\n\n // Get TLDR daemon service\n const { getTldrDaemonServiceSync } = await import('../tldr-daemon.js');\n const tldrService = getTldrDaemonServiceSync(projectPath, venvPath);\n\n // Check if daemon is running\n const status = await tldrService.getStatus();\n if (!status.running) {\n console.log(`[merge-agent] TLDR daemon not running, skipping notification`);\n return;\n }\n\n // Trigger warm to reindex (this will update the index incrementally)\n console.log(`[merge-agent] Triggering TLDR index warm...`);\n await tldrService.warm(true); // background mode\n\n console.log(`[merge-agent] ✓ TLDR daemon notified to reindex`);\n logActivity('tldr_notified', `Notified TLDR daemon to reindex ${changedFiles.length} files`);\n } catch (error: any) {\n // Non-fatal - log warning and continue\n console.warn(`[merge-agent] Failed to notify TLDR daemon: ${error.message}`);\n logActivity('tldr_notify_error', `TLDR notification failed: ${error.message}`);\n }\n}\n\n/**\n * Post-merge handoff: mark merged work as verifying on main and free runtime resources.\n *\n * Leaves the issue, workspace, vBRIEF, branches, and agent state dirs intact.\n * The explicit close-out ceremony performs final archival and destructive cleanup.\n *\n * IDEMPOTENT: Safe to call multiple times for the same issueId. Tracks completed\n * issues and returns immediately on re-entry. This is defense-in-depth against\n * the infinite loop that burned 24,626 Linear API calls (PAN-328).\n */\n\n// Defense-in-depth: track issues that have completed postMergeLifecycle.\n// Prevents re-execution even if caller guards fail. Persists for server lifetime.\nconst _completedPostMerge = new Set<string>();\nconst _postMergeInFlight = new Map<string, Promise<void>>();\n\n// PAN-1531: dropLingeringPreMergeStashes removed. The pre-merge stash kind\n// is no longer created by Overdeck, so there's nothing for the post-merge\n// lifecycle to clean up. Pre-existing pre-merge:* residue in refs/stash is\n// expected and inert.\n\nfunction shellQuote(value: string): string {\n return `'${value.replace(/'/g, `'\"'\"'`)}'`;\n}\n\nexport async function postMergeLifecycle(\n issueId: string,\n projectPath: string,\n sourceBranch?: string,\n options?: PostMergeLifecycleOptions,\n): Promise<void> {\n // PAN-1517: the per-slot swarm runtime is gone. Slot branches no longer exist\n // — parallelism is an in-context concern owned by the work agent (see\n // roles/work.md \"Parallel work via subagents\"). postMergeLifecycle fires only\n // for the issue's main feature branch merging to `main`.\n\n // Guard 1: skip if already completed (defense-in-depth against infinite loops)\n if (_completedPostMerge.has(issueId)) {\n console.log(`[merge-agent] postMergeLifecycle already completed for ${issueId}, skipping`);\n return;\n }\n\n const inFlight = _postMergeInFlight.get(issueId);\n if (inFlight) {\n console.log(`[merge-agent] postMergeLifecycle already running for ${issueId}, joining in-flight run`);\n return inFlight;\n }\n\n const run = (async () => {\n // Guard 2: closed-out is TERMINAL. Close-out flips the spec on main to\n // completed/cancelled, clears review status, and closes the tracker issue.\n // Re-running the handoff after that resurrects the review row and REOPENS\n // the closed issue — observed live on PAN-1190 (2026-06-11): the deacon's\n // stale-mergeStatus sweep saw the cleared row as \"stale\" 47 minutes after\n // close-out and the handoff reopened it into verifying-on-main forever.\n try {\n const { findSpecByIssue } = await import('../pan-dir/specs.js');\n const spec = await Effect.runPromise(findSpecByIssue(projectPath, issueId));\n if (spec && (spec.status === 'completed' || spec.status === 'cancelled')) {\n console.log(`[merge-agent] ${issueId} is closed out (spec ${spec.status}) — skipping post-merge lifecycle`);\n _completedPostMerge.add(issueId);\n return;\n }\n } catch {\n // Spec unreadable — proceed; the guard is best-effort.\n }\n\n const mergeVerification = await verifyMergedBeforeLifecycle(issueId, projectPath, sourceBranch, options);\n if (!mergeVerification.merged) {\n console.warn(`[merge-agent] Refusing post-merge lifecycle for ${issueId}: ${mergeVerification.reason}`);\n return;\n }\n console.log(`[merge-agent] Verified merge before lifecycle for ${issueId}: ${mergeVerification.reason}`);\n\n // Set mergeStatus='merged' after verifying the branch or PR actually landed.\n try {\n setReviewStatusSync(issueId, {\n mergeStatus: 'merged',\n readyForMerge: false,\n ...(options?.markReviewPassed ? { reviewStatus: 'passed' as const } : {}),\n });\n console.log(`[merge-agent] ✓ mergeStatus set to 'merged' for ${issueId}`);\n } catch (err: any) {\n console.warn(`[merge-agent] Could not set mergeStatus: ${err.message}`);\n }\n\n // Step 0: Write pending lifecycle file and spawn detached deploy script.\n // The deploy script rebuilds dist/, kills this server, and starts a fresh process.\n // The fresh process reads the pending file on startup and runs the lifecycle steps\n // with correct module chunk references (no ERR_MODULE_NOT_FOUND after merge).\n //\n // Skip this step when we ARE the fresh process (called from processPendingLifecycle) —\n // dynamic imports already resolve correctly and spawning again would create an infinite loop.\n if (!options?.skipDeploy) {\n const pendingFile = join(OVERDECK_HOME, 'pending-post-merge.json');\n let repoRoot = __dirname.includes('/src/')\n ? __dirname.replace(/\\/src\\/.*$/, '')\n : __dirname.replace(/\\/dist\\/.*$/, '').replace(/\\/lib\\/.*$/, '');\n // If running from a workspace (workspaces/feature-*/), resolve to the main repo root.\n // Without this, the deploy script builds and npm-links from the workspace, hijacking\n // the global `pan` CLI to point at stale workspace code.\n const wsMatch = repoRoot.match(/^(.+)\\/workspaces\\/feature-[^/]+$/);\n if (wsMatch) {\n repoRoot = wsMatch[1];\n console.log(`[merge-agent] Resolved workspace repoRoot to main repo: ${repoRoot}`);\n }\n const deployScript = join(repoRoot, 'scripts', 'post-merge-deploy.sh');\n\n try {\n const pendingData = JSON.stringify({\n issueId,\n projectPath,\n sourceBranch: sourceBranch ?? '',\n timestamp: Date.now(),\n reason: 'post-merge',\n trigger: 'merge-agent',\n });\n await writeFile(pendingFile, pendingData, 'utf-8');\n console.log(`[merge-agent] Wrote pending lifecycle file: ${pendingFile}`);\n\n // Pass 'post-merge' as the reason to the deploy script so it writes the\n // restart marker. We spawn detached and return immediately — the deploy script\n // kills this server. The new server reads the pending file on boot,\n // emits lifecycle_started, and after processing emits lifecycle_complete/failed.\n const child = spawn(deployScript, [repoRoot, issueId, projectPath, sourceBranch ?? '', 'post-merge'], {\n detached: true,\n stdio: 'ignore',\n });\n child.unref();\n console.log(`[merge-agent] Spawned detached deploy script (pid ${child.pid}) — server will restart with new build`);\n return;\n } catch (err: any) {\n console.warn(`[merge-agent] Failed to spawn deploy script: ${err.message}. Falling through to in-process lifecycle (may fail on stale chunks).`);\n }\n }\n\n console.log(`[merge-agent] Running post-merge verify handoff for ${issueId}`);\n\n // 1. Clean up stale workflow labels and keep the legacy merged marker for history.\n // verifying-on-main is applied next and takes precedence in canonical state mapping.\n try {\n const { cleanupMergedLabels } = await import('../lifecycle/label-cleanup.js');\n const ghResolved = resolveGitHubIssueSync(issueId);\n const labelCtx = ghResolved.isGitHub\n ? { issueId, projectPath, github: { owner: ghResolved.owner, repo: ghResolved.repo, number: ghResolved.number } }\n : { issueId, projectPath };\n // PAN-1249: cleanupMergedLabels returns Effect<StepResult>; bridge to Promise.\n const labelResult = await Effect.runPromise(cleanupMergedLabels(labelCtx));\n if (labelResult.success && !labelResult.skipped) {\n console.log(`[merge-agent] ✓ ${labelResult.details?.join('; ')}`);\n logActivity('labels_cleaned', labelResult.details?.join('; ') || 'Labels cleaned');\n } else if (labelResult.skipped) {\n console.log(`[merge-agent] Label cleanup skipped: ${labelResult.details?.join('; ')}`);\n } else {\n console.warn(`[merge-agent] Label cleanup failed (non-fatal): ${labelResult.error}`);\n }\n } catch (err) {\n console.warn(`[merge-agent] Could not clean labels: ${err}`);\n }\n\n try {\n await transitionIssueToVerifyingOnMain(issueId, projectPath);\n void recordFeatureRegistryLifecycle({ issueId, status: 'merged' });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.warn(`[merge-agent] Could not transition issue to verifying_on_main: ${message}`);\n try {\n setReviewStatusSync(issueId, {\n mergeStatus: 'failed',\n readyForMerge: false,\n mergeNotes: `Post-merge verifying_on_main transition failed: ${message}`,\n });\n } catch (statusErr: any) {\n console.warn(`[merge-agent] Could not persist verifying_on_main transition failure: ${statusErr?.message ?? statusErr}`);\n }\n announceMerge('failed', issueId, `Post-merge verifying_on_main transition failed: ${message}`);\n logActivity('merge_failed', `Post-merge verifying_on_main transition failed for ${issueId}: ${message}`);\n throw err;\n }\n\n // 2. Compact old beads (via lifecycle module)\n try {\n const { compactBeads } = await import('../lifecycle/compact-beads.js');\n // PAN-1249: compactBeads returns Effect<StepResult>; bridge to Promise.\n const beadsResult = await Effect.runPromise(compactBeads({ issueId, projectPath }));\n if (beadsResult.success && !beadsResult.skipped) {\n console.log(`[merge-agent] ✓ ${beadsResult.details?.join('; ')}`);\n logActivity('beads_compaction_complete', beadsResult.details?.join('; ') || 'Beads compacted');\n }\n } catch (err) {\n console.warn(`[merge-agent] Beads compaction failed: ${err}`);\n }\n\n // 3. Pause work/planning agents and kill their tmux panes to free resources.\n try {\n const { setAgentPaused, getAgentState } = await import('../agents.js');\n const { killSession, sessionExists } = await import('../tmux.js');\n const issueLower = issueId.toLowerCase();\n const reason = 'awaiting close-out (verify on main)';\n for (const agentId of [`agent-${issueLower}`, `planning-${issueLower}`]) {\n // Pause, then VERIFY the gate actually persisted to state.json. A server\n // restart mid-lifecycle (the PAN-1723 deploy re-runs this from\n // pending-post-merge.json) or a concurrent deacon read-modify-write on\n // state.json can silently drop the pause. An unpaused merged work agent\n // sits idle at its prompt yet still counts against the PAN-1665 work\n // ceiling, throttling dispatch for every live issue (PAN-1726). Assert\n // paused=true after the write; retry once, then fail loudly.\n const initial = await Effect.runPromise(setAgentPaused(agentId, reason, true));\n if (initial === null) {\n // No state.json for this agent — nothing to pause (e.g. planning never ran).\n continue;\n }\n let verify = await Effect.runPromise(getAgentState(agentId));\n if (verify?.paused !== true) {\n await Effect.runPromise(setAgentPaused(agentId, reason, true));\n verify = await Effect.runPromise(getAgentState(agentId));\n }\n if (verify?.paused === true) {\n console.log(`[merge-agent] ✓ Paused ${agentId}: ${reason}`);\n } else {\n console.error(\n `[merge-agent] ✗ FAILED to persist pause for ${agentId} after merge — state.json paused=${verify?.paused}. ` +\n `Idle merged work agent may hold a PAN-1665 work slot and throttle dispatch (PAN-1726).`,\n );\n logActivity('agent_pause_failed', `Could not persist pause for ${agentId} after merge — may throttle dispatch (PAN-1726)`);\n }\n if (await Effect.runPromise(sessionExists(agentId))) {\n await Effect.runPromise(killSession(agentId));\n console.log(`[merge-agent] ✓ Killed ${agentId} tmux session to free resources`);\n logActivity('agent_session_killed', `Freed resources: killed tmux session for ${agentId}`);\n }\n }\n } catch (err) {\n console.warn(`[merge-agent] Could not pause or kill agent sessions: ${err}`);\n }\n\n // 5a. Kill canonical reviewer/synthesis sessions (PAN-915).\n // Sessions persist across review rounds to preserve reviewer context, so the\n // merge is the right moment to tear them down. Issue is done — context value\n // is zero, RSS leak risk is non-zero. Resolve projectKey from the project\n // path so we don't depend on caller-supplied config.\n try {\n const { killAllReviewerSessions } = await import('./review-agent.js');\n const { resolveProjectFromIssueSync } = await import('../projects.js');\n const resolved = resolveProjectFromIssueSync(issueId);\n const projectKey = resolved?.projectKey;\n if (projectKey) {\n const { killed } = await Effect.runPromise(killAllReviewerSessions(projectKey, issueId));\n if (killed.length > 0) {\n console.log(`[merge-agent] ✓ Killed ${killed.length} canonical reviewer session(s) for ${issueId}`);\n logActivity('reviewer_sessions_killed', `Killed ${killed.length} reviewer session(s) for ${issueId} on merge`);\n }\n }\n } catch (err) {\n console.warn(`[merge-agent] Could not kill canonical reviewer sessions: ${err}`);\n }\n\n await killPostMergeRoleSessions(issueId);\n\n // 3c. Create a workspace-scoped memory reset marker so old review-blocker noise stays archived but out of retrieval.\n try {\n const { createResetMarker } = await import('../memory/cli.js');\n const timestamp = new Date().toISOString();\n const marker = await createResetMarker({\n projectId: basename(projectPath),\n scope: 'workspace',\n scopeId: `feature-${issueId.toLowerCase()}`,\n reason: 'post-merge cleanup',\n fromTimestamp: timestamp,\n createdAt: timestamp,\n });\n console.log(`[merge-agent] ✓ Created memory reset marker ${marker.id} for ${marker.scope}:${marker.scopeId}`);\n } catch (err) {\n console.warn(`[merge-agent] Memory reset marker creation failed (non-fatal): ${err}`);\n }\n\n // 4. Stop Docker containers + networks to prevent network pool exhaustion (non-fatal)\n // Orphaned Docker networks accumulate when workspaces are merged but containers are never\n // torn down, eventually exhausting Docker's address pool and blocking new workspace creation.\n try {\n const { findWorkspacePath } = await import('../lifecycle/archive-planning.js');\n const { stopWorkspaceDocker } = await import('../workspace-manager.js');\n const issueLower = issueId.toLowerCase();\n const workspacePath = findWorkspacePath(projectPath, issueLower);\n if (workspacePath) {\n const dockerResult = await Effect.runPromise(stopWorkspaceDocker(workspacePath, issueLower));\n if (dockerResult.containersFound) {\n console.log(`[merge-agent] ✓ Stopped Docker containers: ${dockerResult.steps.join('; ')}`);\n logActivity('docker_cleanup', `Stopped Docker for ${issueId}: ${dockerResult.steps.join('; ')}`);\n }\n }\n } catch (err) {\n console.warn(`[merge-agent] Docker cleanup failed (non-fatal): ${err}`);\n }\n\n await notifyTldrDaemon(projectPath, sourceBranch ?? '');\n await maybeSpawnPostMergeKnowledgeRetro(issueId, projectPath);\n\n // Mark completed BEFORE logging — prevents re-entry even if the log line triggers something\n _completedPostMerge.add(issueId);\n\n console.log(`[merge-agent] Post-merge handoff completed for ${issueId}. Awaiting close-out (verify on main).`);\n announceMerge('completed', issueId);\n logActivity('merge_complete', `Merged ${issueId}. Awaiting close-out (verify on main).`);\n })().finally(() => {\n _postMergeInFlight.delete(issueId);\n });\n _postMergeInFlight.set(issueId, run);\n return run;\n}\n\nasync function transitionIssueToVerifyingOnMain(issueId: string, projectPath: string): Promise<void> {\n const [effectModule, issueLifecycleModule, githubModule, linearModule, rallyModule, errorsModule] = await Promise.all([\n import('effect'),\n import('../../dashboard/server/services/issue-lifecycle.js'),\n import('../../dashboard/server/services/github-client.js'),\n import('../../dashboard/server/services/linear-client.js'),\n import('../../dashboard/server/services/rally-client.js'),\n import('../../dashboard/server/services/typed-errors.js'),\n ]);\n const { Effect, Layer } = effectModule;\n const { IssueLifecycle, IssueLifecycleLive } = issueLifecycleModule;\n const { GitHubClient } = githubModule;\n const { LinearClientOptionalLive } = linearModule;\n const { RallyClientOptionalLive } = rallyModule;\n const { IssueNotFound } = errorsModule;\n const gitHubRepo = (owner: string, repo: string) => shellQuote(`${owner}/${repo}`);\n const githubLayer = Layer.succeed(GitHubClient, {\n getIssue: (_owner: string, _repo: string, number: number) => Effect.fail(new IssueNotFound({ id: String(number) })),\n closeIssue: (owner: string, repo: string, number: number) => Effect.promise(() => execAsync(`gh issue close ${number} --repo ${gitHubRepo(owner, repo)}`, { cwd: projectPath, encoding: 'utf-8' }).then(() => undefined)),\n reopenIssue: (owner: string, repo: string, number: number) => Effect.promise(() => execAsync(`gh issue reopen ${number} --repo ${gitHubRepo(owner, repo)} 2>/dev/null || true`, { cwd: projectPath, encoding: 'utf-8' }).then(() => undefined)),\n addLabel: (owner: string, repo: string, number: number, label: string) => Effect.promise(async () => {\n if (label === 'verifying-on-main') {\n await execAsync(`gh label create ${shellQuote(label)} --repo ${gitHubRepo(owner, repo)} --color \"fbca04\" --description \"Merged — awaiting verification on main\" --force 2>/dev/null || true`, { cwd: projectPath, encoding: 'utf-8' });\n }\n await execAsync(`gh issue edit ${number} --repo ${gitHubRepo(owner, repo)} --add-label ${shellQuote(label)}`, { cwd: projectPath, encoding: 'utf-8' });\n }),\n removeLabel: (owner: string, repo: string, number: number, label: string) => Effect.promise(() => execAsync(`gh issue edit ${number} --repo ${gitHubRepo(owner, repo)} --remove-label ${shellQuote(label)} 2>/dev/null || true`, { cwd: projectPath, encoding: 'utf-8' }).then(() => undefined)),\n ensureLabel: (owner: string, repo: string, label: string, color = '0075ca', description = '') => Effect.promise(async () => {\n await execAsync(`gh label create ${shellQuote(label)} --repo ${gitHubRepo(owner, repo)} --color ${shellQuote(color)} --description ${shellQuote(description)} --force 2>/dev/null || true`, { cwd: projectPath, encoding: 'utf-8' });\n return { id: 0, name: label, color };\n }),\n addComment: () => Effect.void,\n getComments: () => Effect.succeed([]),\n });\n const layer = IssueLifecycleLive.pipe(\n Layer.provide(LinearClientOptionalLive),\n Layer.provide(githubLayer),\n Layer.provide(RallyClientOptionalLive),\n );\n await Effect.runPromise(\n Effect.gen(function* () {\n const lifecycle = yield* IssueLifecycle;\n yield* lifecycle.transitionTo(issueId, 'verifying_on_main');\n }).pipe(Effect.provide(layer)),\n );\n console.log(`[merge-agent] ✓ Transitioned ${issueId} to verifying_on_main`);\n}\n\nfunction isPostMergeRoleSession(sessionName: string, issueLower: string): boolean {\n if ([`agent-${issueLower}-test`, `agent-${issueLower}-ship`, `agent-${issueLower}-merge`].includes(sessionName)) {\n return true;\n }\n if (sessionName.startsWith(`agent-${issueLower}-review-`)) {\n return true;\n }\n return sessionName.startsWith('specialist-')\n && sessionName.includes(`-${issueLower}-`)\n && /-(review|test|merge|ship)(?:-|$)/.test(sessionName);\n}\n\nasync function killPostMergeRoleSessions(issueId: string): Promise<void> {\n try {\n const issueLower = issueId.toLowerCase();\n const sessions = await Effect.runPromise(listSessionNames());\n const targets = sessions.filter((session) => isPostMergeRoleSession(session, issueLower));\n for (const session of targets) {\n await Effect.runPromise(killSession(session));\n }\n if (targets.length > 0) {\n console.log(`[merge-agent] ✓ Killed ${targets.length} review/test/ship session(s) for ${issueId}`);\n logActivity('role_sessions_killed', `Killed ${targets.length} review/test/ship session(s) for ${issueId} on merge`);\n }\n } catch (err) {\n console.warn(`[merge-agent] Could not kill role sessions for ${issueId}: ${err}`);\n }\n}\n\nfunction isPostMergeKnowledgeRetroEnabled(): boolean {\n try {\n return loadConfigSync().config.knowledge?.postMergeAutoRetro === true;\n } catch (err) {\n console.warn(`[merge-agent] Could not read knowledge post-merge retro config: ${err}`);\n return false;\n }\n}\n\nasync function maybeSpawnPostMergeKnowledgeRetro(issueId: string, projectPath: string): Promise<void> {\n if (!isPostMergeKnowledgeRetroEnabled()) return;\n\n try {\n const child = spawn('pan', ['knowledge', issueId, '--retro'], {\n cwd: projectPath,\n detached: true,\n stdio: 'ignore',\n });\n child.once?.('error', (err) => {\n const message = err instanceof Error ? err.message : String(err);\n console.warn(`[merge-agent] Post-merge knowledge retro spawn failed for ${issueId}: ${message}`);\n logActivity('knowledge_retro_error', `Post-merge knowledge retro spawn failed for ${issueId}: ${message}`, issueId);\n });\n child.unref();\n console.log(`[merge-agent] Spawned post-merge knowledge retro for ${issueId} (pid ${child.pid})`);\n logActivity('knowledge_retro_spawned', `Spawned post-merge knowledge retro for ${issueId}`, issueId);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.warn(`[merge-agent] Post-merge knowledge retro spawn failed for ${issueId}: ${message}`);\n logActivity('knowledge_retro_error', `Post-merge knowledge retro spawn failed for ${issueId}: ${message}`, issueId);\n }\n}\n\n/**\n * Reset postMergeLifecycle completion tracking for an issue (used by reopen).\n */\nexport function resetPostMergeState(issueId: string): void {\n _completedPostMerge.delete(issueId);\n _postMergeInFlight.delete(issueId);\n}\n\n/**\n * Parse result markers from agent output\n */\nexport function parseAgentOutput(output: string): MergeResult {\n const lines = output.split('\\n');\n\n let mergeResult: 'SUCCESS' | 'FAILURE' | null = null;\n let resolvedFiles: string[] = [];\n let failedFiles: string[] = [];\n let testsStatus: 'PASS' | 'FAIL' | 'SKIP' | null = null;\n let validationStatus: 'PASS' | 'FAIL' | null = null;\n let reason = '';\n let notes = '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n // Match MERGE_RESULT\n if (trimmed.startsWith('MERGE_RESULT:')) {\n const value = trimmed.substring('MERGE_RESULT:'.length).trim();\n if (value === 'SUCCESS' || value === 'FAILURE') {\n mergeResult = value;\n }\n }\n\n // Match RESOLVED_FILES\n if (trimmed.startsWith('RESOLVED_FILES:')) {\n const value = trimmed.substring('RESOLVED_FILES:'.length).trim();\n resolvedFiles = value\n .split(',')\n .map((f) => f.trim())\n .filter((f) => f.length > 0);\n }\n\n // Match FAILED_FILES\n if (trimmed.startsWith('FAILED_FILES:')) {\n const value = trimmed.substring('FAILED_FILES:'.length).trim();\n failedFiles = value\n .split(',')\n .map((f) => f.trim())\n .filter((f) => f.length > 0);\n }\n\n // Match TESTS\n if (trimmed.startsWith('TESTS:')) {\n const value = trimmed.substring('TESTS:'.length).trim();\n if (value === 'PASS' || value === 'FAIL' || value === 'SKIP') {\n testsStatus = value;\n }\n }\n\n // Match VALIDATION\n if (trimmed.startsWith('VALIDATION:')) {\n const value = trimmed.substring('VALIDATION:'.length).trim();\n if (value === 'PASS' || value === 'FAIL') {\n validationStatus = value;\n }\n }\n\n // Match REASON\n if (trimmed.startsWith('REASON:')) {\n reason = trimmed.substring('REASON:'.length).trim();\n }\n\n // Match NOTES\n if (trimmed.startsWith('NOTES:')) {\n notes = trimmed.substring('NOTES:'.length).trim();\n }\n }\n\n // Build result\n if (mergeResult === 'SUCCESS') {\n return {\n success: true,\n resolvedFiles,\n testsStatus: testsStatus || 'SKIP',\n validationStatus: validationStatus || 'NOT_RUN',\n notes,\n output,\n };\n } else if (mergeResult === 'FAILURE') {\n return {\n success: false,\n failedFiles,\n validationStatus: validationStatus || 'NOT_RUN',\n reason,\n notes,\n output,\n };\n } else {\n // No structured result markers found - try to detect human-readable format\n // Agents sometimes output \"MERGE TASK COMPLETE\" instead of \"MERGE_RESULT: SUCCESS\"\n const lowerOutput = output.toLowerCase();\n\n // Check for success indicators\n const successIndicators = [\n 'merge task complete',\n 'successfully merged',\n 'merge complete',\n 'pushed merge commit',\n 'successfully merged and pushed',\n ];\n\n const failureIndicators = [\n 'merge failed',\n 'merge task failed',\n 'could not merge',\n 'conflict not resolved',\n ];\n\n const hasSuccessIndicator = successIndicators.some(i => lowerOutput.includes(i));\n const hasFailureIndicator = failureIndicators.some(i => lowerOutput.includes(i));\n\n if (hasSuccessIndicator && !hasFailureIndicator) {\n // Extract test status from output if mentioned\n let detectedTestStatus: 'PASS' | 'FAIL' | 'SKIP' = 'SKIP';\n if (lowerOutput.includes('tests: pass') || lowerOutput.includes('tests passed') ||\n output.match(/\\d+ passed/)) {\n detectedTestStatus = 'PASS';\n } else if (lowerOutput.includes('tests: fail') || lowerOutput.includes('tests failed')) {\n detectedTestStatus = 'FAIL';\n }\n\n console.log('[merge-agent] Detected success from human-readable output');\n return {\n success: true,\n testsStatus: detectedTestStatus,\n validationStatus: 'PASS',\n notes: 'Detected from human-readable output (agent did not use structured format)',\n output,\n };\n }\n\n if (hasFailureIndicator) {\n console.log('[merge-agent] Detected failure from human-readable output');\n return {\n success: false,\n validationStatus: 'NOT_RUN',\n reason: 'Detected merge failure from agent output',\n output,\n };\n }\n\n // Truly unrecognized output\n return {\n success: false,\n validationStatus: 'NOT_RUN',\n reason: 'Agent did not report result in expected format',\n output,\n };\n }\n}\n\n/**\n * Get conflict files from git status (async)\n */\nasync function getConflictFiles(projectPath: string): Promise<string[]> {\n try {\n const { stdout: status } = await execAsync('git diff --name-only --diff-filter=U', {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n\n return status\n .split('\\n')\n .map((line) => line.trim())\n .filter((line) => line.length > 0);\n } catch (error) {\n console.error('Failed to get conflict files:', error);\n return [];\n }\n}\n\nasync function resolveMainPreferredSyncConflicts(\n projectPath: string,\n conflictFiles: string[],\n): Promise<{ success: boolean; reason?: string }> {\n if (conflictFiles.length === 0 || !conflictFiles.every(isSyncMainMainPreferredPath)) {\n return { success: false, reason: 'conflicts include non-pipeline-owned files' };\n }\n\n try {\n for (const path of SYNC_MAIN_MAIN_PREFERRED_PATHS) {\n await execAsync(`git rm -r --quiet --ignore-unmatch -- ${path}`, {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n await execAsync(`git checkout origin/main -- ${path}`, {\n cwd: projectPath,\n encoding: 'utf-8',\n }).catch(() => {\n // The path may not exist on origin/main. In that case, the preceding\n // git rm records main's deletion for this pipeline-owned path.\n });\n await execAsync(`git add -A -- ${path}`, {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n }\n\n const remainingConflicts = await getConflictFiles(projectPath);\n if (remainingConflicts.length > 0) {\n return {\n success: false,\n reason: `Unresolved conflicts remain: ${remainingConflicts.join(', ')}`,\n };\n }\n\n await execAsync('git commit --no-edit', {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n return { success: true };\n } catch (error: any) {\n return {\n success: false,\n reason: `Failed to auto-resolve pipeline-owned conflicts: ${error.message}`,\n };\n }\n}\n\n/**\n * Log merge to history\n */\nexport function logMergeHistory(context: MergeConflictContext, result: MergeResult, sessionId?: string): void {\n // Ensure history directory exists\n if (!existsSync(MERGE_HISTORY_DIR)) {\n mkdirSync(MERGE_HISTORY_DIR, { recursive: true });\n }\n\n const entry: MergeHistoryEntry = {\n timestamp: new Date().toISOString(),\n issueId: context.issueId,\n sourceBranch: context.sourceBranch,\n targetBranch: context.targetBranch,\n conflictFiles: context.conflictFiles,\n result: {\n ...result,\n output: undefined, // Don't store full output in history\n },\n sessionId,\n };\n\n appendFileSync(MERGE_HISTORY_FILE, JSON.stringify(entry) + '\\n', 'utf-8');\n}\n\n/**\n * Log activity to the dashboard activity log (event-sourced via emitActivityEntry)\n */\nfunction logActivity(action: string, details: string, issueId?: string): void {\n emitActivityEntrySync({\n source: 'ship',\n level: action.includes('fail') || action.includes('error') ? 'error' : action.includes('warn') ? 'warn' : 'success',\n message: details,\n issueId,\n });\n}\n\n/**\n * Voice-worthy merge milestone announcement. Messages start with one of three\n * distinctive prefixes (\"Merge started\", \"Merge completed\", \"Merge failed\") so\n * pan-tts can filter merge-agent chatter and speak only these three events.\n *\n * Do not change these prefixes without updating the pan-tts filter\n * (~/Projects/pan-tts/src/pan_tts/__main__.py — ALLOWED_MERGE_PREFIXES).\n */\nfunction announceMerge(\n status: 'started' | 'completed' | 'failed',\n issueId: string,\n extra?: string,\n): void {\n const prefix = status === 'started'\n ? 'Merge started'\n : status === 'completed'\n ? 'Merge completed'\n : 'Merge failed';\n const tail = extra ? `. ${extra}` : '';\n emitActivityEntrySync({\n source: 'ship',\n level: status === 'failed' ? 'error' : 'success',\n message: `${prefix} for ${issueId}${tail}`,\n issueId,\n });\n // Upleveled TTS utterance — short, speakable, no issue prefix noise\n const ttsUtterance = status === 'started'\n ? `Starting merge for ${issueId}`\n : status === 'completed'\n ? `${issueId} merged to main`\n : `Merge failed for ${issueId}`;\n emitActivityTtsSync({\n utterance: ttsUtterance,\n priority: status === 'failed' ? 0 : 1,\n issueId,\n source: 'merge-agent',\n eventType: `mergeStatus.${status === 'completed' ? 'merged' : status === 'started' ? 'merging' : 'failed'}`,\n });\n}\n\n/**\n * Capture tmux output and look for result markers (async)\n */\nexport async function captureTmuxOutput(sessionName: string): Promise<string> {\n try {\n return await Effect.runPromise(capturePane(sessionName));\n } catch {\n return '';\n }\n}\n\n/** Patterns to match in tmux capture-pane output (git push/fetch lines) */\nexport const GIT_PATTERNS: Array<{ re: RegExp; operation: GitOperationType; level: 'info' | 'warn' | 'error' }> = [\n { re: /force-with-lease/i, operation: 'force_push_cmd', level: 'warn' },\n { re: /git push/i, operation: 'push_attempt', level: 'info' },\n { re: /git fetch/i, operation: 'fetch_attempt', level: 'info' },\n { re: /\\[rejected\\]/i, operation: 'push_rejected', level: 'error' },\n { re: /non-fast-forward/i, operation: 'non_ff', level: 'error' },\n { re: /retrying/i, operation: 'retry', level: 'warn' },\n { re: /\\[remote rejected\\]/i, operation: 'remote_rejected', level: 'error' },\n { re: /Everything up-to-date/i, operation: 'push_noop', level: 'info' },\n];\n\n/**\n * Scan tmux capture-pane output for git push/fetch patterns and emit each\n * as a git_operations row. Uses seenLineHashes to dedupe within a session.\n */\nexport function scanGitPatterns(\n output: string,\n seenLineHashes: Set<string>,\n issueId: string,\n branch?: string,\n): void {\n const lines = output.split('\\n');\n const ts = new Date().toISOString();\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n // Simple hash: first 120 chars (avoids hashing megabytes)\n const hash = trimmed.slice(0, 120);\n if (seenLineHashes.has(hash)) continue;\n\n for (const { re, operation, level } of GIT_PATTERNS) {\n if (re.test(trimmed)) {\n seenLineHashes.add(hash);\n appendGitOperationSync({\n operation,\n branch,\n issueId,\n status: level === 'error' ? 'failure' : 'success',\n error: level !== 'info' ? trimmed.slice(0, 200) : undefined,\n ts,\n });\n emitActivityEntrySync({\n source: 'ship',\n level,\n message: `[git] ${trimmed.slice(0, 100)}`,\n issueId,\n });\n break; // only match one pattern per line\n }\n }\n }\n}\n\n/**\n * Check if specialist-merge-agent tmux session is running (async)\n */\nexport async function isMergeAgentRunning(): Promise<boolean> {\n return Effect.runPromise(sessionExists('specialist-merge-agent'));\n}\n\n/**\n * Send a message to an agent's tmux session (async)\n */\nexport async function sendMessageToAgent(issueId: string, message: string): Promise<boolean> {\n // Agent sessions are typically named agent-{issueId} (lowercase)\n const sessionName = `agent-${issueId.toLowerCase()}`;\n\n try {\n // Check if session exists\n if (!await Effect.runPromise(sessionExists(sessionName))) {\n console.log(`[merge-agent] Could not send message to ${sessionName} (session does not exist)`);\n return false;\n }\n\n // Send the message using centralized sendKeys\n await Effect.runPromise(sendKeys(sessionName, message));\n\n console.log(`[merge-agent] Sent message to ${sessionName}`);\n logActivity('agent_message', `Sent to ${sessionName}: ${message.slice(0, 100)}...`);\n return true;\n } catch {\n console.log(`[merge-agent] Could not send message to ${sessionName} (session may not exist)`);\n return false;\n }\n}\n\n// PAN-1531: ship-role machinery (buildShipPreparationPrompt, buildShipSyncMainPrompt,\n// spawnShipRoleForTask, spawnMergeAgentForBranches, spawnRebaseAgentForBranch,\n// defaultWorkspaceForIssue) removed. Rebase is now performed in-process via\n// rebaseFeatureBranch() in src/lib/cloister/merge-rebase.ts. See docs/MERGE-WORKFLOW.md.\n\nexport async function salvageStrandedMerge(\n projectPath: string,\n targetBranch: string,\n headBefore: string,\n issueId: string,\n logActivity: (action: string, detail: string) => void,\n): Promise<{ success: boolean; reason?: string } | null> {\n try {\n const { stdout: currentHeadRaw } = await execAsync('git rev-parse HEAD', {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n const currentHead = currentHeadRaw.trim();\n\n if (currentHead === headBefore) {\n // No local merge happened — nothing to salvage\n return null;\n }\n\n // Local HEAD changed — check if it's ahead of remote\n await execAsync(`git fetch origin ${targetBranch}`, {\n cwd: projectPath,\n encoding: 'utf-8',\n timeout: 10000,\n }).catch(() => {});\n\n const { stdout: remoteHeadRaw } = await execAsync(`git rev-parse origin/${targetBranch}`, {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n\n if (remoteHeadRaw.trim() === currentHead) {\n // Already pushed (maybe by another process)\n console.log(`[merge-agent] Salvage check: merge already pushed`);\n return { success: true };\n }\n\n // Stranded merge detected — push it (with divergence guard to protect hotfixes)\n console.log(`[merge-agent] SALVAGING stranded merge for ${issueId}: local HEAD ${currentHead.slice(0, 8)} != remote ${remoteHeadRaw.trim().slice(0, 8)}`);\n logActivity('merge_salvage', `Pushing stranded merge commit ${currentHead.slice(0, 8)} for ${issueId}`);\n\n try {\n await Effect.runPromise(gitPush(projectPath, 'origin', targetBranch, { issueId }));\n } catch (pushErr: unknown) {\n if (pushErr instanceof MainDivergedError) {\n // origin has advanced past our local ancestor — a hotfix landed.\n // Mark stuck so Deacon won't re-trigger, then let the caller handle it.\n markWorkspaceStuck(issueId, 'main_diverged', {\n localSha: pushErr.localSha,\n remoteSha: pushErr.remoteSha,\n });\n logActivity('merge_salvage_diverged', `Salvage aborted: origin/${targetBranch} diverged (remote ${pushErr.remoteSha.slice(0, 7)} not ancestor of local ${pushErr.localSha.slice(0, 7)})`);\n return { success: false, reason: pushErr.message };\n }\n throw pushErr;\n }\n\n console.log(`[merge-agent] Salvage push successful for ${issueId}`);\n logActivity('merge_salvage_success', `Stranded merge pushed successfully`);\n return { success: true };\n } catch (error: any) {\n console.error(`[merge-agent] Salvage failed: ${error.message}`);\n logActivity('merge_salvage_failed', `Salvage push failed: ${error.message}`);\n return null;\n }\n}\n\n/**\n * Result of syncing main into a workspace branch\n */\nexport interface SyncMainResult {\n success: boolean;\n alreadyUpToDate?: boolean;\n commitCount?: number;\n changedFiles?: string[];\n conflictFiles?: string[];\n reason?: string;\n}\n\n/**\n * Scan workspace for leftover git conflict markers (async)\n */\nexport async function scanForConflictMarkers(projectPath: string): Promise<string[]> {\n try {\n // git diff --check exits non-zero and prints filenames when conflict markers exist\n const { stdout } = await execAsync('git diff --check 2>&1 || true', {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n const files = stdout\n .split('\\n')\n .filter(line => line.includes('leftover conflict marker'))\n .map(line => line.split(':')[0].trim())\n .filter(f => f.length > 0);\n return [...new Set(files)];\n } catch {\n return [];\n }\n}\n\n/**\n * Sync the latest main branch into a workspace's feature branch.\n *\n * This performs a `git merge origin/main` in the workspace. If the merge is clean\n * it returns immediately. If conflicts arise, the conflict details are surfaced\n * for manual workspace resolution. The merge is never pushed — this is a local\n * workspace operation.\n *\n * Auto-commits any uncommitted changes before merging (with safety verification).\n */\nexport async function syncMainIntoWorkspace(\n projectPath: string,\n issueId: string,\n): Promise<SyncMainResult> {\n console.log(`[sync-main] Starting sync of main into workspace for ${issueId}`);\n logActivity('sync_main_start', `Starting sync for ${issueId}`);\n\n // PAN-1158 safety net: a workspace bd dolt DB that briefly went empty can\n // leave `.beads/issues.jsonl` reported as deleted by `git status`. The\n // auto-commit below would then propagate that deletion onto the feature\n // branch. Restore the tracked export first so the auto-commit only sees\n // intentional changes.\n await Effect.runPromise(restoreTrackedBeadsExport(projectPath));\n\n // Pre-flight: auto-commit uncommitted changes before merge\n console.log(`[sync-main] Checking for uncommitted changes...`);\n logActivity('sync_main_auto_commit', `Auto-committing uncommitted changes before sync`);\n const autoCommit = await autoCommitWorkspaceChangesBeforeSync(projectPath);\n if (!autoCommit.success) {\n const message = autoCommit.reason || 'Failed to auto-commit uncommitted changes';\n console.error(`[sync-main] ${message}`);\n logActivity('sync_main_blocked', message);\n return { success: false, reason: message };\n }\n if (autoCommit.committed) {\n console.log(`[sync-main] Auto-commit successful`);\n }\n\n // Verify no non-excluded uncommitted changes remain.\n try {\n const { stdout: postCommitStatus } = await execAsync('git status --porcelain', {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n if (postCommitStatus.trim()) {\n const remainingNonExcluded = postCommitStatus\n .trim()\n .split('\\n')\n .filter((line) => !isAutoCommitExcludedPath(parseStatusPath(line)));\n if (remainingNonExcluded.length > 0) {\n const message = 'Uncommitted changes remain after auto-commit — aborting sync';\n console.error(`[sync-main] ${message}`);\n logActivity('sync_main_blocked', message);\n return { success: false, reason: message };\n }\n }\n } catch (error: any) {\n return { success: false, reason: `Failed to check git status: ${error.message}` };\n }\n\n // Clean up stale git lock files\n try {\n const lockCleanup = await Effect.runPromise(cleanupStaleLocks(projectPath));\n if (lockCleanup.found.length > 0) {\n console.log(`[sync-main] Found ${lockCleanup.found.length} lock file(s)`);\n if (lockCleanup.removed.length > 0) {\n console.log(`[sync-main] Cleaned up ${lockCleanup.removed.length} stale lock file(s)`);\n logActivity('git_lock_cleanup', `Removed ${lockCleanup.removed.length} stale lock file(s)`);\n }\n if (lockCleanup.errors.some((e: { file: string; error: string }) => e.error.includes('Git processes are running'))) {\n const message = 'Git processes are still running — cannot safely start sync';\n console.error(`[sync-main] ${message}`);\n logActivity('sync_main_blocked', message);\n return { success: false, reason: message };\n }\n }\n } catch (lockErr: any) {\n console.warn(`[sync-main] Lock cleanup warning: ${lockErr.message} (continuing)`);\n }\n\n // Fetch latest main\n try {\n console.log(`[sync-main] Fetching origin/main...`);\n await execAsync('git fetch origin main', { cwd: projectPath, encoding: 'utf-8' });\n } catch (error: any) {\n return { success: false, reason: `Failed to fetch origin/main: ${error.message}` };\n }\n\n // Attempt the merge\n let mergeOutput = '';\n let hasConflicts = false;\n try {\n const result = await execAsync('git merge origin/main', { cwd: projectPath, encoding: 'utf-8' });\n mergeOutput = (result.stdout || '') + (result.stderr || '');\n } catch (error: any) {\n mergeOutput = (error.stdout || '') + (error.stderr || '');\n hasConflicts = true;\n }\n\n // Already up to date?\n if (mergeOutput.includes('Already up to date') || mergeOutput.includes('Already up-to-date')) {\n console.log(`[sync-main] Already up to date`);\n logActivity('sync_main_noop', `${issueId} already up to date with main`);\n return { success: true, alreadyUpToDate: true };\n }\n\n if (!hasConflicts) {\n // Clean merge — collect stats\n console.log(`[sync-main] Clean merge completed`);\n logActivity('sync_main_success', `Clean merge of main into ${issueId}`);\n\n let changedFiles: string[] = [];\n let commitCount = 0;\n try {\n const { stdout: diffFiles } = await execAsync(\n 'git diff --name-only ORIG_HEAD HEAD 2>/dev/null || git diff --name-only HEAD~1 HEAD',\n { cwd: projectPath, encoding: 'utf-8' },\n );\n changedFiles = diffFiles.trim().split('\\n').filter(f => f.length > 0);\n } catch { /* non-fatal */ }\n try {\n const { stdout: logOut } = await execAsync(\n 'git log ORIG_HEAD..HEAD --oneline 2>/dev/null || echo \"\"',\n { cwd: projectPath, encoding: 'utf-8' },\n );\n commitCount = logOut.trim().split('\\n').filter(l => l.length > 0).length;\n } catch { /* non-fatal */ }\n\n return { success: true, commitCount, changedFiles };\n }\n\n // PAN-1531: sync-main conflict case — surface to operator instead of\n // delegating to an LLM ship role. Abort the merge so the working tree is\n // clean, then return the conflict files for the caller to display.\n const conflictFiles = await getConflictFiles(projectPath);\n const mainPreferredResolution = await resolveMainPreferredSyncConflicts(projectPath, conflictFiles);\n if (mainPreferredResolution.success) {\n console.log(`[sync-main] Auto-resolved ${conflictFiles.length} pipeline-owned conflict(s) with origin/main`);\n logActivity('sync_main_auto_resolved_conflicts', `Auto-resolved ${conflictFiles.length} pipeline-owned conflict(s) in ${issueId} with origin/main`);\n\n let changedFiles: string[] = [];\n let commitCount = 0;\n try {\n const { stdout: diffFiles } = await execAsync(\n 'git diff --name-only ORIG_HEAD HEAD 2>/dev/null || git diff --name-only HEAD~1 HEAD',\n { cwd: projectPath, encoding: 'utf-8' },\n );\n changedFiles = diffFiles.trim().split('\\n').filter(f => f.length > 0);\n } catch { /* non-fatal */ }\n try {\n const { stdout: logOut } = await execAsync(\n 'git log ORIG_HEAD..HEAD --oneline 2>/dev/null || echo \"\"',\n { cwd: projectPath, encoding: 'utf-8' },\n );\n commitCount = logOut.trim().split('\\n').filter(l => l.length > 0).length;\n } catch { /* non-fatal */ }\n\n return { success: true, commitCount, changedFiles };\n }\n\n console.log(`[sync-main] ${conflictFiles.length} conflict(s); aborting merge for manual resolution`);\n logActivity('sync_main_conflicts', `${conflictFiles.length} conflict(s) in ${issueId}: ${conflictFiles.join(', ')}`);\n\n try { await execAsync('git merge --abort', { cwd: projectPath, encoding: 'utf-8' }); } catch { /* non-fatal */ }\n\n return {\n success: false,\n conflictFiles,\n reason: `Sync-main produced ${conflictFiles.length} conflict(s) in ${issueId}: ${conflictFiles.join(', ')}. Resolve manually in the workspace, then re-run sync-main.`,\n };\n\n}\n\n/**\n * Look up and run quality gates for the project at projectPath.\n * Returns empty array if no quality gates are configured.\n *\n * In polyrepo mode (projectPath is a sub-repo of project.path), only gates\n * whose `path` field matches the relative sub-repo path are run. Gates with\n * no `path` field are skipped in polyrepo context.\n */\nexport async function runProjectQualityGates(\n projectPath: string,\n phase: 'pre_push' | 'post_push'\n): Promise<import('./validation.js').QualityGateResult[]> {\n try {\n const config = loadProjectsConfigSync();\n // Find the project whose path matches\n const project = Object.values(config.projects).find(p => projectPath.startsWith(p.path));\n if (!project?.quality_gates || Object.keys(project.quality_gates).length === 0) {\n console.log(`[merge-agent] No quality gates configured for ${projectPath}`);\n return [];\n }\n\n // Detect polyrepo context: if projectPath is a subdirectory of project.path,\n // repoRelPath is non-empty (e.g., 'frontend' or 'backend').\n const repoRelPath = relative(project.path, projectPath);\n const matchedRepo = project.workspace?.repos?.find(repo => (\n repoRelPath === repo.path ||\n projectPath === join(project.path, repo.path)\n ));\n const repoIdentifiers = matchedRepo\n ? new Set([matchedRepo.path, matchedRepo.name])\n : new Set(repoRelPath && !repoRelPath.startsWith('..') ? [repoRelPath] : []);\n\n let gatesToRun = project.quality_gates;\n if (repoRelPath && !repoRelPath.startsWith('..')) {\n // Polyrepo: gates can target either the repo path (\"frontend\") or the\n // configured repo key/alias (\"fe\"). Both map to the same sub-repo.\n const filtered = Object.entries(project.quality_gates).filter(\n ([, gate]) => gate.path && repoIdentifiers.has(gate.path)\n );\n if (filtered.length === 0) {\n console.log(`[merge-agent] No quality gates configured for repo path \"${repoRelPath}\"`);\n return [];\n }\n gatesToRun = Object.fromEntries(filtered);\n console.log(\n `[merge-agent] Polyrepo: running ${Object.keys(gatesToRun).length} gate(s) for path \"${repoRelPath}\" (${[...repoIdentifiers].join(', ')})`\n );\n }\n\n console.log(`[merge-agent] Running ${phase} quality gates for project \"${project.name}\"`);\n return await Effect.runPromise(runQualityGates(gatesToRun, projectPath, phase));\n } catch (error: any) {\n console.error(`[merge-agent] Failed to load quality gates: ${error.message}`);\n return [];\n }\n}\n"],"mappings":"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"}
|
|
@@ -62,9 +62,9 @@ async function reconcileStaleSiblings(mergedIssueId, deps) {
|
|
|
62
62
|
*/
|
|
63
63
|
async function runMergeTrainReconcile(mergedIssueId, opts = {}) {
|
|
64
64
|
if (!(opts.enabled ?? isMergeTrainEnabled)()) return [];
|
|
65
|
-
return reconcileStaleSiblings(mergedIssueId, opts.deps ?? (await import("./merge-train-deps-
|
|
65
|
+
return reconcileStaleSiblings(mergedIssueId, opts.deps ?? (await import("./merge-train-deps-Be5ElC_P.js")).buildRealReconcileDeps());
|
|
66
66
|
}
|
|
67
67
|
//#endregion
|
|
68
68
|
export { runMergeTrainReconcile };
|
|
69
69
|
|
|
70
|
-
//# sourceMappingURL=merge-train-
|
|
70
|
+
//# sourceMappingURL=merge-train-D3qzRccL.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge-train-
|
|
1
|
+
{"version":3,"file":"merge-train-D3qzRccL.js","names":[],"sources":["../../src/lib/cloister/merge-train-reconciler.ts","../../src/lib/cloister/merge-train.ts"],"sourcesContent":["/**\n * Merge-train reconciler (PAN-1691).\n *\n * When a feature merges to main, every *other* ready-for-merge branch is now\n * behind main. Left alone they go stale / CONFLICTING and strand — the cause of\n * PAN-1240 / PAN-1215 / PAN-1213 / PAN-1658. This reconciler is the automatic\n * janitor that runs right after a merge lands: rebase each ready sibling onto\n * the new main, re-verify the clean ones, and hand the genuinely-conflicting\n * ones to a resolving *agent* (never a human — the agent must understand what\n * BOTH features intended across BOTH changesets).\n *\n * The orchestration here is pure and dependency-injected so it is unit-testable\n * without touching git, the DB, or spawning agents. The real wiring + the\n * default-off `merge_train` flag live at the call site (the post-merge path).\n */\n\nexport type SiblingOutcome =\n /** Branch was already up to date with main — nothing to do. */\n | { issueId: string; result: 'unaffected' }\n /** Clean rebase onto the new main — re-verification was dispatched. */\n | { issueId: string; result: 'rebased' }\n /** Genuine conflict — a resolver agent was dispatched. */\n | { issueId: string; result: 'conflict' }\n /** Something failed for this sibling; the rest still proceed. */\n | { issueId: string; result: 'error'; error: string };\n\nexport type RebaseStatus = 'up-to-date' | 'clean' | 'conflict';\n\nexport interface ReconcileDeps {\n /** Ready-for-merge sibling issue IDs (excluding the one that just merged). */\n getReadySiblings: (mergedIssueId: string) => Promise<string[]> | string[];\n /** Rebase this sibling's feature branch onto the updated main. */\n rebaseSibling: (issueId: string) => Promise<{ status: RebaseStatus }>;\n /** Re-run review/test verification on a cleanly-rebased sibling. */\n reDispatchVerification: (issueId: string) => Promise<void> | void;\n /**\n * Dispatch an agent to resolve a rebase conflict. The agent must load and\n * reconcile what BOTH features intended — `mergedIssueId` is the feature that\n * just landed and conflicts with `issueId`.\n */\n dispatchConflictResolver: (issueId: string, mergedIssueId: string) => Promise<void> | void;\n log?: (msg: string) => void;\n}\n\n/**\n * Reconcile every ready sibling after `mergedIssueId` landed on main. Processes\n * siblings sequentially (one git rebase at a time, no contention) and never\n * throws — a failure on one sibling becomes an `error` outcome and the rest\n * proceed.\n */\nexport async function reconcileStaleSiblings(\n mergedIssueId: string,\n deps: ReconcileDeps,\n): Promise<SiblingOutcome[]> {\n const log = deps.log ?? (() => {});\n const siblings = await deps.getReadySiblings(mergedIssueId);\n const outcomes: SiblingOutcome[] = [];\n\n for (const issueId of siblings) {\n try {\n const { status } = await deps.rebaseSibling(issueId);\n if (status === 'up-to-date') {\n outcomes.push({ issueId, result: 'unaffected' });\n } else if (status === 'clean') {\n await deps.reDispatchVerification(issueId);\n log(`[merge-train] ${issueId} rebased clean onto main(+${mergedIssueId}); re-verifying`);\n outcomes.push({ issueId, result: 'rebased' });\n } else {\n await deps.dispatchConflictResolver(issueId, mergedIssueId);\n log(`[merge-train] ${issueId} conflicts with ${mergedIssueId}; dispatched resolver agent`);\n outcomes.push({ issueId, result: 'conflict' });\n }\n } catch (err) {\n const error = err instanceof Error ? err.message : String(err);\n log(`[merge-train] ${issueId} reconcile failed: ${error}`);\n outcomes.push({ issueId, result: 'error', error });\n }\n }\n\n return outcomes;\n}\n","/**\n * Merge-train entry point (PAN-1691).\n *\n * Called from the post-merge path after a feature lands on main. Gated by the\n * default-off `flywheel.merge_train_enabled` flag — when off this is a no-op, so\n * nothing git-mutating runs until an operator deliberately enables it.\n *\n * The real git/spawn deps are lazy-loaded only when the flag is on, so importing\n * this module (and unit-testing the gating) stays light and never pulls in the\n * agent-spawn machinery.\n */\nimport { isMergeTrainEnabled } from '../overdeck/merge-sync.js';\nimport { reconcileStaleSiblings, type ReconcileDeps, type SiblingOutcome } from './merge-train-reconciler.js';\n\nexport interface RunMergeTrainOptions {\n /** Override the flag check (tests). Defaults to the global merge-train flag. */\n enabled?: () => boolean;\n /** Inject reconcile deps (tests). Defaults to the real git/spawn wiring. */\n deps?: ReconcileDeps;\n}\n\n/**\n * Run the merge-train reconciler for the issue that just merged. No-op (returns\n * `[]`) when the merge-train flag is off — the safe default.\n */\nexport async function runMergeTrainReconcile(\n mergedIssueId: string,\n opts: RunMergeTrainOptions = {},\n): Promise<SiblingOutcome[]> {\n const enabled = (opts.enabled ?? isMergeTrainEnabled)();\n if (!enabled) return [];\n\n const deps = opts.deps ?? (await import('./merge-train-deps.js')).buildRealReconcileDeps();\n return reconcileStaleSiblings(mergedIssueId, deps);\n}\n"],"mappings":";;;;;;;;;AAkDA,eAAA,uBAAA,eAAA,MAAA;;;;AAQE,MAAA,MAAA,WAAA,SAAA,KAAA;;AAGI,MAAA,WAAA,aAAA,UAAA,KAAA;;;;;AAGE,SAAA,KAAA,uBAAA,QAAA;AACA,OAAA,iBAAA,QAAA,4BAAA,cAAA,iBAAA;AACA,YAAA,KAAA;;;;;AAEA,SAAA,KAAA,yBAAA,SAAA,cAAA;AACA,OAAA,iBAAA,QAAA,kBAAA,cAAA,6BAAA;AACA,YAAA,KAAA;;;;;;;AAIF,MAAA,iBAAA,QAAA,qBAAA,QAAA;AACA,WAAA,KAAA;;;;;;AAIJ,QAAA;;;;;;;;;;;;;;;;;;;ACtDF,eAAsB,uBACpB,eACA,OAA6B,EAAE,EACJ;AAE3B,KAAI,EADa,KAAK,WAAW,sBACrB,CAAE,QAAO,EAAE;AAGvB,QAAO,uBAAuB,eADjB,KAAK,SAAS,MAAM,OAAO,mCAA0B,wBAAwB,CACxC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Gn as resolveProjectFromIssueSync, Nn as init_projects } from "./config-yaml-DPAieL_G.js";
|
|
2
|
-
import { W as spawnRun, t as init_agents } from "./agents-
|
|
2
|
+
import { W as spawnRun, t as init_agents } from "./agents-C7V4odQ6.js";
|
|
3
3
|
import { c as init_review_status_sync, r as getAllReviewStatusesFromDb } from "./review-status-sync-BWHnrq-k.js";
|
|
4
4
|
import { join } from "path";
|
|
5
5
|
import { exec } from "child_process";
|
|
@@ -66,4 +66,4 @@ function buildRealReconcileDeps() {
|
|
|
66
66
|
//#endregion
|
|
67
67
|
export { buildRealReconcileDeps };
|
|
68
68
|
|
|
69
|
-
//# sourceMappingURL=merge-train-deps-
|
|
69
|
+
//# sourceMappingURL=merge-train-deps-Be5ElC_P.js.map
|
package/dist/dashboard/{merge-train-deps-DBHsOWt9.js.map → merge-train-deps-Be5ElC_P.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge-train-deps-
|
|
1
|
+
{"version":3,"file":"merge-train-deps-Be5ElC_P.js","names":[],"sources":["../../src/lib/cloister/merge-train-deps.ts"],"sourcesContent":["/**\n * Real wiring for the merge-train reconciler (PAN-1691). Lazy-loaded from\n * merge-train.ts only when the `flywheel.merge_train_enabled` flag is on, so it\n * is never imported during normal operation or the gating unit tests.\n *\n * This is pure I/O — git rebase/force-push and agent spawn — and is exercised\n * only with the flag on against a real merge, so it is not unit-tested. The\n * decision logic it feeds (`reconcileStaleSiblings`) and the flag gating\n * (`runMergeTrainReconcile`) are.\n */\nimport { join } from 'path';\nimport { promisify } from 'util';\nimport { exec } from 'child_process';\nimport { getAllReviewStatusesFromDb } from '../overdeck/review-status-sync.js';\nimport { resolveProjectFromIssueSync } from '../projects.js';\nimport { spawnRun } from '../agents.js';\nimport type { ReconcileDeps, RebaseStatus } from './merge-train-reconciler.js';\n\nconst execAsync = promisify(exec);\n\nfunction workspacePathFor(issueId: string): string | null {\n const project = resolveProjectFromIssueSync(issueId);\n if (!project) return null;\n return join(project.projectPath, 'workspaces', `feature-${issueId.toLowerCase()}`);\n}\n\nexport function buildRealReconcileDeps(): ReconcileDeps {\n return {\n getReadySiblings: (mergedIssueId) => {\n const merged = mergedIssueId.toUpperCase();\n return Object.values(getAllReviewStatusesFromDb())\n .filter(\n (rs) =>\n rs.readyForMerge === true &&\n rs.mergeStatus !== 'merged' &&\n rs.issueId.toUpperCase() !== merged,\n )\n .map((rs) => rs.issueId);\n },\n\n rebaseSibling: async (issueId): Promise<{ status: RebaseStatus }> => {\n const ws = workspacePathFor(issueId);\n if (!ws) return { status: 'up-to-date' };\n const run = (cmd: string) => execAsync(cmd, { cwd: ws });\n\n await run('git fetch origin main');\n const { stdout } = await run('git rev-list --count HEAD..origin/main');\n if (stdout.trim() === '0') return { status: 'up-to-date' };\n\n try {\n await run('git rebase origin/main');\n await run('git push --force-with-lease');\n return { status: 'clean' };\n } catch {\n await run('git rebase --abort').catch(() => {});\n return { status: 'conflict' };\n }\n },\n\n reDispatchVerification: async (issueId) => {\n // The branch HEAD moved after the rebase — re-run review on the new HEAD.\n await spawnRun(issueId, 'review', {\n prompt:\n 'Your branch was automatically rebased onto the latest main after another feature merged. ' +\n 'Re-review on the new HEAD and confirm the issue is still correct and ready to merge.',\n });\n },\n\n dispatchConflictResolver: async (issueId, mergedIssueId) => {\n await spawnRun(issueId, 'work', {\n prompt: [\n `Your feature branch could not be cleanly rebased onto the latest main because ${mergedIssueId} just merged and conflicts with your changes.`,\n '',\n `Resolve the rebase conflict WITHOUT degrading either feature:`,\n `1. Read what ${mergedIssueId} changed (git log/diff on origin/main) AND what your issue (${issueId}) intended.`,\n `2. Rebase your branch onto origin/main and resolve each conflict so BOTH features' intent is preserved.`,\n `3. Build, run tests, commit, and push, then re-request review.`,\n '',\n `Do not blindly accept one side of a conflict — understand both changesets first.`,\n ].join('\\n'),\n });\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;yBAa+E;eAClB;aACrB;AAGxC,MAAM,YAAY,UAAU,KAAK;AAEjC,SAAS,iBAAiB,SAAgC;CACxD,MAAM,UAAU,4BAA4B,QAAQ;AACpD,KAAI,CAAC,QAAS,QAAO;AACrB,QAAO,KAAK,QAAQ,aAAa,cAAc,WAAW,QAAQ,aAAa,GAAG;;AAGpF,SAAgB,yBAAwC;AACtD,QAAO;EACL,mBAAmB,kBAAkB;GACnC,MAAM,SAAS,cAAc,aAAa;AAC1C,UAAO,OAAO,OAAO,4BAA4B,CAAC,CAC/C,QACE,OACC,GAAG,kBAAkB,QACrB,GAAG,gBAAgB,YACnB,GAAG,QAAQ,aAAa,KAAK,OAChC,CACA,KAAK,OAAO,GAAG,QAAQ;;EAG5B,eAAe,OAAO,YAA+C;GACnE,MAAM,KAAK,iBAAiB,QAAQ;AACpC,OAAI,CAAC,GAAI,QAAO,EAAE,QAAQ,cAAc;GACxC,MAAM,OAAO,QAAgB,UAAU,KAAK,EAAE,KAAK,IAAI,CAAC;AAExD,SAAM,IAAI,wBAAwB;GAClC,MAAM,EAAE,WAAW,MAAM,IAAI,yCAAyC;AACtE,OAAI,OAAO,MAAM,KAAK,IAAK,QAAO,EAAE,QAAQ,cAAc;AAE1D,OAAI;AACF,UAAM,IAAI,yBAAyB;AACnC,UAAM,IAAI,8BAA8B;AACxC,WAAO,EAAE,QAAQ,SAAS;WACpB;AACN,UAAM,IAAI,qBAAqB,CAAC,YAAY,GAAG;AAC/C,WAAO,EAAE,QAAQ,YAAY;;;EAIjC,wBAAwB,OAAO,YAAY;AAEzC,SAAM,SAAS,SAAS,UAAU,EAChC,QACE,iLAEH,CAAC;;EAGJ,0BAA0B,OAAO,SAAS,kBAAkB;AAC1D,SAAM,SAAS,SAAS,QAAQ,EAC9B,QAAQ;IACN,iFAAiF,cAAc;IAC/F;IACA;IACA,gBAAgB,cAAc,8DAA8D,QAAQ;IACpG;IACA;IACA;IACA;IACD,CAAC,KAAK,KAAK,EACb,CAAC;;EAEL"}
|