@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
@@ -3,8 +3,8 @@ import { C as init_tmux } from "./tmux-l-yvNWhg.js";
3
3
  import { a as init_git_utils, c as rehydrateHeadAnchor, u as snapshotWorkspaceHeadsPromise } from "./git-utils-D96xW28f.js";
4
4
  import { o as init_synthesis_verdict, s as readLatestSynthesisVerdictAsync } from "./synthesis-verdict-BtJAwMS3.js";
5
5
  import { t as getCloisterEventStore } from "./event-store-provider-B2PMoaap.js";
6
- import { t as getAgentEffectiveLastActivityMs } from "./agent-idle-CAwB-K6M.js";
7
- import { t as recordReviewVerdict } from "./review-verdict-writer-CJr_Ob0Y.js";
6
+ import { t as getAgentEffectiveLastActivityMs } from "./agent-idle-D-Qy5BN_.js";
7
+ import { t as recordReviewVerdict } from "./review-verdict-writer-DvFfKAn-.js";
8
8
  import "effect";
9
9
  import "fs";
10
10
  import "path";
@@ -170,4 +170,4 @@ function evaluateReviewConvoyLiveness(issueId, status, agents, now = Date.now())
170
170
  //#endregion
171
171
  export { observeActiveReviewArtifact as a, convergeRowFromVerdictOfRecord as i, reviewTimestampMs as n, REVIEW_SUB_ROLES as r, evaluateReviewConvoyLiveness as t };
172
172
 
173
- //# sourceMappingURL=review-convoy-liveness-BTMvU4CA.js.map
173
+ //# sourceMappingURL=review-convoy-liveness-EvCDv8Q_.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"review-convoy-liveness-BTMvU4CA.js","names":[],"sources":["../../src/lib/cloister/verdict-restore.ts","../../src/lib/cloister/review-monitor.ts","../../src/lib/cloister/review-convoy-liveness.ts"],"sourcesContent":["/**\n * Verdict artifact recovery observation (PAN-3511).\n *\n * Workspace artifacts are evidence from a host-recorded active review run. A\n * recovery path may converge that evidence only through recordReviewVerdict(),\n * which owns the terminal pipeline-state write and its head protections.\n */\nimport { emitActivityEntryOnce, type ActivityEmitOutcome, type EmitActivityOptions } from '../activity-logger.js';\nimport { rehydrateHeadAnchor, snapshotWorkspaceHeadsPromise } from '../git-utils.js';\nimport { getCloisterEventStore } from './event-store-provider.js';\nimport { recordReviewVerdict, type VerdictOutcome, type VerdictWriter } from './review-verdict-writer.js';\nimport {\n readLatestSynthesisVerdictAsync,\n SYNTHESIS_ARTIFACT_FRESH_MS,\n type SynthesisArtifactVerdict,\n} from './synthesis-verdict.js';\n\nexport type ArtifactVerdictObservationOutcome = 'no-artifact' | 'observed' | 'blocked-by-head-guard';\n\nexport type ArtifactVerdictObservationResult =\n | { outcome: 'no-artifact' }\n | { outcome: 'observed'; artifact: SynthesisArtifactVerdict }\n | { outcome: 'blocked-by-head-guard'; artifact: SynthesisArtifactVerdict };\n\nexport interface ArtifactVerdictObservationOptions {\n /** Host-recorded active review run; no run ID means no artifact is eligible. */\n runId?: string;\n workspacePath?: string;\n /** Current review-row anchor used to flag conflicting evidence. */\n rowHead?: string;\n /** Recovery path inspecting evidence, included in diagnostic events. */\n caller?: string;\n deps?: Partial<ArtifactVerdictObservationDeps>;\n}\n\nexport interface ArtifactVerdictObservationDeps {\n readArtifact(issueId: string, options: { runId?: string; workspacePath?: string }): Promise<SynthesisArtifactVerdict | null>;\n emitEvent(type: string, payload: Record<string, unknown>): void;\n emitActivity(options: EmitActivityOptions & { id: string }): Promise<ActivityEmitOutcome>;\n}\n\nexport interface VerdictOfRecordConvergenceOptions {\n /** Host-recorded active review run; no run ID means no artifact is eligible. */\n runId?: string;\n workspacePath: string;\n writer: VerdictWriter;\n /** Caller attribution that augments the artifact's report-derived notes. */\n notes?: string;\n deps?: Partial<VerdictOfRecordConvergenceDeps>;\n}\n\nexport interface VerdictOfRecordConvergenceDeps {\n readArtifact(issueId: string, options: { runId?: string; workspacePath?: string }): Promise<SynthesisArtifactVerdict | null>;\n snapshotWorkspaceHeads(issueId: string, workspacePath: string): Promise<string | undefined>;\n recordVerdict(issueId: string, input: Parameters<typeof recordReviewVerdict>[1]): Promise<VerdictOutcome>;\n}\n\nexport type VerdictOfRecordConvergenceResult =\n | { converged: false }\n | { converged: true; artifact: SynthesisArtifactVerdict; outcome: VerdictOutcome & { landed: true } };\n\n/** Flag evidence whose recorded review head conflicts with the live row anchor. */\nexport function restoreWouldTripHeadGuard(input: {\n artifactHead?: string;\n rowHead?: string;\n}): boolean {\n return !!input.artifactHead && !!input.rowHead && input.artifactHead !== input.rowHead;\n}\n\nfunction defaultEmitEvent(type: string, payload: Record<string, unknown>): void {\n try {\n getCloisterEventStore()?.append({\n type,\n timestamp: new Date().toISOString(),\n payload,\n } as Parameters<NonNullable<ReturnType<typeof getCloisterEventStore>>['append']>[0]);\n } catch (error) {\n console.warn(`[verdict-restore] failed to append ${type}:`, error instanceof Error ? error.message : String(error));\n }\n}\n\nconst DEFAULT_DEPS: ArtifactVerdictObservationDeps = {\n readArtifact: (issueId, options) => readLatestSynthesisVerdictAsync(issueId, options),\n emitEvent: defaultEmitEvent,\n emitActivity: (options) => emitActivityEntryOnce(options),\n};\n\nconst DEFAULT_CONVERGENCE_DEPS: VerdictOfRecordConvergenceDeps = {\n readArtifact: (issueId, options) => readLatestSynthesisVerdictAsync(issueId, options),\n snapshotWorkspaceHeads: snapshotWorkspaceHeadsPromise,\n recordVerdict: recordReviewVerdict,\n};\n\nasync function reportBlockedObservation(\n issueId: string,\n artifact: SynthesisArtifactVerdict,\n rowHead: string | undefined,\n caller: string,\n deps: ArtifactVerdictObservationDeps,\n): Promise<void> {\n deps.emitEvent('review.verdict_restore_blocked', {\n issueId,\n caller,\n verdict: artifact.verdict,\n artifactHead: artifact.headSha,\n rowHead,\n reason: 'artifact-head-mismatch',\n });\n await deps.emitActivity({\n id: `verdict-restore-blocked:${issueId}:${artifact.headSha ?? 'none'}:${rowHead ?? 'none'}:artifact-head-mismatch`,\n source: 'cloister',\n level: 'warn',\n issueId,\n message: `[verdict-restore] ${issueId}: preserved a fresh ${artifact.verdict.toUpperCase()} artifact from run ${artifact.runId}; its evidence head conflicts with the current review cycle.`,\n });\n}\n\n/**\n * Read the active-run artifact asynchronously without mutating review status.\n * Feedback and observability callers use this when they only need evidence.\n */\nexport function readActiveReviewArtifactAsync(\n issueId: string,\n options: Pick<ArtifactVerdictObservationOptions, 'runId' | 'workspacePath'>,\n): Promise<SynthesisArtifactVerdict | null> {\n if (!options.runId) return Promise.resolve(null);\n return readLatestSynthesisVerdictAsync(issueId, options);\n}\n\n/**\n * Converge a pending review dispatch from a fresh artifact owned by the\n * host-recorded active review run. The artifact must name the current workspace\n * head, then the verdict write door decides whether the terminal state can land.\n */\nexport async function convergeRowFromVerdictOfRecord(\n issueId: string,\n options: VerdictOfRecordConvergenceOptions,\n): Promise<VerdictOfRecordConvergenceResult> {\n if (!options.runId) return { converged: false };\n\n const deps = { ...DEFAULT_CONVERGENCE_DEPS, ...options.deps };\n const artifact = await deps.readArtifact(issueId, {\n runId: options.runId,\n workspacePath: options.workspacePath,\n });\n if (\n !artifact\n || !artifact.headSha\n || Date.now() - artifact.mtimeMs > SYNTHESIS_ARTIFACT_FRESH_MS\n ) return { converged: false };\n\n const workspaceHead = await deps.snapshotWorkspaceHeads(issueId, options.workspacePath);\n if (!workspaceHead || artifact.headSha !== workspaceHead) return { converged: false };\n\n const outcome = await deps.recordVerdict(issueId, {\n verdict: artifact.verdict,\n notes: options.notes ?? artifact.notes,\n evidenceHead: rehydrateHeadAnchor(artifact.headSha),\n runId: artifact.runId,\n writer: options.writer,\n });\n return outcome.landed\n ? { converged: true, artifact, outcome }\n : { converged: false };\n}\n\n/**\n * Preserve an active-run artifact as diagnostic evidence. A mismatch is visible\n * through `review.verdict_restore_blocked`; all outcomes leave terminal review\n * state to the canonical reviewer completion path.\n */\nexport async function observeActiveReviewArtifact(\n issueId: string,\n options: ArtifactVerdictObservationOptions,\n): Promise<ArtifactVerdictObservationResult> {\n const deps = { ...DEFAULT_DEPS, ...options.deps };\n if (!options.runId) return { outcome: 'no-artifact' };\n const artifact = await deps.readArtifact(issueId, {\n runId: options.runId,\n ...(options.workspacePath ? { workspacePath: options.workspacePath } : {}),\n });\n if (!artifact) return { outcome: 'no-artifact' };\n\n if (restoreWouldTripHeadGuard({ artifactHead: artifact.headSha, rowHead: options.rowHead })) {\n await reportBlockedObservation(issueId, artifact, options.rowHead, options.caller ?? 'unknown', deps);\n return { outcome: 'blocked-by-head-guard', artifact };\n }\n return { outcome: 'observed', artifact };\n}\n","/**\n * Review run output monitor (PAN-1059 — stall-detection bead)\n *\n * When the synthesis role spawns convoy reviewers as isolated tmux sessions\n * via spawnRun(issueId, 'review', { subRole }), it needs a way to:\n * 1. Know when each reviewer has finished (output file written)\n * 2. Detect stalls (reviewer session died or hasn't written in N minutes)\n * 3. Receive all output paths when every reviewer has settled\n *\n * waitForReviewerOutputs() implements that contract. Synthesis calls it\n * after firing all four spawnRun calls, then reads the resolved output files\n * to synthesize findings.\n */\n\nimport { existsSync } from 'fs';\nimport { stat } from 'fs/promises';\nimport { join } from 'path';\nimport { Effect, Schedule, Duration } from 'effect';\nimport { PAN_DIRNAME } from '../pan-dir/types.js';\nimport { sessionExists, isPaneDead } from '../tmux.js';\n\nexport type ReviewSubRole = 'security' | 'correctness' | 'performance' | 'requirements';\n\nexport const REVIEW_SUB_ROLES: readonly ReviewSubRole[] = [\n 'security',\n 'correctness',\n 'performance',\n 'requirements',\n];\n\nexport interface ReviewerResult {\n subRole: ReviewSubRole;\n outputPath: string;\n status: 'done' | 'stalled' | 'missing';\n}\n\nexport interface WaitForReviewerOutputsOpts {\n issueId: string;\n runId: string;\n workspace: string;\n /** Sub-roles to wait on. Defaults to all four. */\n subRoles?: readonly ReviewSubRole[];\n /**\n * How often to poll for output files, in ms. Default 10_000.\n */\n pollIntervalMs?: number;\n /**\n * Total wait budget before a reviewer is declared stalled, in ms.\n * Default 20 minutes (1_200_000).\n */\n timeoutMs?: number;\n /**\n * If a reviewer's output file has not grown for this many ms, it is\n * declared stalled even if it hasn't hit timeoutMs. Default 8 minutes.\n */\n staleAfterMs?: number;\n}\n\ninterface ReviewerState {\n subRole: ReviewSubRole;\n outputPath: string;\n sessionId: string;\n settled: boolean;\n stalledAt?: number;\n lastModifiedMs: number;\n}export async function waitForReviewerOutputsPromise(\n opts: WaitForReviewerOutputsOpts,\n): Promise<ReviewerResult[]> {\n const {\n issueId,\n runId,\n workspace,\n subRoles = REVIEW_SUB_ROLES,\n pollIntervalMs = 10_000,\n timeoutMs = 20 * 60 * 1_000,\n staleAfterMs = 8 * 60 * 1_000,\n } = opts;\n\n const reviewDir = join(workspace, PAN_DIRNAME, 'review', runId);\n const deadline = Date.now() + timeoutMs;\n\n const states: ReviewerState[] = subRoles.map((subRole) => ({\n subRole,\n outputPath: join(reviewDir, `${subRole}.md`),\n sessionId: `agent-${issueId.toLowerCase()}-review-${subRole}`,\n settled: false,\n lastModifiedMs: 0,\n }));\n\n while (true) {\n const now = Date.now();\n\n for (const s of states) {\n if (s.settled) continue;\n\n if (existsSync(s.outputPath)) {\n try {\n const mtime = (await stat(s.outputPath)).mtimeMs;\n if (mtime === s.lastModifiedMs && now - mtime > staleAfterMs) {\n // File stopped growing — reviewer is done or stuck\n s.settled = true;\n s.stalledAt = undefined;\n continue;\n }\n s.lastModifiedMs = mtime;\n } catch {\n // stat race — try again next poll\n }\n }\n\n // Check if the tmux session is dead\n try {\n const dead = await Effect.runPromise(isPaneDead(s.sessionId));\n if (dead) {\n s.settled = true;\n if (!existsSync(s.outputPath)) {\n s.stalledAt = now; // → missing\n } else if (s.lastModifiedMs > 0 && now - s.lastModifiedMs <= staleAfterMs) {\n s.stalledAt = now; // session died before file settled → stalled\n }\n // else: file exists and mtime settled → done\n continue;\n }\n } catch {\n // Session may not exist\n const exists = await Effect.runPromise(sessionExists(s.sessionId));\n if (!exists) {\n s.settled = true;\n if (!existsSync(s.outputPath)) {\n s.stalledAt = now;\n }\n }\n }\n\n // Hard timeout\n if (now >= deadline) {\n s.settled = true;\n s.stalledAt = now;\n }\n }\n\n if (states.every((s) => s.settled)) break;\n\n await new Promise<void>((resolve) => setTimeout(resolve, pollIntervalMs));\n }\n\n return states.map((s) => {\n if (!existsSync(s.outputPath)) {\n return { subRole: s.subRole, outputPath: s.outputPath, status: 'missing' as const };\n }\n if (s.stalledAt !== undefined) {\n return { subRole: s.subRole, outputPath: s.outputPath, status: 'stalled' as const };\n }\n return { subRole: s.subRole, outputPath: s.outputPath, status: 'done' as const };\n });\n}\n\n/**\n * Compute the expected output path for a convoy reviewer.\n * Synthesis writes findings here; waitForReviewerOutputs polls it.\n */\nexport function reviewerOutputPath(\n workspace: string,\n runId: string,\n subRole: ReviewSubRole,\n): string {\n return join(workspace, PAN_DIRNAME, 'review', runId, `${subRole}.md`);\n}\n\n// ─── Effect variant (PAN-1249) ───────────────────────────────────────────────\n\n/**\n * Effect variant of {@link waitForReviewerOutputs}. Drives the same poll loop\n * via Effect's `Schedule.spaced` and yields after each tick instead of using\n * raw `setTimeout`. Errors thrown by stat / tmux probes are tolerated the same\n * way as the Promise version.\n */\nexport const waitForReviewerOutputs = (\n opts: WaitForReviewerOutputsOpts,\n): Effect.Effect<ReviewerResult[]> =>\n Effect.gen(function* () {\n const {\n issueId,\n runId,\n workspace,\n subRoles = REVIEW_SUB_ROLES,\n pollIntervalMs = 10_000,\n timeoutMs = 20 * 60 * 1_000,\n staleAfterMs = 8 * 60 * 1_000,\n } = opts;\n\n const reviewDir = join(workspace, PAN_DIRNAME, 'review', runId);\n const deadline = Date.now() + timeoutMs;\n\n const states: ReviewerState[] = subRoles.map((subRole) => ({\n subRole,\n outputPath: join(reviewDir, `${subRole}.md`),\n sessionId: `agent-${issueId.toLowerCase()}-review-${subRole}`,\n settled: false,\n lastModifiedMs: 0,\n }));\n\n const tick = Effect.gen(function* () {\n const now = Date.now();\n\n for (const s of states) {\n if (s.settled) continue;\n\n if (existsSync(s.outputPath)) {\n const mtime = yield* Effect.tryPromise({\n try: () => stat(s.outputPath).then((st) => st.mtimeMs),\n catch: () => 0,\n }).pipe(Effect.orElseSucceed(() => 0));\n\n if (mtime) {\n if (mtime === s.lastModifiedMs && now - mtime > staleAfterMs) {\n s.settled = true;\n s.stalledAt = undefined;\n continue;\n }\n s.lastModifiedMs = mtime;\n }\n }\n\n const dead = yield* isPaneDead(s.sessionId).pipe(\n Effect.catch(() => Effect.succeed(null as boolean | null)),\n );\n\n if (dead === true) {\n s.settled = true;\n if (!existsSync(s.outputPath)) {\n s.stalledAt = now;\n } else if (s.lastModifiedMs > 0 && now - s.lastModifiedMs <= staleAfterMs) {\n s.stalledAt = now;\n }\n continue;\n } else if (dead === null) {\n const exists = yield* sessionExists(s.sessionId).pipe(\n Effect.catch(() => Effect.succeed(true)),\n );\n if (!exists) {\n s.settled = true;\n if (!existsSync(s.outputPath)) s.stalledAt = now;\n }\n }\n\n if (Date.now() >= deadline) {\n s.settled = true;\n s.stalledAt = Date.now();\n }\n }\n\n return states.every((s) => s.settled);\n });\n\n yield* tick.pipe(\n Effect.repeat({\n until: (done) => done,\n schedule: Schedule.spaced(Duration.millis(pollIntervalMs)),\n }),\n );\n\n return states.map((s) => {\n if (!existsSync(s.outputPath)) {\n return { subRole: s.subRole, outputPath: s.outputPath, status: 'missing' as const };\n }\n if (s.stalledAt !== undefined) {\n return { subRole: s.subRole, outputPath: s.outputPath, status: 'stalled' as const };\n }\n return { subRole: s.subRole, outputPath: s.outputPath, status: 'done' as const };\n });\n });\n","import { getAgentEffectiveLastActivityMs } from './agent-idle.js';\n\nexport const REVIEW_AGENT_IDLE_THRESHOLD_MS = 15 * 60 * 1000;\nexport const REVIEWING_WATCHDOG_THRESHOLD_MS = 45 * 60 * 1000;\n\ntype ReviewAgentRow = {\n id: string;\n issueId?: string | null;\n role?: string | null;\n status?: string | null;\n lastActivity?: string | null;\n reviewRunId?: string | null;\n};\n\nexport type ReviewConvoyLiveness = {\n active: boolean;\n reason: string;\n};\n\nexport function reviewTimestampMs(value: string | number | undefined): number {\n return value === undefined ? Number.NaN : new Date(value).getTime();\n}\n\nexport function evaluateReviewConvoyLiveness(\n issueId: string,\n status: { reviewSpawnedAt?: string | number; updatedAt?: string },\n agents: readonly ReviewAgentRow[],\n now = Date.now(),\n): ReviewConvoyLiveness {\n const issueUpper = issueId.toUpperCase();\n const reviewAgents = agents.filter((agent) => {\n const agentIssue = (agent.issueId ?? '').trim().toUpperCase();\n const role = agent.role ?? (agent.id.endsWith('-review') ? 'review' : null);\n return agentIssue === issueUpper && role === 'review';\n });\n\n const coordinatorId = `agent-${issueId.toLowerCase()}-review`;\n const coordinator = reviewAgents.find((agent) => agent.id === coordinatorId);\n if (coordinator?.status === 'stopped' || coordinator?.status === 'error') {\n return { active: false, reason: 'coordinator stopped' };\n }\n\n const reviewStartedAt = reviewTimestampMs(status.reviewSpawnedAt ?? status.updatedAt);\n if (Number.isFinite(reviewStartedAt) && now - reviewStartedAt >= REVIEWING_WATCHDOG_THRESHOLD_MS) {\n return { active: false, reason: 'review watchdog expired' };\n }\n\n const currentRunId = coordinator?.reviewRunId;\n const currentReviewAgents = currentRunId\n ? reviewAgents.filter((agent) => agent.reviewRunId === currentRunId)\n : reviewAgents;\n\n for (const agent of currentReviewAgents) {\n if (agent.status === 'stopped' || agent.status === 'error') continue;\n const persistedLastActivity = reviewTimestampMs(agent.lastActivity ?? undefined);\n if (!Number.isFinite(persistedLastActivity) || now - persistedLastActivity < REVIEW_AGENT_IDLE_THRESHOLD_MS) {\n return { active: true, reason: `active review agent ${agent.id}` };\n }\n const effectiveLastActivity = getAgentEffectiveLastActivityMs(agent.id);\n if (effectiveLastActivity !== null && now - effectiveLastActivity < REVIEW_AGENT_IDLE_THRESHOLD_MS) {\n return { active: true, reason: `active review agent ${agent.id}` };\n }\n }\n\n return { active: false, reason: reviewAgents.length === 0 ? 'no review agents' : 'all review agents stale' };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"review-convoy-liveness-EvCDv8Q_.js","names":[],"sources":["../../src/lib/cloister/verdict-restore.ts","../../src/lib/cloister/review-monitor.ts","../../src/lib/cloister/review-convoy-liveness.ts"],"sourcesContent":["/**\n * Verdict artifact recovery observation (PAN-3511).\n *\n * Workspace artifacts are evidence from a host-recorded active review run. A\n * recovery path may converge that evidence only through recordReviewVerdict(),\n * which owns the terminal pipeline-state write and its head protections.\n */\nimport { emitActivityEntryOnce, type ActivityEmitOutcome, type EmitActivityOptions } from '../activity-logger.js';\nimport { rehydrateHeadAnchor, snapshotWorkspaceHeadsPromise } from '../git-utils.js';\nimport { getCloisterEventStore } from './event-store-provider.js';\nimport { recordReviewVerdict, type VerdictOutcome, type VerdictWriter } from './review-verdict-writer.js';\nimport {\n readLatestSynthesisVerdictAsync,\n SYNTHESIS_ARTIFACT_FRESH_MS,\n type SynthesisArtifactVerdict,\n} from './synthesis-verdict.js';\n\nexport type ArtifactVerdictObservationOutcome = 'no-artifact' | 'observed' | 'blocked-by-head-guard';\n\nexport type ArtifactVerdictObservationResult =\n | { outcome: 'no-artifact' }\n | { outcome: 'observed'; artifact: SynthesisArtifactVerdict }\n | { outcome: 'blocked-by-head-guard'; artifact: SynthesisArtifactVerdict };\n\nexport interface ArtifactVerdictObservationOptions {\n /** Host-recorded active review run; no run ID means no artifact is eligible. */\n runId?: string;\n workspacePath?: string;\n /** Current review-row anchor used to flag conflicting evidence. */\n rowHead?: string;\n /** Recovery path inspecting evidence, included in diagnostic events. */\n caller?: string;\n deps?: Partial<ArtifactVerdictObservationDeps>;\n}\n\nexport interface ArtifactVerdictObservationDeps {\n readArtifact(issueId: string, options: { runId?: string; workspacePath?: string }): Promise<SynthesisArtifactVerdict | null>;\n emitEvent(type: string, payload: Record<string, unknown>): void;\n emitActivity(options: EmitActivityOptions & { id: string }): Promise<ActivityEmitOutcome>;\n}\n\nexport interface VerdictOfRecordConvergenceOptions {\n /** Host-recorded active review run; no run ID means no artifact is eligible. */\n runId?: string;\n workspacePath: string;\n writer: VerdictWriter;\n /** Caller attribution that augments the artifact's report-derived notes. */\n notes?: string;\n deps?: Partial<VerdictOfRecordConvergenceDeps>;\n}\n\nexport interface VerdictOfRecordConvergenceDeps {\n readArtifact(issueId: string, options: { runId?: string; workspacePath?: string }): Promise<SynthesisArtifactVerdict | null>;\n snapshotWorkspaceHeads(issueId: string, workspacePath: string): Promise<string | undefined>;\n recordVerdict(issueId: string, input: Parameters<typeof recordReviewVerdict>[1]): Promise<VerdictOutcome>;\n}\n\nexport type VerdictOfRecordConvergenceResult =\n | { converged: false }\n | { converged: true; artifact: SynthesisArtifactVerdict; outcome: VerdictOutcome & { landed: true } };\n\n/** Flag evidence whose recorded review head conflicts with the live row anchor. */\nexport function restoreWouldTripHeadGuard(input: {\n artifactHead?: string;\n rowHead?: string;\n}): boolean {\n return !!input.artifactHead && !!input.rowHead && input.artifactHead !== input.rowHead;\n}\n\nfunction defaultEmitEvent(type: string, payload: Record<string, unknown>): void {\n try {\n getCloisterEventStore()?.append({\n type,\n timestamp: new Date().toISOString(),\n payload,\n } as Parameters<NonNullable<ReturnType<typeof getCloisterEventStore>>['append']>[0]);\n } catch (error) {\n console.warn(`[verdict-restore] failed to append ${type}:`, error instanceof Error ? error.message : String(error));\n }\n}\n\nconst DEFAULT_DEPS: ArtifactVerdictObservationDeps = {\n readArtifact: (issueId, options) => readLatestSynthesisVerdictAsync(issueId, options),\n emitEvent: defaultEmitEvent,\n emitActivity: (options) => emitActivityEntryOnce(options),\n};\n\nconst DEFAULT_CONVERGENCE_DEPS: VerdictOfRecordConvergenceDeps = {\n readArtifact: (issueId, options) => readLatestSynthesisVerdictAsync(issueId, options),\n snapshotWorkspaceHeads: snapshotWorkspaceHeadsPromise,\n recordVerdict: recordReviewVerdict,\n};\n\nasync function reportBlockedObservation(\n issueId: string,\n artifact: SynthesisArtifactVerdict,\n rowHead: string | undefined,\n caller: string,\n deps: ArtifactVerdictObservationDeps,\n): Promise<void> {\n deps.emitEvent('review.verdict_restore_blocked', {\n issueId,\n caller,\n verdict: artifact.verdict,\n artifactHead: artifact.headSha,\n rowHead,\n reason: 'artifact-head-mismatch',\n });\n await deps.emitActivity({\n id: `verdict-restore-blocked:${issueId}:${artifact.headSha ?? 'none'}:${rowHead ?? 'none'}:artifact-head-mismatch`,\n source: 'cloister',\n level: 'warn',\n issueId,\n message: `[verdict-restore] ${issueId}: preserved a fresh ${artifact.verdict.toUpperCase()} artifact from run ${artifact.runId}; its evidence head conflicts with the current review cycle.`,\n });\n}\n\n/**\n * Read the active-run artifact asynchronously without mutating review status.\n * Feedback and observability callers use this when they only need evidence.\n */\nexport function readActiveReviewArtifactAsync(\n issueId: string,\n options: Pick<ArtifactVerdictObservationOptions, 'runId' | 'workspacePath'>,\n): Promise<SynthesisArtifactVerdict | null> {\n if (!options.runId) return Promise.resolve(null);\n return readLatestSynthesisVerdictAsync(issueId, options);\n}\n\n/**\n * Converge a pending review dispatch from a fresh artifact owned by the\n * host-recorded active review run. The artifact must name the current workspace\n * head, then the verdict write door decides whether the terminal state can land.\n */\nexport async function convergeRowFromVerdictOfRecord(\n issueId: string,\n options: VerdictOfRecordConvergenceOptions,\n): Promise<VerdictOfRecordConvergenceResult> {\n if (!options.runId) return { converged: false };\n\n const deps = { ...DEFAULT_CONVERGENCE_DEPS, ...options.deps };\n const artifact = await deps.readArtifact(issueId, {\n runId: options.runId,\n workspacePath: options.workspacePath,\n });\n if (\n !artifact\n || !artifact.headSha\n || Date.now() - artifact.mtimeMs > SYNTHESIS_ARTIFACT_FRESH_MS\n ) return { converged: false };\n\n const workspaceHead = await deps.snapshotWorkspaceHeads(issueId, options.workspacePath);\n if (!workspaceHead || artifact.headSha !== workspaceHead) return { converged: false };\n\n const outcome = await deps.recordVerdict(issueId, {\n verdict: artifact.verdict,\n notes: options.notes ?? artifact.notes,\n evidenceHead: rehydrateHeadAnchor(artifact.headSha),\n runId: artifact.runId,\n writer: options.writer,\n });\n return outcome.landed\n ? { converged: true, artifact, outcome }\n : { converged: false };\n}\n\n/**\n * Preserve an active-run artifact as diagnostic evidence. A mismatch is visible\n * through `review.verdict_restore_blocked`; all outcomes leave terminal review\n * state to the canonical reviewer completion path.\n */\nexport async function observeActiveReviewArtifact(\n issueId: string,\n options: ArtifactVerdictObservationOptions,\n): Promise<ArtifactVerdictObservationResult> {\n const deps = { ...DEFAULT_DEPS, ...options.deps };\n if (!options.runId) return { outcome: 'no-artifact' };\n const artifact = await deps.readArtifact(issueId, {\n runId: options.runId,\n ...(options.workspacePath ? { workspacePath: options.workspacePath } : {}),\n });\n if (!artifact) return { outcome: 'no-artifact' };\n\n if (restoreWouldTripHeadGuard({ artifactHead: artifact.headSha, rowHead: options.rowHead })) {\n await reportBlockedObservation(issueId, artifact, options.rowHead, options.caller ?? 'unknown', deps);\n return { outcome: 'blocked-by-head-guard', artifact };\n }\n return { outcome: 'observed', artifact };\n}\n","/**\n * Review run output monitor (PAN-1059 — stall-detection bead)\n *\n * When the synthesis role spawns convoy reviewers as isolated tmux sessions\n * via spawnRun(issueId, 'review', { subRole }), it needs a way to:\n * 1. Know when each reviewer has finished (output file written)\n * 2. Detect stalls (reviewer session died or hasn't written in N minutes)\n * 3. Receive all output paths when every reviewer has settled\n *\n * waitForReviewerOutputs() implements that contract. Synthesis calls it\n * after firing all four spawnRun calls, then reads the resolved output files\n * to synthesize findings.\n */\n\nimport { existsSync } from 'fs';\nimport { stat } from 'fs/promises';\nimport { join } from 'path';\nimport { Effect, Schedule, Duration } from 'effect';\nimport { PAN_DIRNAME } from '../pan-dir/types.js';\nimport { sessionExists, isPaneDead } from '../tmux.js';\n\nexport type ReviewSubRole = 'security' | 'correctness' | 'performance' | 'requirements';\n\nexport const REVIEW_SUB_ROLES: readonly ReviewSubRole[] = [\n 'security',\n 'correctness',\n 'performance',\n 'requirements',\n];\n\nexport interface ReviewerResult {\n subRole: ReviewSubRole;\n outputPath: string;\n status: 'done' | 'stalled' | 'missing';\n}\n\nexport interface WaitForReviewerOutputsOpts {\n issueId: string;\n runId: string;\n workspace: string;\n /** Sub-roles to wait on. Defaults to all four. */\n subRoles?: readonly ReviewSubRole[];\n /**\n * How often to poll for output files, in ms. Default 10_000.\n */\n pollIntervalMs?: number;\n /**\n * Total wait budget before a reviewer is declared stalled, in ms.\n * Default 20 minutes (1_200_000).\n */\n timeoutMs?: number;\n /**\n * If a reviewer's output file has not grown for this many ms, it is\n * declared stalled even if it hasn't hit timeoutMs. Default 8 minutes.\n */\n staleAfterMs?: number;\n}\n\ninterface ReviewerState {\n subRole: ReviewSubRole;\n outputPath: string;\n sessionId: string;\n settled: boolean;\n stalledAt?: number;\n lastModifiedMs: number;\n}export async function waitForReviewerOutputsPromise(\n opts: WaitForReviewerOutputsOpts,\n): Promise<ReviewerResult[]> {\n const {\n issueId,\n runId,\n workspace,\n subRoles = REVIEW_SUB_ROLES,\n pollIntervalMs = 10_000,\n timeoutMs = 20 * 60 * 1_000,\n staleAfterMs = 8 * 60 * 1_000,\n } = opts;\n\n const reviewDir = join(workspace, PAN_DIRNAME, 'review', runId);\n const deadline = Date.now() + timeoutMs;\n\n const states: ReviewerState[] = subRoles.map((subRole) => ({\n subRole,\n outputPath: join(reviewDir, `${subRole}.md`),\n sessionId: `agent-${issueId.toLowerCase()}-review-${subRole}`,\n settled: false,\n lastModifiedMs: 0,\n }));\n\n while (true) {\n const now = Date.now();\n\n for (const s of states) {\n if (s.settled) continue;\n\n if (existsSync(s.outputPath)) {\n try {\n const mtime = (await stat(s.outputPath)).mtimeMs;\n if (mtime === s.lastModifiedMs && now - mtime > staleAfterMs) {\n // File stopped growing — reviewer is done or stuck\n s.settled = true;\n s.stalledAt = undefined;\n continue;\n }\n s.lastModifiedMs = mtime;\n } catch {\n // stat race — try again next poll\n }\n }\n\n // Check if the tmux session is dead\n try {\n const dead = await Effect.runPromise(isPaneDead(s.sessionId));\n if (dead) {\n s.settled = true;\n if (!existsSync(s.outputPath)) {\n s.stalledAt = now; // → missing\n } else if (s.lastModifiedMs > 0 && now - s.lastModifiedMs <= staleAfterMs) {\n s.stalledAt = now; // session died before file settled → stalled\n }\n // else: file exists and mtime settled → done\n continue;\n }\n } catch {\n // Session may not exist\n const exists = await Effect.runPromise(sessionExists(s.sessionId));\n if (!exists) {\n s.settled = true;\n if (!existsSync(s.outputPath)) {\n s.stalledAt = now;\n }\n }\n }\n\n // Hard timeout\n if (now >= deadline) {\n s.settled = true;\n s.stalledAt = now;\n }\n }\n\n if (states.every((s) => s.settled)) break;\n\n await new Promise<void>((resolve) => setTimeout(resolve, pollIntervalMs));\n }\n\n return states.map((s) => {\n if (!existsSync(s.outputPath)) {\n return { subRole: s.subRole, outputPath: s.outputPath, status: 'missing' as const };\n }\n if (s.stalledAt !== undefined) {\n return { subRole: s.subRole, outputPath: s.outputPath, status: 'stalled' as const };\n }\n return { subRole: s.subRole, outputPath: s.outputPath, status: 'done' as const };\n });\n}\n\n/**\n * Compute the expected output path for a convoy reviewer.\n * Synthesis writes findings here; waitForReviewerOutputs polls it.\n */\nexport function reviewerOutputPath(\n workspace: string,\n runId: string,\n subRole: ReviewSubRole,\n): string {\n return join(workspace, PAN_DIRNAME, 'review', runId, `${subRole}.md`);\n}\n\n// ─── Effect variant (PAN-1249) ───────────────────────────────────────────────\n\n/**\n * Effect variant of {@link waitForReviewerOutputs}. Drives the same poll loop\n * via Effect's `Schedule.spaced` and yields after each tick instead of using\n * raw `setTimeout`. Errors thrown by stat / tmux probes are tolerated the same\n * way as the Promise version.\n */\nexport const waitForReviewerOutputs = (\n opts: WaitForReviewerOutputsOpts,\n): Effect.Effect<ReviewerResult[]> =>\n Effect.gen(function* () {\n const {\n issueId,\n runId,\n workspace,\n subRoles = REVIEW_SUB_ROLES,\n pollIntervalMs = 10_000,\n timeoutMs = 20 * 60 * 1_000,\n staleAfterMs = 8 * 60 * 1_000,\n } = opts;\n\n const reviewDir = join(workspace, PAN_DIRNAME, 'review', runId);\n const deadline = Date.now() + timeoutMs;\n\n const states: ReviewerState[] = subRoles.map((subRole) => ({\n subRole,\n outputPath: join(reviewDir, `${subRole}.md`),\n sessionId: `agent-${issueId.toLowerCase()}-review-${subRole}`,\n settled: false,\n lastModifiedMs: 0,\n }));\n\n const tick = Effect.gen(function* () {\n const now = Date.now();\n\n for (const s of states) {\n if (s.settled) continue;\n\n if (existsSync(s.outputPath)) {\n const mtime = yield* Effect.tryPromise({\n try: () => stat(s.outputPath).then((st) => st.mtimeMs),\n catch: () => 0,\n }).pipe(Effect.orElseSucceed(() => 0));\n\n if (mtime) {\n if (mtime === s.lastModifiedMs && now - mtime > staleAfterMs) {\n s.settled = true;\n s.stalledAt = undefined;\n continue;\n }\n s.lastModifiedMs = mtime;\n }\n }\n\n const dead = yield* isPaneDead(s.sessionId).pipe(\n Effect.catch(() => Effect.succeed(null as boolean | null)),\n );\n\n if (dead === true) {\n s.settled = true;\n if (!existsSync(s.outputPath)) {\n s.stalledAt = now;\n } else if (s.lastModifiedMs > 0 && now - s.lastModifiedMs <= staleAfterMs) {\n s.stalledAt = now;\n }\n continue;\n } else if (dead === null) {\n const exists = yield* sessionExists(s.sessionId).pipe(\n Effect.catch(() => Effect.succeed(true)),\n );\n if (!exists) {\n s.settled = true;\n if (!existsSync(s.outputPath)) s.stalledAt = now;\n }\n }\n\n if (Date.now() >= deadline) {\n s.settled = true;\n s.stalledAt = Date.now();\n }\n }\n\n return states.every((s) => s.settled);\n });\n\n yield* tick.pipe(\n Effect.repeat({\n until: (done) => done,\n schedule: Schedule.spaced(Duration.millis(pollIntervalMs)),\n }),\n );\n\n return states.map((s) => {\n if (!existsSync(s.outputPath)) {\n return { subRole: s.subRole, outputPath: s.outputPath, status: 'missing' as const };\n }\n if (s.stalledAt !== undefined) {\n return { subRole: s.subRole, outputPath: s.outputPath, status: 'stalled' as const };\n }\n return { subRole: s.subRole, outputPath: s.outputPath, status: 'done' as const };\n });\n });\n","import { getAgentEffectiveLastActivityMs } from './agent-idle.js';\n\nexport const REVIEW_AGENT_IDLE_THRESHOLD_MS = 15 * 60 * 1000;\nexport const REVIEWING_WATCHDOG_THRESHOLD_MS = 45 * 60 * 1000;\n\ntype ReviewAgentRow = {\n id: string;\n issueId?: string | null;\n role?: string | null;\n status?: string | null;\n lastActivity?: string | null;\n reviewRunId?: string | null;\n};\n\nexport type ReviewConvoyLiveness = {\n active: boolean;\n reason: string;\n};\n\nexport function reviewTimestampMs(value: string | number | undefined): number {\n return value === undefined ? Number.NaN : new Date(value).getTime();\n}\n\nexport function evaluateReviewConvoyLiveness(\n issueId: string,\n status: { reviewSpawnedAt?: string | number; updatedAt?: string },\n agents: readonly ReviewAgentRow[],\n now = Date.now(),\n): ReviewConvoyLiveness {\n const issueUpper = issueId.toUpperCase();\n const reviewAgents = agents.filter((agent) => {\n const agentIssue = (agent.issueId ?? '').trim().toUpperCase();\n const role = agent.role ?? (agent.id.endsWith('-review') ? 'review' : null);\n return agentIssue === issueUpper && role === 'review';\n });\n\n const coordinatorId = `agent-${issueId.toLowerCase()}-review`;\n const coordinator = reviewAgents.find((agent) => agent.id === coordinatorId);\n if (coordinator?.status === 'stopped' || coordinator?.status === 'error') {\n return { active: false, reason: 'coordinator stopped' };\n }\n\n const reviewStartedAt = reviewTimestampMs(status.reviewSpawnedAt ?? status.updatedAt);\n if (Number.isFinite(reviewStartedAt) && now - reviewStartedAt >= REVIEWING_WATCHDOG_THRESHOLD_MS) {\n return { active: false, reason: 'review watchdog expired' };\n }\n\n const currentRunId = coordinator?.reviewRunId;\n const currentReviewAgents = currentRunId\n ? reviewAgents.filter((agent) => agent.reviewRunId === currentRunId)\n : reviewAgents;\n\n for (const agent of currentReviewAgents) {\n if (agent.status === 'stopped' || agent.status === 'error') continue;\n const persistedLastActivity = reviewTimestampMs(agent.lastActivity ?? undefined);\n if (!Number.isFinite(persistedLastActivity) || now - persistedLastActivity < REVIEW_AGENT_IDLE_THRESHOLD_MS) {\n return { active: true, reason: `active review agent ${agent.id}` };\n }\n const effectiveLastActivity = getAgentEffectiveLastActivityMs(agent.id);\n if (effectiveLastActivity !== null && now - effectiveLastActivity < REVIEW_AGENT_IDLE_THRESHOLD_MS) {\n return { active: true, reason: `active review agent ${agent.id}` };\n }\n }\n\n return { active: false, reason: reviewAgents.length === 0 ? 'no review agents' : 'all review agents stale' };\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { t as resolveReviewParentRunState } from "./review-run-recovery-BvVG3oUr.js";
1
+ import { t as resolveReviewParentRunState } from "./review-run-recovery-DNDI58cM.js";
2
2
  export { resolveReviewParentRunState };
@@ -1,4 +1,4 @@
1
- import { _ as init_agent_state, k as saveAgentState } from "./agent-state-Ci6PhjOE.js";
1
+ import { _ as init_agent_state, k as saveAgentState } from "./agent-state-B196fj1h.js";
2
2
  import { a as PAN_DIRNAME, m as init_types } from "./types-pLpbsNto.js";
3
3
  import { join } from "node:path";
4
4
  import { Effect } from "effect";
@@ -60,4 +60,4 @@ async function resolveReviewParentRunState(parent, options = {}) {
60
60
  //#endregion
61
61
  export { resolveReviewParentRunState as t };
62
62
 
63
- //# sourceMappingURL=review-run-recovery-BvVG3oUr.js.map
63
+ //# sourceMappingURL=review-run-recovery-DNDI58cM.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"review-run-recovery-BvVG3oUr.js","names":[],"sources":["../../src/lib/cloister/review-run-recovery.ts"],"sourcesContent":["import { readdir, stat } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport { Effect } from 'effect';\n\nimport { saveAgentState, type AgentState } from '../agents/agent-state.js';\nimport { PAN_DIRNAME } from '../pan-dir/types.js';\n\nfunction isNotFound(error: unknown): boolean {\n return error instanceof Error && 'code' in error && error.code === 'ENOENT';\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch (error) {\n if (isNotFound(error)) return false;\n throw error;\n }\n}\n\nasync function findRecoverableRunId(parent: AgentState): Promise<string | null> {\n const startedAt = Date.parse(parent.startedAt);\n if (!Number.isFinite(startedAt)) return null;\n\n const reviewRoot = join(parent.workspace, PAN_DIRNAME, 'review');\n let entries;\n try {\n entries = await readdir(reviewRoot, { withFileTypes: true });\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n\n const matchingRunIds = (\n await Promise.all(entries\n .filter(entry => entry.isDirectory() && entry.name.startsWith(`${parent.id}-`))\n .map(async (entry) => {\n try {\n const runStat = await stat(join(reviewRoot, entry.name));\n return runStat.mtimeMs >= startedAt ? entry.name : null;\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n }))\n ).filter((runId): runId is string => runId !== null);\n\n return matchingRunIds.length === 1 ? matchingRunIds[0] : null;\n}\n\n/**\n * Restore a review parent's active run metadata from its durable workspace\n * artifact when the runtime registry lost the cached fields. Recovery is\n * deliberately fail-closed unless exactly one current run directory matches.\n */\nexport async function resolveReviewParentRunState(\n parent: AgentState,\n options: { persistCurrent?: boolean } = {},\n): Promise<AgentState | null> {\n if (!parent.workspace) return null;\n\n const runId = parent.reviewRunId ?? await findRecoverableRunId(parent);\n if (!runId) return null;\n\n const contextManifestPath = parent.reviewContextManifestPath\n ?? join(parent.workspace, PAN_DIRNAME, 'review', runId, 'context.json');\n const resolved = {\n ...parent,\n reviewRunId: runId,\n ...(await pathExists(contextManifestPath) ? { reviewContextManifestPath: contextManifestPath } : {}),\n };\n const changed = resolved.reviewRunId !== parent.reviewRunId\n || resolved.reviewContextManifestPath !== parent.reviewContextManifestPath;\n\n if (changed || options.persistCurrent) {\n await Effect.runPromise(saveAgentState(resolved));\n }\n\n return resolved;\n}\n"],"mappings":";;;;;;kBAK2E;YACzB;AAElD,SAAS,WAAW,OAAyB;AAC3C,QAAO,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS;;AAGrE,eAAe,WAAW,MAAgC;AACxD,KAAI;AACF,QAAM,KAAK,KAAK;AAChB,SAAO;UACA,OAAO;AACd,MAAI,WAAW,MAAM,CAAE,QAAO;AAC9B,QAAM;;;AAIV,eAAe,qBAAqB,QAA4C;CAC9E,MAAM,YAAY,KAAK,MAAM,OAAO,UAAU;AAC9C,KAAI,CAAC,OAAO,SAAS,UAAU,CAAE,QAAO;CAExC,MAAM,aAAa,KAAK,OAAO,WAAW,aAAa,SAAS;CAChE,IAAI;AACJ,KAAI;AACF,YAAU,MAAM,QAAQ,YAAY,EAAE,eAAe,MAAM,CAAC;UACrD,OAAO;AACd,MAAI,WAAW,MAAM,CAAE,QAAO;AAC9B,QAAM;;CAGR,MAAM,kBACJ,MAAM,QAAQ,IAAI,QACf,QAAO,UAAS,MAAM,aAAa,IAAI,MAAM,KAAK,WAAW,GAAG,OAAO,GAAG,GAAG,CAAC,CAC9E,IAAI,OAAO,UAAU;AACpB,MAAI;AAEF,WAAO,MADe,KAAK,KAAK,YAAY,MAAM,KAAK,CAAC,EACzC,WAAW,YAAY,MAAM,OAAO;WAC5C,OAAO;AACd,OAAI,WAAW,MAAM,CAAE,QAAO;AAC9B,SAAM;;GAER,CAAC,EACL,QAAQ,UAA2B,UAAU,KAAK;AAEpD,QAAO,eAAe,WAAW,IAAI,eAAe,KAAK;;;;;;;AAQ3D,eAAsB,4BACpB,QACA,UAAwC,EAAE,EACd;AAC5B,KAAI,CAAC,OAAO,UAAW,QAAO;CAE9B,MAAM,QAAQ,OAAO,eAAe,MAAM,qBAAqB,OAAO;AACtE,KAAI,CAAC,MAAO,QAAO;CAEnB,MAAM,sBAAsB,OAAO,6BAC9B,KAAK,OAAO,WAAA,QAAwB,UAAU,OAAO,eAAe;CACzE,MAAM,WAAW;EACf,GAAG;EACH,aAAa;EACb,GAAI,MAAM,WAAW,oBAAoB,GAAG,EAAE,2BAA2B,qBAAqB,GAAG,EAAE;EACpG;AAID,KAHgB,SAAS,gBAAgB,OAAO,eAC3C,SAAS,8BAA8B,OAAO,6BAEpC,QAAQ,eACrB,OAAM,OAAO,WAAW,eAAe,SAAS,CAAC;AAGnD,QAAO"}
1
+ {"version":3,"file":"review-run-recovery-DNDI58cM.js","names":[],"sources":["../../src/lib/cloister/review-run-recovery.ts"],"sourcesContent":["import { readdir, stat } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport { Effect } from 'effect';\n\nimport { saveAgentState, type AgentState } from '../agents/agent-state.js';\nimport { PAN_DIRNAME } from '../pan-dir/types.js';\n\nfunction isNotFound(error: unknown): boolean {\n return error instanceof Error && 'code' in error && error.code === 'ENOENT';\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch (error) {\n if (isNotFound(error)) return false;\n throw error;\n }\n}\n\nasync function findRecoverableRunId(parent: AgentState): Promise<string | null> {\n const startedAt = Date.parse(parent.startedAt);\n if (!Number.isFinite(startedAt)) return null;\n\n const reviewRoot = join(parent.workspace, PAN_DIRNAME, 'review');\n let entries;\n try {\n entries = await readdir(reviewRoot, { withFileTypes: true });\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n\n const matchingRunIds = (\n await Promise.all(entries\n .filter(entry => entry.isDirectory() && entry.name.startsWith(`${parent.id}-`))\n .map(async (entry) => {\n try {\n const runStat = await stat(join(reviewRoot, entry.name));\n return runStat.mtimeMs >= startedAt ? entry.name : null;\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n }))\n ).filter((runId): runId is string => runId !== null);\n\n return matchingRunIds.length === 1 ? matchingRunIds[0] : null;\n}\n\n/**\n * Restore a review parent's active run metadata from its durable workspace\n * artifact when the runtime registry lost the cached fields. Recovery is\n * deliberately fail-closed unless exactly one current run directory matches.\n */\nexport async function resolveReviewParentRunState(\n parent: AgentState,\n options: { persistCurrent?: boolean } = {},\n): Promise<AgentState | null> {\n if (!parent.workspace) return null;\n\n const runId = parent.reviewRunId ?? await findRecoverableRunId(parent);\n if (!runId) return null;\n\n const contextManifestPath = parent.reviewContextManifestPath\n ?? join(parent.workspace, PAN_DIRNAME, 'review', runId, 'context.json');\n const resolved = {\n ...parent,\n reviewRunId: runId,\n ...(await pathExists(contextManifestPath) ? { reviewContextManifestPath: contextManifestPath } : {}),\n };\n const changed = resolved.reviewRunId !== parent.reviewRunId\n || resolved.reviewContextManifestPath !== parent.reviewContextManifestPath;\n\n if (changed || options.persistCurrent) {\n await Effect.runPromise(saveAgentState(resolved));\n }\n\n return resolved;\n}\n"],"mappings":";;;;;;kBAK2E;YACzB;AAElD,SAAS,WAAW,OAAyB;AAC3C,QAAO,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS;;AAGrE,eAAe,WAAW,MAAgC;AACxD,KAAI;AACF,QAAM,KAAK,KAAK;AAChB,SAAO;UACA,OAAO;AACd,MAAI,WAAW,MAAM,CAAE,QAAO;AAC9B,QAAM;;;AAIV,eAAe,qBAAqB,QAA4C;CAC9E,MAAM,YAAY,KAAK,MAAM,OAAO,UAAU;AAC9C,KAAI,CAAC,OAAO,SAAS,UAAU,CAAE,QAAO;CAExC,MAAM,aAAa,KAAK,OAAO,WAAW,aAAa,SAAS;CAChE,IAAI;AACJ,KAAI;AACF,YAAU,MAAM,QAAQ,YAAY,EAAE,eAAe,MAAM,CAAC;UACrD,OAAO;AACd,MAAI,WAAW,MAAM,CAAE,QAAO;AAC9B,QAAM;;CAGR,MAAM,kBACJ,MAAM,QAAQ,IAAI,QACf,QAAO,UAAS,MAAM,aAAa,IAAI,MAAM,KAAK,WAAW,GAAG,OAAO,GAAG,GAAG,CAAC,CAC9E,IAAI,OAAO,UAAU;AACpB,MAAI;AAEF,WAAO,MADe,KAAK,KAAK,YAAY,MAAM,KAAK,CAAC,EACzC,WAAW,YAAY,MAAM,OAAO;WAC5C,OAAO;AACd,OAAI,WAAW,MAAM,CAAE,QAAO;AAC9B,SAAM;;GAER,CAAC,EACL,QAAQ,UAA2B,UAAU,KAAK;AAEpD,QAAO,eAAe,WAAW,IAAI,eAAe,KAAK;;;;;;;AAQ3D,eAAsB,4BACpB,QACA,UAAwC,EAAE,EACd;AAC5B,KAAI,CAAC,OAAO,UAAW,QAAO;CAE9B,MAAM,QAAQ,OAAO,eAAe,MAAM,qBAAqB,OAAO;AACtE,KAAI,CAAC,MAAO,QAAO;CAEnB,MAAM,sBAAsB,OAAO,6BAC9B,KAAK,OAAO,WAAA,QAAwB,UAAU,OAAO,eAAe;CACzE,MAAM,WAAW;EACf,GAAG;EACH,aAAa;EACb,GAAI,MAAM,WAAW,oBAAoB,GAAG,EAAE,2BAA2B,qBAAqB,GAAG,EAAE;EACpG;AAID,KAHgB,SAAS,gBAAgB,OAAO,eAC3C,SAAS,8BAA8B,OAAO,6BAEpC,QAAQ,eACrB,OAAM,OAAO,WAAW,eAAe,SAAS,CAAC;AAGnD,QAAO"}
@@ -1,3 +1,3 @@
1
- import { _ as markWorkspaceStuck, d as getReviewStatusSync, f as getReviewStatusesSync, h as loadReviewStatuses, i as clearReviewStatus, p as init_review_status, t as FEEDBACK_DELIVERY_STUCK_REASON, w as setReviewStatusSync } from "./review-status-BegAr86G.js";
1
+ import { _ as markWorkspaceStuck, d as getReviewStatusSync, f as getReviewStatusesSync, h as loadReviewStatuses, i as clearReviewStatus, p as init_review_status, t as FEEDBACK_DELIVERY_STUCK_REASON, w as setReviewStatusSync } from "./review-status-yY5qt7Rl.js";
2
2
  init_review_status();
3
3
  export { FEEDBACK_DELIVERY_STUCK_REASON, clearReviewStatus, getReviewStatusSync, getReviewStatusesSync, loadReviewStatuses, markWorkspaceStuck, setReviewStatusSync };
@@ -1,4 +1,4 @@
1
- import { d as getReviewStatusSync, p as init_review_status } from "./review-status-BegAr86G.js";
1
+ import { d as getReviewStatusSync, p as init_review_status } from "./review-status-yY5qt7Rl.js";
2
2
  import { t as enrichReviewStatus } from "./review-status-enrichment-DRJKuIwU.js";
3
3
  import { Effect } from "effect";
4
4
  import { isDeepStrictEqual } from "node:util";
@@ -47,4 +47,4 @@ function emitReviewStatusChanged(append, issueId, status) {
47
47
  //#endregion
48
48
  export { sameCanonicalReviewStatus as n, emitReviewStatusChanged as t };
49
49
 
50
- //# sourceMappingURL=review-status-emit-C9YW6SQL.js.map
50
+ //# sourceMappingURL=review-status-emit-CeMS31DX.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"review-status-emit-C9YW6SQL.js","names":[],"sources":["../../src/dashboard/server/review-status-emit.ts"],"sourcesContent":["import { isDeepStrictEqual } from 'node:util';\nimport { Effect } from 'effect';\nimport { enrichReviewStatus } from '../../lib/review-status-enrichment.js';\nimport { getReviewStatusSync, type ReviewStatus } from '../../lib/review-status.js';\n\n/** Minimal structural append — satisfied by both EventStore.append and the deacon event client. */\nexport type ReviewStatusEventAppend = (event: {\n type: 'review.status_changed';\n timestamp: string;\n payload: { issueId: string; status: unknown };\n}) => void;\n\nfunction canonicalStatus(status: object): Record<string, unknown> {\n const canonical = { ...status } as Record<string, unknown>;\n delete canonical.reviewCoordinatorSessionName;\n delete canonical.reviewSessionNames;\n delete canonical.reviewSubStatuses;\n // EventStore persists payloads through JSON, which removes undefined keys.\n // Normalize both sides to that same shape before comparing DB and event data.\n return JSON.parse(JSON.stringify(canonical)) as Record<string, unknown>;\n}\n\nexport function sameCanonicalReviewStatus(left: object, right: object): boolean {\n return isDeepStrictEqual(canonicalStatus(left), canonicalStatus(right));\n}\n\nexport function emitReviewStatusChanged(\n append: ReviewStatusEventAppend,\n issueId: string,\n status: ReviewStatus,\n): void {\n // PAN-2988 — durable append first, unenriched. A status transition must never\n // be lost because tmux was slow; reviewSessionNames are TerminalTabs sugar only.\n append({\n type: 'review.status_changed',\n timestamp: new Date().toISOString(),\n payload: { issueId, status },\n });\n\n // Best-effort enrichment patch. Skip it when enrichment fails or times out,\n // produces no sugar, or a newer transition lands while enrichment is running.\n void (async () => {\n const enriched = await Effect.runPromise(\n enrichReviewStatus(issueId, status).pipe(\n Effect.timeout('5 seconds'),\n Effect.catch(() => Effect.succeed(null)),\n ),\n );\n if (!enriched) {\n console.warn(\n `[pipeline] review-status enrichment timed out or failed for ${issueId}; unenriched event already appended`,\n );\n return;\n }\n\n const hasSugar =\n (enriched.reviewSessionNames?.length ?? 0) > 0 ||\n !!enriched.reviewCoordinatorSessionName ||\n !!enriched.reviewSubStatuses;\n if (!hasSugar) return;\n\n const current = getReviewStatusSync(issueId);\n if (!current || !sameCanonicalReviewStatus(current, status)) return;\n\n append({\n type: 'review.status_changed',\n timestamp: new Date().toISOString(),\n payload: { issueId, status: enriched },\n });\n })().catch((err) => {\n console.warn(`[pipeline] review-status enrichment patch failed for ${issueId}:`, err);\n });\n}\n"],"mappings":";;;;;oBAGoF;AASpF,SAAS,gBAAgB,QAAyC;CAChE,MAAM,YAAY,EAAE,GAAG,QAAQ;AAC/B,QAAO,UAAU;AACjB,QAAO,UAAU;AACjB,QAAO,UAAU;AAGjB,QAAO,KAAK,MAAM,KAAK,UAAU,UAAU,CAAC;;AAG9C,SAAgB,0BAA0B,MAAc,OAAwB;AAC9E,QAAO,kBAAkB,gBAAgB,KAAK,EAAE,gBAAgB,MAAM,CAAC;;AAGzE,SAAgB,wBACd,QACA,SACA,QACM;AAGN,QAAO;EACL,MAAM;EACN,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC,SAAS;GAAE;GAAS;GAAQ;EAC7B,CAAC;AAIF,EAAM,YAAY;EAChB,MAAM,WAAW,MAAM,OAAO,WAC5B,mBAAmB,SAAS,OAAO,CAAC,KAClC,OAAO,QAAQ,YAAY,EAC3B,OAAO,YAAY,OAAO,QAAQ,KAAK,CAAC,CACzC,CACF;AACD,MAAI,CAAC,UAAU;AACb,WAAQ,KACN,+DAA+D,QAAQ,qCACxE;AACD;;AAOF,MAAI,GAHD,SAAS,oBAAoB,UAAU,KAAK,KAC7C,CAAC,CAAC,SAAS,gCACX,CAAC,CAAC,SAAS,mBACE;EAEf,MAAM,UAAU,oBAAoB,QAAQ;AAC5C,MAAI,CAAC,WAAW,CAAC,0BAA0B,SAAS,OAAO,CAAE;AAE7D,SAAO;GACL,MAAM;GACN,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC,SAAS;IAAE;IAAS,QAAQ;IAAU;GACvC,CAAC;KACA,CAAC,OAAO,QAAQ;AAClB,UAAQ,KAAK,wDAAwD,QAAQ,IAAI,IAAI;GACrF"}
1
+ {"version":3,"file":"review-status-emit-CeMS31DX.js","names":[],"sources":["../../src/dashboard/server/review-status-emit.ts"],"sourcesContent":["import { isDeepStrictEqual } from 'node:util';\nimport { Effect } from 'effect';\nimport { enrichReviewStatus } from '../../lib/review-status-enrichment.js';\nimport { getReviewStatusSync, type ReviewStatus } from '../../lib/review-status.js';\n\n/** Minimal structural append — satisfied by both EventStore.append and the deacon event client. */\nexport type ReviewStatusEventAppend = (event: {\n type: 'review.status_changed';\n timestamp: string;\n payload: { issueId: string; status: unknown };\n}) => void;\n\nfunction canonicalStatus(status: object): Record<string, unknown> {\n const canonical = { ...status } as Record<string, unknown>;\n delete canonical.reviewCoordinatorSessionName;\n delete canonical.reviewSessionNames;\n delete canonical.reviewSubStatuses;\n // EventStore persists payloads through JSON, which removes undefined keys.\n // Normalize both sides to that same shape before comparing DB and event data.\n return JSON.parse(JSON.stringify(canonical)) as Record<string, unknown>;\n}\n\nexport function sameCanonicalReviewStatus(left: object, right: object): boolean {\n return isDeepStrictEqual(canonicalStatus(left), canonicalStatus(right));\n}\n\nexport function emitReviewStatusChanged(\n append: ReviewStatusEventAppend,\n issueId: string,\n status: ReviewStatus,\n): void {\n // PAN-2988 — durable append first, unenriched. A status transition must never\n // be lost because tmux was slow; reviewSessionNames are TerminalTabs sugar only.\n append({\n type: 'review.status_changed',\n timestamp: new Date().toISOString(),\n payload: { issueId, status },\n });\n\n // Best-effort enrichment patch. Skip it when enrichment fails or times out,\n // produces no sugar, or a newer transition lands while enrichment is running.\n void (async () => {\n const enriched = await Effect.runPromise(\n enrichReviewStatus(issueId, status).pipe(\n Effect.timeout('5 seconds'),\n Effect.catch(() => Effect.succeed(null)),\n ),\n );\n if (!enriched) {\n console.warn(\n `[pipeline] review-status enrichment timed out or failed for ${issueId}; unenriched event already appended`,\n );\n return;\n }\n\n const hasSugar =\n (enriched.reviewSessionNames?.length ?? 0) > 0 ||\n !!enriched.reviewCoordinatorSessionName ||\n !!enriched.reviewSubStatuses;\n if (!hasSugar) return;\n\n const current = getReviewStatusSync(issueId);\n if (!current || !sameCanonicalReviewStatus(current, status)) return;\n\n append({\n type: 'review.status_changed',\n timestamp: new Date().toISOString(),\n payload: { issueId, status: enriched },\n });\n })().catch((err) => {\n console.warn(`[pipeline] review-status enrichment patch failed for ${issueId}:`, err);\n });\n}\n"],"mappings":";;;;;oBAGoF;AASpF,SAAS,gBAAgB,QAAyC;CAChE,MAAM,YAAY,EAAE,GAAG,QAAQ;AAC/B,QAAO,UAAU;AACjB,QAAO,UAAU;AACjB,QAAO,UAAU;AAGjB,QAAO,KAAK,MAAM,KAAK,UAAU,UAAU,CAAC;;AAG9C,SAAgB,0BAA0B,MAAc,OAAwB;AAC9E,QAAO,kBAAkB,gBAAgB,KAAK,EAAE,gBAAgB,MAAM,CAAC;;AAGzE,SAAgB,wBACd,QACA,SACA,QACM;AAGN,QAAO;EACL,MAAM;EACN,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC,SAAS;GAAE;GAAS;GAAQ;EAC7B,CAAC;AAIF,EAAM,YAAY;EAChB,MAAM,WAAW,MAAM,OAAO,WAC5B,mBAAmB,SAAS,OAAO,CAAC,KAClC,OAAO,QAAQ,YAAY,EAC3B,OAAO,YAAY,OAAO,QAAQ,KAAK,CAAC,CACzC,CACF;AACD,MAAI,CAAC,UAAU;AACb,WAAQ,KACN,+DAA+D,QAAQ,qCACxE;AACD;;AAOF,MAAI,GAHD,SAAS,oBAAoB,UAAU,KAAK,KAC7C,CAAC,CAAC,SAAS,gCACX,CAAC,CAAC,SAAS,mBACE;EAEf,MAAM,UAAU,oBAAoB,QAAQ;AAC5C,MAAI,CAAC,WAAW,CAAC,0BAA0B,SAAS,OAAO,CAAE;AAE7D,SAAO;GACL,MAAM;GACN,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC,SAAS;IAAE;IAAS,QAAQ;IAAU;GACvC,CAAC;KACA,CAAC,OAAO,QAAQ;AAClB,UAAQ,KAAK,wDAAwD,QAAQ,IAAI,IAAI;GACrF"}
@@ -7690,7 +7690,7 @@ function resetPipelineVerdictsForWorkStartSync(issueId, options = {}) {
7690
7690
  }
7691
7691
  async function deliverUatFailureToWorkAgentHostSide(issueId, status) {
7692
7692
  try {
7693
- const { relayUatFailureFeedbackPromise } = await import("./uat-failure-feedback-DS6W9X3I.js");
7693
+ const { relayUatFailureFeedbackPromise } = await import("./uat-failure-feedback-B-1mL8Zp.js");
7694
7694
  await relayUatFailureFeedbackPromise({
7695
7695
  issueId,
7696
7696
  uatNotes: status.uatNotes,
@@ -7702,7 +7702,7 @@ async function deliverUatFailureToWorkAgentHostSide(issueId, status) {
7702
7702
  }
7703
7703
  async function clearUatFailureAnchorHostSide(issueId) {
7704
7704
  try {
7705
- const { clearUatFailureFeedbackAnchor } = await import("./uat-failure-feedback-DS6W9X3I.js");
7705
+ const { clearUatFailureFeedbackAnchor } = await import("./uat-failure-feedback-B-1mL8Zp.js");
7706
7706
  clearUatFailureFeedbackAnchor(issueId);
7707
7707
  } catch (err) {
7708
7708
  console.warn(`[review-status] host-side UAT-failure anchor clear for ${issueId} did not complete (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
@@ -7728,10 +7728,10 @@ async function deliverTestFailureToWorkAgentHostSide(issueId, status) {
7728
7728
  if (r.success) feedbackPath = r.filePath;
7729
7729
  } catch {}
7730
7730
  const message = `SPECIALIST FEEDBACK: test-agent reported FAILED for ${issueId}.\n\n${feedbackPath ? `MUST READ: ${feedbackPath}\n\n` : ""}${notes ? `${notes.slice(0, 400)}\n\n` : ""}Fix the failing tests, commit and push, then re-run pan done ${issueId}. Do NOT stop at the prompt.`;
7731
- const { resolveIssueFeedbackTarget, surfaceIssueFeedbackNeedsYou } = await import("./feedback-target-BzdJ2wIR.js");
7731
+ const { resolveIssueFeedbackTarget, surfaceIssueFeedbackNeedsYou } = await import("./feedback-target-BBCh3RRq.js");
7732
7732
  const target = await resolveIssueFeedbackTarget(issueId);
7733
7733
  if ("agentId" in target) {
7734
- const { messageAgent } = await import("./agents-BVvL4o_4.js");
7734
+ const { messageAgent } = await import("./agents-CkGhnAmu.js");
7735
7735
  await messageAgent(target.agentId, message, "internal", { owesRework: true });
7736
7736
  console.log(`[review-status] delivered test failure to ${target.agentId} for ${issueId} (host-side)`);
7737
7737
  } else await surfaceIssueFeedbackNeedsYou(issueId, target.reason, {
@@ -8001,4 +8001,4 @@ var init_review_status = __esmMin((() => {
8001
8001
  //#endregion
8002
8002
  export { resolvePipelineTelemetryContext as A, needsReviewDispatch as B, setReviewStatus as C, capturePipelineStage as D, init_review_verdict_guards as E, init_install_id as F, findBlockingFindings as G, init_review_status_reconcile as H, init_config as I, findWorkspaceVerdictConflicts as J, init_review_findings as K, resolveTelemetryEnabled as L, init_service as M, synchronizeAnalyticsServices as N, capturePipelineStageForIssue as O, getOrCreateInstallId as P, readWorkspaceVerdictFallback as Q, telemetryEnvironmentForcesOff as R, setDeaconIgnored as S, clearSupersededReviewInfrastructureFailure as T, reviewGatesPassedSync as U, MERGED_VERIFICATION_REASON as V, verificationSatisfied as W, readJournalStatus as X, init_review_status_record_sync as Y, readJournalStatusSync as Z, markWorkspaceStuck as _, clearStuckMergeStatuses as a, resetPipelineVerdictsForWorkStartSync as b, fixStuckCommentedReviews as c, getReviewStatusSync as d, getReviewStatusesSync as f, loadReviewStatusesForIssues as g, loadReviewStatuses as h, clearReviewStatus as i, getAnalyticsService as j, init_pipeline as k, fixStuckReadyForMerge as l, loadReadyForMergeFlags as m, ReviewStatusError as n, clearWorkspaceStuck as o, init_review_status as p, drainWorkspaceVerdictFallback as q, clearFeedbackDeliveryStuck as r, dispatchReviewHostSide as s, FEEDBACK_DELIVERY_STUCK_REASON as t, getReviewStatus as u, mergeGateEligibility as v, setReviewStatusSync as w, setAutoMerge as x, registerReviewVerdictFeedbackDelivery as y, init_review_dispatch_decision as z };
8003
8003
 
8004
- //# sourceMappingURL=review-status-BegAr86G.js.map
8004
+ //# sourceMappingURL=review-status-yY5qt7Rl.js.map