@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.
Files changed (219) hide show
  1. package/dist/{agent-state-KVbJEG5f.js → agent-state-RLhS7ckO.js} +1 -1
  2. package/dist/{agents-pp8FO7Uj.js → agents-By3u_fQ8.js} +1 -1
  3. package/dist/{agents-CQHK0CSA.js → agents-CgVr77Qq.js} +12 -12
  4. package/dist/{agents-CQHK0CSA.js.map → agents-CgVr77Qq.js.map} +1 -1
  5. package/dist/{backlog-auto-trigger-CyuoS0nl.js → backlog-auto-trigger-B_-1tk4e.js} +5 -5
  6. package/dist/{backlog-auto-trigger-CyuoS0nl.js.map → backlog-auto-trigger-B_-1tk4e.js.map} +1 -1
  7. package/dist/cli/index.js +45 -45
  8. package/dist/{cli-Cewlu3yU.js → cli-DGeSW85B.js} +2 -2
  9. package/dist/{cli-Cewlu3yU.js.map → cli-DGeSW85B.js.map} +1 -1
  10. package/dist/cli-MTB9IOoR.js +3 -0
  11. package/dist/{close-issue-BJGX9_z7.js → close-issue-Djajb6Sv.js} +2 -2
  12. package/dist/{close-issue-BJGX9_z7.js.map → close-issue-Djajb6Sv.js.map} +1 -1
  13. package/dist/{concurrency-Ca-fC7tA.js → concurrency-4rzjHAQA.js} +1 -1
  14. package/dist/{conversation-compaction-B_6s9EvM.js → conversation-compaction-BQvZh09I.js} +4 -4
  15. package/dist/{conversation-compaction-B_6s9EvM.js.map → conversation-compaction-BQvZh09I.js.map} +1 -1
  16. package/dist/dashboard/{advancing-selfheal-CMlGznKv.js → advancing-selfheal-BezNfM3P.js} +3 -3
  17. package/dist/dashboard/{advancing-selfheal-CMlGznKv.js.map → advancing-selfheal-BezNfM3P.js.map} +1 -1
  18. package/dist/dashboard/{agent-state-xk_G8t90.js → agent-state-BUANcaA7.js} +1 -1
  19. package/dist/dashboard/{agent-state-service-C1iUF9Ft.js → agent-state-service-DmFpIMbP.js} +1 -1
  20. package/dist/dashboard/{agent-state-service-5ge9xxhx.js → agent-state-service-tXUdp6UF.js} +2 -2
  21. package/dist/dashboard/{agent-state-service-5ge9xxhx.js.map → agent-state-service-tXUdp6UF.js.map} +1 -1
  22. package/dist/dashboard/{agents-BIggLvi_.js → agents-C-vqmKDi.js} +1 -1
  23. package/dist/dashboard/{agents-DHvsjsuP.js → agents-C7V4odQ6.js} +11 -11
  24. package/dist/dashboard/{agents-DHvsjsuP.js.map → agents-C7V4odQ6.js.map} +1 -1
  25. package/dist/dashboard/{backlog-auto-trigger-COm_R96V.js → backlog-auto-trigger-Cb8PR-5w.js} +2 -2
  26. package/dist/dashboard/{backlog-auto-trigger-COm_R96V.js.map → backlog-auto-trigger-Cb8PR-5w.js.map} +1 -1
  27. package/dist/dashboard/{cli-CvK9xuSS.js → cli-BgzbB6h3.js} +2 -2
  28. package/dist/dashboard/{cli-CvK9xuSS.js.map → cli-BgzbB6h3.js.map} +1 -1
  29. package/dist/dashboard/cli-DCqy_wFa.js +3 -0
  30. package/dist/dashboard/{close-issue-BnpErnD1.js → close-issue-BWf3pxn4.js} +2 -2
  31. package/dist/dashboard/{close-issue-BnpErnD1.js.map → close-issue-BWf3pxn4.js.map} +1 -1
  32. package/dist/dashboard/{closed-issue-reaper-DRqK5k39.js → closed-issue-reaper-D7XPq5pY.js} +4 -4
  33. package/dist/dashboard/{closed-issue-reaper-DRqK5k39.js.map → closed-issue-reaper-D7XPq5pY.js.map} +1 -1
  34. package/dist/dashboard/{closed-issue-reaper-BLCQwTpF.js → closed-issue-reaper-VSTSTROK.js} +1 -1
  35. package/dist/dashboard/{conflict-gate-JdFuhKuF.js → conflict-gate-DwOslTij.js} +1 -1
  36. package/dist/dashboard/{conflict-gate-BhKvrPxn.js → conflict-gate-FFEvTDVA.js} +3 -3
  37. package/dist/dashboard/{conflict-gate-BhKvrPxn.js.map → conflict-gate-FFEvTDVA.js.map} +1 -1
  38. package/dist/dashboard/{conversation-compaction-C5o_eKEm.js → conversation-compaction-CCqN4hWk.js} +4 -4
  39. package/dist/dashboard/{conversation-compaction-C5o_eKEm.js.map → conversation-compaction-CCqN4hWk.js.map} +1 -1
  40. package/dist/dashboard/{conversation-compaction-Ba5aZdrb.js → conversation-compaction-CL5Ti4j4.js} +1 -1
  41. package/dist/dashboard/{deacon-j3qSZ7--.js → deacon-CGHl90eC.js} +107 -50
  42. package/dist/dashboard/{deacon-j3qSZ7--.js.map → deacon-CGHl90eC.js.map} +1 -1
  43. package/dist/dashboard/{deacon-Cv2RMfHs.js → deacon-DMPh40Im.js} +2 -2
  44. package/dist/dashboard/deacon-swarm-CVTiNL0N.js +2 -0
  45. package/dist/dashboard/{deacon-swarm-DadWNi5J.js → deacon-swarm-aMMJTq6t.js} +4 -4
  46. package/dist/dashboard/{deacon-swarm-DadWNi5J.js.map → deacon-swarm-aMMJTq6t.js.map} +1 -1
  47. package/dist/dashboard/deacon.js +4 -4
  48. package/dist/dashboard/{feedback-target-oE4-Esxl.js → feedback-target-dJjFfITP.js} +4 -4
  49. package/dist/dashboard/{feedback-target-oE4-Esxl.js.map → feedback-target-dJjFfITP.js.map} +1 -1
  50. package/dist/dashboard/{feedback-target-C4DaDSZ8.js → feedback-target-nQSM6ygL.js} +1 -1
  51. package/dist/dashboard/{flywheel-ClMjGW_u.js → flywheel-BLoVNtJV.js} +6 -6
  52. package/dist/dashboard/{flywheel-ClMjGW_u.js.map → flywheel-BLoVNtJV.js.map} +1 -1
  53. package/dist/dashboard/{flywheel-DefHRzY4.js → flywheel-Cqf8vu7N.js} +6 -6
  54. package/dist/dashboard/{flywheel-DefHRzY4.js.map → flywheel-Cqf8vu7N.js.map} +1 -1
  55. package/dist/dashboard/{flywheel-BqDdGvrQ.js → flywheel-PJsbrXNY.js} +1 -1
  56. package/dist/dashboard/{flywheel-merge-order-CuDcqTRJ.js → flywheel-merge-order-B7BH8mZ3.js} +2 -2
  57. package/dist/dashboard/{flywheel-merge-order-CuDcqTRJ.js.map → flywheel-merge-order-B7BH8mZ3.js.map} +1 -1
  58. package/dist/dashboard/{flywheel-merge-order-B6YDSElh.js → flywheel-merge-order-CCwF60b-.js} +1 -1
  59. package/dist/dashboard/{harness-resolve-DKeSVsJL.js → harness-resolve-B3f9G1Kp.js} +2 -2
  60. package/dist/dashboard/{harness-resolve-DKeSVsJL.js.map → harness-resolve-B3f9G1Kp.js.map} +1 -1
  61. package/dist/dashboard/{inspect-agent-BKKc0XnB.js → inspect-agent-CKF5-SJr.js} +3 -3
  62. package/dist/dashboard/{inspect-agent-BKKc0XnB.js.map → inspect-agent-CKF5-SJr.js.map} +1 -1
  63. package/dist/dashboard/inspect-agent-CqpEWGWy.js +2 -0
  64. package/dist/dashboard/{issue-lifecycle-DnhQgddB.js → issue-lifecycle-5LbOcATP.js} +7 -7
  65. package/dist/dashboard/{issue-lifecycle-DnhQgddB.js.map → issue-lifecycle-5LbOcATP.js.map} +1 -1
  66. package/dist/dashboard/{issue-lifecycle-nY4esZn8.js → issue-lifecycle-CjYqQ669.js} +1 -1
  67. package/dist/dashboard/{issue-service-singleton-Bnqv1uaa.js → issue-service-singleton-B8iu_TKV.js} +1 -1
  68. package/dist/dashboard/{issue-service-singleton-_AquSBTl.js → issue-service-singleton-CDGt__oQ.js} +2 -2
  69. package/dist/dashboard/{issue-service-singleton-_AquSBTl.js.map → issue-service-singleton-CDGt__oQ.js.map} +1 -1
  70. package/dist/dashboard/{lifecycle-CGKm7doE.js → lifecycle-CEg4sHZH.js} +4 -4
  71. package/dist/dashboard/{lifecycle-CGKm7doE.js.map → lifecycle-CEg4sHZH.js.map} +1 -1
  72. package/dist/dashboard/{merge-agent-Dh8Y9PDO.js → merge-agent-7nBiCytp.js} +1 -1
  73. package/dist/dashboard/{merge-agent-BcgAYStl.js → merge-agent-C81xPIGd.js} +6 -6
  74. package/dist/dashboard/{merge-agent-BcgAYStl.js.map → merge-agent-C81xPIGd.js.map} +1 -1
  75. package/dist/dashboard/merge-ops-DE_6CnmZ.js +2 -0
  76. package/dist/dashboard/{merge-train-DmxlYbEo.js → merge-train-D3qzRccL.js} +2 -2
  77. package/dist/dashboard/{merge-train-DmxlYbEo.js.map → merge-train-D3qzRccL.js.map} +1 -1
  78. package/dist/dashboard/{merge-train-deps-DBHsOWt9.js → merge-train-deps-Be5ElC_P.js} +2 -2
  79. package/dist/dashboard/{merge-train-deps-DBHsOWt9.js.map → merge-train-deps-Be5ElC_P.js.map} +1 -1
  80. package/dist/dashboard/{orphan-proposed-reconciler-Db8tUhce.js → orphan-proposed-reconciler-Boq_XaRe.js} +3 -3
  81. package/dist/dashboard/{orphan-proposed-reconciler-Db8tUhce.js.map → orphan-proposed-reconciler-Boq_XaRe.js.map} +1 -1
  82. package/dist/dashboard/{orphan-proposed-reconciler-mXV--tcB.js → orphan-proposed-reconciler-C0sPnDq_.js} +1 -1
  83. package/dist/dashboard/{reconstruct-cache-_tDZ2D72.js → reconstruct-cache-C2pCltOq.js} +3 -3
  84. package/dist/dashboard/{reconstruct-cache-_tDZ2D72.js.map → reconstruct-cache-C2pCltOq.js.map} +1 -1
  85. package/dist/dashboard/{remote-completion-Bklh6loJ.js → remote-completion-BmxD5UMQ.js} +3 -3
  86. package/dist/dashboard/{remote-completion-Bklh6loJ.js.map → remote-completion-BmxD5UMQ.js.map} +1 -1
  87. package/dist/dashboard/resume-C-nxu9Y4.js +3 -0
  88. package/dist/dashboard/{review-agent-phzqNqIs.js → review-agent-D6P6d3pz.js} +26 -17
  89. package/dist/dashboard/review-agent-D6P6d3pz.js.map +1 -0
  90. package/dist/dashboard/review-agent-DTINEffB.js +3 -0
  91. package/dist/dashboard/{review-context-CCq56O_Y.js → review-context-Dyo0tsBa.js} +2 -2
  92. package/dist/dashboard/{review-context-CCq56O_Y.js.map → review-context-Dyo0tsBa.js.map} +1 -1
  93. package/dist/dashboard/{review-status-BQjqQXZG.js → review-status-B5ZASOo4.js} +18 -5
  94. package/dist/dashboard/review-status-B5ZASOo4.js.map +1 -0
  95. package/dist/dashboard/{review-status-bBkqUj5i.js → review-status-h7BXIA4z.js} +1 -1
  96. package/dist/dashboard/{review-verdict-feedback-wqnikhMK.js → review-verdict-feedback-B70VbfRL.js} +4 -4
  97. package/dist/dashboard/{review-verdict-feedback-wqnikhMK.js.map → review-verdict-feedback-B70VbfRL.js.map} +1 -1
  98. package/dist/dashboard/{review-verdict-feedback-cZh9Wi9G.js → review-verdict-feedback-CorgH49f.js} +1 -1
  99. package/dist/dashboard/{sequence-io-B2GmilSI.js → sequence-io-6YP8fVpH.js} +2 -2
  100. package/dist/dashboard/{sequence-io-B2GmilSI.js.map → sequence-io-6YP8fVpH.js.map} +1 -1
  101. package/dist/dashboard/{sequencer-agent-D8pW2aU_.js → sequencer-agent-ja-nkEc1.js} +5 -5
  102. package/dist/dashboard/{sequencer-agent-D8pW2aU_.js.map → sequencer-agent-ja-nkEc1.js.map} +1 -1
  103. package/dist/dashboard/server.js +66 -55
  104. package/dist/dashboard/server.js.map +1 -1
  105. package/dist/dashboard/{service-CILd-uOt.js → service-HKDrIy0h.js} +24 -24
  106. package/dist/dashboard/{service-CILd-uOt.js.map → service-HKDrIy0h.js.map} +1 -1
  107. package/dist/dashboard/{smart-compaction-Bgp_gwcA.js → smart-compaction-L0LSnF0_.js} +1 -1
  108. package/dist/dashboard/{smart-compaction-CrP90OFE.js → smart-compaction-uke1npUz.js} +2 -2
  109. package/dist/dashboard/{smart-compaction-CrP90OFE.js.map → smart-compaction-uke1npUz.js.map} +1 -1
  110. package/dist/dashboard/{spawn-planning-session-D15KbPOy.js → spawn-planning-session-CSMWco8B.js} +1 -1
  111. package/dist/dashboard/{spawn-planning-session-BmJWHSCu.js → spawn-planning-session-CXc0NkwV.js} +3 -3
  112. package/dist/dashboard/{spawn-planning-session-BmJWHSCu.js.map → spawn-planning-session-CXc0NkwV.js.map} +1 -1
  113. package/dist/dashboard/{specialist-context-CwKkcXL9.js → specialist-context-BGCJ24LH.js} +3 -3
  114. package/dist/dashboard/{specialist-context-CwKkcXL9.js.map → specialist-context-BGCJ24LH.js.map} +1 -1
  115. package/dist/dashboard/{specialist-logs-CvDxquV_.js → specialist-logs--Uz-r8ia.js} +2 -2
  116. package/dist/dashboard/{specialist-logs-CvDxquV_.js.map → specialist-logs--Uz-r8ia.js.map} +1 -1
  117. package/dist/dashboard/{specialists-BCjcG4uE.js → specialists-Ca9XSBCS.js} +1 -1
  118. package/dist/dashboard/{specialists-D07ybnam.js → specialists-DEfrAbWs.js} +36 -36
  119. package/dist/dashboard/{specialists-D07ybnam.js.map → specialists-DEfrAbWs.js.map} +1 -1
  120. package/dist/dashboard/{specialists-F2uMO4QS.js → specialists-DeZx9xom.js} +1 -1
  121. package/dist/dashboard/{specialists-BYi0k8CZ.js → specialists-nZ0mRwpZ.js} +12 -12
  122. package/dist/dashboard/{specialists-BYi0k8CZ.js.map → specialists-nZ0mRwpZ.js.map} +1 -1
  123. package/dist/dashboard/{summary-fork-DedqguZb.js → summary-fork-2a1L2DIU.js} +2 -2
  124. package/dist/dashboard/{summary-fork-EjvOkEPl.js → summary-fork-Xn0QUNmb.js} +3 -3
  125. package/dist/dashboard/{summary-fork-EjvOkEPl.js.map → summary-fork-Xn0QUNmb.js.map} +1 -1
  126. package/dist/dashboard/{teardown-workspace-5LCVbjfq.js → teardown-workspace-DhA6oAIK.js} +2 -2
  127. package/dist/dashboard/{teardown-workspace-5LCVbjfq.js.map → teardown-workspace-DhA6oAIK.js.map} +1 -1
  128. package/dist/dashboard/{test-agent-queue-qfsq6uaC.js → test-agent-queue-B_ukPyIK.js} +3 -3
  129. package/dist/dashboard/{test-agent-queue-qfsq6uaC.js.map → test-agent-queue-B_ukPyIK.js.map} +1 -1
  130. package/dist/dashboard/{uat-train-CtNGgXxj.js → uat-train-BLBAbJ8Y.js} +10 -10
  131. package/dist/dashboard/{uat-train-CtNGgXxj.js.map → uat-train-BLBAbJ8Y.js.map} +1 -1
  132. package/dist/dashboard/{uat-train-C3hWXtip.js → uat-train-DPfGR9_I.js} +1 -1
  133. package/dist/dashboard/{verification-runner-DuZRHjPU.js → verification-runner-KTGTNugM.js} +1 -1
  134. package/dist/dashboard/{verification-runner-BPAhPWR7.js → verification-runner-R2-4XgMO.js} +4 -4
  135. package/dist/dashboard/{verification-runner-BPAhPWR7.js.map → verification-runner-R2-4XgMO.js.map} +1 -1
  136. package/dist/dashboard/{workflows-H-COJ84X.js → workflows-CSKX5MhC.js} +7 -7
  137. package/dist/dashboard/{workflows-H-COJ84X.js.map → workflows-CSKX5MhC.js.map} +1 -1
  138. package/dist/dashboard/workflows-DVwXauef.js +2 -0
  139. package/dist/dashboard/{workspaces-CRU9_vRn.js → workspaces-DXLDgXo2.js} +32 -32
  140. package/dist/dashboard/{workspaces-CRU9_vRn.js.map → workspaces-DXLDgXo2.js.map} +1 -1
  141. package/dist/docs-index.sqlite +0 -0
  142. package/dist/{feedback-target-DTGAhFkM.js → feedback-target-BsKN4Tr_.js} +4 -4
  143. package/dist/{feedback-target-DTGAhFkM.js.map → feedback-target-BsKN4Tr_.js.map} +1 -1
  144. package/dist/{feedback-target-DutF088V.js → feedback-target-DM1WzOKy.js} +1 -1
  145. package/dist/{harness-resolve-Bz1prk6B.js → harness-resolve-B-YDNwNW.js} +2 -2
  146. package/dist/{harness-resolve-Bz1prk6B.js.map → harness-resolve-B-YDNwNW.js.map} +1 -1
  147. package/dist/{issue-lifecycle-fVqvCdKM.js → issue-lifecycle-Bc1l3U9f.js} +7 -7
  148. package/dist/{issue-lifecycle-fVqvCdKM.js.map → issue-lifecycle-Bc1l3U9f.js.map} +1 -1
  149. package/dist/{issue-service-singleton-CZm8p6Rj.js → issue-service-singleton-D-8Q6tdT.js} +1 -1
  150. package/dist/{issue-service-singleton-Cr73JAaR.js → issue-service-singleton-D1433I7n.js} +2 -2
  151. package/dist/{issue-service-singleton-Cr73JAaR.js.map → issue-service-singleton-D1433I7n.js.map} +1 -1
  152. package/dist/{lifecycle-CR6tZfl1.js → lifecycle-SmrqysDf.js} +7 -7
  153. package/dist/{lifecycle-CR6tZfl1.js.map → lifecycle-SmrqysDf.js.map} +1 -1
  154. package/dist/{lifecycle-D_EWiUjk.js → lifecycle-mRMyiIXs.js} +2 -2
  155. package/dist/{merge-agent-LM4avOtA.js → merge-agent-B9hIZ6Z0.js} +6 -6
  156. package/dist/{merge-agent-LM4avOtA.js.map → merge-agent-B9hIZ6Z0.js.map} +1 -1
  157. package/dist/{merge-agent-Bj046HE1.js → merge-agent-DUj22cXL.js} +1 -1
  158. package/dist/{merge-fulfillment-DrnC1Kmg.js → merge-fulfillment-__P4WE-T.js} +3 -3
  159. package/dist/{merge-fulfillment-DrnC1Kmg.js.map → merge-fulfillment-__P4WE-T.js.map} +1 -1
  160. package/dist/{reconstruct-cache-NPqoAUg-.js → reconstruct-cache-9SWp82SQ.js} +3 -3
  161. package/dist/{reconstruct-cache-NPqoAUg-.js.map → reconstruct-cache-9SWp82SQ.js.map} +1 -1
  162. package/dist/{records-BIkN1LQ4.js → records-CTmvuRzz.js} +14 -1
  163. package/dist/{records-BIkN1LQ4.js.map → records-CTmvuRzz.js.map} +1 -1
  164. package/dist/records-backfill-BQcmTmwF.js +2 -0
  165. package/dist/{records-backfill-HM0Pln6N.js → records-backfill-BrNsv_zT.js} +2 -2
  166. package/dist/{records-backfill-HM0Pln6N.js.map → records-backfill-BrNsv_zT.js.map} +1 -1
  167. package/dist/resume-Bn4kDk9x.js +3 -0
  168. package/dist/{review-agent-DFosQzCG.js → review-agent-PhqOhpx3.js} +24 -16
  169. package/dist/{review-agent-DFosQzCG.js.map → review-agent-PhqOhpx3.js.map} +1 -1
  170. package/dist/review-agent-hLbRRxZF.js +3 -0
  171. package/dist/{review-status-iJwfEoNX.js → review-status-B_XXomV0.js} +6 -6
  172. package/dist/{review-status-iJwfEoNX.js.map → review-status-B_XXomV0.js.map} +1 -1
  173. package/dist/{review-status-BbNVlXh1.js → review-status-Bo-EFVKh.js} +1 -1
  174. package/dist/{review-verdict-feedback-BGPw3z1X.js → review-verdict-feedback-CXHkoiKZ.js} +1 -1
  175. package/dist/{review-verdict-feedback-CCZ05atV.js → review-verdict-feedback-DGV13LDb.js} +4 -4
  176. package/dist/{review-verdict-feedback-CCZ05atV.js.map → review-verdict-feedback-DGV13LDb.js.map} +1 -1
  177. package/dist/{sequence-io-CcxZNHcD.js → sequence-io-Cg4NHAco.js} +1 -1
  178. package/dist/{sequence-io-Cx1usxG3.js → sequence-io-CyxvNHfA.js} +2 -2
  179. package/dist/{sequence-io-Cx1usxG3.js.map → sequence-io-CyxvNHfA.js.map} +1 -1
  180. package/dist/{smart-compaction-ByUEgQkF.js → smart-compaction-ByKXTQNy.js} +2 -2
  181. package/dist/{smart-compaction-ByUEgQkF.js.map → smart-compaction-ByKXTQNy.js.map} +1 -1
  182. package/dist/{smart-compaction-DfLe_CtH.js → smart-compaction-U6e_uB1E.js} +1 -1
  183. package/dist/{specialist-context-McuGkaeR.js → specialist-context-BUTadO-U.js} +3 -3
  184. package/dist/{specialist-context-McuGkaeR.js.map → specialist-context-BUTadO-U.js.map} +1 -1
  185. package/dist/{specialist-logs-CUUJ4eiB.js → specialist-logs-pEMCTtGZ.js} +2 -2
  186. package/dist/{specialist-logs-CUUJ4eiB.js.map → specialist-logs-pEMCTtGZ.js.map} +1 -1
  187. package/dist/{specialists-BSrylD1S.js → specialists-BGFnrQ8n.js} +1 -1
  188. package/dist/{specialists-B0elgInc.js → specialists-DeTkkp8b.js} +12 -12
  189. package/dist/{specialists-B0elgInc.js.map → specialists-DeTkkp8b.js.map} +1 -1
  190. package/dist/{summary-fork-DskMRnyb.js → summary-fork-CgzThE2-.js} +2 -2
  191. package/dist/{summary-fork-CPUTKcAT.js → summary-fork-Dx1RMuvU.js} +3 -3
  192. package/dist/{summary-fork-CPUTKcAT.js.map → summary-fork-Dx1RMuvU.js.map} +1 -1
  193. package/dist/{sync-BiNVQHBf.js → sync-Bsww9tuj.js} +2 -2
  194. package/dist/{sync-BiNVQHBf.js.map → sync-Bsww9tuj.js.map} +1 -1
  195. package/dist/sync-CylE_SjB.js +2 -0
  196. package/dist/{verdict-restore-DQiV7_fe.js → verdict-restore-DGpX0ZOJ.js} +5 -5
  197. package/dist/{verdict-restore-DQiV7_fe.js.map → verdict-restore-DGpX0ZOJ.js.map} +1 -1
  198. package/dist/{webhook-handlers-DHBSEDEm.js → webhook-handlers-BjsfIL7u.js} +3 -3
  199. package/dist/{webhook-handlers-DHBSEDEm.js.map → webhook-handlers-BjsfIL7u.js.map} +1 -1
  200. package/dist/{workspace-migrate-duUsWNpd.js → workspace-migrate-BBQ8nAnP.js} +2 -2
  201. package/dist/{workspace-migrate-duUsWNpd.js.map → workspace-migrate-BBQ8nAnP.js.map} +1 -1
  202. package/dist/{workspace-reap-DLCIoJmn.js → workspace-reap-12O9354k.js} +2 -2
  203. package/dist/{workspace-reap-DLCIoJmn.js.map → workspace-reap-12O9354k.js.map} +1 -1
  204. package/package.json +1 -1
  205. package/scripts/file-size-baseline.txt +1 -1
  206. package/dist/cli-DBWG12XK.js +0 -3
  207. package/dist/dashboard/cli-CJDWXE_Y.js +0 -3
  208. package/dist/dashboard/deacon-swarm-D1iAUX-P.js +0 -2
  209. package/dist/dashboard/inspect-agent-vm88mLYr.js +0 -2
  210. package/dist/dashboard/merge-ops-QdBiQ8XB.js +0 -2
  211. package/dist/dashboard/resume-DnIsmIgC.js +0 -3
  212. package/dist/dashboard/review-agent-DgySDb-G.js +0 -3
  213. package/dist/dashboard/review-agent-phzqNqIs.js.map +0 -1
  214. package/dist/dashboard/review-status-BQjqQXZG.js.map +0 -1
  215. package/dist/dashboard/workflows-Cz8OtiB4.js +0 -2
  216. package/dist/records-backfill-DEgtRyKL.js +0 -2
  217. package/dist/resume-DGymuXZM.js +0 -3
  218. package/dist/review-agent-DjVadURa.js +0 -3
  219. package/dist/sync-4jqukkxz.js +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"uat-train-CtNGgXxj.js","names":["resolve","relative","sep","existsSync","readFileSync","relative","isAbsolute","resolve","sep","totalmem","freemem","execFileAsync","promisify","execFile","execFileAsync","promisify","execFile","defaultDeps","execFileAsync","promisify","execFile","execFileAsync","promisify","execFile","resolve","nodeServicesLayer","stat"],"sources":["../../src/dashboard/server/identity.ts","../../src/dashboard/server/services/dashboard-db-task.ts","../../src/dashboard/server/services/flywheel-actions.ts","../../src/lib/cloister/uat-candidate-name.ts","../../src/lib/cloister/uat-generation-engine.ts","../../src/lib/cloister/uat-generation-deps.ts","../../src/lib/cloister/uat-conflict-agent.ts","../../src/lib/cloister/uat-reconciler.ts","../../src/lib/cloister/uat-stack.ts","../../src/lib/cloister/file-size-reconcile.ts","../../src/lib/cloister/uat-promote.ts","../../src/lib/cloister/uat-promote-notify.ts","../../src/dashboard/server/services/uat-train.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { cwd } from 'node:process';\nimport { relative, resolve, sep } from 'node:path';\n\nimport { parse as parseToml } from '@iarna/toml';\n\nimport { CONFIG_FILE } from '../../lib/paths.js';\n\nexport type DashboardMode = 'primary' | 'peer';\n\nexport interface DashboardIdentity {\n readonly repoRoot: string;\n readonly mode: DashboardMode;\n}\n\nexport function getDashboardIdentity(): DashboardIdentity {\n return {\n repoRoot: resolve(cwd()),\n mode: process.env.OVERDECK_DISABLE_DEACON === '1' ? 'peer' : 'primary',\n };\n}\n\nexport function isWorkspaceRepoRoot(repoRoot: string): boolean {\n return /(^|\\/)workspaces\\/feature-[^/]+$/i.test(repoRoot.replaceAll('\\\\', '/'));\n}\n\nfunction isSameOrInside(parent: string, candidate: string): boolean {\n const rel = relative(resolve(parent), resolve(candidate));\n return rel === '' || (!rel.startsWith('..') && !rel.startsWith(sep));\n}\n\nexport function readHostDashboardApiPort(defaultPort = 3011): number {\n if (!existsSync(CONFIG_FILE)) return defaultPort;\n try {\n const config = parseToml(readFileSync(CONFIG_FILE, 'utf-8')) as {\n dashboard?: { api_port?: unknown };\n };\n const value = config.dashboard?.api_port;\n return typeof value === 'number' && Number.isFinite(value) ? value : defaultPort;\n } catch {\n return defaultPort;\n }\n}\n\nexport function shouldRefuseHostDashboardPort(input: {\n readonly repoRoot: string;\n readonly mode: DashboardMode;\n readonly port: number;\n readonly hostDashboardApiPort?: number;\n readonly runningInContainer?: boolean;\n}): boolean {\n const hostDashboardApiPort = input.hostDashboardApiPort ?? readHostDashboardApiPort();\n if (input.port !== hostDashboardApiPort) return false;\n\n const repoRoot = resolve(input.repoRoot);\n const runningInContainer =\n input.runningInContainer ?? (existsSync('/.dockerenv') || repoRoot === '/workspaces/overdeck');\n if (runningInContainer) {\n return false;\n }\n if (input.mode === 'peer') return true;\n\n if (isWorkspaceRepoRoot(repoRoot)) return true;\n\n const workspacesDir = resolve(repoRoot, '..');\n return workspacesDir.endsWith(`${sep}workspaces`) && isSameOrInside(workspacesDir, repoRoot);\n}\n","import { randomUUID } from 'node:crypto';\nimport { Worker } from 'node:worker_threads';\nimport {\n aggregateDiscoveredSessionCost,\n aggregateDiscoveredSessionCostBy,\n countDiscoveredSessions,\n findDiscoveredSessions,\n getDiscoveredSessionById,\n getDiscoveredStats,\n} from '../../../lib/overdeck/discovered-sessions.js';\nimport { getConversationByName } from '../../../lib/overdeck/conversations.js';\nimport { getSetting, setSetting } from '../../../lib/overdeck/control-settings.js';\nimport type { ConversationFilter } from '../../../lib/overdeck/discovered-sessions.js';\nimport { getSessionsFeedFacets, listSessionsFeed } from '../../../lib/overdeck/sessions-feed.js';\nimport type { SessionsFeedFilter } from '../../../lib/overdeck/sessions-feed.js';\nimport { searchSessions } from '../../../lib/conversations/search.js';\nimport type { SearchQuery } from '../../../lib/conversations/search.js';\nimport { scan } from '../../../lib/conversations/scanner.js';\nimport type { ScanOptions } from '../../../lib/conversations/scanner.js';\nimport { enrichSessions, CostThresholdError } from '../../../lib/conversations/enrichment/index.js';\nimport type { EnrichOptions } from '../../../lib/conversations/enrichment/index.js';\nimport { embedSessions } from '../../../lib/conversations/embeddings/index.js';\nimport type { EmbedSessionsOptions } from '../../../lib/conversations/embeddings/index.js';\n\nexport type DashboardDbOperation =\n | 'getDiscoveredStats'\n | 'listDiscoveredSessions'\n | 'listSessionsFeed'\n | 'getSessionsFeedFacets'\n | 'getDiscoveredSessionById'\n | 'aggregateDiscoveredSessionCost'\n | 'aggregateDiscoveredSessionCostBy'\n | 'searchSessions'\n | 'searchSessionsSemantic'\n | 'scanConversations'\n | 'enrichSessions'\n | 'embedSessions'\n | 'getConversationByName'\n | 'getSetting'\n | 'setSetting'\n | 'getArtifactBySlug'\n | 'listArtifactsForWorkspaceOrIssue'\n | 'unshareArtifactBySlug';\n\ntype ProgressHandler = (progress: unknown) => void | Promise<void>;\ntype WorkerLane = 'read' | 'long' | 'semantic';\n\ninterface PendingJob {\n lane: WorkerLane;\n operation: DashboardDbOperation;\n resolve: (value: unknown) => void;\n reject: (err: Error) => void;\n progressListeners: Set<ProgressHandler>;\n progressChain: Promise<void>;\n timeout: NodeJS.Timeout | null;\n}\n\ninterface SharedJob {\n lane: WorkerLane;\n promise: Promise<unknown>;\n progressListeners: Set<ProgressHandler>;\n}\n\ninterface WorkerResponse {\n id: string;\n ok?: boolean;\n result?: unknown;\n progress?: unknown;\n error?: {\n name?: string;\n message?: string;\n stack?: string;\n estimatedCost?: number;\n threshold?: number;\n sessionCount?: number;\n };\n}\n\nconst MAX_PENDING_JOBS = 32;\nconst SEMANTIC_SEARCH_TIMEOUT_MS = Number.parseInt(process.env['OVERDECK_SEMANTIC_SEARCH_TIMEOUT_MS'] ?? '15000', 10);\nconst COALESCED_OPERATIONS = new Set<DashboardDbOperation>([\n 'scanConversations',\n 'enrichSessions',\n 'embedSessions',\n 'searchSessionsSemantic',\n]);\n\nconst workers: Record<WorkerLane, Worker | null> = { read: null, long: null, semantic: null };\nconst pending = new Map<string, PendingJob>();\nconst sharedJobs = new Map<string, SharedJob>();\nlet latestSemanticJobId: string | null = null;\n\nfunction workerScriptUrl(): URL {\n return import.meta.url.endsWith('.ts')\n ? new URL('./dashboard-db-worker.ts', import.meta.url)\n : new URL('./dashboard-db-worker.js', import.meta.url);\n}\n\nfunction failPendingForLane(lane: WorkerLane, err: Error): void {\n for (const [id, job] of pending.entries()) {\n if (job.lane !== lane) continue;\n if (job.timeout) clearTimeout(job.timeout);\n job.reject(err);\n pending.delete(id);\n }\n for (const [key, job] of sharedJobs.entries()) {\n if (job.lane === lane) sharedJobs.delete(key);\n }\n if (lane === 'semantic') latestSemanticJobId = null;\n}\n\nfunction stableStringify(value: unknown): string {\n if (value == null || typeof value !== 'object') return JSON.stringify(value);\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`;\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => typeof v !== 'function' && v !== undefined)\n .sort(([a], [b]) => a.localeCompare(b));\n return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).join(',')}}`;\n}\n\nfunction coalescingKey(operation: DashboardDbOperation, payload: unknown): string | null {\n if (!COALESCED_OPERATIONS.has(operation)) return null;\n return `${operation}:${stableStringify(payload)}`;\n}\n\nfunction workerLane(operation: DashboardDbOperation): WorkerLane {\n if (operation === 'searchSessionsSemantic') return 'semantic';\n return COALESCED_OPERATIONS.has(operation) ? 'long' : 'read';\n}\n\nfunction cancelOlderSemanticSearches(): void {\n if (!latestSemanticJobId && !workers.semantic) return;\n failPendingForLane('semantic', new Error('Superseded by a newer semantic search'));\n void workers.semantic?.terminate();\n workers.semantic = null;\n}\n\nfunction aggregateDiscoveredSessionCostByPayload(payload: unknown) {\n if (typeof payload === 'string') {\n return aggregateDiscoveredSessionCostBy(payload as 'workspace' | 'model' | 'day' | 'month');\n }\n const input = payload as { groupBy?: 'workspace' | 'model' | 'day' | 'month'; filter?: ConversationFilter } | undefined;\n return aggregateDiscoveredSessionCostBy(input?.groupBy ?? 'workspace', input?.filter ?? {});\n}\n\nfunction getWorker(lane: WorkerLane): Worker {\n const existing = workers[lane];\n if (existing) return existing;\n\n const worker = new Worker(workerScriptUrl(), {\n execArgv: process.execArgv.filter((arg) => !arg.startsWith('--inspect')),\n } as ConstructorParameters<typeof Worker>[1]);\n workers[lane] = worker;\n\n worker.on('message', (message: WorkerResponse) => {\n const job = pending.get(message.id);\n if (!job) return;\n\n if (message.progress !== undefined) {\n job.progressChain = job.progressChain.then(async () => {\n for (const listener of job.progressListeners) {\n await listener(message.progress);\n }\n });\n return;\n }\n\n pending.delete(message.id);\n if (job.timeout) clearTimeout(job.timeout);\n if (job.lane === 'semantic' && latestSemanticJobId === message.id) latestSemanticJobId = null;\n\n if (message.ok) {\n job.progressChain.then(() => job.resolve(message.result), job.reject);\n return;\n }\n\n const err = message.error?.name === 'CostThresholdError'\n ? new CostThresholdError(\n message.error.estimatedCost ?? 0,\n message.error.threshold ?? 0,\n message.error.sessionCount ?? 0,\n )\n : new Error(message.error?.message ?? 'Dashboard database worker failed');\n err.name = message.error?.name ?? 'DashboardDatabaseWorkerError';\n err.stack = message.error?.stack;\n job.progressChain.then(() => job.reject(err), job.reject);\n });\n\n worker.on('error', (err) => {\n failPendingForLane(lane, err);\n if (workers[lane] === worker) workers[lane] = null;\n });\n\n worker.on('exit', (code) => {\n if (code !== 0) failPendingForLane(lane, new Error(`Dashboard database ${lane} worker exited with code ${code}`));\n if (workers[lane] === worker) workers[lane] = null;\n });\n\n return worker;\n}\n\nasync function runInline(\n operation: DashboardDbOperation,\n payload: unknown,\n onProgress?: (progress: unknown) => void | Promise<void>,\n): Promise<unknown> {\n switch (operation) {\n case 'getDiscoveredStats':\n return getDiscoveredStats();\n case 'listDiscoveredSessions': {\n const filter = payload as ConversationFilter;\n return {\n sessions: findDiscoveredSessions(filter),\n total: countDiscoveredSessions({ ...filter, limit: undefined, offset: undefined }),\n };\n }\n case 'listSessionsFeed':\n return listSessionsFeed(payload as SessionsFeedFilter);\n case 'getSessionsFeedFacets':\n return getSessionsFeedFacets(payload as SessionsFeedFilter);\n case 'getDiscoveredSessionById':\n return getDiscoveredSessionById(payload as number);\n case 'aggregateDiscoveredSessionCost':\n return aggregateDiscoveredSessionCost(payload as ConversationFilter);\n case 'aggregateDiscoveredSessionCostBy':\n return aggregateDiscoveredSessionCostByPayload(payload);\n case 'searchSessions':\n case 'searchSessionsSemantic':\n return searchSessions(payload as SearchQuery);\n case 'scanConversations':\n return scan({ ...(payload as ScanOptions), onProgress });\n case 'enrichSessions':\n return enrichSessions({ ...(payload as EnrichOptions), onProgress });\n case 'embedSessions':\n return embedSessions({ ...(payload as EmbedSessionsOptions), autoInstall: true, onProgress });\n case 'getConversationByName':\n return getConversationByName(payload as string);\n case 'getSetting':\n return getSetting(payload as string);\n case 'setSetting': {\n const input = payload as { key: string; value: string };\n setSetting(input.key, input.value);\n return null;\n }\n case 'getArtifactBySlug': {\n const { getArtifactBySlugJob } = await import('./artifact-index-jobs.js');\n return getArtifactBySlugJob(payload as string);\n }\n case 'listArtifactsForWorkspaceOrIssue': {\n const { listArtifactsForWorkspaceOrIssueJob } = await import('./artifact-index-jobs.js');\n return listArtifactsForWorkspaceOrIssueJob(payload as string);\n }\n case 'unshareArtifactBySlug': {\n const { unshareArtifactBySlugJob } = await import('./artifact-index-jobs.js');\n return unshareArtifactBySlugJob(payload as string);\n }\n }\n}\n\nexport function runDashboardDbJob<T>(\n operation: DashboardDbOperation,\n payload?: unknown,\n onProgress?: (progress: unknown) => void | Promise<void>,\n): Promise<T> {\n if (import.meta.url.endsWith('.ts') && process.env['VITEST']) {\n return runInline(operation, payload, onProgress) as Promise<T>;\n }\n\n const key = coalescingKey(operation, payload);\n const existing = key ? sharedJobs.get(key) : undefined;\n if (existing) {\n if (onProgress) existing.progressListeners.add(onProgress);\n return existing.promise as Promise<T>;\n }\n\n if (pending.size >= MAX_PENDING_JOBS) {\n return Promise.reject(new Error('Dashboard database worker queue is full'));\n }\n\n const id = randomUUID();\n const lane = workerLane(operation);\n if (operation === 'searchSessionsSemantic') {\n cancelOlderSemanticSearches();\n latestSemanticJobId = id;\n }\n const progressListeners = new Set<ProgressHandler>();\n if (onProgress) progressListeners.add(onProgress);\n\n const promise = new Promise<T>((resolve, reject) => {\n const timeout = operation === 'searchSessionsSemantic'\n ? setTimeout(() => {\n const job = pending.get(id);\n if (!job) return;\n pending.delete(id);\n for (const [sharedKey, sharedJob] of sharedJobs.entries()) {\n if (sharedJob.promise === promise) sharedJobs.delete(sharedKey);\n }\n if (latestSemanticJobId === id) latestSemanticJobId = null;\n reject(new Error('Semantic search timed out'));\n void workers.semantic?.terminate();\n workers.semantic = null;\n }, Number.isFinite(SEMANTIC_SEARCH_TIMEOUT_MS) ? SEMANTIC_SEARCH_TIMEOUT_MS : 15000)\n : null;\n pending.set(id, {\n lane,\n operation,\n resolve: resolve as (value: unknown) => void,\n reject,\n progressListeners,\n progressChain: Promise.resolve(),\n timeout,\n });\n getWorker(lane).postMessage({ id, operation, payload });\n });\n\n if (key) {\n sharedJobs.set(key, { lane, promise, progressListeners });\n promise.then(\n () => sharedJobs.delete(key),\n () => sharedJobs.delete(key),\n );\n }\n\n return promise;\n}\n","import { exec, spawn } from 'node:child_process';\nimport { readFile, realpath } from 'node:fs/promises';\nimport { freemem, totalmem } from 'node:os';\nimport { isAbsolute, join, relative, resolve, sep } from 'node:path';\nimport { promisify } from 'node:util';\nimport type { FlywheelStatus } from '@overdeck/contracts';\nimport { Effect } from 'effect';\nimport { loadConfigNoMigration, resolveModel, type FlywheelScope, type RoleEffort } from '../../../lib/config-yaml.js';\nimport { resolveHarness } from '../../../lib/harness-resolve.js';\nimport { FLYWHEEL_ORCHESTRATOR_AGENT_ID, isFlywheelDevcontainerRuntime, loadResumeSessionId, saveResumeSessionId, spawnFlywheelAgent } from '../../../lib/cloister/flywheel.js';\nimport { FLYWHEEL_ACTIVE_RUN_ID_KEY, FLYWHEEL_GLOBAL_PAUSE_KEY } from '../../../lib/overdeck/control-settings.js';\nimport { isPaneDead, killSession, sessionExists } from '../../../lib/tmux.js';\nimport {\n abortFlywheelRun,\n getFlywheelRunDetail,\n listFlywheelRuns,\n nextFlywheelRunId,\n readFlywheelLaunchMetadata,\n resolveLiveFlywheelRunId,\n writeFlywheelLaunchMetadata,\n writeLatestFlywheelStatus,\n} from './flywheel-run-state.js';\nimport { runDashboardDbJob } from './dashboard-db-task.js';\n\ninterface StartOptions {\n brief?: string;\n cwd?: string;\n}\n\ninterface ReportOpenOptions {\n runId?: string;\n opener?: (path: string) => void | Promise<void>;\n}\n\ninterface FlywheelGateSnapshot {\n paused: boolean;\n activeRunId: string | null;\n}\n\ninterface ResolvedFlywheelRoleConfig {\n harness: 'claude-code' | 'ohmypi' | 'codex';\n model: string;\n effort: RoleEffort;\n minAgents: number;\n maxAgents: number;\n scope: FlywheelScope;\n}\n\nconst DEFAULT_BRIEF_PATH = 'docs/flywheel-brief.md';\nconst execAsync = promisify(exec);\n\nfunction mb(bytes: number): number {\n return Math.round(bytes / 1024 / 1024);\n}\n\nfunction isInsideRoot(projectRoot: string, candidate: string): boolean {\n const relativePath = relative(projectRoot, candidate);\n return relativePath === '' || (!relativePath.startsWith('..') && !isAbsolute(relativePath));\n}\n\nexport function resolveFlywheelStartBriefPath(cwd: string, requestedPath?: string): { absolutePath: string; displayPath: string } {\n const rawPath = requestedPath?.trim() || DEFAULT_BRIEF_PATH;\n if (rawPath.includes('\\0')) throw new Error('Brief path is invalid');\n\n const root = resolve(cwd);\n const absolutePath = isAbsolute(rawPath) ? resolve(rawPath) : resolve(root, rawPath);\n if (!isInsideRoot(root, absolutePath)) throw new Error('Brief path must stay inside the project root');\n\n const normalizedRoot = root.endsWith(sep) ? root : `${root}${sep}`;\n const displayPath = absolutePath === root ? '.' : relative(root, absolutePath);\n return { absolutePath, displayPath: absolutePath.startsWith(normalizedRoot) ? displayPath : absolutePath };\n}\n\nasync function assertExistingPathInsideRoot(projectRoot: string, candidate: string): Promise<void> {\n const [realRoot, realCandidate] = await Promise.all([realpath(projectRoot), realpath(candidate)]);\n if (!isInsideRoot(realRoot, realCandidate)) throw new Error('Brief path must stay inside the project root');\n}\n\nexport async function requireFlywheelBrief(cwd: string, requestedPath?: string): Promise<{ absolutePath: string; displayPath: string }> {\n const resolved = resolveFlywheelStartBriefPath(cwd, requestedPath);\n try {\n await assertExistingPathInsideRoot(cwd, resolved.absolutePath);\n await readFile(resolved.absolutePath, 'utf8');\n return resolved;\n } catch (error) {\n const code = typeof error === 'object' && error !== null && 'code' in error ? error.code : undefined;\n if (code === 'ENOENT') throw new Error(`Flywheel brief not found: ${resolved.displayPath}`);\n throw error;\n }\n}\n\nasync function dashboardSetting(key: string): Promise<string | null> {\n return runDashboardDbJob<string | null>('getSetting', key);\n}\n\nasync function setDashboardSetting(key: string, value: string): Promise<void> {\n await runDashboardDbJob('setSetting', { key, value });\n}\n\nasync function getActiveRunId(): Promise<string | null> {\n const value = await dashboardSetting(FLYWHEEL_ACTIVE_RUN_ID_KEY);\n return value && value.trim() ? value : null;\n}\n\nasync function setActiveRunId(runId: string | null): Promise<void> {\n await setDashboardSetting(FLYWHEEL_ACTIVE_RUN_ID_KEY, runId ?? '');\n}\n\nasync function isPaused(): Promise<boolean> {\n return (await dashboardSetting(FLYWHEEL_GLOBAL_PAUSE_KEY)) === 'true';\n}\n\nasync function setPaused(paused: boolean): Promise<void> {\n await setDashboardSetting(FLYWHEEL_GLOBAL_PAUSE_KEY, paused ? 'true' : 'false');\n}\n\nasync function readGateSnapshot(): Promise<FlywheelGateSnapshot> {\n const [paused, activeRunId] = await Promise.all([isPaused(), getActiveRunId()]);\n return { paused, activeRunId };\n}\n\nasync function resolveFlywheelRoleConfig(): Promise<ResolvedFlywheelRoleConfig> {\n const { config } = await Effect.runPromise(loadConfigNoMigration());\n const flywheel = config.roles?.flywheel;\n const model = resolveModel('flywheel', undefined, config);\n // Harness is provider-default-only (PAN-1984): derive from the model's provider via the\n // canonical resolver — never a per-role pin or a hardcoded claude-code fallback (PAN-1865).\n const harness = await resolveHarness({ model });\n return {\n harness,\n model,\n effort: flywheel?.effort ?? 'high',\n minAgents: flywheel?.minAgents ?? 20,\n maxAgents: flywheel?.maxAgents ?? 30,\n scope: flywheel?.scope ?? 'pan-only',\n };\n}\n\nasync function gitOutput(command: string, cwd: string): Promise<string> {\n const { stdout } = await execAsync(command, { cwd, encoding: 'utf8' });\n return stdout.trim();\n}\n\nasync function createInitialFlywheelStatus(\n runId: string,\n startedAt: string,\n cwd: string,\n agentModel: string | undefined,\n agentHarness: 'claude-code' | 'ohmypi' | 'codex' | undefined,\n roleConfig: ResolvedFlywheelRoleConfig,\n): Promise<FlywheelStatus> {\n const ramTotalMb = mb(totalmem());\n return {\n runId,\n startedAt,\n elapsedMs: 0,\n orchestrator: {\n harness: agentHarness ?? roleConfig.harness,\n model: agentModel ?? roleConfig.model,\n effort: roleConfig.effort,\n ctxPercent: 0,\n },\n headline: {\n bugsFixed: 0,\n swarmItemsMerged: 0,\n swarmItemsTotal: 0,\n prsMerged: 0,\n awaitingUat: 0,\n },\n activePipeline: [],\n substrateBugs: [],\n agents: [{\n id: FLYWHEEL_ORCHESTRATOR_AGENT_ID,\n label: 'flywheel-orchestrator',\n status: 'running',\n role: 'flywheel',\n model: agentModel,\n }],\n parked: [],\n suggestions: [],\n system: {\n mainHead: await gitOutput('git rev-parse --short HEAD', cwd).catch(() => 'unknown'),\n ramUsedMb: Math.max(0, ramTotalMb - mb(freemem())),\n ramTotalMb,\n swapUsedMb: 0,\n swapTotalMb: 0,\n agentsActive: 1,\n agentsCap: roleConfig.maxAgents,\n },\n openQuestions: [],\n ticks: 0,\n lastTickAt: startedAt,\n };\n}\n\nexport async function startFlywheelRunForDashboard(options: StartOptions = {}): Promise<{ runId: string; briefDisplayPath: string; agentModel?: string }> {\n const cwd = options.cwd ?? process.cwd();\n if (isFlywheelDevcontainerRuntime()) {\n throw new Error('Refusing to spawn flywheel-orchestrator inside a workspace devcontainer');\n }\n\n // Self-healing gate check (PAN-1245). Mirrors the CLI's spawnFlywheel guard\n // so the two start paths agree about what counts as \"active\".\n const activeRunId = await resolveLiveFlywheelRunId();\n if (activeRunId) {\n // Auto-abort when the orchestrator process has exited (Pi/ohmypi exited\n // without writing report.md or aborted.json — a \"keep-alive corpse\").\n // sessionExists only checks if the tmux session exists, not if the pane\n // process is alive. Use isPaneDead which checks #{pane_dead}.\n const sessionMissing = !(await Effect.runPromise(sessionExists(FLYWHEEL_ORCHESTRATOR_AGENT_ID)));\n const paneDead = sessionMissing || await Effect.runPromise(isPaneDead(FLYWHEEL_ORCHESTRATOR_AGENT_ID));\n if (paneDead) {\n console.log(`[flywheel] ${activeRunId} gate stuck — orchestrator pane is dead; auto-aborting`);\n // Kill the stale tmux session directly — stopAgent may fail if the agent\n // is not yet in the DB (e.g. after a rebuild). killSession is sufficient\n // to clear the \"already running\" guard in spawnFlywheelAgent.\n await Effect.runPromise(killSession(FLYWHEEL_ORCHESTRATOR_AGENT_ID)).catch(() => {});\n await abortFlywheelRun(activeRunId);\n } else {\n throw new Error(`Flywheel run ${activeRunId} is already active; pause, resume, or report it before starting another run`);\n }\n }\n\n const brief = await requireFlywheelBrief(cwd, options.brief ?? DEFAULT_BRIEF_PATH);\n const runId = await nextFlywheelRunId();\n const startedAt = new Date().toISOString();\n await writeFlywheelLaunchMetadata({\n version: 1,\n runId,\n workspace: cwd,\n briefPath: brief.absolutePath,\n briefDisplayPath: brief.displayPath,\n });\n const roleConfig = await resolveFlywheelRoleConfig();\n const agent = await spawnFlywheelAgent(runId, {\n briefPath: brief.absolutePath,\n workspace: cwd,\n model: roleConfig.model,\n harness: roleConfig.harness,\n effort: roleConfig.effort,\n minAgents: roleConfig.minAgents,\n maxAgents: roleConfig.maxAgents,\n scope: roleConfig.scope,\n });\n await setActiveRunId(runId);\n await setPaused(false);\n await writeLatestFlywheelStatus(await createInitialFlywheelStatus(\n runId,\n startedAt,\n cwd,\n agent.model,\n agent.harness,\n roleConfig,\n ));\n const resolved = resolveFlywheelStartBriefPath(cwd, options.brief ?? DEFAULT_BRIEF_PATH);\n return { runId, briefDisplayPath: resolved.displayPath, agentModel: agent.model };\n}\n\nexport async function pauseFlywheelRunForDashboard(): Promise<{ before: FlywheelGateSnapshot; after: FlywheelGateSnapshot; changed: boolean }> {\n const before = await readGateSnapshot();\n if (before.paused) return { before, after: before, changed: false };\n if (before.activeRunId) {\n try {\n const { readFile } = await import('node:fs/promises');\n const { join } = await import('node:path');\n const { getAgentDir } = await import('../../../lib/agents.js');\n const sessionId = (await readFile(join(getAgentDir(FLYWHEEL_ORCHESTRATOR_AGENT_ID), 'session.id'), 'utf-8')).trim();\n if (sessionId) await saveResumeSessionId(before.activeRunId, sessionId);\n } catch { /* non-fatal: resume falls back to fresh if session.id is missing */ }\n }\n await setPaused(true);\n await import('../../../lib/agents.js').then(({ stopAgent }) => Effect.runPromise(stopAgent(FLYWHEEL_ORCHESTRATOR_AGENT_ID)));\n return { before, after: await readGateSnapshot(), changed: true };\n}\n\n// Discard the current flywheel run without writing a report (PAN-1245). Stops\n// the orchestrator if still attached, writes `aborted.json`, clears the gate.\n// Safe to call from any state: returns { aborted: null } when nothing was live.\nexport async function abortFlywheelRunForDashboard(): Promise<{ aborted: string | null }> {\n const candidate = await getActiveRunId();\n if (!candidate) {\n // Defensive cleanup: gate could be in a half-cleared state.\n const stale = await resolveLiveFlywheelRunId();\n return { aborted: stale ?? null };\n }\n const { stopAgent } = await import('../../../lib/agents.js');\n await Effect.runPromise(stopAgent(FLYWHEEL_ORCHESTRATOR_AGENT_ID));\n await abortFlywheelRun(candidate);\n return { aborted: candidate };\n}\n\nexport async function resumeFlywheelRunForDashboard(): Promise<{ before: FlywheelGateSnapshot; after: FlywheelGateSnapshot; changed: boolean }> {\n const before = await readGateSnapshot();\n if (!before.paused && await Effect.runPromise(sessionExists(FLYWHEEL_ORCHESTRATOR_AGENT_ID))) {\n return { before, after: before, changed: false };\n }\n if (!before.activeRunId) throw new Error('No active flywheel run to resume');\n\n const launch = await readFlywheelLaunchMetadata(before.activeRunId);\n if (!launch) {\n throw new Error(`Flywheel run ${before.activeRunId} is missing launch metadata; cannot resume safely`);\n }\n const brief = await requireFlywheelBrief(launch.workspace, launch.briefPath);\n const roleConfig = await resolveFlywheelRoleConfig();\n const resumeSessionId = await loadResumeSessionId(before.activeRunId) ?? undefined;\n await spawnFlywheelAgent(before.activeRunId, {\n workspace: launch.workspace,\n briefPath: brief.absolutePath,\n model: roleConfig.model,\n harness: roleConfig.harness,\n effort: roleConfig.effort,\n minAgents: roleConfig.minAgents,\n maxAgents: roleConfig.maxAgents,\n scope: roleConfig.scope,\n resumeSessionId,\n });\n await setPaused(false);\n return { before, after: await readGateSnapshot(), changed: true };\n}\n\nfunction getPlatformOpenCommand(): string {\n switch (process.platform) {\n case 'linux': return 'xdg-open';\n case 'darwin': return 'open';\n case 'win32': return 'explorer';\n default: throw new Error(`Opening files is not supported on ${process.platform}`);\n }\n}\n\nfunction openPathDetached(path: string): void {\n const child = spawn(getPlatformOpenCommand(), [path], {\n detached: true,\n stdio: 'ignore',\n });\n child.unref();\n}\n\nasync function resolveReportOpenRunId(runId: string | undefined): Promise<string> {\n if (runId) return runId;\n const runs = await listFlywheelRuns();\n const run = runs.find((candidate) => candidate.status === 'complete') ?? runs[0];\n if (!run) throw new Error('no flywheel run report to open');\n return run.id;\n}\n\nexport async function openFlywheelRunReportForDashboard(options: ReportOpenOptions = {}): Promise<{ runId: string; path: string }> {\n const runId = await resolveReportOpenRunId(options.runId);\n const detail = await getFlywheelRunDetail(runId);\n if (!detail) throw new Error(`Flywheel run not found: ${runId}`);\n if (!detail.paths.report) throw new Error(`No report exists for ${runId}`);\n await (options.opener ?? openPathDetached)(detail.paths.report);\n return { runId, path: detail.paths.report };\n}\n\nexport async function readCurrentFlywheelStatusForDashboard(): Promise<FlywheelStatus | null> {\n // Use the self-healing resolver so the dashboard \"current run\" surface and\n // the CLI's `pan flywheel status` agree (PAN-1245).\n const activeRunId = await resolveLiveFlywheelRunId();\n if (!activeRunId) return null;\n const activeRun = await getFlywheelRunDetail(activeRunId);\n return activeRun?.status === 'running' ? activeRun.latest : null;\n}\n","/**\n * UAT candidate branch naming (PAN-1691) — codename + short date.\n *\n * One candidate branch is assembled per label/day and force-reset on rebuild.\n * Format: `uat/<label>-<codename>-<MMDD>`, e.g. `uat/pan-otter-0609`. Codenames\n * are short, memorable words you can say out loud (\"ship the otter candidate\").\n *\n * The core is pure and fully injectable (wordlist / index pick / collision\n * date) so it's deterministically testable. The default codename index is a\n * stable hash of label + day so rebuilds for the same day reuse the same branch.\n */\n\nexport const UAT_CODENAMES: readonly string[] = [\n 'otter', 'falcon', 'cedar', 'quartz', 'ember', 'marlin', 'willow', 'cobalt',\n 'sable', 'heron', 'onyx', 'larch', 'perch', 'flint', 'dune', 'reef', 'vale',\n 'crow', 'moss', 'thorn', 'birch', 'koi', 'slate', 'wren',\n];\n\nfunction mmdd(dateIso: string): string {\n const [, month, day] = dateIso.slice(0, 10).split('-');\n return `${month ?? '00'}${day ?? '00'}`;\n}\n\nexport interface UatNameDeps {\n /** Project label, e.g. 'pan'. */\n label: string;\n /** ISO date string; only the YYYY-MM-DD prefix is used (→ MMDD). */\n dateIso: string;\n /** Injectable wordlist (default UAT_CODENAMES). */\n codenames?: readonly string[];\n /** Returns an index in [0, n) (default: stable hash of label + MMDD). */\n pick?: (n: number) => number;\n}\n\n/**\n * Build the deterministic `uat/<label>-<codename>-<MMDD>` branch name. Pure.\n */\nexport function makeUatCandidateName(deps: UatNameDeps): string {\n const day = mmdd(deps.dateIso);\n const words = deps.codenames ?? UAT_CODENAMES;\n const pick = deps.pick ?? ((n: number) => stableIndex(`${deps.label}-${day}`, n));\n const index = words.length > 0 ? pick(words.length) % words.length : 0;\n const word = words[index] ?? 'candidate';\n return `uat/${deps.label}-${word}-${day}`;\n}\n\nfunction stableIndex(seed: string, modulo: number): number {\n if (modulo <= 0) return 0;\n let hash = 2166136261;\n for (let i = 0; i < seed.length; i++) {\n hash ^= seed.charCodeAt(i);\n hash = Math.imul(hash, 16777619);\n }\n return (hash >>> 0) % modulo;\n}\n","/**\n * UAT generation assembly engine (PAN-1737: UAT batch trains).\n *\n * Builds one *generation* — a deterministic-per-day `uat/<label>-<codename>-<MMDD>` branch\n * off current main containing as many ready features as possible, merged in\n * queue order, with cross-feature conflicts resolved on the branch by the\n * injected `resolveConflict` hook (the assembly agent). A feature whose\n * conflict cannot be resolved is *held out* with a human-readable reason and\n * assembly continues — a single bad merge never blocks the batch.\n *\n * Each rebuild for the same label/day force-resets the same branch + persistent\n * worktree under `<projectRoot>/workspaces/`, keeping one authoritative current\n * UAT candidate instead of proliferating stale branches.\n *\n * Pure orchestrator: all git and store I/O is injected (see\n * uat-generation-deps.ts for the real wiring), so every path — happy,\n * conflict-resolved, held-out, failed — is unit-testable with fake deps.\n */\nimport { makeUatCandidateName } from './uat-candidate-name.js';\nimport type {\n UatGeneration,\n UatGenerationMember,\n UatGenerationStatus,\n} from '../overdeck/merge-sync.js';\n\n/** A feature eligible for bundling, in merge-queue order. */\nexport interface ReadyFeature {\n issueId: string;\n title: string;\n /** Feature branch, e.g. feature/pan-1704. */\n branch: string;\n pr?: number;\n prUrl?: string;\n /** Issue ids this feature's changed files overlap with (from computeMergeQueue). */\n conflictsWith?: readonly string[];\n}\n\n/** Context handed to the conflict-resolution hook while the merge is mid-conflict. */\nexport interface ConflictContext {\n feature: ReadyFeature;\n /** Members already merged onto the branch, in order. */\n mergedIssueIds: readonly string[];\n /** The already-merged members this feature is known to overlap with. */\n conflictingIssueIds: readonly string[];\n branchName: string;\n worktreePath: string;\n}\n\n/** Successful resolution: the hook committed the merge on the branch. */\nexport interface ConflictResolutionResult {\n files: string[];\n commitSha: string;\n}\n\nexport interface GenerationGitDeps {\n /** `git fetch origin main` and return the origin/main head SHA. */\n fetchMain(): Promise<string>;\n /** Create or reset the generation worktree branch off origin/main. */\n createWorktree(branchName: string, worktreePath: string): Promise<void>;\n /** Head SHA of a feature branch (origin-first). */\n branchHeadSha(branch: string): Promise<string>;\n /**\n * Merge a feature branch into the generation worktree. On conflict the\n * worktree is left MID-CONFLICT (the hook needs that state); the engine\n * decides whether to resolve or abort.\n */\n mergeBranch(featureBranch: string): Promise<\n { ok: true } | { ok: false; conflict: boolean; reason: string }\n >;\n /** `git merge --abort` in the generation worktree. */\n abortMerge(): Promise<void>;\n /** Push the generation branch to origin. */\n push(branchName: string): Promise<void>;\n}\n\nexport interface GenerationStorePort {\n /** Insert or reset the generation row at assembly start. */\n insert(gen: Omit<UatGeneration, 'createdAt' | 'updatedAt'>): void;\n update(\n name: string,\n patch: Partial<Pick<UatGeneration, 'status' | 'baseSha' | 'members' | 'heldOut' | 'resolutions' | 'cleanedAt'>>,\n ): void;\n /** Existing generation names. */\n listNames(): string[];\n listChain(projectRoot: string, statuses?: readonly UatGenerationStatus[]): UatGeneration[];\n}\n\nexport interface AssembleGenerationInput {\n projectRoot: string;\n /** Project label for the codename, e.g. 'pan'. */\n label: string;\n /** ISO date; MMDD goes into the branch name. */\n dateIso: string;\n /** Ready features in merge-queue order. */\n features: readonly ReadyFeature[];\n /** Branch names (beyond the store) already present, e.g. live git refs. */\n takenBranchNames?: readonly string[];\n}\n\nexport interface AssembleGenerationDeps {\n git: GenerationGitDeps;\n store: GenerationStorePort;\n /**\n * The assembly agent. Called mid-conflict; must either conclude the merge\n * with a commit (returning what it resolved) or return null to give up.\n * Absent hook = every conflict is held out.\n */\n resolveConflict?: (ctx: ConflictContext) => Promise<ConflictResolutionResult | null>;\n log?: (msg: string) => void;\n}\n\n/** Branch name → worktree folder name (slashes collapse to dashes). */\nexport function generationFolderName(branchName: string): string {\n return branchName.replace(/[^a-zA-Z0-9._-]+/g, '-');\n}\n\n/**\n * Assemble the next generation from the given ready set. Always returns the\n * generation row (status 'ready' or 'failed'); throws only on store failures.\n * On success, older 'ready' generations for the project flip to 'superseded'.\n */\nexport async function assembleUatGeneration(\n input: AssembleGenerationInput,\n deps: AssembleGenerationDeps,\n): Promise<UatGeneration> {\n const log = deps.log ?? (() => {});\n\n const baseSha = await deps.git.fetchMain();\n const name = makeUatCandidateName({\n label: input.label,\n dateIso: input.dateIso,\n });\n const worktreePath = `${input.projectRoot}/workspaces/${generationFolderName(name)}`;\n\n deps.store.insert({\n name,\n worktreePath,\n projectRoot: input.projectRoot,\n baseSha,\n status: 'assembling',\n members: [],\n heldOut: [],\n resolutions: [],\n stackStartedAt: null,\n cleanedAt: null,\n });\n\n const members: UatGeneration['members'] = [];\n const heldOut: UatGeneration['heldOut'] = [];\n const resolutions: UatGeneration['resolutions'] = [];\n const finish = (status: UatGenerationStatus): UatGeneration => {\n deps.store.update(name, { status, members, heldOut, resolutions });\n return {\n name, worktreePath, projectRoot: input.projectRoot, baseSha,\n status, members, heldOut, resolutions,\n stackStartedAt: null, cleanedAt: null, createdAt: '', updatedAt: '',\n };\n };\n\n try {\n await deps.git.createWorktree(name, worktreePath);\n } catch (err) {\n log(`[uat-generation] ${name}: worktree creation failed: ${err instanceof Error ? err.message : String(err)}`);\n return finish('failed');\n }\n\n for (const feature of input.features) {\n const mergedIssueIds = members.map((m) => m.issueId);\n const attemptedHeadSha = async () => deps.git.branchHeadSha(feature.branch).catch(() => 'unknown');\n const recordMember = async (): Promise<UatGenerationMember> => ({\n issueId: feature.issueId,\n title: feature.title,\n branch: feature.branch,\n headSha: await attemptedHeadSha(),\n mergeOrder: members.length + 1,\n ...(feature.pr !== undefined ? { pr: feature.pr } : {}),\n ...(feature.prUrl !== undefined ? { prUrl: feature.prUrl } : {}),\n });\n const holdOut = async (reason: string): Promise<void> => {\n heldOut.push({ issueId: feature.issueId, branch: feature.branch, headSha: await attemptedHeadSha(), reason });\n };\n\n let result: Awaited<ReturnType<GenerationGitDeps['mergeBranch']>>;\n try {\n result = await deps.git.mergeBranch(feature.branch);\n } catch (err) {\n await deps.git.abortMerge().catch(() => {});\n await holdOut(`merge failed: ${err instanceof Error ? err.message.split('\\n')[0] : String(err)}`);\n deps.store.update(name, { members, heldOut, resolutions });\n continue;\n }\n\n if (result.ok) {\n members.push(await recordMember());\n deps.store.update(name, { members, heldOut, resolutions });\n continue;\n }\n\n if (!result.conflict || !deps.resolveConflict) {\n await deps.git.abortMerge().catch(() => {});\n await holdOut(result.conflict\n ? `conflicts with ${conflictingWith(feature, mergedIssueIds).join(', ') || 'an earlier member'} — no assembly agent available`\n : result.reason);\n deps.store.update(name, { members, heldOut, resolutions });\n continue;\n }\n\n const conflictingIssueIds = conflictingWith(feature, mergedIssueIds);\n log(`[uat-generation] ${name}: resolving conflict ${feature.issueId} <-> ${conflictingIssueIds.join(', ') || '(unknown member)'}`);\n let resolution: ConflictResolutionResult | null = null;\n try {\n resolution = await deps.resolveConflict({\n feature,\n mergedIssueIds,\n conflictingIssueIds,\n branchName: name,\n worktreePath,\n });\n } catch (err) {\n log(`[uat-generation] ${name}: conflict agent threw: ${err instanceof Error ? err.message : String(err)}`);\n resolution = null;\n }\n\n if (resolution) {\n members.push(await recordMember());\n resolutions.push({\n issueIds: [feature.issueId, ...conflictingIssueIds],\n files: resolution.files,\n commitSha: resolution.commitSha,\n });\n } else {\n await deps.git.abortMerge().catch(() => {});\n await holdOut(`conflict with ${conflictingIssueIds.join(', ') || 'an earlier member'} could not be auto-resolved — waits for the next generation`);\n }\n deps.store.update(name, { members, heldOut, resolutions });\n }\n\n if (members.length === 0) {\n log(`[uat-generation] ${name}: nothing merged (${heldOut.length} held out) — marking failed`);\n return finish('failed');\n }\n\n try {\n await deps.git.push(name);\n } catch (err) {\n log(`[uat-generation] ${name}: push failed: ${err instanceof Error ? err.message : String(err)}`);\n return finish('failed');\n }\n\n // This generation is now the current one; older ready generations remain\n // testable but are no longer the freshest — flip them to superseded.\n for (const older of deps.store.listChain(input.projectRoot, ['ready'])) {\n if (older.name !== name) deps.store.update(older.name, { status: 'superseded' });\n }\n\n log(`[uat-generation] ${name}: ready — ${members.length} member(s), ${resolutions.length} resolution(s), ${heldOut.length} held out`);\n return finish('ready');\n}\n\nfunction conflictingWith(feature: ReadyFeature, mergedIssueIds: readonly string[]): string[] {\n const merged = new Set(mergedIssueIds.map((id) => id.toUpperCase()));\n return (feature.conflictsWith ?? []).filter((id) => merged.has(id.toUpperCase()));\n}\n\nexport interface GenerationCleanupDeps {\n store: GenerationStorePort;\n /** Remove the generation's worktree directory (idempotent). */\n removeWorktree(worktreePath: string): Promise<void>;\n /** Delete the generation branch locally and on origin (idempotent). */\n deleteBranch(branchName: string): Promise<void>;\n /** Tear down the generation's live stack if one is running. */\n teardownStack?: (generation: UatGeneration) => Promise<void>;\n log?: (msg: string) => void;\n}\n\n/**\n * Reap dead generations' branches/worktrees, and trim the live chain to the\n * newest `keep` (default 3). Trimmed superseded generations flip to\n * 'invalidated' (their branch is gone — no longer testable). Rows are never\n * deleted: the chain is the audit trail. Promoted generations keep their row\n * but lose branch/worktree/stack — their content lives on main now.\n */\nexport async function cleanupUatGenerations(\n projectRoot: string,\n deps: GenerationCleanupDeps,\n options: { keep?: number } = {},\n): Promise<void> {\n const log = deps.log ?? (() => {});\n const keep = options.keep ?? 3;\n\n const live = deps.store.listChain(projectRoot, ['ready', 'superseded']);\n const trimmed = live.slice(keep);\n const dead = deps.store.listChain(projectRoot, ['invalidated', 'promoted', 'failed'])\n .filter((gen) => !gen.cleanedAt);\n\n for (const gen of [...trimmed, ...dead]) {\n let cleaned = true;\n if (gen.stackStartedAt && deps.teardownStack) {\n await deps.teardownStack(gen).catch((err) => {\n cleaned = false;\n log(`[uat-generation] cleanup ${gen.name}: stack teardown failed: ${err instanceof Error ? err.message : String(err)}`);\n });\n }\n await deps.removeWorktree(gen.worktreePath).catch((err) => {\n cleaned = false;\n log(`[uat-generation] cleanup ${gen.name}: worktree removal failed: ${err instanceof Error ? err.message : String(err)}`);\n });\n await deps.deleteBranch(gen.name).catch((err) => {\n cleaned = false;\n log(`[uat-generation] cleanup ${gen.name}: branch deletion failed: ${err instanceof Error ? err.message : String(err)}`);\n });\n if (!cleaned) continue;\n const cleanedAt = new Date().toISOString();\n if (gen.status === 'ready' || gen.status === 'superseded') {\n deps.store.update(gen.name, { status: 'invalidated', cleanedAt });\n log(`[uat-generation] cleanup: trimmed ${gen.name} (beyond newest ${keep})`);\n } else {\n deps.store.update(gen.name, { cleanedAt });\n }\n }\n}\n","/**\n * Real git + store wiring for the UAT generation engine (PAN-1737).\n *\n * Generation worktrees are PERSISTENT under `<projectRoot>/workspaces/` — the\n * live UAT stack serves from them, and the deterministic daily folder name yields the Traefik\n * host (`uat-<label>-<codename>-<mmdd>.overdeck.localhost`) via the standard\n * FEATURE_FOLDER devcontainer template.\n *\n * Pure I/O — exercised live; the orchestrator (uat-generation-engine.ts) holds\n * the tested logic.\n */\nimport { execFile } from 'child_process';\nimport { isAbsolute, relative, resolve } from 'path';\nimport { promisify } from 'util';\nimport {\n insertUatGenerationSync,\n listUatGenerationNamesSync,\n listUatGenerationsSync,\n updateUatGenerationSync,\n} from '../overdeck/merge-sync.js';\nimport type { GenerationGitDeps, GenerationStorePort } from './uat-generation-engine.js';\n\nconst execFileAsync = promisify(execFile);\n\nconst runGit = (args: string[], cwd: string) =>\n execFileAsync('git', args, { cwd, maxBuffer: 16 * 1024 * 1024 });\n\nfunction safeBranchName(branchName: string, prefix: 'feature' | 'uat'): string {\n const pattern = prefix === 'feature'\n ? /^feature\\/[A-Za-z0-9][A-Za-z0-9._-]*$/\n : /^uat\\/[A-Za-z0-9][A-Za-z0-9._-]*$/;\n if (!pattern.test(branchName)) {\n throw new Error(`unsafe ${prefix} branch name: ${branchName}`);\n }\n return branchName;\n}\n\nfunction safeGenerationWorktreePath(projectRoot: string, worktreePath: string): string {\n const workspacesRoot = resolve(projectRoot, 'workspaces');\n const target = resolve(worktreePath);\n const rel = relative(workspacesRoot, target);\n if (rel === '' || rel.startsWith('..') || isAbsolute(rel)) {\n throw new Error(`unsafe UAT worktree path outside project workspaces: ${worktreePath}`);\n }\n return target;\n}\n\n/** Store port backed by uat-generations-db. */\nexport function buildUatGenerationStore(): GenerationStorePort {\n return {\n insert: (gen) => { insertUatGenerationSync(gen); },\n update: (name, patch) => { updateUatGenerationSync(name, patch); },\n listNames: () => listUatGenerationNamesSync(),\n listChain: (projectRoot, statuses) =>\n listUatGenerationsSync({ projectRoot, ...(statuses ? { statuses } : {}) }),\n };\n}\n\n/**\n * Git deps for one assembly run. The worktree path is bound on createWorktree\n * and reused by the merge/push operations that follow.\n */\nexport function buildUatGenerationGitDeps(projectRoot: string): GenerationGitDeps {\n let worktreePath = '';\n\n return {\n fetchMain: async () => {\n await runGit(['fetch', 'origin', 'main'], projectRoot);\n const { stdout } = await runGit(['rev-parse', 'origin/main'], projectRoot);\n return stdout.trim();\n },\n\n createWorktree: async (branchName, path) => {\n const safeBranch = safeBranchName(branchName, 'uat');\n worktreePath = safeGenerationWorktreePath(projectRoot, path);\n // A leftover worktree at this path means a previous assembly of the SAME\n // name crashed mid-build (names are collision-checked) — reclaim it.\n await runGit(['worktree', 'remove', '--force', worktreePath], projectRoot).catch(() => {});\n await runGit(['worktree', 'prune'], projectRoot).catch(() => {});\n await runGit(['worktree', 'add', '-B', safeBranch, worktreePath, 'origin/main'], projectRoot);\n },\n\n branchHeadSha: async (branch) => {\n const safeBranch = safeBranchName(branch, 'feature');\n const tryRef = async (ref: string) => (await runGit(['rev-parse', ref], projectRoot)).stdout.trim();\n return tryRef(`origin/${safeBranch}`).catch(() => tryRef(safeBranch));\n },\n\n mergeBranch: async (featureBranch) => {\n // Prefer the origin ref — feature branches are pushed by work agents and\n // the local ref may lag.\n const safeFeatureBranch = safeBranchName(featureBranch, 'feature');\n const originRef = `origin/${safeFeatureBranch}`;\n const ref = await runGit(['rev-parse', '--verify', originRef], worktreePath)\n .then(() => originRef)\n .catch(() => safeFeatureBranch);\n try {\n await runGit(['merge', '--no-edit', ref], worktreePath);\n return { ok: true as const };\n } catch (err) {\n const { stdout } = await runGit(['ls-files', '-u'], worktreePath).catch(() => ({ stdout: '' }));\n const conflict = stdout.trim().length > 0;\n // Leave the worktree mid-conflict — the engine decides whether the\n // assembly agent gets a shot before aborting.\n return {\n ok: false as const,\n conflict,\n reason: err instanceof Error ? (err.message.split('\\n')[0] ?? 'merge failed') : String(err),\n };\n }\n },\n\n abortMerge: async () => {\n await runGit(['merge', '--abort'], worktreePath).catch(() => {});\n },\n\n push: async (branchName) => {\n await runGit(['push', '-u', '--force-with-lease', 'origin', safeBranchName(branchName, 'uat')], worktreePath);\n },\n };\n}\n\n/** Branch names currently present on origin under uat/* — naming collision input. */\nexport async function listRemoteUatBranches(projectRoot: string): Promise<string[]> {\n const { stdout } = await runGit(['ls-remote', '--heads', 'origin', 'uat/*'], projectRoot).catch(() => ({ stdout: '' }));\n return stdout\n .split('\\n')\n .map((line) => line.split('\\t')[1] ?? '')\n .filter((ref) => ref.startsWith('refs/heads/'))\n .map((ref) => ref.slice('refs/heads/'.length));\n}\n\n/** Cleanup deps: remove a generation worktree and delete its branch everywhere. */\nexport function buildUatGenerationCleanupGit(projectRoot: string): {\n removeWorktree(worktreePath: string): Promise<void>;\n deleteBranch(branchName: string): Promise<void>;\n} {\n return {\n removeWorktree: async (path) => {\n const safePath = safeGenerationWorktreePath(projectRoot, path);\n await runGit(['worktree', 'remove', '--force', safePath], projectRoot).catch(() => {});\n await runGit(['worktree', 'prune'], projectRoot).catch(() => {});\n },\n deleteBranch: async (branchName) => {\n const safeBranch = safeBranchName(branchName, 'uat');\n await runGit(['branch', '-D', safeBranch], projectRoot).catch(() => {});\n await runGit(['push', 'origin', '--delete', safeBranch], projectRoot).catch(() => {});\n },\n };\n}\n","/**\n * Assembly-agent conflict resolution (PAN-1737: UAT batch trains).\n *\n * Implements the engine's `resolveConflict` hook: when merging a feature onto\n * a UAT generation branch conflicts, a TIMEBOXED headless agent run is given\n * the mid-conflict worktree with one mission — resolve THIS merge's conflict\n * markers, changing nothing beyond the resolution.\n *\n * Division of labor, deliberately strict:\n * - The agent only EDITS FILES (headless `claude -p` with acceptEdits; no\n * shell access). It cannot stage, commit, push, or wander.\n * - This module verifies the result in code (no leftover conflict markers,\n * no unmerged index entries), stages, and concludes the merge commit.\n * - Any failure — agent missing, timeout, markers left, commit rejected —\n * returns null; the engine then aborts the merge and holds the feature\n * out. The assembly NEVER wedges on a conflict.\n *\n * The merge commit subject keeps git's standard `Merge branch ...` form (it\n * survives commitlint's default-ignores when this history is later promoted\n * to main); the auditable `uat-assembly:` marker lives in the body.\n */\nimport { execFile } from 'child_process';\nimport { promisify } from 'util';\nimport type { ConflictContext, ConflictResolutionResult } from './uat-generation-engine.js';\n\nconst execFileAsync = promisify(execFile);\n\nexport const DEFAULT_CONFLICT_TIMEOUT_MS = 5 * 60 * 1000;\n\n/** Injectable I/O so the hook's decision logic is unit-testable. */\nexport interface ConflictAgentDeps {\n /** Paths with unmerged index entries (`git diff --name-only --diff-filter=U`). */\n listConflictedFiles(cwd: string): Promise<string[]>;\n /** Staged paths before/after the agent run (`git diff --cached --name-only`). */\n stagedFiles(cwd: string): Promise<string[]>;\n /** Run the headless resolution agent; throws on spawn failure or timeout. */\n runAgent(args: { prompt: string; cwd: string; timeoutMs: number }): Promise<void>;\n /** Subset of `files` still containing conflict markers after the agent ran. */\n filesWithConflictMarkers(cwd: string, files: readonly string[]): Promise<string[]>;\n /** Unstaged or untracked paths outside the conflicted-file allowlist. */\n unsafeChangedFiles(cwd: string, allowedFiles: readonly string[]): Promise<string[]>;\n /** Discard untrusted worktree/untracked changes outside the allowlist. */\n discardFiles(cwd: string, files: readonly string[]): Promise<void>;\n /** Stage only the conflicted files that the agent was allowed to edit. */\n stageFiles(cwd: string, files: readonly string[]): Promise<void>;\n /** True if unmerged index entries remain after staging. */\n hasUnmergedEntries(cwd: string): Promise<boolean>;\n /** Conclude the in-progress merge with the given message. */\n commitMerge(cwd: string, message: string): Promise<void>;\n headSha(cwd: string): Promise<string>;\n log?: (msg: string) => void;\n}\n\nexport interface ConflictAgentOptions {\n timeoutMs?: number;\n deps?: Partial<ConflictAgentDeps>;\n}\n\nfunction splitNul(stdout: string): string[] {\n return stdout.split('\\0').filter((p) => p.length > 0);\n}\n\nfunction allowlist(files: readonly string[]): ReadonlySet<string> {\n return new Set(files.map((f) => f.replace(/\\\\/g, '/')));\n}\n\nexport function buildConflictResolutionPrompt(ctx: ConflictContext, conflictedFiles: readonly string[]): string {\n const counterparts = ctx.conflictingIssueIds.length > 0\n ? ctx.conflictingIssueIds.join(', ')\n : 'an earlier batch member';\n return [\n `You are resolving a git merge conflict inside a UAT batch assembly worktree.`,\n ``,\n `The branch ${ctx.branchName} bundles already-reviewed features for human testing.`,\n `Merging ${ctx.feature.branch} (${ctx.feature.issueId}: ${ctx.feature.title}) conflicted with ${counterparts}.`,\n ``,\n `Conflicted files:`,\n ...conflictedFiles.map((f) => ` - ${f}`),\n ``,\n `Your ONLY job: edit these files to resolve every conflict marker (<<<<<<<, =======, >>>>>>>)`,\n `so that BOTH features' intended behavior is preserved. Both sides passed review —`,\n `combine them; do not pick one side wholesale unless the changes are genuinely identical in intent.`,\n ``,\n `Rules:`,\n `- Change NOTHING beyond what resolving the conflict requires.`,\n `- Do not refactor, reformat, or \"improve\" surrounding code.`,\n `- Do not run git commands; the caller stages and commits.`,\n `- If the conflict cannot be safely resolved, leave the markers in place and say so.`,\n ].join('\\n');\n}\n\nfunction buildMergeCommitMessage(ctx: ConflictContext, files: readonly string[]): string {\n const pair = [ctx.feature.issueId, ...ctx.conflictingIssueIds].join(' <-> ');\n return [\n `Merge branch '${ctx.feature.branch}' into ${ctx.branchName}`,\n ``,\n `uat-assembly: resolve ${pair}`,\n `files: ${files.join(', ')}`,\n ].join('\\n');\n}\n\nfunction defaultDeps(): ConflictAgentDeps {\n const run = (cmd: string, args: string[], cwd: string, timeoutMs?: number) =>\n execFileAsync(cmd, args, { cwd, maxBuffer: 16 * 1024 * 1024, ...(timeoutMs ? { timeout: timeoutMs } : {}) });\n\n return {\n listConflictedFiles: async (cwd) => {\n const { stdout } = await run('git', ['diff', '--name-only', '--diff-filter=U'], cwd);\n return stdout.split('\\n').map((l) => l.trim()).filter(Boolean);\n },\n stagedFiles: async (cwd) => {\n const { stdout } = await run('git', ['diff', '--cached', '--name-only', '-z'], cwd);\n return splitNul(stdout);\n },\n runAgent: async ({ prompt, cwd, timeoutMs }) => {\n await run('claude', ['-p', prompt, '--permission-mode', 'acceptEdits'], cwd, timeoutMs);\n },\n filesWithConflictMarkers: async (cwd, files) => {\n if (files.length === 0) return [];\n // -l: names only; -E for the alternation. grep exits 1 on no matches.\n const { stdout } = await execFileAsync(\n 'grep', ['-lE', '^(<<<<<<<|=======$|>>>>>>>)', '--', ...files],\n { cwd, maxBuffer: 16 * 1024 * 1024 },\n ).catch((err: { code?: number; stdout?: string }) => {\n if (err && err.code === 1) return { stdout: '' };\n throw err;\n });\n return stdout.split('\\n').map((l) => l.trim()).filter(Boolean);\n },\n unsafeChangedFiles: async (cwd, files) => {\n const allowed = allowlist(files);\n const [{ stdout: unstaged }, { stdout: untracked }] = await Promise.all([\n run('git', ['diff', '--name-only', '-z'], cwd),\n run('git', ['ls-files', '--others', '--exclude-standard', '-z'], cwd),\n ]);\n return [...new Set([...splitNul(unstaged), ...splitNul(untracked)])]\n .filter((file) => !allowed.has(file.replace(/\\\\/g, '/')));\n },\n discardFiles: async (cwd, files) => {\n if (files.length === 0) return;\n await run('git', ['restore', '--staged', '--worktree', '--', ...files], cwd).catch(() => {});\n await run('git', ['clean', '-fd', '--', ...files], cwd).catch(() => {});\n },\n stageFiles: async (cwd, files) => {\n if (files.length === 0) return;\n await run('git', ['add', '--', ...files], cwd);\n },\n hasUnmergedEntries: async (cwd) => {\n const { stdout } = await run('git', ['ls-files', '-u'], cwd);\n return stdout.trim().length > 0;\n },\n commitMerge: async (cwd, message) => {\n await run('git', ['commit', '-m', message], cwd);\n },\n headSha: async (cwd) => (await run('git', ['rev-parse', 'HEAD'], cwd)).stdout.trim(),\n };\n}\n\n/**\n * Build the engine's `resolveConflict` hook. Returns the resolution on\n * success; null on ANY failure (the engine aborts the merge and holds the\n * feature out — never throws into the assembly loop).\n */\nexport function buildConflictAgentHook(\n options: ConflictAgentOptions = {},\n): (ctx: ConflictContext) => Promise<ConflictResolutionResult | null> {\n const deps: ConflictAgentDeps = { ...defaultDeps(), ...(options.deps ?? {}) };\n const timeoutMs = options.timeoutMs ?? DEFAULT_CONFLICT_TIMEOUT_MS;\n const log = deps.log ?? (() => {});\n\n return async (ctx) => {\n const tag = `[uat-conflict-agent] ${ctx.branchName} ${ctx.feature.issueId}`;\n let files: string[];\n let stagedBefore: string[];\n try {\n files = await deps.listConflictedFiles(ctx.worktreePath);\n stagedBefore = await deps.stagedFiles(ctx.worktreePath);\n } catch (err) {\n log(`${tag}: could not inspect conflicted state: ${err instanceof Error ? err.message : String(err)}`);\n return null;\n }\n if (files.length === 0) {\n log(`${tag}: merge failed without unmerged paths — not a content conflict, giving up`);\n return null;\n }\n\n try {\n await deps.runAgent({\n prompt: buildConflictResolutionPrompt(ctx, files),\n cwd: ctx.worktreePath,\n timeoutMs,\n });\n } catch (err) {\n log(`${tag}: agent run failed (${err instanceof Error ? err.message.split('\\n')[0] : String(err)})`);\n return null;\n }\n\n try {\n const unsafe = await deps.unsafeChangedFiles(ctx.worktreePath, files);\n if (unsafe.length > 0) {\n log(`${tag}: agent touched non-conflicted path(s): ${unsafe.join(', ')}`);\n await deps.discardFiles(ctx.worktreePath, unsafe).catch((err) => {\n log(`${tag}: cleanup of non-conflicted path(s) failed: ${err instanceof Error ? err.message : String(err)}`);\n });\n return null;\n }\n\n const unresolved = await deps.filesWithConflictMarkers(ctx.worktreePath, files);\n if (unresolved.length > 0) {\n log(`${tag}: conflict markers remain in ${unresolved.join(', ')}`);\n return null;\n }\n await deps.stageFiles(ctx.worktreePath, files);\n if (await deps.hasUnmergedEntries(ctx.worktreePath)) {\n log(`${tag}: unmerged index entries remain after staging`);\n return null;\n }\n\n const stagedBeforeSet = new Set(stagedBefore);\n const allowed = allowlist(files);\n const stagedAfter = await deps.stagedFiles(ctx.worktreePath);\n const newlyStagedOutsideAllowlist = stagedAfter.filter((file) =>\n !stagedBeforeSet.has(file) && !allowed.has(file.replace(/\\\\/g, '/')),\n );\n if (newlyStagedOutsideAllowlist.length > 0) {\n log(`${tag}: cached diff includes non-conflicted path(s): ${newlyStagedOutsideAllowlist.join(', ')}`);\n return null;\n }\n\n await deps.commitMerge(ctx.worktreePath, buildMergeCommitMessage(ctx, files));\n const commitSha = await deps.headSha(ctx.worktreePath);\n log(`${tag}: resolved ${files.length} file(s) at ${commitSha.slice(0, 9)}`);\n return { files, commitSha };\n } catch (err) {\n log(`${tag}: verification/commit failed: ${err instanceof Error ? err.message.split('\\n')[0] : String(err)}`);\n return null;\n }\n };\n}\n","/**\n * UAT generation reconciler (PAN-1737: UAT batch trains).\n *\n * The loop that keeps \"always one batch ready\" true with zero human action.\n * Each tick (interval-driven from the dashboard server, or forced via the\n * API's rebuild action):\n *\n * 1. No-op unless the merge train is enabled and a flywheel run is active.\n * 2. Invalidate live generations that went stale — main advanced past their\n * baseSha, a member left the ready set, or a member branch gained\n * commits. Invalidation tears down the generation's live stack.\n * (Older SUBSET generations are NOT stale: a smaller batch off current\n * main remains perfectly testable and promotable.)\n * 3. If no live generation matches the current desired set, assemble the\n * next generation in the background. Single-flight per project; a failed\n * assembly for the SAME desired signature backs off before retrying.\n * 4. Trim/reap the chain (cleanup hook).\n *\n * Pure orchestration with injected deps — interval wiring and real data\n * sources live in the dashboard server service.\n */\nimport type { ReadyFeature, GenerationStorePort } from './uat-generation-engine.js';\nimport type { UatGeneration } from '../overdeck/merge-sync.js';\n\n/**\n * An 'assembling' row with NO PROGRESS for this long is marked failed (train\n * un-wedge). Keyed on updated_at — the engine patches the row after every\n * member step — so a big batch grinding through many timeboxed conflict\n * resolutions is never killed for total age (the first live run killed a\n * slow-but-alive 5-member assembly at a flat 30min of created_at age; with\n * many developers driving hundreds of agents, assemblies will only get\n * bigger). A crashed engine stops updating the row and trips this within the\n * hour. Cooperative cancellation of the in-flight engine is PAN-1755.\n */\nexport const STUCK_ASSEMBLING_MS = 60 * 60 * 1000;\n/** Minimum age before re-attempting an assembly that failed for the same input. */\nexport const FAILED_RETRY_BACKOFF_MS = 10 * 60 * 1000;\n\nexport interface UatReconcilerDeps {\n /** Gate: flywheel.merge_train_enabled. */\n isEnabled(): boolean;\n /**\n * Current ready set in merge order, or null when it cannot be computed\n * (no active flywheel run) — null means \"do nothing\", not \"empty\".\n */\n getReadySet(): Promise<readonly ReadyFeature[] | null>;\n /** Current origin/main head SHA (fetching first). */\n getMainHeadSha(): Promise<string>;\n /** Current head SHA per feature branch (origin-first). */\n getBranchHeadSha(branch: string): Promise<string>;\n store: GenerationStorePort;\n /** Assemble the next generation from the desired set (the engine). */\n assemble(features: readonly ReadyFeature[]): Promise<UatGeneration>;\n /** Tear down a generation's live stack (no-op when none is running). */\n teardownStack(generation: UatGeneration): Promise<void>;\n /** Chain trim/reap (cleanupUatGenerations wiring). */\n cleanup(): Promise<void>;\n now?: () => number;\n log?: (msg: string) => void;\n}\n\nexport interface ReconcileResult {\n action: 'disabled' | 'no-queue' | 'idle' | 'assembled' | 'assembly-failed' | 'backoff' | 'in-flight';\n invalidated: string[];\n generation?: UatGeneration;\n}\n\n/**\n * Single-flight per project: a tick that arrives while one is running gets\n * 'in-flight' immediately instead of queuing (assemblies are minutes-long;\n * the interval would pile up).\n */\nconst inFlightReconciles = new Map<string, Promise<ReconcileResult>>();\n\n/** Stable signature of \"what we want assembled\" — backoff bookkeeping. */\nfunction desiredSignature(features: readonly ReadyFeature[], headShas: ReadonlyMap<string, string>, mainSha: string): string {\n const parts = [...features]\n .map((f) => `${f.issueId.toUpperCase()}@${headShas.get(f.branch) ?? 'unknown'}`)\n .sort();\n return `${mainSha}|${parts.join(',')}`;\n}\n\nfunction generationSignature(gen: UatGeneration): string {\n const parts = [\n ...gen.members.map((m) => `${m.issueId.toUpperCase()}@${m.headSha}`),\n ...gen.heldOut.map((h) => `${h.issueId.toUpperCase()}@${h.headSha ?? 'held'}`),\n ].sort();\n return `${gen.baseSha}|${parts.join(',')}`;\n}\n\nfunction liveSignatureMatches(\n gen: UatGeneration,\n features: readonly ReadyFeature[],\n headShas: ReadonlyMap<string, string>,\n mainSha: string,\n): boolean {\n if (gen.baseSha !== mainSha) return false;\n const desiredIds = new Set(features.map((f) => f.issueId.toUpperCase()));\n const genIds = new Set([\n ...gen.members.map((m) => m.issueId.toUpperCase()),\n ...gen.heldOut.map((h) => h.issueId.toUpperCase()),\n ]);\n if (desiredIds.size !== genIds.size) return false;\n for (const id of desiredIds) if (!genIds.has(id)) return false;\n for (const member of gen.members) {\n const current = headShas.get(member.branch);\n if (current !== member.headSha) return false;\n }\n for (const held of gen.heldOut) {\n if (!held.branch || !held.headSha) return false;\n const current = headShas.get(held.branch);\n if (current !== held.headSha) return false;\n }\n return true;\n}\n\n/**\n * A live generation is INVALIDATED only when its own CONTENT can no longer be\n * trusted for promotion: a bundled member left the ready queue (its work must\n * not merge) or a member's branch gained commits (the tested tree is outdated).\n *\n * Deliberately NOT invalidation triggers (the first live run, 2026-06-10,\n * churned every ready batch to death within minutes):\n * - main advancing past baseSha — an active flywheel run lands strike commits\n * on main continuously; the batch tree is still perfectly testable. Base\n * movement makes liveSignatureMatches() fail, so a FRESH superset assembles\n * and supersedes this one when ready; promotion safety against a moved base\n * is enforced at promote time (member-file overlap check + clean merge).\n * - held-out drift (departed / branch moved / metadata missing) — held-out\n * features are not in the tree; their changes also flow through\n * liveSignatureMatches() into a fresh assembly, never destruction of a\n * testable batch.\n */\nfunction isStale(\n gen: UatGeneration,\n desiredIds: ReadonlySet<string>,\n headShas: ReadonlyMap<string, string>,\n): string | null {\n for (const member of gen.members) {\n if (!desiredIds.has(member.issueId.toUpperCase())) {\n return `${member.issueId} left the ready queue`;\n }\n const current = headShas.get(member.branch);\n if (current !== member.headSha) {\n return `${member.issueId} branch gained commits`;\n }\n }\n return null;\n}\n\nexport async function reconcileUatGenerations(\n projectRoot: string,\n deps: UatReconcilerDeps,\n options: { force?: boolean } = {},\n): Promise<ReconcileResult> {\n const log = deps.log ?? (() => {});\n const now = deps.now ?? Date.now;\n const invalidated: string[] = [];\n\n if (!deps.isEnabled()) return { action: 'disabled', invalidated };\n\n if (inFlightReconciles.has(projectRoot)) return { action: 'in-flight', invalidated };\n\n const tick = async (): Promise<ReconcileResult> => {\n const readySet = await deps.getReadySet();\n if (readySet === null) return { action: 'no-queue', invalidated };\n\n const mainSha = await deps.getMainHeadSha();\n const headShas = new Map<string, string>();\n for (const f of readySet) {\n headShas.set(f.branch, await deps.getBranchHeadSha(f.branch).catch(() => 'unknown'));\n }\n const desiredIds = new Set(readySet.map((f) => f.issueId.toUpperCase()));\n\n // 1. Invalidate stale live generations (stack teardown included).\n for (const gen of deps.store.listChain(projectRoot, ['ready', 'superseded'])) {\n const staleReason = isStale(gen, desiredIds, headShas);\n if (!staleReason) continue;\n log(`[uat-reconciler] invalidating ${gen.name}: ${staleReason}`);\n deps.store.update(gen.name, { status: 'invalidated' });\n invalidated.push(gen.name);\n await deps.teardownStack(gen).catch((err) => {\n log(`[uat-reconciler] stack teardown for ${gen.name} failed: ${err instanceof Error ? err.message : String(err)}`);\n });\n }\n\n // 2. Un-wedge crashed assemblies; respect a live one. \"Stuck\" means NO\n // PROGRESS (updated_at stale) — the engine patches the row after every\n // member step, so total age is the wrong signal: a big batch grinding\n // through many timeboxed conflict resolutions is alive, just slow.\n const assembling = deps.store.listChain(projectRoot, ['assembling']);\n for (const gen of assembling) {\n const lastProgress = Date.parse(gen.updatedAt || gen.createdAt || '');\n // Unparseable timestamps: freshness can't be proven — treat as stuck.\n const sinceProgress = Number.isFinite(lastProgress) ? now() - lastProgress : Number.POSITIVE_INFINITY;\n if (sinceProgress > STUCK_ASSEMBLING_MS) {\n log(`[uat-reconciler] marking stuck assembling generation ${gen.name} failed (no progress for ${Math.round(sinceProgress / 60000)}m)`);\n deps.store.update(gen.name, { status: 'failed' });\n }\n }\n if (!options.force && deps.store.listChain(projectRoot, ['assembling']).length > 0) {\n return { action: 'in-flight', invalidated };\n }\n\n if (readySet.length === 0) {\n await deps.cleanup().catch(() => {});\n return { action: 'idle', invalidated };\n }\n\n // 3. Assemble when nothing live answers the current desired set.\n const desired = desiredSignature(readySet, headShas, mainSha);\n if (!options.force) {\n const live = deps.store.listChain(projectRoot, ['ready', 'superseded']);\n if (live.some((gen) => liveSignatureMatches(gen, readySet, headShas, mainSha))) {\n await deps.cleanup().catch(() => {});\n return { action: 'idle', invalidated };\n }\n const failed = deps.store.listChain(projectRoot, ['failed']);\n const recentFailure = failed.find((gen) =>\n generationSignature(gen) === desired &&\n now() - Date.parse(gen.updatedAt || gen.createdAt || '') < FAILED_RETRY_BACKOFF_MS,\n );\n if (recentFailure) {\n log(`[uat-reconciler] backing off — ${recentFailure.name} failed recently for the same input`);\n return { action: 'backoff', invalidated };\n }\n }\n\n log(`[uat-reconciler] assembling next generation for ${readySet.length} ready feature(s)`);\n const generation = await deps.assemble(readySet);\n await deps.cleanup().catch(() => {});\n return {\n action: generation.status === 'ready' ? 'assembled' : 'assembly-failed',\n invalidated,\n generation,\n };\n };\n\n const pending = tick().finally(() => inFlightReconciles.delete(projectRoot));\n inFlightReconciles.set(projectRoot, pending);\n return pending;\n}\n","/**\n * Live UAT stack lifecycle for batch generations (PAN-1737, absorbs PAN-1738).\n *\n * Each ready generation can serve a real dashboard stack from its persistent\n * worktree: the devcontainer renders via the standard FEATURE_FOLDER template,\n * so the folder name (`uat-<label>-<codename>-<mmdd>`) yields the Traefik host\n * `uat-<label>-<codename>-<mmdd>.overdeck.localhost` with zero new infra.\n *\n * HARD INVARIANT — max 2 UAT stacks run concurrently. Docker's default\n * address pool fits ~31 bridge networks; accumulating UAT stacks would\n * eventually block ALL workspace creation (\"all predefined address pools\n * have been fully subnetted\"). Starting a third stack tears down the oldest\n * first, and invalidation/promotion always tear the generation's stack down.\n *\n * All process exec is async (server-reachable code — never execSync).\n */\nimport { existsSync } from 'fs';\nimport { join, dirname, basename } from 'path';\nimport { readFile } from 'node:fs/promises';\nimport { execFile } from 'child_process';\nimport { promisify } from 'util';\nimport { ensureDevcontainerSync } from '../workspace/ensure-devcontainer.js';\nimport {\n listUatGenerationsWithStacksSync,\n setUatGenerationStackStartedAtSync,\n type UatGeneration,\n} from '../overdeck/merge-sync.js';\n\nconst execFileAsync = promisify(execFile);\n\nexport const MAX_UAT_STACKS = 2;\n\nconst COMPOSE_FILES = [\n 'docker-compose.devcontainer.yml',\n 'docker-compose.yml',\n 'docker-compose.yaml',\n 'compose.yml',\n 'compose.yaml',\n];\n\nconst stackMutationLocks = new Map<string, Promise<void>>();\n\nasync function withStackMutationLock<T>(projectRoot: string, fn: () => Promise<T>): Promise<T> {\n const previous = stackMutationLocks.get(projectRoot) ?? Promise.resolve();\n let release!: () => void;\n const current = new Promise<void>((resolve) => { release = resolve; });\n const tail = previous.catch(() => {}).then(() => current);\n stackMutationLocks.set(projectRoot, tail);\n await previous.catch(() => {});\n try {\n return await fn();\n } finally {\n release();\n if (stackMutationLocks.get(projectRoot) === tail) stackMutationLocks.delete(projectRoot);\n }\n}\n\nexport interface UatStackDeps {\n /** Render `.devcontainer/` if missing; issueId only resolves project config. */\n ensureDevcontainer(workspacePath: string, issueId: string): { ok: boolean; error?: string };\n composeUp(composeFile: string, projectName: string): Promise<void>;\n composeDown(composeFile: string, projectName: string): Promise<void>;\n /** Number of running containers for the compose project. */\n composePsCount(composeFile: string, projectName: string): Promise<number>;\n findComposeFile(workspacePath: string): string | null;\n readComposeFile(composeFile: string): Promise<string>;\n store: {\n setStack(name: string, startedAt: string | null): void;\n listWithStacks(): UatGeneration[];\n };\n log?: (msg: string) => void;\n}\n\nfunction defaultDeps(): UatStackDeps {\n const compose = (args: string[], cwd: string) =>\n execFileAsync('docker', ['compose', ...args], {\n cwd,\n encoding: 'utf-8',\n timeout: 300_000,\n maxBuffer: 10 * 1024 * 1024,\n });\n\n return {\n ensureDevcontainer: (workspacePath, issueId) => {\n const result = ensureDevcontainerSync({ workspacePath, issueId });\n return result.step.success\n ? { ok: true }\n : { ok: false, error: result.step.error ?? 'devcontainer render failed' };\n },\n composeUp: async (composeFile, projectName) => {\n await compose(['-f', composeFile, '-p', projectName, 'up', '-d', '--build'], dirname(composeFile));\n },\n composeDown: async (composeFile, projectName) => {\n await compose(['-f', composeFile, '-p', projectName, 'down', '-v', '--remove-orphans'], dirname(composeFile));\n },\n composePsCount: async (composeFile, projectName) => {\n const { stdout } = await compose(['-f', composeFile, '-p', projectName, 'ps', '-q'], dirname(composeFile));\n return stdout.split('\\n').filter((l) => l.trim().length > 0).length;\n },\n findComposeFile: (workspacePath) => {\n const devcontainerDir = join(workspacePath, '.devcontainer');\n for (const file of COMPOSE_FILES) {\n const fullPath = join(devcontainerDir, file);\n if (existsSync(fullPath)) return fullPath;\n }\n return null;\n },\n readComposeFile: (composeFile) => readFile(composeFile, 'utf-8'),\n store: {\n setStack: (name, startedAt) => setUatGenerationStackStartedAtSync(name, startedAt),\n listWithStacks: () => listUatGenerationsWithStacksSync(),\n },\n };\n}\n\nexport function uatStackFolderName(gen: UatGeneration): string {\n return basename(gen.worktreePath);\n}\n\nfunction composeProjectName(gen: UatGeneration): string {\n return `overdeck-${uatStackFolderName(gen)}`;\n}\n\n/**\n * The frontend URL the generation's stack serves (or will serve). Prefers the\n * Host(`…`) Traefik label in the rendered compose file; falls back to the\n * FEATURE_FOLDER convention.\n */\nexport async function uatFrontendUrl(gen: UatGeneration, deps: Partial<UatStackDeps> = {}): Promise<string> {\n const d = { ...defaultDeps(), ...deps };\n const folder = uatStackFolderName(gen);\n const composeFile = d.findComposeFile(gen.worktreePath);\n if (composeFile) {\n try {\n const content = await d.readComposeFile(composeFile);\n const hostMatch = content.match(new RegExp('Host\\\\(`(' + folder.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&') + '\\\\.[^`]+)`\\\\)'));\n if (hostMatch?.[1]) return `https://${hostMatch[1]}`;\n } catch { /* fall through to convention */ }\n }\n return `https://${folder}.overdeck.localhost`;\n}\n\nexport type UatStackStatus = 'running' | 'absent';\n\nexport interface UatStackProbe {\n status: UatStackStatus;\n frontendUrl: string;\n}\n\n/** Probe a generation's stack. Self-corrects stale stackStartedAt records. */\nexport async function probeUatStack(gen: UatGeneration, deps: Partial<UatStackDeps> = {}): Promise<UatStackProbe> {\n const d = { ...defaultDeps(), ...deps };\n const frontendUrl = await uatFrontendUrl(gen, d);\n const composeFile = d.findComposeFile(gen.worktreePath);\n if (!composeFile || !gen.stackStartedAt) return { status: 'absent', frontendUrl };\n const count = await d.composePsCount(composeFile, composeProjectName(gen)).catch(() => 0);\n if (count === 0) {\n // Record says running but nothing is — heal the record.\n try { d.store.setStack(gen.name, null); } catch { /* row may be gone */ }\n return { status: 'absent', frontendUrl };\n }\n return { status: 'running', frontendUrl };\n}\n\nexport interface EnsureUatStackResult {\n success: boolean;\n error?: string;\n frontendUrl?: string;\n /** Generations whose stacks were torn down to respect MAX_UAT_STACKS. */\n evicted: string[];\n}\n\n/**\n * Bring up the live stack for a generation (idempotent — compose up on a\n * running project is a no-op). Enforces MAX_UAT_STACKS by tearing down the\n * oldest running UAT stack(s) first.\n */\nexport async function ensureUatStack(gen: UatGeneration, deps: Partial<UatStackDeps> = {}): Promise<EnsureUatStackResult> {\n return withStackMutationLock(gen.projectRoot, async () => {\n const d = { ...defaultDeps(), ...deps };\n const log = d.log ?? (() => {});\n const evicted: string[] = [];\n\n const member = gen.members[0];\n if (!member) return { success: false, error: 'generation has no members', evicted };\n\n // Cap enforcement happens inside the lock and re-reads the live stack set so\n // concurrent start requests cannot both observe the same stale pre-start set.\n const others = d.store.listWithStacks().filter((g) => g.name !== gen.name);\n while (others.length > 0 && others.length >= MAX_UAT_STACKS) {\n const oldest = others.shift()!;\n log(`[uat-stack] cap ${MAX_UAT_STACKS} reached — tearing down oldest stack ${oldest.name}`);\n try {\n await teardownUatStackUnlocked(oldest, d);\n } catch (err) {\n return {\n success: false,\n error: err instanceof Error ? (err.message.split('\\n')[0] ?? 'stack teardown failed') : String(err),\n evicted,\n };\n }\n evicted.push(oldest.name);\n }\n\n const rendered = d.ensureDevcontainer(gen.worktreePath, member.issueId);\n if (!rendered.ok) return { success: false, error: rendered.error ?? 'devcontainer render failed', evicted };\n\n const composeFile = d.findComposeFile(gen.worktreePath);\n if (!composeFile) {\n return { success: false, error: `no compose file under ${gen.worktreePath}/.devcontainer`, evicted };\n }\n\n try {\n await d.composeUp(composeFile, composeProjectName(gen));\n } catch (err) {\n return {\n success: false,\n error: err instanceof Error ? (err.message.split('\\n')[0] ?? 'compose up failed') : String(err),\n evicted,\n };\n }\n\n d.store.setStack(gen.name, new Date().toISOString());\n const frontendUrl = await uatFrontendUrl(gen, d);\n log(`[uat-stack] ${gen.name}: stack up at ${frontendUrl}`);\n return { success: true, frontendUrl, evicted };\n });\n}\n\nasync function teardownUatStackUnlocked(gen: UatGeneration, d: UatStackDeps): Promise<void> {\n const composeFile = d.findComposeFile(gen.worktreePath);\n if (composeFile) {\n try {\n await d.composeDown(composeFile, composeProjectName(gen));\n } catch (err) {\n d.log?.(`[uat-stack] ${gen.name}: compose down failed: ${err instanceof Error ? err.message.split('\\n')[0] : String(err)}`);\n throw err;\n }\n }\n try { d.store.setStack(gen.name, null); } catch { /* row may be gone */ }\n}\n\n/**\n * Tear the generation's stack down (idempotent) and clear its stack record.\n * MUST be called on invalidation, promotion, and generation cleanup —\n * orphaned uat networks eventually block all workspace creation.\n */\nexport async function teardownUatStack(gen: UatGeneration, deps: Partial<UatStackDeps> = {}): Promise<void> {\n await withStackMutationLock(gen.projectRoot, async () => {\n const d = { ...defaultDeps(), ...deps };\n await teardownUatStackUnlocked(gen, d);\n });\n}\n","import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nconst execFileAsync = promisify(execFile);\n\nexport async function reconcileFileSizeBaseline(\n worktreePath: string,\n runGit: (args: string[], cwd: string) => Promise<{ stdout: string }>,\n): Promise<{ changed: boolean }> {\n await execFileAsync('bash', ['scripts/lint-file-size.sh', '--update'], {\n cwd: worktreePath,\n maxBuffer: 16 * 1024 * 1024,\n });\n\n const { stdout } = await runGit(['status', '--porcelain', '--', 'scripts/file-size-baseline.txt'], worktreePath);\n if (stdout.trim() === '') return { changed: false };\n\n await runGit(['add', 'scripts/file-size-baseline.txt'], worktreePath);\n return { changed: true };\n}\n","/**\n * Batch promotion (PAN-1737: UAT batch trains) — \"merge what you tested\".\n *\n * Promoting a generation merges the uat/* branch itself into main (one no-ff\n * merge), so main receives EXACTLY the tree the operator exercised — including\n * the assembly agent's conflict resolutions. The deliberate exception is\n * file-size baseline reconciliation: scripts/file-size-baseline.txt is CI\n * metadata, so stale-high entries may be lowered or dropped before push.\n * Per-feature PRs are marked merged by GitHub automatically (their head commits\n * become reachable from main), and each member issue then flows through the\n * standard per-issue post-merge lifecycle exactly once, behind the PAN-328\n * in-flight guard.\n *\n * Hard precondition: the generation's baseSha must still equal origin/main —\n * promoting a stale tree would silently drop commits that landed since\n * assembly. Stale promotes are rejected with reassemble guidance (the\n * reconciler rebuilds automatically anyway).\n *\n * Pure orchestration with injected deps; real git in buildUatPromoteGitDeps.\n */\nimport { join } from 'path';\nimport { tmpdir } from 'os';\nimport { promisify } from 'util';\nimport { execFile } from 'child_process';\nimport type { UatGeneration, UatGenerationStatus } from '../overdeck/merge-sync.js';\nimport { reconcileFileSizeBaseline } from './file-size-reconcile.js';\nimport type { GenerationStorePort } from './uat-generation-engine.js';\n\nconst execFileAsync = promisify(execFile);\n\nfunction safeUatBranchName(branchName: string): string {\n if (!/^uat\\/[A-Za-z0-9][A-Za-z0-9._-]*$/.test(branchName)) {\n throw new Error(`unsafe UAT branch name: ${branchName}`);\n }\n return branchName;\n}\n\nexport interface UatPromoteGitDeps {\n /** `git fetch origin main` and return the origin/main head SHA. */\n fetchMain(): Promise<string>;\n /**\n * Merge the generation branch into main with the given message (no-ff, in a\n * throwaway worktree — NEVER the primary checkout) and push. Returns the\n * merge commit SHA.\n */\n mergeIntoMain(branchName: string, message: string): Promise<string>;\n /** Files changed on origin/main since the given SHA (base-movement footprint). */\n changedFilesSince(baseSha: string): Promise<string[]>;\n /** Files the batch branch changes vs its merge-base with origin/main. */\n batchChangedFiles(branchName: string): Promise<string[]>;\n}\n\nexport interface UatPromoteDeps {\n git: UatPromoteGitDeps;\n store: GenerationStorePort & { get(name: string): UatGeneration | null };\n teardownStack(generation: UatGeneration): Promise<void>;\n /**\n * Kick off the per-issue post-merge lifecycle (through the PAN-328 guard).\n * Returns false when a run for that issue is already in flight.\n */\n firePostMerge(issueId: string, options?: { sourceBranch?: string; verifiedMergedRef?: string }): boolean;\n /**\n * Authoritative per-member merge eligibility (PAN-1759), checked at promote\n * time as defense-in-depth: batch membership is computed from orchestrator\n * verbs at assembly time, and a member's pipeline state can change (or have\n * been wrong) between assembly and the operator clicking Merge. RUN-20\n * assembled a batch containing a mid-review issue; this gate refuses to land\n * it on main.\n */\n memberEligibility(issueId: string): { eligible: boolean; reason?: string };\n log?: (msg: string) => void;\n}\n\nexport type PromoteFailureReason = 'not-found' | 'wrong-status' | 'member-not-ready' | 'stale-base' | 'merge-failed';\n\nexport type PromoteResult =\n | {\n success: true;\n generation: string;\n mergeSha: string;\n members: string[];\n /** Members whose post-merge lifecycle started (false = already in flight). */\n postMergeStarted: string[];\n invalidated: string[];\n }\n | { success: false; reason: PromoteFailureReason; message: string };\n\nconst PROMOTABLE: readonly UatGenerationStatus[] = ['ready', 'superseded'];\n\nexport function buildPromoteMergeMessage(gen: UatGeneration): string {\n const ids = gen.members.map((m) => m.issueId).join(', ');\n const resolutionNote = gen.resolutions.length > 0\n ? `\\n\\nIncludes ${gen.resolutions.length} uat-assembly conflict resolution(s): ` +\n gen.resolutions.map((r) => r.issueIds.join(' <-> ')).join('; ')\n : '';\n return `Merge UAT batch ${gen.name} (${ids})${resolutionNote}`;\n}\n\nexport async function promoteUatGeneration(\n name: string,\n projectRoot: string,\n deps: UatPromoteDeps,\n): Promise<PromoteResult> {\n const log = deps.log ?? (() => {});\n\n const gen = deps.store.get(name);\n if (!gen || gen.projectRoot !== projectRoot) {\n return { success: false, reason: 'not-found', message: `No UAT generation named ${name}` };\n }\n if (!PROMOTABLE.includes(gen.status)) {\n return {\n success: false,\n reason: 'wrong-status',\n message: `${name} is ${gen.status} — only a ready or superseded batch can be merged to main`,\n };\n }\n\n const notReady = gen.members\n .map((m) => ({ issueId: m.issueId, gate: deps.memberEligibility(m.issueId) }))\n .filter(({ gate }) => !gate.eligible);\n if (notReady.length > 0) {\n const detail = notReady.map(({ issueId, gate }) => `${issueId} (${gate.reason ?? 'not eligible'})`).join(', ');\n return {\n success: false,\n reason: 'member-not-ready',\n message:\n `${name} contains member(s) the pipeline has not cleared to merge: ${detail} — ` +\n `wait for them to pass review+test, or for the reconciler to rebuild the batch without them.`,\n };\n }\n\n const mainSha = await deps.git.fetchMain();\n if (gen.baseSha !== mainSha) {\n // Main moved since assembly. An active flywheel run lands commits on main\n // continuously, so exact base equality would make ready batches almost\n // never promotable (first live run, 2026-06-10). Safety is preserved by a\n // narrower check: reject only when main's NEW commits touch files the\n // batch also touches — then the tested behavior genuinely may not match\n // what lands. Disjoint movement (docs, unrelated fixes) proceeds; the\n // no-ff merge itself still hard-fails on any textual conflict.\n const [mainChanged, batchChanged] = await Promise.all([\n deps.git.changedFilesSince(gen.baseSha),\n deps.git.batchChangedFiles(gen.name),\n ]);\n const batchSet = new Set(batchChanged);\n const overlap = mainChanged.filter((f) => batchSet.has(f));\n if (overlap.length > 0) {\n return {\n success: false,\n reason: 'stale-base',\n message:\n `${name} was assembled off ${gen.baseSha.slice(0, 9)} but main is now at ${mainSha.slice(0, 9)}, ` +\n `and the new commits touch ${overlap.length} file(s) this batch also changes ` +\n `(${overlap.slice(0, 3).join(', ')}${overlap.length > 3 ? ', …' : ''}) — ` +\n `the tree you tested may not match what would land. A fresh batch reassembles automatically; re-test before merging.`,\n };\n }\n log(`[uat-promote] ${name}: base moved ${gen.baseSha.slice(0, 9)} → ${mainSha.slice(0, 9)} with no member-file overlap — proceeding`);\n }\n\n let mergeSha: string;\n try {\n mergeSha = await deps.git.mergeIntoMain(gen.name, buildPromoteMergeMessage(gen));\n } catch (err) {\n return {\n success: false,\n reason: 'merge-failed',\n message: err instanceof Error ? (err.message.split('\\n')[0] ?? 'merge failed') : String(err),\n };\n }\n log(`[uat-promote] ${name}: merged to main at ${mergeSha.slice(0, 9)} (${gen.members.length} member(s))`);\n\n // The batch is on main: this generation is done, every other live\n // generation is stale by definition (main moved).\n deps.store.update(gen.name, { status: 'promoted' });\n await deps.teardownStack(gen).catch((err) => {\n log(`[uat-promote] ${name}: stack teardown failed: ${err instanceof Error ? err.message : String(err)}`);\n });\n\n const invalidated: string[] = [];\n for (const other of deps.store.listChain(projectRoot, ['ready', 'superseded'])) {\n if (other.name === gen.name) continue;\n deps.store.update(other.name, { status: 'invalidated' });\n invalidated.push(other.name);\n await deps.teardownStack(other).catch(() => {});\n }\n\n // Standard per-issue post-merge handoff, exactly once each (PAN-328 guard).\n const postMergeStarted: string[] = [];\n for (const member of gen.members) {\n if (deps.firePostMerge(member.issueId, {\n sourceBranch: member.branch,\n verifiedMergedRef: member.headSha || member.branch,\n })) postMergeStarted.push(member.issueId);\n else log(`[uat-promote] ${name}: post-merge for ${member.issueId} already in flight — skipped`);\n }\n\n return {\n success: true,\n generation: gen.name,\n mergeSha,\n members: gen.members.map((m) => m.issueId),\n postMergeStarted,\n invalidated,\n };\n}\n\n/** Real git wiring: throwaway detached worktree off origin/main, no-ff merge, baseline reconcile, push. */\nexport function buildUatPromoteGitDeps(projectRoot: string): UatPromoteGitDeps {\n const runGit = (args: string[], cwd: string) =>\n execFileAsync('git', args, { cwd, maxBuffer: 16 * 1024 * 1024 });\n\n return {\n fetchMain: async () => {\n await runGit(['fetch', 'origin', 'main'], projectRoot);\n return (await runGit(['rev-parse', 'origin/main'], projectRoot)).stdout.trim();\n },\n mergeIntoMain: async (branchName, message) => {\n const safeBranch = safeUatBranchName(branchName);\n const worktreePath = join(tmpdir(), `uat-promote-${safeBranch.replace(/[^a-z0-9]/gi, '-')}`);\n await runGit(['worktree', 'remove', '--force', worktreePath], projectRoot).catch(() => {});\n await runGit(['worktree', 'prune'], projectRoot).catch(() => {});\n await runGit(['worktree', 'add', '--detach', worktreePath, 'origin/main'], projectRoot);\n try {\n const originRef = `origin/${safeBranch}`;\n const ref = await runGit(['rev-parse', '--verify', originRef], worktreePath)\n .then(() => originRef)\n .catch(() => safeBranch);\n await runGit(['merge', '--no-ff', ref, '-m', message], worktreePath);\n const { changed } = await reconcileFileSizeBaseline(worktreePath, runGit);\n if (changed) await runGit(['commit', '--amend', '--no-edit'], worktreePath);\n await runGit(['push', 'origin', 'HEAD:main'], worktreePath);\n return (await runGit(['rev-parse', 'HEAD'], worktreePath)).stdout.trim();\n } finally {\n await runGit(['worktree', 'remove', '--force', worktreePath], projectRoot).catch(() => {});\n await runGit(['worktree', 'prune'], projectRoot).catch(() => {});\n }\n },\n changedFilesSince: async (baseSha) => {\n if (!/^[0-9a-f]{7,40}$/i.test(baseSha)) throw new Error(`unsafe base sha: ${baseSha}`);\n const { stdout } = await runGit(['diff', '--name-only', `${baseSha}..origin/main`], projectRoot);\n return stdout.split('\\n').map((l) => l.trim()).filter(Boolean);\n },\n batchChangedFiles: async (branchName) => {\n const safeBranch = safeUatBranchName(branchName);\n const ref = await runGit(['rev-parse', '--verify', `origin/${safeBranch}`], projectRoot)\n .then(() => `origin/${safeBranch}`)\n .catch(() => safeBranch);\n const mergeBase = (await runGit(['merge-base', 'origin/main', ref], projectRoot)).stdout.trim();\n const { stdout } = await runGit(['diff', '--name-only', `${mergeBase}..${ref}`], projectRoot);\n return stdout.split('\\n').map((l) => l.trim()).filter(Boolean);\n },\n };\n}\n","import { Effect } from 'effect';\nimport { messageAgent } from '../agents.js';\nimport { getFlywheelActiveRunId, isFlywheelGloballyPaused } from '../overdeck/control-settings.js';\nimport { sessionExists } from '../tmux.js';\nimport { recordDeaconNudge } from './deacon-nudge-log.js';\nimport { FLYWHEEL_ORCHESTRATOR_AGENT_ID } from './flywheel.js';\nimport type { PromoteResult } from './uat-promote.js';\n\ntype SessionExists = (name: string) => boolean | Promise<boolean>;\n\nexport interface NotifyDeps {\n getActiveRunId?: typeof getFlywheelActiveRunId;\n isPaused?: typeof isFlywheelGloballyPaused;\n sessionExists?: SessionExists;\n message?: typeof messageAgent;\n recordNudge?: typeof recordDeaconNudge;\n}\n\nfunction buildPromoteNudge(result: Extract<PromoteResult, { success: true }>): string {\n const members = result.members.length > 0 ? result.members.join(', ') : '(no members recorded)';\n return (\n `The operator just promoted UAT generation ${result.generation} to main at ${result.mergeSha}. ` +\n `Run a fresh Observe->Act loop NOW. Re-derive activePipeline from current source of truth and EXCLUDE ` +\n `the merged member(s): ${members}. Re-assemble a clean UAT batch with only members that are currently ` +\n `review+test passed, close out the promoted issue(s), emit a fresh status snapshot, and re-arm the next ` +\n `tick. Do NOT ask the operator a question, do NOT pause, and do NOT reuse the stale pre-promote ready set.`\n );\n}\n\nexport async function notifyFlywheelOfUatPromote(result: PromoteResult, deps: NotifyDeps = {}): Promise<void> {\n try {\n if (result.success !== true) return;\n\n const getActiveRunId = deps.getActiveRunId ?? getFlywheelActiveRunId;\n const isPaused = deps.isPaused ?? isFlywheelGloballyPaused;\n const sessionExistsDep = deps.sessionExists ?? ((name: string) => Effect.runPromise(sessionExists(name)));\n const message = deps.message ?? messageAgent;\n const recordNudge = deps.recordNudge ?? recordDeaconNudge;\n\n if (!getActiveRunId()) return;\n if (isPaused()) return;\n\n const delivered = await sessionExistsDep(FLYWHEEL_ORCHESTRATOR_AGENT_ID);\n if (delivered) {\n try {\n await message(FLYWHEEL_ORCHESTRATOR_AGENT_ID, buildPromoteNudge(result), 'uat-promote-notify');\n } catch {\n /* best-effort — the nudge log below still records that the session was present */\n }\n }\n\n recordNudge({\n patrol: 'uat-promote-notify',\n issueId: result.members[0] ?? result.generation,\n action: 'notified flywheel-orchestrator to re-derive ready set after UAT promote',\n reason:\n 'operator promoted a UAT batch; flywheel must immediately rebuild to drop merged + regressed members before its next tick',\n state: {\n generation: result.generation,\n members: result.members,\n mergeSha: result.mergeSha,\n delivered,\n },\n });\n } catch {\n /* best-effort — promote success must never be converted into a notification failure */\n }\n}\n","/**\n * UAT batch train service (PAN-1737) — real wiring for the reconciler,\n * assembly engine, stacks, and promote path, plus the payload builders the\n * flywheel routes expose.\n *\n * The reconciler interval is the heartbeat of \"always one batch ready\":\n * every 60s (gated per-tick on flywheel.merge_train_enabled, no-op without an\n * active flywheel run) it compares the ready set against the generation chain\n * and assembles/invalidates as needed. Assemblies run minutes — the reconciler\n * is single-flight per project, so ticks never pile up.\n */\nimport { stat } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { Effect } from 'effect';\nimport { layer as nodeServicesLayer } from '@effect/platform-node/NodeServices';\nimport {\n assembleUatGeneration,\n cleanupUatGenerations,\n type ReadyFeature,\n} from '../../../lib/cloister/uat-generation-engine.js';\nimport {\n buildUatGenerationGitDeps,\n buildUatGenerationStore,\n buildUatGenerationCleanupGit,\n listRemoteUatBranches,\n} from '../../../lib/cloister/uat-generation-deps.js';\nimport { buildConflictAgentHook } from '../../../lib/cloister/uat-conflict-agent.js';\nimport { reconcileUatGenerations, type ReconcileResult } from '../../../lib/cloister/uat-reconciler.js';\nimport { ensureUatStack, probeUatStack, teardownUatStack } from '../../../lib/cloister/uat-stack.js';\nimport {\n promoteUatGeneration,\n buildUatPromoteGitDeps,\n type PromoteResult,\n} from '../../../lib/cloister/uat-promote.js';\nimport { notifyFlywheelOfUatPromote } from '../../../lib/cloister/uat-promote-notify.js';\nimport {\n getUatGenerationSync,\n isMergeTrainEnabled,\n listUatGenerationsSync,\n type UatGeneration,\n} from '../../../lib/overdeck/merge-sync.js';\nimport { extractACFromDocument } from '../../../lib/vbrief/acceptance-criteria.js';\nimport { findVBriefByIssue, readVBriefDocument } from '../../../lib/vbrief/vbrief-index.js';\nimport { findProjectByPathSync } from '../../../lib/projects.js';\nimport { getDashboardIdentity } from '../identity.js';\nimport { readCurrentFlywheelStatusForDashboard } from './flywheel-actions.js';\n\nconst RECONCILE_INTERVAL_MS = 60_000;\nconst CHAIN_PAYLOAD_LIMIT = 10;\nconst ACCEPTANCE_CRITERIA_READ_CONCURRENCY = 4;\n\ntype AcceptanceCriteriaSummary = Array<{ title: string; status: string }>;\n\ninterface AcceptanceCriteriaCacheEntry {\n path: string;\n mtimeMs: number;\n criteria: AcceptanceCriteriaSummary;\n}\n\nconst acceptanceCriteriaByIssue = new Map<string, AcceptanceCriteriaCacheEntry>();\n\nexport function resolveUatProjectRoot(cwdPath = process.cwd()): string {\n const registeredProject = findProjectByPathSync(cwdPath);\n if (registeredProject) return resolve(registeredProject.path);\n\n const normalized = resolve(cwdPath);\n const workspaceMatch = normalized.match(/^(.*)\\/workspaces\\/feature-[^/]+(?:\\/.*)?$/i);\n return workspaceMatch?.[1] ? resolve(workspaceMatch[1]) : normalized;\n}\n\nfunction projectRoot(): string {\n return resolveUatProjectRoot();\n}\n\nexport function canStartUatTrainReconciler(): boolean {\n const identity = getDashboardIdentity();\n return identity.mode === 'primary' && resolve(identity.repoRoot) === projectRoot();\n}\n\n/** Ready set in merge order, or null when no flywheel run is active. */\nasync function getReadySet(): Promise<ReadyFeature[] | null> {\n const status = await readCurrentFlywheelStatusForDashboard();\n if (!status) return null;\n const { computeMergeQueue, resolveMergeQueuePrUrl } = await import('../../../lib/flywheel-merge-order.js');\n const queue = await Effect.runPromise(\n computeMergeQueue(status.activePipeline, projectRoot(), {\n getPrUrl: resolveMergeQueuePrUrl,\n onIneligible: (issueId, reason) =>\n console.log(`[uat-train] ${issueId} has a merge verb but is not merge-eligible (${reason}) — excluded from the ready set`),\n }).pipe(\n Effect.provide(nodeServicesLayer),\n ),\n );\n const fromVerbs: ReadyFeature[] = queue.map((item) => ({\n issueId: item.issueId,\n title: item.title,\n branch: item.branchName,\n ...(item.pr !== undefined ? { pr: item.pr } : {}),\n ...(item.prUrl !== undefined ? { prUrl: item.prUrl } : {}),\n conflictsWith: item.conflictsWith,\n }));\n const swept = await sweepEligibleFeatures(new Set(fromVerbs.map((f) => f.issueId)));\n return [...fromVerbs, ...swept];\n}\n\n/**\n * PAN-2484: the verb-based ready set misses merge-eligible issues that entered\n * the pipeline outside the active flywheel run (operator revivals, strikes,\n * externally-requested reviews) — those sat at readyForMerge with no train ever\n * assembling around them. Sweep review status for THIS project's merge-eligible\n * issues whose feature branch exists and append any the verb set missed. The\n * flywheel's ordering still leads; swept extras ride at the tail (the assembly\n * engine's conflict hook owns any cross-feature conflicts).\n */\nasync function sweepEligibleFeatures(alreadyIncluded: ReadySet): Promise<ReadyFeature[]> {\n try {\n const { loadReviewStatuses, mergeGateEligibility } = await import('../../../lib/review-status.js');\n const { resolveProjectFromIssueSync } = await import('../../../lib/projects.js');\n const { execFile } = await import('node:child_process');\n const { promisify } = await import('node:util');\n const execFileAsync = promisify(execFile);\n const root = projectRoot();\n const extras: ReadyFeature[] = [];\n for (const [issueId, rs] of Object.entries(loadReviewStatuses())) {\n if (alreadyIncluded.has(issueId)) continue;\n if (rs.readyForMerge !== true) continue;\n if (!mergeGateEligibility(rs).eligible) continue;\n const project = resolveProjectFromIssueSync(issueId);\n if (!project || resolve(project.projectPath) !== root) continue;\n const branch = `feature/${issueId.toLowerCase()}`;\n try {\n await execFileAsync('git', ['show-ref', '--verify', '--quiet', `refs/heads/${branch}`], { cwd: root, timeout: 10_000 });\n } catch {\n continue; // no local branch — nothing to assemble\n }\n console.log(`[uat-train] ${issueId} is merge-eligible without a flywheel merge verb — swept into the ready set (PAN-2484)`);\n extras.push({ issueId, title: issueId, branch, conflictsWith: [] });\n }\n return extras;\n } catch (err) {\n console.warn(`[uat-train] eligibility sweep failed: ${err instanceof Error ? err.message : String(err)}`);\n return [];\n }\n}\n\ntype ReadySet = Set<string>;\n\nfunction codenameLabel(features: readonly ReadyFeature[]): string {\n const prefix = features[0]?.issueId.split('-')[0];\n return (prefix ?? 'uat').toLowerCase();\n}\n\nasync function assembleFromReadySet(features: readonly ReadyFeature[]): Promise<UatGeneration> {\n const root = projectRoot();\n const store = buildUatGenerationStore();\n return assembleUatGeneration(\n {\n projectRoot: root,\n label: codenameLabel(features),\n dateIso: new Date().toISOString(),\n features,\n takenBranchNames: await listRemoteUatBranches(root),\n },\n {\n git: buildUatGenerationGitDeps(root),\n store,\n resolveConflict: buildConflictAgentHook(),\n log: (msg) => console.log(msg),\n },\n );\n}\n\nasync function runCleanup(): Promise<void> {\n const root = projectRoot();\n await cleanupUatGenerations(root, {\n store: buildUatGenerationStore(),\n ...buildUatGenerationCleanupGit(root),\n teardownStack: (gen) => teardownUatStack(gen),\n log: (msg) => console.log(msg),\n });\n}\n\n/** One reconciler pass. `force` rebuilds even when a live generation matches. */\nexport async function runUatTrainReconcile(options: { force?: boolean } = {}): Promise<ReconcileResult> {\n const root = projectRoot();\n const gitDeps = buildUatGenerationGitDeps(root);\n return reconcileUatGenerations(root, {\n isEnabled: () => isMergeTrainEnabled(),\n getReadySet,\n getMainHeadSha: () => gitDeps.fetchMain(),\n getBranchHeadSha: (branch) => gitDeps.branchHeadSha(branch),\n store: buildUatGenerationStore(),\n assemble: assembleFromReadySet,\n teardownStack: (gen) => teardownUatStack(gen),\n cleanup: runCleanup,\n log: (msg) => console.log(msg),\n }, options);\n}\n\nlet reconcilerTimer: ReturnType<typeof setInterval> | null = null;\n\nexport function startUatTrainReconciler(): boolean {\n if (!canStartUatTrainReconciler()) return false;\n if (reconcilerTimer) return true;\n reconcilerTimer = setInterval(() => {\n void runUatTrainReconcile().catch((err) => {\n console.warn('[uat-train] reconcile tick failed:', err instanceof Error ? err.message : err);\n });\n }, RECONCILE_INTERVAL_MS);\n reconcilerTimer.unref?.();\n void runUatTrainReconcile().catch((err) => {\n console.warn('[uat-train] initial reconcile failed:', err instanceof Error ? err.message : err);\n });\n return true;\n}\n\nexport function stopUatTrainReconciler(): void {\n if (reconcilerTimer) {\n clearInterval(reconcilerTimer);\n reconcilerTimer = null;\n }\n}\n\n// ─── Route payloads ───────────────────────────────────────────────────────────\n\nexport interface UatGenerationMemberPayload {\n issueId: string;\n title: string;\n branch: string;\n pr?: number;\n prUrl?: string;\n mergeOrder: number;\n /** What-to-UAT checklist from the issue's vBRIEF spec (shared extractor). */\n acceptanceCriteria: Array<{ title: string; status: string }>;\n}\n\nexport interface UatGenerationPayload {\n name: string;\n status: UatGeneration['status'];\n baseSha: string;\n createdAt: string;\n updatedAt: string;\n members: UatGenerationMemberPayload[];\n heldOut: UatGeneration['heldOut'];\n resolutions: UatGeneration['resolutions'];\n stack: { status: 'running' | 'absent'; frontendUrl: string };\n}\n\nasync function mapBounded<T>(items: readonly T[], concurrency: number, worker: (item: T) => Promise<void>): Promise<void> {\n let next = 0;\n const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {\n while (next < items.length) {\n const item = items[next++]!;\n await worker(item);\n }\n });\n await Promise.all(workers);\n}\n\nasync function loadAcceptanceCriteriaCache(issueIds: ReadonlySet<string>): Promise<Map<string, AcceptanceCriteriaSummary>> {\n const cache = new Map<string, AcceptanceCriteriaSummary>();\n if (issueIds.size === 0) return cache;\n\n const root = projectRoot();\n await mapBounded([...issueIds], ACCEPTANCE_CRITERIA_READ_CONCURRENCY, async (issueId) => {\n const upperIssueId = issueId.toUpperCase();\n const existing = acceptanceCriteriaByIssue.get(upperIssueId);\n if (existing) {\n try {\n const { mtimeMs } = await stat(existing.path);\n if (existing.mtimeMs === mtimeMs) {\n cache.set(upperIssueId, existing.criteria);\n return;\n }\n } catch {\n acceptanceCriteriaByIssue.delete(upperIssueId);\n }\n }\n\n try {\n const found = await Effect.runPromise(findVBriefByIssue(root, upperIssueId));\n if (!found) {\n acceptanceCriteriaByIssue.delete(upperIssueId);\n cache.set(upperIssueId, []);\n return;\n }\n const { mtimeMs } = await stat(found.path);\n const document = await Effect.runPromise(readVBriefDocument(found.path));\n const criteria = extractACFromDocument(document).map((ac) => ({ title: ac.title, status: ac.status }));\n acceptanceCriteriaByIssue.set(upperIssueId, { path: found.path, mtimeMs, criteria });\n cache.set(upperIssueId, criteria);\n } catch {\n cache.set(upperIssueId, []);\n }\n });\n return cache;\n}\n\n/** The generation chain, newest first, enriched for the UAT batches card. */\nexport async function getUatGenerationsPayload(): Promise<UatGenerationPayload[]> {\n const status = await readCurrentFlywheelStatusForDashboard();\n if (!status) return [];\n\n const chain = listUatGenerationsSync({ projectRoot: projectRoot(), limit: CHAIN_PAYLOAD_LIMIT });\n if (chain.length === 0) return [];\n const memberIssueIds = new Set(chain.flatMap((gen) => gen.members.map((member) => member.issueId.toUpperCase())));\n const acCache = await loadAcceptanceCriteriaCache(memberIssueIds);\n const payload: UatGenerationPayload[] = [];\n for (const gen of chain) {\n const probe = await probeUatStack(gen);\n const members: UatGenerationMemberPayload[] = [];\n for (const member of gen.members) {\n members.push({\n issueId: member.issueId,\n title: member.title,\n branch: member.branch,\n ...(member.pr !== undefined ? { pr: member.pr } : {}),\n ...(member.prUrl !== undefined ? { prUrl: member.prUrl } : {}),\n mergeOrder: member.mergeOrder,\n acceptanceCriteria: acCache.get(member.issueId.toUpperCase()) ?? [],\n });\n }\n payload.push({\n name: gen.name,\n status: gen.status,\n baseSha: gen.baseSha,\n createdAt: gen.createdAt,\n updatedAt: gen.updatedAt,\n members,\n heldOut: gen.heldOut,\n resolutions: gen.resolutions,\n stack: { status: probe.status, frontendUrl: probe.frontendUrl },\n });\n }\n return payload;\n}\n\nexport interface UatCandidatePayload {\n branchName: string;\n bundled: string[];\n status: 'ready';\n}\n\n/** The authoritative active UAT candidate, if one is ready to test/ship. */\nexport async function getUatCandidatePayload(): Promise<UatCandidatePayload | null> {\n const [candidate] = listUatGenerationsSync({\n projectRoot: projectRoot(),\n statuses: ['ready'],\n limit: 1,\n });\n if (!candidate) return null;\n return {\n branchName: candidate.name,\n bundled: candidate.members.map((member) => member.issueId),\n status: 'ready',\n };\n}\n\nexport async function postUatGenerationStackPayload(name: string): Promise<\n { ok: true; frontendUrl: string; evicted: string[] } | { ok: false; error: string; status: number }\n> {\n const gen = getUatGenerationSync(name);\n if (!gen) return { ok: false, error: `No UAT generation named ${name}`, status: 404 };\n if (gen.status !== 'ready' && gen.status !== 'superseded') {\n return { ok: false, error: `${name} is ${gen.status} — only live batches can serve a stack`, status: 409 };\n }\n const result = await ensureUatStack(gen);\n if (!result.success) return { ok: false, error: result.error ?? 'stack start failed', status: 500 };\n return { ok: true, frontendUrl: result.frontendUrl!, evicted: result.evicted };\n}\n\nexport async function postUatGenerationPromotePayload(\n name: string,\n firePostMerge: (issueId: string) => boolean,\n): Promise<PromoteResult> {\n const root = projectRoot();\n const { reviewRecordEligibility } = await import('../../../lib/flywheel-merge-order.js');\n const result = await promoteUatGeneration(name, root, {\n git: buildUatPromoteGitDeps(root),\n store: { ...buildUatGenerationStore(), get: (n) => getUatGenerationSync(n) },\n teardownStack: (gen) => teardownUatStack(gen),\n firePostMerge,\n memberEligibility: reviewRecordEligibility,\n log: (msg) => console.log(msg),\n });\n await notifyFlywheelOfUatPromote(result).catch(() => {});\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAMiD;AASjD,SAAgB,uBAA0C;AACxD,QAAO;EACL,UAAUA,UAAQ,KAAK,CAAC;EACxB,MAAM,QAAQ,IAAI,4BAA4B,MAAM,SAAS;EAC9D;;AAGH,SAAgB,oBAAoB,UAA2B;AAC7D,QAAO,oCAAoC,KAAK,SAAS,WAAW,MAAM,IAAI,CAAC;;AAGjF,SAAS,eAAe,QAAgB,WAA4B;CAClE,MAAM,MAAMC,WAASD,UAAQ,OAAO,EAAEA,UAAQ,UAAU,CAAC;AACzD,QAAO,QAAQ,MAAO,CAAC,IAAI,WAAW,KAAK,IAAI,CAAC,IAAI,WAAWE,MAAI;;AAGrE,SAAgB,yBAAyB,cAAc,MAAc;AACnE,KAAI,CAACC,aAAW,YAAY,CAAE,QAAO;AACrC,KAAI;EAIF,MAAM,SAAA,GAAA,YAAA,OAHmBC,eAAa,aAAa,QAAQ,CAGvC,CAAC,WAAW;AAChC,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,MAAM,GAAG,QAAQ;SAC/D;AACN,SAAO;;;AAIX,SAAgB,8BAA8B,OAMlC;CACV,MAAM,uBAAuB,MAAM,wBAAwB,0BAA0B;AACrF,KAAI,MAAM,SAAS,qBAAsB,QAAO;CAEhD,MAAM,WAAWJ,UAAQ,MAAM,SAAS;AAGxC,KADE,MAAM,uBAAuBG,aAAW,cAAc,IAAI,aAAa,wBAEvE,QAAO;AAET,KAAI,MAAM,SAAS,OAAQ,QAAO;AAElC,KAAI,oBAAoB,SAAS,CAAE,QAAO;CAE1C,MAAM,gBAAgBH,UAAQ,UAAU,KAAK;AAC7C,QAAO,cAAc,SAAS,GAAGE,MAAI,YAAY,IAAI,eAAe,eAAe,SAAS;;;;;;;;;;;;;;ACa9F,MAAA,mBAAA;AACA,MAAA,6BAAA,OAAA,SAAA,QAAA,IAAA,0CAAA,SAAA,GAAA;AACA,MAAA,uBAAA,IAAA,IAAA;;;;;;AAOA,MAAA,UAAA;;;;;AACA,MAAA,0BAAA,IAAA,KAAA;AACA,MAAA,6BAAA,IAAA,KAAA;AACA,IAAA,sBAAA;AAEA,SAAA,kBAAA;AACE,QAAA,OAAA,KAAA,IAAA,SAAA,MAAA,GAAA,IAAA,IAAA,4BAAA,OAAA,KAAA,IAAA,GAAA,IAAA,IAAA,4BAAA,OAAA,KAAA,IAAA;;AAKF,SAAA,mBAAA,MAAA,KAAA;AACE,MAAA,MAAA,CAAA,IAAA,QAAA,QAAA,SAAA,EAAA;AACE,MAAA,IAAA,SAAA,KAAA;AACA,MAAA,IAAA,QAAA,cAAA,IAAA,QAAA;AACA,MAAA,OAAA,IAAA;AACA,UAAA,OAAA,GAAA;;AAEF,MAAA,MAAA,CAAA,KAAA,QAAA,WAAA,SAAA,CAAA,KAAA,IAAA,SAAA,KAAA,YAAA,OAAA,IAAA;AAGA,KAAA,SAAA,WAAA,uBAAA;;AAGF,SAAA,gBAAA,OAAA;AACE,KAAA,SAAA,QAAA,OAAA,UAAA,SAAA,QAAA,KAAA,UAAA,MAAA;AACA,KAAA,MAAA,QAAA,MAAA,CAAA,QAAA,IAAA,MAAA,IAAA,gBAAA,CAAA,KAAA,IAAA,CAAA;AAIA,QAAA,IAAA,OAAA,QAAA,MAAA,CAAA,QAAA,GAAA,OAAA,OAAA,MAAA,cAAA,MAAA,KAAA,EAAA,CAAA,MAAA,CAAA,IAAA,CAAA,OAAA,EAAA,cAAA,EAAA,CAAA,CAAA,KAAA,CAAA,GAAA,OAAA,GAAA,KAAA,UAAA,EAAA,CAAA,GAAA,gBAAA,EAAA,GAAA,CAAA,KAAA,IAAA,CAAA;;AAGF,SAAA,cAAA,WAAA,SAAA;AACE,KAAA,CAAA,qBAAA,IAAA,UAAA,CAAA,QAAA;AACA,QAAA,GAAA,UAAA,GAAA,gBAAA,QAAA;;AAGF,SAAA,WAAA,WAAA;AACE,KAAA,cAAA,yBAAA,QAAA;AACA,QAAA,qBAAA,IAAA,UAAA,GAAA,SAAA;;AAGF,SAAA,8BAAA;AACE,KAAA,CAAA,uBAAA,CAAA,QAAA,SAAA;AACA,oBAAA,4BAAA,IAAA,MAAA,wCAAA,CAAA;AACA,SAAA,UAAA,WAAA;AACA,SAAA,WAAA;;AAGF,SAAA,wCAAA,SAAA;AACE,KAAA,OAAA,YAAA,SAAA,QAAA,iCAAA,QAAA;;AAIA,QAAA,iCAAA,OAAA,WAAA,aAAA,OAAA,UAAA,EAAA,CAAA;;AAGF,SAAA,UAAA,MAAA;;AAEE,KAAA,SAAA,QAAA;;AAKA,SAAA,QAAA;AAEA,QAAA,GAAA,YAAA,YAAA;;AAEE,MAAA,CAAA,IAAA;AAEA,MAAA,QAAA,aAAA,KAAA,GAAA;AACE,OAAA,gBAAA,IAAA,cAAA,KAAA,YAAA;AACE,SAAA,MAAA,YAAA,IAAA,kBAAA,OAAA,SAAA,QAAA,SAAA;;AAIF;;AAGF,UAAA,OAAA,QAAA,GAAA;AACA,MAAA,IAAA,QAAA,cAAA,IAAA,QAAA;AACA,MAAA,IAAA,SAAA,cAAA,wBAAA,QAAA,GAAA,uBAAA;AAEA,MAAA,QAAA,IAAA;AACE,OAAA,cAAA,WAAA,IAAA,QAAA,QAAA,OAAA,EAAA,IAAA,OAAA;AACA;;;AAUF,MAAA,OAAA,QAAA,OAAA,QAAA;AACA,MAAA,QAAA,QAAA,OAAA;AACA,MAAA,cAAA,WAAA,IAAA,OAAA,IAAA,EAAA,IAAA,OAAA;;AAGF,QAAA,GAAA,UAAA,QAAA;AACE,qBAAA,MAAA,IAAA;AACA,MAAA,QAAA,UAAA,OAAA,SAAA,QAAA;;AAGF,QAAA,GAAA,SAAA,SAAA;AACE,MAAA,SAAA,EAAA,oBAAA,sBAAA,IAAA,MAAA,sBAAA,KAAA,2BAAA,OAAA,CAAA;AACA,MAAA,QAAA,UAAA,OAAA,SAAA,QAAA;;AAGF,QAAA;;AAGF,eAAA,UAAA,WAAA,SAAA,YAAA;AAKE,SAAA,WAAA;;;;AAKI,UAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,cAAA,MAAA,KAAA,MAAA,MAAA;AACA,UAAA;;;;AAIA,UAAA,qBAAA,QAAA;;;;AAIA,UAAA,oCAAA,QAAA;;;;AAIA,UAAA,yBAAA,QAAA;;;;AAKN,SAAA,kBAAA,WAAA,SAAA,YAAA;AAKE,KAAA,OAAA,KAAA,IAAA,SAAA,MAAA,IAAA,QAAA,IAAA,UAAA,QAAA,UAAA,WAAA,SAAA,WAAA;;;AAMA,KAAA,UAAA;AACE,MAAA,WAAA,UAAA,kBAAA,IAAA,WAAA;AACA,SAAA,SAAA;;AAGF,KAAA,QAAA,QAAA,iBAAA,QAAA,QAAA,uBAAA,IAAA,MAAA,0CAAA,CAAA;;;AAMA,KAAA,cAAA,0BAAA;AACE,+BAAA;AACA,wBAAA;;;AAGF,KAAA,WAAA,mBAAA,IAAA,WAAA;;;AAMQ,OAAA,CAAA,QAAA,IAAA,GAAA,CAAA;AACA,WAAA,OAAA,GAAA;AACA,QAAA,MAAA,CAAA,WAAA,cAAA,WAAA,SAAA,CAAA,KAAA,UAAA,YAAA,QAAA,YAAA,OAAA,UAAA;AAGA,OAAA,wBAAA,GAAA,uBAAA;AACA,0BAAA,IAAA,MAAA,4BAAA,CAAA;AACA,WAAA,UAAA,WAAA;AACA,WAAA,WAAA;;AAGN,UAAA,IAAA,IAAA;;;;;;;;;AASA,YAAA,KAAA,CAAA,YAAA;;;;;;AAGF,KAAA,KAAA;AACE,aAAA,IAAA,KAAA;;;;;AACA,UAAA,WAAA,WAAA,OAAA,IAAA,QAAA,WAAA,OAAA,IAAA,CAAA;;AAMF,QAAA;;;;;;;ACnRF,MAAA,qBAAA;AACA,MAAA,YAAA,UAAA,KAAA;AAEA,SAAA,GAAA,OAAA;AACE,QAAA,KAAA,MAAA,QAAA,OAAA,KAAA;;AAGF,SAAA,aAAA,aAAA,WAAA;;AAEE,QAAA,iBAAA,MAAA,CAAA,aAAA,WAAA,KAAA,IAAA,CAAA,aAAA,aAAA;;AAGF,SAAA,8BAAA,KAAA,eAAA;;AAEE,KAAA,QAAA,SAAA,KAAA,CAAA,OAAA,IAAA,MAAA,wBAAA;;;AAIA,KAAA,CAAA,aAAA,MAAA,aAAA,CAAA,OAAA,IAAA,MAAA,+CAAA;;;AAIA,QAAA;;;;;AAGF,eAAA,6BAAA,aAAA,WAAA;;AAEE,KAAA,CAAA,aAAA,UAAA,cAAA,CAAA,OAAA,IAAA,MAAA,+CAAA;;AAGF,eAAA,qBAAA,KAAA,eAAA;;AAEE,KAAA;AACE,QAAA,6BAAA,KAAA,SAAA,aAAA;AACA,QAAA,SAAA,SAAA,cAAA,OAAA;AACA,SAAA;;AAGA,OAAA,OAAA,UAAA,YAAA,UAAA,QAAA,UAAA,QAAA,MAAA,OAAA,KAAA,OAAA,SAAA,OAAA,IAAA,MAAA,6BAAA,SAAA,cAAA;AACA,QAAA;;;AAIJ,eAAA,iBAAA,KAAA;AACE,QAAA,kBAAA,cAAA,IAAA;;AAGF,eAAA,oBAAA,KAAA,OAAA;AACE,OAAA,kBAAA,cAAA;;;;;AAGF,eAAA,iBAAA;;AAEE,QAAA,SAAA,MAAA,MAAA,GAAA,QAAA;;AAGF,eAAA,eAAA,OAAA;AACE,OAAA,oBAAA,4BAAA,SAAA,GAAA;;AAGF,eAAA,WAAA;AACE,QAAA,MAAA,iBAAA,0BAAA,KAAA;;AAGF,eAAA,UAAA,QAAA;AACE,OAAA,oBAAA,2BAAA,SAAA,SAAA,QAAA;;AAGF,eAAA,mBAAA;;AAEE,QAAA;;;;;AAGF,eAAA,4BAAA;;;;AAOE,QAAA;;;;;;;;;AAUF,eAAA,UAAA,SAAA,KAAA;;;;;AAEE,QAAA,OAAA,MAAA;;AAGF,eAAA,4BAAA,OAAA,WAAA,KAAA,YAAA,cAAA,YAAA;;AASE,QAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CF,eAAA,6BAAA,UAAA,EAAA,EAAA;;AAEE,KAAA,+BAAA,CAAA,OAAA,IAAA,MAAA,0EAAA;;AAOA,KAAA,YAAA,KAAA,CAAA,MAAA,OAAA,WAAA,cAAA,wBAAA,CAAA,IAAA,MAAA,OAAA,WAAA,WAAA,wBAAA,CAAA,EAAA;AAQI,UAAA,IAAA,cAAA,YAAA,wDAAA;AAIA,QAAA,OAAA,WAAA,YAAA,+BAAA,CAAA,CAAA,YAAA,GAAA;AACA,QAAA,iBAAA,YAAA;;;;;AASJ,OAAA,4BAAA;;;;;;;;;;;;;;;;;;AAkBA,OAAA,eAAA,MAAA;AACA,OAAA,UAAA,MAAA;AACA,OAAA,0BAAA,MAAA,4BAAA,OAAA,WAAA,KAAA,MAAA,OAAA,MAAA,SAAA,WAAA,CAAA;AASA,QAAA;;;;;;AAGF,eAAA,+BAAA;;AAEE,KAAA,OAAA,OAAA,QAAA;;;;;AACA,KAAA,OAAA,YAAA,KAAA;;;;;AAMI,MAAA,UAAA,OAAA,oBAAA,OAAA,aAAA,UAAA;;AAGJ,OAAA,UAAA,KAAA;AACA,OAAA,OAAA,wBAAA,MAAA,EAAA,gBAAA,OAAA,WAAA,UAAA,+BAAA,CAAA,CAAA;AACA,QAAA;;;;;;AAMF,eAAA,+BAAA;;AAEE,KAAA,CAAA,UAAA,QAAA,EAAA,SAAA,MAAA,0BAAA,IAAA,MAAA;;AAMA,OAAA,OAAA,WAAA,UAAA,+BAAA,CAAA;AACA,OAAA,iBAAA,UAAA;AACA,QAAA,EAAA,SAAA,WAAA;;AAGF,eAAA,gCAAA;;AAEE,KAAA,CAAA,OAAA,UAAA,MAAA,OAAA,WAAA,cAAA,wBAAA,CAAA,CAAA,QAAA;;;;;AAGA,KAAA,CAAA,OAAA,YAAA,OAAA,IAAA,MAAA,mCAAA;;AAGA,KAAA,CAAA,OAAA,OAAA,IAAA,MAAA,gBAAA,OAAA,YAAA,mDAAA;;;;AAMA,OAAA,mBAAA,OAAA,aAAA;;;;;;;;;;;AAWA,OAAA,UAAA,MAAA;AACA,QAAA;;;;;;AAGF,SAAA,yBAAA;AACE,SAAA,QAAA,UAAA;;;;;;;AAQF,SAAA,iBAAA,MAAA;AAKE,OAAA,wBAAA,EAAA,CAAA,KAAA,EAAA;;;;;AAGF,eAAA,uBAAA,OAAA;AACE,KAAA,MAAA,QAAA;;;AAGA,KAAA,CAAA,IAAA,OAAA,IAAA,MAAA,iCAAA;AACA,QAAA,IAAA;;AAGF,eAAA,kCAAA,UAAA,EAAA,EAAA;;;AAGE,KAAA,CAAA,OAAA,OAAA,IAAA,MAAA,2BAAA,QAAA;AACA,KAAA,CAAA,OAAA,MAAA,OAAA,OAAA,IAAA,MAAA,wBAAA,QAAA;AACA,QAAA,QAAA,UAAA,kBAAA,OAAA,MAAA,OAAA;AACA,QAAA;;;;;AAGF,eAAA,wCAAA;;AAIE,KAAA,CAAA,YAAA,QAAA;;AAEA,QAAA,WAAA,WAAA,YAAA,UAAA,SAAA;;;;;;;;;;;;;;;AC5VF,MAAa,gBAAmC;CAC9C;CAAS;CAAU;CAAS;CAAU;CAAS;CAAU;CAAU;CACnE;CAAS;CAAS;CAAQ;CAAS;CAAS;CAAS;CAAQ;CAAQ;CACrE;CAAQ;CAAQ;CAAS;CAAS;CAAO;CAAS;CACnD;AAED,SAAS,KAAK,SAAyB;CACrC,MAAM,GAAG,OAAO,OAAO,QAAQ,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI;AACtD,QAAO,GAAG,SAAS,OAAO,OAAO;;;;;AAiBnC,SAAgB,qBAAqB,MAA2B;CAC9D,MAAM,MAAM,KAAK,KAAK,QAAQ;CAC9B,MAAM,QAAQ,KAAK,aAAa;CAChC,MAAM,OAAO,KAAK,UAAU,MAAc,YAAY,GAAG,KAAK,MAAM,GAAG,OAAO,EAAE;CAEhF,MAAM,OAAO,MADC,MAAM,SAAS,IAAI,KAAK,MAAM,OAAO,GAAG,MAAM,SAAS,MACxC;AAC7B,QAAO,OAAO,KAAK,MAAM,GAAG,KAAK,GAAG;;AAGtC,SAAS,YAAY,MAAc,QAAwB;AACzD,KAAI,UAAU,EAAG,QAAO;CACxB,IAAI,OAAO;AACX,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAQ,KAAK,WAAW,EAAE;AAC1B,SAAO,KAAK,KAAK,MAAM,SAAS;;AAElC,SAAQ,SAAS,KAAK;;;;;;;;;;;;;;;;;;;;;;;AC2DxB,SAAgB,qBAAqB,YAA4B;AAC/D,QAAO,WAAW,QAAQ,qBAAqB,IAAI;;;;;;;AAQrD,eAAsB,sBACpB,OACA,MACwB;CACxB,MAAM,MAAM,KAAK,cAAc;CAE/B,MAAM,UAAU,MAAM,KAAK,IAAI,WAAW;CAC1C,MAAM,OAAO,qBAAqB;EAChC,OAAO,MAAM;EACb,SAAS,MAAM;EAChB,CAAC;CACF,MAAM,eAAe,GAAG,MAAM,YAAY,cAAc,qBAAqB,KAAK;AAElF,MAAK,MAAM,OAAO;EAChB;EACA;EACA,aAAa,MAAM;EACnB;EACA,QAAQ;EACR,SAAS,EAAE;EACX,SAAS,EAAE;EACX,aAAa,EAAE;EACf,gBAAgB;EAChB,WAAW;EACZ,CAAC;CAEF,MAAM,UAAoC,EAAE;CAC5C,MAAM,UAAoC,EAAE;CAC5C,MAAM,cAA4C,EAAE;CACpD,MAAM,UAAU,WAA+C;AAC7D,OAAK,MAAM,OAAO,MAAM;GAAE;GAAQ;GAAS;GAAS;GAAa,CAAC;AAClE,SAAO;GACL;GAAM;GAAc,aAAa,MAAM;GAAa;GACpD;GAAQ;GAAS;GAAS;GAC1B,gBAAgB;GAAM,WAAW;GAAM,WAAW;GAAI,WAAW;GAClE;;AAGH,KAAI;AACF,QAAM,KAAK,IAAI,eAAe,MAAM,aAAa;UAC1C,KAAK;AACZ,MAAI,oBAAoB,KAAK,8BAA8B,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAAG;AAC9G,SAAO,OAAO,SAAS;;AAGzB,MAAK,MAAM,WAAW,MAAM,UAAU;EACpC,MAAM,iBAAiB,QAAQ,KAAK,MAAM,EAAE,QAAQ;EACpD,MAAM,mBAAmB,YAAY,KAAK,IAAI,cAAc,QAAQ,OAAO,CAAC,YAAY,UAAU;EAClG,MAAM,eAAe,aAA2C;GAC9D,SAAS,QAAQ;GACjB,OAAO,QAAQ;GACf,QAAQ,QAAQ;GAChB,SAAS,MAAM,kBAAkB;GACjC,YAAY,QAAQ,SAAS;GAC7B,GAAI,QAAQ,OAAO,KAAA,IAAY,EAAE,IAAI,QAAQ,IAAI,GAAG,EAAE;GACtD,GAAI,QAAQ,UAAU,KAAA,IAAY,EAAE,OAAO,QAAQ,OAAO,GAAG,EAAE;GAChE;EACD,MAAM,UAAU,OAAO,WAAkC;AACvD,WAAQ,KAAK;IAAE,SAAS,QAAQ;IAAS,QAAQ,QAAQ;IAAQ,SAAS,MAAM,kBAAkB;IAAE;IAAQ,CAAC;;EAG/G,IAAI;AACJ,MAAI;AACF,YAAS,MAAM,KAAK,IAAI,YAAY,QAAQ,OAAO;WAC5C,KAAK;AACZ,SAAM,KAAK,IAAI,YAAY,CAAC,YAAY,GAAG;AAC3C,SAAM,QAAQ,iBAAiB,eAAe,QAAQ,IAAI,QAAQ,MAAM,KAAK,CAAC,KAAK,OAAO,IAAI,GAAG;AACjG,QAAK,MAAM,OAAO,MAAM;IAAE;IAAS;IAAS;IAAa,CAAC;AAC1D;;AAGF,MAAI,OAAO,IAAI;AACb,WAAQ,KAAK,MAAM,cAAc,CAAC;AAClC,QAAK,MAAM,OAAO,MAAM;IAAE;IAAS;IAAS;IAAa,CAAC;AAC1D;;AAGF,MAAI,CAAC,OAAO,YAAY,CAAC,KAAK,iBAAiB;AAC7C,SAAM,KAAK,IAAI,YAAY,CAAC,YAAY,GAAG;AAC3C,SAAM,QAAQ,OAAO,WACjB,kBAAkB,gBAAgB,SAAS,eAAe,CAAC,KAAK,KAAK,IAAI,oBAAoB,kCAC7F,OAAO,OAAO;AAClB,QAAK,MAAM,OAAO,MAAM;IAAE;IAAS;IAAS;IAAa,CAAC;AAC1D;;EAGF,MAAM,sBAAsB,gBAAgB,SAAS,eAAe;AACpE,MAAI,oBAAoB,KAAK,uBAAuB,QAAQ,QAAQ,OAAO,oBAAoB,KAAK,KAAK,IAAI,qBAAqB;EAClI,IAAI,aAA8C;AAClD,MAAI;AACF,gBAAa,MAAM,KAAK,gBAAgB;IACtC;IACA;IACA;IACA,YAAY;IACZ;IACD,CAAC;WACK,KAAK;AACZ,OAAI,oBAAoB,KAAK,0BAA0B,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAAG;AAC1G,gBAAa;;AAGf,MAAI,YAAY;AACd,WAAQ,KAAK,MAAM,cAAc,CAAC;AAClC,eAAY,KAAK;IACf,UAAU,CAAC,QAAQ,SAAS,GAAG,oBAAoB;IACnD,OAAO,WAAW;IAClB,WAAW,WAAW;IACvB,CAAC;SACG;AACL,SAAM,KAAK,IAAI,YAAY,CAAC,YAAY,GAAG;AAC3C,SAAM,QAAQ,iBAAiB,oBAAoB,KAAK,KAAK,IAAI,oBAAoB,6DAA6D;;AAEpJ,OAAK,MAAM,OAAO,MAAM;GAAE;GAAS;GAAS;GAAa,CAAC;;AAG5D,KAAI,QAAQ,WAAW,GAAG;AACxB,MAAI,oBAAoB,KAAK,oBAAoB,QAAQ,OAAO,6BAA6B;AAC7F,SAAO,OAAO,SAAS;;AAGzB,KAAI;AACF,QAAM,KAAK,IAAI,KAAK,KAAK;UAClB,KAAK;AACZ,MAAI,oBAAoB,KAAK,iBAAiB,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAAG;AACjG,SAAO,OAAO,SAAS;;AAKzB,MAAK,MAAM,SAAS,KAAK,MAAM,UAAU,MAAM,aAAa,CAAC,QAAQ,CAAC,CACpE,KAAI,MAAM,SAAS,KAAM,MAAK,MAAM,OAAO,MAAM,MAAM,EAAE,QAAQ,cAAc,CAAC;AAGlF,KAAI,oBAAoB,KAAK,YAAY,QAAQ,OAAO,cAAc,YAAY,OAAO,kBAAkB,QAAQ,OAAO,WAAW;AACrI,QAAO,OAAO,QAAQ;;AAGxB,SAAS,gBAAgB,SAAuB,gBAA6C;CAC3F,MAAM,SAAS,IAAI,IAAI,eAAe,KAAK,OAAO,GAAG,aAAa,CAAC,CAAC;AACpE,SAAQ,QAAQ,iBAAiB,EAAE,EAAE,QAAQ,OAAO,OAAO,IAAI,GAAG,aAAa,CAAC,CAAC;;;;;;;;;AAqBnF,eAAsB,sBACpB,aACA,MACA,UAA6B,EAAE,EAChB;CACf,MAAM,MAAM,KAAK,cAAc;CAC/B,MAAM,OAAO,QAAQ,QAAQ;CAG7B,MAAM,UADO,KAAK,MAAM,UAAU,aAAa,CAAC,SAAS,aAAa,CAClD,CAAC,MAAM,KAAK;CAChC,MAAM,OAAO,KAAK,MAAM,UAAU,aAAa;EAAC;EAAe;EAAY;EAAS,CAAC,CAClF,QAAQ,QAAQ,CAAC,IAAI,UAAU;AAElC,MAAK,MAAM,OAAO,CAAC,GAAG,SAAS,GAAG,KAAK,EAAE;EACvC,IAAI,UAAU;AACd,MAAI,IAAI,kBAAkB,KAAK,cAC7B,OAAM,KAAK,cAAc,IAAI,CAAC,OAAO,QAAQ;AAC3C,aAAU;AACV,OAAI,4BAA4B,IAAI,KAAK,2BAA2B,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAAG;IACvH;AAEJ,QAAM,KAAK,eAAe,IAAI,aAAa,CAAC,OAAO,QAAQ;AACzD,aAAU;AACV,OAAI,4BAA4B,IAAI,KAAK,6BAA6B,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAAG;IACzH;AACF,QAAM,KAAK,aAAa,IAAI,KAAK,CAAC,OAAO,QAAQ;AAC/C,aAAU;AACV,OAAI,4BAA4B,IAAI,KAAK,4BAA4B,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAAG;IACxH;AACF,MAAI,CAAC,QAAS;EACd,MAAM,6BAAY,IAAI,MAAM,EAAC,aAAa;AAC1C,MAAI,IAAI,WAAW,WAAW,IAAI,WAAW,cAAc;AACzD,QAAK,MAAM,OAAO,IAAI,MAAM;IAAE,QAAQ;IAAe;IAAW,CAAC;AACjE,OAAI,qCAAqC,IAAI,KAAK,kBAAkB,KAAK,GAAG;QAE5E,MAAK,MAAM,OAAO,IAAI,MAAM,EAAE,WAAW,CAAC;;;;;;;;;;;;;;;;ACvShD,MAAMS,kBAAgBC,YAAUC,WAAS;AAEzC,MAAM,UAAU,MAAgB,QAC9BF,gBAAc,OAAO,MAAM;CAAE;CAAK,WAAW,KAAK,OAAO;CAAM,CAAC;AAElE,SAAS,eAAe,YAAoB,QAAmC;AAI7E,KAAI,EAHY,WAAW,YACvB,0CACA,qCACS,KAAK,WAAW,CAC3B,OAAM,IAAI,MAAM,UAAU,OAAO,gBAAgB,aAAa;AAEhE,QAAO;;AAGT,SAAS,2BAA2B,aAAqB,cAA8B;CACrF,MAAM,iBAAiB,QAAQ,aAAa,aAAa;CACzD,MAAM,SAAS,QAAQ,aAAa;CACpC,MAAM,MAAM,SAAS,gBAAgB,OAAO;AAC5C,KAAI,QAAQ,MAAM,IAAI,WAAW,KAAK,IAAI,WAAW,IAAI,CACvD,OAAM,IAAI,MAAM,wDAAwD,eAAe;AAEzF,QAAO;;;AAIT,SAAgB,0BAA+C;AAC7D,QAAO;EACL,SAAS,QAAQ;AAAE,2BAAwB,IAAI;;EAC/C,SAAS,MAAM,UAAU;AAAE,2BAAwB,MAAM,MAAM;;EAC/D,iBAAiB,4BAA4B;EAC7C,YAAY,aAAa,aACvB,uBAAuB;GAAE;GAAa,GAAI,WAAW,EAAE,UAAU,GAAG,EAAE;GAAG,CAAC;EAC7E;;;;;;AAOH,SAAgB,0BAA0B,aAAwC;CAChF,IAAI,eAAe;AAEnB,QAAO;EACL,WAAW,YAAY;AACrB,SAAM,OAAO;IAAC;IAAS;IAAU;IAAO,EAAE,YAAY;GACtD,MAAM,EAAE,WAAW,MAAM,OAAO,CAAC,aAAa,cAAc,EAAE,YAAY;AAC1E,UAAO,OAAO,MAAM;;EAGtB,gBAAgB,OAAO,YAAY,SAAS;GAC1C,MAAM,aAAa,eAAe,YAAY,MAAM;AACpD,kBAAe,2BAA2B,aAAa,KAAK;AAG5D,SAAM,OAAO;IAAC;IAAY;IAAU;IAAW;IAAa,EAAE,YAAY,CAAC,YAAY,GAAG;AAC1F,SAAM,OAAO,CAAC,YAAY,QAAQ,EAAE,YAAY,CAAC,YAAY,GAAG;AAChE,SAAM,OAAO;IAAC;IAAY;IAAO;IAAM;IAAY;IAAc;IAAc,EAAE,YAAY;;EAG/F,eAAe,OAAO,WAAW;GAC/B,MAAM,aAAa,eAAe,QAAQ,UAAU;GACpD,MAAM,SAAS,OAAO,SAAiB,MAAM,OAAO,CAAC,aAAa,IAAI,EAAE,YAAY,EAAE,OAAO,MAAM;AACnG,UAAO,OAAO,UAAU,aAAa,CAAC,YAAY,OAAO,WAAW,CAAC;;EAGvE,aAAa,OAAO,kBAAkB;GAGpC,MAAM,oBAAoB,eAAe,eAAe,UAAU;GAClE,MAAM,YAAY,UAAU;GAC5B,MAAM,MAAM,MAAM,OAAO;IAAC;IAAa;IAAY;IAAU,EAAE,aAAa,CACzE,WAAW,UAAU,CACrB,YAAY,kBAAkB;AACjC,OAAI;AACF,UAAM,OAAO;KAAC;KAAS;KAAa;KAAI,EAAE,aAAa;AACvD,WAAO,EAAE,IAAI,MAAe;YACrB,KAAK;IACZ,MAAM,EAAE,WAAW,MAAM,OAAO,CAAC,YAAY,KAAK,EAAE,aAAa,CAAC,aAAa,EAAE,QAAQ,IAAI,EAAE;AAI/F,WAAO;KACL,IAAI;KACJ,UALe,OAAO,MAAM,CAAC,SAAS;KAMtC,QAAQ,eAAe,QAAS,IAAI,QAAQ,MAAM,KAAK,CAAC,MAAM,iBAAkB,OAAO,IAAI;KAC5F;;;EAIL,YAAY,YAAY;AACtB,SAAM,OAAO,CAAC,SAAS,UAAU,EAAE,aAAa,CAAC,YAAY,GAAG;;EAGlE,MAAM,OAAO,eAAe;AAC1B,SAAM,OAAO;IAAC;IAAQ;IAAM;IAAsB;IAAU,eAAe,YAAY,MAAM;IAAC,EAAE,aAAa;;EAEhH;;;AAIH,eAAsB,sBAAsB,aAAwC;CAClF,MAAM,EAAE,WAAW,MAAM,OAAO;EAAC;EAAa;EAAW;EAAU;EAAQ,EAAE,YAAY,CAAC,aAAa,EAAE,QAAQ,IAAI,EAAE;AACvH,QAAO,OACJ,MAAM,KAAK,CACX,KAAK,SAAS,KAAK,MAAM,IAAK,CAAC,MAAM,GAAG,CACxC,QAAQ,QAAQ,IAAI,WAAW,cAAc,CAAC,CAC9C,KAAK,QAAQ,IAAI,MAAM,GAAqB,CAAC;;;AAIlD,SAAgB,6BAA6B,aAG3C;AACA,QAAO;EACL,gBAAgB,OAAO,SAAS;AAE9B,SAAM,OAAO;IAAC;IAAY;IAAU;IADnB,2BAA2B,aAAa,KACF;IAAC,EAAE,YAAY,CAAC,YAAY,GAAG;AACtF,SAAM,OAAO,CAAC,YAAY,QAAQ,EAAE,YAAY,CAAC,YAAY,GAAG;;EAElE,cAAc,OAAO,eAAe;GAClC,MAAM,aAAa,eAAe,YAAY,MAAM;AACpD,SAAM,OAAO;IAAC;IAAU;IAAM;IAAW,EAAE,YAAY,CAAC,YAAY,GAAG;AACvE,SAAM,OAAO;IAAC;IAAQ;IAAU;IAAY;IAAW,EAAE,YAAY,CAAC,YAAY,GAAG;;EAExF;;;;;;;;;;;;;;;;;;;;;;;;;AC3HH,MAAMG,kBAAgBC,YAAUC,WAAS;AAiCzC,SAAS,SAAS,QAA0B;AAC1C,QAAO,OAAO,MAAM,KAAK,CAAC,QAAQ,MAAM,EAAE,SAAS,EAAE;;AAGvD,SAAS,UAAU,OAA+C;AAChE,QAAO,IAAI,IAAI,MAAM,KAAK,MAAM,EAAE,QAAQ,OAAO,IAAI,CAAC,CAAC;;AAGzD,SAAgB,8BAA8B,KAAsB,iBAA4C;CAC9G,MAAM,eAAe,IAAI,oBAAoB,SAAS,IAClD,IAAI,oBAAoB,KAAK,KAAK,GAClC;AACJ,QAAO;EACL;EACA;EACA,cAAc,IAAI,WAAW;EAC7B,WAAW,IAAI,QAAQ,OAAO,IAAI,IAAI,QAAQ,QAAQ,IAAI,IAAI,QAAQ,MAAM,oBAAoB,aAAa;EAC7G;EACA;EACA,GAAG,gBAAgB,KAAK,MAAM,OAAO,IAAI;EACzC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,KAAK,KAAK;;AAGd,SAAS,wBAAwB,KAAsB,OAAkC;CACvF,MAAM,OAAO,CAAC,IAAI,QAAQ,SAAS,GAAG,IAAI,oBAAoB,CAAC,KAAK,QAAQ;AAC5E,QAAO;EACL,iBAAiB,IAAI,QAAQ,OAAO,SAAS,IAAI;EACjD;EACA,yBAAyB;EACzB,UAAU,MAAM,KAAK,KAAK;EAC3B,CAAC,KAAK,KAAK;;AAGd,SAASC,gBAAiC;CACxC,MAAM,OAAO,KAAa,MAAgB,KAAa,cACrDH,gBAAc,KAAK,MAAM;EAAE;EAAK,WAAW,KAAK,OAAO;EAAM,GAAI,YAAY,EAAE,SAAS,WAAW,GAAG,EAAE;EAAG,CAAC;AAE9G,QAAO;EACL,qBAAqB,OAAO,QAAQ;GAClC,MAAM,EAAE,WAAW,MAAM,IAAI,OAAO;IAAC;IAAQ;IAAe;IAAkB,EAAE,IAAI;AACpF,UAAO,OAAO,MAAM,KAAK,CAAC,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC,OAAO,QAAQ;;EAEhE,aAAa,OAAO,QAAQ;GAC1B,MAAM,EAAE,WAAW,MAAM,IAAI,OAAO;IAAC;IAAQ;IAAY;IAAe;IAAK,EAAE,IAAI;AACnF,UAAO,SAAS,OAAO;;EAEzB,UAAU,OAAO,EAAE,QAAQ,KAAK,gBAAgB;AAC9C,SAAM,IAAI,UAAU;IAAC;IAAM;IAAQ;IAAqB;IAAc,EAAE,KAAK,UAAU;;EAEzF,0BAA0B,OAAO,KAAK,UAAU;AAC9C,OAAI,MAAM,WAAW,EAAG,QAAO,EAAE;GAEjC,MAAM,EAAE,WAAW,MAAMA,gBACvB,QAAQ;IAAC;IAAO;IAA+B;IAAM,GAAG;IAAM,EAC9D;IAAE;IAAK,WAAW,KAAK,OAAO;IAAM,CACrC,CAAC,OAAO,QAA4C;AACnD,QAAI,OAAO,IAAI,SAAS,EAAG,QAAO,EAAE,QAAQ,IAAI;AAChD,UAAM;KACN;AACF,UAAO,OAAO,MAAM,KAAK,CAAC,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC,OAAO,QAAQ;;EAEhE,oBAAoB,OAAO,KAAK,UAAU;GACxC,MAAM,UAAU,UAAU,MAAM;GAChC,MAAM,CAAC,EAAE,QAAQ,YAAY,EAAE,QAAQ,eAAe,MAAM,QAAQ,IAAI,CACtE,IAAI,OAAO;IAAC;IAAQ;IAAe;IAAK,EAAE,IAAI,EAC9C,IAAI,OAAO;IAAC;IAAY;IAAY;IAAsB;IAAK,EAAE,IAAI,CACtE,CAAC;AACF,UAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,SAAS,SAAS,EAAE,GAAG,SAAS,UAAU,CAAC,CAAC,CAAC,CACjE,QAAQ,SAAS,CAAC,QAAQ,IAAI,KAAK,QAAQ,OAAO,IAAI,CAAC,CAAC;;EAE7D,cAAc,OAAO,KAAK,UAAU;AAClC,OAAI,MAAM,WAAW,EAAG;AACxB,SAAM,IAAI,OAAO;IAAC;IAAW;IAAY;IAAc;IAAM,GAAG;IAAM,EAAE,IAAI,CAAC,YAAY,GAAG;AAC5F,SAAM,IAAI,OAAO;IAAC;IAAS;IAAO;IAAM,GAAG;IAAM,EAAE,IAAI,CAAC,YAAY,GAAG;;EAEzE,YAAY,OAAO,KAAK,UAAU;AAChC,OAAI,MAAM,WAAW,EAAG;AACxB,SAAM,IAAI,OAAO;IAAC;IAAO;IAAM,GAAG;IAAM,EAAE,IAAI;;EAEhD,oBAAoB,OAAO,QAAQ;GACjC,MAAM,EAAE,WAAW,MAAM,IAAI,OAAO,CAAC,YAAY,KAAK,EAAE,IAAI;AAC5D,UAAO,OAAO,MAAM,CAAC,SAAS;;EAEhC,aAAa,OAAO,KAAK,YAAY;AACnC,SAAM,IAAI,OAAO;IAAC;IAAU;IAAM;IAAQ,EAAE,IAAI;;EAElD,SAAS,OAAO,SAAS,MAAM,IAAI,OAAO,CAAC,aAAa,OAAO,EAAE,IAAI,EAAE,OAAO,MAAM;EACrF;;;;;;;AAQH,SAAgB,uBACd,UAAgC,EAAE,EACkC;CACpE,MAAM,OAA0B;EAAE,GAAGG,eAAa;EAAE,GAAI,QAAQ,QAAQ,EAAE;EAAG;CAC7E,MAAM,YAAY,QAAQ,aAAA;CAC1B,MAAM,MAAM,KAAK,cAAc;AAE/B,QAAO,OAAO,QAAQ;EACpB,MAAM,MAAM,wBAAwB,IAAI,WAAW,GAAG,IAAI,QAAQ;EAClE,IAAI;EACJ,IAAI;AACJ,MAAI;AACF,WAAQ,MAAM,KAAK,oBAAoB,IAAI,aAAa;AACxD,kBAAe,MAAM,KAAK,YAAY,IAAI,aAAa;WAChD,KAAK;AACZ,OAAI,GAAG,IAAI,wCAAwC,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAAG;AACtG,UAAO;;AAET,MAAI,MAAM,WAAW,GAAG;AACtB,OAAI,GAAG,IAAI,2EAA2E;AACtF,UAAO;;AAGT,MAAI;AACF,SAAM,KAAK,SAAS;IAClB,QAAQ,8BAA8B,KAAK,MAAM;IACjD,KAAK,IAAI;IACT;IACD,CAAC;WACK,KAAK;AACZ,OAAI,GAAG,IAAI,sBAAsB,eAAe,QAAQ,IAAI,QAAQ,MAAM,KAAK,CAAC,KAAK,OAAO,IAAI,CAAC,GAAG;AACpG,UAAO;;AAGT,MAAI;GACF,MAAM,SAAS,MAAM,KAAK,mBAAmB,IAAI,cAAc,MAAM;AACrE,OAAI,OAAO,SAAS,GAAG;AACrB,QAAI,GAAG,IAAI,0CAA0C,OAAO,KAAK,KAAK,GAAG;AACzE,UAAM,KAAK,aAAa,IAAI,cAAc,OAAO,CAAC,OAAO,QAAQ;AAC/D,SAAI,GAAG,IAAI,8CAA8C,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAAG;MAC5G;AACF,WAAO;;GAGT,MAAM,aAAa,MAAM,KAAK,yBAAyB,IAAI,cAAc,MAAM;AAC/E,OAAI,WAAW,SAAS,GAAG;AACzB,QAAI,GAAG,IAAI,+BAA+B,WAAW,KAAK,KAAK,GAAG;AAClE,WAAO;;AAET,SAAM,KAAK,WAAW,IAAI,cAAc,MAAM;AAC9C,OAAI,MAAM,KAAK,mBAAmB,IAAI,aAAa,EAAE;AACnD,QAAI,GAAG,IAAI,+CAA+C;AAC1D,WAAO;;GAGT,MAAM,kBAAkB,IAAI,IAAI,aAAa;GAC7C,MAAM,UAAU,UAAU,MAAM;GAEhC,MAAM,+BAA8B,MADV,KAAK,YAAY,IAAI,aAAa,EACZ,QAAQ,SACtD,CAAC,gBAAgB,IAAI,KAAK,IAAI,CAAC,QAAQ,IAAI,KAAK,QAAQ,OAAO,IAAI,CAAC,CACrE;AACD,OAAI,4BAA4B,SAAS,GAAG;AAC1C,QAAI,GAAG,IAAI,iDAAiD,4BAA4B,KAAK,KAAK,GAAG;AACrG,WAAO;;AAGT,SAAM,KAAK,YAAY,IAAI,cAAc,wBAAwB,KAAK,MAAM,CAAC;GAC7E,MAAM,YAAY,MAAM,KAAK,QAAQ,IAAI,aAAa;AACtD,OAAI,GAAG,IAAI,aAAa,MAAM,OAAO,cAAc,UAAU,MAAM,GAAG,EAAE,GAAG;AAC3E,UAAO;IAAE;IAAO;IAAW;WACpB,KAAK;AACZ,OAAI,GAAG,IAAI,gCAAgC,eAAe,QAAQ,IAAI,QAAQ,MAAM,KAAK,CAAC,KAAK,OAAO,IAAI,GAAG;AAC7G,UAAO;;;;;;;;;ACnKb,MAAM,qCAAqB,IAAI,KAAuC;;AAGtE,SAAS,iBAAiB,UAAmC,UAAuC,SAAyB;AAI3H,QAAO,GAAG,QAAQ,GAHJ,CAAC,GAAG,SAAS,CACxB,KAAK,MAAM,GAAG,EAAE,QAAQ,aAAa,CAAC,GAAG,SAAS,IAAI,EAAE,OAAO,IAAI,YAAY,CAC/E,MACuB,CAAC,KAAK,IAAI;;AAGtC,SAAS,oBAAoB,KAA4B;CACvD,MAAM,QAAQ,CACZ,GAAG,IAAI,QAAQ,KAAK,MAAM,GAAG,EAAE,QAAQ,aAAa,CAAC,GAAG,EAAE,UAAU,EACpE,GAAG,IAAI,QAAQ,KAAK,MAAM,GAAG,EAAE,QAAQ,aAAa,CAAC,GAAG,EAAE,WAAW,SAAS,CAC/E,CAAC,MAAM;AACR,QAAO,GAAG,IAAI,QAAQ,GAAG,MAAM,KAAK,IAAI;;AAG1C,SAAS,qBACP,KACA,UACA,UACA,SACS;AACT,KAAI,IAAI,YAAY,QAAS,QAAO;CACpC,MAAM,aAAa,IAAI,IAAI,SAAS,KAAK,MAAM,EAAE,QAAQ,aAAa,CAAC,CAAC;CACxE,MAAM,SAAS,IAAI,IAAI,CACrB,GAAG,IAAI,QAAQ,KAAK,MAAM,EAAE,QAAQ,aAAa,CAAC,EAClD,GAAG,IAAI,QAAQ,KAAK,MAAM,EAAE,QAAQ,aAAa,CAAC,CACnD,CAAC;AACF,KAAI,WAAW,SAAS,OAAO,KAAM,QAAO;AAC5C,MAAK,MAAM,MAAM,WAAY,KAAI,CAAC,OAAO,IAAI,GAAG,CAAE,QAAO;AACzD,MAAK,MAAM,UAAU,IAAI,QAEvB,KADgB,SAAS,IAAI,OAAO,OACzB,KAAK,OAAO,QAAS,QAAO;AAEzC,MAAK,MAAM,QAAQ,IAAI,SAAS;AAC9B,MAAI,CAAC,KAAK,UAAU,CAAC,KAAK,QAAS,QAAO;AAE1C,MADgB,SAAS,IAAI,KAAK,OACvB,KAAK,KAAK,QAAS,QAAO;;AAEvC,QAAO;;;;;;;;;;;;;;;;;;;AAoBT,SAAS,QACP,KACA,YACA,UACe;AACf,MAAK,MAAM,UAAU,IAAI,SAAS;AAChC,MAAI,CAAC,WAAW,IAAI,OAAO,QAAQ,aAAa,CAAC,CAC/C,QAAO,GAAG,OAAO,QAAQ;AAG3B,MADgB,SAAS,IAAI,OAAO,OACzB,KAAK,OAAO,QACrB,QAAO,GAAG,OAAO,QAAQ;;AAG7B,QAAO;;AAGT,eAAsB,wBACpB,aACA,MACA,UAA+B,EAAE,EACP;CAC1B,MAAM,MAAM,KAAK,cAAc;CAC/B,MAAM,MAAM,KAAK,OAAO,KAAK;CAC7B,MAAM,cAAwB,EAAE;AAEhC,KAAI,CAAC,KAAK,WAAW,CAAE,QAAO;EAAE,QAAQ;EAAY;EAAa;AAEjE,KAAI,mBAAmB,IAAI,YAAY,CAAE,QAAO;EAAE,QAAQ;EAAa;EAAa;CAEpF,MAAM,OAAO,YAAsC;EACjD,MAAM,WAAW,MAAM,KAAK,aAAa;AACzC,MAAI,aAAa,KAAM,QAAO;GAAE,QAAQ;GAAY;GAAa;EAEjE,MAAM,UAAU,MAAM,KAAK,gBAAgB;EAC3C,MAAM,2BAAW,IAAI,KAAqB;AAC1C,OAAK,MAAM,KAAK,SACd,UAAS,IAAI,EAAE,QAAQ,MAAM,KAAK,iBAAiB,EAAE,OAAO,CAAC,YAAY,UAAU,CAAC;EAEtF,MAAM,aAAa,IAAI,IAAI,SAAS,KAAK,MAAM,EAAE,QAAQ,aAAa,CAAC,CAAC;AAGxE,OAAK,MAAM,OAAO,KAAK,MAAM,UAAU,aAAa,CAAC,SAAS,aAAa,CAAC,EAAE;GAC5E,MAAM,cAAc,QAAQ,KAAK,YAAY,SAAS;AACtD,OAAI,CAAC,YAAa;AAClB,OAAI,iCAAiC,IAAI,KAAK,IAAI,cAAc;AAChE,QAAK,MAAM,OAAO,IAAI,MAAM,EAAE,QAAQ,eAAe,CAAC;AACtD,eAAY,KAAK,IAAI,KAAK;AAC1B,SAAM,KAAK,cAAc,IAAI,CAAC,OAAO,QAAQ;AAC3C,QAAI,uCAAuC,IAAI,KAAK,WAAW,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAAG;KAClH;;EAOJ,MAAM,aAAa,KAAK,MAAM,UAAU,aAAa,CAAC,aAAa,CAAC;AACpE,OAAK,MAAM,OAAO,YAAY;GAC5B,MAAM,eAAe,KAAK,MAAM,IAAI,aAAa,IAAI,aAAa,GAAG;GAErE,MAAM,gBAAgB,OAAO,SAAS,aAAa,GAAG,KAAK,GAAG,eAAe,OAAO;AACpF,OAAI,gBAAA,MAAqC;AACvC,QAAI,wDAAwD,IAAI,KAAK,2BAA2B,KAAK,MAAM,gBAAgB,IAAM,CAAC,IAAI;AACtI,SAAK,MAAM,OAAO,IAAI,MAAM,EAAE,QAAQ,UAAU,CAAC;;;AAGrD,MAAI,CAAC,QAAQ,SAAS,KAAK,MAAM,UAAU,aAAa,CAAC,aAAa,CAAC,CAAC,SAAS,EAC/E,QAAO;GAAE,QAAQ;GAAa;GAAa;AAG7C,MAAI,SAAS,WAAW,GAAG;AACzB,SAAM,KAAK,SAAS,CAAC,YAAY,GAAG;AACpC,UAAO;IAAE,QAAQ;IAAQ;IAAa;;EAIxC,MAAM,UAAU,iBAAiB,UAAU,UAAU,QAAQ;AAC7D,MAAI,CAAC,QAAQ,OAAO;AAElB,OADa,KAAK,MAAM,UAAU,aAAa,CAAC,SAAS,aAAa,CAC9D,CAAC,MAAM,QAAQ,qBAAqB,KAAK,UAAU,UAAU,QAAQ,CAAC,EAAE;AAC9E,UAAM,KAAK,SAAS,CAAC,YAAY,GAAG;AACpC,WAAO;KAAE,QAAQ;KAAQ;KAAa;;GAGxC,MAAM,gBADS,KAAK,MAAM,UAAU,aAAa,CAAC,SAAS,CAC/B,CAAC,MAAM,QACjC,oBAAoB,IAAI,KAAK,WAC7B,KAAK,GAAG,KAAK,MAAM,IAAI,aAAa,IAAI,aAAa,GAAG,GAAA,IACzD;AACD,OAAI,eAAe;AACjB,QAAI,kCAAkC,cAAc,KAAK,qCAAqC;AAC9F,WAAO;KAAE,QAAQ;KAAW;KAAa;;;AAI7C,MAAI,mDAAmD,SAAS,OAAO,mBAAmB;EAC1F,MAAM,aAAa,MAAM,KAAK,SAAS,SAAS;AAChD,QAAM,KAAK,SAAS,CAAC,YAAY,GAAG;AACpC,SAAO;GACL,QAAQ,WAAW,WAAW,UAAU,cAAc;GACtD;GACA;GACD;;CAGH,MAAM,UAAU,MAAM,CAAC,cAAc,mBAAmB,OAAO,YAAY,CAAC;AAC5E,oBAAmB,IAAI,aAAa,QAAQ;AAC5C,QAAO;;;;;;;;;;;;;;;;;;;;iBCtN0B;AAEnC,MAAMC,kBAAgBC,YAAUC,WAAS;AAIzC,MAAM,gBAAgB;CACpB;CACA;CACA;CACA;CACA;CACD;AAED,MAAM,qCAAqB,IAAI,KAA4B;AAE3D,eAAe,sBAAyB,aAAqB,IAAkC;CAC7F,MAAM,WAAW,mBAAmB,IAAI,YAAY,IAAI,QAAQ,SAAS;CACzE,IAAI;CACJ,MAAM,UAAU,IAAI,SAAe,YAAY;AAAE,YAAU;GAAW;CACtE,MAAM,OAAO,SAAS,YAAY,GAAG,CAAC,WAAW,QAAQ;AACzD,oBAAmB,IAAI,aAAa,KAAK;AACzC,OAAM,SAAS,YAAY,GAAG;AAC9B,KAAI;AACF,SAAO,MAAM,IAAI;WACT;AACR,WAAS;AACT,MAAI,mBAAmB,IAAI,YAAY,KAAK,KAAM,oBAAmB,OAAO,YAAY;;;AAoB5F,SAAS,cAA4B;CACnC,MAAM,WAAW,MAAgB,QAC/BF,gBAAc,UAAU,CAAC,WAAW,GAAG,KAAK,EAAE;EAC5C;EACA,UAAU;EACV,SAAS;EACT,WAAW,KAAK,OAAO;EACxB,CAAC;AAEJ,QAAO;EACL,qBAAqB,eAAe,YAAY;GAC9C,MAAM,SAAS,uBAAuB;IAAE;IAAe;IAAS,CAAC;AACjE,UAAO,OAAO,KAAK,UACf,EAAE,IAAI,MAAM,GACZ;IAAE,IAAI;IAAO,OAAO,OAAO,KAAK,SAAS;IAA8B;;EAE7E,WAAW,OAAO,aAAa,gBAAgB;AAC7C,SAAM,QAAQ;IAAC;IAAM;IAAa;IAAM;IAAa;IAAM;IAAM;IAAU,EAAE,QAAQ,YAAY,CAAC;;EAEpG,aAAa,OAAO,aAAa,gBAAgB;AAC/C,SAAM,QAAQ;IAAC;IAAM;IAAa;IAAM;IAAa;IAAQ;IAAM;IAAmB,EAAE,QAAQ,YAAY,CAAC;;EAE/G,gBAAgB,OAAO,aAAa,gBAAgB;GAClD,MAAM,EAAE,WAAW,MAAM,QAAQ;IAAC;IAAM;IAAa;IAAM;IAAa;IAAM;IAAK,EAAE,QAAQ,YAAY,CAAC;AAC1G,UAAO,OAAO,MAAM,KAAK,CAAC,QAAQ,MAAM,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC;;EAE/D,kBAAkB,kBAAkB;GAClC,MAAM,kBAAkB,KAAK,eAAe,gBAAgB;AAC5D,QAAK,MAAM,QAAQ,eAAe;IAChC,MAAM,WAAW,KAAK,iBAAiB,KAAK;AAC5C,QAAI,WAAW,SAAS,CAAE,QAAO;;AAEnC,UAAO;;EAET,kBAAkB,gBAAgB,SAAS,aAAa,QAAQ;EAChE,OAAO;GACL,WAAW,MAAM,cAAc,mCAAmC,MAAM,UAAU;GAClF,sBAAsB,kCAAkC;GACzD;EACF;;AAGH,SAAgB,mBAAmB,KAA4B;AAC7D,QAAO,SAAS,IAAI,aAAa;;AAGnC,SAAS,mBAAmB,KAA4B;AACtD,QAAO,YAAY,mBAAmB,IAAI;;;;;;;AAQ5C,eAAsB,eAAe,KAAoB,OAA8B,EAAE,EAAmB;CAC1G,MAAM,IAAI;EAAE,GAAG,aAAa;EAAE,GAAG;EAAM;CACvC,MAAM,SAAS,mBAAmB,IAAI;CACtC,MAAM,cAAc,EAAE,gBAAgB,IAAI,aAAa;AACvD,KAAI,YACF,KAAI;EAEF,MAAM,aAAY,MADI,EAAE,gBAAgB,YAAY,EAC1B,MAAM,IAAI,OAAO,cAAc,OAAO,QAAQ,uBAAuB,OAAO,GAAG,gBAAgB,CAAC;AAC1H,MAAI,YAAY,GAAI,QAAO,WAAW,UAAU;SAC1C;AAEV,QAAO,WAAW,OAAO;;;AAW3B,eAAsB,cAAc,KAAoB,OAA8B,EAAE,EAA0B;CAChH,MAAM,IAAI;EAAE,GAAG,aAAa;EAAE,GAAG;EAAM;CACvC,MAAM,cAAc,MAAM,eAAe,KAAK,EAAE;CAChD,MAAM,cAAc,EAAE,gBAAgB,IAAI,aAAa;AACvD,KAAI,CAAC,eAAe,CAAC,IAAI,eAAgB,QAAO;EAAE,QAAQ;EAAU;EAAa;AAEjF,KAAI,MADgB,EAAE,eAAe,aAAa,mBAAmB,IAAI,CAAC,CAAC,YAAY,EAAE,KAC3E,GAAG;AAEf,MAAI;AAAE,KAAE,MAAM,SAAS,IAAI,MAAM,KAAK;UAAU;AAChD,SAAO;GAAE,QAAQ;GAAU;GAAa;;AAE1C,QAAO;EAAE,QAAQ;EAAW;EAAa;;;;;;;AAgB3C,eAAsB,eAAe,KAAoB,OAA8B,EAAE,EAAiC;AACxH,QAAO,sBAAsB,IAAI,aAAa,YAAY;EACxD,MAAM,IAAI;GAAE,GAAG,aAAa;GAAE,GAAG;GAAM;EACvC,MAAM,MAAM,EAAE,cAAc;EAC5B,MAAM,UAAoB,EAAE;EAE5B,MAAM,SAAS,IAAI,QAAQ;AAC3B,MAAI,CAAC,OAAQ,QAAO;GAAE,SAAS;GAAO,OAAO;GAA6B;GAAS;EAInF,MAAM,SAAS,EAAE,MAAM,gBAAgB,CAAC,QAAQ,MAAM,EAAE,SAAS,IAAI,KAAK;AAC1E,SAAO,OAAO,SAAS,KAAK,OAAO,UAAA,GAA0B;GAC3D,MAAM,SAAS,OAAO,OAAO;AAC7B,OAAI,yDAAyE,OAAO,OAAO;AAC3F,OAAI;AACF,UAAM,yBAAyB,QAAQ,EAAE;YAClC,KAAK;AACZ,WAAO;KACL,SAAS;KACT,OAAO,eAAe,QAAS,IAAI,QAAQ,MAAM,KAAK,CAAC,MAAM,0BAA2B,OAAO,IAAI;KACnG;KACD;;AAEH,WAAQ,KAAK,OAAO,KAAK;;EAG3B,MAAM,WAAW,EAAE,mBAAmB,IAAI,cAAc,OAAO,QAAQ;AACvE,MAAI,CAAC,SAAS,GAAI,QAAO;GAAE,SAAS;GAAO,OAAO,SAAS,SAAS;GAA8B;GAAS;EAE3G,MAAM,cAAc,EAAE,gBAAgB,IAAI,aAAa;AACvD,MAAI,CAAC,YACH,QAAO;GAAE,SAAS;GAAO,OAAO,yBAAyB,IAAI,aAAa;GAAiB;GAAS;AAGtG,MAAI;AACF,SAAM,EAAE,UAAU,aAAa,mBAAmB,IAAI,CAAC;WAChD,KAAK;AACZ,UAAO;IACL,SAAS;IACT,OAAO,eAAe,QAAS,IAAI,QAAQ,MAAM,KAAK,CAAC,MAAM,sBAAuB,OAAO,IAAI;IAC/F;IACD;;AAGH,IAAE,MAAM,SAAS,IAAI,uBAAM,IAAI,MAAM,EAAC,aAAa,CAAC;EACpD,MAAM,cAAc,MAAM,eAAe,KAAK,EAAE;AAChD,MAAI,eAAe,IAAI,KAAK,gBAAgB,cAAc;AAC1D,SAAO;GAAE,SAAS;GAAM;GAAa;GAAS;GAC9C;;AAGJ,eAAe,yBAAyB,KAAoB,GAAgC;CAC1F,MAAM,cAAc,EAAE,gBAAgB,IAAI,aAAa;AACvD,KAAI,YACF,KAAI;AACF,QAAM,EAAE,YAAY,aAAa,mBAAmB,IAAI,CAAC;UAClD,KAAK;AACZ,IAAE,MAAM,eAAe,IAAI,KAAK,yBAAyB,eAAe,QAAQ,IAAI,QAAQ,MAAM,KAAK,CAAC,KAAK,OAAO,IAAI,GAAG;AAC3H,QAAM;;AAGV,KAAI;AAAE,IAAE,MAAM,SAAS,IAAI,MAAM,KAAK;SAAU;;;;;;;AAQlD,eAAsB,iBAAiB,KAAoB,OAA8B,EAAE,EAAiB;AAC1G,OAAM,sBAAsB,IAAI,aAAa,YAAY;AAEvD,QAAM,yBAAyB,KAAK;GADxB,GAAG,aAAa;GAAE,GAAG;GACI,CAAC;GACtC;;;;ACxPJ,MAAMG,kBAAgB,UAAU,SAAS;AAEzC,eAAsB,0BACpB,cACA,QAC+B;AAC/B,OAAMA,gBAAc,QAAQ,CAAC,6BAA6B,WAAW,EAAE;EACrE,KAAK;EACL,WAAW,KAAK,OAAO;EACxB,CAAC;CAEF,MAAM,EAAE,WAAW,MAAM,OAAO;EAAC;EAAU;EAAe;EAAM;EAAiC,EAAE,aAAa;AAChH,KAAI,OAAO,MAAM,KAAK,GAAI,QAAO,EAAE,SAAS,OAAO;AAEnD,OAAM,OAAO,CAAC,OAAO,iCAAiC,EAAE,aAAa;AACrE,QAAO,EAAE,SAAS,MAAM;;;;;;;;;;;;;;;;;;;;;;;;ACU1B,MAAM,gBAAgBC,YAAUC,WAAS;AAEzC,SAAS,kBAAkB,YAA4B;AACrD,KAAI,CAAC,oCAAoC,KAAK,WAAW,CACvD,OAAM,IAAI,MAAM,2BAA2B,aAAa;AAE1D,QAAO;;AAqDT,MAAM,aAA6C,CAAC,SAAS,aAAa;AAE1E,SAAgB,yBAAyB,KAA4B;CACnE,MAAM,MAAM,IAAI,QAAQ,KAAK,MAAM,EAAE,QAAQ,CAAC,KAAK,KAAK;CACxD,MAAM,iBAAiB,IAAI,YAAY,SAAS,IAC5C,gBAAgB,IAAI,YAAY,OAAO,0CACvC,IAAI,YAAY,KAAK,MAAM,EAAE,SAAS,KAAK,QAAQ,CAAC,CAAC,KAAK,KAAK,GAC/D;AACJ,QAAO,mBAAmB,IAAI,KAAK,IAAI,IAAI,GAAG;;AAGhD,eAAsB,qBACpB,MACA,aACA,MACwB;CACxB,MAAM,MAAM,KAAK,cAAc;CAE/B,MAAM,MAAM,KAAK,MAAM,IAAI,KAAK;AAChC,KAAI,CAAC,OAAO,IAAI,gBAAgB,YAC9B,QAAO;EAAE,SAAS;EAAO,QAAQ;EAAa,SAAS,2BAA2B;EAAQ;AAE5F,KAAI,CAAC,WAAW,SAAS,IAAI,OAAO,CAClC,QAAO;EACL,SAAS;EACT,QAAQ;EACR,SAAS,GAAG,KAAK,MAAM,IAAI,OAAO;EACnC;CAGH,MAAM,WAAW,IAAI,QAClB,KAAK,OAAO;EAAE,SAAS,EAAE;EAAS,MAAM,KAAK,kBAAkB,EAAE,QAAQ;EAAE,EAAE,CAC7E,QAAQ,EAAE,WAAW,CAAC,KAAK,SAAS;AACvC,KAAI,SAAS,SAAS,EAEpB,QAAO;EACL,SAAS;EACT,QAAQ;EACR,SACE,GAAG,KAAK,6DALG,SAAS,KAAK,EAAE,SAAS,WAAW,GAAG,QAAQ,IAAI,KAAK,UAAU,eAAe,GAAG,CAAC,KAAK,KAK1B,CAAC;EAE/E;CAGH,MAAM,UAAU,MAAM,KAAK,IAAI,WAAW;AAC1C,KAAI,IAAI,YAAY,SAAS;EAQ3B,MAAM,CAAC,aAAa,gBAAgB,MAAM,QAAQ,IAAI,CACpD,KAAK,IAAI,kBAAkB,IAAI,QAAQ,EACvC,KAAK,IAAI,kBAAkB,IAAI,KAAK,CACrC,CAAC;EACF,MAAM,WAAW,IAAI,IAAI,aAAa;EACtC,MAAM,UAAU,YAAY,QAAQ,MAAM,SAAS,IAAI,EAAE,CAAC;AAC1D,MAAI,QAAQ,SAAS,EACnB,QAAO;GACL,SAAS;GACT,QAAQ;GACR,SACE,GAAG,KAAK,qBAAqB,IAAI,QAAQ,MAAM,GAAG,EAAE,CAAC,sBAAsB,QAAQ,MAAM,GAAG,EAAE,CAAC,8BAClE,QAAQ,OAAO,oCACxC,QAAQ,MAAM,GAAG,EAAE,CAAC,KAAK,KAAK,GAAG,QAAQ,SAAS,IAAI,QAAQ,GAAG;GAExE;AAEH,MAAI,iBAAiB,KAAK,eAAe,IAAI,QAAQ,MAAM,GAAG,EAAE,CAAC,KAAK,QAAQ,MAAM,GAAG,EAAE,CAAC,2CAA2C;;CAGvI,IAAI;AACJ,KAAI;AACF,aAAW,MAAM,KAAK,IAAI,cAAc,IAAI,MAAM,yBAAyB,IAAI,CAAC;UACzE,KAAK;AACZ,SAAO;GACL,SAAS;GACT,QAAQ;GACR,SAAS,eAAe,QAAS,IAAI,QAAQ,MAAM,KAAK,CAAC,MAAM,iBAAkB,OAAO,IAAI;GAC7F;;AAEH,KAAI,iBAAiB,KAAK,sBAAsB,SAAS,MAAM,GAAG,EAAE,CAAC,IAAI,IAAI,QAAQ,OAAO,aAAa;AAIzG,MAAK,MAAM,OAAO,IAAI,MAAM,EAAE,QAAQ,YAAY,CAAC;AACnD,OAAM,KAAK,cAAc,IAAI,CAAC,OAAO,QAAQ;AAC3C,MAAI,iBAAiB,KAAK,2BAA2B,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAAG;GACxG;CAEF,MAAM,cAAwB,EAAE;AAChC,MAAK,MAAM,SAAS,KAAK,MAAM,UAAU,aAAa,CAAC,SAAS,aAAa,CAAC,EAAE;AAC9E,MAAI,MAAM,SAAS,IAAI,KAAM;AAC7B,OAAK,MAAM,OAAO,MAAM,MAAM,EAAE,QAAQ,eAAe,CAAC;AACxD,cAAY,KAAK,MAAM,KAAK;AAC5B,QAAM,KAAK,cAAc,MAAM,CAAC,YAAY,GAAG;;CAIjD,MAAM,mBAA6B,EAAE;AACrC,MAAK,MAAM,UAAU,IAAI,QACvB,KAAI,KAAK,cAAc,OAAO,SAAS;EACrC,cAAc,OAAO;EACrB,mBAAmB,OAAO,WAAW,OAAO;EAC7C,CAAC,CAAE,kBAAiB,KAAK,OAAO,QAAQ;KACpC,KAAI,iBAAiB,KAAK,mBAAmB,OAAO,QAAQ,8BAA8B;AAGjG,QAAO;EACL,SAAS;EACT,YAAY,IAAI;EAChB;EACA,SAAS,IAAI,QAAQ,KAAK,MAAM,EAAE,QAAQ;EAC1C;EACA;EACD;;;AAIH,SAAgB,uBAAuB,aAAwC;CAC7E,MAAM,UAAU,MAAgB,QAC9B,cAAc,OAAO,MAAM;EAAE;EAAK,WAAW,KAAK,OAAO;EAAM,CAAC;AAElE,QAAO;EACL,WAAW,YAAY;AACrB,SAAM,OAAO;IAAC;IAAS;IAAU;IAAO,EAAE,YAAY;AACtD,WAAQ,MAAM,OAAO,CAAC,aAAa,cAAc,EAAE,YAAY,EAAE,OAAO,MAAM;;EAEhF,eAAe,OAAO,YAAY,YAAY;GAC5C,MAAM,aAAa,kBAAkB,WAAW;GAChD,MAAM,eAAe,KAAK,QAAQ,EAAE,eAAe,WAAW,QAAQ,eAAe,IAAI,GAAG;AAC5F,SAAM,OAAO;IAAC;IAAY;IAAU;IAAW;IAAa,EAAE,YAAY,CAAC,YAAY,GAAG;AAC1F,SAAM,OAAO,CAAC,YAAY,QAAQ,EAAE,YAAY,CAAC,YAAY,GAAG;AAChE,SAAM,OAAO;IAAC;IAAY;IAAO;IAAY;IAAc;IAAc,EAAE,YAAY;AACvF,OAAI;IACF,MAAM,YAAY,UAAU;AAI5B,UAAM,OAAO;KAAC;KAAS;KAAW,MAHhB,OAAO;MAAC;MAAa;MAAY;MAAU,EAAE,aAAa,CACzE,WAAW,UAAU,CACrB,YAAY,WAAW;KACa;KAAM;KAAQ,EAAE,aAAa;IACpE,MAAM,EAAE,YAAY,MAAM,0BAA0B,cAAc,OAAO;AACzE,QAAI,QAAS,OAAM,OAAO;KAAC;KAAU;KAAW;KAAY,EAAE,aAAa;AAC3E,UAAM,OAAO;KAAC;KAAQ;KAAU;KAAY,EAAE,aAAa;AAC3D,YAAQ,MAAM,OAAO,CAAC,aAAa,OAAO,EAAE,aAAa,EAAE,OAAO,MAAM;aAChE;AACR,UAAM,OAAO;KAAC;KAAY;KAAU;KAAW;KAAa,EAAE,YAAY,CAAC,YAAY,GAAG;AAC1F,UAAM,OAAO,CAAC,YAAY,QAAQ,EAAE,YAAY,CAAC,YAAY,GAAG;;;EAGpE,mBAAmB,OAAO,YAAY;AACpC,OAAI,CAAC,oBAAoB,KAAK,QAAQ,CAAE,OAAM,IAAI,MAAM,oBAAoB,UAAU;GACtF,MAAM,EAAE,WAAW,MAAM,OAAO;IAAC;IAAQ;IAAe,GAAG,QAAQ;IAAe,EAAE,YAAY;AAChG,UAAO,OAAO,MAAM,KAAK,CAAC,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC,OAAO,QAAQ;;EAEhE,mBAAmB,OAAO,eAAe;GACvC,MAAM,aAAa,kBAAkB,WAAW;GAChD,MAAM,MAAM,MAAM,OAAO;IAAC;IAAa;IAAY,UAAU;IAAa,EAAE,YAAY,CACrF,WAAW,UAAU,aAAa,CAClC,YAAY,WAAW;GAE1B,MAAM,EAAE,WAAW,MAAM,OAAO;IAAC;IAAQ;IAAe,IADrC,MAAM,OAAO;KAAC;KAAc;KAAe;KAAI,EAAE,YAAY,EAAE,OAAO,MACrB,CAAC,IAAI;IAAM,EAAE,YAAY;AAC7F,UAAO,OAAO,MAAM,KAAK,CAAC,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC,OAAO,QAAQ;;EAEjE;;;;uBC1PgG;WACxD;AAe3C,SAAS,kBAAkB,QAA2D;CACpF,MAAM,UAAU,OAAO,QAAQ,SAAS,IAAI,OAAO,QAAQ,KAAK,KAAK,GAAG;AACxE,QACE,6CAA6C,OAAO,WAAW,cAAc,OAAO,SAAS,+HAEpE,QAAQ;;AAMrC,eAAsB,2BAA2B,QAAuB,OAAmB,EAAE,EAAiB;AAC5G,KAAI;AACF,MAAI,OAAO,YAAY,KAAM;EAE7B,MAAM,iBAAiB,KAAK,kBAAkB;EAC9C,MAAM,WAAW,KAAK,YAAY;EAClC,MAAM,mBAAmB,KAAK,mBAAmB,SAAiB,OAAO,WAAW,cAAc,KAAK,CAAC;EACxG,MAAM,UAAU,KAAK,WAAW;EAChC,MAAM,cAAc,KAAK,eAAe;AAExC,MAAI,CAAC,gBAAgB,CAAE;AACvB,MAAI,UAAU,CAAE;EAEhB,MAAM,YAAY,MAAM,iBAAiB,+BAA+B;AACxE,MAAI,UACF,KAAI;AACF,SAAM,QAAQ,gCAAgC,kBAAkB,OAAO,EAAE,qBAAqB;UACxF;AAKV,cAAY;GACV,QAAQ;GACR,SAAS,OAAO,QAAQ,MAAM,OAAO;GACrC,QAAQ;GACR,QACE;GACF,OAAO;IACL,YAAY,OAAO;IACnB,SAAS,OAAO;IAChB,UAAU,OAAO;IACjB;IACD;GACF,CAAC;SACI;;;;;;;;;;;;;;;iBCxBmC;AAO7C,MAAM,wBAAwB;AAC9B,MAAM,sBAAsB;AAC5B,MAAM,uCAAuC;AAU7C,MAAM,4CAA4B,IAAI,KAA2C;AAEjF,SAAgB,sBAAsB,UAAU,QAAQ,KAAK,EAAU;CACrE,MAAM,oBAAoB,sBAAsB,QAAQ;AACxD,KAAI,kBAAmB,QAAOC,UAAQ,kBAAkB,KAAK;CAE7D,MAAM,aAAaA,UAAQ,QAAQ;CACnC,MAAM,iBAAiB,WAAW,MAAM,8CAA8C;AACtF,QAAO,iBAAiB,KAAKA,UAAQ,eAAe,GAAG,GAAG;;AAG5D,SAAS,cAAsB;AAC7B,QAAO,uBAAuB;;AAGhC,SAAgB,6BAAsC;CACpD,MAAM,WAAW,sBAAsB;AACvC,QAAO,SAAS,SAAS,aAAaA,UAAQ,SAAS,SAAS,KAAK,aAAa;;;AAIpF,eAAe,cAA8C;CAC3D,MAAM,SAAS,MAAM,uCAAuC;AAC5D,KAAI,CAAC,OAAQ,QAAO;CACpB,MAAM,EAAE,mBAAmB,2BAA2B,MAAM,OAAO;CAUnE,MAAM,aAA4B,MATd,OAAO,WACzB,kBAAkB,OAAO,gBAAgB,aAAa,EAAE;EACtD,UAAU;EACV,eAAe,SAAS,WACtB,QAAQ,IAAI,eAAe,QAAQ,+CAA+C,OAAO,iCAAiC;EAC7H,CAAC,CAAC,KACD,OAAO,QAAQC,MAAkB,CAClC,CACF,EACuC,KAAK,UAAU;EACrD,SAAS,KAAK;EACd,OAAO,KAAK;EACZ,QAAQ,KAAK;EACb,GAAI,KAAK,OAAO,KAAA,IAAY,EAAE,IAAI,KAAK,IAAI,GAAG,EAAE;EAChD,GAAI,KAAK,UAAU,KAAA,IAAY,EAAE,OAAO,KAAK,OAAO,GAAG,EAAE;EACzD,eAAe,KAAK;EACrB,EAAE;CACH,MAAM,QAAQ,MAAM,sBAAsB,IAAI,IAAI,UAAU,KAAK,MAAM,EAAE,QAAQ,CAAC,CAAC;AACnF,QAAO,CAAC,GAAG,WAAW,GAAG,MAAM;;;;;;;;;;;AAYjC,eAAe,sBAAsB,iBAAoD;AACvF,KAAI;EACF,MAAM,EAAE,oBAAoB,yBAAyB,MAAM,OAAO;EAClE,MAAM,EAAE,gCAAgC,MAAM,OAAO;EACrD,MAAM,EAAE,aAAa,MAAM,OAAO;EAClC,MAAM,EAAE,cAAc,MAAM,OAAO;EACnC,MAAM,gBAAgB,UAAU,SAAS;EACzC,MAAM,OAAO,aAAa;EAC1B,MAAM,SAAyB,EAAE;AACjC,OAAK,MAAM,CAAC,SAAS,OAAO,OAAO,QAAQ,oBAAoB,CAAC,EAAE;AAChE,OAAI,gBAAgB,IAAI,QAAQ,CAAE;AAClC,OAAI,GAAG,kBAAkB,KAAM;AAC/B,OAAI,CAAC,qBAAqB,GAAG,CAAC,SAAU;GACxC,MAAM,UAAU,4BAA4B,QAAQ;AACpD,OAAI,CAAC,WAAWD,UAAQ,QAAQ,YAAY,KAAK,KAAM;GACvD,MAAM,SAAS,WAAW,QAAQ,aAAa;AAC/C,OAAI;AACF,UAAM,cAAc,OAAO;KAAC;KAAY;KAAY;KAAW,cAAc;KAAS,EAAE;KAAE,KAAK;KAAM,SAAS;KAAQ,CAAC;WACjH;AACN;;AAEF,WAAQ,IAAI,eAAe,QAAQ,wFAAwF;AAC3H,UAAO,KAAK;IAAE;IAAS,OAAO;IAAS;IAAQ,eAAe,EAAE;IAAE,CAAC;;AAErE,SAAO;UACA,KAAK;AACZ,UAAQ,KAAK,yCAAyC,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAAG;AACzG,SAAO,EAAE;;;AAMb,SAAS,cAAc,UAA2C;AAEhE,SADe,SAAS,IAAI,QAAQ,MAAM,IAAI,CAAC,MAC7B,OAAO,aAAa;;AAGxC,eAAe,qBAAqB,UAA2D;CAC7F,MAAM,OAAO,aAAa;CAC1B,MAAM,QAAQ,yBAAyB;AACvC,QAAO,sBACL;EACE,aAAa;EACb,OAAO,cAAc,SAAS;EAC9B,0BAAS,IAAI,MAAM,EAAC,aAAa;EACjC;EACA,kBAAkB,MAAM,sBAAsB,KAAK;EACpD,EACD;EACE,KAAK,0BAA0B,KAAK;EACpC;EACA,iBAAiB,wBAAwB;EACzC,MAAM,QAAQ,QAAQ,IAAI,IAAI;EAC/B,CACF;;AAGH,eAAe,aAA4B;CACzC,MAAM,OAAO,aAAa;AAC1B,OAAM,sBAAsB,MAAM;EAChC,OAAO,yBAAyB;EAChC,GAAG,6BAA6B,KAAK;EACrC,gBAAgB,QAAQ,iBAAiB,IAAI;EAC7C,MAAM,QAAQ,QAAQ,IAAI,IAAI;EAC/B,CAAC;;;AAIJ,eAAsB,qBAAqB,UAA+B,EAAE,EAA4B;CACtG,MAAM,OAAO,aAAa;CAC1B,MAAM,UAAU,0BAA0B,KAAK;AAC/C,QAAO,wBAAwB,MAAM;EACnC,iBAAiB,qBAAqB;EACtC;EACA,sBAAsB,QAAQ,WAAW;EACzC,mBAAmB,WAAW,QAAQ,cAAc,OAAO;EAC3D,OAAO,yBAAyB;EAChC,UAAU;EACV,gBAAgB,QAAQ,iBAAiB,IAAI;EAC7C,SAAS;EACT,MAAM,QAAQ,QAAQ,IAAI,IAAI;EAC/B,EAAE,QAAQ;;AAGb,IAAI,kBAAyD;AAE7D,SAAgB,0BAAmC;AACjD,KAAI,CAAC,4BAA4B,CAAE,QAAO;AAC1C,KAAI,gBAAiB,QAAO;AAC5B,mBAAkB,kBAAkB;AAC7B,wBAAsB,CAAC,OAAO,QAAQ;AACzC,WAAQ,KAAK,sCAAsC,eAAe,QAAQ,IAAI,UAAU,IAAI;IAC5F;IACD,sBAAsB;AACzB,iBAAgB,SAAS;AACpB,uBAAsB,CAAC,OAAO,QAAQ;AACzC,UAAQ,KAAK,yCAAyC,eAAe,QAAQ,IAAI,UAAU,IAAI;GAC/F;AACF,QAAO;;AAGT,SAAgB,yBAA+B;AAC7C,KAAI,iBAAiB;AACnB,gBAAc,gBAAgB;AAC9B,oBAAkB;;;AA6BtB,eAAe,WAAc,OAAqB,aAAqB,QAAmD;CACxH,IAAI,OAAO;CACX,MAAM,UAAU,MAAM,KAAK,EAAE,QAAQ,KAAK,IAAI,aAAa,MAAM,OAAO,EAAE,EAAE,YAAY;AACtF,SAAO,OAAO,MAAM,QAAQ;GAC1B,MAAM,OAAO,MAAM;AACnB,SAAM,OAAO,KAAK;;GAEpB;AACF,OAAM,QAAQ,IAAI,QAAQ;;AAG5B,eAAe,4BAA4B,UAAgF;CACzH,MAAM,wBAAQ,IAAI,KAAwC;AAC1D,KAAI,SAAS,SAAS,EAAG,QAAO;CAEhC,MAAM,OAAO,aAAa;AAC1B,OAAM,WAAW,CAAC,GAAG,SAAS,EAAE,sCAAsC,OAAO,YAAY;EACvF,MAAM,eAAe,QAAQ,aAAa;EAC1C,MAAM,WAAW,0BAA0B,IAAI,aAAa;AAC5D,MAAI,SACF,KAAI;GACF,MAAM,EAAE,YAAY,MAAME,OAAK,SAAS,KAAK;AAC7C,OAAI,SAAS,YAAY,SAAS;AAChC,UAAM,IAAI,cAAc,SAAS,SAAS;AAC1C;;UAEI;AACN,6BAA0B,OAAO,aAAa;;AAIlD,MAAI;GACF,MAAM,QAAQ,MAAM,OAAO,WAAW,kBAAkB,MAAM,aAAa,CAAC;AAC5E,OAAI,CAAC,OAAO;AACV,8BAA0B,OAAO,aAAa;AAC9C,UAAM,IAAI,cAAc,EAAE,CAAC;AAC3B;;GAEF,MAAM,EAAE,YAAY,MAAMA,OAAK,MAAM,KAAK;GAE1C,MAAM,WAAW,sBAAsB,MADhB,OAAO,WAAW,mBAAmB,MAAM,KAAK,CAAC,CACxB,CAAC,KAAK,QAAQ;IAAE,OAAO,GAAG;IAAO,QAAQ,GAAG;IAAQ,EAAE;AACtG,6BAA0B,IAAI,cAAc;IAAE,MAAM,MAAM;IAAM;IAAS;IAAU,CAAC;AACpF,SAAM,IAAI,cAAc,SAAS;UAC3B;AACN,SAAM,IAAI,cAAc,EAAE,CAAC;;GAE7B;AACF,QAAO;;;AAIT,eAAsB,2BAA4D;AAEhF,KAAI,CAAC,MADgB,uCAAuC,CAC/C,QAAO,EAAE;CAEtB,MAAM,QAAQ,uBAAuB;EAAE,aAAa,aAAa;EAAE,OAAO;EAAqB,CAAC;AAChG,KAAI,MAAM,WAAW,EAAG,QAAO,EAAE;CAEjC,MAAM,UAAU,MAAM,4BAA4B,IADvB,IAAI,MAAM,SAAS,QAAQ,IAAI,QAAQ,KAAK,WAAW,OAAO,QAAQ,aAAa,CAAC,CAAC,CAChD,CAAC;CACjE,MAAM,UAAkC,EAAE;AAC1C,MAAK,MAAM,OAAO,OAAO;EACvB,MAAM,QAAQ,MAAM,cAAc,IAAI;EACtC,MAAM,UAAwC,EAAE;AAChD,OAAK,MAAM,UAAU,IAAI,QACvB,SAAQ,KAAK;GACX,SAAS,OAAO;GAChB,OAAO,OAAO;GACd,QAAQ,OAAO;GACf,GAAI,OAAO,OAAO,KAAA,IAAY,EAAE,IAAI,OAAO,IAAI,GAAG,EAAE;GACpD,GAAI,OAAO,UAAU,KAAA,IAAY,EAAE,OAAO,OAAO,OAAO,GAAG,EAAE;GAC7D,YAAY,OAAO;GACnB,oBAAoB,QAAQ,IAAI,OAAO,QAAQ,aAAa,CAAC,IAAI,EAAE;GACpE,CAAC;AAEJ,UAAQ,KAAK;GACX,MAAM,IAAI;GACV,QAAQ,IAAI;GACZ,SAAS,IAAI;GACb,WAAW,IAAI;GACf,WAAW,IAAI;GACf;GACA,SAAS,IAAI;GACb,aAAa,IAAI;GACjB,OAAO;IAAE,QAAQ,MAAM;IAAQ,aAAa,MAAM;IAAa;GAChE,CAAC;;AAEJ,QAAO;;;AAUT,eAAsB,yBAA8D;CAClF,MAAM,CAAC,aAAa,uBAAuB;EACzC,aAAa,aAAa;EAC1B,UAAU,CAAC,QAAQ;EACnB,OAAO;EACR,CAAC;AACF,KAAI,CAAC,UAAW,QAAO;AACvB,QAAO;EACL,YAAY,UAAU;EACtB,SAAS,UAAU,QAAQ,KAAK,WAAW,OAAO,QAAQ;EAC1D,QAAQ;EACT;;AAGH,eAAsB,8BAA8B,MAElD;CACA,MAAM,MAAM,qBAAqB,KAAK;AACtC,KAAI,CAAC,IAAK,QAAO;EAAE,IAAI;EAAO,OAAO,2BAA2B;EAAQ,QAAQ;EAAK;AACrF,KAAI,IAAI,WAAW,WAAW,IAAI,WAAW,aAC3C,QAAO;EAAE,IAAI;EAAO,OAAO,GAAG,KAAK,MAAM,IAAI,OAAO;EAAyC,QAAQ;EAAK;CAE5G,MAAM,SAAS,MAAM,eAAe,IAAI;AACxC,KAAI,CAAC,OAAO,QAAS,QAAO;EAAE,IAAI;EAAO,OAAO,OAAO,SAAS;EAAsB,QAAQ;EAAK;AACnG,QAAO;EAAE,IAAI;EAAM,aAAa,OAAO;EAAc,SAAS,OAAO;EAAS;;AAGhF,eAAsB,gCACpB,MACA,eACwB;CACxB,MAAM,OAAO,aAAa;CAC1B,MAAM,EAAE,4BAA4B,MAAM,OAAO;CACjD,MAAM,SAAS,MAAM,qBAAqB,MAAM,MAAM;EACpD,KAAK,uBAAuB,KAAK;EACjC,OAAO;GAAE,GAAG,yBAAyB;GAAE,MAAM,MAAM,qBAAqB,EAAE;GAAE;EAC5E,gBAAgB,QAAQ,iBAAiB,IAAI;EAC7C;EACA,mBAAmB;EACnB,MAAM,QAAQ,QAAQ,IAAI,IAAI;EAC/B,CAAC;AACF,OAAM,2BAA2B,OAAO,CAAC,YAAY,GAAG;AACxD,QAAO"}
1
+ {"version":3,"file":"uat-train-BLBAbJ8Y.js","names":["resolve","relative","sep","existsSync","readFileSync","relative","isAbsolute","resolve","sep","totalmem","freemem","execFileAsync","promisify","execFile","execFileAsync","promisify","execFile","defaultDeps","execFileAsync","promisify","execFile","execFileAsync","promisify","execFile","resolve","nodeServicesLayer","stat"],"sources":["../../src/dashboard/server/identity.ts","../../src/dashboard/server/services/dashboard-db-task.ts","../../src/dashboard/server/services/flywheel-actions.ts","../../src/lib/cloister/uat-candidate-name.ts","../../src/lib/cloister/uat-generation-engine.ts","../../src/lib/cloister/uat-generation-deps.ts","../../src/lib/cloister/uat-conflict-agent.ts","../../src/lib/cloister/uat-reconciler.ts","../../src/lib/cloister/uat-stack.ts","../../src/lib/cloister/file-size-reconcile.ts","../../src/lib/cloister/uat-promote.ts","../../src/lib/cloister/uat-promote-notify.ts","../../src/dashboard/server/services/uat-train.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { cwd } from 'node:process';\nimport { relative, resolve, sep } from 'node:path';\n\nimport { parse as parseToml } from '@iarna/toml';\n\nimport { CONFIG_FILE } from '../../lib/paths.js';\n\nexport type DashboardMode = 'primary' | 'peer';\n\nexport interface DashboardIdentity {\n readonly repoRoot: string;\n readonly mode: DashboardMode;\n}\n\nexport function getDashboardIdentity(): DashboardIdentity {\n return {\n repoRoot: resolve(cwd()),\n mode: process.env.OVERDECK_DISABLE_DEACON === '1' ? 'peer' : 'primary',\n };\n}\n\nexport function isWorkspaceRepoRoot(repoRoot: string): boolean {\n return /(^|\\/)workspaces\\/feature-[^/]+$/i.test(repoRoot.replaceAll('\\\\', '/'));\n}\n\nfunction isSameOrInside(parent: string, candidate: string): boolean {\n const rel = relative(resolve(parent), resolve(candidate));\n return rel === '' || (!rel.startsWith('..') && !rel.startsWith(sep));\n}\n\nexport function readHostDashboardApiPort(defaultPort = 3011): number {\n if (!existsSync(CONFIG_FILE)) return defaultPort;\n try {\n const config = parseToml(readFileSync(CONFIG_FILE, 'utf-8')) as {\n dashboard?: { api_port?: unknown };\n };\n const value = config.dashboard?.api_port;\n return typeof value === 'number' && Number.isFinite(value) ? value : defaultPort;\n } catch {\n return defaultPort;\n }\n}\n\nexport function shouldRefuseHostDashboardPort(input: {\n readonly repoRoot: string;\n readonly mode: DashboardMode;\n readonly port: number;\n readonly hostDashboardApiPort?: number;\n readonly runningInContainer?: boolean;\n}): boolean {\n const hostDashboardApiPort = input.hostDashboardApiPort ?? readHostDashboardApiPort();\n if (input.port !== hostDashboardApiPort) return false;\n\n const repoRoot = resolve(input.repoRoot);\n const runningInContainer =\n input.runningInContainer ?? (existsSync('/.dockerenv') || repoRoot === '/workspaces/overdeck');\n if (runningInContainer) {\n return false;\n }\n if (input.mode === 'peer') return true;\n\n if (isWorkspaceRepoRoot(repoRoot)) return true;\n\n const workspacesDir = resolve(repoRoot, '..');\n return workspacesDir.endsWith(`${sep}workspaces`) && isSameOrInside(workspacesDir, repoRoot);\n}\n","import { randomUUID } from 'node:crypto';\nimport { Worker } from 'node:worker_threads';\nimport {\n aggregateDiscoveredSessionCost,\n aggregateDiscoveredSessionCostBy,\n countDiscoveredSessions,\n findDiscoveredSessions,\n getDiscoveredSessionById,\n getDiscoveredStats,\n} from '../../../lib/overdeck/discovered-sessions.js';\nimport { getConversationByName } from '../../../lib/overdeck/conversations.js';\nimport { getSetting, setSetting } from '../../../lib/overdeck/control-settings.js';\nimport type { ConversationFilter } from '../../../lib/overdeck/discovered-sessions.js';\nimport { getSessionsFeedFacets, listSessionsFeed } from '../../../lib/overdeck/sessions-feed.js';\nimport type { SessionsFeedFilter } from '../../../lib/overdeck/sessions-feed.js';\nimport { searchSessions } from '../../../lib/conversations/search.js';\nimport type { SearchQuery } from '../../../lib/conversations/search.js';\nimport { scan } from '../../../lib/conversations/scanner.js';\nimport type { ScanOptions } from '../../../lib/conversations/scanner.js';\nimport { enrichSessions, CostThresholdError } from '../../../lib/conversations/enrichment/index.js';\nimport type { EnrichOptions } from '../../../lib/conversations/enrichment/index.js';\nimport { embedSessions } from '../../../lib/conversations/embeddings/index.js';\nimport type { EmbedSessionsOptions } from '../../../lib/conversations/embeddings/index.js';\n\nexport type DashboardDbOperation =\n | 'getDiscoveredStats'\n | 'listDiscoveredSessions'\n | 'listSessionsFeed'\n | 'getSessionsFeedFacets'\n | 'getDiscoveredSessionById'\n | 'aggregateDiscoveredSessionCost'\n | 'aggregateDiscoveredSessionCostBy'\n | 'searchSessions'\n | 'searchSessionsSemantic'\n | 'scanConversations'\n | 'enrichSessions'\n | 'embedSessions'\n | 'getConversationByName'\n | 'getSetting'\n | 'setSetting'\n | 'getArtifactBySlug'\n | 'listArtifactsForWorkspaceOrIssue'\n | 'unshareArtifactBySlug';\n\ntype ProgressHandler = (progress: unknown) => void | Promise<void>;\ntype WorkerLane = 'read' | 'long' | 'semantic';\n\ninterface PendingJob {\n lane: WorkerLane;\n operation: DashboardDbOperation;\n resolve: (value: unknown) => void;\n reject: (err: Error) => void;\n progressListeners: Set<ProgressHandler>;\n progressChain: Promise<void>;\n timeout: NodeJS.Timeout | null;\n}\n\ninterface SharedJob {\n lane: WorkerLane;\n promise: Promise<unknown>;\n progressListeners: Set<ProgressHandler>;\n}\n\ninterface WorkerResponse {\n id: string;\n ok?: boolean;\n result?: unknown;\n progress?: unknown;\n error?: {\n name?: string;\n message?: string;\n stack?: string;\n estimatedCost?: number;\n threshold?: number;\n sessionCount?: number;\n };\n}\n\nconst MAX_PENDING_JOBS = 32;\nconst SEMANTIC_SEARCH_TIMEOUT_MS = Number.parseInt(process.env['OVERDECK_SEMANTIC_SEARCH_TIMEOUT_MS'] ?? '15000', 10);\nconst COALESCED_OPERATIONS = new Set<DashboardDbOperation>([\n 'scanConversations',\n 'enrichSessions',\n 'embedSessions',\n 'searchSessionsSemantic',\n]);\n\nconst workers: Record<WorkerLane, Worker | null> = { read: null, long: null, semantic: null };\nconst pending = new Map<string, PendingJob>();\nconst sharedJobs = new Map<string, SharedJob>();\nlet latestSemanticJobId: string | null = null;\n\nfunction workerScriptUrl(): URL {\n return import.meta.url.endsWith('.ts')\n ? new URL('./dashboard-db-worker.ts', import.meta.url)\n : new URL('./dashboard-db-worker.js', import.meta.url);\n}\n\nfunction failPendingForLane(lane: WorkerLane, err: Error): void {\n for (const [id, job] of pending.entries()) {\n if (job.lane !== lane) continue;\n if (job.timeout) clearTimeout(job.timeout);\n job.reject(err);\n pending.delete(id);\n }\n for (const [key, job] of sharedJobs.entries()) {\n if (job.lane === lane) sharedJobs.delete(key);\n }\n if (lane === 'semantic') latestSemanticJobId = null;\n}\n\nfunction stableStringify(value: unknown): string {\n if (value == null || typeof value !== 'object') return JSON.stringify(value);\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`;\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => typeof v !== 'function' && v !== undefined)\n .sort(([a], [b]) => a.localeCompare(b));\n return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).join(',')}}`;\n}\n\nfunction coalescingKey(operation: DashboardDbOperation, payload: unknown): string | null {\n if (!COALESCED_OPERATIONS.has(operation)) return null;\n return `${operation}:${stableStringify(payload)}`;\n}\n\nfunction workerLane(operation: DashboardDbOperation): WorkerLane {\n if (operation === 'searchSessionsSemantic') return 'semantic';\n return COALESCED_OPERATIONS.has(operation) ? 'long' : 'read';\n}\n\nfunction cancelOlderSemanticSearches(): void {\n if (!latestSemanticJobId && !workers.semantic) return;\n failPendingForLane('semantic', new Error('Superseded by a newer semantic search'));\n void workers.semantic?.terminate();\n workers.semantic = null;\n}\n\nfunction aggregateDiscoveredSessionCostByPayload(payload: unknown) {\n if (typeof payload === 'string') {\n return aggregateDiscoveredSessionCostBy(payload as 'workspace' | 'model' | 'day' | 'month');\n }\n const input = payload as { groupBy?: 'workspace' | 'model' | 'day' | 'month'; filter?: ConversationFilter } | undefined;\n return aggregateDiscoveredSessionCostBy(input?.groupBy ?? 'workspace', input?.filter ?? {});\n}\n\nfunction getWorker(lane: WorkerLane): Worker {\n const existing = workers[lane];\n if (existing) return existing;\n\n const worker = new Worker(workerScriptUrl(), {\n execArgv: process.execArgv.filter((arg) => !arg.startsWith('--inspect')),\n } as ConstructorParameters<typeof Worker>[1]);\n workers[lane] = worker;\n\n worker.on('message', (message: WorkerResponse) => {\n const job = pending.get(message.id);\n if (!job) return;\n\n if (message.progress !== undefined) {\n job.progressChain = job.progressChain.then(async () => {\n for (const listener of job.progressListeners) {\n await listener(message.progress);\n }\n });\n return;\n }\n\n pending.delete(message.id);\n if (job.timeout) clearTimeout(job.timeout);\n if (job.lane === 'semantic' && latestSemanticJobId === message.id) latestSemanticJobId = null;\n\n if (message.ok) {\n job.progressChain.then(() => job.resolve(message.result), job.reject);\n return;\n }\n\n const err = message.error?.name === 'CostThresholdError'\n ? new CostThresholdError(\n message.error.estimatedCost ?? 0,\n message.error.threshold ?? 0,\n message.error.sessionCount ?? 0,\n )\n : new Error(message.error?.message ?? 'Dashboard database worker failed');\n err.name = message.error?.name ?? 'DashboardDatabaseWorkerError';\n err.stack = message.error?.stack;\n job.progressChain.then(() => job.reject(err), job.reject);\n });\n\n worker.on('error', (err) => {\n failPendingForLane(lane, err);\n if (workers[lane] === worker) workers[lane] = null;\n });\n\n worker.on('exit', (code) => {\n if (code !== 0) failPendingForLane(lane, new Error(`Dashboard database ${lane} worker exited with code ${code}`));\n if (workers[lane] === worker) workers[lane] = null;\n });\n\n return worker;\n}\n\nasync function runInline(\n operation: DashboardDbOperation,\n payload: unknown,\n onProgress?: (progress: unknown) => void | Promise<void>,\n): Promise<unknown> {\n switch (operation) {\n case 'getDiscoveredStats':\n return getDiscoveredStats();\n case 'listDiscoveredSessions': {\n const filter = payload as ConversationFilter;\n return {\n sessions: findDiscoveredSessions(filter),\n total: countDiscoveredSessions({ ...filter, limit: undefined, offset: undefined }),\n };\n }\n case 'listSessionsFeed':\n return listSessionsFeed(payload as SessionsFeedFilter);\n case 'getSessionsFeedFacets':\n return getSessionsFeedFacets(payload as SessionsFeedFilter);\n case 'getDiscoveredSessionById':\n return getDiscoveredSessionById(payload as number);\n case 'aggregateDiscoveredSessionCost':\n return aggregateDiscoveredSessionCost(payload as ConversationFilter);\n case 'aggregateDiscoveredSessionCostBy':\n return aggregateDiscoveredSessionCostByPayload(payload);\n case 'searchSessions':\n case 'searchSessionsSemantic':\n return searchSessions(payload as SearchQuery);\n case 'scanConversations':\n return scan({ ...(payload as ScanOptions), onProgress });\n case 'enrichSessions':\n return enrichSessions({ ...(payload as EnrichOptions), onProgress });\n case 'embedSessions':\n return embedSessions({ ...(payload as EmbedSessionsOptions), autoInstall: true, onProgress });\n case 'getConversationByName':\n return getConversationByName(payload as string);\n case 'getSetting':\n return getSetting(payload as string);\n case 'setSetting': {\n const input = payload as { key: string; value: string };\n setSetting(input.key, input.value);\n return null;\n }\n case 'getArtifactBySlug': {\n const { getArtifactBySlugJob } = await import('./artifact-index-jobs.js');\n return getArtifactBySlugJob(payload as string);\n }\n case 'listArtifactsForWorkspaceOrIssue': {\n const { listArtifactsForWorkspaceOrIssueJob } = await import('./artifact-index-jobs.js');\n return listArtifactsForWorkspaceOrIssueJob(payload as string);\n }\n case 'unshareArtifactBySlug': {\n const { unshareArtifactBySlugJob } = await import('./artifact-index-jobs.js');\n return unshareArtifactBySlugJob(payload as string);\n }\n }\n}\n\nexport function runDashboardDbJob<T>(\n operation: DashboardDbOperation,\n payload?: unknown,\n onProgress?: (progress: unknown) => void | Promise<void>,\n): Promise<T> {\n if (import.meta.url.endsWith('.ts') && process.env['VITEST']) {\n return runInline(operation, payload, onProgress) as Promise<T>;\n }\n\n const key = coalescingKey(operation, payload);\n const existing = key ? sharedJobs.get(key) : undefined;\n if (existing) {\n if (onProgress) existing.progressListeners.add(onProgress);\n return existing.promise as Promise<T>;\n }\n\n if (pending.size >= MAX_PENDING_JOBS) {\n return Promise.reject(new Error('Dashboard database worker queue is full'));\n }\n\n const id = randomUUID();\n const lane = workerLane(operation);\n if (operation === 'searchSessionsSemantic') {\n cancelOlderSemanticSearches();\n latestSemanticJobId = id;\n }\n const progressListeners = new Set<ProgressHandler>();\n if (onProgress) progressListeners.add(onProgress);\n\n const promise = new Promise<T>((resolve, reject) => {\n const timeout = operation === 'searchSessionsSemantic'\n ? setTimeout(() => {\n const job = pending.get(id);\n if (!job) return;\n pending.delete(id);\n for (const [sharedKey, sharedJob] of sharedJobs.entries()) {\n if (sharedJob.promise === promise) sharedJobs.delete(sharedKey);\n }\n if (latestSemanticJobId === id) latestSemanticJobId = null;\n reject(new Error('Semantic search timed out'));\n void workers.semantic?.terminate();\n workers.semantic = null;\n }, Number.isFinite(SEMANTIC_SEARCH_TIMEOUT_MS) ? SEMANTIC_SEARCH_TIMEOUT_MS : 15000)\n : null;\n pending.set(id, {\n lane,\n operation,\n resolve: resolve as (value: unknown) => void,\n reject,\n progressListeners,\n progressChain: Promise.resolve(),\n timeout,\n });\n getWorker(lane).postMessage({ id, operation, payload });\n });\n\n if (key) {\n sharedJobs.set(key, { lane, promise, progressListeners });\n promise.then(\n () => sharedJobs.delete(key),\n () => sharedJobs.delete(key),\n );\n }\n\n return promise;\n}\n","import { exec, spawn } from 'node:child_process';\nimport { readFile, realpath } from 'node:fs/promises';\nimport { freemem, totalmem } from 'node:os';\nimport { isAbsolute, join, relative, resolve, sep } from 'node:path';\nimport { promisify } from 'node:util';\nimport type { FlywheelStatus } from '@overdeck/contracts';\nimport { Effect } from 'effect';\nimport { loadConfigNoMigration, resolveModel, type FlywheelScope, type RoleEffort } from '../../../lib/config-yaml.js';\nimport { resolveHarness } from '../../../lib/harness-resolve.js';\nimport { FLYWHEEL_ORCHESTRATOR_AGENT_ID, isFlywheelDevcontainerRuntime, loadResumeSessionId, saveResumeSessionId, spawnFlywheelAgent } from '../../../lib/cloister/flywheel.js';\nimport { FLYWHEEL_ACTIVE_RUN_ID_KEY, FLYWHEEL_GLOBAL_PAUSE_KEY } from '../../../lib/overdeck/control-settings.js';\nimport { isPaneDead, killSession, sessionExists } from '../../../lib/tmux.js';\nimport {\n abortFlywheelRun,\n getFlywheelRunDetail,\n listFlywheelRuns,\n nextFlywheelRunId,\n readFlywheelLaunchMetadata,\n resolveLiveFlywheelRunId,\n writeFlywheelLaunchMetadata,\n writeLatestFlywheelStatus,\n} from './flywheel-run-state.js';\nimport { runDashboardDbJob } from './dashboard-db-task.js';\n\ninterface StartOptions {\n brief?: string;\n cwd?: string;\n}\n\ninterface ReportOpenOptions {\n runId?: string;\n opener?: (path: string) => void | Promise<void>;\n}\n\ninterface FlywheelGateSnapshot {\n paused: boolean;\n activeRunId: string | null;\n}\n\ninterface ResolvedFlywheelRoleConfig {\n harness: 'claude-code' | 'ohmypi' | 'codex';\n model: string;\n effort: RoleEffort;\n minAgents: number;\n maxAgents: number;\n scope: FlywheelScope;\n}\n\nconst DEFAULT_BRIEF_PATH = 'docs/flywheel-brief.md';\nconst execAsync = promisify(exec);\n\nfunction mb(bytes: number): number {\n return Math.round(bytes / 1024 / 1024);\n}\n\nfunction isInsideRoot(projectRoot: string, candidate: string): boolean {\n const relativePath = relative(projectRoot, candidate);\n return relativePath === '' || (!relativePath.startsWith('..') && !isAbsolute(relativePath));\n}\n\nexport function resolveFlywheelStartBriefPath(cwd: string, requestedPath?: string): { absolutePath: string; displayPath: string } {\n const rawPath = requestedPath?.trim() || DEFAULT_BRIEF_PATH;\n if (rawPath.includes('\\0')) throw new Error('Brief path is invalid');\n\n const root = resolve(cwd);\n const absolutePath = isAbsolute(rawPath) ? resolve(rawPath) : resolve(root, rawPath);\n if (!isInsideRoot(root, absolutePath)) throw new Error('Brief path must stay inside the project root');\n\n const normalizedRoot = root.endsWith(sep) ? root : `${root}${sep}`;\n const displayPath = absolutePath === root ? '.' : relative(root, absolutePath);\n return { absolutePath, displayPath: absolutePath.startsWith(normalizedRoot) ? displayPath : absolutePath };\n}\n\nasync function assertExistingPathInsideRoot(projectRoot: string, candidate: string): Promise<void> {\n const [realRoot, realCandidate] = await Promise.all([realpath(projectRoot), realpath(candidate)]);\n if (!isInsideRoot(realRoot, realCandidate)) throw new Error('Brief path must stay inside the project root');\n}\n\nexport async function requireFlywheelBrief(cwd: string, requestedPath?: string): Promise<{ absolutePath: string; displayPath: string }> {\n const resolved = resolveFlywheelStartBriefPath(cwd, requestedPath);\n try {\n await assertExistingPathInsideRoot(cwd, resolved.absolutePath);\n await readFile(resolved.absolutePath, 'utf8');\n return resolved;\n } catch (error) {\n const code = typeof error === 'object' && error !== null && 'code' in error ? error.code : undefined;\n if (code === 'ENOENT') throw new Error(`Flywheel brief not found: ${resolved.displayPath}`);\n throw error;\n }\n}\n\nasync function dashboardSetting(key: string): Promise<string | null> {\n return runDashboardDbJob<string | null>('getSetting', key);\n}\n\nasync function setDashboardSetting(key: string, value: string): Promise<void> {\n await runDashboardDbJob('setSetting', { key, value });\n}\n\nasync function getActiveRunId(): Promise<string | null> {\n const value = await dashboardSetting(FLYWHEEL_ACTIVE_RUN_ID_KEY);\n return value && value.trim() ? value : null;\n}\n\nasync function setActiveRunId(runId: string | null): Promise<void> {\n await setDashboardSetting(FLYWHEEL_ACTIVE_RUN_ID_KEY, runId ?? '');\n}\n\nasync function isPaused(): Promise<boolean> {\n return (await dashboardSetting(FLYWHEEL_GLOBAL_PAUSE_KEY)) === 'true';\n}\n\nasync function setPaused(paused: boolean): Promise<void> {\n await setDashboardSetting(FLYWHEEL_GLOBAL_PAUSE_KEY, paused ? 'true' : 'false');\n}\n\nasync function readGateSnapshot(): Promise<FlywheelGateSnapshot> {\n const [paused, activeRunId] = await Promise.all([isPaused(), getActiveRunId()]);\n return { paused, activeRunId };\n}\n\nasync function resolveFlywheelRoleConfig(): Promise<ResolvedFlywheelRoleConfig> {\n const { config } = await Effect.runPromise(loadConfigNoMigration());\n const flywheel = config.roles?.flywheel;\n const model = resolveModel('flywheel', undefined, config);\n // Harness is provider-default-only (PAN-1984): derive from the model's provider via the\n // canonical resolver — never a per-role pin or a hardcoded claude-code fallback (PAN-1865).\n const harness = await resolveHarness({ model });\n return {\n harness,\n model,\n effort: flywheel?.effort ?? 'high',\n minAgents: flywheel?.minAgents ?? 20,\n maxAgents: flywheel?.maxAgents ?? 30,\n scope: flywheel?.scope ?? 'pan-only',\n };\n}\n\nasync function gitOutput(command: string, cwd: string): Promise<string> {\n const { stdout } = await execAsync(command, { cwd, encoding: 'utf8' });\n return stdout.trim();\n}\n\nasync function createInitialFlywheelStatus(\n runId: string,\n startedAt: string,\n cwd: string,\n agentModel: string | undefined,\n agentHarness: 'claude-code' | 'ohmypi' | 'codex' | undefined,\n roleConfig: ResolvedFlywheelRoleConfig,\n): Promise<FlywheelStatus> {\n const ramTotalMb = mb(totalmem());\n return {\n runId,\n startedAt,\n elapsedMs: 0,\n orchestrator: {\n harness: agentHarness ?? roleConfig.harness,\n model: agentModel ?? roleConfig.model,\n effort: roleConfig.effort,\n ctxPercent: 0,\n },\n headline: {\n bugsFixed: 0,\n swarmItemsMerged: 0,\n swarmItemsTotal: 0,\n prsMerged: 0,\n awaitingUat: 0,\n },\n activePipeline: [],\n substrateBugs: [],\n agents: [{\n id: FLYWHEEL_ORCHESTRATOR_AGENT_ID,\n label: 'flywheel-orchestrator',\n status: 'running',\n role: 'flywheel',\n model: agentModel,\n }],\n parked: [],\n suggestions: [],\n system: {\n mainHead: await gitOutput('git rev-parse --short HEAD', cwd).catch(() => 'unknown'),\n ramUsedMb: Math.max(0, ramTotalMb - mb(freemem())),\n ramTotalMb,\n swapUsedMb: 0,\n swapTotalMb: 0,\n agentsActive: 1,\n agentsCap: roleConfig.maxAgents,\n },\n openQuestions: [],\n ticks: 0,\n lastTickAt: startedAt,\n };\n}\n\nexport async function startFlywheelRunForDashboard(options: StartOptions = {}): Promise<{ runId: string; briefDisplayPath: string; agentModel?: string }> {\n const cwd = options.cwd ?? process.cwd();\n if (isFlywheelDevcontainerRuntime()) {\n throw new Error('Refusing to spawn flywheel-orchestrator inside a workspace devcontainer');\n }\n\n // Self-healing gate check (PAN-1245). Mirrors the CLI's spawnFlywheel guard\n // so the two start paths agree about what counts as \"active\".\n const activeRunId = await resolveLiveFlywheelRunId();\n if (activeRunId) {\n // Auto-abort when the orchestrator process has exited (Pi/ohmypi exited\n // without writing report.md or aborted.json — a \"keep-alive corpse\").\n // sessionExists only checks if the tmux session exists, not if the pane\n // process is alive. Use isPaneDead which checks #{pane_dead}.\n const sessionMissing = !(await Effect.runPromise(sessionExists(FLYWHEEL_ORCHESTRATOR_AGENT_ID)));\n const paneDead = sessionMissing || await Effect.runPromise(isPaneDead(FLYWHEEL_ORCHESTRATOR_AGENT_ID));\n if (paneDead) {\n console.log(`[flywheel] ${activeRunId} gate stuck — orchestrator pane is dead; auto-aborting`);\n // Kill the stale tmux session directly — stopAgent may fail if the agent\n // is not yet in the DB (e.g. after a rebuild). killSession is sufficient\n // to clear the \"already running\" guard in spawnFlywheelAgent.\n await Effect.runPromise(killSession(FLYWHEEL_ORCHESTRATOR_AGENT_ID)).catch(() => {});\n await abortFlywheelRun(activeRunId);\n } else {\n throw new Error(`Flywheel run ${activeRunId} is already active; pause, resume, or report it before starting another run`);\n }\n }\n\n const brief = await requireFlywheelBrief(cwd, options.brief ?? DEFAULT_BRIEF_PATH);\n const runId = await nextFlywheelRunId();\n const startedAt = new Date().toISOString();\n await writeFlywheelLaunchMetadata({\n version: 1,\n runId,\n workspace: cwd,\n briefPath: brief.absolutePath,\n briefDisplayPath: brief.displayPath,\n });\n const roleConfig = await resolveFlywheelRoleConfig();\n const agent = await spawnFlywheelAgent(runId, {\n briefPath: brief.absolutePath,\n workspace: cwd,\n model: roleConfig.model,\n harness: roleConfig.harness,\n effort: roleConfig.effort,\n minAgents: roleConfig.minAgents,\n maxAgents: roleConfig.maxAgents,\n scope: roleConfig.scope,\n });\n await setActiveRunId(runId);\n await setPaused(false);\n await writeLatestFlywheelStatus(await createInitialFlywheelStatus(\n runId,\n startedAt,\n cwd,\n agent.model,\n agent.harness,\n roleConfig,\n ));\n const resolved = resolveFlywheelStartBriefPath(cwd, options.brief ?? DEFAULT_BRIEF_PATH);\n return { runId, briefDisplayPath: resolved.displayPath, agentModel: agent.model };\n}\n\nexport async function pauseFlywheelRunForDashboard(): Promise<{ before: FlywheelGateSnapshot; after: FlywheelGateSnapshot; changed: boolean }> {\n const before = await readGateSnapshot();\n if (before.paused) return { before, after: before, changed: false };\n if (before.activeRunId) {\n try {\n const { readFile } = await import('node:fs/promises');\n const { join } = await import('node:path');\n const { getAgentDir } = await import('../../../lib/agents.js');\n const sessionId = (await readFile(join(getAgentDir(FLYWHEEL_ORCHESTRATOR_AGENT_ID), 'session.id'), 'utf-8')).trim();\n if (sessionId) await saveResumeSessionId(before.activeRunId, sessionId);\n } catch { /* non-fatal: resume falls back to fresh if session.id is missing */ }\n }\n await setPaused(true);\n await import('../../../lib/agents.js').then(({ stopAgent }) => Effect.runPromise(stopAgent(FLYWHEEL_ORCHESTRATOR_AGENT_ID)));\n return { before, after: await readGateSnapshot(), changed: true };\n}\n\n// Discard the current flywheel run without writing a report (PAN-1245). Stops\n// the orchestrator if still attached, writes `aborted.json`, clears the gate.\n// Safe to call from any state: returns { aborted: null } when nothing was live.\nexport async function abortFlywheelRunForDashboard(): Promise<{ aborted: string | null }> {\n const candidate = await getActiveRunId();\n if (!candidate) {\n // Defensive cleanup: gate could be in a half-cleared state.\n const stale = await resolveLiveFlywheelRunId();\n return { aborted: stale ?? null };\n }\n const { stopAgent } = await import('../../../lib/agents.js');\n await Effect.runPromise(stopAgent(FLYWHEEL_ORCHESTRATOR_AGENT_ID));\n await abortFlywheelRun(candidate);\n return { aborted: candidate };\n}\n\nexport async function resumeFlywheelRunForDashboard(): Promise<{ before: FlywheelGateSnapshot; after: FlywheelGateSnapshot; changed: boolean }> {\n const before = await readGateSnapshot();\n if (!before.paused && await Effect.runPromise(sessionExists(FLYWHEEL_ORCHESTRATOR_AGENT_ID))) {\n return { before, after: before, changed: false };\n }\n if (!before.activeRunId) throw new Error('No active flywheel run to resume');\n\n const launch = await readFlywheelLaunchMetadata(before.activeRunId);\n if (!launch) {\n throw new Error(`Flywheel run ${before.activeRunId} is missing launch metadata; cannot resume safely`);\n }\n const brief = await requireFlywheelBrief(launch.workspace, launch.briefPath);\n const roleConfig = await resolveFlywheelRoleConfig();\n const resumeSessionId = await loadResumeSessionId(before.activeRunId) ?? undefined;\n await spawnFlywheelAgent(before.activeRunId, {\n workspace: launch.workspace,\n briefPath: brief.absolutePath,\n model: roleConfig.model,\n harness: roleConfig.harness,\n effort: roleConfig.effort,\n minAgents: roleConfig.minAgents,\n maxAgents: roleConfig.maxAgents,\n scope: roleConfig.scope,\n resumeSessionId,\n });\n await setPaused(false);\n return { before, after: await readGateSnapshot(), changed: true };\n}\n\nfunction getPlatformOpenCommand(): string {\n switch (process.platform) {\n case 'linux': return 'xdg-open';\n case 'darwin': return 'open';\n case 'win32': return 'explorer';\n default: throw new Error(`Opening files is not supported on ${process.platform}`);\n }\n}\n\nfunction openPathDetached(path: string): void {\n const child = spawn(getPlatformOpenCommand(), [path], {\n detached: true,\n stdio: 'ignore',\n });\n child.unref();\n}\n\nasync function resolveReportOpenRunId(runId: string | undefined): Promise<string> {\n if (runId) return runId;\n const runs = await listFlywheelRuns();\n const run = runs.find((candidate) => candidate.status === 'complete') ?? runs[0];\n if (!run) throw new Error('no flywheel run report to open');\n return run.id;\n}\n\nexport async function openFlywheelRunReportForDashboard(options: ReportOpenOptions = {}): Promise<{ runId: string; path: string }> {\n const runId = await resolveReportOpenRunId(options.runId);\n const detail = await getFlywheelRunDetail(runId);\n if (!detail) throw new Error(`Flywheel run not found: ${runId}`);\n if (!detail.paths.report) throw new Error(`No report exists for ${runId}`);\n await (options.opener ?? openPathDetached)(detail.paths.report);\n return { runId, path: detail.paths.report };\n}\n\nexport async function readCurrentFlywheelStatusForDashboard(): Promise<FlywheelStatus | null> {\n // Use the self-healing resolver so the dashboard \"current run\" surface and\n // the CLI's `pan flywheel status` agree (PAN-1245).\n const activeRunId = await resolveLiveFlywheelRunId();\n if (!activeRunId) return null;\n const activeRun = await getFlywheelRunDetail(activeRunId);\n return activeRun?.status === 'running' ? activeRun.latest : null;\n}\n","/**\n * UAT candidate branch naming (PAN-1691) — codename + short date.\n *\n * One candidate branch is assembled per label/day and force-reset on rebuild.\n * Format: `uat/<label>-<codename>-<MMDD>`, e.g. `uat/pan-otter-0609`. Codenames\n * are short, memorable words you can say out loud (\"ship the otter candidate\").\n *\n * The core is pure and fully injectable (wordlist / index pick / collision\n * date) so it's deterministically testable. The default codename index is a\n * stable hash of label + day so rebuilds for the same day reuse the same branch.\n */\n\nexport const UAT_CODENAMES: readonly string[] = [\n 'otter', 'falcon', 'cedar', 'quartz', 'ember', 'marlin', 'willow', 'cobalt',\n 'sable', 'heron', 'onyx', 'larch', 'perch', 'flint', 'dune', 'reef', 'vale',\n 'crow', 'moss', 'thorn', 'birch', 'koi', 'slate', 'wren',\n];\n\nfunction mmdd(dateIso: string): string {\n const [, month, day] = dateIso.slice(0, 10).split('-');\n return `${month ?? '00'}${day ?? '00'}`;\n}\n\nexport interface UatNameDeps {\n /** Project label, e.g. 'pan'. */\n label: string;\n /** ISO date string; only the YYYY-MM-DD prefix is used (→ MMDD). */\n dateIso: string;\n /** Injectable wordlist (default UAT_CODENAMES). */\n codenames?: readonly string[];\n /** Returns an index in [0, n) (default: stable hash of label + MMDD). */\n pick?: (n: number) => number;\n}\n\n/**\n * Build the deterministic `uat/<label>-<codename>-<MMDD>` branch name. Pure.\n */\nexport function makeUatCandidateName(deps: UatNameDeps): string {\n const day = mmdd(deps.dateIso);\n const words = deps.codenames ?? UAT_CODENAMES;\n const pick = deps.pick ?? ((n: number) => stableIndex(`${deps.label}-${day}`, n));\n const index = words.length > 0 ? pick(words.length) % words.length : 0;\n const word = words[index] ?? 'candidate';\n return `uat/${deps.label}-${word}-${day}`;\n}\n\nfunction stableIndex(seed: string, modulo: number): number {\n if (modulo <= 0) return 0;\n let hash = 2166136261;\n for (let i = 0; i < seed.length; i++) {\n hash ^= seed.charCodeAt(i);\n hash = Math.imul(hash, 16777619);\n }\n return (hash >>> 0) % modulo;\n}\n","/**\n * UAT generation assembly engine (PAN-1737: UAT batch trains).\n *\n * Builds one *generation* — a deterministic-per-day `uat/<label>-<codename>-<MMDD>` branch\n * off current main containing as many ready features as possible, merged in\n * queue order, with cross-feature conflicts resolved on the branch by the\n * injected `resolveConflict` hook (the assembly agent). A feature whose\n * conflict cannot be resolved is *held out* with a human-readable reason and\n * assembly continues — a single bad merge never blocks the batch.\n *\n * Each rebuild for the same label/day force-resets the same branch + persistent\n * worktree under `<projectRoot>/workspaces/`, keeping one authoritative current\n * UAT candidate instead of proliferating stale branches.\n *\n * Pure orchestrator: all git and store I/O is injected (see\n * uat-generation-deps.ts for the real wiring), so every path — happy,\n * conflict-resolved, held-out, failed — is unit-testable with fake deps.\n */\nimport { makeUatCandidateName } from './uat-candidate-name.js';\nimport type {\n UatGeneration,\n UatGenerationMember,\n UatGenerationStatus,\n} from '../overdeck/merge-sync.js';\n\n/** A feature eligible for bundling, in merge-queue order. */\nexport interface ReadyFeature {\n issueId: string;\n title: string;\n /** Feature branch, e.g. feature/pan-1704. */\n branch: string;\n pr?: number;\n prUrl?: string;\n /** Issue ids this feature's changed files overlap with (from computeMergeQueue). */\n conflictsWith?: readonly string[];\n}\n\n/** Context handed to the conflict-resolution hook while the merge is mid-conflict. */\nexport interface ConflictContext {\n feature: ReadyFeature;\n /** Members already merged onto the branch, in order. */\n mergedIssueIds: readonly string[];\n /** The already-merged members this feature is known to overlap with. */\n conflictingIssueIds: readonly string[];\n branchName: string;\n worktreePath: string;\n}\n\n/** Successful resolution: the hook committed the merge on the branch. */\nexport interface ConflictResolutionResult {\n files: string[];\n commitSha: string;\n}\n\nexport interface GenerationGitDeps {\n /** `git fetch origin main` and return the origin/main head SHA. */\n fetchMain(): Promise<string>;\n /** Create or reset the generation worktree branch off origin/main. */\n createWorktree(branchName: string, worktreePath: string): Promise<void>;\n /** Head SHA of a feature branch (origin-first). */\n branchHeadSha(branch: string): Promise<string>;\n /**\n * Merge a feature branch into the generation worktree. On conflict the\n * worktree is left MID-CONFLICT (the hook needs that state); the engine\n * decides whether to resolve or abort.\n */\n mergeBranch(featureBranch: string): Promise<\n { ok: true } | { ok: false; conflict: boolean; reason: string }\n >;\n /** `git merge --abort` in the generation worktree. */\n abortMerge(): Promise<void>;\n /** Push the generation branch to origin. */\n push(branchName: string): Promise<void>;\n}\n\nexport interface GenerationStorePort {\n /** Insert or reset the generation row at assembly start. */\n insert(gen: Omit<UatGeneration, 'createdAt' | 'updatedAt'>): void;\n update(\n name: string,\n patch: Partial<Pick<UatGeneration, 'status' | 'baseSha' | 'members' | 'heldOut' | 'resolutions' | 'cleanedAt'>>,\n ): void;\n /** Existing generation names. */\n listNames(): string[];\n listChain(projectRoot: string, statuses?: readonly UatGenerationStatus[]): UatGeneration[];\n}\n\nexport interface AssembleGenerationInput {\n projectRoot: string;\n /** Project label for the codename, e.g. 'pan'. */\n label: string;\n /** ISO date; MMDD goes into the branch name. */\n dateIso: string;\n /** Ready features in merge-queue order. */\n features: readonly ReadyFeature[];\n /** Branch names (beyond the store) already present, e.g. live git refs. */\n takenBranchNames?: readonly string[];\n}\n\nexport interface AssembleGenerationDeps {\n git: GenerationGitDeps;\n store: GenerationStorePort;\n /**\n * The assembly agent. Called mid-conflict; must either conclude the merge\n * with a commit (returning what it resolved) or return null to give up.\n * Absent hook = every conflict is held out.\n */\n resolveConflict?: (ctx: ConflictContext) => Promise<ConflictResolutionResult | null>;\n log?: (msg: string) => void;\n}\n\n/** Branch name → worktree folder name (slashes collapse to dashes). */\nexport function generationFolderName(branchName: string): string {\n return branchName.replace(/[^a-zA-Z0-9._-]+/g, '-');\n}\n\n/**\n * Assemble the next generation from the given ready set. Always returns the\n * generation row (status 'ready' or 'failed'); throws only on store failures.\n * On success, older 'ready' generations for the project flip to 'superseded'.\n */\nexport async function assembleUatGeneration(\n input: AssembleGenerationInput,\n deps: AssembleGenerationDeps,\n): Promise<UatGeneration> {\n const log = deps.log ?? (() => {});\n\n const baseSha = await deps.git.fetchMain();\n const name = makeUatCandidateName({\n label: input.label,\n dateIso: input.dateIso,\n });\n const worktreePath = `${input.projectRoot}/workspaces/${generationFolderName(name)}`;\n\n deps.store.insert({\n name,\n worktreePath,\n projectRoot: input.projectRoot,\n baseSha,\n status: 'assembling',\n members: [],\n heldOut: [],\n resolutions: [],\n stackStartedAt: null,\n cleanedAt: null,\n });\n\n const members: UatGeneration['members'] = [];\n const heldOut: UatGeneration['heldOut'] = [];\n const resolutions: UatGeneration['resolutions'] = [];\n const finish = (status: UatGenerationStatus): UatGeneration => {\n deps.store.update(name, { status, members, heldOut, resolutions });\n return {\n name, worktreePath, projectRoot: input.projectRoot, baseSha,\n status, members, heldOut, resolutions,\n stackStartedAt: null, cleanedAt: null, createdAt: '', updatedAt: '',\n };\n };\n\n try {\n await deps.git.createWorktree(name, worktreePath);\n } catch (err) {\n log(`[uat-generation] ${name}: worktree creation failed: ${err instanceof Error ? err.message : String(err)}`);\n return finish('failed');\n }\n\n for (const feature of input.features) {\n const mergedIssueIds = members.map((m) => m.issueId);\n const attemptedHeadSha = async () => deps.git.branchHeadSha(feature.branch).catch(() => 'unknown');\n const recordMember = async (): Promise<UatGenerationMember> => ({\n issueId: feature.issueId,\n title: feature.title,\n branch: feature.branch,\n headSha: await attemptedHeadSha(),\n mergeOrder: members.length + 1,\n ...(feature.pr !== undefined ? { pr: feature.pr } : {}),\n ...(feature.prUrl !== undefined ? { prUrl: feature.prUrl } : {}),\n });\n const holdOut = async (reason: string): Promise<void> => {\n heldOut.push({ issueId: feature.issueId, branch: feature.branch, headSha: await attemptedHeadSha(), reason });\n };\n\n let result: Awaited<ReturnType<GenerationGitDeps['mergeBranch']>>;\n try {\n result = await deps.git.mergeBranch(feature.branch);\n } catch (err) {\n await deps.git.abortMerge().catch(() => {});\n await holdOut(`merge failed: ${err instanceof Error ? err.message.split('\\n')[0] : String(err)}`);\n deps.store.update(name, { members, heldOut, resolutions });\n continue;\n }\n\n if (result.ok) {\n members.push(await recordMember());\n deps.store.update(name, { members, heldOut, resolutions });\n continue;\n }\n\n if (!result.conflict || !deps.resolveConflict) {\n await deps.git.abortMerge().catch(() => {});\n await holdOut(result.conflict\n ? `conflicts with ${conflictingWith(feature, mergedIssueIds).join(', ') || 'an earlier member'} — no assembly agent available`\n : result.reason);\n deps.store.update(name, { members, heldOut, resolutions });\n continue;\n }\n\n const conflictingIssueIds = conflictingWith(feature, mergedIssueIds);\n log(`[uat-generation] ${name}: resolving conflict ${feature.issueId} <-> ${conflictingIssueIds.join(', ') || '(unknown member)'}`);\n let resolution: ConflictResolutionResult | null = null;\n try {\n resolution = await deps.resolveConflict({\n feature,\n mergedIssueIds,\n conflictingIssueIds,\n branchName: name,\n worktreePath,\n });\n } catch (err) {\n log(`[uat-generation] ${name}: conflict agent threw: ${err instanceof Error ? err.message : String(err)}`);\n resolution = null;\n }\n\n if (resolution) {\n members.push(await recordMember());\n resolutions.push({\n issueIds: [feature.issueId, ...conflictingIssueIds],\n files: resolution.files,\n commitSha: resolution.commitSha,\n });\n } else {\n await deps.git.abortMerge().catch(() => {});\n await holdOut(`conflict with ${conflictingIssueIds.join(', ') || 'an earlier member'} could not be auto-resolved — waits for the next generation`);\n }\n deps.store.update(name, { members, heldOut, resolutions });\n }\n\n if (members.length === 0) {\n log(`[uat-generation] ${name}: nothing merged (${heldOut.length} held out) — marking failed`);\n return finish('failed');\n }\n\n try {\n await deps.git.push(name);\n } catch (err) {\n log(`[uat-generation] ${name}: push failed: ${err instanceof Error ? err.message : String(err)}`);\n return finish('failed');\n }\n\n // This generation is now the current one; older ready generations remain\n // testable but are no longer the freshest — flip them to superseded.\n for (const older of deps.store.listChain(input.projectRoot, ['ready'])) {\n if (older.name !== name) deps.store.update(older.name, { status: 'superseded' });\n }\n\n log(`[uat-generation] ${name}: ready — ${members.length} member(s), ${resolutions.length} resolution(s), ${heldOut.length} held out`);\n return finish('ready');\n}\n\nfunction conflictingWith(feature: ReadyFeature, mergedIssueIds: readonly string[]): string[] {\n const merged = new Set(mergedIssueIds.map((id) => id.toUpperCase()));\n return (feature.conflictsWith ?? []).filter((id) => merged.has(id.toUpperCase()));\n}\n\nexport interface GenerationCleanupDeps {\n store: GenerationStorePort;\n /** Remove the generation's worktree directory (idempotent). */\n removeWorktree(worktreePath: string): Promise<void>;\n /** Delete the generation branch locally and on origin (idempotent). */\n deleteBranch(branchName: string): Promise<void>;\n /** Tear down the generation's live stack if one is running. */\n teardownStack?: (generation: UatGeneration) => Promise<void>;\n log?: (msg: string) => void;\n}\n\n/**\n * Reap dead generations' branches/worktrees, and trim the live chain to the\n * newest `keep` (default 3). Trimmed superseded generations flip to\n * 'invalidated' (their branch is gone — no longer testable). Rows are never\n * deleted: the chain is the audit trail. Promoted generations keep their row\n * but lose branch/worktree/stack — their content lives on main now.\n */\nexport async function cleanupUatGenerations(\n projectRoot: string,\n deps: GenerationCleanupDeps,\n options: { keep?: number } = {},\n): Promise<void> {\n const log = deps.log ?? (() => {});\n const keep = options.keep ?? 3;\n\n const live = deps.store.listChain(projectRoot, ['ready', 'superseded']);\n const trimmed = live.slice(keep);\n const dead = deps.store.listChain(projectRoot, ['invalidated', 'promoted', 'failed'])\n .filter((gen) => !gen.cleanedAt);\n\n for (const gen of [...trimmed, ...dead]) {\n let cleaned = true;\n if (gen.stackStartedAt && deps.teardownStack) {\n await deps.teardownStack(gen).catch((err) => {\n cleaned = false;\n log(`[uat-generation] cleanup ${gen.name}: stack teardown failed: ${err instanceof Error ? err.message : String(err)}`);\n });\n }\n await deps.removeWorktree(gen.worktreePath).catch((err) => {\n cleaned = false;\n log(`[uat-generation] cleanup ${gen.name}: worktree removal failed: ${err instanceof Error ? err.message : String(err)}`);\n });\n await deps.deleteBranch(gen.name).catch((err) => {\n cleaned = false;\n log(`[uat-generation] cleanup ${gen.name}: branch deletion failed: ${err instanceof Error ? err.message : String(err)}`);\n });\n if (!cleaned) continue;\n const cleanedAt = new Date().toISOString();\n if (gen.status === 'ready' || gen.status === 'superseded') {\n deps.store.update(gen.name, { status: 'invalidated', cleanedAt });\n log(`[uat-generation] cleanup: trimmed ${gen.name} (beyond newest ${keep})`);\n } else {\n deps.store.update(gen.name, { cleanedAt });\n }\n }\n}\n","/**\n * Real git + store wiring for the UAT generation engine (PAN-1737).\n *\n * Generation worktrees are PERSISTENT under `<projectRoot>/workspaces/` — the\n * live UAT stack serves from them, and the deterministic daily folder name yields the Traefik\n * host (`uat-<label>-<codename>-<mmdd>.overdeck.localhost`) via the standard\n * FEATURE_FOLDER devcontainer template.\n *\n * Pure I/O — exercised live; the orchestrator (uat-generation-engine.ts) holds\n * the tested logic.\n */\nimport { execFile } from 'child_process';\nimport { isAbsolute, relative, resolve } from 'path';\nimport { promisify } from 'util';\nimport {\n insertUatGenerationSync,\n listUatGenerationNamesSync,\n listUatGenerationsSync,\n updateUatGenerationSync,\n} from '../overdeck/merge-sync.js';\nimport type { GenerationGitDeps, GenerationStorePort } from './uat-generation-engine.js';\n\nconst execFileAsync = promisify(execFile);\n\nconst runGit = (args: string[], cwd: string) =>\n execFileAsync('git', args, { cwd, maxBuffer: 16 * 1024 * 1024 });\n\nfunction safeBranchName(branchName: string, prefix: 'feature' | 'uat'): string {\n const pattern = prefix === 'feature'\n ? /^feature\\/[A-Za-z0-9][A-Za-z0-9._-]*$/\n : /^uat\\/[A-Za-z0-9][A-Za-z0-9._-]*$/;\n if (!pattern.test(branchName)) {\n throw new Error(`unsafe ${prefix} branch name: ${branchName}`);\n }\n return branchName;\n}\n\nfunction safeGenerationWorktreePath(projectRoot: string, worktreePath: string): string {\n const workspacesRoot = resolve(projectRoot, 'workspaces');\n const target = resolve(worktreePath);\n const rel = relative(workspacesRoot, target);\n if (rel === '' || rel.startsWith('..') || isAbsolute(rel)) {\n throw new Error(`unsafe UAT worktree path outside project workspaces: ${worktreePath}`);\n }\n return target;\n}\n\n/** Store port backed by uat-generations-db. */\nexport function buildUatGenerationStore(): GenerationStorePort {\n return {\n insert: (gen) => { insertUatGenerationSync(gen); },\n update: (name, patch) => { updateUatGenerationSync(name, patch); },\n listNames: () => listUatGenerationNamesSync(),\n listChain: (projectRoot, statuses) =>\n listUatGenerationsSync({ projectRoot, ...(statuses ? { statuses } : {}) }),\n };\n}\n\n/**\n * Git deps for one assembly run. The worktree path is bound on createWorktree\n * and reused by the merge/push operations that follow.\n */\nexport function buildUatGenerationGitDeps(projectRoot: string): GenerationGitDeps {\n let worktreePath = '';\n\n return {\n fetchMain: async () => {\n await runGit(['fetch', 'origin', 'main'], projectRoot);\n const { stdout } = await runGit(['rev-parse', 'origin/main'], projectRoot);\n return stdout.trim();\n },\n\n createWorktree: async (branchName, path) => {\n const safeBranch = safeBranchName(branchName, 'uat');\n worktreePath = safeGenerationWorktreePath(projectRoot, path);\n // A leftover worktree at this path means a previous assembly of the SAME\n // name crashed mid-build (names are collision-checked) — reclaim it.\n await runGit(['worktree', 'remove', '--force', worktreePath], projectRoot).catch(() => {});\n await runGit(['worktree', 'prune'], projectRoot).catch(() => {});\n await runGit(['worktree', 'add', '-B', safeBranch, worktreePath, 'origin/main'], projectRoot);\n },\n\n branchHeadSha: async (branch) => {\n const safeBranch = safeBranchName(branch, 'feature');\n const tryRef = async (ref: string) => (await runGit(['rev-parse', ref], projectRoot)).stdout.trim();\n return tryRef(`origin/${safeBranch}`).catch(() => tryRef(safeBranch));\n },\n\n mergeBranch: async (featureBranch) => {\n // Prefer the origin ref — feature branches are pushed by work agents and\n // the local ref may lag.\n const safeFeatureBranch = safeBranchName(featureBranch, 'feature');\n const originRef = `origin/${safeFeatureBranch}`;\n const ref = await runGit(['rev-parse', '--verify', originRef], worktreePath)\n .then(() => originRef)\n .catch(() => safeFeatureBranch);\n try {\n await runGit(['merge', '--no-edit', ref], worktreePath);\n return { ok: true as const };\n } catch (err) {\n const { stdout } = await runGit(['ls-files', '-u'], worktreePath).catch(() => ({ stdout: '' }));\n const conflict = stdout.trim().length > 0;\n // Leave the worktree mid-conflict — the engine decides whether the\n // assembly agent gets a shot before aborting.\n return {\n ok: false as const,\n conflict,\n reason: err instanceof Error ? (err.message.split('\\n')[0] ?? 'merge failed') : String(err),\n };\n }\n },\n\n abortMerge: async () => {\n await runGit(['merge', '--abort'], worktreePath).catch(() => {});\n },\n\n push: async (branchName) => {\n await runGit(['push', '-u', '--force-with-lease', 'origin', safeBranchName(branchName, 'uat')], worktreePath);\n },\n };\n}\n\n/** Branch names currently present on origin under uat/* — naming collision input. */\nexport async function listRemoteUatBranches(projectRoot: string): Promise<string[]> {\n const { stdout } = await runGit(['ls-remote', '--heads', 'origin', 'uat/*'], projectRoot).catch(() => ({ stdout: '' }));\n return stdout\n .split('\\n')\n .map((line) => line.split('\\t')[1] ?? '')\n .filter((ref) => ref.startsWith('refs/heads/'))\n .map((ref) => ref.slice('refs/heads/'.length));\n}\n\n/** Cleanup deps: remove a generation worktree and delete its branch everywhere. */\nexport function buildUatGenerationCleanupGit(projectRoot: string): {\n removeWorktree(worktreePath: string): Promise<void>;\n deleteBranch(branchName: string): Promise<void>;\n} {\n return {\n removeWorktree: async (path) => {\n const safePath = safeGenerationWorktreePath(projectRoot, path);\n await runGit(['worktree', 'remove', '--force', safePath], projectRoot).catch(() => {});\n await runGit(['worktree', 'prune'], projectRoot).catch(() => {});\n },\n deleteBranch: async (branchName) => {\n const safeBranch = safeBranchName(branchName, 'uat');\n await runGit(['branch', '-D', safeBranch], projectRoot).catch(() => {});\n await runGit(['push', 'origin', '--delete', safeBranch], projectRoot).catch(() => {});\n },\n };\n}\n","/**\n * Assembly-agent conflict resolution (PAN-1737: UAT batch trains).\n *\n * Implements the engine's `resolveConflict` hook: when merging a feature onto\n * a UAT generation branch conflicts, a TIMEBOXED headless agent run is given\n * the mid-conflict worktree with one mission — resolve THIS merge's conflict\n * markers, changing nothing beyond the resolution.\n *\n * Division of labor, deliberately strict:\n * - The agent only EDITS FILES (headless `claude -p` with acceptEdits; no\n * shell access). It cannot stage, commit, push, or wander.\n * - This module verifies the result in code (no leftover conflict markers,\n * no unmerged index entries), stages, and concludes the merge commit.\n * - Any failure — agent missing, timeout, markers left, commit rejected —\n * returns null; the engine then aborts the merge and holds the feature\n * out. The assembly NEVER wedges on a conflict.\n *\n * The merge commit subject keeps git's standard `Merge branch ...` form (it\n * survives commitlint's default-ignores when this history is later promoted\n * to main); the auditable `uat-assembly:` marker lives in the body.\n */\nimport { execFile } from 'child_process';\nimport { promisify } from 'util';\nimport type { ConflictContext, ConflictResolutionResult } from './uat-generation-engine.js';\n\nconst execFileAsync = promisify(execFile);\n\nexport const DEFAULT_CONFLICT_TIMEOUT_MS = 5 * 60 * 1000;\n\n/** Injectable I/O so the hook's decision logic is unit-testable. */\nexport interface ConflictAgentDeps {\n /** Paths with unmerged index entries (`git diff --name-only --diff-filter=U`). */\n listConflictedFiles(cwd: string): Promise<string[]>;\n /** Staged paths before/after the agent run (`git diff --cached --name-only`). */\n stagedFiles(cwd: string): Promise<string[]>;\n /** Run the headless resolution agent; throws on spawn failure or timeout. */\n runAgent(args: { prompt: string; cwd: string; timeoutMs: number }): Promise<void>;\n /** Subset of `files` still containing conflict markers after the agent ran. */\n filesWithConflictMarkers(cwd: string, files: readonly string[]): Promise<string[]>;\n /** Unstaged or untracked paths outside the conflicted-file allowlist. */\n unsafeChangedFiles(cwd: string, allowedFiles: readonly string[]): Promise<string[]>;\n /** Discard untrusted worktree/untracked changes outside the allowlist. */\n discardFiles(cwd: string, files: readonly string[]): Promise<void>;\n /** Stage only the conflicted files that the agent was allowed to edit. */\n stageFiles(cwd: string, files: readonly string[]): Promise<void>;\n /** True if unmerged index entries remain after staging. */\n hasUnmergedEntries(cwd: string): Promise<boolean>;\n /** Conclude the in-progress merge with the given message. */\n commitMerge(cwd: string, message: string): Promise<void>;\n headSha(cwd: string): Promise<string>;\n log?: (msg: string) => void;\n}\n\nexport interface ConflictAgentOptions {\n timeoutMs?: number;\n deps?: Partial<ConflictAgentDeps>;\n}\n\nfunction splitNul(stdout: string): string[] {\n return stdout.split('\\0').filter((p) => p.length > 0);\n}\n\nfunction allowlist(files: readonly string[]): ReadonlySet<string> {\n return new Set(files.map((f) => f.replace(/\\\\/g, '/')));\n}\n\nexport function buildConflictResolutionPrompt(ctx: ConflictContext, conflictedFiles: readonly string[]): string {\n const counterparts = ctx.conflictingIssueIds.length > 0\n ? ctx.conflictingIssueIds.join(', ')\n : 'an earlier batch member';\n return [\n `You are resolving a git merge conflict inside a UAT batch assembly worktree.`,\n ``,\n `The branch ${ctx.branchName} bundles already-reviewed features for human testing.`,\n `Merging ${ctx.feature.branch} (${ctx.feature.issueId}: ${ctx.feature.title}) conflicted with ${counterparts}.`,\n ``,\n `Conflicted files:`,\n ...conflictedFiles.map((f) => ` - ${f}`),\n ``,\n `Your ONLY job: edit these files to resolve every conflict marker (<<<<<<<, =======, >>>>>>>)`,\n `so that BOTH features' intended behavior is preserved. Both sides passed review —`,\n `combine them; do not pick one side wholesale unless the changes are genuinely identical in intent.`,\n ``,\n `Rules:`,\n `- Change NOTHING beyond what resolving the conflict requires.`,\n `- Do not refactor, reformat, or \"improve\" surrounding code.`,\n `- Do not run git commands; the caller stages and commits.`,\n `- If the conflict cannot be safely resolved, leave the markers in place and say so.`,\n ].join('\\n');\n}\n\nfunction buildMergeCommitMessage(ctx: ConflictContext, files: readonly string[]): string {\n const pair = [ctx.feature.issueId, ...ctx.conflictingIssueIds].join(' <-> ');\n return [\n `Merge branch '${ctx.feature.branch}' into ${ctx.branchName}`,\n ``,\n `uat-assembly: resolve ${pair}`,\n `files: ${files.join(', ')}`,\n ].join('\\n');\n}\n\nfunction defaultDeps(): ConflictAgentDeps {\n const run = (cmd: string, args: string[], cwd: string, timeoutMs?: number) =>\n execFileAsync(cmd, args, { cwd, maxBuffer: 16 * 1024 * 1024, ...(timeoutMs ? { timeout: timeoutMs } : {}) });\n\n return {\n listConflictedFiles: async (cwd) => {\n const { stdout } = await run('git', ['diff', '--name-only', '--diff-filter=U'], cwd);\n return stdout.split('\\n').map((l) => l.trim()).filter(Boolean);\n },\n stagedFiles: async (cwd) => {\n const { stdout } = await run('git', ['diff', '--cached', '--name-only', '-z'], cwd);\n return splitNul(stdout);\n },\n runAgent: async ({ prompt, cwd, timeoutMs }) => {\n await run('claude', ['-p', prompt, '--permission-mode', 'acceptEdits'], cwd, timeoutMs);\n },\n filesWithConflictMarkers: async (cwd, files) => {\n if (files.length === 0) return [];\n // -l: names only; -E for the alternation. grep exits 1 on no matches.\n const { stdout } = await execFileAsync(\n 'grep', ['-lE', '^(<<<<<<<|=======$|>>>>>>>)', '--', ...files],\n { cwd, maxBuffer: 16 * 1024 * 1024 },\n ).catch((err: { code?: number; stdout?: string }) => {\n if (err && err.code === 1) return { stdout: '' };\n throw err;\n });\n return stdout.split('\\n').map((l) => l.trim()).filter(Boolean);\n },\n unsafeChangedFiles: async (cwd, files) => {\n const allowed = allowlist(files);\n const [{ stdout: unstaged }, { stdout: untracked }] = await Promise.all([\n run('git', ['diff', '--name-only', '-z'], cwd),\n run('git', ['ls-files', '--others', '--exclude-standard', '-z'], cwd),\n ]);\n return [...new Set([...splitNul(unstaged), ...splitNul(untracked)])]\n .filter((file) => !allowed.has(file.replace(/\\\\/g, '/')));\n },\n discardFiles: async (cwd, files) => {\n if (files.length === 0) return;\n await run('git', ['restore', '--staged', '--worktree', '--', ...files], cwd).catch(() => {});\n await run('git', ['clean', '-fd', '--', ...files], cwd).catch(() => {});\n },\n stageFiles: async (cwd, files) => {\n if (files.length === 0) return;\n await run('git', ['add', '--', ...files], cwd);\n },\n hasUnmergedEntries: async (cwd) => {\n const { stdout } = await run('git', ['ls-files', '-u'], cwd);\n return stdout.trim().length > 0;\n },\n commitMerge: async (cwd, message) => {\n await run('git', ['commit', '-m', message], cwd);\n },\n headSha: async (cwd) => (await run('git', ['rev-parse', 'HEAD'], cwd)).stdout.trim(),\n };\n}\n\n/**\n * Build the engine's `resolveConflict` hook. Returns the resolution on\n * success; null on ANY failure (the engine aborts the merge and holds the\n * feature out — never throws into the assembly loop).\n */\nexport function buildConflictAgentHook(\n options: ConflictAgentOptions = {},\n): (ctx: ConflictContext) => Promise<ConflictResolutionResult | null> {\n const deps: ConflictAgentDeps = { ...defaultDeps(), ...(options.deps ?? {}) };\n const timeoutMs = options.timeoutMs ?? DEFAULT_CONFLICT_TIMEOUT_MS;\n const log = deps.log ?? (() => {});\n\n return async (ctx) => {\n const tag = `[uat-conflict-agent] ${ctx.branchName} ${ctx.feature.issueId}`;\n let files: string[];\n let stagedBefore: string[];\n try {\n files = await deps.listConflictedFiles(ctx.worktreePath);\n stagedBefore = await deps.stagedFiles(ctx.worktreePath);\n } catch (err) {\n log(`${tag}: could not inspect conflicted state: ${err instanceof Error ? err.message : String(err)}`);\n return null;\n }\n if (files.length === 0) {\n log(`${tag}: merge failed without unmerged paths — not a content conflict, giving up`);\n return null;\n }\n\n try {\n await deps.runAgent({\n prompt: buildConflictResolutionPrompt(ctx, files),\n cwd: ctx.worktreePath,\n timeoutMs,\n });\n } catch (err) {\n log(`${tag}: agent run failed (${err instanceof Error ? err.message.split('\\n')[0] : String(err)})`);\n return null;\n }\n\n try {\n const unsafe = await deps.unsafeChangedFiles(ctx.worktreePath, files);\n if (unsafe.length > 0) {\n log(`${tag}: agent touched non-conflicted path(s): ${unsafe.join(', ')}`);\n await deps.discardFiles(ctx.worktreePath, unsafe).catch((err) => {\n log(`${tag}: cleanup of non-conflicted path(s) failed: ${err instanceof Error ? err.message : String(err)}`);\n });\n return null;\n }\n\n const unresolved = await deps.filesWithConflictMarkers(ctx.worktreePath, files);\n if (unresolved.length > 0) {\n log(`${tag}: conflict markers remain in ${unresolved.join(', ')}`);\n return null;\n }\n await deps.stageFiles(ctx.worktreePath, files);\n if (await deps.hasUnmergedEntries(ctx.worktreePath)) {\n log(`${tag}: unmerged index entries remain after staging`);\n return null;\n }\n\n const stagedBeforeSet = new Set(stagedBefore);\n const allowed = allowlist(files);\n const stagedAfter = await deps.stagedFiles(ctx.worktreePath);\n const newlyStagedOutsideAllowlist = stagedAfter.filter((file) =>\n !stagedBeforeSet.has(file) && !allowed.has(file.replace(/\\\\/g, '/')),\n );\n if (newlyStagedOutsideAllowlist.length > 0) {\n log(`${tag}: cached diff includes non-conflicted path(s): ${newlyStagedOutsideAllowlist.join(', ')}`);\n return null;\n }\n\n await deps.commitMerge(ctx.worktreePath, buildMergeCommitMessage(ctx, files));\n const commitSha = await deps.headSha(ctx.worktreePath);\n log(`${tag}: resolved ${files.length} file(s) at ${commitSha.slice(0, 9)}`);\n return { files, commitSha };\n } catch (err) {\n log(`${tag}: verification/commit failed: ${err instanceof Error ? err.message.split('\\n')[0] : String(err)}`);\n return null;\n }\n };\n}\n","/**\n * UAT generation reconciler (PAN-1737: UAT batch trains).\n *\n * The loop that keeps \"always one batch ready\" true with zero human action.\n * Each tick (interval-driven from the dashboard server, or forced via the\n * API's rebuild action):\n *\n * 1. No-op unless the merge train is enabled and a flywheel run is active.\n * 2. Invalidate live generations that went stale — main advanced past their\n * baseSha, a member left the ready set, or a member branch gained\n * commits. Invalidation tears down the generation's live stack.\n * (Older SUBSET generations are NOT stale: a smaller batch off current\n * main remains perfectly testable and promotable.)\n * 3. If no live generation matches the current desired set, assemble the\n * next generation in the background. Single-flight per project; a failed\n * assembly for the SAME desired signature backs off before retrying.\n * 4. Trim/reap the chain (cleanup hook).\n *\n * Pure orchestration with injected deps — interval wiring and real data\n * sources live in the dashboard server service.\n */\nimport type { ReadyFeature, GenerationStorePort } from './uat-generation-engine.js';\nimport type { UatGeneration } from '../overdeck/merge-sync.js';\n\n/**\n * An 'assembling' row with NO PROGRESS for this long is marked failed (train\n * un-wedge). Keyed on updated_at — the engine patches the row after every\n * member step — so a big batch grinding through many timeboxed conflict\n * resolutions is never killed for total age (the first live run killed a\n * slow-but-alive 5-member assembly at a flat 30min of created_at age; with\n * many developers driving hundreds of agents, assemblies will only get\n * bigger). A crashed engine stops updating the row and trips this within the\n * hour. Cooperative cancellation of the in-flight engine is PAN-1755.\n */\nexport const STUCK_ASSEMBLING_MS = 60 * 60 * 1000;\n/** Minimum age before re-attempting an assembly that failed for the same input. */\nexport const FAILED_RETRY_BACKOFF_MS = 10 * 60 * 1000;\n\nexport interface UatReconcilerDeps {\n /** Gate: flywheel.merge_train_enabled. */\n isEnabled(): boolean;\n /**\n * Current ready set in merge order, or null when it cannot be computed\n * (no active flywheel run) — null means \"do nothing\", not \"empty\".\n */\n getReadySet(): Promise<readonly ReadyFeature[] | null>;\n /** Current origin/main head SHA (fetching first). */\n getMainHeadSha(): Promise<string>;\n /** Current head SHA per feature branch (origin-first). */\n getBranchHeadSha(branch: string): Promise<string>;\n store: GenerationStorePort;\n /** Assemble the next generation from the desired set (the engine). */\n assemble(features: readonly ReadyFeature[]): Promise<UatGeneration>;\n /** Tear down a generation's live stack (no-op when none is running). */\n teardownStack(generation: UatGeneration): Promise<void>;\n /** Chain trim/reap (cleanupUatGenerations wiring). */\n cleanup(): Promise<void>;\n now?: () => number;\n log?: (msg: string) => void;\n}\n\nexport interface ReconcileResult {\n action: 'disabled' | 'no-queue' | 'idle' | 'assembled' | 'assembly-failed' | 'backoff' | 'in-flight';\n invalidated: string[];\n generation?: UatGeneration;\n}\n\n/**\n * Single-flight per project: a tick that arrives while one is running gets\n * 'in-flight' immediately instead of queuing (assemblies are minutes-long;\n * the interval would pile up).\n */\nconst inFlightReconciles = new Map<string, Promise<ReconcileResult>>();\n\n/** Stable signature of \"what we want assembled\" — backoff bookkeeping. */\nfunction desiredSignature(features: readonly ReadyFeature[], headShas: ReadonlyMap<string, string>, mainSha: string): string {\n const parts = [...features]\n .map((f) => `${f.issueId.toUpperCase()}@${headShas.get(f.branch) ?? 'unknown'}`)\n .sort();\n return `${mainSha}|${parts.join(',')}`;\n}\n\nfunction generationSignature(gen: UatGeneration): string {\n const parts = [\n ...gen.members.map((m) => `${m.issueId.toUpperCase()}@${m.headSha}`),\n ...gen.heldOut.map((h) => `${h.issueId.toUpperCase()}@${h.headSha ?? 'held'}`),\n ].sort();\n return `${gen.baseSha}|${parts.join(',')}`;\n}\n\nfunction liveSignatureMatches(\n gen: UatGeneration,\n features: readonly ReadyFeature[],\n headShas: ReadonlyMap<string, string>,\n mainSha: string,\n): boolean {\n if (gen.baseSha !== mainSha) return false;\n const desiredIds = new Set(features.map((f) => f.issueId.toUpperCase()));\n const genIds = new Set([\n ...gen.members.map((m) => m.issueId.toUpperCase()),\n ...gen.heldOut.map((h) => h.issueId.toUpperCase()),\n ]);\n if (desiredIds.size !== genIds.size) return false;\n for (const id of desiredIds) if (!genIds.has(id)) return false;\n for (const member of gen.members) {\n const current = headShas.get(member.branch);\n if (current !== member.headSha) return false;\n }\n for (const held of gen.heldOut) {\n if (!held.branch || !held.headSha) return false;\n const current = headShas.get(held.branch);\n if (current !== held.headSha) return false;\n }\n return true;\n}\n\n/**\n * A live generation is INVALIDATED only when its own CONTENT can no longer be\n * trusted for promotion: a bundled member left the ready queue (its work must\n * not merge) or a member's branch gained commits (the tested tree is outdated).\n *\n * Deliberately NOT invalidation triggers (the first live run, 2026-06-10,\n * churned every ready batch to death within minutes):\n * - main advancing past baseSha — an active flywheel run lands strike commits\n * on main continuously; the batch tree is still perfectly testable. Base\n * movement makes liveSignatureMatches() fail, so a FRESH superset assembles\n * and supersedes this one when ready; promotion safety against a moved base\n * is enforced at promote time (member-file overlap check + clean merge).\n * - held-out drift (departed / branch moved / metadata missing) — held-out\n * features are not in the tree; their changes also flow through\n * liveSignatureMatches() into a fresh assembly, never destruction of a\n * testable batch.\n */\nfunction isStale(\n gen: UatGeneration,\n desiredIds: ReadonlySet<string>,\n headShas: ReadonlyMap<string, string>,\n): string | null {\n for (const member of gen.members) {\n if (!desiredIds.has(member.issueId.toUpperCase())) {\n return `${member.issueId} left the ready queue`;\n }\n const current = headShas.get(member.branch);\n if (current !== member.headSha) {\n return `${member.issueId} branch gained commits`;\n }\n }\n return null;\n}\n\nexport async function reconcileUatGenerations(\n projectRoot: string,\n deps: UatReconcilerDeps,\n options: { force?: boolean } = {},\n): Promise<ReconcileResult> {\n const log = deps.log ?? (() => {});\n const now = deps.now ?? Date.now;\n const invalidated: string[] = [];\n\n if (!deps.isEnabled()) return { action: 'disabled', invalidated };\n\n if (inFlightReconciles.has(projectRoot)) return { action: 'in-flight', invalidated };\n\n const tick = async (): Promise<ReconcileResult> => {\n const readySet = await deps.getReadySet();\n if (readySet === null) return { action: 'no-queue', invalidated };\n\n const mainSha = await deps.getMainHeadSha();\n const headShas = new Map<string, string>();\n for (const f of readySet) {\n headShas.set(f.branch, await deps.getBranchHeadSha(f.branch).catch(() => 'unknown'));\n }\n const desiredIds = new Set(readySet.map((f) => f.issueId.toUpperCase()));\n\n // 1. Invalidate stale live generations (stack teardown included).\n for (const gen of deps.store.listChain(projectRoot, ['ready', 'superseded'])) {\n const staleReason = isStale(gen, desiredIds, headShas);\n if (!staleReason) continue;\n log(`[uat-reconciler] invalidating ${gen.name}: ${staleReason}`);\n deps.store.update(gen.name, { status: 'invalidated' });\n invalidated.push(gen.name);\n await deps.teardownStack(gen).catch((err) => {\n log(`[uat-reconciler] stack teardown for ${gen.name} failed: ${err instanceof Error ? err.message : String(err)}`);\n });\n }\n\n // 2. Un-wedge crashed assemblies; respect a live one. \"Stuck\" means NO\n // PROGRESS (updated_at stale) — the engine patches the row after every\n // member step, so total age is the wrong signal: a big batch grinding\n // through many timeboxed conflict resolutions is alive, just slow.\n const assembling = deps.store.listChain(projectRoot, ['assembling']);\n for (const gen of assembling) {\n const lastProgress = Date.parse(gen.updatedAt || gen.createdAt || '');\n // Unparseable timestamps: freshness can't be proven — treat as stuck.\n const sinceProgress = Number.isFinite(lastProgress) ? now() - lastProgress : Number.POSITIVE_INFINITY;\n if (sinceProgress > STUCK_ASSEMBLING_MS) {\n log(`[uat-reconciler] marking stuck assembling generation ${gen.name} failed (no progress for ${Math.round(sinceProgress / 60000)}m)`);\n deps.store.update(gen.name, { status: 'failed' });\n }\n }\n if (!options.force && deps.store.listChain(projectRoot, ['assembling']).length > 0) {\n return { action: 'in-flight', invalidated };\n }\n\n if (readySet.length === 0) {\n await deps.cleanup().catch(() => {});\n return { action: 'idle', invalidated };\n }\n\n // 3. Assemble when nothing live answers the current desired set.\n const desired = desiredSignature(readySet, headShas, mainSha);\n if (!options.force) {\n const live = deps.store.listChain(projectRoot, ['ready', 'superseded']);\n if (live.some((gen) => liveSignatureMatches(gen, readySet, headShas, mainSha))) {\n await deps.cleanup().catch(() => {});\n return { action: 'idle', invalidated };\n }\n const failed = deps.store.listChain(projectRoot, ['failed']);\n const recentFailure = failed.find((gen) =>\n generationSignature(gen) === desired &&\n now() - Date.parse(gen.updatedAt || gen.createdAt || '') < FAILED_RETRY_BACKOFF_MS,\n );\n if (recentFailure) {\n log(`[uat-reconciler] backing off — ${recentFailure.name} failed recently for the same input`);\n return { action: 'backoff', invalidated };\n }\n }\n\n log(`[uat-reconciler] assembling next generation for ${readySet.length} ready feature(s)`);\n const generation = await deps.assemble(readySet);\n await deps.cleanup().catch(() => {});\n return {\n action: generation.status === 'ready' ? 'assembled' : 'assembly-failed',\n invalidated,\n generation,\n };\n };\n\n const pending = tick().finally(() => inFlightReconciles.delete(projectRoot));\n inFlightReconciles.set(projectRoot, pending);\n return pending;\n}\n","/**\n * Live UAT stack lifecycle for batch generations (PAN-1737, absorbs PAN-1738).\n *\n * Each ready generation can serve a real dashboard stack from its persistent\n * worktree: the devcontainer renders via the standard FEATURE_FOLDER template,\n * so the folder name (`uat-<label>-<codename>-<mmdd>`) yields the Traefik host\n * `uat-<label>-<codename>-<mmdd>.overdeck.localhost` with zero new infra.\n *\n * HARD INVARIANT — max 2 UAT stacks run concurrently. Docker's default\n * address pool fits ~31 bridge networks; accumulating UAT stacks would\n * eventually block ALL workspace creation (\"all predefined address pools\n * have been fully subnetted\"). Starting a third stack tears down the oldest\n * first, and invalidation/promotion always tear the generation's stack down.\n *\n * All process exec is async (server-reachable code — never execSync).\n */\nimport { existsSync } from 'fs';\nimport { join, dirname, basename } from 'path';\nimport { readFile } from 'node:fs/promises';\nimport { execFile } from 'child_process';\nimport { promisify } from 'util';\nimport { ensureDevcontainerSync } from '../workspace/ensure-devcontainer.js';\nimport {\n listUatGenerationsWithStacksSync,\n setUatGenerationStackStartedAtSync,\n type UatGeneration,\n} from '../overdeck/merge-sync.js';\n\nconst execFileAsync = promisify(execFile);\n\nexport const MAX_UAT_STACKS = 2;\n\nconst COMPOSE_FILES = [\n 'docker-compose.devcontainer.yml',\n 'docker-compose.yml',\n 'docker-compose.yaml',\n 'compose.yml',\n 'compose.yaml',\n];\n\nconst stackMutationLocks = new Map<string, Promise<void>>();\n\nasync function withStackMutationLock<T>(projectRoot: string, fn: () => Promise<T>): Promise<T> {\n const previous = stackMutationLocks.get(projectRoot) ?? Promise.resolve();\n let release!: () => void;\n const current = new Promise<void>((resolve) => { release = resolve; });\n const tail = previous.catch(() => {}).then(() => current);\n stackMutationLocks.set(projectRoot, tail);\n await previous.catch(() => {});\n try {\n return await fn();\n } finally {\n release();\n if (stackMutationLocks.get(projectRoot) === tail) stackMutationLocks.delete(projectRoot);\n }\n}\n\nexport interface UatStackDeps {\n /** Render `.devcontainer/` if missing; issueId only resolves project config. */\n ensureDevcontainer(workspacePath: string, issueId: string): { ok: boolean; error?: string };\n composeUp(composeFile: string, projectName: string): Promise<void>;\n composeDown(composeFile: string, projectName: string): Promise<void>;\n /** Number of running containers for the compose project. */\n composePsCount(composeFile: string, projectName: string): Promise<number>;\n findComposeFile(workspacePath: string): string | null;\n readComposeFile(composeFile: string): Promise<string>;\n store: {\n setStack(name: string, startedAt: string | null): void;\n listWithStacks(): UatGeneration[];\n };\n log?: (msg: string) => void;\n}\n\nfunction defaultDeps(): UatStackDeps {\n const compose = (args: string[], cwd: string) =>\n execFileAsync('docker', ['compose', ...args], {\n cwd,\n encoding: 'utf-8',\n timeout: 300_000,\n maxBuffer: 10 * 1024 * 1024,\n });\n\n return {\n ensureDevcontainer: (workspacePath, issueId) => {\n const result = ensureDevcontainerSync({ workspacePath, issueId });\n return result.step.success\n ? { ok: true }\n : { ok: false, error: result.step.error ?? 'devcontainer render failed' };\n },\n composeUp: async (composeFile, projectName) => {\n await compose(['-f', composeFile, '-p', projectName, 'up', '-d', '--build'], dirname(composeFile));\n },\n composeDown: async (composeFile, projectName) => {\n await compose(['-f', composeFile, '-p', projectName, 'down', '-v', '--remove-orphans'], dirname(composeFile));\n },\n composePsCount: async (composeFile, projectName) => {\n const { stdout } = await compose(['-f', composeFile, '-p', projectName, 'ps', '-q'], dirname(composeFile));\n return stdout.split('\\n').filter((l) => l.trim().length > 0).length;\n },\n findComposeFile: (workspacePath) => {\n const devcontainerDir = join(workspacePath, '.devcontainer');\n for (const file of COMPOSE_FILES) {\n const fullPath = join(devcontainerDir, file);\n if (existsSync(fullPath)) return fullPath;\n }\n return null;\n },\n readComposeFile: (composeFile) => readFile(composeFile, 'utf-8'),\n store: {\n setStack: (name, startedAt) => setUatGenerationStackStartedAtSync(name, startedAt),\n listWithStacks: () => listUatGenerationsWithStacksSync(),\n },\n };\n}\n\nexport function uatStackFolderName(gen: UatGeneration): string {\n return basename(gen.worktreePath);\n}\n\nfunction composeProjectName(gen: UatGeneration): string {\n return `overdeck-${uatStackFolderName(gen)}`;\n}\n\n/**\n * The frontend URL the generation's stack serves (or will serve). Prefers the\n * Host(`…`) Traefik label in the rendered compose file; falls back to the\n * FEATURE_FOLDER convention.\n */\nexport async function uatFrontendUrl(gen: UatGeneration, deps: Partial<UatStackDeps> = {}): Promise<string> {\n const d = { ...defaultDeps(), ...deps };\n const folder = uatStackFolderName(gen);\n const composeFile = d.findComposeFile(gen.worktreePath);\n if (composeFile) {\n try {\n const content = await d.readComposeFile(composeFile);\n const hostMatch = content.match(new RegExp('Host\\\\(`(' + folder.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&') + '\\\\.[^`]+)`\\\\)'));\n if (hostMatch?.[1]) return `https://${hostMatch[1]}`;\n } catch { /* fall through to convention */ }\n }\n return `https://${folder}.overdeck.localhost`;\n}\n\nexport type UatStackStatus = 'running' | 'absent';\n\nexport interface UatStackProbe {\n status: UatStackStatus;\n frontendUrl: string;\n}\n\n/** Probe a generation's stack. Self-corrects stale stackStartedAt records. */\nexport async function probeUatStack(gen: UatGeneration, deps: Partial<UatStackDeps> = {}): Promise<UatStackProbe> {\n const d = { ...defaultDeps(), ...deps };\n const frontendUrl = await uatFrontendUrl(gen, d);\n const composeFile = d.findComposeFile(gen.worktreePath);\n if (!composeFile || !gen.stackStartedAt) return { status: 'absent', frontendUrl };\n const count = await d.composePsCount(composeFile, composeProjectName(gen)).catch(() => 0);\n if (count === 0) {\n // Record says running but nothing is — heal the record.\n try { d.store.setStack(gen.name, null); } catch { /* row may be gone */ }\n return { status: 'absent', frontendUrl };\n }\n return { status: 'running', frontendUrl };\n}\n\nexport interface EnsureUatStackResult {\n success: boolean;\n error?: string;\n frontendUrl?: string;\n /** Generations whose stacks were torn down to respect MAX_UAT_STACKS. */\n evicted: string[];\n}\n\n/**\n * Bring up the live stack for a generation (idempotent — compose up on a\n * running project is a no-op). Enforces MAX_UAT_STACKS by tearing down the\n * oldest running UAT stack(s) first.\n */\nexport async function ensureUatStack(gen: UatGeneration, deps: Partial<UatStackDeps> = {}): Promise<EnsureUatStackResult> {\n return withStackMutationLock(gen.projectRoot, async () => {\n const d = { ...defaultDeps(), ...deps };\n const log = d.log ?? (() => {});\n const evicted: string[] = [];\n\n const member = gen.members[0];\n if (!member) return { success: false, error: 'generation has no members', evicted };\n\n // Cap enforcement happens inside the lock and re-reads the live stack set so\n // concurrent start requests cannot both observe the same stale pre-start set.\n const others = d.store.listWithStacks().filter((g) => g.name !== gen.name);\n while (others.length > 0 && others.length >= MAX_UAT_STACKS) {\n const oldest = others.shift()!;\n log(`[uat-stack] cap ${MAX_UAT_STACKS} reached — tearing down oldest stack ${oldest.name}`);\n try {\n await teardownUatStackUnlocked(oldest, d);\n } catch (err) {\n return {\n success: false,\n error: err instanceof Error ? (err.message.split('\\n')[0] ?? 'stack teardown failed') : String(err),\n evicted,\n };\n }\n evicted.push(oldest.name);\n }\n\n const rendered = d.ensureDevcontainer(gen.worktreePath, member.issueId);\n if (!rendered.ok) return { success: false, error: rendered.error ?? 'devcontainer render failed', evicted };\n\n const composeFile = d.findComposeFile(gen.worktreePath);\n if (!composeFile) {\n return { success: false, error: `no compose file under ${gen.worktreePath}/.devcontainer`, evicted };\n }\n\n try {\n await d.composeUp(composeFile, composeProjectName(gen));\n } catch (err) {\n return {\n success: false,\n error: err instanceof Error ? (err.message.split('\\n')[0] ?? 'compose up failed') : String(err),\n evicted,\n };\n }\n\n d.store.setStack(gen.name, new Date().toISOString());\n const frontendUrl = await uatFrontendUrl(gen, d);\n log(`[uat-stack] ${gen.name}: stack up at ${frontendUrl}`);\n return { success: true, frontendUrl, evicted };\n });\n}\n\nasync function teardownUatStackUnlocked(gen: UatGeneration, d: UatStackDeps): Promise<void> {\n const composeFile = d.findComposeFile(gen.worktreePath);\n if (composeFile) {\n try {\n await d.composeDown(composeFile, composeProjectName(gen));\n } catch (err) {\n d.log?.(`[uat-stack] ${gen.name}: compose down failed: ${err instanceof Error ? err.message.split('\\n')[0] : String(err)}`);\n throw err;\n }\n }\n try { d.store.setStack(gen.name, null); } catch { /* row may be gone */ }\n}\n\n/**\n * Tear the generation's stack down (idempotent) and clear its stack record.\n * MUST be called on invalidation, promotion, and generation cleanup —\n * orphaned uat networks eventually block all workspace creation.\n */\nexport async function teardownUatStack(gen: UatGeneration, deps: Partial<UatStackDeps> = {}): Promise<void> {\n await withStackMutationLock(gen.projectRoot, async () => {\n const d = { ...defaultDeps(), ...deps };\n await teardownUatStackUnlocked(gen, d);\n });\n}\n","import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nconst execFileAsync = promisify(execFile);\n\nexport async function reconcileFileSizeBaseline(\n worktreePath: string,\n runGit: (args: string[], cwd: string) => Promise<{ stdout: string }>,\n): Promise<{ changed: boolean }> {\n await execFileAsync('bash', ['scripts/lint-file-size.sh', '--update'], {\n cwd: worktreePath,\n maxBuffer: 16 * 1024 * 1024,\n });\n\n const { stdout } = await runGit(['status', '--porcelain', '--', 'scripts/file-size-baseline.txt'], worktreePath);\n if (stdout.trim() === '') return { changed: false };\n\n await runGit(['add', 'scripts/file-size-baseline.txt'], worktreePath);\n return { changed: true };\n}\n","/**\n * Batch promotion (PAN-1737: UAT batch trains) — \"merge what you tested\".\n *\n * Promoting a generation merges the uat/* branch itself into main (one no-ff\n * merge), so main receives EXACTLY the tree the operator exercised — including\n * the assembly agent's conflict resolutions. The deliberate exception is\n * file-size baseline reconciliation: scripts/file-size-baseline.txt is CI\n * metadata, so stale-high entries may be lowered or dropped before push.\n * Per-feature PRs are marked merged by GitHub automatically (their head commits\n * become reachable from main), and each member issue then flows through the\n * standard per-issue post-merge lifecycle exactly once, behind the PAN-328\n * in-flight guard.\n *\n * Hard precondition: the generation's baseSha must still equal origin/main —\n * promoting a stale tree would silently drop commits that landed since\n * assembly. Stale promotes are rejected with reassemble guidance (the\n * reconciler rebuilds automatically anyway).\n *\n * Pure orchestration with injected deps; real git in buildUatPromoteGitDeps.\n */\nimport { join } from 'path';\nimport { tmpdir } from 'os';\nimport { promisify } from 'util';\nimport { execFile } from 'child_process';\nimport type { UatGeneration, UatGenerationStatus } from '../overdeck/merge-sync.js';\nimport { reconcileFileSizeBaseline } from './file-size-reconcile.js';\nimport type { GenerationStorePort } from './uat-generation-engine.js';\n\nconst execFileAsync = promisify(execFile);\n\nfunction safeUatBranchName(branchName: string): string {\n if (!/^uat\\/[A-Za-z0-9][A-Za-z0-9._-]*$/.test(branchName)) {\n throw new Error(`unsafe UAT branch name: ${branchName}`);\n }\n return branchName;\n}\n\nexport interface UatPromoteGitDeps {\n /** `git fetch origin main` and return the origin/main head SHA. */\n fetchMain(): Promise<string>;\n /**\n * Merge the generation branch into main with the given message (no-ff, in a\n * throwaway worktree — NEVER the primary checkout) and push. Returns the\n * merge commit SHA.\n */\n mergeIntoMain(branchName: string, message: string): Promise<string>;\n /** Files changed on origin/main since the given SHA (base-movement footprint). */\n changedFilesSince(baseSha: string): Promise<string[]>;\n /** Files the batch branch changes vs its merge-base with origin/main. */\n batchChangedFiles(branchName: string): Promise<string[]>;\n}\n\nexport interface UatPromoteDeps {\n git: UatPromoteGitDeps;\n store: GenerationStorePort & { get(name: string): UatGeneration | null };\n teardownStack(generation: UatGeneration): Promise<void>;\n /**\n * Kick off the per-issue post-merge lifecycle (through the PAN-328 guard).\n * Returns false when a run for that issue is already in flight.\n */\n firePostMerge(issueId: string, options?: { sourceBranch?: string; verifiedMergedRef?: string }): boolean;\n /**\n * Authoritative per-member merge eligibility (PAN-1759), checked at promote\n * time as defense-in-depth: batch membership is computed from orchestrator\n * verbs at assembly time, and a member's pipeline state can change (or have\n * been wrong) between assembly and the operator clicking Merge. RUN-20\n * assembled a batch containing a mid-review issue; this gate refuses to land\n * it on main.\n */\n memberEligibility(issueId: string): { eligible: boolean; reason?: string };\n log?: (msg: string) => void;\n}\n\nexport type PromoteFailureReason = 'not-found' | 'wrong-status' | 'member-not-ready' | 'stale-base' | 'merge-failed';\n\nexport type PromoteResult =\n | {\n success: true;\n generation: string;\n mergeSha: string;\n members: string[];\n /** Members whose post-merge lifecycle started (false = already in flight). */\n postMergeStarted: string[];\n invalidated: string[];\n }\n | { success: false; reason: PromoteFailureReason; message: string };\n\nconst PROMOTABLE: readonly UatGenerationStatus[] = ['ready', 'superseded'];\n\nexport function buildPromoteMergeMessage(gen: UatGeneration): string {\n const ids = gen.members.map((m) => m.issueId).join(', ');\n const resolutionNote = gen.resolutions.length > 0\n ? `\\n\\nIncludes ${gen.resolutions.length} uat-assembly conflict resolution(s): ` +\n gen.resolutions.map((r) => r.issueIds.join(' <-> ')).join('; ')\n : '';\n return `Merge UAT batch ${gen.name} (${ids})${resolutionNote}`;\n}\n\nexport async function promoteUatGeneration(\n name: string,\n projectRoot: string,\n deps: UatPromoteDeps,\n): Promise<PromoteResult> {\n const log = deps.log ?? (() => {});\n\n const gen = deps.store.get(name);\n if (!gen || gen.projectRoot !== projectRoot) {\n return { success: false, reason: 'not-found', message: `No UAT generation named ${name}` };\n }\n if (!PROMOTABLE.includes(gen.status)) {\n return {\n success: false,\n reason: 'wrong-status',\n message: `${name} is ${gen.status} — only a ready or superseded batch can be merged to main`,\n };\n }\n\n const notReady = gen.members\n .map((m) => ({ issueId: m.issueId, gate: deps.memberEligibility(m.issueId) }))\n .filter(({ gate }) => !gate.eligible);\n if (notReady.length > 0) {\n const detail = notReady.map(({ issueId, gate }) => `${issueId} (${gate.reason ?? 'not eligible'})`).join(', ');\n return {\n success: false,\n reason: 'member-not-ready',\n message:\n `${name} contains member(s) the pipeline has not cleared to merge: ${detail} — ` +\n `wait for them to pass review+test, or for the reconciler to rebuild the batch without them.`,\n };\n }\n\n const mainSha = await deps.git.fetchMain();\n if (gen.baseSha !== mainSha) {\n // Main moved since assembly. An active flywheel run lands commits on main\n // continuously, so exact base equality would make ready batches almost\n // never promotable (first live run, 2026-06-10). Safety is preserved by a\n // narrower check: reject only when main's NEW commits touch files the\n // batch also touches — then the tested behavior genuinely may not match\n // what lands. Disjoint movement (docs, unrelated fixes) proceeds; the\n // no-ff merge itself still hard-fails on any textual conflict.\n const [mainChanged, batchChanged] = await Promise.all([\n deps.git.changedFilesSince(gen.baseSha),\n deps.git.batchChangedFiles(gen.name),\n ]);\n const batchSet = new Set(batchChanged);\n const overlap = mainChanged.filter((f) => batchSet.has(f));\n if (overlap.length > 0) {\n return {\n success: false,\n reason: 'stale-base',\n message:\n `${name} was assembled off ${gen.baseSha.slice(0, 9)} but main is now at ${mainSha.slice(0, 9)}, ` +\n `and the new commits touch ${overlap.length} file(s) this batch also changes ` +\n `(${overlap.slice(0, 3).join(', ')}${overlap.length > 3 ? ', …' : ''}) — ` +\n `the tree you tested may not match what would land. A fresh batch reassembles automatically; re-test before merging.`,\n };\n }\n log(`[uat-promote] ${name}: base moved ${gen.baseSha.slice(0, 9)} → ${mainSha.slice(0, 9)} with no member-file overlap — proceeding`);\n }\n\n let mergeSha: string;\n try {\n mergeSha = await deps.git.mergeIntoMain(gen.name, buildPromoteMergeMessage(gen));\n } catch (err) {\n return {\n success: false,\n reason: 'merge-failed',\n message: err instanceof Error ? (err.message.split('\\n')[0] ?? 'merge failed') : String(err),\n };\n }\n log(`[uat-promote] ${name}: merged to main at ${mergeSha.slice(0, 9)} (${gen.members.length} member(s))`);\n\n // The batch is on main: this generation is done, every other live\n // generation is stale by definition (main moved).\n deps.store.update(gen.name, { status: 'promoted' });\n await deps.teardownStack(gen).catch((err) => {\n log(`[uat-promote] ${name}: stack teardown failed: ${err instanceof Error ? err.message : String(err)}`);\n });\n\n const invalidated: string[] = [];\n for (const other of deps.store.listChain(projectRoot, ['ready', 'superseded'])) {\n if (other.name === gen.name) continue;\n deps.store.update(other.name, { status: 'invalidated' });\n invalidated.push(other.name);\n await deps.teardownStack(other).catch(() => {});\n }\n\n // Standard per-issue post-merge handoff, exactly once each (PAN-328 guard).\n const postMergeStarted: string[] = [];\n for (const member of gen.members) {\n if (deps.firePostMerge(member.issueId, {\n sourceBranch: member.branch,\n verifiedMergedRef: member.headSha || member.branch,\n })) postMergeStarted.push(member.issueId);\n else log(`[uat-promote] ${name}: post-merge for ${member.issueId} already in flight — skipped`);\n }\n\n return {\n success: true,\n generation: gen.name,\n mergeSha,\n members: gen.members.map((m) => m.issueId),\n postMergeStarted,\n invalidated,\n };\n}\n\n/** Real git wiring: throwaway detached worktree off origin/main, no-ff merge, baseline reconcile, push. */\nexport function buildUatPromoteGitDeps(projectRoot: string): UatPromoteGitDeps {\n const runGit = (args: string[], cwd: string) =>\n execFileAsync('git', args, { cwd, maxBuffer: 16 * 1024 * 1024 });\n\n return {\n fetchMain: async () => {\n await runGit(['fetch', 'origin', 'main'], projectRoot);\n return (await runGit(['rev-parse', 'origin/main'], projectRoot)).stdout.trim();\n },\n mergeIntoMain: async (branchName, message) => {\n const safeBranch = safeUatBranchName(branchName);\n const worktreePath = join(tmpdir(), `uat-promote-${safeBranch.replace(/[^a-z0-9]/gi, '-')}`);\n await runGit(['worktree', 'remove', '--force', worktreePath], projectRoot).catch(() => {});\n await runGit(['worktree', 'prune'], projectRoot).catch(() => {});\n await runGit(['worktree', 'add', '--detach', worktreePath, 'origin/main'], projectRoot);\n try {\n const originRef = `origin/${safeBranch}`;\n const ref = await runGit(['rev-parse', '--verify', originRef], worktreePath)\n .then(() => originRef)\n .catch(() => safeBranch);\n await runGit(['merge', '--no-ff', ref, '-m', message], worktreePath);\n const { changed } = await reconcileFileSizeBaseline(worktreePath, runGit);\n if (changed) await runGit(['commit', '--amend', '--no-edit'], worktreePath);\n await runGit(['push', 'origin', 'HEAD:main'], worktreePath);\n return (await runGit(['rev-parse', 'HEAD'], worktreePath)).stdout.trim();\n } finally {\n await runGit(['worktree', 'remove', '--force', worktreePath], projectRoot).catch(() => {});\n await runGit(['worktree', 'prune'], projectRoot).catch(() => {});\n }\n },\n changedFilesSince: async (baseSha) => {\n if (!/^[0-9a-f]{7,40}$/i.test(baseSha)) throw new Error(`unsafe base sha: ${baseSha}`);\n const { stdout } = await runGit(['diff', '--name-only', `${baseSha}..origin/main`], projectRoot);\n return stdout.split('\\n').map((l) => l.trim()).filter(Boolean);\n },\n batchChangedFiles: async (branchName) => {\n const safeBranch = safeUatBranchName(branchName);\n const ref = await runGit(['rev-parse', '--verify', `origin/${safeBranch}`], projectRoot)\n .then(() => `origin/${safeBranch}`)\n .catch(() => safeBranch);\n const mergeBase = (await runGit(['merge-base', 'origin/main', ref], projectRoot)).stdout.trim();\n const { stdout } = await runGit(['diff', '--name-only', `${mergeBase}..${ref}`], projectRoot);\n return stdout.split('\\n').map((l) => l.trim()).filter(Boolean);\n },\n };\n}\n","import { Effect } from 'effect';\nimport { messageAgent } from '../agents.js';\nimport { getFlywheelActiveRunId, isFlywheelGloballyPaused } from '../overdeck/control-settings.js';\nimport { sessionExists } from '../tmux.js';\nimport { recordDeaconNudge } from './deacon-nudge-log.js';\nimport { FLYWHEEL_ORCHESTRATOR_AGENT_ID } from './flywheel.js';\nimport type { PromoteResult } from './uat-promote.js';\n\ntype SessionExists = (name: string) => boolean | Promise<boolean>;\n\nexport interface NotifyDeps {\n getActiveRunId?: typeof getFlywheelActiveRunId;\n isPaused?: typeof isFlywheelGloballyPaused;\n sessionExists?: SessionExists;\n message?: typeof messageAgent;\n recordNudge?: typeof recordDeaconNudge;\n}\n\nfunction buildPromoteNudge(result: Extract<PromoteResult, { success: true }>): string {\n const members = result.members.length > 0 ? result.members.join(', ') : '(no members recorded)';\n return (\n `The operator just promoted UAT generation ${result.generation} to main at ${result.mergeSha}. ` +\n `Run a fresh Observe->Act loop NOW. Re-derive activePipeline from current source of truth and EXCLUDE ` +\n `the merged member(s): ${members}. Re-assemble a clean UAT batch with only members that are currently ` +\n `review+test passed, close out the promoted issue(s), emit a fresh status snapshot, and re-arm the next ` +\n `tick. Do NOT ask the operator a question, do NOT pause, and do NOT reuse the stale pre-promote ready set.`\n );\n}\n\nexport async function notifyFlywheelOfUatPromote(result: PromoteResult, deps: NotifyDeps = {}): Promise<void> {\n try {\n if (result.success !== true) return;\n\n const getActiveRunId = deps.getActiveRunId ?? getFlywheelActiveRunId;\n const isPaused = deps.isPaused ?? isFlywheelGloballyPaused;\n const sessionExistsDep = deps.sessionExists ?? ((name: string) => Effect.runPromise(sessionExists(name)));\n const message = deps.message ?? messageAgent;\n const recordNudge = deps.recordNudge ?? recordDeaconNudge;\n\n if (!getActiveRunId()) return;\n if (isPaused()) return;\n\n const delivered = await sessionExistsDep(FLYWHEEL_ORCHESTRATOR_AGENT_ID);\n if (delivered) {\n try {\n await message(FLYWHEEL_ORCHESTRATOR_AGENT_ID, buildPromoteNudge(result), 'uat-promote-notify');\n } catch {\n /* best-effort — the nudge log below still records that the session was present */\n }\n }\n\n recordNudge({\n patrol: 'uat-promote-notify',\n issueId: result.members[0] ?? result.generation,\n action: 'notified flywheel-orchestrator to re-derive ready set after UAT promote',\n reason:\n 'operator promoted a UAT batch; flywheel must immediately rebuild to drop merged + regressed members before its next tick',\n state: {\n generation: result.generation,\n members: result.members,\n mergeSha: result.mergeSha,\n delivered,\n },\n });\n } catch {\n /* best-effort — promote success must never be converted into a notification failure */\n }\n}\n","/**\n * UAT batch train service (PAN-1737) — real wiring for the reconciler,\n * assembly engine, stacks, and promote path, plus the payload builders the\n * flywheel routes expose.\n *\n * The reconciler interval is the heartbeat of \"always one batch ready\":\n * every 60s (gated per-tick on flywheel.merge_train_enabled, no-op without an\n * active flywheel run) it compares the ready set against the generation chain\n * and assembles/invalidates as needed. Assemblies run minutes — the reconciler\n * is single-flight per project, so ticks never pile up.\n */\nimport { stat } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { Effect } from 'effect';\nimport { layer as nodeServicesLayer } from '@effect/platform-node/NodeServices';\nimport {\n assembleUatGeneration,\n cleanupUatGenerations,\n type ReadyFeature,\n} from '../../../lib/cloister/uat-generation-engine.js';\nimport {\n buildUatGenerationGitDeps,\n buildUatGenerationStore,\n buildUatGenerationCleanupGit,\n listRemoteUatBranches,\n} from '../../../lib/cloister/uat-generation-deps.js';\nimport { buildConflictAgentHook } from '../../../lib/cloister/uat-conflict-agent.js';\nimport { reconcileUatGenerations, type ReconcileResult } from '../../../lib/cloister/uat-reconciler.js';\nimport { ensureUatStack, probeUatStack, teardownUatStack } from '../../../lib/cloister/uat-stack.js';\nimport {\n promoteUatGeneration,\n buildUatPromoteGitDeps,\n type PromoteResult,\n} from '../../../lib/cloister/uat-promote.js';\nimport { notifyFlywheelOfUatPromote } from '../../../lib/cloister/uat-promote-notify.js';\nimport {\n getUatGenerationSync,\n isMergeTrainEnabled,\n listUatGenerationsSync,\n type UatGeneration,\n} from '../../../lib/overdeck/merge-sync.js';\nimport { extractACFromDocument } from '../../../lib/vbrief/acceptance-criteria.js';\nimport { findVBriefByIssue, readVBriefDocument } from '../../../lib/vbrief/vbrief-index.js';\nimport { findProjectByPathSync } from '../../../lib/projects.js';\nimport { getDashboardIdentity } from '../identity.js';\nimport { readCurrentFlywheelStatusForDashboard } from './flywheel-actions.js';\n\nconst RECONCILE_INTERVAL_MS = 60_000;\nconst CHAIN_PAYLOAD_LIMIT = 10;\nconst ACCEPTANCE_CRITERIA_READ_CONCURRENCY = 4;\n\ntype AcceptanceCriteriaSummary = Array<{ title: string; status: string }>;\n\ninterface AcceptanceCriteriaCacheEntry {\n path: string;\n mtimeMs: number;\n criteria: AcceptanceCriteriaSummary;\n}\n\nconst acceptanceCriteriaByIssue = new Map<string, AcceptanceCriteriaCacheEntry>();\n\nexport function resolveUatProjectRoot(cwdPath = process.cwd()): string {\n const registeredProject = findProjectByPathSync(cwdPath);\n if (registeredProject) return resolve(registeredProject.path);\n\n const normalized = resolve(cwdPath);\n const workspaceMatch = normalized.match(/^(.*)\\/workspaces\\/feature-[^/]+(?:\\/.*)?$/i);\n return workspaceMatch?.[1] ? resolve(workspaceMatch[1]) : normalized;\n}\n\nfunction projectRoot(): string {\n return resolveUatProjectRoot();\n}\n\nexport function canStartUatTrainReconciler(): boolean {\n const identity = getDashboardIdentity();\n return identity.mode === 'primary' && resolve(identity.repoRoot) === projectRoot();\n}\n\n/** Ready set in merge order, or null when no flywheel run is active. */\nasync function getReadySet(): Promise<ReadyFeature[] | null> {\n const status = await readCurrentFlywheelStatusForDashboard();\n if (!status) return null;\n const { computeMergeQueue, resolveMergeQueuePrUrl } = await import('../../../lib/flywheel-merge-order.js');\n const queue = await Effect.runPromise(\n computeMergeQueue(status.activePipeline, projectRoot(), {\n getPrUrl: resolveMergeQueuePrUrl,\n onIneligible: (issueId, reason) =>\n console.log(`[uat-train] ${issueId} has a merge verb but is not merge-eligible (${reason}) — excluded from the ready set`),\n }).pipe(\n Effect.provide(nodeServicesLayer),\n ),\n );\n const fromVerbs: ReadyFeature[] = queue.map((item) => ({\n issueId: item.issueId,\n title: item.title,\n branch: item.branchName,\n ...(item.pr !== undefined ? { pr: item.pr } : {}),\n ...(item.prUrl !== undefined ? { prUrl: item.prUrl } : {}),\n conflictsWith: item.conflictsWith,\n }));\n const swept = await sweepEligibleFeatures(new Set(fromVerbs.map((f) => f.issueId)));\n return [...fromVerbs, ...swept];\n}\n\n/**\n * PAN-2484: the verb-based ready set misses merge-eligible issues that entered\n * the pipeline outside the active flywheel run (operator revivals, strikes,\n * externally-requested reviews) — those sat at readyForMerge with no train ever\n * assembling around them. Sweep review status for THIS project's merge-eligible\n * issues whose feature branch exists and append any the verb set missed. The\n * flywheel's ordering still leads; swept extras ride at the tail (the assembly\n * engine's conflict hook owns any cross-feature conflicts).\n */\nasync function sweepEligibleFeatures(alreadyIncluded: ReadySet): Promise<ReadyFeature[]> {\n try {\n const { loadReviewStatuses, mergeGateEligibility } = await import('../../../lib/review-status.js');\n const { resolveProjectFromIssueSync } = await import('../../../lib/projects.js');\n const { execFile } = await import('node:child_process');\n const { promisify } = await import('node:util');\n const execFileAsync = promisify(execFile);\n const root = projectRoot();\n const extras: ReadyFeature[] = [];\n for (const [issueId, rs] of Object.entries(loadReviewStatuses())) {\n if (alreadyIncluded.has(issueId)) continue;\n if (rs.readyForMerge !== true) continue;\n if (!mergeGateEligibility(rs).eligible) continue;\n const project = resolveProjectFromIssueSync(issueId);\n if (!project || resolve(project.projectPath) !== root) continue;\n const branch = `feature/${issueId.toLowerCase()}`;\n try {\n await execFileAsync('git', ['show-ref', '--verify', '--quiet', `refs/heads/${branch}`], { cwd: root, timeout: 10_000 });\n } catch {\n continue; // no local branch — nothing to assemble\n }\n console.log(`[uat-train] ${issueId} is merge-eligible without a flywheel merge verb — swept into the ready set (PAN-2484)`);\n extras.push({ issueId, title: issueId, branch, conflictsWith: [] });\n }\n return extras;\n } catch (err) {\n console.warn(`[uat-train] eligibility sweep failed: ${err instanceof Error ? err.message : String(err)}`);\n return [];\n }\n}\n\ntype ReadySet = Set<string>;\n\nfunction codenameLabel(features: readonly ReadyFeature[]): string {\n const prefix = features[0]?.issueId.split('-')[0];\n return (prefix ?? 'uat').toLowerCase();\n}\n\nasync function assembleFromReadySet(features: readonly ReadyFeature[]): Promise<UatGeneration> {\n const root = projectRoot();\n const store = buildUatGenerationStore();\n return assembleUatGeneration(\n {\n projectRoot: root,\n label: codenameLabel(features),\n dateIso: new Date().toISOString(),\n features,\n takenBranchNames: await listRemoteUatBranches(root),\n },\n {\n git: buildUatGenerationGitDeps(root),\n store,\n resolveConflict: buildConflictAgentHook(),\n log: (msg) => console.log(msg),\n },\n );\n}\n\nasync function runCleanup(): Promise<void> {\n const root = projectRoot();\n await cleanupUatGenerations(root, {\n store: buildUatGenerationStore(),\n ...buildUatGenerationCleanupGit(root),\n teardownStack: (gen) => teardownUatStack(gen),\n log: (msg) => console.log(msg),\n });\n}\n\n/** One reconciler pass. `force` rebuilds even when a live generation matches. */\nexport async function runUatTrainReconcile(options: { force?: boolean } = {}): Promise<ReconcileResult> {\n const root = projectRoot();\n const gitDeps = buildUatGenerationGitDeps(root);\n return reconcileUatGenerations(root, {\n isEnabled: () => isMergeTrainEnabled(),\n getReadySet,\n getMainHeadSha: () => gitDeps.fetchMain(),\n getBranchHeadSha: (branch) => gitDeps.branchHeadSha(branch),\n store: buildUatGenerationStore(),\n assemble: assembleFromReadySet,\n teardownStack: (gen) => teardownUatStack(gen),\n cleanup: runCleanup,\n log: (msg) => console.log(msg),\n }, options);\n}\n\nlet reconcilerTimer: ReturnType<typeof setInterval> | null = null;\n\nexport function startUatTrainReconciler(): boolean {\n if (!canStartUatTrainReconciler()) return false;\n if (reconcilerTimer) return true;\n reconcilerTimer = setInterval(() => {\n void runUatTrainReconcile().catch((err) => {\n console.warn('[uat-train] reconcile tick failed:', err instanceof Error ? err.message : err);\n });\n }, RECONCILE_INTERVAL_MS);\n reconcilerTimer.unref?.();\n void runUatTrainReconcile().catch((err) => {\n console.warn('[uat-train] initial reconcile failed:', err instanceof Error ? err.message : err);\n });\n return true;\n}\n\nexport function stopUatTrainReconciler(): void {\n if (reconcilerTimer) {\n clearInterval(reconcilerTimer);\n reconcilerTimer = null;\n }\n}\n\n// ─── Route payloads ───────────────────────────────────────────────────────────\n\nexport interface UatGenerationMemberPayload {\n issueId: string;\n title: string;\n branch: string;\n pr?: number;\n prUrl?: string;\n mergeOrder: number;\n /** What-to-UAT checklist from the issue's vBRIEF spec (shared extractor). */\n acceptanceCriteria: Array<{ title: string; status: string }>;\n}\n\nexport interface UatGenerationPayload {\n name: string;\n status: UatGeneration['status'];\n baseSha: string;\n createdAt: string;\n updatedAt: string;\n members: UatGenerationMemberPayload[];\n heldOut: UatGeneration['heldOut'];\n resolutions: UatGeneration['resolutions'];\n stack: { status: 'running' | 'absent'; frontendUrl: string };\n}\n\nasync function mapBounded<T>(items: readonly T[], concurrency: number, worker: (item: T) => Promise<void>): Promise<void> {\n let next = 0;\n const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {\n while (next < items.length) {\n const item = items[next++]!;\n await worker(item);\n }\n });\n await Promise.all(workers);\n}\n\nasync function loadAcceptanceCriteriaCache(issueIds: ReadonlySet<string>): Promise<Map<string, AcceptanceCriteriaSummary>> {\n const cache = new Map<string, AcceptanceCriteriaSummary>();\n if (issueIds.size === 0) return cache;\n\n const root = projectRoot();\n await mapBounded([...issueIds], ACCEPTANCE_CRITERIA_READ_CONCURRENCY, async (issueId) => {\n const upperIssueId = issueId.toUpperCase();\n const existing = acceptanceCriteriaByIssue.get(upperIssueId);\n if (existing) {\n try {\n const { mtimeMs } = await stat(existing.path);\n if (existing.mtimeMs === mtimeMs) {\n cache.set(upperIssueId, existing.criteria);\n return;\n }\n } catch {\n acceptanceCriteriaByIssue.delete(upperIssueId);\n }\n }\n\n try {\n const found = await Effect.runPromise(findVBriefByIssue(root, upperIssueId));\n if (!found) {\n acceptanceCriteriaByIssue.delete(upperIssueId);\n cache.set(upperIssueId, []);\n return;\n }\n const { mtimeMs } = await stat(found.path);\n const document = await Effect.runPromise(readVBriefDocument(found.path));\n const criteria = extractACFromDocument(document).map((ac) => ({ title: ac.title, status: ac.status }));\n acceptanceCriteriaByIssue.set(upperIssueId, { path: found.path, mtimeMs, criteria });\n cache.set(upperIssueId, criteria);\n } catch {\n cache.set(upperIssueId, []);\n }\n });\n return cache;\n}\n\n/** The generation chain, newest first, enriched for the UAT batches card. */\nexport async function getUatGenerationsPayload(): Promise<UatGenerationPayload[]> {\n const status = await readCurrentFlywheelStatusForDashboard();\n if (!status) return [];\n\n const chain = listUatGenerationsSync({ projectRoot: projectRoot(), limit: CHAIN_PAYLOAD_LIMIT });\n if (chain.length === 0) return [];\n const memberIssueIds = new Set(chain.flatMap((gen) => gen.members.map((member) => member.issueId.toUpperCase())));\n const acCache = await loadAcceptanceCriteriaCache(memberIssueIds);\n const payload: UatGenerationPayload[] = [];\n for (const gen of chain) {\n const probe = await probeUatStack(gen);\n const members: UatGenerationMemberPayload[] = [];\n for (const member of gen.members) {\n members.push({\n issueId: member.issueId,\n title: member.title,\n branch: member.branch,\n ...(member.pr !== undefined ? { pr: member.pr } : {}),\n ...(member.prUrl !== undefined ? { prUrl: member.prUrl } : {}),\n mergeOrder: member.mergeOrder,\n acceptanceCriteria: acCache.get(member.issueId.toUpperCase()) ?? [],\n });\n }\n payload.push({\n name: gen.name,\n status: gen.status,\n baseSha: gen.baseSha,\n createdAt: gen.createdAt,\n updatedAt: gen.updatedAt,\n members,\n heldOut: gen.heldOut,\n resolutions: gen.resolutions,\n stack: { status: probe.status, frontendUrl: probe.frontendUrl },\n });\n }\n return payload;\n}\n\nexport interface UatCandidatePayload {\n branchName: string;\n bundled: string[];\n status: 'ready';\n}\n\n/** The authoritative active UAT candidate, if one is ready to test/ship. */\nexport async function getUatCandidatePayload(): Promise<UatCandidatePayload | null> {\n const [candidate] = listUatGenerationsSync({\n projectRoot: projectRoot(),\n statuses: ['ready'],\n limit: 1,\n });\n if (!candidate) return null;\n return {\n branchName: candidate.name,\n bundled: candidate.members.map((member) => member.issueId),\n status: 'ready',\n };\n}\n\nexport async function postUatGenerationStackPayload(name: string): Promise<\n { ok: true; frontendUrl: string; evicted: string[] } | { ok: false; error: string; status: number }\n> {\n const gen = getUatGenerationSync(name);\n if (!gen) return { ok: false, error: `No UAT generation named ${name}`, status: 404 };\n if (gen.status !== 'ready' && gen.status !== 'superseded') {\n return { ok: false, error: `${name} is ${gen.status} — only live batches can serve a stack`, status: 409 };\n }\n const result = await ensureUatStack(gen);\n if (!result.success) return { ok: false, error: result.error ?? 'stack start failed', status: 500 };\n return { ok: true, frontendUrl: result.frontendUrl!, evicted: result.evicted };\n}\n\nexport async function postUatGenerationPromotePayload(\n name: string,\n firePostMerge: (issueId: string) => boolean,\n): Promise<PromoteResult> {\n const root = projectRoot();\n const { reviewRecordEligibility } = await import('../../../lib/flywheel-merge-order.js');\n const result = await promoteUatGeneration(name, root, {\n git: buildUatPromoteGitDeps(root),\n store: { ...buildUatGenerationStore(), get: (n) => getUatGenerationSync(n) },\n teardownStack: (gen) => teardownUatStack(gen),\n firePostMerge,\n memberEligibility: reviewRecordEligibility,\n log: (msg) => console.log(msg),\n });\n await notifyFlywheelOfUatPromote(result).catch(() => {});\n return result;\n}\n"],"mappings":"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"}