@overdeck/core 0.50.3 → 0.50.4

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 (208) hide show
  1. package/dist/agents-DNMRPC8n.js.map +1 -1
  2. package/dist/{build-info-BJuEVWOh.js → build-info-ioRdOmqT.js} +3 -3
  3. package/dist/{build-info-BJuEVWOh.js.map → build-info-ioRdOmqT.js.map} +1 -1
  4. package/dist/build-info-kMMitDOP.js +2 -0
  5. package/dist/cli/index.js +4 -4
  6. package/dist/conversation-compaction-GFfO5UZL.js.map +1 -1
  7. package/dist/dashboard/{advancing-selfheal-Bq8V0QUa.js → advancing-selfheal-Dp5_Gai3.js} +3 -3
  8. package/dist/dashboard/{advancing-selfheal-Bq8V0QUa.js.map → advancing-selfheal-Dp5_Gai3.js.map} +1 -1
  9. package/dist/dashboard/{agent-gc-Coz8zQt_.js → agent-gc-JzoSs5Pi.js} +3 -3
  10. package/dist/dashboard/{agent-gc-Coz8zQt_.js.map → agent-gc-JzoSs5Pi.js.map} +1 -1
  11. package/dist/dashboard/{agent-idle-CAwB-K6M.js → agent-idle-D-Qy5BN_.js} +2 -2
  12. package/dist/dashboard/{agent-idle-CAwB-K6M.js.map → agent-idle-D-Qy5BN_.js.map} +1 -1
  13. package/dist/dashboard/{agent-state-Ci6PhjOE.js → agent-state-B196fj1h.js} +38 -16
  14. package/dist/dashboard/agent-state-B196fj1h.js.map +1 -0
  15. package/dist/dashboard/{agent-state-CbUEwUEQ.js → agent-state-IpWnw8lv.js} +1 -1
  16. package/dist/dashboard/{agent-state-service-D6IVrX3B.js → agent-state-service-CCo5-9DR.js} +3 -3
  17. package/dist/dashboard/{agent-state-service-D6IVrX3B.js.map → agent-state-service-CCo5-9DR.js.map} +1 -1
  18. package/dist/dashboard/{agent-state-service-DcN1j6eK.js → agent-state-service-D_F3VZ23.js} +1 -1
  19. package/dist/dashboard/{agents-BVvL4o_4.js → agents-CkGhnAmu.js} +1 -1
  20. package/dist/dashboard/{backlog-auto-trigger-BuSBqG2V.js → backlog-auto-trigger-B5Z2zBtC.js} +2 -2
  21. package/dist/dashboard/{backlog-auto-trigger-BuSBqG2V.js.map → backlog-auto-trigger-B5Z2zBtC.js.map} +1 -1
  22. package/dist/dashboard/build-info-BTjOB0Xr.js +2 -0
  23. package/dist/dashboard/{build-info-Cj3Y0K87.js → build-info-D0EJNj6C.js} +3 -3
  24. package/dist/dashboard/{build-info-Cj3Y0K87.js.map → build-info-D0EJNj6C.js.map} +1 -1
  25. package/dist/dashboard/cli-CIznFrcE.js +2 -0
  26. package/dist/dashboard/{cli-DIhQieif.js → cli-DCbVrSsg.js} +2 -2
  27. package/dist/dashboard/{cli-DIhQieif.js.map → cli-DCbVrSsg.js.map} +1 -1
  28. package/dist/dashboard/{close-issue-CRlmrE5g.js → close-issue-Bmu9aOIx.js} +2 -2
  29. package/dist/dashboard/{close-issue-CRlmrE5g.js.map → close-issue-Bmu9aOIx.js.map} +1 -1
  30. package/dist/dashboard/{closed-issue-reaper-DrHyYgJR.js → closed-issue-reaper-COxfTtLz.js} +5 -5
  31. package/dist/dashboard/{closed-issue-reaper-DrHyYgJR.js.map → closed-issue-reaper-COxfTtLz.js.map} +1 -1
  32. package/dist/dashboard/{closed-issue-reaper-DWlw2EtL.js → closed-issue-reaper-DwMwG_Pz.js} +1 -1
  33. package/dist/dashboard/{compaction-continuation-DhYgsXmF.js → compaction-continuation-Bqc212Zm.js} +2 -2
  34. package/dist/dashboard/{compaction-continuation-DhYgsXmF.js.map → compaction-continuation-Bqc212Zm.js.map} +1 -1
  35. package/dist/dashboard/{conflict-gate-vczgdVTa.js → conflict-gate-9mPVg7-5.js} +1 -1
  36. package/dist/dashboard/{conflict-gate-Bt-lLgYf.js → conflict-gate-CGQTcR0I.js} +3 -3
  37. package/dist/dashboard/{conflict-gate-Bt-lLgYf.js.map → conflict-gate-CGQTcR0I.js.map} +1 -1
  38. package/dist/dashboard/{conversation-compaction-CsdCbcQB.js → conversation-compaction-C7P6V8Iw.js} +5 -5
  39. package/dist/dashboard/{conversation-compaction-CsdCbcQB.js.map → conversation-compaction-C7P6V8Iw.js.map} +1 -1
  40. package/dist/dashboard/{conversation-compaction-D0zgWM1f.js → conversation-compaction-Cck5oi-V.js} +1 -1
  41. package/dist/dashboard/{conversation-service-6LaymUV-.js → conversation-service-CsOpwI9Y.js} +37 -5
  42. package/dist/dashboard/{conversation-service-6LaymUV-.js.map → conversation-service-CsOpwI9Y.js.map} +1 -1
  43. package/dist/dashboard/conversation-service-plOFkoMo.js +3 -0
  44. package/dist/dashboard/dashboard-db-worker.js +1 -1
  45. package/dist/dashboard/{deacon-CU47dU7U.js → deacon-B8oMMIgN.js} +62 -62
  46. package/dist/dashboard/{deacon-CU47dU7U.js.map → deacon-B8oMMIgN.js.map} +1 -1
  47. package/dist/dashboard/{deacon-DYsOa_ss.js → deacon-b7J57Svt.js} +2 -2
  48. package/dist/dashboard/{deacon-pending-lifecycle-ijGX8wLb.js → deacon-pending-lifecycle-3NIJ7ew5.js} +2 -2
  49. package/dist/dashboard/{deacon-pending-lifecycle-ijGX8wLb.js.map → deacon-pending-lifecycle-3NIJ7ew5.js.map} +1 -1
  50. package/dist/dashboard/deacon.js +5 -5
  51. package/dist/dashboard/{delivery-DMYxIE0J.js → delivery-lnI-gQKo.js} +1 -1
  52. package/dist/dashboard/{deploy-patrol-C4eXco1S.js → deploy-patrol-u3v2krg9.js} +2 -2
  53. package/dist/dashboard/{deploy-patrol-C4eXco1S.js.map → deploy-patrol-u3v2krg9.js.map} +1 -1
  54. package/dist/dashboard/{feedback-stuck-retirement-Dd5XNrMr.js → feedback-stuck-retirement-CoFNqjCc.js} +2 -2
  55. package/dist/dashboard/{feedback-stuck-retirement-Dd5XNrMr.js.map → feedback-stuck-retirement-CoFNqjCc.js.map} +1 -1
  56. package/dist/dashboard/{feedback-target-BzdJ2wIR.js → feedback-target-BBCh3RRq.js} +1 -1
  57. package/dist/dashboard/{feedback-target-BcsA_GRV.js → feedback-target-CeCq6EhW.js} +5 -5
  58. package/dist/dashboard/{feedback-target-BcsA_GRV.js.map → feedback-target-CeCq6EhW.js.map} +1 -1
  59. package/dist/dashboard/{flywheel-Is3w2lcT.js → flywheel-DKldI63J.js} +7 -7
  60. package/dist/dashboard/{flywheel-Is3w2lcT.js.map → flywheel-DKldI63J.js.map} +1 -1
  61. package/dist/dashboard/{flywheel-D0guNqkh.js → flywheel-DpRPLErh.js} +7 -7
  62. package/dist/dashboard/{flywheel-D0guNqkh.js.map → flywheel-DpRPLErh.js.map} +1 -1
  63. package/dist/dashboard/{flywheel-B34cdfbd.js → flywheel-jeZFYvm-.js} +1 -1
  64. package/dist/dashboard/{flywheel-merge-order-6HiPR2WG.js → flywheel-merge-order-0OnoosaO.js} +1 -1
  65. package/dist/dashboard/{flywheel-merge-order-BsNPMu0S.js → flywheel-merge-order-PG5op4j7.js} +2 -2
  66. package/dist/dashboard/{flywheel-merge-order-BsNPMu0S.js.map → flywheel-merge-order-PG5op4j7.js.map} +1 -1
  67. package/dist/dashboard/{harness-resolve-CzTFVZ5l.js → harness-resolve-C0HWj3H9.js} +2 -2
  68. package/dist/dashboard/{harness-resolve-CzTFVZ5l.js.map → harness-resolve-C0HWj3H9.js.map} +1 -1
  69. package/dist/dashboard/{identity-Dc7rQLln.js → identity-Cot1Hg4G.js} +2 -2
  70. package/dist/dashboard/{identity-Dc7rQLln.js.map → identity-Cot1Hg4G.js.map} +1 -1
  71. package/dist/dashboard/{inspect-agent-CP7Bu47O.js → inspect-agent-BDmLWbGx.js} +3 -3
  72. package/dist/dashboard/{inspect-agent-CP7Bu47O.js.map → inspect-agent-BDmLWbGx.js.map} +1 -1
  73. package/dist/dashboard/inspect-agent-BcOkA-hC.js +2 -0
  74. package/dist/dashboard/{issue-lifecycle-BPbczKyy.js → issue-lifecycle-1B_v8Rf5.js} +6 -6
  75. package/dist/dashboard/{issue-lifecycle-BPbczKyy.js.map → issue-lifecycle-1B_v8Rf5.js.map} +1 -1
  76. package/dist/dashboard/{issue-lifecycle-FaugTi39.js → issue-lifecycle-CIrfbIbj.js} +1 -1
  77. package/dist/dashboard/{issue-service-singleton-Dsad9NTt.js → issue-service-singleton-BVwPzjwF.js} +1 -1
  78. package/dist/dashboard/{issue-service-singleton-BrZQJLRI.js → issue-service-singleton-KcRrOiFD.js} +2 -2
  79. package/dist/dashboard/{issue-service-singleton-BrZQJLRI.js.map → issue-service-singleton-KcRrOiFD.js.map} +1 -1
  80. package/dist/dashboard/{jsonl-resolver-DQEe0DHM.js → jsonl-resolver-jxbif7MD.js} +3 -3
  81. package/dist/dashboard/{jsonl-resolver-DQEe0DHM.js.map → jsonl-resolver-jxbif7MD.js.map} +1 -1
  82. package/dist/dashboard/{kimi-code-Cx5YUJpS.js → kimi-code-BZh3NgcN.js} +1 -1
  83. package/dist/dashboard/{lifecycle-DZFHmpGI.js → lifecycle-CvcZ4_fA.js} +4 -4
  84. package/dist/dashboard/{lifecycle-DZFHmpGI.js.map → lifecycle-CvcZ4_fA.js.map} +1 -1
  85. package/dist/dashboard/{linear-mcp-auth-DylPJABZ.js → linear-mcp-auth-CZoj_-d7.js} +1 -1
  86. package/dist/dashboard/{linear-mcp-auth-BFlpMvWh.js → linear-mcp-auth-Dx74yCAY.js} +2 -2
  87. package/dist/dashboard/{linear-mcp-auth-BFlpMvWh.js.map → linear-mcp-auth-Dx74yCAY.js.map} +1 -1
  88. package/dist/dashboard/{memory-governor-B7uDWRo9.js → memory-governor-Bv3LAqk3.js} +5 -5
  89. package/dist/dashboard/{memory-governor-B7uDWRo9.js.map → memory-governor-Bv3LAqk3.js.map} +1 -1
  90. package/dist/dashboard/{merge-agent-ddEcZDZD.js → merge-agent-1zVLl6k_.js} +1 -1
  91. package/dist/dashboard/{merge-agent-ttRY053V.js → merge-agent-8zrQu48t.js} +8 -8
  92. package/dist/dashboard/{merge-agent-ttRY053V.js.map → merge-agent-8zrQu48t.js.map} +1 -1
  93. package/dist/dashboard/merge-ops-CFJaMPLu.js +2 -0
  94. package/dist/dashboard/{merge-train-f_un38UJ.js → merge-train-BYH_cF7G.js} +2 -2
  95. package/dist/dashboard/{merge-train-f_un38UJ.js.map → merge-train-BYH_cF7G.js.map} +1 -1
  96. package/dist/dashboard/{merge-train-deps-GPlHOjK6.js → merge-train-deps-BTfXM5AR.js} +2 -2
  97. package/dist/dashboard/{merge-train-deps-GPlHOjK6.js.map → merge-train-deps-BTfXM5AR.js.map} +1 -1
  98. package/dist/dashboard/{messaging-BasbftDu.js → messaging-Bk6vGsMw.js} +1 -1
  99. package/dist/dashboard/{orphan-proposed-reconciler-DFHdQSEg.js → orphan-proposed-reconciler-B898fk26.js} +6 -6
  100. package/dist/dashboard/{orphan-proposed-reconciler-DFHdQSEg.js.map → orphan-proposed-reconciler-B898fk26.js.map} +1 -1
  101. package/dist/dashboard/{orphan-proposed-reconciler-DzAxrM99.js → orphan-proposed-reconciler-Bx_aWS4B.js} +1 -1
  102. package/dist/dashboard/{parked-residue-DMtbxJml.js → parked-residue-q2kzrYSm.js} +9 -9
  103. package/dist/dashboard/{parked-residue-DMtbxJml.js.map → parked-residue-q2kzrYSm.js.map} +1 -1
  104. package/dist/dashboard/{post-merge-lifecycle-worker-jzN2g_EY.js → post-merge-lifecycle-worker-Cl7-uf4g.js} +2 -2
  105. package/dist/dashboard/{post-merge-lifecycle-worker-jzN2g_EY.js.map → post-merge-lifecycle-worker-Cl7-uf4g.js.map} +1 -1
  106. package/dist/dashboard/{project-resource-refresh-queue-cqOeJ0fB.js → project-resource-refresh-queue-O_dRfPHn.js} +1 -1
  107. package/dist/dashboard/{project-resource-refresh-queue-DebwjPPA.js → project-resource-refresh-queue-se3T9IYP.js} +4 -4
  108. package/dist/dashboard/{project-resource-refresh-queue-DebwjPPA.js.map → project-resource-refresh-queue-se3T9IYP.js.map} +1 -1
  109. package/dist/dashboard/{reconstruct-cache-Bb2Jg2DZ.js → reconstruct-cache-XpWC9L0F.js} +2 -2
  110. package/dist/dashboard/{reconstruct-cache-Bb2Jg2DZ.js.map → reconstruct-cache-XpWC9L0F.js.map} +1 -1
  111. package/dist/dashboard/{release-engine-78iCajIc.js → release-engine-BuzxOamf.js} +2 -2
  112. package/dist/dashboard/{release-engine-78iCajIc.js.map → release-engine-BuzxOamf.js.map} +1 -1
  113. package/dist/dashboard/{remote-completion-D5wzZCte.js → remote-completion-BCZgeKPS.js} +3 -3
  114. package/dist/dashboard/{remote-completion-D5wzZCte.js.map → remote-completion-BCZgeKPS.js.map} +1 -1
  115. package/dist/dashboard/{resolver-BGZh3lXA.js → resolver-BeNrqNou.js} +5 -5
  116. package/dist/dashboard/{resolver-BGZh3lXA.js.map → resolver-BeNrqNou.js.map} +1 -1
  117. package/dist/dashboard/{resource-pressure-patrol-Bg23rPV-.js → resource-pressure-patrol-Ccg_aCgO.js} +3 -3
  118. package/dist/dashboard/{resource-pressure-patrol-Bg23rPV-.js.map → resource-pressure-patrol-Ccg_aCgO.js.map} +1 -1
  119. package/dist/dashboard/{resume-BKyB4hA1.js → resume-KGCrn0DV.js} +1 -1
  120. package/dist/dashboard/{review-agent-ZdP-HmwY.js → review-agent-BmM6YMZW.js} +1 -1
  121. package/dist/dashboard/{review-agent-DHs-JngN.js → review-agent-DRWVU8fP.js} +13 -13
  122. package/dist/dashboard/{review-agent-DHs-JngN.js.map → review-agent-DRWVU8fP.js.map} +1 -1
  123. package/dist/dashboard/{review-convoy-liveness-BTMvU4CA.js → review-convoy-liveness-EvCDv8Q_.js} +3 -3
  124. package/dist/dashboard/{review-convoy-liveness-BTMvU4CA.js.map → review-convoy-liveness-EvCDv8Q_.js.map} +1 -1
  125. package/dist/dashboard/{review-run-recovery-BYalQzbh.js → review-run-recovery-Ctm48Snt.js} +1 -1
  126. package/dist/dashboard/{review-run-recovery-BvVG3oUr.js → review-run-recovery-DNDI58cM.js} +2 -2
  127. package/dist/dashboard/{review-run-recovery-BvVG3oUr.js.map → review-run-recovery-DNDI58cM.js.map} +1 -1
  128. package/dist/dashboard/{review-status-DXTFzCmR.js → review-status-FnlvQuPI.js} +1 -1
  129. package/dist/dashboard/{review-status-emit-C9YW6SQL.js → review-status-emit-CeMS31DX.js} +2 -2
  130. package/dist/dashboard/{review-status-emit-C9YW6SQL.js.map → review-status-emit-CeMS31DX.js.map} +1 -1
  131. package/dist/dashboard/{review-status-BegAr86G.js → review-status-yY5qt7Rl.js} +5 -5
  132. package/dist/dashboard/{review-status-BegAr86G.js.map → review-status-yY5qt7Rl.js.map} +1 -1
  133. package/dist/dashboard/{review-verdict-feedback-qlVBi19-.js → review-verdict-feedback-BNgRj1e3.js} +4 -4
  134. package/dist/dashboard/{review-verdict-feedback-qlVBi19-.js.map → review-verdict-feedback-BNgRj1e3.js.map} +1 -1
  135. package/dist/dashboard/{review-verdict-feedback-Dp139Lmv.js → review-verdict-feedback-D39cuSV1.js} +1 -1
  136. package/dist/dashboard/{review-verdict-writer-CJr_Ob0Y.js → review-verdict-writer-DvFfKAn-.js} +2 -2
  137. package/dist/dashboard/{review-verdict-writer-CJr_Ob0Y.js.map → review-verdict-writer-DvFfKAn-.js.map} +1 -1
  138. package/dist/dashboard/{review-verdict-writer-BVco3WoI.js → review-verdict-writer-PcGBkvJ4.js} +1 -1
  139. package/dist/dashboard/{sequence-io-iljWlslY.js → sequence-io-DZyschsf.js} +2 -2
  140. package/dist/dashboard/{sequence-io-iljWlslY.js.map → sequence-io-DZyschsf.js.map} +1 -1
  141. package/dist/dashboard/{sequencer-agent-BGGVKxtw.js → sequencer-agent-BXDw5cNx.js} +5 -5
  142. package/dist/dashboard/{sequencer-agent-BGGVKxtw.js.map → sequencer-agent-BXDw5cNx.js.map} +1 -1
  143. package/dist/dashboard/server.js +79 -72
  144. package/dist/dashboard/server.js.map +1 -1
  145. package/dist/dashboard/{service-kzhN0DLg.js → service-DP50Omvn.js} +33 -33
  146. package/dist/dashboard/{service-kzhN0DLg.js.map → service-DP50Omvn.js.map} +1 -1
  147. package/dist/dashboard/{smart-compaction-owKHJDbL.js → smart-compaction-944GL8PN.js} +1 -1
  148. package/dist/dashboard/{smart-compaction-C96F2aFQ.js → smart-compaction-BzDfF6yb.js} +2 -2
  149. package/dist/dashboard/{smart-compaction-C96F2aFQ.js.map → smart-compaction-BzDfF6yb.js.map} +1 -1
  150. package/dist/dashboard/{spawn-planning-session-CiJdOHTD.js → spawn-planning-session-BcVnHJjm.js} +1 -1
  151. package/dist/dashboard/{spawn-planning-session-FmJa6PCS.js → spawn-planning-session-CH8IYli7.js} +3 -3
  152. package/dist/dashboard/{spawn-planning-session-FmJa6PCS.js.map → spawn-planning-session-CH8IYli7.js.map} +1 -1
  153. package/dist/dashboard/{specialist-context-_ootmzn2.js → specialist-context-DhDTXVht.js} +3 -3
  154. package/dist/dashboard/{specialist-context-_ootmzn2.js.map → specialist-context-DhDTXVht.js.map} +1 -1
  155. package/dist/dashboard/{specialist-logs-BbAX221-.js → specialist-logs-C1bcnZ-G.js} +2 -2
  156. package/dist/dashboard/{specialist-logs-BbAX221-.js.map → specialist-logs-C1bcnZ-G.js.map} +1 -1
  157. package/dist/dashboard/{specialists-COZoYaiz.js → specialists-BKmReEWT.js} +53 -53
  158. package/dist/dashboard/{specialists-COZoYaiz.js.map → specialists-BKmReEWT.js.map} +1 -1
  159. package/dist/dashboard/{specialists-RPLJnYc1.js → specialists-Bvrvt0br.js} +12 -12
  160. package/dist/dashboard/{specialists-RPLJnYc1.js.map → specialists-Bvrvt0br.js.map} +1 -1
  161. package/dist/dashboard/{specialists-DTAOVZpU.js → specialists-CD6GEKXk.js} +1 -1
  162. package/dist/dashboard/{specialists-D8sYZnPb.js → specialists-eFWpJRBc.js} +1 -1
  163. package/dist/dashboard/{stall-sweeper-9bRBZhrQ.js → stall-sweeper-D3fZvg7O.js} +3 -3
  164. package/dist/dashboard/{stall-sweeper-9bRBZhrQ.js.map → stall-sweeper-D3fZvg7O.js.map} +1 -1
  165. package/dist/dashboard/{state-migrate-J3tFJnbd.js → state-migrate-KZ2goscQ.js} +2 -2
  166. package/dist/dashboard/{state-migrate-J3tFJnbd.js.map → state-migrate-KZ2goscQ.js.map} +1 -1
  167. package/dist/dashboard/{stuck-remediation-BvTK_NYN.js → stuck-remediation-Cb2V1roj.js} +1 -1
  168. package/dist/dashboard/{stuck-remediation-4Rf_xMEb.js → stuck-remediation-CnoV0kcv.js} +5 -5
  169. package/dist/dashboard/{stuck-remediation-4Rf_xMEb.js.map → stuck-remediation-CnoV0kcv.js.map} +1 -1
  170. package/dist/dashboard/{summary-fork-PLcckTRE.js → summary-fork-CxuNBM0F.js} +2 -2
  171. package/dist/dashboard/{summary-fork-D-cylknJ.js → summary-fork-lC2vMXly.js} +4 -4
  172. package/dist/dashboard/{summary-fork-D-cylknJ.js.map → summary-fork-lC2vMXly.js.map} +1 -1
  173. package/dist/dashboard/{teardown-workspace-CTL16rnG.js → teardown-workspace-C-9murBw.js} +4 -4
  174. package/dist/dashboard/{teardown-workspace-CTL16rnG.js.map → teardown-workspace-C-9murBw.js.map} +1 -1
  175. package/dist/dashboard/{test-agent-queue-CMgYZiR_.js → test-agent-queue-YCBRzaAB.js} +3 -3
  176. package/dist/dashboard/{test-agent-queue-CMgYZiR_.js.map → test-agent-queue-YCBRzaAB.js.map} +1 -1
  177. package/dist/dashboard/{uat-failure-feedback-DS6W9X3I.js → uat-failure-feedback-B-1mL8Zp.js} +3 -3
  178. package/dist/dashboard/{uat-failure-feedback-DS6W9X3I.js.map → uat-failure-feedback-B-1mL8Zp.js.map} +1 -1
  179. package/dist/dashboard/{uat-promote-verification-CRsIHE_q.js → uat-promote-verification-BEBPcpPg.js} +2 -2
  180. package/dist/dashboard/{uat-promote-verification-CRsIHE_q.js.map → uat-promote-verification-BEBPcpPg.js.map} +1 -1
  181. package/dist/dashboard/{uat-train-BoA9r6_I.js → uat-train-BFo3OQUB.js} +1 -1
  182. package/dist/dashboard/{uat-train-Cd8CJeNe.js → uat-train-COgoJomz.js} +8 -8
  183. package/dist/dashboard/{uat-train-Cd8CJeNe.js.map → uat-train-COgoJomz.js.map} +1 -1
  184. package/dist/dashboard/{validate-BdmvTIym.js → validate-069_N1e2.js} +2 -2
  185. package/dist/dashboard/{validate-BdmvTIym.js.map → validate-069_N1e2.js.map} +1 -1
  186. package/dist/dashboard/{verification-runner-BO0K3xNA.js → verification-runner-CQnZyC5A.js} +1 -1
  187. package/dist/dashboard/{verification-runner-fEY1Qj-l.js → verification-runner-fy3wNrWn.js} +4 -4
  188. package/dist/dashboard/{verification-runner-fEY1Qj-l.js.map → verification-runner-fy3wNrWn.js.map} +1 -1
  189. package/dist/dashboard/workflows-Bk6Kt0ZQ.js +2 -0
  190. package/dist/dashboard/{workflows-CxVnygYU.js → workflows-N4QMkDzN.js} +10 -10
  191. package/dist/dashboard/{workflows-CxVnygYU.js.map → workflows-N4QMkDzN.js.map} +1 -1
  192. package/dist/dashboard/{workspaces-Cpz0xJWQ.js → workspaces-DmoyfHoU.js} +33 -33
  193. package/dist/dashboard/{workspaces-Cpz0xJWQ.js.map → workspaces-DmoyfHoU.js.map} +1 -1
  194. package/dist/{lifecycle-C6k0HWEO.js → lifecycle-DCFcjg7y.js} +1 -1
  195. package/dist/{lifecycle-D5oRpvvB.js → lifecycle-GkaHQJNK.js} +2 -2
  196. package/dist/{lifecycle-D5oRpvvB.js.map → lifecycle-GkaHQJNK.js.map} +1 -1
  197. package/dist/{merge-agent-Drufc7tU.js → merge-agent-CogNFasY.js} +2 -2
  198. package/dist/{merge-agent-Drufc7tU.js.map → merge-agent-CogNFasY.js.map} +1 -1
  199. package/dist/{merge-agent-CBOVY_eC.js → merge-agent-eteKQ9Rb.js} +1 -1
  200. package/package.json +1 -1
  201. package/dist/build-info-BgrwLUy6.js +0 -2
  202. package/dist/dashboard/agent-state-Ci6PhjOE.js.map +0 -1
  203. package/dist/dashboard/build-info-CYlxvZSQ.js +0 -2
  204. package/dist/dashboard/cli-ZAS502uC.js +0 -2
  205. package/dist/dashboard/conversation-service-Dlks_hSS.js +0 -3
  206. package/dist/dashboard/inspect-agent-DTuSg95i.js +0 -2
  207. package/dist/dashboard/merge-ops-Bgc-EScO.js +0 -2
  208. package/dist/dashboard/workflows-CgGNyl1z.js +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"teardown-workspace-CTL16rnG.js","names":[],"sources":["../../src/lib/lifecycle/teardown-workspace.ts"],"sourcesContent":["/**\n * teardown-workspace — Full workspace cleanup.\n *\n * Consolidates workspace teardown from close-out.ts and workspace-manager.ts.\n * Handles: tmux sessions, TLDR daemon, Docker containers, git worktrees,\n * agent state directories, and (optionally) git branches.\n *\n * The workspace-manager's removeWorkspace() handles additional project-specific\n * cleanup (DNS, tunnels, Hume, ports) that this module does not cover.\n * In Phase 2, removeWorkspace() will delegate to this module for the common steps.\n */\n\nimport { existsSync } from 'fs';\nimport { readFile, rm } from 'fs/promises';\nimport { join, basename, dirname } from 'path';\nimport { homedir } from 'os';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport { Effect } from 'effect';\nimport { AGENTS_DIR } from '../paths.js';\nimport { pidsWithCwdUnder } from '../process-cwd.js';\nimport { killSession, sessionExists, listSessionNames } from '../tmux.js';\nimport type { LifecycleContext, StepResult, TeardownOptions } from './types.js';\nimport { stepOk, stepSkipped, stepFailed } from './types.js';\nimport { findAllWorkspacePaths, findWorkspacePath } from './archive-planning.js';\nimport { getContainersReferencingWorkspacePath } from '../workspace-manager.js';\nimport { DEVCONTAINER_DIRNAME } from '../workspace/devcontainer-renderer.js';\nimport { removeAgentStateDir } from '../agents/state-dir-removal.js';\n\nconst execAsync = promisify(exec);\n\n/**\n * Kill tmux sessions associated with an issue.\n */\nfunction killTmuxSessions(issueLower: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => killTmuxSessionsImpl(issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:tmux-sessions', `Failed: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function killTmuxSessionsImpl(issueLower: string): Promise<StepResult> {\n const step = 'teardown:tmux-sessions';\n let killed = 0;\n\n // Exact-match sessions (agent, test, merge, planning, strike).\n const exactPatterns = [\n `agent-${issueLower}`,\n `test-${issueLower}`,\n `merge-${issueLower}`,\n `planning-${issueLower}`,\n `strike-${issueLower}`,\n ];\n for (const session of exactPatterns) {\n if (await Effect.runPromise(sessionExists(session))) {\n try {\n await Effect.runPromise(killSession(session));\n killed++;\n } catch {\n // session may have died between check and kill\n }\n }\n }\n\n // Pattern-match sessions for review coordinators, review specialists, and\n // canonical specialists. Today's naming uses the UPPER issue ID (PAN-1024)\n // not the lower form, and includes session families that the prior\n // single-regex didn't cover. PAN-1024 close-out left\n // `review-coordinator-PAN-1024-...` and\n // `specialist-overdeck-PAN-1024-test-agent` alive (2026-05-09).\n //\n // Patterns we now match (case-insensitive on the issue ID):\n // - review-coordinator-<ISSUE>-<timestamp>\n // - review-<ISSUE>-<timestamp>-<role> (legacy)\n // - specialist-<projectKey>-<ISSUE>-<role> (canonical PAN-830/915)\n try {\n const allSessions = await Effect.runPromise(listSessionNames());\n const escapedLower = issueLower.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const escapedUpper = issueLower.toUpperCase().replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const issuePart = `(${escapedLower}|${escapedUpper})`;\n const patterns: RegExp[] = [\n new RegExp(`^review-coordinator-${issuePart}-\\\\d+`),\n new RegExp(`^review-${issuePart}-\\\\d+`),\n new RegExp(`^specialist-[^-]+(?:-[^-]+)*?-${issuePart}-`),\n ];\n const matchedSessions = allSessions.filter(s => patterns.some(p => p.test(s)));\n for (const session of matchedSessions) {\n try {\n await Effect.runPromise(killSession(session));\n killed++;\n } catch {\n // session may have died between check and kill\n }\n }\n } catch {\n // Session listing may fail if tmux server is not running\n }\n\n // NOTE: Per-project ephemeral specialists (specialist-{project}-{type}) are NOT killed here.\n // They belong to the project, not the issue, and accumulate context across issues via --resume.\n // Their grace period / idle timeout handles cleanup when no new work arrives.\n\n if (killed > 0) {\n return stepOk(step, [`Killed ${killed} tmux session(s)`]);\n }\n return stepSkipped(step, ['No tmux sessions found']);\n}\n\n/**\n * Stop TLDR daemon if workspace has a .venv.\n */\nfunction stopTldrDaemon(workspacePath: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => stopTldrDaemonImpl(workspacePath),\n catch: (err) => err,\n }).pipe(\n Effect.catch(() =>\n Effect.succeed(stepSkipped('teardown:tldr-daemon', ['TLDR daemon not running or failed to stop (non-fatal)'])),\n ),\n );\n}\n\nasync function stopTldrDaemonImpl(workspacePath: string): Promise<StepResult> {\n const step = 'teardown:tldr-daemon';\n const venvPath = join(workspacePath, '.venv');\n if (!existsSync(venvPath)) {\n return stepSkipped(step, ['No .venv found']);\n }\n try {\n const { getTldrDaemonServiceSync } = await import('../tldr-daemon.js');\n const tldrService = getTldrDaemonServiceSync(workspacePath, venvPath);\n await tldrService.stop();\n return stepOk(step, ['Stopped TLDR daemon']);\n } catch {\n return stepSkipped(step, ['TLDR daemon not running or failed to stop (non-fatal)']);\n }\n}\n\n/**\n * Stop Docker containers for the workspace.\n */\nfunction stopDocker(\n workspacePath: string,\n issueLower: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => stopDockerImpl(workspacePath, issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch(() =>\n Effect.succeed(stepSkipped('teardown:docker', ['Docker cleanup skipped (not running or failed)'])),\n ),\n );\n}\n\nasync function stopDockerImpl(\n workspacePath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:docker';\n try {\n const { stopWorkspaceDocker } = await import('../workspace-manager.js');\n await Effect.runPromise(stopWorkspaceDocker(workspacePath, issueLower));\n return stepOk(step, ['Stopped Docker containers']);\n } catch {\n return stepSkipped(step, ['Docker cleanup skipped (not running or failed)']);\n }\n}\n\n/**\n * Detect whether a PID belongs to a process running inside a Docker container.\n * Container processes appear in host lsof output when paths are bind-mounted.\n */\nasync function isDockerContainerProcess(pid: string): Promise<boolean> {\n try {\n const cgroup = await readFile(`/proc/${pid}/cgroup`, 'utf-8');\n return cgroup.includes('/docker-') || cgroup.includes('/docker/');\n } catch {\n return false;\n }\n}\n\n/**\n * Kill orphaned host processes for a workspace.\n */\nfunction killOrphanedProcesses(workspacePath: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => killOrphanedProcessesImpl(workspacePath),\n catch: (err) => err,\n }).pipe(\n Effect.catch(() =>\n Effect.succeed(stepSkipped('teardown:orphaned-processes', ['Orphaned process cleanup failed (non-fatal)'])),\n ),\n );\n}\n\nasync function killOrphanedProcessesImpl(workspacePath: string): Promise<StepResult> {\n const step = 'teardown:orphaned-processes';\n try {\n // Find PIDs with cwd inside the workspace. NEVER select by open files\n // (`lsof +D`): Bun's hardlinked node_modules make the dashboard and every\n // conversation's PTY supervisor appear to hold files \"in\" any workspace\n // via mmap'd native addons — see src/lib/process-cwd.ts.\n const pids = await pidsWithCwdUnder(workspacePath);\n\n if (pids.length === 0) {\n return stepSkipped(step, ['No orphaned processes found']);\n }\n\n // Don't kill our own process or the dashboard\n const myPid = String(process.pid);\n const safePids = pids.filter(p => p !== myPid);\n\n if (safePids.length === 0) {\n return stepSkipped(step, ['No orphaned processes to kill']);\n }\n\n // Filter out Docker container processes — they appear in host lsof due to bind mounts\n const hostPids: string[] = [];\n for (const pid of safePids) {\n if (!(await isDockerContainerProcess(pid))) {\n hostPids.push(pid);\n }\n }\n\n if (hostPids.length === 0) {\n return stepSkipped(step, ['No orphaned host processes to kill (all were container processes)']);\n }\n\n await execAsync(`kill ${hostPids.join(' ')} 2>/dev/null || true`, { encoding: 'utf-8', timeout: 5000 });\n return stepOk(step, [`Killed ${hostPids.length} orphaned process(es)`]);\n } catch {\n return stepSkipped(step, ['Orphaned process cleanup failed (non-fatal)']);\n }\n}\n\n/**\n * Remove git worktree for the workspace.\n */\nfunction removeWorktree(\n projectPath: string,\n workspacePath: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => removeWorktreeImpl(projectPath, workspacePath),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:worktree', `Failed to remove workspace: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function removeWorktreeImpl(\n projectPath: string,\n workspacePath: string,\n): Promise<StepResult> {\n const step = 'teardown:worktree';\n if (!existsSync(workspacePath)) {\n return stepSkipped(step, ['Workspace directory does not exist']);\n }\n\n // Guard: never delete workspace (and its `.devcontainer/`) while containers\n // still reference compose paths inside it.\n const orphanedContainers = await Effect.runPromise(getContainersReferencingWorkspacePath(workspacePath));\n if (orphanedContainers.length > 0) {\n return stepFailed(\n step,\n `Cannot remove workspace: ${orphanedContainers.length} Docker container(s) still reference compose paths in ${DEVCONTAINER_DIRNAME}/. ` +\n `Run workspace Docker cleanup first or stop the containers manually.`,\n );\n }\n\n try {\n await execAsync(`git worktree remove \"${workspacePath}\" --force`, { cwd: projectPath });\n return stepOk(step, ['Removed git worktree']);\n } catch {\n // worktree remove failed — try direct removal\n try {\n await rm(workspacePath, { recursive: true, force: true });\n return stepOk(step, ['Removed workspace directory after worktree removal failed']);\n } catch (err) {\n return stepFailed(step, `Failed to remove workspace: ${(err as Error).message}`);\n }\n }\n}\n\n/**\n * Remove every agent state directory tied to an issue.\n */\nfunction removeAgentState(issueLower: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => removeAgentStateImpl(issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:agent-state', `Failed: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function removeAgentStateImpl(issueLower: string): Promise<StepResult> {\n const step = 'teardown:agent-state';\n const { readdir } = await import('fs/promises');\n\n let entries: string[];\n try {\n entries = await readdir(AGENTS_DIR);\n } catch {\n return stepSkipped(step, ['Agents directory not present']);\n }\n\n const work = `agent-${issueLower}`;\n const planner = `planning-${issueLower}`;\n const strike = `strike-${issueLower}`;\n const specialistPrefix = `agent-${issueLower}-`;\n const targets = entries.filter(name =>\n name === work || name === planner || name === strike || name.startsWith(specialistPrefix),\n );\n\n let removed = 0;\n let preservedTranscripts = 0;\n for (const name of targets) {\n try {\n const result = await removeAgentStateDir(join(AGENTS_DIR, name));\n if (result.removedDir || result.preservedTranscripts > 0) removed++;\n preservedTranscripts += result.preservedTranscripts;\n } catch { /* non-fatal */ }\n }\n\n if (removed > 0) {\n return stepOk(step, [\n `Cleaned ${removed} agent state director${removed === 1 ? 'y' : 'ies'} (${preservedTranscripts} transcript files preserved)`,\n ]);\n }\n return stepSkipped(step, ['No agent state directories found']);\n}\n\n/**\n * Delete feature branches (local + remote).\n */\nfunction deleteBranches(\n projectPath: string,\n issueLower: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => deleteBranchesImpl(projectPath, issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:branches', `Failed: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function deleteBranchesImpl(\n projectPath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:branches';\n const branchName = `feature/${issueLower}`;\n const details: string[] = [];\n const localSlots = await execAsync(`git for-each-ref --format=\"%(refname:short)\" \"refs/heads/feature/${issueLower}-slot-*\"`, { cwd: projectPath, encoding: 'utf-8' })\n .then(({ stdout }) => stdout.trim().split('\\n').filter(Boolean)).catch(() => [] as string[]);\n const remoteSlots = await execAsync(`git ls-remote --heads origin \"feature/${issueLower}-slot-*\"`, { cwd: projectPath, encoding: 'utf-8' })\n .then(({ stdout }) => stdout.trim().split('\\n').map(line => line.split('refs/heads/')[1]).filter((name): name is string => Boolean(name))).catch(() => [] as string[]);\n\n // Delete local branch\n try {\n await execAsync(`git branch -D \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted local branch ${branchName}`);\n } catch {\n details.push(`Local branch ${branchName} not found (already deleted)`);\n }\n\n for (const slotBranch of localSlots) {\n try {\n await execAsync(`git branch -D ${JSON.stringify(slotBranch)}`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted local branch ${slotBranch}`);\n } catch { details.push(`Failed to delete local branch ${slotBranch}`); }\n }\n\n // Delete remote branch\n try {\n await execAsync(`git push origin --delete \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted remote branch ${branchName}`);\n } catch {\n details.push(`Remote branch ${branchName} not found (already deleted)`);\n }\n\n for (const slotBranch of remoteSlots) {\n try {\n await execAsync(`git push origin --delete ${JSON.stringify(slotBranch)}`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted remote branch ${slotBranch}`);\n } catch { details.push(`Failed to delete remote branch ${slotBranch}`); }\n }\n\n return stepOk(step, details);\n}\n\n/**\n * Whether a local branch exists and is fully merged into main.\n */\nasync function localBranchMergedState(\n projectPath: string,\n branchName: string,\n): Promise<{ exists: boolean; merged: boolean }> {\n try {\n await execAsync(`git show-ref --verify --quiet \"refs/heads/${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n } catch {\n return { exists: false, merged: false };\n }\n try {\n await execAsync(`git merge-base --is-ancestor \"${branchName}\" main`, { cwd: projectPath, encoding: 'utf-8' });\n return { exists: true, merged: true };\n } catch {\n return { exists: true, merged: false };\n }\n}\n\n/**\n * Delete the strike branch (local + remote) once it is merged into main.\n *\n * Strike work (`pan strike`) lands on main directly, so a merged\n * `strike/<issue>` branch is pure residue — it is deleted on every teardown,\n * not just when `deleteBranches` is requested (PAN-1721). An UNMERGED strike\n * branch may hold the only copy of an aborted strike's work, so it is\n * preserved and reported instead.\n */\nfunction deleteStrikeBranch(\n projectPath: string,\n issueLower: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => deleteStrikeBranchImpl(projectPath, issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:strike-branch', `Failed: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function deleteStrikeBranchImpl(\n projectPath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:strike-branch';\n const branchName = `strike/${issueLower}`;\n const details: string[] = [];\n\n // Local branch\n const local = await localBranchMergedState(projectPath, branchName);\n if (local.exists) {\n if (local.merged) {\n try {\n await execAsync(`git branch -D \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted local branch ${branchName}`);\n } catch (err) {\n details.push(`Failed to delete local branch ${branchName}: ${(err as Error).message}`);\n }\n } else {\n details.push(`Preserved local branch ${branchName} — not merged to main`);\n }\n }\n\n // Remote branch\n try {\n const { stdout: remoteRef } = await execAsync(\n `git ls-remote --heads origin \"${branchName}\" 2>/dev/null || true`,\n { cwd: projectPath, encoding: 'utf-8' },\n );\n if (remoteRef.trim()) {\n let remoteMerged = false;\n try {\n await execAsync(`git fetch origin \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n await execAsync('git merge-base --is-ancestor FETCH_HEAD main', { cwd: projectPath, encoding: 'utf-8' });\n remoteMerged = true;\n } catch {\n remoteMerged = false;\n }\n if (remoteMerged) {\n await execAsync(`git push origin --delete \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted remote branch ${branchName}`);\n } else {\n details.push(`Preserved remote branch ${branchName} — not merged to main`);\n }\n }\n } catch {\n // remote check/delete failed — non-fatal\n }\n\n if (details.length === 0) {\n return stepSkipped(step, [`No strike branch ${branchName} found`]);\n }\n return details.some(d => d.startsWith('Failed'))\n ? stepFailed(step, details.join('; '))\n : stepOk(step, details);\n}\n\n/**\n * Remove leftover strike worktrees (`workspaces/feature-<issue>-strike`).\n *\n * Runs when the regular worktree removal is gated off (remove_workspace=false\n * at close-out): a strike worktree has no post-close purpose once the strike\n * branch is merged or gone, and leaving it keeps the closed issue alive in\n * resource discovery (PAN-1721). Worktrees whose strike branch has unmerged\n * commits are preserved alongside the branch.\n */\nfunction removeStrikeWorktrees(\n projectPath: string,\n issueLower: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => removeStrikeWorktreesImpl(projectPath, issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:strike-worktree', `Failed: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function removeStrikeWorktreesImpl(\n projectPath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:strike-worktree';\n const strikePaths = findAllWorkspacePaths(projectPath, issueLower).filter(p => p.endsWith('-strike'));\n if (strikePaths.length === 0) {\n return stepSkipped(step, ['No strike worktree found']);\n }\n\n const branch = await localBranchMergedState(projectPath, `strike/${issueLower}`);\n if (branch.exists && !branch.merged) {\n return stepSkipped(step, [`Preserved strike worktree — strike/${issueLower} not merged to main`]);\n }\n\n const details: string[] = [];\n for (const p of strikePaths) {\n const result = await removeWorktreeImpl(projectPath, p);\n details.push(...(result.details ?? [result.error ?? result.step]));\n if (!result.success && !result.skipped) {\n return stepFailed(step, `Failed to remove strike worktree ${p}: ${result.error ?? 'unknown error'}`);\n }\n }\n return stepOk(step, details);\n}\n\n/**\n * Clear shadow state for an issue.\n */\nfunction clearShadowState(issueId: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => clearShadowStateImpl(issueId),\n catch: (err) => err,\n }).pipe(\n Effect.catch(() =>\n Effect.succeed(stepSkipped('teardown:shadow-state', ['Shadow state cleanup skipped (non-fatal)'])),\n ),\n );\n}\n\nasync function clearShadowStateImpl(issueId: string): Promise<StepResult> {\n const step = 'teardown:shadow-state';\n try {\n const { removeShadowState } = await import('../shadow-state.js');\n const result = removeShadowState(issueId);\n if (result.success) {\n return stepOk(step, [`Cleared shadow state for ${issueId}`]);\n }\n return stepSkipped(step, ['No shadow state found']);\n } catch {\n return stepSkipped(step, ['Shadow state cleanup skipped (non-fatal)']);\n }\n}\n\n/**\n * Remove legacy .planning/<issue>/ directory from project root.\n */\nfunction clearLegacyPlanningDir(\n projectPath: string,\n issueLower: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: async () => {\n const step = 'teardown:legacy-planning-dir';\n const legacyDir = join(projectPath, '.planning', issueLower);\n if (!existsSync(legacyDir)) {\n return stepSkipped(step, ['No legacy planning directory found']);\n }\n await rm(legacyDir, { recursive: true, force: true });\n return stepOk(step, [`Deleted legacy planning dir: ${legacyDir}`]);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:legacy-planning-dir', `Failed to delete legacy planning dir: ${(err as Error).message}`)),\n ),\n );\n}\n\n/**\n * Build template placeholders for project-specific cleanup (tunnel, Hume).\n */\nfunction buildPlaceholders(\n ctx: LifecycleContext,\n opts: TeardownOptions,\n workspacePath: string,\n) {\n const issueLower = ctx.issueId.toLowerCase();\n const featureFolder = `feature-${issueLower}`;\n const projName = opts.projectName || ctx.projectName || basename(ctx.projectPath);\n const domain = opts.workspaceConfig?.dns?.domain || 'localhost';\n return {\n FEATURE_NAME: issueLower,\n FEATURE_FOLDER: featureFolder,\n BRANCH_NAME: `feature/${issueLower}`,\n COMPOSE_PROJECT: `${projName}-${featureFolder}`,\n DOMAIN: domain,\n PROJECT_NAME: projName,\n PROJECT_PATH: ctx.projectPath,\n WORKSPACE_PATH: workspacePath,\n };\n}\n\n/**\n * Remove Cloudflare tunnel ingress for workspace.\n */\nfunction removeTunnelConfig(\n tunnelConfig: any,\n placeholders: Record<string, string>,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: async () => {\n const { removeTunnelIngress } = await import('../tunnel.js');\n const result = await Effect.runPromise(removeTunnelIngress(tunnelConfig, placeholders as any));\n return stepOk('teardown:tunnel', result.steps || ['Removed tunnel ingress']);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepSkipped('teardown:tunnel', [`Tunnel cleanup warning: ${(err as Error).message}`])),\n ),\n );\n}\n\n/**\n * Remove Hume EVI config for workspace.\n */\nfunction removeHumeEviConfig(\n humeConfig: any,\n placeholders: Record<string, string>,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: async () => {\n const { deleteHumeConfig } = await import('../hume.js');\n const result = await Effect.runPromise(deleteHumeConfig(humeConfig, placeholders as any));\n return stepOk('teardown:hume', result.steps || ['Removed Hume EVI config']);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepSkipped('teardown:hume', [`Hume cleanup warning: ${(err as Error).message}`])),\n ),\n );\n}\n\n/**\n * Full workspace teardown.\n */\nexport function teardownWorkspace(\n ctx: LifecycleContext,\n opts: TeardownOptions = {},\n): Effect.Effect<StepResult[]> {\n return Effect.gen(function* () {\n const issueLower = ctx.issueId.toLowerCase();\n const workspacePath = findWorkspacePath(ctx.projectPath, issueLower);\n const shouldDeleteWorkspace = opts.deleteWorkspace !== false; // default true\n const results: StepResult[] = [];\n\n // 1. Kill tmux sessions\n results.push(yield* killTmuxSessions(issueLower));\n\n // 2. Clear shadow state (always runs)\n results.push(yield* clearShadowState(ctx.issueId));\n\n // 3. Clear legacy planning directory (always runs)\n results.push(yield* clearLegacyPlanningDir(ctx.projectPath, issueLower));\n\n // 4-9: Workspace-specific cleanup\n if (workspacePath && existsSync(workspacePath)) {\n // 4. Stop TLDR daemon (only if deleting workspace)\n if (shouldDeleteWorkspace) {\n results.push(yield* stopTldrDaemon(workspacePath));\n }\n\n // 5. Stop Docker containers (only if deleting workspace)\n if (shouldDeleteWorkspace && !opts.skipDocker) {\n results.push(yield* stopDocker(workspacePath, issueLower));\n }\n\n // 5b. Kill orphaned host processes (Vite, node) that survive Docker teardown\n if (shouldDeleteWorkspace) {\n results.push(yield* killOrphanedProcesses(workspacePath));\n }\n\n // 7-8: Project-specific cleanup (tunnel, Hume)\n if (shouldDeleteWorkspace && (opts.workspaceConfig?.tunnel || opts.workspaceConfig?.hume)) {\n const placeholders = buildPlaceholders(ctx, opts, workspacePath);\n\n if (opts.workspaceConfig.tunnel) {\n results.push(yield* removeTunnelConfig(opts.workspaceConfig.tunnel, placeholders));\n }\n if (opts.workspaceConfig.hume) {\n results.push(yield* removeHumeEviConfig(opts.workspaceConfig.hume, placeholders));\n }\n }\n\n // 9. Remove worktree + workspace directory (only if deleting workspace).\n if (shouldDeleteWorkspace) {\n const allPaths = findAllWorkspacePaths(ctx.projectPath, issueLower);\n for (const p of allPaths) {\n results.push(yield* removeWorktree(ctx.projectPath, p));\n }\n }\n } else {\n results.push(stepSkipped('teardown:workspace', ['No workspace found to clean up']));\n }\n\n // 9b. Strike worktrees are residue once the strike branch is merged —\n // remove them even when deleteWorkspace is gated off (PAN-1721). When\n // shouldDeleteWorkspace is true the loop above already removed them via\n // findAllWorkspacePaths.\n if (!shouldDeleteWorkspace) {\n results.push(yield* removeStrikeWorktrees(ctx.projectPath, issueLower));\n }\n\n // 10. Remove agent state\n results.push(yield* removeAgentState(issueLower));\n\n // 11. Delete branches. Feature branches only if explicitly requested;\n // the strike branch is always a candidate once merged to main (PAN-1721).\n results.push(yield* deleteStrikeBranch(ctx.projectPath, issueLower));\n if (opts.deleteBranches) {\n results.push(yield* deleteBranches(ctx.projectPath, issueLower));\n }\n\n // 12. Prune checkpoint refs for this issue's agents.\n results.push(yield* pruneCheckpointRefs(ctx.projectPath, issueLower));\n\n // 13. Prune specialist registry entries for this issue.\n results.push(yield* pruneSpecialistRegistry(ctx.issueId));\n\n return results;\n });\n}\n\nfunction pruneSpecialistRegistry(issueId: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: async () => {\n const step = 'teardown:specialist-registry';\n const { pruneSpecialistRegistryEntriesForIssue } = await import('../cloister/specialists.js');\n const removed = pruneSpecialistRegistryEntriesForIssue(issueId);\n return removed > 0\n ? stepOk(step, [`Pruned ${removed} specialist registry entr${removed === 1 ? 'y' : 'ies'} for ${issueId}`])\n : stepSkipped(step, [`No specialist registry entries for ${issueId}`]);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepSkipped('teardown:specialist-registry', [`Specialist registry prune failed (non-fatal): ${(err as Error).message}`])),\n ),\n );\n}\n\nfunction pruneCheckpointRefs(projectPath: string, issueLower: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: async () => {\n const step = 'teardown:checkpoint-refs';\n const { pruneCheckpointRefsForAgents } = await import('../checkpoint/checkpoint-manager.js');\n const agentIds = [`agent-${issueLower}`, `planning-${issueLower}`, `strike-${issueLower}`];\n const pruned = await Effect.runPromise(pruneCheckpointRefsForAgents(projectPath, agentIds));\n return stepOk(step, [`Pruned ${pruned} checkpoint ref(s) for ${agentIds.join(', ')}`]);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:checkpoint-refs', `Checkpoint prune failed: ${(err as Error).message}`)),\n ),\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"teardown-workspace-C-9murBw.js","names":[],"sources":["../../src/lib/lifecycle/teardown-workspace.ts"],"sourcesContent":["/**\n * teardown-workspace — Full workspace cleanup.\n *\n * Consolidates workspace teardown from close-out.ts and workspace-manager.ts.\n * Handles: tmux sessions, TLDR daemon, Docker containers, git worktrees,\n * agent state directories, and (optionally) git branches.\n *\n * The workspace-manager's removeWorkspace() handles additional project-specific\n * cleanup (DNS, tunnels, Hume, ports) that this module does not cover.\n * In Phase 2, removeWorkspace() will delegate to this module for the common steps.\n */\n\nimport { existsSync } from 'fs';\nimport { readFile, rm } from 'fs/promises';\nimport { join, basename, dirname } from 'path';\nimport { homedir } from 'os';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport { Effect } from 'effect';\nimport { AGENTS_DIR } from '../paths.js';\nimport { pidsWithCwdUnder } from '../process-cwd.js';\nimport { killSession, sessionExists, listSessionNames } from '../tmux.js';\nimport type { LifecycleContext, StepResult, TeardownOptions } from './types.js';\nimport { stepOk, stepSkipped, stepFailed } from './types.js';\nimport { findAllWorkspacePaths, findWorkspacePath } from './archive-planning.js';\nimport { getContainersReferencingWorkspacePath } from '../workspace-manager.js';\nimport { DEVCONTAINER_DIRNAME } from '../workspace/devcontainer-renderer.js';\nimport { removeAgentStateDir } from '../agents/state-dir-removal.js';\n\nconst execAsync = promisify(exec);\n\n/**\n * Kill tmux sessions associated with an issue.\n */\nfunction killTmuxSessions(issueLower: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => killTmuxSessionsImpl(issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:tmux-sessions', `Failed: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function killTmuxSessionsImpl(issueLower: string): Promise<StepResult> {\n const step = 'teardown:tmux-sessions';\n let killed = 0;\n\n // Exact-match sessions (agent, test, merge, planning, strike).\n const exactPatterns = [\n `agent-${issueLower}`,\n `test-${issueLower}`,\n `merge-${issueLower}`,\n `planning-${issueLower}`,\n `strike-${issueLower}`,\n ];\n for (const session of exactPatterns) {\n if (await Effect.runPromise(sessionExists(session))) {\n try {\n await Effect.runPromise(killSession(session));\n killed++;\n } catch {\n // session may have died between check and kill\n }\n }\n }\n\n // Pattern-match sessions for review coordinators, review specialists, and\n // canonical specialists. Today's naming uses the UPPER issue ID (PAN-1024)\n // not the lower form, and includes session families that the prior\n // single-regex didn't cover. PAN-1024 close-out left\n // `review-coordinator-PAN-1024-...` and\n // `specialist-overdeck-PAN-1024-test-agent` alive (2026-05-09).\n //\n // Patterns we now match (case-insensitive on the issue ID):\n // - review-coordinator-<ISSUE>-<timestamp>\n // - review-<ISSUE>-<timestamp>-<role> (legacy)\n // - specialist-<projectKey>-<ISSUE>-<role> (canonical PAN-830/915)\n try {\n const allSessions = await Effect.runPromise(listSessionNames());\n const escapedLower = issueLower.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const escapedUpper = issueLower.toUpperCase().replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const issuePart = `(${escapedLower}|${escapedUpper})`;\n const patterns: RegExp[] = [\n new RegExp(`^review-coordinator-${issuePart}-\\\\d+`),\n new RegExp(`^review-${issuePart}-\\\\d+`),\n new RegExp(`^specialist-[^-]+(?:-[^-]+)*?-${issuePart}-`),\n ];\n const matchedSessions = allSessions.filter(s => patterns.some(p => p.test(s)));\n for (const session of matchedSessions) {\n try {\n await Effect.runPromise(killSession(session));\n killed++;\n } catch {\n // session may have died between check and kill\n }\n }\n } catch {\n // Session listing may fail if tmux server is not running\n }\n\n // NOTE: Per-project ephemeral specialists (specialist-{project}-{type}) are NOT killed here.\n // They belong to the project, not the issue, and accumulate context across issues via --resume.\n // Their grace period / idle timeout handles cleanup when no new work arrives.\n\n if (killed > 0) {\n return stepOk(step, [`Killed ${killed} tmux session(s)`]);\n }\n return stepSkipped(step, ['No tmux sessions found']);\n}\n\n/**\n * Stop TLDR daemon if workspace has a .venv.\n */\nfunction stopTldrDaemon(workspacePath: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => stopTldrDaemonImpl(workspacePath),\n catch: (err) => err,\n }).pipe(\n Effect.catch(() =>\n Effect.succeed(stepSkipped('teardown:tldr-daemon', ['TLDR daemon not running or failed to stop (non-fatal)'])),\n ),\n );\n}\n\nasync function stopTldrDaemonImpl(workspacePath: string): Promise<StepResult> {\n const step = 'teardown:tldr-daemon';\n const venvPath = join(workspacePath, '.venv');\n if (!existsSync(venvPath)) {\n return stepSkipped(step, ['No .venv found']);\n }\n try {\n const { getTldrDaemonServiceSync } = await import('../tldr-daemon.js');\n const tldrService = getTldrDaemonServiceSync(workspacePath, venvPath);\n await tldrService.stop();\n return stepOk(step, ['Stopped TLDR daemon']);\n } catch {\n return stepSkipped(step, ['TLDR daemon not running or failed to stop (non-fatal)']);\n }\n}\n\n/**\n * Stop Docker containers for the workspace.\n */\nfunction stopDocker(\n workspacePath: string,\n issueLower: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => stopDockerImpl(workspacePath, issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch(() =>\n Effect.succeed(stepSkipped('teardown:docker', ['Docker cleanup skipped (not running or failed)'])),\n ),\n );\n}\n\nasync function stopDockerImpl(\n workspacePath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:docker';\n try {\n const { stopWorkspaceDocker } = await import('../workspace-manager.js');\n await Effect.runPromise(stopWorkspaceDocker(workspacePath, issueLower));\n return stepOk(step, ['Stopped Docker containers']);\n } catch {\n return stepSkipped(step, ['Docker cleanup skipped (not running or failed)']);\n }\n}\n\n/**\n * Detect whether a PID belongs to a process running inside a Docker container.\n * Container processes appear in host lsof output when paths are bind-mounted.\n */\nasync function isDockerContainerProcess(pid: string): Promise<boolean> {\n try {\n const cgroup = await readFile(`/proc/${pid}/cgroup`, 'utf-8');\n return cgroup.includes('/docker-') || cgroup.includes('/docker/');\n } catch {\n return false;\n }\n}\n\n/**\n * Kill orphaned host processes for a workspace.\n */\nfunction killOrphanedProcesses(workspacePath: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => killOrphanedProcessesImpl(workspacePath),\n catch: (err) => err,\n }).pipe(\n Effect.catch(() =>\n Effect.succeed(stepSkipped('teardown:orphaned-processes', ['Orphaned process cleanup failed (non-fatal)'])),\n ),\n );\n}\n\nasync function killOrphanedProcessesImpl(workspacePath: string): Promise<StepResult> {\n const step = 'teardown:orphaned-processes';\n try {\n // Find PIDs with cwd inside the workspace. NEVER select by open files\n // (`lsof +D`): Bun's hardlinked node_modules make the dashboard and every\n // conversation's PTY supervisor appear to hold files \"in\" any workspace\n // via mmap'd native addons — see src/lib/process-cwd.ts.\n const pids = await pidsWithCwdUnder(workspacePath);\n\n if (pids.length === 0) {\n return stepSkipped(step, ['No orphaned processes found']);\n }\n\n // Don't kill our own process or the dashboard\n const myPid = String(process.pid);\n const safePids = pids.filter(p => p !== myPid);\n\n if (safePids.length === 0) {\n return stepSkipped(step, ['No orphaned processes to kill']);\n }\n\n // Filter out Docker container processes — they appear in host lsof due to bind mounts\n const hostPids: string[] = [];\n for (const pid of safePids) {\n if (!(await isDockerContainerProcess(pid))) {\n hostPids.push(pid);\n }\n }\n\n if (hostPids.length === 0) {\n return stepSkipped(step, ['No orphaned host processes to kill (all were container processes)']);\n }\n\n await execAsync(`kill ${hostPids.join(' ')} 2>/dev/null || true`, { encoding: 'utf-8', timeout: 5000 });\n return stepOk(step, [`Killed ${hostPids.length} orphaned process(es)`]);\n } catch {\n return stepSkipped(step, ['Orphaned process cleanup failed (non-fatal)']);\n }\n}\n\n/**\n * Remove git worktree for the workspace.\n */\nfunction removeWorktree(\n projectPath: string,\n workspacePath: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => removeWorktreeImpl(projectPath, workspacePath),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:worktree', `Failed to remove workspace: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function removeWorktreeImpl(\n projectPath: string,\n workspacePath: string,\n): Promise<StepResult> {\n const step = 'teardown:worktree';\n if (!existsSync(workspacePath)) {\n return stepSkipped(step, ['Workspace directory does not exist']);\n }\n\n // Guard: never delete workspace (and its `.devcontainer/`) while containers\n // still reference compose paths inside it.\n const orphanedContainers = await Effect.runPromise(getContainersReferencingWorkspacePath(workspacePath));\n if (orphanedContainers.length > 0) {\n return stepFailed(\n step,\n `Cannot remove workspace: ${orphanedContainers.length} Docker container(s) still reference compose paths in ${DEVCONTAINER_DIRNAME}/. ` +\n `Run workspace Docker cleanup first or stop the containers manually.`,\n );\n }\n\n try {\n await execAsync(`git worktree remove \"${workspacePath}\" --force`, { cwd: projectPath });\n return stepOk(step, ['Removed git worktree']);\n } catch {\n // worktree remove failed — try direct removal\n try {\n await rm(workspacePath, { recursive: true, force: true });\n return stepOk(step, ['Removed workspace directory after worktree removal failed']);\n } catch (err) {\n return stepFailed(step, `Failed to remove workspace: ${(err as Error).message}`);\n }\n }\n}\n\n/**\n * Remove every agent state directory tied to an issue.\n */\nfunction removeAgentState(issueLower: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => removeAgentStateImpl(issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:agent-state', `Failed: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function removeAgentStateImpl(issueLower: string): Promise<StepResult> {\n const step = 'teardown:agent-state';\n const { readdir } = await import('fs/promises');\n\n let entries: string[];\n try {\n entries = await readdir(AGENTS_DIR);\n } catch {\n return stepSkipped(step, ['Agents directory not present']);\n }\n\n const work = `agent-${issueLower}`;\n const planner = `planning-${issueLower}`;\n const strike = `strike-${issueLower}`;\n const specialistPrefix = `agent-${issueLower}-`;\n const targets = entries.filter(name =>\n name === work || name === planner || name === strike || name.startsWith(specialistPrefix),\n );\n\n let removed = 0;\n let preservedTranscripts = 0;\n for (const name of targets) {\n try {\n const result = await removeAgentStateDir(join(AGENTS_DIR, name));\n if (result.removedDir || result.preservedTranscripts > 0) removed++;\n preservedTranscripts += result.preservedTranscripts;\n } catch { /* non-fatal */ }\n }\n\n if (removed > 0) {\n return stepOk(step, [\n `Cleaned ${removed} agent state director${removed === 1 ? 'y' : 'ies'} (${preservedTranscripts} transcript files preserved)`,\n ]);\n }\n return stepSkipped(step, ['No agent state directories found']);\n}\n\n/**\n * Delete feature branches (local + remote).\n */\nfunction deleteBranches(\n projectPath: string,\n issueLower: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => deleteBranchesImpl(projectPath, issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:branches', `Failed: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function deleteBranchesImpl(\n projectPath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:branches';\n const branchName = `feature/${issueLower}`;\n const details: string[] = [];\n const localSlots = await execAsync(`git for-each-ref --format=\"%(refname:short)\" \"refs/heads/feature/${issueLower}-slot-*\"`, { cwd: projectPath, encoding: 'utf-8' })\n .then(({ stdout }) => stdout.trim().split('\\n').filter(Boolean)).catch(() => [] as string[]);\n const remoteSlots = await execAsync(`git ls-remote --heads origin \"feature/${issueLower}-slot-*\"`, { cwd: projectPath, encoding: 'utf-8' })\n .then(({ stdout }) => stdout.trim().split('\\n').map(line => line.split('refs/heads/')[1]).filter((name): name is string => Boolean(name))).catch(() => [] as string[]);\n\n // Delete local branch\n try {\n await execAsync(`git branch -D \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted local branch ${branchName}`);\n } catch {\n details.push(`Local branch ${branchName} not found (already deleted)`);\n }\n\n for (const slotBranch of localSlots) {\n try {\n await execAsync(`git branch -D ${JSON.stringify(slotBranch)}`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted local branch ${slotBranch}`);\n } catch { details.push(`Failed to delete local branch ${slotBranch}`); }\n }\n\n // Delete remote branch\n try {\n await execAsync(`git push origin --delete \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted remote branch ${branchName}`);\n } catch {\n details.push(`Remote branch ${branchName} not found (already deleted)`);\n }\n\n for (const slotBranch of remoteSlots) {\n try {\n await execAsync(`git push origin --delete ${JSON.stringify(slotBranch)}`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted remote branch ${slotBranch}`);\n } catch { details.push(`Failed to delete remote branch ${slotBranch}`); }\n }\n\n return stepOk(step, details);\n}\n\n/**\n * Whether a local branch exists and is fully merged into main.\n */\nasync function localBranchMergedState(\n projectPath: string,\n branchName: string,\n): Promise<{ exists: boolean; merged: boolean }> {\n try {\n await execAsync(`git show-ref --verify --quiet \"refs/heads/${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n } catch {\n return { exists: false, merged: false };\n }\n try {\n await execAsync(`git merge-base --is-ancestor \"${branchName}\" main`, { cwd: projectPath, encoding: 'utf-8' });\n return { exists: true, merged: true };\n } catch {\n return { exists: true, merged: false };\n }\n}\n\n/**\n * Delete the strike branch (local + remote) once it is merged into main.\n *\n * Strike work (`pan strike`) lands on main directly, so a merged\n * `strike/<issue>` branch is pure residue — it is deleted on every teardown,\n * not just when `deleteBranches` is requested (PAN-1721). An UNMERGED strike\n * branch may hold the only copy of an aborted strike's work, so it is\n * preserved and reported instead.\n */\nfunction deleteStrikeBranch(\n projectPath: string,\n issueLower: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => deleteStrikeBranchImpl(projectPath, issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:strike-branch', `Failed: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function deleteStrikeBranchImpl(\n projectPath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:strike-branch';\n const branchName = `strike/${issueLower}`;\n const details: string[] = [];\n\n // Local branch\n const local = await localBranchMergedState(projectPath, branchName);\n if (local.exists) {\n if (local.merged) {\n try {\n await execAsync(`git branch -D \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted local branch ${branchName}`);\n } catch (err) {\n details.push(`Failed to delete local branch ${branchName}: ${(err as Error).message}`);\n }\n } else {\n details.push(`Preserved local branch ${branchName} — not merged to main`);\n }\n }\n\n // Remote branch\n try {\n const { stdout: remoteRef } = await execAsync(\n `git ls-remote --heads origin \"${branchName}\" 2>/dev/null || true`,\n { cwd: projectPath, encoding: 'utf-8' },\n );\n if (remoteRef.trim()) {\n let remoteMerged = false;\n try {\n await execAsync(`git fetch origin \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n await execAsync('git merge-base --is-ancestor FETCH_HEAD main', { cwd: projectPath, encoding: 'utf-8' });\n remoteMerged = true;\n } catch {\n remoteMerged = false;\n }\n if (remoteMerged) {\n await execAsync(`git push origin --delete \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted remote branch ${branchName}`);\n } else {\n details.push(`Preserved remote branch ${branchName} — not merged to main`);\n }\n }\n } catch {\n // remote check/delete failed — non-fatal\n }\n\n if (details.length === 0) {\n return stepSkipped(step, [`No strike branch ${branchName} found`]);\n }\n return details.some(d => d.startsWith('Failed'))\n ? stepFailed(step, details.join('; '))\n : stepOk(step, details);\n}\n\n/**\n * Remove leftover strike worktrees (`workspaces/feature-<issue>-strike`).\n *\n * Runs when the regular worktree removal is gated off (remove_workspace=false\n * at close-out): a strike worktree has no post-close purpose once the strike\n * branch is merged or gone, and leaving it keeps the closed issue alive in\n * resource discovery (PAN-1721). Worktrees whose strike branch has unmerged\n * commits are preserved alongside the branch.\n */\nfunction removeStrikeWorktrees(\n projectPath: string,\n issueLower: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => removeStrikeWorktreesImpl(projectPath, issueLower),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:strike-worktree', `Failed: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function removeStrikeWorktreesImpl(\n projectPath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:strike-worktree';\n const strikePaths = findAllWorkspacePaths(projectPath, issueLower).filter(p => p.endsWith('-strike'));\n if (strikePaths.length === 0) {\n return stepSkipped(step, ['No strike worktree found']);\n }\n\n const branch = await localBranchMergedState(projectPath, `strike/${issueLower}`);\n if (branch.exists && !branch.merged) {\n return stepSkipped(step, [`Preserved strike worktree — strike/${issueLower} not merged to main`]);\n }\n\n const details: string[] = [];\n for (const p of strikePaths) {\n const result = await removeWorktreeImpl(projectPath, p);\n details.push(...(result.details ?? [result.error ?? result.step]));\n if (!result.success && !result.skipped) {\n return stepFailed(step, `Failed to remove strike worktree ${p}: ${result.error ?? 'unknown error'}`);\n }\n }\n return stepOk(step, details);\n}\n\n/**\n * Clear shadow state for an issue.\n */\nfunction clearShadowState(issueId: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => clearShadowStateImpl(issueId),\n catch: (err) => err,\n }).pipe(\n Effect.catch(() =>\n Effect.succeed(stepSkipped('teardown:shadow-state', ['Shadow state cleanup skipped (non-fatal)'])),\n ),\n );\n}\n\nasync function clearShadowStateImpl(issueId: string): Promise<StepResult> {\n const step = 'teardown:shadow-state';\n try {\n const { removeShadowState } = await import('../shadow-state.js');\n const result = removeShadowState(issueId);\n if (result.success) {\n return stepOk(step, [`Cleared shadow state for ${issueId}`]);\n }\n return stepSkipped(step, ['No shadow state found']);\n } catch {\n return stepSkipped(step, ['Shadow state cleanup skipped (non-fatal)']);\n }\n}\n\n/**\n * Remove legacy .planning/<issue>/ directory from project root.\n */\nfunction clearLegacyPlanningDir(\n projectPath: string,\n issueLower: string,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: async () => {\n const step = 'teardown:legacy-planning-dir';\n const legacyDir = join(projectPath, '.planning', issueLower);\n if (!existsSync(legacyDir)) {\n return stepSkipped(step, ['No legacy planning directory found']);\n }\n await rm(legacyDir, { recursive: true, force: true });\n return stepOk(step, [`Deleted legacy planning dir: ${legacyDir}`]);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:legacy-planning-dir', `Failed to delete legacy planning dir: ${(err as Error).message}`)),\n ),\n );\n}\n\n/**\n * Build template placeholders for project-specific cleanup (tunnel, Hume).\n */\nfunction buildPlaceholders(\n ctx: LifecycleContext,\n opts: TeardownOptions,\n workspacePath: string,\n) {\n const issueLower = ctx.issueId.toLowerCase();\n const featureFolder = `feature-${issueLower}`;\n const projName = opts.projectName || ctx.projectName || basename(ctx.projectPath);\n const domain = opts.workspaceConfig?.dns?.domain || 'localhost';\n return {\n FEATURE_NAME: issueLower,\n FEATURE_FOLDER: featureFolder,\n BRANCH_NAME: `feature/${issueLower}`,\n COMPOSE_PROJECT: `${projName}-${featureFolder}`,\n DOMAIN: domain,\n PROJECT_NAME: projName,\n PROJECT_PATH: ctx.projectPath,\n WORKSPACE_PATH: workspacePath,\n };\n}\n\n/**\n * Remove Cloudflare tunnel ingress for workspace.\n */\nfunction removeTunnelConfig(\n tunnelConfig: any,\n placeholders: Record<string, string>,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: async () => {\n const { removeTunnelIngress } = await import('../tunnel.js');\n const result = await Effect.runPromise(removeTunnelIngress(tunnelConfig, placeholders as any));\n return stepOk('teardown:tunnel', result.steps || ['Removed tunnel ingress']);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepSkipped('teardown:tunnel', [`Tunnel cleanup warning: ${(err as Error).message}`])),\n ),\n );\n}\n\n/**\n * Remove Hume EVI config for workspace.\n */\nfunction removeHumeEviConfig(\n humeConfig: any,\n placeholders: Record<string, string>,\n): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: async () => {\n const { deleteHumeConfig } = await import('../hume.js');\n const result = await Effect.runPromise(deleteHumeConfig(humeConfig, placeholders as any));\n return stepOk('teardown:hume', result.steps || ['Removed Hume EVI config']);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepSkipped('teardown:hume', [`Hume cleanup warning: ${(err as Error).message}`])),\n ),\n );\n}\n\n/**\n * Full workspace teardown.\n */\nexport function teardownWorkspace(\n ctx: LifecycleContext,\n opts: TeardownOptions = {},\n): Effect.Effect<StepResult[]> {\n return Effect.gen(function* () {\n const issueLower = ctx.issueId.toLowerCase();\n const workspacePath = findWorkspacePath(ctx.projectPath, issueLower);\n const shouldDeleteWorkspace = opts.deleteWorkspace !== false; // default true\n const results: StepResult[] = [];\n\n // 1. Kill tmux sessions\n results.push(yield* killTmuxSessions(issueLower));\n\n // 2. Clear shadow state (always runs)\n results.push(yield* clearShadowState(ctx.issueId));\n\n // 3. Clear legacy planning directory (always runs)\n results.push(yield* clearLegacyPlanningDir(ctx.projectPath, issueLower));\n\n // 4-9: Workspace-specific cleanup\n if (workspacePath && existsSync(workspacePath)) {\n // 4. Stop TLDR daemon (only if deleting workspace)\n if (shouldDeleteWorkspace) {\n results.push(yield* stopTldrDaemon(workspacePath));\n }\n\n // 5. Stop Docker containers (only if deleting workspace)\n if (shouldDeleteWorkspace && !opts.skipDocker) {\n results.push(yield* stopDocker(workspacePath, issueLower));\n }\n\n // 5b. Kill orphaned host processes (Vite, node) that survive Docker teardown\n if (shouldDeleteWorkspace) {\n results.push(yield* killOrphanedProcesses(workspacePath));\n }\n\n // 7-8: Project-specific cleanup (tunnel, Hume)\n if (shouldDeleteWorkspace && (opts.workspaceConfig?.tunnel || opts.workspaceConfig?.hume)) {\n const placeholders = buildPlaceholders(ctx, opts, workspacePath);\n\n if (opts.workspaceConfig.tunnel) {\n results.push(yield* removeTunnelConfig(opts.workspaceConfig.tunnel, placeholders));\n }\n if (opts.workspaceConfig.hume) {\n results.push(yield* removeHumeEviConfig(opts.workspaceConfig.hume, placeholders));\n }\n }\n\n // 9. Remove worktree + workspace directory (only if deleting workspace).\n if (shouldDeleteWorkspace) {\n const allPaths = findAllWorkspacePaths(ctx.projectPath, issueLower);\n for (const p of allPaths) {\n results.push(yield* removeWorktree(ctx.projectPath, p));\n }\n }\n } else {\n results.push(stepSkipped('teardown:workspace', ['No workspace found to clean up']));\n }\n\n // 9b. Strike worktrees are residue once the strike branch is merged —\n // remove them even when deleteWorkspace is gated off (PAN-1721). When\n // shouldDeleteWorkspace is true the loop above already removed them via\n // findAllWorkspacePaths.\n if (!shouldDeleteWorkspace) {\n results.push(yield* removeStrikeWorktrees(ctx.projectPath, issueLower));\n }\n\n // 10. Remove agent state\n results.push(yield* removeAgentState(issueLower));\n\n // 11. Delete branches. Feature branches only if explicitly requested;\n // the strike branch is always a candidate once merged to main (PAN-1721).\n results.push(yield* deleteStrikeBranch(ctx.projectPath, issueLower));\n if (opts.deleteBranches) {\n results.push(yield* deleteBranches(ctx.projectPath, issueLower));\n }\n\n // 12. Prune checkpoint refs for this issue's agents.\n results.push(yield* pruneCheckpointRefs(ctx.projectPath, issueLower));\n\n // 13. Prune specialist registry entries for this issue.\n results.push(yield* pruneSpecialistRegistry(ctx.issueId));\n\n return results;\n });\n}\n\nfunction pruneSpecialistRegistry(issueId: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: async () => {\n const step = 'teardown:specialist-registry';\n const { pruneSpecialistRegistryEntriesForIssue } = await import('../cloister/specialists.js');\n const removed = pruneSpecialistRegistryEntriesForIssue(issueId);\n return removed > 0\n ? stepOk(step, [`Pruned ${removed} specialist registry entr${removed === 1 ? 'y' : 'ies'} for ${issueId}`])\n : stepSkipped(step, [`No specialist registry entries for ${issueId}`]);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepSkipped('teardown:specialist-registry', [`Specialist registry prune failed (non-fatal): ${(err as Error).message}`])),\n ),\n );\n}\n\nfunction pruneCheckpointRefs(projectPath: string, issueLower: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: async () => {\n const step = 'teardown:checkpoint-refs';\n const { pruneCheckpointRefsForAgents } = await import('../checkpoint/checkpoint-manager.js');\n const agentIds = [`agent-${issueLower}`, `planning-${issueLower}`, `strike-${issueLower}`];\n const pruned = await Effect.runPromise(pruneCheckpointRefsForAgents(projectPath, agentIds));\n return stepOk(step, [`Pruned ${pruned} checkpoint ref(s) for ${agentIds.join(', ')}`]);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('teardown:checkpoint-refs', `Checkpoint prune failed: ${(err as Error).message}`)),\n ),\n );\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { On as resolveProjectFromIssueSync, fn as init_projects } from "./config-yaml-Dce9VUd6.js";
2
- import { ln as spawnRun, q as init_agents } from "./agent-state-Ci6PhjOE.js";
3
- import { p as init_review_status, w as setReviewStatusSync } from "./review-status-BegAr86G.js";
2
+ import { ln as spawnRun, q as init_agents } from "./agent-state-B196fj1h.js";
3
+ import { p as init_review_status, w as setReviewStatusSync } from "./review-status-yY5qt7Rl.js";
4
4
  import { n as shouldSkipDispatchAsMerged } from "./merge-verification-BPBW3Hu3.js";
5
5
  import { r as init_test_verdict, t as clearTestVerdictArtifact } from "./test-verdict-DhjFE4Go.js";
6
6
  import { Data, Effect } from "effect";
@@ -158,4 +158,4 @@ const dispatchTestAgentAndNotify = (issueId, workspace, branch, notifyAgent) =>
158
158
  //#endregion
159
159
  export { buildTestRolePrompt, dispatchTestAgentAndNotify };
160
160
 
161
- //# sourceMappingURL=test-agent-queue-CMgYZiR_.js.map
161
+ //# sourceMappingURL=test-agent-queue-YCBRzaAB.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"test-agent-queue-CMgYZiR_.js","names":[],"sources":["../../src/lib/cloister/test-agent-queue.ts"],"sourcesContent":["/**\n * Direct dispatch logic for triggering the test role after review passes.\n *\n * Test and browser UAT now run inside the role-based `test` run. There is no\n * separate UAT specialist and no per-project test-agent specialist pool.\n */\n\nimport { Data, Effect } from 'effect';\nimport { setReviewStatusSync } from '../review-status.js';\nimport { spawnRun } from '../agents.js';\nimport { resolveProjectFromIssueSync } from '../projects.js';\nimport { clearTestVerdictArtifact } from './test-verdict.js';\nimport { shouldSkipDispatchAsMerged } from './merge-verification.js';\n\nexport function buildTestRolePrompt(options: {\n issueId: string;\n workspace?: string;\n branch?: string;\n}): string {\n const workspaceLine = options.workspace ? `WORKSPACE: ${options.workspace}` : 'WORKSPACE: resolve from the run state';\n const branchLine = options.branch ? `BRANCH: ${options.branch}` : `BRANCH: feature/${options.issueId.toLowerCase()}`;\n return `TEST TASK for ${options.issueId}:\n\n${workspaceLine}\n${branchLine}\n\nRun the role-based verification flow for this already-reviewed branch.\n\nRequired steps:\n1. Before every repository command, verify you are in the workspace above with \\`pwd\\`. If not, stop and switch back to that workspace before continuing.\n2. Work only in the workspace above. Never run build, test, git, or dashboard commands from the main checkout or another worktree.\n3. Read .pan/records/${options.issueId.toLowerCase()}.json, the canonical xBRIEF under .pan/specs/ for ${options.issueId}, issue notes, and project instructions to determine required verification. Do not require retired workspace-local .pan/continue.json or .pan/spec.vbrief.json files to exist.\n4. Run the configured project gates (at minimum typecheck, lint, and tests when present/applicable).\n5. Decide whether browser UAT is required from acceptance criteria, issue notes, PR notes, or UI/dashboard wording.\n6. If UAT is required, build and run the dashboard from the workspace above, not from main. If a dashboard from another checkout is already running, stop it and start the workspace-built dashboard.\n7. If UAT is required, use the Playwright MCP tools available to the test role. Do not spawn or wake a separate UAT agent.\n8. Record automated gates and browser UAT as separate verdicts. The top-level status is ONLY the configured automated-gate result. When UAT is required, add uatStatus and uatNotes; omit both when UAT is not required. FIRST write .pan/test/result.json so the pipeline can recover both verdicts if the signal is interrupted:\n {\"status\":\"passed\",\"notes\":\"<automated gate evidence>\",\"uatStatus\":\"passed\",\"uatNotes\":\"<browser paths and evidence>\"}\n Allowed values for status and uatStatus are \"passed\" or \"failed\". A required UAT that cannot run or leaves any criterion unproven is uatStatus \"failed\", even when status is \"passed\". Create .pan/test/ if needed and write this artifact BEFORE signaling the verdict.\n9. Signal the same separate verdicts through the local trusted CLI, never by an unauthenticated HTTP request. Examples:\n Automated gates pass and required UAT passes:\n pan admin specialists done test ${options.issueId} --status passed --notes \"<automated gate evidence>\" --uat-status passed --uat-notes \"<browser evidence>\"\n Automated gates pass but required UAT fails or cannot run:\n pan admin specialists done test ${options.issueId} --status passed --notes \"<automated gate evidence>\" --uat-status failed --uat-notes \"<blocking condition and exact unmet criteria>\"\n Automated gates fail (include UAT flags too if UAT was attempted):\n pan admin specialists done test ${options.issueId} --status failed --notes \"<failing commands and output>\"\n10. Make exactly ONE CLI signal attempt. If it fails, the .pan/test/result.json artifact from step 8 is the durable verdict and the deacon recovers from it — do NOT retry the signal in a loop. Report the failure in your summary and stop.\n11. Report TESTS PASSED only when automated gates and required UAT passed. Otherwise report TESTS FAILED with commands run, UAT paths exercised, and concise evidence.\n\nBoundaries:\n- Do NOT edit code, tests, fixtures, snapshots, or configuration.\n- Do NOT commit, push, merge, close issues, or call any merge endpoint.\n- Do NOT spawn, wake, or delegate to test-agent or uat-agent specialists.`;\n}async function dispatchTestAgentAndNotifyPromise(\n issueId: string,\n workspace?: string,\n branch?: string,\n notifyAgent?: (agentId: string, msg: string) => Promise<void>,\n): Promise<void> {\n let testTaskDelivered = false;\n\n try {\n const resolved = resolveProjectFromIssueSync(issueId);\n if (!resolved) {\n console.error(`[test-dispatch] No project configured for ${issueId} — cannot spawn test role`);\n setReviewStatusSync(issueId, {\n testStatus: 'dispatch_failed',\n testNotes: `No project configured for ${issueId}. Add it to projects.yaml.`,\n });\n return;\n }\n\n // Clear any stale verdict artifact so a previous cycle's result.json can\n // never be misread by the deacon failsafe as this dispatch's verdict (H3).\n if (workspace) clearTestVerdictArtifact(workspace);\n\n const prompt = buildTestRolePrompt({ issueId, workspace, branch });\n\n const mergedGuard = await shouldSkipDispatchAsMerged(issueId);\n if (mergedGuard.skip) {\n console.log(`[test-dispatch] Skipping test dispatch for ${issueId} — ${mergedGuard.reason}`);\n return;\n }\n\n const run = await spawnRun(issueId, 'test', {\n workspace,\n prompt,\n startedBy: 'test-agent-queue',\n });\n\n setReviewStatusSync(issueId, { testStatus: 'testing' });\n testTaskDelivered = true;\n console.log(`[test-dispatch] Started test role for ${issueId} (${run.id})`);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (msg.includes('already running')) {\n setReviewStatusSync(issueId, { testStatus: 'testing' });\n testTaskDelivered = true;\n console.log(`[test-dispatch] Test role already running for ${issueId}`);\n } else {\n console.error(`[test-dispatch] Failed to dispatch test role for ${issueId}:`, err);\n try {\n setReviewStatusSync(issueId, {\n testStatus: 'dispatch_failed',\n testNotes: `Dispatch failed: ${msg}`,\n });\n } catch (statusErr) {\n console.error(`[test-dispatch] Failed to set dispatch_failed status for ${issueId}:`, statusErr);\n }\n }\n }\n\n // Only notify work agent when test was successfully dispatched\n if (testTaskDelivered && notifyAgent) {\n try {\n await notifyAgent(\n `agent-${issueId.toLowerCase()}`,\n `REVIEW PASSED for ${issueId}. The test role has been dispatched automatically. Do NOT poll or check status — you will be notified when tests complete.`,\n );\n } catch (err) {\n console.log(\n `[test-dispatch] Could not notify work agent for ${issueId} (may not be running): ${(err as Error).message}`,\n );\n }\n }\n}\n\n// ─── Effect variant (PAN-1249) ───────────────────────────────────────────────\n\n/** A test-role dispatch error — wraps spawnRun / setReviewStatus failures. */\nexport class TestDispatchError extends Data.TaggedError('TestDispatchError')<{\n readonly issueId: string;\n readonly message: string;\n readonly cause?: unknown;\n}> {}\n\nexport interface DispatchTestAgentResult {\n readonly delivered: boolean;\n readonly notified: boolean;\n readonly runId?: string;\n readonly reason?: 'no-project' | 'already-running' | 'spawn-failed';\n}\n\n/**\n * Effect variant of {@link dispatchTestAgentAndNotify}. Same semantics as the\n * Promise version, but failures funnel through a typed error channel. The\n * `notifyAgent` step is allowed to fail without failing the outer Effect — its\n * outcome is reported via {@link DispatchTestAgentResult.notified}.\n */\nexport const dispatchTestAgentAndNotify = (\n issueId: string,\n workspace?: string,\n branch?: string,\n notifyAgent?: (agentId: string, msg: string) => Promise<void>,\n): Effect.Effect<DispatchTestAgentResult> =>\n Effect.gen(function* () {\n const resolved = yield* Effect.sync(() => resolveProjectFromIssueSync(issueId));\n if (!resolved) {\n yield* Effect.sync(() => {\n console.error(`[test-dispatch] No project configured for ${issueId} — cannot spawn test role`);\n setReviewStatusSync(issueId, {\n testStatus: 'dispatch_failed',\n testNotes: `No project configured for ${issueId}. Add it to projects.yaml.`,\n });\n });\n return { delivered: false, notified: false, reason: 'no-project' as const };\n }\n\n // Clear any stale verdict artifact so a previous cycle's result.json can\n // never be misread by the deacon failsafe as this dispatch's verdict (H3).\n if (workspace) yield* Effect.sync(() => clearTestVerdictArtifact(workspace));\n\n const prompt = buildTestRolePrompt({ issueId, workspace, branch });\n\n const mergedGuard = yield* Effect.promise(() => shouldSkipDispatchAsMerged(issueId));\n if (mergedGuard.skip) {\n console.log(`[test-dispatch] Skipping test dispatch for ${issueId} — ${mergedGuard.reason}`);\n return { delivered: false, notified: false, reason: 'spawn-failed' };\n }\n\n const spawnProgram: Effect.Effect<DispatchTestAgentResult, never> = Effect.tryPromise({\n try: () => spawnRun(issueId, 'test', { workspace, prompt, startedBy: 'test-agent-queue' }),\n catch: (cause) => {\n const msg = cause instanceof Error ? cause.message : String(cause);\n return new TestDispatchError({ issueId, message: msg, cause });\n },\n }).pipe(\n Effect.matchEffect({\n onFailure: (err: TestDispatchError) => {\n // \"already running\" is non-fatal — treat as delivered.\n if (err.message.includes('already running')) {\n return Effect.sync((): DispatchTestAgentResult => {\n setReviewStatusSync(issueId, { testStatus: 'testing' });\n console.log(`[test-dispatch] Test role already running for ${issueId}`);\n return {\n delivered: true,\n notified: false,\n reason: 'already-running',\n };\n });\n }\n return Effect.sync((): DispatchTestAgentResult => {\n console.error(`[test-dispatch] Failed to dispatch test role for ${issueId}: ${err.message}`);\n try {\n setReviewStatusSync(issueId, {\n testStatus: 'dispatch_failed',\n testNotes: `Dispatch failed: ${err.message}`,\n });\n } catch (statusErr) {\n console.error(`[test-dispatch] Failed to set dispatch_failed status for ${issueId}:`, statusErr);\n }\n return {\n delivered: false,\n notified: false,\n reason: 'spawn-failed',\n };\n });\n },\n onSuccess: (run) =>\n Effect.sync((): DispatchTestAgentResult => {\n setReviewStatusSync(issueId, { testStatus: 'testing' });\n console.log(`[test-dispatch] Started test role for ${issueId} (${run.id})`);\n return {\n delivered: true,\n notified: false,\n runId: run.id,\n };\n }),\n }),\n );\n\n const spawnResult: DispatchTestAgentResult = yield* spawnProgram;\n\n if (spawnResult.delivered && notifyAgent) {\n const notified = yield* Effect.tryPromise({\n try: () =>\n notifyAgent(\n `agent-${issueId.toLowerCase()}`,\n `REVIEW PASSED for ${issueId}. The test role has been dispatched automatically. Do NOT poll or check status — you will be notified when tests complete.`,\n ).then(() => true),\n catch: (err) => {\n console.log(\n `[test-dispatch] Could not notify work agent for ${issueId} (may not be running): ${(err as Error).message}`,\n );\n return false;\n },\n }).pipe(Effect.orElseSucceed(() => false));\n return { ...spawnResult, notified };\n }\n\n return spawnResult;\n });\n"],"mappings":"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"}
1
+ {"version":3,"file":"test-agent-queue-YCBRzaAB.js","names":[],"sources":["../../src/lib/cloister/test-agent-queue.ts"],"sourcesContent":["/**\n * Direct dispatch logic for triggering the test role after review passes.\n *\n * Test and browser UAT now run inside the role-based `test` run. There is no\n * separate UAT specialist and no per-project test-agent specialist pool.\n */\n\nimport { Data, Effect } from 'effect';\nimport { setReviewStatusSync } from '../review-status.js';\nimport { spawnRun } from '../agents.js';\nimport { resolveProjectFromIssueSync } from '../projects.js';\nimport { clearTestVerdictArtifact } from './test-verdict.js';\nimport { shouldSkipDispatchAsMerged } from './merge-verification.js';\n\nexport function buildTestRolePrompt(options: {\n issueId: string;\n workspace?: string;\n branch?: string;\n}): string {\n const workspaceLine = options.workspace ? `WORKSPACE: ${options.workspace}` : 'WORKSPACE: resolve from the run state';\n const branchLine = options.branch ? `BRANCH: ${options.branch}` : `BRANCH: feature/${options.issueId.toLowerCase()}`;\n return `TEST TASK for ${options.issueId}:\n\n${workspaceLine}\n${branchLine}\n\nRun the role-based verification flow for this already-reviewed branch.\n\nRequired steps:\n1. Before every repository command, verify you are in the workspace above with \\`pwd\\`. If not, stop and switch back to that workspace before continuing.\n2. Work only in the workspace above. Never run build, test, git, or dashboard commands from the main checkout or another worktree.\n3. Read .pan/records/${options.issueId.toLowerCase()}.json, the canonical xBRIEF under .pan/specs/ for ${options.issueId}, issue notes, and project instructions to determine required verification. Do not require retired workspace-local .pan/continue.json or .pan/spec.vbrief.json files to exist.\n4. Run the configured project gates (at minimum typecheck, lint, and tests when present/applicable).\n5. Decide whether browser UAT is required from acceptance criteria, issue notes, PR notes, or UI/dashboard wording.\n6. If UAT is required, build and run the dashboard from the workspace above, not from main. If a dashboard from another checkout is already running, stop it and start the workspace-built dashboard.\n7. If UAT is required, use the Playwright MCP tools available to the test role. Do not spawn or wake a separate UAT agent.\n8. Record automated gates and browser UAT as separate verdicts. The top-level status is ONLY the configured automated-gate result. When UAT is required, add uatStatus and uatNotes; omit both when UAT is not required. FIRST write .pan/test/result.json so the pipeline can recover both verdicts if the signal is interrupted:\n {\"status\":\"passed\",\"notes\":\"<automated gate evidence>\",\"uatStatus\":\"passed\",\"uatNotes\":\"<browser paths and evidence>\"}\n Allowed values for status and uatStatus are \"passed\" or \"failed\". A required UAT that cannot run or leaves any criterion unproven is uatStatus \"failed\", even when status is \"passed\". Create .pan/test/ if needed and write this artifact BEFORE signaling the verdict.\n9. Signal the same separate verdicts through the local trusted CLI, never by an unauthenticated HTTP request. Examples:\n Automated gates pass and required UAT passes:\n pan admin specialists done test ${options.issueId} --status passed --notes \"<automated gate evidence>\" --uat-status passed --uat-notes \"<browser evidence>\"\n Automated gates pass but required UAT fails or cannot run:\n pan admin specialists done test ${options.issueId} --status passed --notes \"<automated gate evidence>\" --uat-status failed --uat-notes \"<blocking condition and exact unmet criteria>\"\n Automated gates fail (include UAT flags too if UAT was attempted):\n pan admin specialists done test ${options.issueId} --status failed --notes \"<failing commands and output>\"\n10. Make exactly ONE CLI signal attempt. If it fails, the .pan/test/result.json artifact from step 8 is the durable verdict and the deacon recovers from it — do NOT retry the signal in a loop. Report the failure in your summary and stop.\n11. Report TESTS PASSED only when automated gates and required UAT passed. Otherwise report TESTS FAILED with commands run, UAT paths exercised, and concise evidence.\n\nBoundaries:\n- Do NOT edit code, tests, fixtures, snapshots, or configuration.\n- Do NOT commit, push, merge, close issues, or call any merge endpoint.\n- Do NOT spawn, wake, or delegate to test-agent or uat-agent specialists.`;\n}async function dispatchTestAgentAndNotifyPromise(\n issueId: string,\n workspace?: string,\n branch?: string,\n notifyAgent?: (agentId: string, msg: string) => Promise<void>,\n): Promise<void> {\n let testTaskDelivered = false;\n\n try {\n const resolved = resolveProjectFromIssueSync(issueId);\n if (!resolved) {\n console.error(`[test-dispatch] No project configured for ${issueId} — cannot spawn test role`);\n setReviewStatusSync(issueId, {\n testStatus: 'dispatch_failed',\n testNotes: `No project configured for ${issueId}. Add it to projects.yaml.`,\n });\n return;\n }\n\n // Clear any stale verdict artifact so a previous cycle's result.json can\n // never be misread by the deacon failsafe as this dispatch's verdict (H3).\n if (workspace) clearTestVerdictArtifact(workspace);\n\n const prompt = buildTestRolePrompt({ issueId, workspace, branch });\n\n const mergedGuard = await shouldSkipDispatchAsMerged(issueId);\n if (mergedGuard.skip) {\n console.log(`[test-dispatch] Skipping test dispatch for ${issueId} — ${mergedGuard.reason}`);\n return;\n }\n\n const run = await spawnRun(issueId, 'test', {\n workspace,\n prompt,\n startedBy: 'test-agent-queue',\n });\n\n setReviewStatusSync(issueId, { testStatus: 'testing' });\n testTaskDelivered = true;\n console.log(`[test-dispatch] Started test role for ${issueId} (${run.id})`);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (msg.includes('already running')) {\n setReviewStatusSync(issueId, { testStatus: 'testing' });\n testTaskDelivered = true;\n console.log(`[test-dispatch] Test role already running for ${issueId}`);\n } else {\n console.error(`[test-dispatch] Failed to dispatch test role for ${issueId}:`, err);\n try {\n setReviewStatusSync(issueId, {\n testStatus: 'dispatch_failed',\n testNotes: `Dispatch failed: ${msg}`,\n });\n } catch (statusErr) {\n console.error(`[test-dispatch] Failed to set dispatch_failed status for ${issueId}:`, statusErr);\n }\n }\n }\n\n // Only notify work agent when test was successfully dispatched\n if (testTaskDelivered && notifyAgent) {\n try {\n await notifyAgent(\n `agent-${issueId.toLowerCase()}`,\n `REVIEW PASSED for ${issueId}. The test role has been dispatched automatically. Do NOT poll or check status — you will be notified when tests complete.`,\n );\n } catch (err) {\n console.log(\n `[test-dispatch] Could not notify work agent for ${issueId} (may not be running): ${(err as Error).message}`,\n );\n }\n }\n}\n\n// ─── Effect variant (PAN-1249) ───────────────────────────────────────────────\n\n/** A test-role dispatch error — wraps spawnRun / setReviewStatus failures. */\nexport class TestDispatchError extends Data.TaggedError('TestDispatchError')<{\n readonly issueId: string;\n readonly message: string;\n readonly cause?: unknown;\n}> {}\n\nexport interface DispatchTestAgentResult {\n readonly delivered: boolean;\n readonly notified: boolean;\n readonly runId?: string;\n readonly reason?: 'no-project' | 'already-running' | 'spawn-failed';\n}\n\n/**\n * Effect variant of {@link dispatchTestAgentAndNotify}. Same semantics as the\n * Promise version, but failures funnel through a typed error channel. The\n * `notifyAgent` step is allowed to fail without failing the outer Effect — its\n * outcome is reported via {@link DispatchTestAgentResult.notified}.\n */\nexport const dispatchTestAgentAndNotify = (\n issueId: string,\n workspace?: string,\n branch?: string,\n notifyAgent?: (agentId: string, msg: string) => Promise<void>,\n): Effect.Effect<DispatchTestAgentResult> =>\n Effect.gen(function* () {\n const resolved = yield* Effect.sync(() => resolveProjectFromIssueSync(issueId));\n if (!resolved) {\n yield* Effect.sync(() => {\n console.error(`[test-dispatch] No project configured for ${issueId} — cannot spawn test role`);\n setReviewStatusSync(issueId, {\n testStatus: 'dispatch_failed',\n testNotes: `No project configured for ${issueId}. Add it to projects.yaml.`,\n });\n });\n return { delivered: false, notified: false, reason: 'no-project' as const };\n }\n\n // Clear any stale verdict artifact so a previous cycle's result.json can\n // never be misread by the deacon failsafe as this dispatch's verdict (H3).\n if (workspace) yield* Effect.sync(() => clearTestVerdictArtifact(workspace));\n\n const prompt = buildTestRolePrompt({ issueId, workspace, branch });\n\n const mergedGuard = yield* Effect.promise(() => shouldSkipDispatchAsMerged(issueId));\n if (mergedGuard.skip) {\n console.log(`[test-dispatch] Skipping test dispatch for ${issueId} — ${mergedGuard.reason}`);\n return { delivered: false, notified: false, reason: 'spawn-failed' };\n }\n\n const spawnProgram: Effect.Effect<DispatchTestAgentResult, never> = Effect.tryPromise({\n try: () => spawnRun(issueId, 'test', { workspace, prompt, startedBy: 'test-agent-queue' }),\n catch: (cause) => {\n const msg = cause instanceof Error ? cause.message : String(cause);\n return new TestDispatchError({ issueId, message: msg, cause });\n },\n }).pipe(\n Effect.matchEffect({\n onFailure: (err: TestDispatchError) => {\n // \"already running\" is non-fatal — treat as delivered.\n if (err.message.includes('already running')) {\n return Effect.sync((): DispatchTestAgentResult => {\n setReviewStatusSync(issueId, { testStatus: 'testing' });\n console.log(`[test-dispatch] Test role already running for ${issueId}`);\n return {\n delivered: true,\n notified: false,\n reason: 'already-running',\n };\n });\n }\n return Effect.sync((): DispatchTestAgentResult => {\n console.error(`[test-dispatch] Failed to dispatch test role for ${issueId}: ${err.message}`);\n try {\n setReviewStatusSync(issueId, {\n testStatus: 'dispatch_failed',\n testNotes: `Dispatch failed: ${err.message}`,\n });\n } catch (statusErr) {\n console.error(`[test-dispatch] Failed to set dispatch_failed status for ${issueId}:`, statusErr);\n }\n return {\n delivered: false,\n notified: false,\n reason: 'spawn-failed',\n };\n });\n },\n onSuccess: (run) =>\n Effect.sync((): DispatchTestAgentResult => {\n setReviewStatusSync(issueId, { testStatus: 'testing' });\n console.log(`[test-dispatch] Started test role for ${issueId} (${run.id})`);\n return {\n delivered: true,\n notified: false,\n runId: run.id,\n };\n }),\n }),\n );\n\n const spawnResult: DispatchTestAgentResult = yield* spawnProgram;\n\n if (spawnResult.delivered && notifyAgent) {\n const notified = yield* Effect.tryPromise({\n try: () =>\n notifyAgent(\n `agent-${issueId.toLowerCase()}`,\n `REVIEW PASSED for ${issueId}. The test role has been dispatched automatically. Do NOT poll or check status — you will be notified when tests complete.`,\n ).then(() => true),\n catch: (err) => {\n console.log(\n `[test-dispatch] Could not notify work agent for ${issueId} (may not be running): ${(err as Error).message}`,\n );\n return false;\n },\n }).pipe(Effect.orElseSucceed(() => false));\n return { ...spawnResult, notified };\n }\n\n return spawnResult;\n });\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { n as __esmMin } from "./rolldown-runtime-DNCzzKnI.js";
2
2
  import { On as resolveProjectFromIssueSync, fn as init_projects } from "./config-yaml-Dce9VUd6.js";
3
- import { Gt as messageAgent, Wt as init_messaging } from "./agent-state-Ci6PhjOE.js";
3
+ import { Gt as messageAgent, Wt as init_messaging } from "./agent-state-B196fj1h.js";
4
4
  import { a as writeFeedbackFile, i as init_feedback_writer } from "./feedback-writer-lbXBFycU.js";
5
- import { n as resolveIssueFeedbackTarget, r as surfaceIssueFeedbackNeedsYou, t as init_feedback_target } from "./feedback-target-BcsA_GRV.js";
5
+ import { n as resolveIssueFeedbackTarget, r as surfaceIssueFeedbackNeedsYou, t as init_feedback_target } from "./feedback-target-CeCq6EhW.js";
6
6
  import { join } from "node:path";
7
7
  import { Effect } from "effect";
8
8
  //#region ../../lib/cloister/uat-failure-feedback.ts
@@ -133,4 +133,4 @@ __esmMin((() => {
133
133
  }))();
134
134
  export { clearUatFailureFeedbackAnchor, relayUatFailureFeedbackPromise };
135
135
 
136
- //# sourceMappingURL=uat-failure-feedback-DS6W9X3I.js.map
136
+ //# sourceMappingURL=uat-failure-feedback-B-1mL8Zp.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"uat-failure-feedback-DS6W9X3I.js","names":[],"sources":["../../src/lib/cloister/uat-failure-feedback.ts"],"sourcesContent":["/**\n * UAT failure feedback relay (PAN-3575).\n *\n * A failed UAT verdict blocks merge, so it must use the same feedback-target\n * door as review and verification failures to start work-agent rework or\n * surface a durable needs-you escalation.\n */\n\nimport { join } from 'node:path';\nimport { Effect } from 'effect';\nimport { messageAgent } from '../agents/messaging.js';\nimport { resolveProjectFromIssueSync } from '../projects.js';\nimport { writeFeedbackFile } from './feedback-writer.js';\nimport { resolveIssueFeedbackTarget, surfaceIssueFeedbackNeedsYou } from './feedback-target.js';\n\nexport interface UatFailureFeedbackOptions {\n issueId: string;\n uatNotes?: string;\n workspacePath?: string;\n /** Stable UAT verdict identity used to suppress duplicate status posts. */\n anchor?: string;\n}\n\nexport interface UatFailureFeedbackResult {\n feedbackPath?: string;\n agentMessageSent: boolean;\n needsYouSurfaced: boolean;\n deduplicated: boolean;\n}\n\n/** Bound process-local dedup state even if terminal cleanup is delayed. */\nexport const MAX_UAT_FAILURE_FEEDBACK_ANCHORS = 256;\nconst lastNotifiedAnchor = new Map<string, string | undefined>();\n\nfunction rememberUatFailureFeedbackAnchor(issueId: string, anchor: string | undefined): void {\n // Refresh matching entries so actively failing issues are retained under LRU eviction.\n lastNotifiedAnchor.delete(issueId);\n lastNotifiedAnchor.set(issueId, anchor);\n while (lastNotifiedAnchor.size > MAX_UAT_FAILURE_FEEDBACK_ANCHORS) {\n const oldestIssueId = lastNotifiedAnchor.keys().next().value;\n if (oldestIssueId === undefined) return;\n lastNotifiedAnchor.delete(oldestIssueId);\n }\n}\n\n/** Clear one UAT verdict anchor when a new UAT cycle or terminal lifecycle begins. */\nexport function clearUatFailureFeedbackAnchor(issueId: string): void {\n lastNotifiedAnchor.delete(issueId.toUpperCase());\n}\n\n/** Reset internal UAT feedback state for isolated unit tests. */\nexport function resetUatFailureFeedbackStateForTests(): void {\n lastNotifiedAnchor.clear();\n}\n\nfunction buildUatFailureFeedbackBody(issueId: string, uatNotes: string): string {\n return `# UAT FAILED for ${issueId}\n\n## Failed acceptance criteria\n\n${uatNotes}\n\n## Required action\n\nRead the failed UAT acceptance criteria above, reproduce each failure, then implement and verify the required rework before committing and pushing your fix.\n`;\n}\n\nexport async function relayUatFailureFeedbackPromise(\n opts: UatFailureFeedbackOptions,\n): Promise<UatFailureFeedbackResult> {\n const issueId = opts.issueId.toUpperCase();\n const uatNotes = opts.uatNotes?.trim() || 'No UAT notes were provided.';\n const result: UatFailureFeedbackResult = {\n agentMessageSent: false,\n needsYouSurfaced: false,\n deduplicated: false,\n };\n\n if (lastNotifiedAnchor.has(issueId) && lastNotifiedAnchor.get(issueId) === opts.anchor) {\n return { ...result, deduplicated: true };\n }\n\n const resolved = resolveProjectFromIssueSync(issueId);\n const workspacePath = opts.workspacePath\n ?? (resolved ? join(resolved.projectPath, 'workspaces', `feature-${issueId.toLowerCase()}`) : undefined);\n\n let fileResult;\n try {\n fileResult = await Effect.runPromise(writeFeedbackFile({\n issueId,\n workspacePath,\n specialist: 'uat-agent',\n outcome: 'failed',\n summary: `UAT FAILED: ${uatNotes.slice(0, 80)}`,\n markdownBody: buildUatFailureFeedbackBody(issueId, uatNotes),\n }));\n } catch (err) {\n console.warn(`[uat-failure-feedback] Failed to write feedback for ${issueId}: ${err instanceof Error ? err.message : String(err)}`);\n return result;\n }\n\n if (!fileResult.success || !fileResult.filePath) {\n console.warn(`[uat-failure-feedback] Failed to write feedback for ${issueId}: ${fileResult.error ?? 'no feedback path returned'}`);\n return result;\n }\n\n rememberUatFailureFeedbackAnchor(issueId, opts.anchor);\n result.feedbackPath = fileResult.filePath;\n\n const message = `SPECIALIST FEEDBACK: uat-agent reported UAT FAILED for ${issueId}.\n\nMUST READ: ${fileResult.filePath}\n\nUse your Read tool to open this file, read every line, then fix every failed UAT acceptance criterion. Do NOT stop at the prompt.`;\n\n let target: Awaited<ReturnType<typeof resolveIssueFeedbackTarget>>;\n try {\n target = await resolveIssueFeedbackTarget(issueId);\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n console.warn(`[uat-failure-feedback] Could not resolve a feedback target for ${issueId}: ${reason}`);\n await surfaceIssueFeedbackNeedsYou(issueId, `Could not resolve UAT feedback target: ${reason}`, {\n specialist: 'uat-agent',\n feedbackPath: fileResult.filePath,\n });\n result.needsYouSurfaced = true;\n return result;\n }\n\n if (!('agentId' in target)) {\n await surfaceIssueFeedbackNeedsYou(issueId, target.reason, {\n specialist: 'uat-agent',\n feedbackPath: fileResult.filePath,\n });\n result.needsYouSurfaced = true;\n return result;\n }\n\n try {\n const outcome = await messageAgent(target.agentId, message, 'internal', { owesRework: true });\n if (outcome.delivered) {\n result.agentMessageSent = true;\n return result;\n }\n\n const reason = outcome.reason ?? 'delivery was not accepted';\n console.warn(`[uat-failure-feedback] Could not message ${target.agentId}; feedback file remains available: ${reason}`);\n await surfaceIssueFeedbackNeedsYou(issueId, `Feedback delivery to ${target.agentId} failed: ${reason}`, {\n specialist: 'uat-agent',\n feedbackPath: fileResult.filePath,\n });\n result.needsYouSurfaced = true;\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n console.warn(`[uat-failure-feedback] Could not message ${target.agentId}; feedback file remains available: ${reason}`);\n await surfaceIssueFeedbackNeedsYou(issueId, `Feedback delivery to ${target.agentId} failed: ${reason}`, {\n specialist: 'uat-agent',\n feedbackPath: fileResult.filePath,\n });\n result.needsYouSurfaced = true;\n }\n\n return result;\n}\n\n/** Effect variant of {@link relayUatFailureFeedbackPromise}. */\nexport const relayUatFailureFeedback = (\n opts: UatFailureFeedbackOptions,\n): Effect.Effect<UatFailureFeedbackResult> => Effect.promise(() => relayUatFailureFeedbackPromise(opts));\n"],"mappings":"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"}
1
+ {"version":3,"file":"uat-failure-feedback-B-1mL8Zp.js","names":[],"sources":["../../src/lib/cloister/uat-failure-feedback.ts"],"sourcesContent":["/**\n * UAT failure feedback relay (PAN-3575).\n *\n * A failed UAT verdict blocks merge, so it must use the same feedback-target\n * door as review and verification failures to start work-agent rework or\n * surface a durable needs-you escalation.\n */\n\nimport { join } from 'node:path';\nimport { Effect } from 'effect';\nimport { messageAgent } from '../agents/messaging.js';\nimport { resolveProjectFromIssueSync } from '../projects.js';\nimport { writeFeedbackFile } from './feedback-writer.js';\nimport { resolveIssueFeedbackTarget, surfaceIssueFeedbackNeedsYou } from './feedback-target.js';\n\nexport interface UatFailureFeedbackOptions {\n issueId: string;\n uatNotes?: string;\n workspacePath?: string;\n /** Stable UAT verdict identity used to suppress duplicate status posts. */\n anchor?: string;\n}\n\nexport interface UatFailureFeedbackResult {\n feedbackPath?: string;\n agentMessageSent: boolean;\n needsYouSurfaced: boolean;\n deduplicated: boolean;\n}\n\n/** Bound process-local dedup state even if terminal cleanup is delayed. */\nexport const MAX_UAT_FAILURE_FEEDBACK_ANCHORS = 256;\nconst lastNotifiedAnchor = new Map<string, string | undefined>();\n\nfunction rememberUatFailureFeedbackAnchor(issueId: string, anchor: string | undefined): void {\n // Refresh matching entries so actively failing issues are retained under LRU eviction.\n lastNotifiedAnchor.delete(issueId);\n lastNotifiedAnchor.set(issueId, anchor);\n while (lastNotifiedAnchor.size > MAX_UAT_FAILURE_FEEDBACK_ANCHORS) {\n const oldestIssueId = lastNotifiedAnchor.keys().next().value;\n if (oldestIssueId === undefined) return;\n lastNotifiedAnchor.delete(oldestIssueId);\n }\n}\n\n/** Clear one UAT verdict anchor when a new UAT cycle or terminal lifecycle begins. */\nexport function clearUatFailureFeedbackAnchor(issueId: string): void {\n lastNotifiedAnchor.delete(issueId.toUpperCase());\n}\n\n/** Reset internal UAT feedback state for isolated unit tests. */\nexport function resetUatFailureFeedbackStateForTests(): void {\n lastNotifiedAnchor.clear();\n}\n\nfunction buildUatFailureFeedbackBody(issueId: string, uatNotes: string): string {\n return `# UAT FAILED for ${issueId}\n\n## Failed acceptance criteria\n\n${uatNotes}\n\n## Required action\n\nRead the failed UAT acceptance criteria above, reproduce each failure, then implement and verify the required rework before committing and pushing your fix.\n`;\n}\n\nexport async function relayUatFailureFeedbackPromise(\n opts: UatFailureFeedbackOptions,\n): Promise<UatFailureFeedbackResult> {\n const issueId = opts.issueId.toUpperCase();\n const uatNotes = opts.uatNotes?.trim() || 'No UAT notes were provided.';\n const result: UatFailureFeedbackResult = {\n agentMessageSent: false,\n needsYouSurfaced: false,\n deduplicated: false,\n };\n\n if (lastNotifiedAnchor.has(issueId) && lastNotifiedAnchor.get(issueId) === opts.anchor) {\n return { ...result, deduplicated: true };\n }\n\n const resolved = resolveProjectFromIssueSync(issueId);\n const workspacePath = opts.workspacePath\n ?? (resolved ? join(resolved.projectPath, 'workspaces', `feature-${issueId.toLowerCase()}`) : undefined);\n\n let fileResult;\n try {\n fileResult = await Effect.runPromise(writeFeedbackFile({\n issueId,\n workspacePath,\n specialist: 'uat-agent',\n outcome: 'failed',\n summary: `UAT FAILED: ${uatNotes.slice(0, 80)}`,\n markdownBody: buildUatFailureFeedbackBody(issueId, uatNotes),\n }));\n } catch (err) {\n console.warn(`[uat-failure-feedback] Failed to write feedback for ${issueId}: ${err instanceof Error ? err.message : String(err)}`);\n return result;\n }\n\n if (!fileResult.success || !fileResult.filePath) {\n console.warn(`[uat-failure-feedback] Failed to write feedback for ${issueId}: ${fileResult.error ?? 'no feedback path returned'}`);\n return result;\n }\n\n rememberUatFailureFeedbackAnchor(issueId, opts.anchor);\n result.feedbackPath = fileResult.filePath;\n\n const message = `SPECIALIST FEEDBACK: uat-agent reported UAT FAILED for ${issueId}.\n\nMUST READ: ${fileResult.filePath}\n\nUse your Read tool to open this file, read every line, then fix every failed UAT acceptance criterion. Do NOT stop at the prompt.`;\n\n let target: Awaited<ReturnType<typeof resolveIssueFeedbackTarget>>;\n try {\n target = await resolveIssueFeedbackTarget(issueId);\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n console.warn(`[uat-failure-feedback] Could not resolve a feedback target for ${issueId}: ${reason}`);\n await surfaceIssueFeedbackNeedsYou(issueId, `Could not resolve UAT feedback target: ${reason}`, {\n specialist: 'uat-agent',\n feedbackPath: fileResult.filePath,\n });\n result.needsYouSurfaced = true;\n return result;\n }\n\n if (!('agentId' in target)) {\n await surfaceIssueFeedbackNeedsYou(issueId, target.reason, {\n specialist: 'uat-agent',\n feedbackPath: fileResult.filePath,\n });\n result.needsYouSurfaced = true;\n return result;\n }\n\n try {\n const outcome = await messageAgent(target.agentId, message, 'internal', { owesRework: true });\n if (outcome.delivered) {\n result.agentMessageSent = true;\n return result;\n }\n\n const reason = outcome.reason ?? 'delivery was not accepted';\n console.warn(`[uat-failure-feedback] Could not message ${target.agentId}; feedback file remains available: ${reason}`);\n await surfaceIssueFeedbackNeedsYou(issueId, `Feedback delivery to ${target.agentId} failed: ${reason}`, {\n specialist: 'uat-agent',\n feedbackPath: fileResult.filePath,\n });\n result.needsYouSurfaced = true;\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n console.warn(`[uat-failure-feedback] Could not message ${target.agentId}; feedback file remains available: ${reason}`);\n await surfaceIssueFeedbackNeedsYou(issueId, `Feedback delivery to ${target.agentId} failed: ${reason}`, {\n specialist: 'uat-agent',\n feedbackPath: fileResult.filePath,\n });\n result.needsYouSurfaced = true;\n }\n\n return result;\n}\n\n/** Effect variant of {@link relayUatFailureFeedbackPromise}. */\nexport const relayUatFailureFeedback = (\n opts: UatFailureFeedbackOptions,\n): Effect.Effect<UatFailureFeedbackResult> => Effect.promise(() => relayUatFailureFeedbackPromise(opts));\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { On as resolveProjectFromIssueSync, fn as init_projects } from "./config-yaml-Dce9VUd6.js";
2
2
  import { a as init_git_utils, c as rehydrateHeadAnchor } from "./git-utils-D96xW28f.js";
3
3
  import { c as init_merge_sync, j as listUatGenerationsSync } from "./merge-sync-CTGkR1_m.js";
4
- import { d as getReviewStatusSync, p as init_review_status, w as setReviewStatusSync } from "./review-status-BegAr86G.js";
4
+ import { d as getReviewStatusSync, p as init_review_status, w as setReviewStatusSync } from "./review-status-yY5qt7Rl.js";
5
5
  import { execFile } from "node:child_process";
6
6
  import { promisify } from "node:util";
7
7
  //#region ../../lib/cloister/uat-promote-verification.ts
@@ -89,4 +89,4 @@ async function healUatPromotionVerification(issueId, deps = DEFAULT_HEAL_DEPS) {
89
89
  //#endregion
90
90
  export { buildUatPromotionStamp, healUatPromotionVerification, recordUatPromotionVerdicts };
91
91
 
92
- //# sourceMappingURL=uat-promote-verification-CRsIHE_q.js.map
92
+ //# sourceMappingURL=uat-promote-verification-BEBPcpPg.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"uat-promote-verification-CRsIHE_q.js","names":[],"sources":["../../src/lib/cloister/uat-promote-verification.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nimport { rehydrateHeadAnchor } from '../git-utils.js';\nimport { listUatGenerationsSync } from '../overdeck/merge-sync.js';\nimport type { UatGeneration, UatGenerationMember } from '../overdeck/merge-types.js';\nimport { resolveProjectFromIssueSync } from '../projects.js';\nimport { getReviewStatusSync, setReviewStatusSync } from '../review-status.js';\nimport type { ReviewStatus } from '../review-status.js';\nimport type { ReviewStatusUpdate } from '../workspace-anchor-drift.js';\n\nconst execFileAsync = promisify(execFile);\n\nexport interface UatPromotionEvidence {\n generationName: string;\n mergeSha?: string;\n}\n\n/** Build the verification verdict recorded when an operator promotes a tested UAT batch. */\nexport function buildUatPromotionStamp(\n current: Pick<ReviewStatus, 'verificationStatus'> | null,\n member: UatGenerationMember,\n evidence: UatPromotionEvidence,\n): ReviewStatusUpdate {\n if (current?.verificationStatus === 'passed' || current?.verificationStatus === 'skipped') {\n return {};\n }\n\n const shortSha = evidence.mergeSha?.slice(0, 9);\n const promotedAt = shortSha ? ` promoted to main at ${shortSha}` : ' promoted to main';\n return {\n verificationStatus: 'passed',\n verificationNotes: `uat-promotion: operator UAT of batch ${evidence.generationName}${promotedAt} (PAN-3114)`,\n ...(member.headSha ? { lastVerifiedCommit: rehydrateHeadAnchor(member.headSha) } : {}),\n };\n}\n\n/** Record UAT-promotion verification verdicts through the review-status write door. */\nexport function recordUatPromotionVerdicts(gen: UatGeneration, mergeSha: string): string[] {\n const stampedIssueIds: string[] = [];\n\n for (const member of gen.members) {\n const current = getReviewStatusSync(member.issueId);\n const stamp = buildUatPromotionStamp(current, member, {\n generationName: gen.name,\n mergeSha,\n });\n if (Object.keys(stamp).length === 0) continue;\n\n setReviewStatusSync(member.issueId, stamp, current ?? undefined);\n stampedIssueIds.push(member.issueId);\n console.log(`✓ Recorded UAT-promotion verification verdict for ${member.issueId} (${gen.name} at ${mergeSha.slice(0, 9)})`);\n }\n\n return stampedIssueIds;\n}\n\nexport interface UatPromotionHealDeps {\n resolveProject(issueId: string): { projectPath: string } | null;\n listGenerations(options: { projectRoot: string; statuses: readonly ['promoted'] }): UatGeneration[];\n getReviewStatus(issueId: string): ReviewStatus | null;\n setReviewStatus(issueId: string, update: ReviewStatusUpdate, existing?: ReviewStatus): ReviewStatus;\n findMergeSha(projectRoot: string, generationName: string): Promise<string | undefined>;\n}\n\nasync function findUatPromotionMergeSha(\n projectRoot: string,\n generationName: string,\n): Promise<string | undefined> {\n try {\n const { stdout } = await execFileAsync('git', [\n 'log',\n '--fixed-strings',\n `--grep=Merge UAT batch ${generationName}`,\n '--format=%H',\n '-1',\n 'origin/main',\n ], {\n cwd: projectRoot,\n encoding: 'utf-8',\n timeout: 10_000,\n });\n return stdout.trim() || undefined;\n } catch {\n return undefined;\n }\n}\n\nconst DEFAULT_HEAL_DEPS: UatPromotionHealDeps = {\n resolveProject: resolveProjectFromIssueSync,\n listGenerations: listUatGenerationsSync,\n getReviewStatus: getReviewStatusSync,\n setReviewStatus: setReviewStatusSync,\n findMergeSha: findUatPromotionMergeSha,\n};\n\n/** Restore discarded UAT-promotion evidence for a member of a previously promoted batch. */\nexport async function healUatPromotionVerification(\n issueId: string,\n deps: UatPromotionHealDeps = DEFAULT_HEAL_DEPS,\n): Promise<{ generation: string; mergeSha?: string } | null> {\n const project = deps.resolveProject(issueId);\n if (!project) return null;\n\n const generation = deps.listGenerations({\n projectRoot: project.projectPath,\n statuses: ['promoted'],\n })\n .filter((candidate) => candidate.members.some((member) => member.issueId === issueId))\n .sort((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];\n if (!generation) return null;\n\n const member = generation.members.find((candidate) => candidate.issueId === issueId)!;\n const current = deps.getReviewStatus(issueId);\n if (current?.verificationStatus === 'passed' || current?.verificationStatus === 'skipped') {\n return null;\n }\n\n const mergeSha = await deps.findMergeSha(project.projectPath, generation.name).catch(() => undefined);\n const stamp = buildUatPromotionStamp(current, member, {\n generationName: generation.name,\n mergeSha,\n });\n deps.setReviewStatus(issueId, stamp, current ?? undefined);\n\n return {\n generation: generation.name,\n ...(mergeSha ? { mergeSha } : {}),\n };\n}\n"],"mappings":";;;;;;;gBAGsD;iBACa;eAEN;oBACkB;AAI/E,MAAM,gBAAgB,UAAU,SAAS;;AAQzC,SAAgB,uBACd,SACA,QACA,UACoB;AACpB,KAAI,SAAS,uBAAuB,YAAY,SAAS,uBAAuB,UAC9E,QAAO,EAAE;CAGX,MAAM,WAAW,SAAS,UAAU,MAAM,GAAG,EAAE;CAC/C,MAAM,aAAa,WAAW,wBAAwB,aAAa;AACnE,QAAO;EACL,oBAAoB;EACpB,mBAAmB,wCAAwC,SAAS,iBAAiB,WAAW;EAChG,GAAI,OAAO,UAAU,EAAE,oBAAoB,oBAAoB,OAAO,QAAQ,EAAE,GAAG,EAAE;EACtF;;;AAIH,SAAgB,2BAA2B,KAAoB,UAA4B;CACzF,MAAM,kBAA4B,EAAE;AAEpC,MAAK,MAAM,UAAU,IAAI,SAAS;EAChC,MAAM,UAAU,oBAAoB,OAAO,QAAQ;EACnD,MAAM,QAAQ,uBAAuB,SAAS,QAAQ;GACpD,gBAAgB,IAAI;GACpB;GACD,CAAC;AACF,MAAI,OAAO,KAAK,MAAM,CAAC,WAAW,EAAG;AAErC,sBAAoB,OAAO,SAAS,OAAO,WAAW,KAAA,EAAU;AAChE,kBAAgB,KAAK,OAAO,QAAQ;AACpC,UAAQ,IAAI,qDAAqD,OAAO,QAAQ,IAAI,IAAI,KAAK,MAAM,SAAS,MAAM,GAAG,EAAE,CAAC,GAAG;;AAG7H,QAAO;;AAWT,eAAe,yBACb,aACA,gBAC6B;AAC7B,KAAI;EACF,MAAM,EAAE,WAAW,MAAM,cAAc,OAAO;GAC5C;GACA;GACA,0BAA0B;GAC1B;GACA;GACA;GACD,EAAE;GACD,KAAK;GACL,UAAU;GACV,SAAS;GACV,CAAC;AACF,SAAO,OAAO,MAAM,IAAI,KAAA;SAClB;AACN;;;AAIJ,MAAM,oBAA0C;CAC9C,gBAAgB;CAChB,iBAAiB;CACjB,iBAAiB;CACjB,iBAAiB;CACjB,cAAc;CACf;;AAGD,eAAsB,6BACpB,SACA,OAA6B,mBAC8B;CAC3D,MAAM,UAAU,KAAK,eAAe,QAAQ;AAC5C,KAAI,CAAC,QAAS,QAAO;CAErB,MAAM,aAAa,KAAK,gBAAgB;EACtC,aAAa,QAAQ;EACrB,UAAU,CAAC,WAAW;EACvB,CAAC,CACC,QAAQ,cAAc,UAAU,QAAQ,MAAM,WAAW,OAAO,YAAY,QAAQ,CAAC,CACrF,MAAM,MAAM,UAAU,MAAM,UAAU,cAAc,KAAK,UAAU,CAAC,CAAC;AACxE,KAAI,CAAC,WAAY,QAAO;CAExB,MAAM,SAAS,WAAW,QAAQ,MAAM,cAAc,UAAU,YAAY,QAAQ;CACpF,MAAM,UAAU,KAAK,gBAAgB,QAAQ;AAC7C,KAAI,SAAS,uBAAuB,YAAY,SAAS,uBAAuB,UAC9E,QAAO;CAGT,MAAM,WAAW,MAAM,KAAK,aAAa,QAAQ,aAAa,WAAW,KAAK,CAAC,YAAY,KAAA,EAAU;CACrG,MAAM,QAAQ,uBAAuB,SAAS,QAAQ;EACpD,gBAAgB,WAAW;EAC3B;EACD,CAAC;AACF,MAAK,gBAAgB,SAAS,OAAO,WAAW,KAAA,EAAU;AAE1D,QAAO;EACL,YAAY,WAAW;EACvB,GAAI,WAAW,EAAE,UAAU,GAAG,EAAE;EACjC"}
1
+ {"version":3,"file":"uat-promote-verification-BEBPcpPg.js","names":[],"sources":["../../src/lib/cloister/uat-promote-verification.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nimport { rehydrateHeadAnchor } from '../git-utils.js';\nimport { listUatGenerationsSync } from '../overdeck/merge-sync.js';\nimport type { UatGeneration, UatGenerationMember } from '../overdeck/merge-types.js';\nimport { resolveProjectFromIssueSync } from '../projects.js';\nimport { getReviewStatusSync, setReviewStatusSync } from '../review-status.js';\nimport type { ReviewStatus } from '../review-status.js';\nimport type { ReviewStatusUpdate } from '../workspace-anchor-drift.js';\n\nconst execFileAsync = promisify(execFile);\n\nexport interface UatPromotionEvidence {\n generationName: string;\n mergeSha?: string;\n}\n\n/** Build the verification verdict recorded when an operator promotes a tested UAT batch. */\nexport function buildUatPromotionStamp(\n current: Pick<ReviewStatus, 'verificationStatus'> | null,\n member: UatGenerationMember,\n evidence: UatPromotionEvidence,\n): ReviewStatusUpdate {\n if (current?.verificationStatus === 'passed' || current?.verificationStatus === 'skipped') {\n return {};\n }\n\n const shortSha = evidence.mergeSha?.slice(0, 9);\n const promotedAt = shortSha ? ` promoted to main at ${shortSha}` : ' promoted to main';\n return {\n verificationStatus: 'passed',\n verificationNotes: `uat-promotion: operator UAT of batch ${evidence.generationName}${promotedAt} (PAN-3114)`,\n ...(member.headSha ? { lastVerifiedCommit: rehydrateHeadAnchor(member.headSha) } : {}),\n };\n}\n\n/** Record UAT-promotion verification verdicts through the review-status write door. */\nexport function recordUatPromotionVerdicts(gen: UatGeneration, mergeSha: string): string[] {\n const stampedIssueIds: string[] = [];\n\n for (const member of gen.members) {\n const current = getReviewStatusSync(member.issueId);\n const stamp = buildUatPromotionStamp(current, member, {\n generationName: gen.name,\n mergeSha,\n });\n if (Object.keys(stamp).length === 0) continue;\n\n setReviewStatusSync(member.issueId, stamp, current ?? undefined);\n stampedIssueIds.push(member.issueId);\n console.log(`✓ Recorded UAT-promotion verification verdict for ${member.issueId} (${gen.name} at ${mergeSha.slice(0, 9)})`);\n }\n\n return stampedIssueIds;\n}\n\nexport interface UatPromotionHealDeps {\n resolveProject(issueId: string): { projectPath: string } | null;\n listGenerations(options: { projectRoot: string; statuses: readonly ['promoted'] }): UatGeneration[];\n getReviewStatus(issueId: string): ReviewStatus | null;\n setReviewStatus(issueId: string, update: ReviewStatusUpdate, existing?: ReviewStatus): ReviewStatus;\n findMergeSha(projectRoot: string, generationName: string): Promise<string | undefined>;\n}\n\nasync function findUatPromotionMergeSha(\n projectRoot: string,\n generationName: string,\n): Promise<string | undefined> {\n try {\n const { stdout } = await execFileAsync('git', [\n 'log',\n '--fixed-strings',\n `--grep=Merge UAT batch ${generationName}`,\n '--format=%H',\n '-1',\n 'origin/main',\n ], {\n cwd: projectRoot,\n encoding: 'utf-8',\n timeout: 10_000,\n });\n return stdout.trim() || undefined;\n } catch {\n return undefined;\n }\n}\n\nconst DEFAULT_HEAL_DEPS: UatPromotionHealDeps = {\n resolveProject: resolveProjectFromIssueSync,\n listGenerations: listUatGenerationsSync,\n getReviewStatus: getReviewStatusSync,\n setReviewStatus: setReviewStatusSync,\n findMergeSha: findUatPromotionMergeSha,\n};\n\n/** Restore discarded UAT-promotion evidence for a member of a previously promoted batch. */\nexport async function healUatPromotionVerification(\n issueId: string,\n deps: UatPromotionHealDeps = DEFAULT_HEAL_DEPS,\n): Promise<{ generation: string; mergeSha?: string } | null> {\n const project = deps.resolveProject(issueId);\n if (!project) return null;\n\n const generation = deps.listGenerations({\n projectRoot: project.projectPath,\n statuses: ['promoted'],\n })\n .filter((candidate) => candidate.members.some((member) => member.issueId === issueId))\n .sort((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];\n if (!generation) return null;\n\n const member = generation.members.find((candidate) => candidate.issueId === issueId)!;\n const current = deps.getReviewStatus(issueId);\n if (current?.verificationStatus === 'passed' || current?.verificationStatus === 'skipped') {\n return null;\n }\n\n const mergeSha = await deps.findMergeSha(project.projectPath, generation.name).catch(() => undefined);\n const stamp = buildUatPromotionStamp(current, member, {\n generationName: generation.name,\n mergeSha,\n });\n deps.setReviewStatus(issueId, stamp, current ?? undefined);\n\n return {\n generation: generation.name,\n ...(mergeSha ? { mergeSha } : {}),\n };\n}\n"],"mappings":";;;;;;;gBAGsD;iBACa;eAEN;oBACkB;AAI/E,MAAM,gBAAgB,UAAU,SAAS;;AAQzC,SAAgB,uBACd,SACA,QACA,UACoB;AACpB,KAAI,SAAS,uBAAuB,YAAY,SAAS,uBAAuB,UAC9E,QAAO,EAAE;CAGX,MAAM,WAAW,SAAS,UAAU,MAAM,GAAG,EAAE;CAC/C,MAAM,aAAa,WAAW,wBAAwB,aAAa;AACnE,QAAO;EACL,oBAAoB;EACpB,mBAAmB,wCAAwC,SAAS,iBAAiB,WAAW;EAChG,GAAI,OAAO,UAAU,EAAE,oBAAoB,oBAAoB,OAAO,QAAQ,EAAE,GAAG,EAAE;EACtF;;;AAIH,SAAgB,2BAA2B,KAAoB,UAA4B;CACzF,MAAM,kBAA4B,EAAE;AAEpC,MAAK,MAAM,UAAU,IAAI,SAAS;EAChC,MAAM,UAAU,oBAAoB,OAAO,QAAQ;EACnD,MAAM,QAAQ,uBAAuB,SAAS,QAAQ;GACpD,gBAAgB,IAAI;GACpB;GACD,CAAC;AACF,MAAI,OAAO,KAAK,MAAM,CAAC,WAAW,EAAG;AAErC,sBAAoB,OAAO,SAAS,OAAO,WAAW,KAAA,EAAU;AAChE,kBAAgB,KAAK,OAAO,QAAQ;AACpC,UAAQ,IAAI,qDAAqD,OAAO,QAAQ,IAAI,IAAI,KAAK,MAAM,SAAS,MAAM,GAAG,EAAE,CAAC,GAAG;;AAG7H,QAAO;;AAWT,eAAe,yBACb,aACA,gBAC6B;AAC7B,KAAI;EACF,MAAM,EAAE,WAAW,MAAM,cAAc,OAAO;GAC5C;GACA;GACA,0BAA0B;GAC1B;GACA;GACA;GACD,EAAE;GACD,KAAK;GACL,UAAU;GACV,SAAS;GACV,CAAC;AACF,SAAO,OAAO,MAAM,IAAI,KAAA;SAClB;AACN;;;AAIJ,MAAM,oBAA0C;CAC9C,gBAAgB;CAChB,iBAAiB;CACjB,iBAAiB;CACjB,iBAAiB;CACjB,cAAc;CACf;;AAGD,eAAsB,6BACpB,SACA,OAA6B,mBAC8B;CAC3D,MAAM,UAAU,KAAK,eAAe,QAAQ;AAC5C,KAAI,CAAC,QAAS,QAAO;CAErB,MAAM,aAAa,KAAK,gBAAgB;EACtC,aAAa,QAAQ;EACrB,UAAU,CAAC,WAAW;EACvB,CAAC,CACC,QAAQ,cAAc,UAAU,QAAQ,MAAM,WAAW,OAAO,YAAY,QAAQ,CAAC,CACrF,MAAM,MAAM,UAAU,MAAM,UAAU,cAAc,KAAK,UAAU,CAAC,CAAC;AACxE,KAAI,CAAC,WAAY,QAAO;CAExB,MAAM,SAAS,WAAW,QAAQ,MAAM,cAAc,UAAU,YAAY,QAAQ;CACpF,MAAM,UAAU,KAAK,gBAAgB,QAAQ;AAC7C,KAAI,SAAS,uBAAuB,YAAY,SAAS,uBAAuB,UAC9E,QAAO;CAGT,MAAM,WAAW,MAAM,KAAK,aAAa,QAAQ,aAAa,WAAW,KAAK,CAAC,YAAY,KAAA,EAAU;CACrG,MAAM,QAAQ,uBAAuB,SAAS,QAAQ;EACpD,gBAAgB,WAAW;EAC3B;EACD,CAAC;AACF,MAAK,gBAAgB,SAAS,OAAO,WAAW,KAAA,EAAU;AAE1D,QAAO;EACL,YAAY,WAAW;EACvB,GAAI,WAAW,EAAE,UAAU,GAAG,EAAE;EACjC"}
@@ -1,2 +1,2 @@
1
- import { a as postUatGenerationStackPayload, c as runUatTrainReconcileAllProjects, i as postUatGenerationPromotePayload, l as startUatTrainReconciler, n as getUatCandidatePayload, o as resolveUatProjectRoot, r as getUatGenerationsPayload, s as runUatTrainReconcile, t as canStartUatTrainReconciler, u as stopUatTrainReconciler } from "./uat-train-Cd8CJeNe.js";
1
+ import { a as postUatGenerationStackPayload, c as runUatTrainReconcileAllProjects, i as postUatGenerationPromotePayload, l as startUatTrainReconciler, n as getUatCandidatePayload, o as resolveUatProjectRoot, r as getUatGenerationsPayload, s as runUatTrainReconcile, t as canStartUatTrainReconciler, u as stopUatTrainReconciler } from "./uat-train-COgoJomz.js";
2
2
  export { canStartUatTrainReconciler, getUatCandidatePayload, getUatGenerationsPayload, postUatGenerationPromotePayload, postUatGenerationStackPayload, resolveUatProjectRoot, runUatTrainReconcile, runUatTrainReconcileAllProjects, startUatTrainReconciler, stopUatTrainReconciler };
@@ -1,14 +1,14 @@
1
1
  import { $t as findProjectByPathSync, On as resolveProjectFromIssueSync, fn as init_projects, vn as listProjectsSync } from "./config-yaml-Dce9VUd6.js";
2
- import { t as getDashboardIdentity } from "./identity-Dc7rQLln.js";
2
+ import { t as getDashboardIdentity } from "./identity-Cot1Hg4G.js";
3
3
  import { C as init_tmux, U as sessionExists } from "./tmux-l-yvNWhg.js";
4
4
  import { c as init_control_settings, i as getFlywheelActiveRunId, p as isFlywheelGloballyPaused } from "./control-settings-BkaVFRdm.js";
5
- import { Gt as messageAgent, q as init_agents } from "./agent-state-Ci6PhjOE.js";
5
+ import { Gt as messageAgent, q as init_agents } from "./agent-state-B196fj1h.js";
6
6
  import { A as init_xbrief_index, M as readXBriefDocument, k as findXBriefByIssue } from "./io-Cm4Sf_6P.js";
7
7
  import { i as init_project_repos, l as resolveProjectReposFromResolvedIssueSync, o as resolveConfiguredReposSync } from "./project-repos-J6jc42uD.js";
8
8
  import { A as listUatGenerationNamesSync, D as getUatGenerationSync, I as updateUatGenerationSync, M as listUatGenerationsWithStacksSync, N as markUatGenerationRepoPromotedSync, O as hasUncleanedTerminalUatGenerationSync, P as setUatGenerationStackStartedAtSync, c as init_merge_sync, j as listUatGenerationsSync, k as insertUatGenerationSync } from "./merge-sync-CTGkR1_m.js";
9
- import { t as FLYWHEEL_ORCHESTRATOR_AGENT_ID } from "./flywheel-Is3w2lcT.js";
9
+ import { t as FLYWHEEL_ORCHESTRATOR_AGENT_ID } from "./flywheel-DKldI63J.js";
10
10
  import { t as recordDeaconNudge } from "./deacon-nudge-log-ZirGPRn1.js";
11
- import { s as listEligibleCandidatesByProject } from "./flywheel-merge-order-BsNPMu0S.js";
11
+ import { s as listEligibleCandidatesByProject } from "./flywheel-merge-order-PG5op4j7.js";
12
12
  import { t as extractACFromDocument } from "./acceptance-criteria-D-OIjvuX.js";
13
13
  import { n as ensureDevcontainerSync, r as init_ensure_devcontainer } from "./ensure-devcontainer-C2rKQXgr.js";
14
14
  import { n as loadShipRecords, r as publicShipStatus, t as aggregateGenerationShipStatus } from "./ship-status-CJvaa4QB.js";
@@ -3310,7 +3310,7 @@ async function runUatTrainReconcileForProject(projectPath, options = {}) {
3310
3310
  * Get ready set for a specific project path.
3311
3311
  */
3312
3312
  async function getReadySetForProject(projectPath) {
3313
- const { computeMergeQueueFromCandidates, listEligibleCandidatesByProject, resolveMergeQueuePrUrl } = await import("./flywheel-merge-order-6HiPR2WG.js");
3313
+ const { computeMergeQueueFromCandidates, listEligibleCandidatesByProject, resolveMergeQueuePrUrl } = await import("./flywheel-merge-order-0OnoosaO.js");
3314
3314
  const candidates = await listEligibleCandidatesByProject(projectPath);
3315
3315
  if (candidates.length === 0) return [];
3316
3316
  return (await Effect.runPromise(computeMergeQueueFromCandidates(candidates, projectPath, { getPrUrl: resolveMergeQueuePrUrl }).pipe(Effect.provide(layer)))).map((item) => ({
@@ -3348,7 +3348,7 @@ async function resolveProjectRepos(projectPath, preferredIssueId) {
3348
3348
  }
3349
3349
  /** Polyrepo ready set: per-candidate contributions across member repos. */
3350
3350
  async function getPolyrepoReadySetForProject(projectPath) {
3351
- const { computePolyrepoMergeQueueFromCandidates, listEligibleCandidatesByProject, resolveMergeQueuePrUrl } = await import("./flywheel-merge-order-6HiPR2WG.js");
3351
+ const { computePolyrepoMergeQueueFromCandidates, listEligibleCandidatesByProject, resolveMergeQueuePrUrl } = await import("./flywheel-merge-order-0OnoosaO.js");
3352
3352
  const candidates = await listEligibleCandidatesByProject(projectPath);
3353
3353
  if (candidates.length === 0) return [];
3354
3354
  const reposByIssue = new Map(candidates.map((c) => [c.issueId, resolveReposForIssue(c.issueId)]));
@@ -3669,7 +3669,7 @@ async function postUatGenerationStackPayload(name) {
3669
3669
  }
3670
3670
  async function postUatGenerationPromotePayload(name, firePostMerge, shipVersion) {
3671
3671
  const root = resolve(getUatGenerationSync(name)?.projectRoot ?? projectRoot());
3672
- const [{ reviewRecordEligibility }, { recordUatPromotionVerdicts }] = await Promise.all([import("./flywheel-merge-order-6HiPR2WG.js"), import("./uat-promote-verification-CRsIHE_q.js")]);
3672
+ const [{ reviewRecordEligibility }, { recordUatPromotionVerdicts }] = await Promise.all([import("./flywheel-merge-order-0OnoosaO.js"), import("./uat-promote-verification-BEBPcpPg.js")]);
3673
3673
  const projectConfig = findProjectByPathSync(root);
3674
3674
  const polyrepo = projectConfig?.workspace?.type === "polyrepo";
3675
3675
  const storedRepos = polyrepo ? getUatGenerationSync(name)?.repos ?? [] : [];
@@ -3716,4 +3716,4 @@ async function postUatGenerationPromotePayload(name, firePostMerge, shipVersion)
3716
3716
  //#endregion
3717
3717
  export { postUatGenerationStackPayload as a, runUatTrainReconcileAllProjects as c, postUatGenerationPromotePayload as i, startUatTrainReconciler as l, getUatCandidatePayload as n, resolveUatProjectRoot as o, getUatGenerationsPayload as r, runUatTrainReconcile as s, canStartUatTrainReconciler as t, stopUatTrainReconciler as u };
3718
3718
 
3719
- //# sourceMappingURL=uat-train-Cd8CJeNe.js.map
3719
+ //# sourceMappingURL=uat-train-COgoJomz.js.map