@overdeck/core 0.45.10 → 0.45.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/dist/{agent-state-KVbJEG5f.js → agent-state-RLhS7ckO.js} +1 -1
  2. package/dist/{agents-pp8FO7Uj.js → agents-By3u_fQ8.js} +1 -1
  3. package/dist/{agents-CQHK0CSA.js → agents-CgVr77Qq.js} +12 -12
  4. package/dist/{agents-CQHK0CSA.js.map → agents-CgVr77Qq.js.map} +1 -1
  5. package/dist/{backlog-auto-trigger-CyuoS0nl.js → backlog-auto-trigger-B_-1tk4e.js} +5 -5
  6. package/dist/{backlog-auto-trigger-CyuoS0nl.js.map → backlog-auto-trigger-B_-1tk4e.js.map} +1 -1
  7. package/dist/cli/index.js +45 -45
  8. package/dist/{cli-Cewlu3yU.js → cli-DGeSW85B.js} +2 -2
  9. package/dist/{cli-Cewlu3yU.js.map → cli-DGeSW85B.js.map} +1 -1
  10. package/dist/cli-MTB9IOoR.js +3 -0
  11. package/dist/{close-issue-BJGX9_z7.js → close-issue-Djajb6Sv.js} +2 -2
  12. package/dist/{close-issue-BJGX9_z7.js.map → close-issue-Djajb6Sv.js.map} +1 -1
  13. package/dist/{concurrency-Ca-fC7tA.js → concurrency-4rzjHAQA.js} +1 -1
  14. package/dist/{conversation-compaction-B_6s9EvM.js → conversation-compaction-BQvZh09I.js} +4 -4
  15. package/dist/{conversation-compaction-B_6s9EvM.js.map → conversation-compaction-BQvZh09I.js.map} +1 -1
  16. package/dist/dashboard/{advancing-selfheal-CMlGznKv.js → advancing-selfheal-BezNfM3P.js} +3 -3
  17. package/dist/dashboard/{advancing-selfheal-CMlGznKv.js.map → advancing-selfheal-BezNfM3P.js.map} +1 -1
  18. package/dist/dashboard/{agent-state-xk_G8t90.js → agent-state-BUANcaA7.js} +1 -1
  19. package/dist/dashboard/{agent-state-service-C1iUF9Ft.js → agent-state-service-DmFpIMbP.js} +1 -1
  20. package/dist/dashboard/{agent-state-service-5ge9xxhx.js → agent-state-service-tXUdp6UF.js} +2 -2
  21. package/dist/dashboard/{agent-state-service-5ge9xxhx.js.map → agent-state-service-tXUdp6UF.js.map} +1 -1
  22. package/dist/dashboard/{agents-BIggLvi_.js → agents-C-vqmKDi.js} +1 -1
  23. package/dist/dashboard/{agents-DHvsjsuP.js → agents-C7V4odQ6.js} +11 -11
  24. package/dist/dashboard/{agents-DHvsjsuP.js.map → agents-C7V4odQ6.js.map} +1 -1
  25. package/dist/dashboard/{backlog-auto-trigger-COm_R96V.js → backlog-auto-trigger-Cb8PR-5w.js} +2 -2
  26. package/dist/dashboard/{backlog-auto-trigger-COm_R96V.js.map → backlog-auto-trigger-Cb8PR-5w.js.map} +1 -1
  27. package/dist/dashboard/{cli-CvK9xuSS.js → cli-BgzbB6h3.js} +2 -2
  28. package/dist/dashboard/{cli-CvK9xuSS.js.map → cli-BgzbB6h3.js.map} +1 -1
  29. package/dist/dashboard/cli-DCqy_wFa.js +3 -0
  30. package/dist/dashboard/{close-issue-BnpErnD1.js → close-issue-BWf3pxn4.js} +2 -2
  31. package/dist/dashboard/{close-issue-BnpErnD1.js.map → close-issue-BWf3pxn4.js.map} +1 -1
  32. package/dist/dashboard/{closed-issue-reaper-DRqK5k39.js → closed-issue-reaper-D7XPq5pY.js} +4 -4
  33. package/dist/dashboard/{closed-issue-reaper-DRqK5k39.js.map → closed-issue-reaper-D7XPq5pY.js.map} +1 -1
  34. package/dist/dashboard/{closed-issue-reaper-BLCQwTpF.js → closed-issue-reaper-VSTSTROK.js} +1 -1
  35. package/dist/dashboard/{conflict-gate-JdFuhKuF.js → conflict-gate-DwOslTij.js} +1 -1
  36. package/dist/dashboard/{conflict-gate-BhKvrPxn.js → conflict-gate-FFEvTDVA.js} +3 -3
  37. package/dist/dashboard/{conflict-gate-BhKvrPxn.js.map → conflict-gate-FFEvTDVA.js.map} +1 -1
  38. package/dist/dashboard/{conversation-compaction-C5o_eKEm.js → conversation-compaction-CCqN4hWk.js} +4 -4
  39. package/dist/dashboard/{conversation-compaction-C5o_eKEm.js.map → conversation-compaction-CCqN4hWk.js.map} +1 -1
  40. package/dist/dashboard/{conversation-compaction-Ba5aZdrb.js → conversation-compaction-CL5Ti4j4.js} +1 -1
  41. package/dist/dashboard/{deacon-j3qSZ7--.js → deacon-CGHl90eC.js} +107 -50
  42. package/dist/dashboard/{deacon-j3qSZ7--.js.map → deacon-CGHl90eC.js.map} +1 -1
  43. package/dist/dashboard/{deacon-Cv2RMfHs.js → deacon-DMPh40Im.js} +2 -2
  44. package/dist/dashboard/deacon-swarm-CVTiNL0N.js +2 -0
  45. package/dist/dashboard/{deacon-swarm-DadWNi5J.js → deacon-swarm-aMMJTq6t.js} +4 -4
  46. package/dist/dashboard/{deacon-swarm-DadWNi5J.js.map → deacon-swarm-aMMJTq6t.js.map} +1 -1
  47. package/dist/dashboard/deacon.js +4 -4
  48. package/dist/dashboard/{feedback-target-oE4-Esxl.js → feedback-target-dJjFfITP.js} +4 -4
  49. package/dist/dashboard/{feedback-target-oE4-Esxl.js.map → feedback-target-dJjFfITP.js.map} +1 -1
  50. package/dist/dashboard/{feedback-target-C4DaDSZ8.js → feedback-target-nQSM6ygL.js} +1 -1
  51. package/dist/dashboard/{flywheel-ClMjGW_u.js → flywheel-BLoVNtJV.js} +6 -6
  52. package/dist/dashboard/{flywheel-ClMjGW_u.js.map → flywheel-BLoVNtJV.js.map} +1 -1
  53. package/dist/dashboard/{flywheel-DefHRzY4.js → flywheel-Cqf8vu7N.js} +6 -6
  54. package/dist/dashboard/{flywheel-DefHRzY4.js.map → flywheel-Cqf8vu7N.js.map} +1 -1
  55. package/dist/dashboard/{flywheel-BqDdGvrQ.js → flywheel-PJsbrXNY.js} +1 -1
  56. package/dist/dashboard/{flywheel-merge-order-CuDcqTRJ.js → flywheel-merge-order-B7BH8mZ3.js} +2 -2
  57. package/dist/dashboard/{flywheel-merge-order-CuDcqTRJ.js.map → flywheel-merge-order-B7BH8mZ3.js.map} +1 -1
  58. package/dist/dashboard/{flywheel-merge-order-B6YDSElh.js → flywheel-merge-order-CCwF60b-.js} +1 -1
  59. package/dist/dashboard/{harness-resolve-DKeSVsJL.js → harness-resolve-B3f9G1Kp.js} +2 -2
  60. package/dist/dashboard/{harness-resolve-DKeSVsJL.js.map → harness-resolve-B3f9G1Kp.js.map} +1 -1
  61. package/dist/dashboard/{inspect-agent-BKKc0XnB.js → inspect-agent-CKF5-SJr.js} +3 -3
  62. package/dist/dashboard/{inspect-agent-BKKc0XnB.js.map → inspect-agent-CKF5-SJr.js.map} +1 -1
  63. package/dist/dashboard/inspect-agent-CqpEWGWy.js +2 -0
  64. package/dist/dashboard/{issue-lifecycle-DnhQgddB.js → issue-lifecycle-5LbOcATP.js} +7 -7
  65. package/dist/dashboard/{issue-lifecycle-DnhQgddB.js.map → issue-lifecycle-5LbOcATP.js.map} +1 -1
  66. package/dist/dashboard/{issue-lifecycle-nY4esZn8.js → issue-lifecycle-CjYqQ669.js} +1 -1
  67. package/dist/dashboard/{issue-service-singleton-Bnqv1uaa.js → issue-service-singleton-B8iu_TKV.js} +1 -1
  68. package/dist/dashboard/{issue-service-singleton-_AquSBTl.js → issue-service-singleton-CDGt__oQ.js} +2 -2
  69. package/dist/dashboard/{issue-service-singleton-_AquSBTl.js.map → issue-service-singleton-CDGt__oQ.js.map} +1 -1
  70. package/dist/dashboard/{lifecycle-CGKm7doE.js → lifecycle-CEg4sHZH.js} +4 -4
  71. package/dist/dashboard/{lifecycle-CGKm7doE.js.map → lifecycle-CEg4sHZH.js.map} +1 -1
  72. package/dist/dashboard/{merge-agent-Dh8Y9PDO.js → merge-agent-7nBiCytp.js} +1 -1
  73. package/dist/dashboard/{merge-agent-BcgAYStl.js → merge-agent-C81xPIGd.js} +6 -6
  74. package/dist/dashboard/{merge-agent-BcgAYStl.js.map → merge-agent-C81xPIGd.js.map} +1 -1
  75. package/dist/dashboard/merge-ops-DE_6CnmZ.js +2 -0
  76. package/dist/dashboard/{merge-train-DmxlYbEo.js → merge-train-D3qzRccL.js} +2 -2
  77. package/dist/dashboard/{merge-train-DmxlYbEo.js.map → merge-train-D3qzRccL.js.map} +1 -1
  78. package/dist/dashboard/{merge-train-deps-DBHsOWt9.js → merge-train-deps-Be5ElC_P.js} +2 -2
  79. package/dist/dashboard/{merge-train-deps-DBHsOWt9.js.map → merge-train-deps-Be5ElC_P.js.map} +1 -1
  80. package/dist/dashboard/{orphan-proposed-reconciler-Db8tUhce.js → orphan-proposed-reconciler-Boq_XaRe.js} +3 -3
  81. package/dist/dashboard/{orphan-proposed-reconciler-Db8tUhce.js.map → orphan-proposed-reconciler-Boq_XaRe.js.map} +1 -1
  82. package/dist/dashboard/{orphan-proposed-reconciler-mXV--tcB.js → orphan-proposed-reconciler-C0sPnDq_.js} +1 -1
  83. package/dist/dashboard/{reconstruct-cache-_tDZ2D72.js → reconstruct-cache-C2pCltOq.js} +3 -3
  84. package/dist/dashboard/{reconstruct-cache-_tDZ2D72.js.map → reconstruct-cache-C2pCltOq.js.map} +1 -1
  85. package/dist/dashboard/{remote-completion-Bklh6loJ.js → remote-completion-BmxD5UMQ.js} +3 -3
  86. package/dist/dashboard/{remote-completion-Bklh6loJ.js.map → remote-completion-BmxD5UMQ.js.map} +1 -1
  87. package/dist/dashboard/resume-C-nxu9Y4.js +3 -0
  88. package/dist/dashboard/{review-agent-phzqNqIs.js → review-agent-D6P6d3pz.js} +26 -17
  89. package/dist/dashboard/review-agent-D6P6d3pz.js.map +1 -0
  90. package/dist/dashboard/review-agent-DTINEffB.js +3 -0
  91. package/dist/dashboard/{review-context-CCq56O_Y.js → review-context-Dyo0tsBa.js} +2 -2
  92. package/dist/dashboard/{review-context-CCq56O_Y.js.map → review-context-Dyo0tsBa.js.map} +1 -1
  93. package/dist/dashboard/{review-status-BQjqQXZG.js → review-status-B5ZASOo4.js} +18 -5
  94. package/dist/dashboard/review-status-B5ZASOo4.js.map +1 -0
  95. package/dist/dashboard/{review-status-bBkqUj5i.js → review-status-h7BXIA4z.js} +1 -1
  96. package/dist/dashboard/{review-verdict-feedback-wqnikhMK.js → review-verdict-feedback-B70VbfRL.js} +4 -4
  97. package/dist/dashboard/{review-verdict-feedback-wqnikhMK.js.map → review-verdict-feedback-B70VbfRL.js.map} +1 -1
  98. package/dist/dashboard/{review-verdict-feedback-cZh9Wi9G.js → review-verdict-feedback-CorgH49f.js} +1 -1
  99. package/dist/dashboard/{sequence-io-B2GmilSI.js → sequence-io-6YP8fVpH.js} +2 -2
  100. package/dist/dashboard/{sequence-io-B2GmilSI.js.map → sequence-io-6YP8fVpH.js.map} +1 -1
  101. package/dist/dashboard/{sequencer-agent-D8pW2aU_.js → sequencer-agent-ja-nkEc1.js} +5 -5
  102. package/dist/dashboard/{sequencer-agent-D8pW2aU_.js.map → sequencer-agent-ja-nkEc1.js.map} +1 -1
  103. package/dist/dashboard/server.js +66 -55
  104. package/dist/dashboard/server.js.map +1 -1
  105. package/dist/dashboard/{service-CILd-uOt.js → service-HKDrIy0h.js} +24 -24
  106. package/dist/dashboard/{service-CILd-uOt.js.map → service-HKDrIy0h.js.map} +1 -1
  107. package/dist/dashboard/{smart-compaction-Bgp_gwcA.js → smart-compaction-L0LSnF0_.js} +1 -1
  108. package/dist/dashboard/{smart-compaction-CrP90OFE.js → smart-compaction-uke1npUz.js} +2 -2
  109. package/dist/dashboard/{smart-compaction-CrP90OFE.js.map → smart-compaction-uke1npUz.js.map} +1 -1
  110. package/dist/dashboard/{spawn-planning-session-D15KbPOy.js → spawn-planning-session-CSMWco8B.js} +1 -1
  111. package/dist/dashboard/{spawn-planning-session-BmJWHSCu.js → spawn-planning-session-CXc0NkwV.js} +3 -3
  112. package/dist/dashboard/{spawn-planning-session-BmJWHSCu.js.map → spawn-planning-session-CXc0NkwV.js.map} +1 -1
  113. package/dist/dashboard/{specialist-context-CwKkcXL9.js → specialist-context-BGCJ24LH.js} +3 -3
  114. package/dist/dashboard/{specialist-context-CwKkcXL9.js.map → specialist-context-BGCJ24LH.js.map} +1 -1
  115. package/dist/dashboard/{specialist-logs-CvDxquV_.js → specialist-logs--Uz-r8ia.js} +2 -2
  116. package/dist/dashboard/{specialist-logs-CvDxquV_.js.map → specialist-logs--Uz-r8ia.js.map} +1 -1
  117. package/dist/dashboard/{specialists-BCjcG4uE.js → specialists-Ca9XSBCS.js} +1 -1
  118. package/dist/dashboard/{specialists-D07ybnam.js → specialists-DEfrAbWs.js} +36 -36
  119. package/dist/dashboard/{specialists-D07ybnam.js.map → specialists-DEfrAbWs.js.map} +1 -1
  120. package/dist/dashboard/{specialists-F2uMO4QS.js → specialists-DeZx9xom.js} +1 -1
  121. package/dist/dashboard/{specialists-BYi0k8CZ.js → specialists-nZ0mRwpZ.js} +12 -12
  122. package/dist/dashboard/{specialists-BYi0k8CZ.js.map → specialists-nZ0mRwpZ.js.map} +1 -1
  123. package/dist/dashboard/{summary-fork-DedqguZb.js → summary-fork-2a1L2DIU.js} +2 -2
  124. package/dist/dashboard/{summary-fork-EjvOkEPl.js → summary-fork-Xn0QUNmb.js} +3 -3
  125. package/dist/dashboard/{summary-fork-EjvOkEPl.js.map → summary-fork-Xn0QUNmb.js.map} +1 -1
  126. package/dist/dashboard/{teardown-workspace-5LCVbjfq.js → teardown-workspace-DhA6oAIK.js} +2 -2
  127. package/dist/dashboard/{teardown-workspace-5LCVbjfq.js.map → teardown-workspace-DhA6oAIK.js.map} +1 -1
  128. package/dist/dashboard/{test-agent-queue-qfsq6uaC.js → test-agent-queue-B_ukPyIK.js} +3 -3
  129. package/dist/dashboard/{test-agent-queue-qfsq6uaC.js.map → test-agent-queue-B_ukPyIK.js.map} +1 -1
  130. package/dist/dashboard/{uat-train-CtNGgXxj.js → uat-train-BLBAbJ8Y.js} +10 -10
  131. package/dist/dashboard/{uat-train-CtNGgXxj.js.map → uat-train-BLBAbJ8Y.js.map} +1 -1
  132. package/dist/dashboard/{uat-train-C3hWXtip.js → uat-train-DPfGR9_I.js} +1 -1
  133. package/dist/dashboard/{verification-runner-DuZRHjPU.js → verification-runner-KTGTNugM.js} +1 -1
  134. package/dist/dashboard/{verification-runner-BPAhPWR7.js → verification-runner-R2-4XgMO.js} +4 -4
  135. package/dist/dashboard/{verification-runner-BPAhPWR7.js.map → verification-runner-R2-4XgMO.js.map} +1 -1
  136. package/dist/dashboard/{workflows-H-COJ84X.js → workflows-CSKX5MhC.js} +7 -7
  137. package/dist/dashboard/{workflows-H-COJ84X.js.map → workflows-CSKX5MhC.js.map} +1 -1
  138. package/dist/dashboard/workflows-DVwXauef.js +2 -0
  139. package/dist/dashboard/{workspaces-CRU9_vRn.js → workspaces-DXLDgXo2.js} +32 -32
  140. package/dist/dashboard/{workspaces-CRU9_vRn.js.map → workspaces-DXLDgXo2.js.map} +1 -1
  141. package/dist/docs-index.sqlite +0 -0
  142. package/dist/{feedback-target-DTGAhFkM.js → feedback-target-BsKN4Tr_.js} +4 -4
  143. package/dist/{feedback-target-DTGAhFkM.js.map → feedback-target-BsKN4Tr_.js.map} +1 -1
  144. package/dist/{feedback-target-DutF088V.js → feedback-target-DM1WzOKy.js} +1 -1
  145. package/dist/{harness-resolve-Bz1prk6B.js → harness-resolve-B-YDNwNW.js} +2 -2
  146. package/dist/{harness-resolve-Bz1prk6B.js.map → harness-resolve-B-YDNwNW.js.map} +1 -1
  147. package/dist/{issue-lifecycle-fVqvCdKM.js → issue-lifecycle-Bc1l3U9f.js} +7 -7
  148. package/dist/{issue-lifecycle-fVqvCdKM.js.map → issue-lifecycle-Bc1l3U9f.js.map} +1 -1
  149. package/dist/{issue-service-singleton-CZm8p6Rj.js → issue-service-singleton-D-8Q6tdT.js} +1 -1
  150. package/dist/{issue-service-singleton-Cr73JAaR.js → issue-service-singleton-D1433I7n.js} +2 -2
  151. package/dist/{issue-service-singleton-Cr73JAaR.js.map → issue-service-singleton-D1433I7n.js.map} +1 -1
  152. package/dist/{lifecycle-CR6tZfl1.js → lifecycle-SmrqysDf.js} +7 -7
  153. package/dist/{lifecycle-CR6tZfl1.js.map → lifecycle-SmrqysDf.js.map} +1 -1
  154. package/dist/{lifecycle-D_EWiUjk.js → lifecycle-mRMyiIXs.js} +2 -2
  155. package/dist/{merge-agent-LM4avOtA.js → merge-agent-B9hIZ6Z0.js} +6 -6
  156. package/dist/{merge-agent-LM4avOtA.js.map → merge-agent-B9hIZ6Z0.js.map} +1 -1
  157. package/dist/{merge-agent-Bj046HE1.js → merge-agent-DUj22cXL.js} +1 -1
  158. package/dist/{merge-fulfillment-DrnC1Kmg.js → merge-fulfillment-__P4WE-T.js} +3 -3
  159. package/dist/{merge-fulfillment-DrnC1Kmg.js.map → merge-fulfillment-__P4WE-T.js.map} +1 -1
  160. package/dist/{reconstruct-cache-NPqoAUg-.js → reconstruct-cache-9SWp82SQ.js} +3 -3
  161. package/dist/{reconstruct-cache-NPqoAUg-.js.map → reconstruct-cache-9SWp82SQ.js.map} +1 -1
  162. package/dist/{records-BIkN1LQ4.js → records-CTmvuRzz.js} +14 -1
  163. package/dist/{records-BIkN1LQ4.js.map → records-CTmvuRzz.js.map} +1 -1
  164. package/dist/records-backfill-BQcmTmwF.js +2 -0
  165. package/dist/{records-backfill-HM0Pln6N.js → records-backfill-BrNsv_zT.js} +2 -2
  166. package/dist/{records-backfill-HM0Pln6N.js.map → records-backfill-BrNsv_zT.js.map} +1 -1
  167. package/dist/resume-Bn4kDk9x.js +3 -0
  168. package/dist/{review-agent-DFosQzCG.js → review-agent-PhqOhpx3.js} +24 -16
  169. package/dist/{review-agent-DFosQzCG.js.map → review-agent-PhqOhpx3.js.map} +1 -1
  170. package/dist/review-agent-hLbRRxZF.js +3 -0
  171. package/dist/{review-status-iJwfEoNX.js → review-status-B_XXomV0.js} +6 -6
  172. package/dist/{review-status-iJwfEoNX.js.map → review-status-B_XXomV0.js.map} +1 -1
  173. package/dist/{review-status-BbNVlXh1.js → review-status-Bo-EFVKh.js} +1 -1
  174. package/dist/{review-verdict-feedback-BGPw3z1X.js → review-verdict-feedback-CXHkoiKZ.js} +1 -1
  175. package/dist/{review-verdict-feedback-CCZ05atV.js → review-verdict-feedback-DGV13LDb.js} +4 -4
  176. package/dist/{review-verdict-feedback-CCZ05atV.js.map → review-verdict-feedback-DGV13LDb.js.map} +1 -1
  177. package/dist/{sequence-io-CcxZNHcD.js → sequence-io-Cg4NHAco.js} +1 -1
  178. package/dist/{sequence-io-Cx1usxG3.js → sequence-io-CyxvNHfA.js} +2 -2
  179. package/dist/{sequence-io-Cx1usxG3.js.map → sequence-io-CyxvNHfA.js.map} +1 -1
  180. package/dist/{smart-compaction-ByUEgQkF.js → smart-compaction-ByKXTQNy.js} +2 -2
  181. package/dist/{smart-compaction-ByUEgQkF.js.map → smart-compaction-ByKXTQNy.js.map} +1 -1
  182. package/dist/{smart-compaction-DfLe_CtH.js → smart-compaction-U6e_uB1E.js} +1 -1
  183. package/dist/{specialist-context-McuGkaeR.js → specialist-context-BUTadO-U.js} +3 -3
  184. package/dist/{specialist-context-McuGkaeR.js.map → specialist-context-BUTadO-U.js.map} +1 -1
  185. package/dist/{specialist-logs-CUUJ4eiB.js → specialist-logs-pEMCTtGZ.js} +2 -2
  186. package/dist/{specialist-logs-CUUJ4eiB.js.map → specialist-logs-pEMCTtGZ.js.map} +1 -1
  187. package/dist/{specialists-BSrylD1S.js → specialists-BGFnrQ8n.js} +1 -1
  188. package/dist/{specialists-B0elgInc.js → specialists-DeTkkp8b.js} +12 -12
  189. package/dist/{specialists-B0elgInc.js.map → specialists-DeTkkp8b.js.map} +1 -1
  190. package/dist/{summary-fork-DskMRnyb.js → summary-fork-CgzThE2-.js} +2 -2
  191. package/dist/{summary-fork-CPUTKcAT.js → summary-fork-Dx1RMuvU.js} +3 -3
  192. package/dist/{summary-fork-CPUTKcAT.js.map → summary-fork-Dx1RMuvU.js.map} +1 -1
  193. package/dist/{sync-BiNVQHBf.js → sync-Bsww9tuj.js} +2 -2
  194. package/dist/{sync-BiNVQHBf.js.map → sync-Bsww9tuj.js.map} +1 -1
  195. package/dist/sync-CylE_SjB.js +2 -0
  196. package/dist/{verdict-restore-DQiV7_fe.js → verdict-restore-DGpX0ZOJ.js} +5 -5
  197. package/dist/{verdict-restore-DQiV7_fe.js.map → verdict-restore-DGpX0ZOJ.js.map} +1 -1
  198. package/dist/{webhook-handlers-DHBSEDEm.js → webhook-handlers-BjsfIL7u.js} +3 -3
  199. package/dist/{webhook-handlers-DHBSEDEm.js.map → webhook-handlers-BjsfIL7u.js.map} +1 -1
  200. package/dist/{workspace-migrate-duUsWNpd.js → workspace-migrate-BBQ8nAnP.js} +2 -2
  201. package/dist/{workspace-migrate-duUsWNpd.js.map → workspace-migrate-BBQ8nAnP.js.map} +1 -1
  202. package/dist/{workspace-reap-DLCIoJmn.js → workspace-reap-12O9354k.js} +2 -2
  203. package/dist/{workspace-reap-DLCIoJmn.js.map → workspace-reap-12O9354k.js.map} +1 -1
  204. package/package.json +1 -1
  205. package/scripts/file-size-baseline.txt +1 -1
  206. package/dist/cli-DBWG12XK.js +0 -3
  207. package/dist/dashboard/cli-CJDWXE_Y.js +0 -3
  208. package/dist/dashboard/deacon-swarm-D1iAUX-P.js +0 -2
  209. package/dist/dashboard/inspect-agent-vm88mLYr.js +0 -2
  210. package/dist/dashboard/merge-ops-QdBiQ8XB.js +0 -2
  211. package/dist/dashboard/resume-DnIsmIgC.js +0 -3
  212. package/dist/dashboard/review-agent-DgySDb-G.js +0 -3
  213. package/dist/dashboard/review-agent-phzqNqIs.js.map +0 -1
  214. package/dist/dashboard/review-status-BQjqQXZG.js.map +0 -1
  215. package/dist/dashboard/workflows-Cz8OtiB4.js +0 -2
  216. package/dist/records-backfill-DEgtRyKL.js +0 -2
  217. package/dist/resume-DGymuXZM.js +0 -3
  218. package/dist/review-agent-DjVadURa.js +0 -3
  219. package/dist/sync-4jqukkxz.js +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"issue-lifecycle-DnhQgddB.js","names":["applyEvent","applyEventReducer","readFile","stat","dirname"],"sources":["../../src/lib/queue-position.ts","../../src/dashboard/server/read-model.ts","../../src/lib/briefing-assembler.ts","../../src/dashboard/server/services/session-context-writer.ts","../../src/dashboard/server/services/domain-services.ts","../../src/dashboard/server/services/issue-lifecycle.ts"],"sourcesContent":["/**\n * Queue position utilities for the specialist pipeline (PAN-366).\n *\n * These pure helpers are extracted from the review-status API handler so they\n * can be unit-tested independently of the Express server.\n */\n\nimport { Effect } from 'effect';\nimport type { ReviewStatus } from './review-status.js';\nimport type { HookItem } from './hooks.js';\n\n/** Sentinel value meaning the specialist is actively processing this issue now. */\nexport const SPECIALIST_ACTIVE_POSITION = 0;\n\nexport interface QueuePositionResult {\n /** null = not queued; 0 = actively processing; 1+ = position in queue */\n queuePosition: number | null;\n /** Which specialist is handling (or will handle) this issue */\n activeSpecialist: 'review' | 'test' | 'merge' | null;\n}\n\n/**\n * Derive queue position from the persisted review status fields alone.\n * Returns {0, specialist} when the status indicates active processing,\n * or {null, null} when no active phase is detected (caller must check queues).\n */\nexport function computeQueuePositionFromStatusSync(\n status: Pick<ReviewStatus, 'reviewStatus' | 'testStatus' | 'mergeStatus'> | null\n): QueuePositionResult {\n if (status?.reviewStatus === 'reviewing') {\n return { queuePosition: SPECIALIST_ACTIVE_POSITION, activeSpecialist: 'review' };\n }\n if (status?.testStatus === 'testing') {\n return { queuePosition: SPECIALIST_ACTIVE_POSITION, activeSpecialist: 'test' };\n }\n if (status?.mergeStatus === 'merging') {\n return { queuePosition: SPECIALIST_ACTIVE_POSITION, activeSpecialist: 'merge' };\n }\n return { queuePosition: null, activeSpecialist: null };\n}\n\n/**\n * Find this issueId's 1-based position in a specialist's pending queue.\n * Returns -1 if not found (caller interprets as \"not queued\").\n */\nexport function findPositionInQueueSync(issueId: string, items: HookItem[]): number {\n const upper = issueId.toUpperCase();\n const idx = items.findIndex(\n (item) => item.payload?.issueId?.toUpperCase() === upper\n );\n return idx >= 0 ? idx + 1 : -1;\n}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n\n/** Effect variant of {@link computeQueuePositionFromStatusSync}. Pure; cannot fail. */\nexport const computeQueuePositionFromStatus = (\n status: Pick<ReviewStatus, 'reviewStatus' | 'testStatus' | 'mergeStatus'> | null,\n): Effect.Effect<QueuePositionResult, never> =>\n Effect.sync(() => computeQueuePositionFromStatusSync(status));\n\n/** Effect variant of {@link findPositionInQueueSync}. Pure; cannot fail. */\nexport const findPositionInQueue = (\n issueId: string,\n items: HookItem[],\n): Effect.Effect<number, never> =>\n Effect.sync(() => findPositionInQueueSync(issueId, items));\n","/**\n * Server-side Read Model — clean data architecture (PAN-433)\n *\n * Holds an in-memory projection of the dashboard state, bootstrapped once from\n * existing lib modules (JSON-cleaned), then maintained incrementally by domain\n * events via the shared applyEvent reducer.\n *\n * getSnapshot() returns the read model directly — no lib calls, no dirty data,\n * no Schema crashes. This is the T3Code pattern.\n */\n\nimport { Effect, Layer, Context } from 'effect';\nimport type { DashboardSnapshot, DomainEvent, TurnDiffSummary } from '@overdeck/contracts';\nimport {\n type ReadModelState,\n INITIAL_READ_MODEL_STATE,\n applyEvent as applyEventReducer,\n getMaxTurnDiffSummariesPerAgent,\n isTerminalTurnDiffSummaryStatus,\n trimTurnDiffSummaries,\n} from '@overdeck/contracts';\nimport type { AgentSnapshot, AgentStatus, Role, AgentResolution, ReviewStatusSnapshot, ReviewStatusValue, TestStatusValue, UatStatusValue, MergeStatusValue, VerificationStatusValue } from '@overdeck/contracts';\nimport type { ReviewStatus } from '../../lib/review-status.js';\nimport { listOverdeckAgentStatesSync } from '../../lib/overdeck/agent-state-sync.js';\nimport { computeQueuePositionFromStatusSync } from '../../lib/queue-position.js'\nimport { AgentsResolver, type Agent as OverdeckAgent } from '../../lib/overdeck/agents.js';\n\n// ─── Exported async helpers (used by bootstrap Effect + tests) ───────────────\n\nconst MAX_SNAPSHOT_ACTIVITY_ENTRIES = 50;\n\ntype DashboardSnapshotWithActivity = DashboardSnapshot & {\n recentActivity?: unknown[];\n};\n\nexport function activityEntriesFromStoredEvents(\n events: ReadonlyArray<{ timestamp: string; payload: unknown }>,\n): unknown[] {\n return events.slice(0, MAX_SNAPSHOT_ACTIVITY_ENTRIES).map((event) => {\n const entry = event.payload && typeof event.payload === 'object'\n ? event.payload as Record<string, unknown>\n : {};\n return { id: entry.id, timestamp: event.timestamp, ...entry };\n });\n}\n\n\n// PAN-1510: bootstrap previously only seeded `issuesRaw` from the projection\n// cache or replaced it wholesale from `issueService.getIssues()`. Both paths\n// missed issues filed during the previous dashboard's poll-write window — the\n// projection cache flush is debounced at 2s, so a fresh `cache.set('github',\n// 'issues', ...)` in IssueDataService can survive a restart while the\n// projection cache entry remains stale. The helpers below mirror PAN-1506's\n// `discoverNewAgentIds`/merge pattern for issues.\n\nexport function getIssueIdentifierKey(issue: unknown): string | null {\n if (!issue || typeof issue !== 'object') return null;\n const item = issue as { identifier?: unknown; id?: unknown };\n if (typeof item.identifier === 'string' && item.identifier.length > 0) {\n return item.identifier.toLowerCase();\n }\n if (typeof item.id === 'string' && item.id.length > 0) {\n return item.id.toLowerCase();\n }\n return null;\n}\n\nexport function discoverNewIssues(\n cachedIssues: unknown[],\n currentIssues: unknown[],\n): unknown[] {\n const cachedIds = new Set<string>();\n for (const issue of cachedIssues) {\n const id = getIssueIdentifierKey(issue);\n if (id !== null) cachedIds.add(id);\n }\n const newIssues: unknown[] = [];\n for (const issue of currentIssues) {\n const id = getIssueIdentifierKey(issue);\n if (id !== null && !cachedIds.has(id)) newIssues.push(issue);\n }\n return newIssues;\n}\n\nexport function mergeIssuesByIdentifier(\n cachedIssues: unknown[],\n currentIssues: unknown[],\n): unknown[] {\n const merged = new Map<string, unknown>();\n const unidentified: unknown[] = [];\n for (const issue of cachedIssues) {\n const id = getIssueIdentifierKey(issue);\n if (id !== null) merged.set(id, issue);\n else unidentified.push(issue);\n }\n for (const issue of currentIssues) {\n const id = getIssueIdentifierKey(issue);\n if (id !== null) merged.set(id, issue);\n }\n return [...unidentified, ...merged.values()];\n}\n\nexport function shouldSkipCheckpointReconciliation(agent: Pick<AgentSnapshot, 'status' | 'workspace'>): boolean {\n return !agent.workspace || isTerminalTurnDiffSummaryStatus(agent.status)\n}\n\ntype IssueReadSourceState = {\n identifier?: unknown;\n id?: unknown;\n status?: unknown;\n state?: unknown;\n canonicalStatus?: unknown;\n rawTrackerState?: unknown;\n completedAt?: unknown;\n}\n\nfunction normalizeIssueId(value: unknown): string | null {\n if (typeof value !== 'string') return null;\n const trimmed = value.trim();\n return trimmed ? trimmed.toUpperCase() : null;\n}\n\nexport function getClosedIssueIdsForReadSource(issues: unknown[]): Set<string> {\n const closed = new Set<string>();\n for (const issue of issues) {\n if (!issue || typeof issue !== 'object') continue;\n const item = issue as IssueReadSourceState;\n const issueId = normalizeIssueId(item.identifier) ?? normalizeIssueId(item.id);\n if (!issueId) continue;\n const state = String(item.state ?? '').toLowerCase();\n const status = String(item.status ?? '').toLowerCase();\n const canonicalStatus = String(item.canonicalStatus ?? '').toLowerCase();\n const rawTrackerState = String(item.rawTrackerState ?? '').toLowerCase();\n if (\n item.completedAt ||\n state === 'closed' ||\n status === 'done' ||\n status === 'closed' ||\n status === 'cancelled' ||\n status === 'canceled' ||\n status === 'completed' ||\n canonicalStatus === 'done' ||\n canonicalStatus === 'closed' ||\n canonicalStatus === 'cancelled' ||\n canonicalStatus === 'canceled' ||\n canonicalStatus === 'completed' ||\n rawTrackerState === 'closed' ||\n rawTrackerState === 'done' ||\n rawTrackerState === 'completed'\n ) {\n closed.add(issueId);\n }\n }\n return closed;\n}\n\nexport function pruneAgentsForReadSource(\n agentsById: Record<string, AgentSnapshot>,\n issues: unknown[],\n): { agentsById: Record<string, AgentSnapshot>; prunedCount: number } {\n const closedIssueIds = getClosedIssueIdsForReadSource(issues);\n // PAN-1908: authoritative membership is the SQLite agents table, not state.json.\n const liveAgentIds = new Set(listOverdeckAgentStatesSync().map(a => a.id));\n const nextAgentsById: Record<string, AgentSnapshot> = {};\n let prunedCount = 0;\n\n for (const agent of Object.values(agentsById)) {\n if (closedIssueIds.has(agent.issueId.toUpperCase())) {\n prunedCount++;\n continue;\n }\n if (!liveAgentIds.has(agent.id)) {\n prunedCount++;\n continue;\n }\n nextAgentsById[agent.id] = agent;\n }\n\n return { agentsById: nextAgentsById, prunedCount };\n}\n\n// ─── Cached event store reference (avoids async dynamic import on each pushUpdated) ──\nlet _cachedEventStore: any = null;\n\ntype Jsonish = null | boolean | number | string | Jsonish[] | { [key: string]: Jsonish };\n\nfunction toJsonish(value: unknown, seen = new WeakSet<object>()): Jsonish | undefined {\n if (value == null) return null;\n if (typeof value === 'string' || typeof value === 'boolean') return value;\n if (typeof value === 'number') return Number.isFinite(value) ? value : null;\n if (value instanceof Date) return value.toISOString();\n if (Array.isArray(value)) {\n const next: Jsonish[] = [];\n for (const item of value) {\n const clean = toJsonish(item, seen);\n if (clean !== undefined) next.push(clean);\n }\n return next;\n }\n if (typeof value === 'object') {\n if (seen.has(value)) return null;\n seen.add(value);\n const next: { [key: string]: Jsonish } = {};\n for (const [key, item] of Object.entries(value as Record<string, unknown>)) {\n const clean = toJsonish(item, seen);\n if (clean !== undefined) next[key] = clean;\n }\n seen.delete(value);\n return next;\n }\n return undefined;\n}\n\nfunction cleanIssues(issues: unknown[]): unknown[] {\n return issues.map((issue) => toJsonish(issue) ?? null);\n}\n\n// ─── Value validators for strict literal types ──────────────────────────────\n\nconst VALID_AGENT_STATUSES = new Set<AgentStatus>([\"starting\", \"running\", \"stopped\", \"error\", \"unknown\"]);\nconst VALID_ROLES = new Set<Role>([\"plan\", \"work\", \"review\", \"test\", \"ship\", \"flywheel\", \"strike\", \"sequencer\", \"knowledge\"]);\nconst VALID_RESOLUTIONS = new Set<AgentResolution>([\"working\", \"done\", \"needs_input\", \"stuck\", \"completed\", \"unclear\", \"abandoned\", \"api_error\"]);\ntype SpecialistAgentName = 'review-agent' | 'test-agent' | 'merge-agent' | 'inspect-agent' | 'uat-agent';\ntype SpecialistLifecycleState = 'active' | 'sleeping' | 'uninitialized';\n\nconst VALID_SPECIALIST_NAMES = new Set<SpecialistAgentName>([\"review-agent\", \"test-agent\", \"merge-agent\", \"inspect-agent\", \"uat-agent\"]);\nconst VALID_SPECIALIST_LIFECYCLE_STATES = new Set<SpecialistLifecycleState>([\"active\", \"sleeping\", \"uninitialized\"]);\nconst VALID_REVIEW_STATUSES = new Set<ReviewStatusValue>([\"pending\", \"reviewing\", \"passed\", \"failed\", \"blocked\", \"skipped\"]);\nconst VALID_TEST_STATUSES = new Set<TestStatusValue>([\"pending\", \"testing\", \"passed\", \"failed\", \"skipped\", \"dispatch_failed\"]);\nconst VALID_UAT_STATUSES = new Set<UatStatusValue>([\"pending\", \"testing\", \"passed\", \"failed\"]);\nconst VALID_MERGE_STATUSES = new Set<MergeStatusValue>([\"pending\", \"queued\", \"merging\", \"verifying\", \"merged\", \"failed\"]);\nconst VALID_VERIFICATION_STATUSES = new Set<VerificationStatusValue>([\"pending\", \"running\", \"passed\", \"failed\", \"skipped\"]);\n\nexport function toAgentStatus(v: unknown): AgentStatus {\n return VALID_AGENT_STATUSES.has(v as AgentStatus) ? v as AgentStatus : \"unknown\";\n}\n\n/** Pending-input fields carried by an agent snapshot (PAN-1591). */\ntype PendingInputFields = Pick<\n AgentSnapshot,\n | 'hasPendingQuestion'\n | 'pendingQuestionCount'\n | 'pendingQuestionPrompt'\n | 'pendingQuestionReason'\n | 'pendingInputCount'\n | 'pendingInputKinds'\n | 'pendingAskUserQuestion'\n>;\n\n/**\n * PAN-1591 — a non-running agent cannot be awaiting interactive input. The\n * enrichment poller only emits for live agents, so a `hasPendingQuestion: true`\n * computed while the agent was last alive lingers in the cache forever; the\n * `agent.status_changed` reducer already strips it on a RUNTIME stop, but the\n * bootstrap projection of an agent that was ALREADY stopped at server start\n * never did — surfacing phantom \"Waiting on your input\" rows that report \"no\n * longer waiting\" on click. This applies the same rule at projection time:\n * clear every pending-input field unless the agent is running/starting.\n */\nexport function projectPendingInput(status: AgentStatus, src: PendingInputFields): PendingInputFields {\n if (status !== 'running' && status !== 'starting') {\n return {\n hasPendingQuestion: undefined,\n pendingQuestionCount: undefined,\n pendingQuestionPrompt: undefined,\n pendingQuestionReason: undefined,\n pendingInputCount: undefined,\n pendingInputKinds: undefined,\n pendingAskUserQuestion: undefined,\n };\n }\n return src;\n}\nexport function toRole(v: unknown): Role | undefined {\n return v && VALID_ROLES.has(v as Role) ? v as Role : undefined;\n}\n\nexport function toAgentResolution(v: unknown): AgentResolution | undefined {\n return v && VALID_RESOLUTIONS.has(v as AgentResolution) ? v as AgentResolution : undefined;\n}\nexport function toSpecialistAgentName(v: unknown): SpecialistAgentName | undefined {\n return VALID_SPECIALIST_NAMES.has(v as SpecialistAgentName) ? v as SpecialistAgentName : undefined;\n}\nexport function toSpecialistLifecycleState(v: unknown): SpecialistLifecycleState {\n return VALID_SPECIALIST_LIFECYCLE_STATES.has(v as SpecialistLifecycleState) ? v as SpecialistLifecycleState : \"uninitialized\";\n}\nexport function toReviewStatus(v: unknown): ReviewStatusValue | undefined {\n return v && VALID_REVIEW_STATUSES.has(v as ReviewStatusValue) ? v as ReviewStatusValue : undefined;\n}\nexport function toTestStatus(v: unknown): TestStatusValue | undefined {\n return v && VALID_TEST_STATUSES.has(v as TestStatusValue) ? v as TestStatusValue : undefined;\n}\nexport function toUatStatus(v: unknown): UatStatusValue | undefined {\n return v && VALID_UAT_STATUSES.has(v as UatStatusValue) ? v as UatStatusValue : undefined;\n}\nexport function toMergeStatus(v: unknown): MergeStatusValue | undefined {\n return v && VALID_MERGE_STATUSES.has(v as MergeStatusValue) ? v as MergeStatusValue : undefined;\n}\nexport function toVerificationStatus(v: unknown): VerificationStatusValue | undefined {\n return v && VALID_VERIFICATION_STATUSES.has(v as VerificationStatusValue) ? v as VerificationStatusValue : undefined;\n}\n\ntype ReviewStatusSnapshotInput = ReviewStatus & {\n reviewCoordinatorSessionName?: string;\n reviewSessionNames?: string[];\n reviewSubStatuses?: Record<string, 'running' | 'done'>;\n activeSpecialist?: string;\n};\n\nexport function toReviewStatusSnapshot(status: ReviewStatusSnapshotInput): ReviewStatusSnapshot {\n return {\n issueId: status.issueId,\n reviewStatus: toReviewStatus(status.reviewStatus),\n testStatus: toTestStatus(status.testStatus),\n uatStatus: toUatStatus(status.uatStatus),\n uatNotes: status.uatNotes || undefined,\n mergeStatus: toMergeStatus(status.mergeStatus),\n verificationStatus: toVerificationStatus(status.verificationStatus),\n verificationNotes: status.verificationNotes || undefined,\n verificationCycleCount: typeof status.verificationCycleCount === 'number' ? status.verificationCycleCount : undefined,\n readyForMerge: !!status.readyForMerge,\n updatedAt: status.updatedAt,\n prUrl: status.prUrl || undefined,\n stuck: !!status.stuck ? true : undefined,\n stuckReason: status.stuckReason || undefined,\n stuckAt: status.stuckAt || undefined,\n stuckDetails: status.stuckDetails || undefined,\n reviewedAtCommit: status.reviewedAtCommit || undefined,\n reviewSpawnedAt: status.reviewSpawnedAt || undefined,\n testRetryCount: typeof status.testRetryCount === 'number' ? status.testRetryCount : undefined,\n reviewRetryCount: typeof status.reviewRetryCount === 'number' ? status.reviewRetryCount : undefined,\n recoveryStartedAt: status.recoveryStartedAt || undefined,\n deaconIgnored: !!status.deaconIgnored ? true : undefined,\n deaconIgnoredAt: status.deaconIgnoredAt || undefined,\n deaconIgnoredReason: status.deaconIgnoredReason || undefined,\n // PAN-1691: tri-state routing key — preserve true/false/undefined as-is.\n autoMerge: status.autoMerge,\n reviewCoordinatorSessionName: status.reviewCoordinatorSessionName || undefined,\n reviewSessionNames: status.reviewSessionNames && status.reviewSessionNames.length > 0 ? status.reviewSessionNames : undefined,\n reviewSubStatuses: status.reviewSubStatuses,\n queuePosition: computeQueuePositionFromStatusSync(status).queuePosition ?? undefined,\n activeSpecialist: status.activeSpecialist || undefined,\n mergeRetryCount: typeof status.mergeRetryCount === 'number' ? status.mergeRetryCount : undefined,\n mergeNotes: status.mergeNotes || undefined,\n blockerReasons: status.blockerReasons && status.blockerReasons.length > 0 ? status.blockerReasons : undefined,\n autoRequeueCount: typeof status.autoRequeueCount === 'number' ? status.autoRequeueCount : undefined,\n };\n}\n\n// ─── ReadModelService ────────────────────────────────────────────────────────\n\nexport interface ReadModelServiceShape {\n /** Return the current read model state as a DashboardSnapshot. */\n readonly getSnapshot: Effect.Effect<DashboardSnapshotWithActivity>;\n /** Return a single pending channel permission request without rebuilding a full snapshot. */\n readonly getChannelPermissionRequest: (\n requestId: string,\n ) => Effect.Effect<import('@overdeck/contracts').ChannelPermissionRequestSnapshot | null>;\n /** Return a recent resolved channel permission decision for safe delivery retries. */\n readonly getResolvedChannelPermissionDecision: (\n requestId: string,\n ) => Effect.Effect<import('@overdeck/contracts').ResolvedChannelPermissionDecision | null>;\n /** Return in-memory turn diff summaries for a single agent. */\n readonly getTurnDiffSummaries: (agentId: string) => Effect.Effect<TurnDiffSummary[]>;\n /** Return the agentId for a given sessionId (from agent snapshot or runtime claudeSessionId). */\n readonly getAgentIdBySessionId: (sessionId: string) => Effect.Effect<string | null>;\n /** Apply a domain event to the read model (called by event store on append). */\n readonly applyEvent: (event: DomainEvent) => void;\n /** Bootstrap the read model from existing lib module state. */\n readonly bootstrap: Effect.Effect<void>;\n}\n\n// ─── Overdeck → legacy AgentSnapshot adapter ─────────────────────────────────\n//\n// Maps overdeck's 18-field Agent (durable config only) to the legacy\n// AgentSnapshot wire format. Runtime/ephemeral fields (lastActivity, branch,\n// costSoFar, phase, hasPendingQuestion, etc.) start undefined and are filled\n// by in-memory events from the enrichment poller and domain event stream.\n\nfunction overdeckStatusToLegacy(\n status: OverdeckAgent['status'],\n): AgentStatus {\n if (status === 'crashed') return 'error';\n // 'idle'/'waiting' = agent is alive but waiting (tool-call paused, AUQ, etc.)\n if (status === 'idle' || status === 'waiting') return 'running';\n return status; // 'starting' | 'running' | 'stopped' are 1:1\n}\n\nfunction agentSnapshotFromOverdeck(agent: OverdeckAgent): AgentSnapshot {\n return {\n id: agent.id,\n issueId: agent.issueId,\n workspace: agent.workspace || undefined,\n runtime: agent.harness,\n model: agent.model,\n status: overdeckStatusToLegacy(agent.status),\n startedAt: agent.startedAt?.toISOString(),\n sessionId: agent.sessionId ?? undefined,\n role: agent.role,\n stoppedByUser: agent.stoppedByUser ?? undefined,\n paused: agent.paused ?? undefined,\n pausedReason: agent.pausedReason ?? undefined,\n troubled: agent.troubled ?? undefined,\n consecutiveFailures: agent.consecutiveFailures,\n firstFailureInRunAt: agent.firstFailureInRunAt?.toISOString(),\n lastFailureNextRetryAt: agent.lastFailureNextRetryAt?.toISOString(),\n };\n}\n\nexport class ReadModelService extends Context.Service<\n ReadModelService,\n ReadModelServiceShape\n>()('overdeck/dashboard/ReadModelService') {}\n\n// ─── Live implementation ─────────────────────────────────────────────────────\n\nexport const ReadModelServiceLive = Layer.effect(\n ReadModelService,\n Effect.gen(function* () {\n let state: ReadModelState = { ...INITIAL_READ_MODEL_STATE };\n\n function cloneTurnDiffSummaries(summaries: TurnDiffSummary[] | undefined): TurnDiffSummary[] {\n if (!summaries || summaries.length === 0) return [];\n return summaries.map(summary => ({\n ...summary,\n files: summary.files.map(file => ({ ...file })),\n assistantMessageId: summary.assistantMessageId ?? undefined,\n checkpointRef: summary.checkpointRef ?? undefined,\n }));\n }\n\n function buildSnapshot(): DashboardSnapshotWithActivity {\n // turnDiffSummariesByAgentId is intentionally excluded from the snapshot.\n //\n // Per-agent checkpoint history can grow to thousands of turns × hundreds\n // of files; in production we measured 484 MB across 44 agents, which the\n // browser's WebSocket client rejects as \"Max payload size exceeded\" and\n // closes the socket with code 1006 — leaving the kanban and command deck\n // perpetually empty. The data is still maintained in `state` and served\n // on-demand via GET /api/agents/:id/diffs, so chat-timeline components\n // fetch it only for the agent the user is actually viewing.\n return {\n sequence: state.sequence,\n agents: Object.values(state.agentsById),\n // PAN-1048 — specialistsByName projection retired. The DashboardSnapshot\n // schema still has a `specialists` field for backward compat with the\n // wire format; we always send an empty array and clients derive the\n // same data from agentsById filtered by role.\n specialists: [],\n reviewStatuses: Object.values(state.reviewStatusByIssueId),\n agentRuntimeById: state.agentRuntimeById,\n channelPermissionRequests: Object.values(state.channelPermissionRequestsById ?? {}),\n issues: state.issuesRaw,\n recentActivity: state.recentActivity,\n resources: state.resources ?? undefined,\n memory: {\n observationsByIssueId: state.observationsByIssueId,\n statusByIssueId: state.statusByIssueId,\n rollupsByIssueId: state.rollupsByIssueId,\n resetMarkersByScopeId: state.resetMarkersByScopeId,\n healthByIssueId: state.healthByIssueId,\n },\n scanProgress: state.scanProgress,\n enrichStats: state.enrichStats,\n enrichProgressBySessionId: state.enrichProgressBySessionId,\n embedProgressBySessionId: state.embedProgressBySessionId,\n timestamp: new Date().toISOString(),\n };\n }\n\n const applyEvent = (event: DomainEvent): void => {\n state = applyEventReducer(state, event);\n };\n\n const getSnapshot: Effect.Effect<DashboardSnapshotWithActivity> = Effect.gen(function* () {\n // Refresh issues from the shared issue service before building snapshot.\n // IssueDataService polls trackers in the background; its cached issues are\n // the freshest available without blocking on API calls.\n //\n // PAN-1510: merge issueService's view with the current state.issuesRaw\n // so a hard browser reload always reflects the union of (projection\n // cache + freshest issueService data). issueService entries win on\n // identifier conflicts (fresher status, labels, etc.), and cached\n // entries that issueService is missing (transient empty fetch,\n // partial poll, single-tracker failure) are preserved instead of\n // dropped. Identifier-based merge is the same shape PAN-1506 used to\n // surface newly-spawned agents through the bootstrap fast-path.\n try {\n const { getSharedIssueService } = yield* Effect.promise(\n () => import('./services/issue-service-singleton.js'),\n );\n const issueService = getSharedIssueService();\n const currentIssues = cleanIssues(issueService.getIssues());\n if (currentIssues.length > 0 || state.issuesRaw.length === 0) {\n state = {\n ...state,\n issuesRaw: mergeIssuesByIdentifier(state.issuesRaw, currentIssues),\n };\n }\n } catch (err) {\n console.error('[ReadModel] Failed to refresh issues for snapshot:', err);\n }\n\n const pruned = pruneAgentsForReadSource(state.agentsById, state.issuesRaw);\n if (pruned.prunedCount > 0) {\n state = { ...state, agentsById: pruned.agentsById };\n console.log(`[ReadModel] Pruned ${pruned.prunedCount} stale agent${pruned.prunedCount === 1 ? '' : 's'} from read source`);\n }\n\n return buildSnapshot();\n });\n\n const getChannelPermissionRequest = (\n requestId: string,\n ): Effect.Effect<import('@overdeck/contracts').ChannelPermissionRequestSnapshot | null> =>\n Effect.succeed(state.channelPermissionRequestsById?.[requestId] ?? null);\n\n const getResolvedChannelPermissionDecision = (\n requestId: string,\n ): Effect.Effect<import('@overdeck/contracts').ResolvedChannelPermissionDecision | null> =>\n Effect.succeed(state.resolvedChannelPermissionDecisionsById?.[requestId] ?? null);\n\n const getTurnDiffSummaries = (agentId: string): Effect.Effect<TurnDiffSummary[]> =>\n Effect.sync(() => cloneTurnDiffSummaries(state.turnDiffSummariesByAgentId[agentId]));\n\n const getAgentIdBySessionId = (sessionId: string): Effect.Effect<string | null> =>\n Effect.sync(() => state.agentIdBySessionId[sessionId] ?? null);\n\n // ── Bootstrap inline during layer construction ───────────────────────────\n const agentsResolver = yield* AgentsResolver;\n yield* Effect.gen(function* () {\n // PAN-1938 source-swap: agents now come from overdeck.db via AgentsResolver.\n // reconstructCache still runs for reviewStatusByIssueId (which reads\n // git-backed per-issue records, NOT panopticon.db SQLite cache tables)\n // and for its side effects (agent-backfill sync, checkpoint cleanup).\n const { reconstructCacheAuto } = yield* Effect.promise(() =>\n import('../../lib/reconstruct/reconstruct-cache.js'),\n );\n\n const [overdeckAgents, result] = yield* Effect.all([\n agentsResolver.list({}),\n Effect.promise(() => reconstructCacheAuto()),\n ]);\n\n const agentsById: Record<string, AgentSnapshot> = Object.fromEntries(\n overdeckAgents.map((a) => [a.id, agentSnapshotFromOverdeck(a)]),\n );\n\n // ── Sequence from event store (labels the snapshot, not a replay source) ─\n let sequence = 0;\n let recentActivity: unknown[] = [];\n try {\n const { getEventStore } = yield* Effect.promise(\n () => import('./event-store.js'),\n );\n const eventStore = getEventStore();\n sequence = eventStore.getLatestSequence();\n recentActivity = activityEntriesFromStoredEvents(\n eventStore.queryByType('activity.entry', MAX_SNAPSHOT_ACTIVITY_ENTRIES),\n );\n } catch {\n // Event store may not be initialized yet\n }\n\n state = {\n ...INITIAL_READ_MODEL_STATE,\n sequence,\n agentsById,\n reviewStatusByIssueId: result.reviewStatusByIssueId,\n issuesRaw: [],\n recentActivity,\n };\n\n console.log(\n `[ReadModel] Bootstrapped from local database: ` +\n `${Object.keys(agentsById).length} agents, ` +\n `${Object.keys(result.reviewStatusByIssueId).length} review statuses, ` +\n `${result.issuesEnumerated} in-flight issue(s), seq=${sequence}`,\n );\n\n // ── Checkpoint reconciliation (deferred — non-blocking) ──────────────────\n // Fire-and-forget: scan workspaces for git checkpoints in the background\n // so the ReadModel layer resolves immediately and the dashboard starts fast.\n void (async () => {\n try {\n const { listCheckpoints, diffCheckpointFiles, getCheckpointTimestamp, deleteLegacyCheckpointRefs } = await import('../../lib/checkpoint/checkpoint-manager.js');\n\n const agents = Object.values(state.agentsById);\n\n // One-time: clean up unscoped legacy refs from before per-agent namespacing.\n // Run against the first agent's workspace (all worktrees share the same parent .git).\n const firstAgentWithWorkspace = agents.find(a => a.workspace);\n if (firstAgentWithWorkspace?.workspace) {\n const deleted = await Effect.runPromise(deleteLegacyCheckpointRefs(firstAgentWithWorkspace.workspace));\n if (deleted > 0) {\n console.log(`[ReadModel] Deleted ${deleted} legacy unscoped checkpoint refs`);\n }\n }\n let reconciled = 0;\n for (const agent of agents) {\n if (shouldSkipCheckpointReconciliation(agent)) continue;\n\n const workspace = agent.workspace;\n if (!workspace) continue;\n const existingSummaries = state.turnDiffSummariesByAgentId[agent.id];\n if (existingSummaries && existingSummaries.length > 0) continue;\n\n try {\n const checkpoints = await Effect.runPromise(listCheckpoints(workspace, agent.id));\n if (checkpoints.length === 0) continue;\n\n const maxRetainedSummaries = getMaxTurnDiffSummariesPerAgent();\n const retainedCheckpoints = checkpoints.length > maxRetainedSummaries\n ? checkpoints.slice(-maxRetainedSummaries)\n : checkpoints;\n const checkpointOffset = checkpoints.length - retainedCheckpoints.length;\n\n const summaries: Array<{\n turnId: string;\n completedAt: string;\n files: Array<{ path: string; kind?: string; additions?: number; deletions?: number }>;\n checkpointRef?: string;\n assistantMessageId?: string;\n checkpointTurnCount?: number;\n }> = [];\n\n for (let i = 0; i < retainedCheckpoints.length; i++) {\n const absoluteIndex = checkpointOffset + i;\n const turnId = retainedCheckpoints[i];\n if (!turnId) continue;\n const prevTurnId = absoluteIndex > 0 ? checkpoints[absoluteIndex - 1] ?? null : null;\n let files: Array<{ path: string; kind?: string; additions?: number; deletions?: number }> = [];\n if (prevTurnId) {\n try {\n files = await Effect.runPromise(diffCheckpointFiles(workspace, agent.id, prevTurnId, turnId));\n } catch { /* checkpoint might be stale */ }\n }\n const completedAt = await Effect.runPromise(getCheckpointTimestamp(workspace, agent.id, turnId));\n summaries.push({\n turnId,\n completedAt,\n files,\n checkpointRef: `refs/pan/turn/${agent.id}/${turnId}`,\n checkpointTurnCount: absoluteIndex + 1,\n });\n }\n\n if (summaries.length > 0) {\n state = {\n ...state,\n turnDiffSummariesByAgentId: {\n ...state.turnDiffSummariesByAgentId,\n [agent.id]: trimTurnDiffSummaries(summaries),\n },\n };\n reconciled++;\n }\n } catch { /* agent workspace may not be a git repo */ }\n }\n\n if (reconciled > 0) {\n console.log(`[ReadModel] Reconciled checkpoints for ${reconciled} agent(s)`);\n }\n } catch (err) {\n console.warn('[ReadModel] Checkpoint reconciliation failed:', err);\n }\n })();\n\n // ── Issue listener (always) ──────────────────────────────────────────────\n // Issues come from external trackers (Linear/GitHub) with unpredictable shapes.\n // JSON round-trip strips undefined values that can't be serialized over WebSocket.\n try {\n const { getSharedIssueService } = yield* Effect.promise(\n () => import('./services/issue-service-singleton.js'),\n );\n const issueService = getSharedIssueService();\n\n // PAN-1510: merge issueService's view with whatever the projection\n // cache loaded so newly-filed issues (filed during the previous\n // session's debounced-flush window, or already loaded into\n // IssueDataService's in-memory cache before the read model bootstrap\n // wired its onIssuesChanged callback) reach `issuesRaw`. Without the\n // merge, the bootstrap window between issueService.start() loading\n // its SQLite cache and read-model wiring `onIssuesChanged` could\n // strand fresh issues — the subsequent `pushSnapshot` would either\n // hit a null callback or be skipped by `issuesChanged()` because\n // `lastFetchedIssues` already matched the new GitHub fetch.\n const mergeT0 = performance.now();\n const currentIssues = cleanIssues(issueService.getIssues());\n if (currentIssues.length > 0 || state.issuesRaw.length === 0) {\n const newIssues = discoverNewIssues(state.issuesRaw, currentIssues);\n if (newIssues.length > 0) {\n const sample = newIssues\n .slice(0, 5)\n .map(i => getIssueIdentifierKey(i) ?? 'unknown')\n .join(', ');\n const more = newIssues.length > 5 ? `, +${newIssues.length - 5} more` : '';\n console.log(\n `[ReadModel] Bootstrap: merging ${newIssues.length} new issue(s) from issueService not in projection cache (${sample}${more})`,\n );\n }\n state = {\n ...state,\n issuesRaw: mergeIssuesByIdentifier(state.issuesRaw, currentIssues),\n };\n }\n console.log(`[boot-timing] ReadModel bootstrap merge completed at +${Math.round(performance.now() - mergeT0)}ms`);\n\n // Wire live issue updates — when IssueDataService polls new data,\n // update the read model directly AND emit to event store for\n // WebSocket subscribers (PAN-433).\n issueService.onIssuesChanged((issues) => {\n const cleaned = cleanIssues(issues);\n state = { ...state, issuesRaw: cleaned };\n\n // Fan-out issues.snapshot to live WebSocket subscribers via in-memory PubSub.\n // Uses emitOnly (NOT append) — issues.snapshot is ~1.5 MB and must never be\n // persisted to the event log. Persisting it causes startup OOM on replay.\n // Uses cached reference to avoid async dynamic import delay\n // (delay caused frontend to miss updates after patchIssue)\n try {\n if (!_cachedEventStore) {\n import('./event-store.js').then(({ getEventStore }) => {\n _cachedEventStore = getEventStore();\n try {\n _cachedEventStore.emitOnly({\n type: 'issues.snapshot',\n timestamp: new Date().toISOString(),\n payload: { issues: cleaned },\n } as any);\n } catch { /* event store not ready */ }\n }).catch(() => {});\n } else {\n _cachedEventStore.emitOnly({\n type: 'issues.snapshot',\n timestamp: new Date().toISOString(),\n payload: { issues: cleaned },\n } as any);\n }\n } catch { /* event store not ready yet */ }\n\n // PAN-1866: debounced incremental sequencer pass on every backlog delta\n import('../../lib/backlog/backlog-auto-trigger.js').then(({ triggerDebouncedIncrementalPass }) => {\n triggerDebouncedIncrementalPass(process.cwd());\n }).catch(() => {});\n });\n } catch {\n console.warn('[ReadModel] IssueDataService not available at bootstrap, starting with empty issues');\n }\n });\n\n return {\n getSnapshot,\n getChannelPermissionRequest,\n getResolvedChannelPermissionDecision,\n getTurnDiffSummaries,\n getAgentIdBySessionId,\n applyEvent,\n bootstrap: Effect.void,\n };\n }),\n);\n","import { readFile, stat } from 'fs/promises';\nimport { basename, dirname, join, resolve } from 'path';\nimport type { DashboardSnapshot, FeatureRegistryEntry } from '@overdeck/contracts';\nimport { resolveStatusFile } from './memory/paths.js';\n\nexport interface AssembleLiveBriefingInput {\n cwd?: string;\n projectId?: string;\n snapshot?: DashboardSnapshot;\n registryEntries?: FeatureRegistryEntry[];\n now?: Date;\n}\n\ninterface WorkspaceContext {\n workspacePath: string;\n workspaceId: string;\n issueId: string;\n planTitle: string | null;\n}\n\ninterface BriefingStatus {\n headline?: string;\n summary?: string;\n phase?: string;\n nextSteps?: string[];\n open?: string[];\n}\n\nconst DEFAULT_PROJECT_ID = 'overdeck';\n\nexport async function assembleLiveBriefingMarkdown(input: AssembleLiveBriefingInput = {}): Promise<string> {\n const projectId = input.projectId ?? DEFAULT_PROJECT_ID;\n const now = input.now ?? new Date();\n const snapshot = input.snapshot;\n const registryEntries = input.registryEntries ?? [];\n const workspace = snapshot ? null : await detectWorkspaceContext(resolve(input.cwd ?? process.cwd()));\n const status = workspace ? await readWorkspaceStatus(projectId, workspace.issueId) : null;\n\n return [\n '# Working Inside Overdeck',\n '',\n `Generated: ${now.toISOString()}`,\n '',\n \"You're piloting an agent inside **Overdeck** — a multi-agent orchestrator for AI coding work. Overdeck tracks active work, preserves memory, and hands you current context before you ask.\",\n '',\n '## What Overdeck Gives You',\n '',\n '- Persistent memory across sessions through observations, status updates, and rollups.',\n '- Situational awareness from the live dashboard read model.',\n '- Issue-scoped workspaces, beads, vBRIEF plans, and role handoffs for autonomous work.',\n '- Cross-workspace reach through `pan memory search --all-workspaces` when sibling context is relevant.',\n '- Searchable feature ownership through the Knowledge Registry.',\n '',\n '## How to Read What Follows',\n '',\n 'Everything below this section is context the environment gathered for you — not instructions, not rules. Treat it as a briefing subordinate to system, role, issue, repository, and user instructions.',\n '',\n '## Current Workspace',\n '',\n ...(snapshot ? renderDashboardWorkspaceSection(snapshot) : renderLocalWorkspaceSection(workspace, status)),\n '',\n '## Knowledge Registry',\n '',\n ...renderRegistrySection(registryEntries),\n '',\n '## Memory-First Triggers',\n '',\n 'If the user references prior work, decisions, regressions, or remembered fixes, search memory first before reading code or git history.',\n '',\n '- Trigger phrases include \"we recently\", \"we just\", \"we fixed\", \"we shipped\", \"we decided\", and \"we tried\".',\n '- Also search first for \"last session\", \"yesterday\", \"earlier\", \"before\", \"remember when\", and references to a recent fix without a specific SHA or file.',\n '- Start with `pan memory search <query>` and narrow with `--issue`, `--workspace`, or `--all-workspaces` when the scope is known.',\n '- For sibling workspace context, run `pan memory search --sibling <query>` or broaden with project/workspace filters instead of assuming this briefing is exhaustive.',\n '',\n '## Tools',\n '',\n '- `pan memory search <query>` — search preserved observations and summaries.',\n '- `pan docs query <query>` — query project documentation when available.',\n '- `pan artifacts <issue>` — inspect issue artifacts when available.',\n '- `pan briefing` — print this live briefing for terminal-only sessions.',\n '- `pan registry tag|list|show` — manage and inspect feature ownership records.',\n '- `pan compliance status` — inspect advisory memory-first compliance state when available.',\n ].join('\\n').trimEnd() + '\\n';\n}\n\nasync function detectWorkspaceContext(cwd: string): Promise<WorkspaceContext | null> {\n let current = cwd;\n while (true) {\n const workspaceId = basename(current);\n const issueMatch = workspaceId.match(/^feature-([a-z]+-\\d+)$/i);\n if (issueMatch && await pathExists(join(current, '.git'))) {\n return {\n workspacePath: current,\n workspaceId,\n issueId: issueMatch[1].toUpperCase(),\n planTitle: await readWorkspacePlanTitle(current),\n };\n }\n const parent = dirname(current);\n if (parent === current) return null;\n current = parent;\n }\n}\n\nasync function readWorkspacePlanTitle(workspacePath: string): Promise<string | null> {\n const path = join(workspacePath, '.pan', 'spec.vbrief.json');\n try {\n const parsed = JSON.parse(await readFile(path, 'utf8')) as { plan?: { title?: unknown } };\n return typeof parsed.plan?.title === 'string' ? parsed.plan.title : null;\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n}\n\nasync function readWorkspaceStatus(projectId: string, issueId: string): Promise<BriefingStatus | null> {\n try {\n const parsed = JSON.parse(await readFile(resolveStatusFile(projectId, issueId), 'utf8')) as BriefingStatus;\n return parsed && typeof parsed === 'object' ? parsed : null;\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n}\n\nfunction renderLocalWorkspaceSection(workspace: WorkspaceContext | null, status: BriefingStatus | null): string[] {\n if (!workspace) {\n return [\n '- No Overdeck workspace was detected from the current directory.',\n '- Run `pan briefing` from inside a `workspaces/feature-<issue>` checkout to include workspace-specific context.',\n ];\n }\n\n const lines = [\n `- Workspace: ${workspace.workspaceId}`,\n `- Issue: ${workspace.issueId}`,\n `- Path: ${workspace.workspacePath}`,\n ];\n if (workspace.planTitle) lines.push(`- Plan: ${workspace.planTitle}`);\n if (status?.phase) lines.push(`- Phase: ${status.phase}`);\n if (status?.headline) lines.push(`- Headline: ${status.headline}`);\n if (status?.summary) lines.push(`- Summary: ${status.summary}`);\n if (status?.nextSteps?.length) lines.push(`- Next steps: ${status.nextSteps.join('; ')}`);\n if (status?.open?.length) lines.push(`- Open questions: ${status.open.join('; ')}`);\n return lines;\n}\n\nfunction renderDashboardWorkspaceSection(snapshot: DashboardSnapshot): string[] {\n const runningAgents = snapshot.agents.filter((agent) => agent.status === 'running' || agent.hasLiveTmuxSession);\n const pausedAgents = snapshot.agents.filter((agent) => agent.paused);\n const troubledAgents = snapshot.agents.filter((agent) => agent.troubled || (agent.consecutiveFailures ?? 0) > 0);\n const activeIssues = new Set(snapshot.agents.map((agent) => agent.issueId).filter(Boolean));\n const failedReviews = snapshot.reviewStatuses.filter((status) =>\n status.testStatus === 'failed' ||\n status.uatStatus === 'failed' ||\n status.verificationStatus === 'failed' ||\n status.reviewStatus === 'failed' ||\n (status.blockerReasons?.length ?? 0) > 0,\n );\n\n const lines = [\n `- Running agents: ${runningAgents.length}`,\n `- Active issues with agents: ${activeIssues.size}`,\n `- Paused gates: ${pausedAgents.length}`,\n `- Troubled agents: ${troubledAgents.length}`,\n `- Failed verification/review states needing attention: ${failedReviews.length}`,\n ];\n\n const recentStatuses = Object.entries(readStatusByIssue(snapshot))\n .slice(0, 8)\n .map(([issueId, status]) => {\n const headline = typeof status.headline === 'string' && status.headline.trim()\n ? status.headline.trim()\n : 'No headline recorded';\n const phase = typeof status.phase === 'string' && status.phase.trim()\n ? ` (${status.phase.trim()})`\n : '';\n return `- ${issueId}${phase}: ${headline}`;\n });\n\n if (recentStatuses.length > 0) {\n lines.push('', 'Recent workspace statuses:', ...recentStatuses);\n } else {\n lines.push('', '- No workspace status summaries are currently available.');\n }\n\n return lines;\n}\n\nfunction readStatusByIssue(snapshot: DashboardSnapshot): Record<string, { headline?: unknown; phase?: unknown }> {\n const memory = (snapshot as { memory?: { statusByIssueId?: unknown } }).memory;\n return memory?.statusByIssueId && typeof memory.statusByIssueId === 'object'\n ? memory.statusByIssueId as Record<string, { headline?: unknown; phase?: unknown }>\n : {};\n}\n\nfunction renderRegistrySection(entries: FeatureRegistryEntry[]): string[] {\n if (entries.length === 0) {\n return [\n '- No feature registry entries are currently available.',\n '- Use `pan registry tag <issueId> <feature>` to add manual ownership records.',\n '- Use `pan registry list` and `pan registry show <feature>` to inspect ownership from terminal-only sessions.',\n ];\n }\n\n return [\n '| Feature | Issue | Workspace | Agent | Status | Tags |',\n '|---|---|---|---|---|---|',\n ...entries.slice(0, 25).map((entry) => [\n entry.featureName,\n entry.owningIssueId ?? '—',\n entry.owningWorkspaceId ?? '—',\n entry.owningAgentId ?? '—',\n entry.status,\n entry.tags.length > 0 ? entry.tags.join(', ') : '—',\n ].map(markdownCell).join(' | ')).map((row) => `| ${row} |`),\n ];\n}\n\nfunction markdownCell(value: string): string {\n return value.replaceAll('|', '\\\\|').replace(/\\s+/g, ' ').trim();\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\nfunction isNotFound(error: unknown): boolean {\n return typeof error === 'object' && error !== null && 'code' in error && error.code === 'ENOENT';\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport type { DashboardSnapshot, FeatureRegistryEntry } from '@overdeck/contracts';\nimport { assembleLiveBriefingMarkdown } from '../../../lib/briefing-assembler.js';\nimport { resolveSessionContextBriefingPath } from '../../../lib/briefing-freshness.js';\nimport { listFeatureRegistryEntries } from '../../../lib/registry/feature-registry-storage.js';\n\nexport interface SessionContextWriter {\n schedule(): void;\n writeNow(): Promise<void>;\n stop(): void;\n}\n\nexport interface StartSessionContextWriterOptions {\n readSnapshot: () => Promise<DashboardSnapshot>;\n subscribe: (listener: () => void) => () => void;\n debounceMs?: number;\n path?: string;\n now?: () => Date;\n mkdir?: typeof mkdir;\n writeFile?: typeof writeFile;\n listRegistryEntries?: () => Promise<FeatureRegistryEntry[]>;\n setTimeout?: typeof setTimeout;\n clearTimeout?: typeof clearTimeout;\n logger?: Pick<Console, 'error' | 'warn'>;\n}\n\nexport function startSessionContextWriter(options: StartSessionContextWriterOptions): SessionContextWriter {\n const debounceMs = options.debounceMs ?? 500;\n const path = options.path ?? resolveSessionContextBriefingPath();\n const mkdirFn = options.mkdir ?? mkdir;\n const writeFileFn = options.writeFile ?? writeFile;\n const listRegistryEntriesFn = options.listRegistryEntries ?? (() => listFeatureRegistryEntries({ limit: 50 }));\n const setTimeoutFn = options.setTimeout ?? setTimeout;\n const clearTimeoutFn = options.clearTimeout ?? clearTimeout;\n const now = options.now ?? (() => new Date());\n const logger = options.logger ?? console;\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n let stopped = false;\n let writing = false;\n let pendingAfterWrite = false;\n\n const writeLatest = async (): Promise<void> => {\n if (stopped) return;\n if (writing) {\n pendingAfterWrite = true;\n return;\n }\n\n writing = true;\n try {\n const [snapshot, registryEntries] = await Promise.all([\n options.readSnapshot(),\n readRegistryEntries(listRegistryEntriesFn, logger),\n ]);\n const content = await assembleLiveBriefingMarkdown({ snapshot, registryEntries, now: now() });\n await mkdirFn(dirname(path), { recursive: true });\n await writeFileFn(path, content, 'utf8');\n } catch (error) {\n logger.error('[session-context] Failed to write live briefing:', error);\n } finally {\n writing = false;\n if (pendingAfterWrite && !stopped) {\n pendingAfterWrite = false;\n schedule();\n }\n }\n };\n\n const schedule = (): void => {\n if (stopped) return;\n if (timer) clearTimeoutFn(timer);\n timer = setTimeoutFn(() => {\n timer = null;\n void writeLatest();\n }, debounceMs);\n };\n\n const unsubscribe = options.subscribe(schedule);\n void writeLatest();\n\n return {\n schedule,\n writeNow: writeLatest,\n stop() {\n stopped = true;\n if (timer) {\n clearTimeoutFn(timer);\n timer = null;\n }\n unsubscribe();\n },\n };\n}\n\nasync function readRegistryEntries(\n listRegistryEntries: () => Promise<FeatureRegistryEntry[]>,\n logger: Pick<Console, 'warn'>,\n): Promise<FeatureRegistryEntry[]> {\n try {\n return await listRegistryEntries();\n } catch (error) {\n logger.warn('[session-context] Failed to load feature registry entries:', error);\n return [];\n }\n}\n","/**\n * Domain service wrappers — Effect services over existing lib modules (PAN-428 B5, PAN-433)\n *\n * EventStoreService: wraps the SQLite event store in Effect.\n * SnapshotService was removed in PAN-433 — replaced by ReadModelService (read-model.ts).\n *\n * The EventStoreServiceLive layer also wires event store → read model: every\n * appended event is pushed to ReadModelService.applyEvent() so the in-memory\n * projection stays current.\n */\n\nimport { Effect, Layer, Queue, Context, Stream } from 'effect';\nimport { initEventStore } from '../event-store.js';\nimport type { StoredEvent } from '../event-store.js';\nimport { ReadModelService } from '../read-model.js';\nimport { startSessionContextWriter } from './session-context-writer.js';\nimport { emitActivityDetailedSync } from '../../../lib/activity-logger.js';\nimport { captureCheckpoint, diffCheckpointFiles, listCheckpoints } from '../../../lib/checkpoint/checkpoint-manager.js';\nimport { randomUUID } from 'crypto';\n\n// ─── EventStoreService ────────────────────────────────────────────────────────\n\nexport interface EventStoreServiceShape {\n /** Append a domain event; returns the assigned sequence number. */\n readonly append: (event: Record<string, unknown>) => Effect.Effect<number>;\n /** Queue a domain event append without blocking the HTTP/WebSocket hot path. */\n readonly appendAsync: (event: Record<string, unknown>) => Effect.Effect<number>;\n /** Return all stored events with sequence > fromSequence. */\n readonly readFrom: (fromSequence: number) => Effect.Effect<StoredEvent[]>;\n /** Return events of a given type, most recent first, capped at limit. */\n readonly queryByType: (type: string, limit?: number) => Effect.Effect<StoredEvent[]>;\n /** Return the latest sequence number (0 if empty). */\n readonly getLatestSequence: Effect.Effect<number>;\n /** Subscribe to live events as an Effect Stream. */\n readonly streamEvents: Stream.Stream<StoredEvent>;\n}\n\nexport class EventStoreService extends Context.Service<\n EventStoreService,\n EventStoreServiceShape\n>()('overdeck/dashboard/EventStoreService') {}\n\n/** Map a domain event to a detailed activity log entry. Returns null for uninteresting events. */\nfunction shouldRefreshSessionContext(type: string): boolean {\n return type === 'issues.snapshot' ||\n type.startsWith('agent.') ||\n type.startsWith('review.') ||\n type.startsWith('pipeline.') ||\n type.startsWith('planning.') ||\n type.startsWith('plan.') ||\n type.startsWith('workspace.') ||\n type.startsWith('issue.') ||\n type.startsWith('memory.') ||\n type.startsWith('cost.') ||\n type.startsWith('merge.') ||\n type.startsWith('system.health_');\n}\n\nexport function mapDomainEventToDetailed(event: StoredEvent): {\n source: string;\n level: 'info' | 'warn' | 'error' | 'success';\n message: string;\n issueId?: string;\n triggeringEvent: string;\n} | null {\n const p = (event.payload ?? {}) as Record<string, unknown>;\n const issueId = (p['issueId'] as string) ?? undefined;\n\n switch (event.type) {\n case 'agent.created':\n return { source: 'cloister', level: 'info', message: `Agent created for ${p['agentId']}`, issueId, triggeringEvent: event.type };\n case 'agent.started':\n return { source: 'cloister', level: 'info', message: `Agent started: ${p['agentId']}`, issueId, triggeringEvent: event.type };\n case 'agent.stopped':\n return { source: 'cloister', level: 'info', message: `Agent stopped: ${p['agentId']}`, issueId, triggeringEvent: event.type };\n case 'agent.status_changed': {\n const prev = p['previousStatus'] ? ` (was ${p['previousStatus']})` : '';\n return { source: 'cloister', level: 'info', message: `Agent ${p['agentId']} status → ${p['status']}${prev}`, issueId, triggeringEvent: event.type };\n }\n case 'agent.enrichment_changed': {\n const parts: string[] = [];\n if (p['agentPhase']) parts.push(`phase=${p['agentPhase']}`);\n if (p['resolution']) parts.push(`resolution=${p['resolution']}`);\n if (p['hasPendingQuestion']) parts.push(`questions=${p['pendingQuestionCount']}`);\n if (parts.length === 0) return null;\n return { source: 'cloister', level: 'info', message: `Agent ${p['agentId']} enrichment: ${parts.join(', ')}`, issueId, triggeringEvent: event.type };\n }\n case 'planning.started':\n return { source: 'planning', level: 'info', message: `Planning started: ${p['sessionName']}`, issueId, triggeringEvent: event.type };\n case 'planning.failed':\n return { source: 'planning', level: 'error', message: `Planning failed: ${p['error']}`, issueId, triggeringEvent: event.type };\n case 'planning.sync':\n return { source: 'planning', level: 'info', message: `Planning sync: ${p['status']}${p['progress'] !== undefined ? ` (${p['progress']}%)` : ''}`, issueId, triggeringEvent: event.type };\n case 'plan.item_status_changed':\n return { source: 'plan', level: 'info', message: `Plan item ${p['itemId']} → ${p['status']}`, issueId, triggeringEvent: event.type };\n case 'plan.subitem_status_changed':\n return { source: 'plan', level: 'info', message: `Plan sub-item ${p['subItemId']} → ${p['status']}`, issueId, triggeringEvent: event.type };\n case 'plan.items_unblocked': {\n const items = p['items'] as string[] | undefined;\n if (!items || items.length === 0) return null;\n return { source: 'plan', level: 'success', message: `${items.length} plan item(s) unblocked`, issueId, triggeringEvent: event.type };\n }\n case 'pipeline.status_changed':\n return { source: 'pipeline', level: 'info', message: `Pipeline status updated for ${issueId}`, issueId, triggeringEvent: event.type };\n case 'review.status_changed':\n return { source: 'review', level: 'info', message: `Review status updated for ${issueId}`, issueId, triggeringEvent: event.type };\n case 'pipeline.verification-started':\n return { source: 'verification', level: 'info', message: `Verification started for ${issueId}`, issueId, triggeringEvent: event.type };\n case 'pipeline.verification-failed':\n return { source: 'verification', level: 'error', message: `Verification failed for ${issueId}${p['failedCheck'] ? `: ${p['failedCheck']}` : ''}`, issueId, triggeringEvent: event.type };\n case 'pipeline.review-started':\n return { source: 'review', level: 'info', message: `Review specialist started for ${issueId}`, issueId, triggeringEvent: event.type };\n case 'pipeline.review-completed':\n return { source: 'review', level: p['passed'] ? 'success' : 'error', message: `Review ${p['passed'] ? 'passed' : 'failed'} for ${issueId}`, issueId, triggeringEvent: event.type };\n case 'pipeline.test-started':\n return { source: 'test', level: 'info', message: `Test specialist started for ${issueId}`, issueId, triggeringEvent: event.type };\n case 'pipeline.test-completed':\n return { source: 'test', level: p['passed'] ? 'success' : 'error', message: `Tests ${p['passed'] ? 'passed' : 'failed'} for ${issueId}`, issueId, triggeringEvent: event.type };\n case 'specialist.started':\n return { source: 'cloister', level: 'info', message: `Specialist ${p['name'] ?? '?'} started${p['currentIssue'] ? ` for ${p['currentIssue']}` : ''}`, issueId, triggeringEvent: event.type };\n case 'specialist.completed':\n return { source: 'cloister', level: 'success', message: `Specialist ${p['name']} completed`, issueId, triggeringEvent: event.type };\n case 'specialist.failed':\n return { source: 'cloister', level: 'error', message: `Specialist ${p['name']} failed: ${p['error']}`, issueId, triggeringEvent: event.type };\n case 'workspace.created':\n return { source: 'workspace', level: 'info', message: `Workspace created at ${p['workspacePath']}`, issueId, triggeringEvent: event.type };\n case 'workspace.wipe_started':\n return { source: 'workspace', level: 'warn', message: `Deep-wipe started for ${issueId}`, issueId, triggeringEvent: event.type };\n case 'workspace.destroyed':\n return { source: 'workspace', level: 'info', message: `Workspace destroyed for ${issueId}`, issueId, triggeringEvent: event.type };\n case 'workspace.deleted':\n return { source: 'workspace', level: 'info', message: `Workspace deleted for ${issueId}`, issueId, triggeringEvent: event.type };\n case 'workspace.aborted':\n return { source: 'workspace', level: 'warn', message: `Workspace aborted for ${issueId}`, issueId, triggeringEvent: event.type };\n case 'issue.statusChanged':\n return { source: 'tracker', level: 'info', message: `Issue ${p['issueId']} status → ${p['canonicalStatus']}`, issueId: p['issueId'] as string, triggeringEvent: event.type };\n case 'dashboard.lifecycle_started':\n return { source: 'dashboard', level: 'info', message: `Dashboard lifecycle started: ${p['reason']}`, issueId, triggeringEvent: event.type };\n case 'dashboard.lifecycle_completed':\n return { source: 'dashboard', level: 'success', message: `Dashboard lifecycle completed: ${p['reason']}`, issueId, triggeringEvent: event.type };\n case 'dashboard.lifecycle_failed':\n return { source: 'dashboard', level: 'error', message: `Dashboard lifecycle failed: ${p['reason']} — ${p['error']}`, issueId, triggeringEvent: event.type };\n case 'merge.ready':\n return { source: 'ship', level: 'success', message: `Issue ${issueId} ready for merge`, issueId, triggeringEvent: event.type };\n case 'system.health_severity_changed':\n return {\n source: 'system-health',\n level: p['severity'] === 'critical' ? 'error' : p['severity'] === 'warning' ? 'warn' : 'info',\n message: `System health ${p['previousSeverity']} → ${p['severity']}`,\n issueId,\n triggeringEvent: event.type,\n };\n case 'cost.event_recorded': {\n const cost = Number(p['cost']);\n if (Number.isFinite(cost) && cost <= 0) return null;\n return { source: 'costs', level: 'info', message: `Cost event: $${(p['cost'] as number)?.toFixed?.(2) ?? p['cost']} for ${p['agentId']}`, issueId, triggeringEvent: event.type };\n }\n default:\n return null;\n }\n}\n\nexport const EventStoreServiceLive = Layer.effect(\n EventStoreService,\n Effect.gen(function* () {\n const store = yield* Effect.promise(() => initEventStore());\n const readModel = yield* ReadModelService;\n\n // Wire event store → read model: every appended event updates the projection.\n // The EventEmitter subscription is sync, so applyEvent runs inline on append.\n store.subscribe((event) => {\n readModel.applyEvent({\n type: event.type,\n sequence: event.sequence,\n timestamp: event.timestamp,\n payload: event.payload,\n } as any);\n });\n\n startSessionContextWriter({\n readSnapshot: () => Effect.runPromise(readModel.getSnapshot),\n subscribe: (listener) => store.subscribe((event) => {\n if (shouldRefreshSessionContext(event.type)) listener();\n }),\n });\n\n // Auto-emit detailed activity entries for state-change domain events.\n // Skip activity.* events to avoid infinite loops.\n store.subscribe((event) => {\n if (event.type.startsWith('activity.')) return;\n const detailed = mapDomainEventToDetailed(event);\n if (detailed) emitActivityDetailedSync(detailed);\n });\n\n // Capture checkpoints when agent activity changes.\n // Fires async (non-blocking) on every agent.activity_changed event for agents\n // with a workspace. Creates a git checkpoint, computes file changes since the\n // previous checkpoint, and emits agent.turn_diff_completed.\n // Rate-limited to one checkpoint per agent per 10 seconds.\n const checkpointCooldown = new Map<string, number>(); // agentId → last checkpoint timestamp\n store.subscribe((event) => {\n if (event.type !== 'agent.activity_changed') return;\n const p = (event.payload ?? {}) as Record<string, unknown>;\n const agentId = p['agentId'] as string | undefined;\n if (!agentId) return;\n\n // Rate limit: skip if last checkpoint was <10s ago\n const lastCheckpoint = checkpointCooldown.get(agentId) ?? 0;\n if (Date.now() - lastCheckpoint < 10_000) return;\n checkpointCooldown.set(agentId, Date.now());\n\n // Fire-and-forget async checkpoint — don't block the event loop\n (async () => {\n try {\n // Look up agent workspace from read model snapshot\n const snapshot = await Effect.runPromise(readModel.getSnapshot);\n const agent = (snapshot.agents as any[]).find((a: any) => a.id === agentId);\n if (!agent?.workspace) return;\n\n const workspace: string = agent.workspace;\n const turnId = `turn-${Date.now()}-${randomUUID().slice(0, 8)}`;\n\n // Capture checkpoint (git tag at current working tree state)\n await Effect.runPromise(captureCheckpoint(workspace, agentId, turnId));\n\n // Compute file changes from previous checkpoint\n const checkpoints = await Effect.runPromise(listCheckpoints(workspace, agentId));\n const prevCheckpoint = checkpoints.length >= 2 ? checkpoints[checkpoints.length - 2] : null;\n let files: Array<{ path: string; kind?: string; additions?: number; deletions?: number }> = [];\n if (prevCheckpoint) {\n files = await Effect.runPromise(diffCheckpointFiles(workspace, agentId, prevCheckpoint, turnId));\n }\n\n // Only emit if there are actual changes\n if (files.length === 0) return;\n\n // Emit turn_diff_completed event\n store.appendAsync({\n type: 'agent.turn_diff_completed',\n timestamp: new Date().toISOString(),\n payload: {\n agentId,\n turnId,\n completedAt: new Date().toISOString(),\n files,\n checkpointRef: `refs/pan/turn/${agentId}/${turnId}`,\n assistantMessageId: undefined,\n checkpointTurnCount: checkpoints.length,\n },\n } as any);\n } catch (err) {\n // Checkpoint capture is best-effort — log but don't crash\n console.error('[checkpoint] Failed to capture checkpoint for', agentId, err);\n }\n })();\n });\n\n // PAN-1635: bridge Claude-native compaction to the conversation \"compacting\"\n // flag. Only the Overdeck-native path emits conversation.compacting_changed\n // directly; a built-in Claude Code /compact is invisible to it. But the\n // PreCompact hook emits activity{tool:\"compact\"} and PostCompact emits\n // activity{idle} for *both* paths, so mirror that signal here. This gives the\n // dashboard a live \"Compacting…\" indicator during Claude's own /compact (and\n // visibly explains a prompt eaten by submit-time compaction). Conversation\n // agentIds (`conv-*`) double as the conversation name.\n const compactingConvs = new Set<string>();\n store.subscribe((event) => {\n if (event.type !== 'agent.activity_changed') return;\n const p = (event.payload ?? {}) as Record<string, unknown>;\n const agentId = p['agentId'] as string | undefined;\n if (!agentId || !agentId.startsWith('conv-')) return;\n const compacting = p['currentTool'] === 'compact';\n if (compacting === compactingConvs.has(agentId)) return;\n if (compacting) compactingConvs.add(agentId);\n else compactingConvs.delete(agentId);\n store.emitOnly({\n type: 'conversation.compacting_changed',\n timestamp: event.timestamp ?? new Date().toISOString(),\n payload: { conversationName: agentId, compacting },\n } as never);\n });\n\n const streamEvents = Stream.callback<StoredEvent>((queue) =>\n Effect.acquireRelease(\n Effect.sync(() =>\n store.subscribe((event) => Queue.offerUnsafe(queue, event)),\n ),\n (unsubscribe) => Effect.sync(unsubscribe),\n ),\n );\n\n return {\n append: (event) => Effect.sync(() => store.append(event as never)),\n appendAsync: (event) => Effect.promise(() => store.appendAsync(event as never)),\n readFrom: (fromSequence) => Effect.sync(() => store.readFrom(fromSequence)),\n queryByType: (type, limit) => Effect.sync(() => store.queryByType(type, limit)),\n getLatestSequence: Effect.sync(() => store.getLatestSequence()),\n streamEvents,\n };\n }),\n);\n","/**\n * IssueLifecycle Effect service (PAN-449)\n *\n * High-level issue state management that dispatches to the correct tracker\n * client (Linear, GitHub, Rally) based on the issue ID prefix.\n *\n * Routes should use IssueLifecycle instead of calling tracker clients directly.\n * The service handles:\n * - State transitions (In Planning, In Progress, In Review, Done)\n * - Label add/remove (GitHub only; no-op for Linear/Rally)\n * - Issue close with label cleanup\n */\n\nimport { Effect, Layer, Option, Context } from 'effect';\nimport { resolveGitHubIssueSync, resolveTrackerTypeSync } from '../../../lib/tracker-utils.js';\nimport { GitHubClient } from './github-client.js';\nimport { GitHubClientOptionalLive } from './github-client.js';\nimport { LinearClient } from './linear-client.js';\nimport { LinearClientOptionalLive } from './linear-client.js';\nimport { RallyClient } from './rally-client.js';\nimport { RallyClientLive } from './rally-client.js';\nimport type { RallyClientShape } from './rally-client.js';\nimport type { LinearState } from './linear-client.js';\nimport { TrackerNotConfigured, TrackerApiError, IssueNotFound, RateLimited } from './typed-errors.js';\nimport { EventStoreService } from './domain-services.js';\nimport { getSharedIssueService } from './issue-service-singleton.js';\n\n// ─── Event emission helper ────────────────────────────────────────────────────\n\n/**\n * Attempt to emit a domain event via the shared event store.\n * Non-fatal: silently no-ops if EventStoreService is absent (e.g. in unit tests).\n */\nfunction emitEvent(event: Record<string, unknown>): Effect.Effect<void, never> {\n return Effect.gen(function* () {\n const storeOption = yield* Effect.serviceOption(EventStoreService);\n if (Option.isSome(storeOption)) {\n yield* storeOption.value.append(event);\n }\n });\n}\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type IssueState = 'open' | 'in_planning' | 'in_progress' | 'in_review' | 'verifying_on_main' | 'closed' | 'canceled';\n\n// ─── Service interface ────────────────────────────────────────────────────────\n\nexport interface IssueLifecycleShape {\n /**\n * Transition an issue to a new lifecycle state.\n *\n * For Linear: resolves the Linear state ID from team workflow.\n * For GitHub: adds/removes workflow labels (state change via labels).\n * For Rally: delegates to RallyClient.updateState.\n */\n readonly transitionTo: (\n issueId: string,\n state: IssueState,\n ) => Effect.Effect<\n void,\n TrackerNotConfigured | IssueNotFound | TrackerApiError | RateLimited\n >;\n\n /**\n * Add a label to an issue.\n * GitHub only: creates the label if it doesn't exist, then adds it.\n * Linear / Rally: no-op (trackers don't use workflow labels the same way).\n */\n readonly addLabel: (\n issueId: string,\n label: string,\n ) => Effect.Effect<void, TrackerNotConfigured | TrackerApiError | RateLimited>;\n\n /**\n * Remove a label from an issue.\n * GitHub only; non-fatal if label is not present.\n * Linear / Rally: no-op.\n */\n readonly removeLabel: (\n issueId: string,\n label: string,\n ) => Effect.Effect<void, TrackerNotConfigured | TrackerApiError | RateLimited>;\n\n /**\n * Close an issue and clean up workflow labels.\n *\n * - Transitions issue to 'closed' state.\n * - For GitHub: removes in-progress/in-review/planned labels.\n */\n readonly close: (\n issueId: string,\n ) => Effect.Effect<\n void,\n TrackerNotConfigured | IssueNotFound | TrackerApiError | RateLimited\n >;\n}\n\n// ─── Service tag ──────────────────────────────────────────────────────────────\n\nexport class IssueLifecycle extends Context.Service<IssueLifecycle, IssueLifecycleShape>()(\n 'overdeck/dashboard/IssueLifecycle',\n) {}\n\n// ─── Linear state helpers ─────────────────────────────────────────────────────\n\n/** Map a normalized IssueState to a Linear state type string. */\nfunction linearStateType(state: IssueState): string {\n switch (state) {\n case 'open':\n return 'unstarted';\n case 'in_planning':\n case 'in_progress':\n case 'in_review':\n case 'verifying_on_main':\n return 'started';\n case 'closed':\n return 'completed';\n case 'canceled':\n return 'canceled';\n }\n}\n\n/** Find the best matching Linear state from a list given a target normalized state. */\nfunction findLinearState(states: ReadonlyArray<LinearState>, state: IssueState): LinearState | null {\n if (state === 'in_review' || state === 'verifying_on_main') {\n const names = state === 'verifying_on_main'\n ? ['verifying on main', 'verifying', 'in review']\n : ['in review'];\n const explicit = states.find((s) => names.includes(s.name.toLowerCase()));\n if (explicit) return explicit;\n // Fall back to first \"started\" state\n return states.filter((s) => s.type === 'started')[0] ?? null;\n }\n\n if (state === 'in_planning') {\n // Prefer a state named \"In Planning\" or \"Planning\"\n const explicit = states.find(\n (s) => s.name.toLowerCase() === 'in planning' || s.name.toLowerCase() === 'planning',\n );\n if (explicit) return explicit;\n // Fall back to first \"started\" state\n return states.filter((s) => s.type === 'started')[0] ?? null;\n }\n\n const targetType = linearStateType(state);\n const matching = states.filter((s) => s.type === targetType);\n return matching[0] ?? null;\n}\n\n// ─── GitHub label helpers ─────────────────────────────────────────────────────\n\nconst GITHUB_STATE_LABELS: Record<IssueState, { add: string[]; remove: string[] }> = {\n open: { add: [], remove: ['in-progress', 'in-review', 'planned', 'in-planning', 'review-ready', 'done', 'merged', 'verifying-on-main', 'needs-close-out', 'closed-out', 'wontfix', 'duplicate'] },\n in_planning: { add: ['planned'], remove: ['in-progress', 'in-review', 'review-ready', 'done', 'merged', 'verifying-on-main', 'needs-close-out', 'closed-out', 'wontfix', 'duplicate'] },\n in_progress: { add: ['in-progress'], remove: ['planned', 'in-planning', 'in-review', 'review-ready', 'done', 'merged', 'verifying-on-main', 'needs-close-out', 'closed-out', 'wontfix', 'duplicate'] },\n in_review: { add: ['in-review'], remove: ['in-progress', 'planned', 'in-planning', 'done', 'merged', 'verifying-on-main', 'needs-close-out', 'closed-out', 'wontfix', 'duplicate'] },\n verifying_on_main: { add: ['verifying-on-main'], remove: ['in-progress', 'in-review', 'planned', 'in-planning', 'review-ready', 'ready-for-merge', 'done', 'needs-close-out', 'closed-out', 'wontfix', 'duplicate'] },\n closed: { add: [], remove: ['in-progress', 'in-review', 'planned', 'in-planning', 'review-ready', 'done', 'merged', 'verifying-on-main', 'needs-close-out', 'closed-out', 'wontfix', 'duplicate'] },\n canceled: { add: ['wontfix'], remove: ['in-progress', 'in-review', 'planned', 'in-planning', 'review-ready', 'done', 'merged', 'verifying-on-main', 'needs-close-out', 'closed-out', 'duplicate'] },\n};\n\nconst GITHUB_LABEL_METADATA: Record<string, { color: string; description: string }> = {\n 'planned': { color: 'cfd3d7', description: 'Planning complete' },\n 'in-progress': { color: 'fbca04', description: 'Implementation in progress' },\n 'in-review': { color: 'd4c5f9', description: 'Under review' },\n 'verifying-on-main': { color: 'fbca04', description: 'Merged — awaiting verification on main' },\n 'wontfix': { color: 'ffffff', description: 'Will not be fixed' },\n};\n\nfunction githubLabelMetadata(label: string): { color: string; description: string } {\n return GITHUB_LABEL_METADATA[label] ?? { color: '0075ca', description: '' };\n}\n\n// ─── Live layer implementation ────────────────────────────────────────────────\n\n/** Map IssueState to canonical status string for cache patching. */\nfunction canonicalStatus(state: IssueState): string {\n switch (state) {\n case 'open': return 'open';\n case 'in_planning': return 'in_planning';\n case 'in_progress': return 'in_progress';\n case 'in_review': return 'in_review';\n case 'verifying_on_main': return 'verifying_on_main';\n case 'closed': return 'closed';\n case 'canceled': return 'canceled';\n }\n}\n\nexport const IssueLifecycleLive = Layer.effect(\n IssueLifecycle,\n Effect.gen(function* () {\n const linear = yield* LinearClient;\n const github = yield* GitHubClient;\n const rally = yield* RallyClient;\n\n const impl: IssueLifecycleShape = {\n transitionTo: (issueId, state) =>\n Effect.gen(function* () {\n const trackerType = resolveTrackerTypeSync(issueId);\n\n if (trackerType === 'github') {\n // GitHub state is managed via labels\n const ghInfo = resolveGitHubIssueSync(issueId);\n if (!ghInfo.isGitHub) return;\n const labelOps = GITHUB_STATE_LABELS[state];\n for (const label of labelOps.add) {\n const metadata = githubLabelMetadata(label);\n yield* github.ensureLabel(ghInfo.owner, ghInfo.repo, label, metadata.color, metadata.description);\n yield* github.addLabel(ghInfo.owner, ghInfo.repo, ghInfo.number, label);\n }\n for (const label of labelOps.remove) {\n yield* github.removeLabel(ghInfo.owner, ghInfo.repo, ghInfo.number, label);\n }\n if (state === 'closed' || state === 'canceled') {\n yield* github.closeIssue(ghInfo.owner, ghInfo.repo, ghInfo.number);\n } else {\n // Reopen the issue if it's currently closed (e.g. reopening after incorrect merge)\n yield* github.reopenIssue(ghInfo.owner, ghInfo.repo, ghInfo.number).pipe(\n Effect.catch(() => Effect.void) // Non-fatal if already open\n );\n }\n } else if (trackerType === 'rally') {\n const normalizedState =\n state === 'in_planning' || state === 'open' ? 'open'\n : state === 'in_progress' || state === 'in_review' || state === 'verifying_on_main' ? 'in_progress'\n : 'closed';\n yield* rally.updateState(issueId, normalizedState);\n } else {\n // Linear\n const issue = yield* linear.getIssue(issueId);\n const states = yield* linear.getTeamStates(issue.team.id);\n const target = findLinearState(states, state);\n // No-op if the issue is already in the target state\n if (target && issue.state?.id !== target.id) {\n yield* linear.updateState(issue.id, target.id);\n }\n }\n\n // Patch the in-memory cache and emit a domain event (non-fatal)\n yield* Effect.try({ try: () => getSharedIssueService().patchIssue(issueId, { canonicalStatus: canonicalStatus(state) }), catch: () => void 0 }).pipe(Effect.ignore);\n yield* emitEvent({\n type: 'issue.transitioned',\n timestamp: new Date().toISOString(),\n payload: { issueId, state },\n });\n }),\n\n addLabel: (issueId, label) =>\n Effect.gen(function* () {\n const trackerType = resolveTrackerTypeSync(issueId);\n if (trackerType !== 'github') return; // no-op for Linear/Rally\n\n const ghInfo = resolveGitHubIssueSync(issueId);\n if (!ghInfo.isGitHub) return;\n yield* github.addLabel(ghInfo.owner, ghInfo.repo, ghInfo.number, label);\n }),\n\n removeLabel: (issueId, label) =>\n Effect.gen(function* () {\n const trackerType = resolveTrackerTypeSync(issueId);\n if (trackerType !== 'github') return; // no-op for Linear/Rally\n\n const ghInfo = resolveGitHubIssueSync(issueId);\n if (!ghInfo.isGitHub) return;\n yield* github.removeLabel(ghInfo.owner, ghInfo.repo, ghInfo.number, label);\n }),\n\n close: (issueId) =>\n Effect.gen(function* () {\n const trackerType = resolveTrackerTypeSync(issueId);\n\n if (trackerType === 'github') {\n const ghInfo = resolveGitHubIssueSync(issueId);\n if (!ghInfo.isGitHub) return;\n // Remove workflow labels then close\n for (const label of ['in-progress', 'in-review', 'planned', 'in-planning', 'verifying-on-main', 'ready-for-merge']) {\n yield* github.removeLabel(ghInfo.owner, ghInfo.repo, ghInfo.number, label);\n }\n yield* github.closeIssue(ghInfo.owner, ghInfo.repo, ghInfo.number);\n } else if (trackerType === 'rally') {\n yield* rally.updateState(issueId, 'closed');\n } else {\n // Linear\n const issue = yield* linear.getIssue(issueId);\n const states = yield* linear.getTeamStates(issue.team.id);\n const target = findLinearState(states, 'closed');\n if (target) {\n yield* linear.updateState(issue.id, target.id);\n }\n }\n\n // Patch the in-memory cache and emit issue.closed domain event (non-fatal)\n yield* Effect.try({ try: () => getSharedIssueService().patchIssue(issueId, { canonicalStatus: 'closed' }), catch: () => void 0 }).pipe(Effect.ignore);\n yield* emitEvent({\n type: 'issue.closed',\n timestamp: new Date().toISOString(),\n payload: { issueId },\n });\n }),\n };\n\n return impl;\n }),\n);\n\n/**\n * Convenience layer that wires all tracker client dependencies.\n * Use this to provide IssueLifecycle without manually composing layers.\n */\nexport const IssueLifecycleWithClientLive = IssueLifecycleLive.pipe(\n Layer.provide(LinearClientOptionalLive),\n Layer.provide(GitHubClientOptionalLive),\n Layer.provide(\n // RallyClient is optional — provide a fallback that fails with TrackerNotConfigured\n Layer.effect(\n RallyClient,\n Effect.gen(function* () {\n // Dynamic: try to build RallyClientLive, fall back to stub if not configured\n return yield* Effect.gen(function* () {\n const { getRallyConfig } = yield* Effect.promise(() =>\n import('./tracker-config.js'),\n );\n const config = getRallyConfig();\n if (!config) {\n const fail = <A>(): Effect.Effect<A, TrackerNotConfigured> => Effect.fail(new TrackerNotConfigured({ tracker: 'rally' }));\n const fallback: RallyClientShape = {\n getIssue: () => fail(),\n getChildIssues: () => fail(),\n updateState: () => fail(),\n addComment: () => fail(),\n };\n return fallback;\n }\n // Delegate to RallyClientLive's logic\n return yield* RallyClient;\n }).pipe(Effect.provide(RallyClientLive));\n }),\n ),\n ),\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"issue-lifecycle-5LbOcATP.js","names":["applyEvent","applyEventReducer","readFile","stat","dirname"],"sources":["../../src/lib/queue-position.ts","../../src/dashboard/server/read-model.ts","../../src/lib/briefing-assembler.ts","../../src/dashboard/server/services/session-context-writer.ts","../../src/dashboard/server/services/domain-services.ts","../../src/dashboard/server/services/issue-lifecycle.ts"],"sourcesContent":["/**\n * Queue position utilities for the specialist pipeline (PAN-366).\n *\n * These pure helpers are extracted from the review-status API handler so they\n * can be unit-tested independently of the Express server.\n */\n\nimport { Effect } from 'effect';\nimport type { ReviewStatus } from './review-status.js';\nimport type { HookItem } from './hooks.js';\n\n/** Sentinel value meaning the specialist is actively processing this issue now. */\nexport const SPECIALIST_ACTIVE_POSITION = 0;\n\nexport interface QueuePositionResult {\n /** null = not queued; 0 = actively processing; 1+ = position in queue */\n queuePosition: number | null;\n /** Which specialist is handling (or will handle) this issue */\n activeSpecialist: 'review' | 'test' | 'merge' | null;\n}\n\n/**\n * Derive queue position from the persisted review status fields alone.\n * Returns {0, specialist} when the status indicates active processing,\n * or {null, null} when no active phase is detected (caller must check queues).\n */\nexport function computeQueuePositionFromStatusSync(\n status: Pick<ReviewStatus, 'reviewStatus' | 'testStatus' | 'mergeStatus'> | null\n): QueuePositionResult {\n if (status?.reviewStatus === 'reviewing') {\n return { queuePosition: SPECIALIST_ACTIVE_POSITION, activeSpecialist: 'review' };\n }\n if (status?.testStatus === 'testing') {\n return { queuePosition: SPECIALIST_ACTIVE_POSITION, activeSpecialist: 'test' };\n }\n if (status?.mergeStatus === 'merging') {\n return { queuePosition: SPECIALIST_ACTIVE_POSITION, activeSpecialist: 'merge' };\n }\n return { queuePosition: null, activeSpecialist: null };\n}\n\n/**\n * Find this issueId's 1-based position in a specialist's pending queue.\n * Returns -1 if not found (caller interprets as \"not queued\").\n */\nexport function findPositionInQueueSync(issueId: string, items: HookItem[]): number {\n const upper = issueId.toUpperCase();\n const idx = items.findIndex(\n (item) => item.payload?.issueId?.toUpperCase() === upper\n );\n return idx >= 0 ? idx + 1 : -1;\n}\n\n// ─── Effect variants (PAN-1249) ───────────────────────────────────────────────\n\n/** Effect variant of {@link computeQueuePositionFromStatusSync}. Pure; cannot fail. */\nexport const computeQueuePositionFromStatus = (\n status: Pick<ReviewStatus, 'reviewStatus' | 'testStatus' | 'mergeStatus'> | null,\n): Effect.Effect<QueuePositionResult, never> =>\n Effect.sync(() => computeQueuePositionFromStatusSync(status));\n\n/** Effect variant of {@link findPositionInQueueSync}. Pure; cannot fail. */\nexport const findPositionInQueue = (\n issueId: string,\n items: HookItem[],\n): Effect.Effect<number, never> =>\n Effect.sync(() => findPositionInQueueSync(issueId, items));\n","/**\n * Server-side Read Model — clean data architecture (PAN-433)\n *\n * Holds an in-memory projection of the dashboard state, bootstrapped once from\n * existing lib modules (JSON-cleaned), then maintained incrementally by domain\n * events via the shared applyEvent reducer.\n *\n * getSnapshot() returns the read model directly — no lib calls, no dirty data,\n * no Schema crashes. This is the T3Code pattern.\n */\n\nimport { Effect, Layer, Context } from 'effect';\nimport type { DashboardSnapshot, DomainEvent, TurnDiffSummary } from '@overdeck/contracts';\nimport {\n type ReadModelState,\n INITIAL_READ_MODEL_STATE,\n applyEvent as applyEventReducer,\n getMaxTurnDiffSummariesPerAgent,\n isTerminalTurnDiffSummaryStatus,\n trimTurnDiffSummaries,\n} from '@overdeck/contracts';\nimport type { AgentSnapshot, AgentStatus, Role, AgentResolution, ReviewStatusSnapshot, ReviewStatusValue, TestStatusValue, UatStatusValue, MergeStatusValue, VerificationStatusValue } from '@overdeck/contracts';\nimport type { ReviewStatus } from '../../lib/review-status.js';\nimport { listOverdeckAgentStatesSync } from '../../lib/overdeck/agent-state-sync.js';\nimport { computeQueuePositionFromStatusSync } from '../../lib/queue-position.js'\nimport { AgentsResolver, type Agent as OverdeckAgent } from '../../lib/overdeck/agents.js';\n\n// ─── Exported async helpers (used by bootstrap Effect + tests) ───────────────\n\nconst MAX_SNAPSHOT_ACTIVITY_ENTRIES = 50;\n\ntype DashboardSnapshotWithActivity = DashboardSnapshot & {\n recentActivity?: unknown[];\n};\n\nexport function activityEntriesFromStoredEvents(\n events: ReadonlyArray<{ timestamp: string; payload: unknown }>,\n): unknown[] {\n return events.slice(0, MAX_SNAPSHOT_ACTIVITY_ENTRIES).map((event) => {\n const entry = event.payload && typeof event.payload === 'object'\n ? event.payload as Record<string, unknown>\n : {};\n return { id: entry.id, timestamp: event.timestamp, ...entry };\n });\n}\n\n\n// PAN-1510: bootstrap previously only seeded `issuesRaw` from the projection\n// cache or replaced it wholesale from `issueService.getIssues()`. Both paths\n// missed issues filed during the previous dashboard's poll-write window — the\n// projection cache flush is debounced at 2s, so a fresh `cache.set('github',\n// 'issues', ...)` in IssueDataService can survive a restart while the\n// projection cache entry remains stale. The helpers below mirror PAN-1506's\n// `discoverNewAgentIds`/merge pattern for issues.\n\nexport function getIssueIdentifierKey(issue: unknown): string | null {\n if (!issue || typeof issue !== 'object') return null;\n const item = issue as { identifier?: unknown; id?: unknown };\n if (typeof item.identifier === 'string' && item.identifier.length > 0) {\n return item.identifier.toLowerCase();\n }\n if (typeof item.id === 'string' && item.id.length > 0) {\n return item.id.toLowerCase();\n }\n return null;\n}\n\nexport function discoverNewIssues(\n cachedIssues: unknown[],\n currentIssues: unknown[],\n): unknown[] {\n const cachedIds = new Set<string>();\n for (const issue of cachedIssues) {\n const id = getIssueIdentifierKey(issue);\n if (id !== null) cachedIds.add(id);\n }\n const newIssues: unknown[] = [];\n for (const issue of currentIssues) {\n const id = getIssueIdentifierKey(issue);\n if (id !== null && !cachedIds.has(id)) newIssues.push(issue);\n }\n return newIssues;\n}\n\nexport function mergeIssuesByIdentifier(\n cachedIssues: unknown[],\n currentIssues: unknown[],\n): unknown[] {\n const merged = new Map<string, unknown>();\n const unidentified: unknown[] = [];\n for (const issue of cachedIssues) {\n const id = getIssueIdentifierKey(issue);\n if (id !== null) merged.set(id, issue);\n else unidentified.push(issue);\n }\n for (const issue of currentIssues) {\n const id = getIssueIdentifierKey(issue);\n if (id !== null) merged.set(id, issue);\n }\n return [...unidentified, ...merged.values()];\n}\n\nexport function shouldSkipCheckpointReconciliation(agent: Pick<AgentSnapshot, 'status' | 'workspace'>): boolean {\n return !agent.workspace || isTerminalTurnDiffSummaryStatus(agent.status)\n}\n\ntype IssueReadSourceState = {\n identifier?: unknown;\n id?: unknown;\n status?: unknown;\n state?: unknown;\n canonicalStatus?: unknown;\n rawTrackerState?: unknown;\n completedAt?: unknown;\n}\n\nfunction normalizeIssueId(value: unknown): string | null {\n if (typeof value !== 'string') return null;\n const trimmed = value.trim();\n return trimmed ? trimmed.toUpperCase() : null;\n}\n\nexport function getClosedIssueIdsForReadSource(issues: unknown[]): Set<string> {\n const closed = new Set<string>();\n for (const issue of issues) {\n if (!issue || typeof issue !== 'object') continue;\n const item = issue as IssueReadSourceState;\n const issueId = normalizeIssueId(item.identifier) ?? normalizeIssueId(item.id);\n if (!issueId) continue;\n const state = String(item.state ?? '').toLowerCase();\n const status = String(item.status ?? '').toLowerCase();\n const canonicalStatus = String(item.canonicalStatus ?? '').toLowerCase();\n const rawTrackerState = String(item.rawTrackerState ?? '').toLowerCase();\n if (\n item.completedAt ||\n state === 'closed' ||\n status === 'done' ||\n status === 'closed' ||\n status === 'cancelled' ||\n status === 'canceled' ||\n status === 'completed' ||\n canonicalStatus === 'done' ||\n canonicalStatus === 'closed' ||\n canonicalStatus === 'cancelled' ||\n canonicalStatus === 'canceled' ||\n canonicalStatus === 'completed' ||\n rawTrackerState === 'closed' ||\n rawTrackerState === 'done' ||\n rawTrackerState === 'completed'\n ) {\n closed.add(issueId);\n }\n }\n return closed;\n}\n\nexport function pruneAgentsForReadSource(\n agentsById: Record<string, AgentSnapshot>,\n issues: unknown[],\n): { agentsById: Record<string, AgentSnapshot>; prunedCount: number } {\n const closedIssueIds = getClosedIssueIdsForReadSource(issues);\n // PAN-1908: authoritative membership is the SQLite agents table, not state.json.\n const liveAgentIds = new Set(listOverdeckAgentStatesSync().map(a => a.id));\n const nextAgentsById: Record<string, AgentSnapshot> = {};\n let prunedCount = 0;\n\n for (const agent of Object.values(agentsById)) {\n if (closedIssueIds.has(agent.issueId.toUpperCase())) {\n prunedCount++;\n continue;\n }\n if (!liveAgentIds.has(agent.id)) {\n prunedCount++;\n continue;\n }\n nextAgentsById[agent.id] = agent;\n }\n\n return { agentsById: nextAgentsById, prunedCount };\n}\n\n// ─── Cached event store reference (avoids async dynamic import on each pushUpdated) ──\nlet _cachedEventStore: any = null;\n\ntype Jsonish = null | boolean | number | string | Jsonish[] | { [key: string]: Jsonish };\n\nfunction toJsonish(value: unknown, seen = new WeakSet<object>()): Jsonish | undefined {\n if (value == null) return null;\n if (typeof value === 'string' || typeof value === 'boolean') return value;\n if (typeof value === 'number') return Number.isFinite(value) ? value : null;\n if (value instanceof Date) return value.toISOString();\n if (Array.isArray(value)) {\n const next: Jsonish[] = [];\n for (const item of value) {\n const clean = toJsonish(item, seen);\n if (clean !== undefined) next.push(clean);\n }\n return next;\n }\n if (typeof value === 'object') {\n if (seen.has(value)) return null;\n seen.add(value);\n const next: { [key: string]: Jsonish } = {};\n for (const [key, item] of Object.entries(value as Record<string, unknown>)) {\n const clean = toJsonish(item, seen);\n if (clean !== undefined) next[key] = clean;\n }\n seen.delete(value);\n return next;\n }\n return undefined;\n}\n\nfunction cleanIssues(issues: unknown[]): unknown[] {\n return issues.map((issue) => toJsonish(issue) ?? null);\n}\n\n// ─── Value validators for strict literal types ──────────────────────────────\n\nconst VALID_AGENT_STATUSES = new Set<AgentStatus>([\"starting\", \"running\", \"stopped\", \"error\", \"unknown\"]);\nconst VALID_ROLES = new Set<Role>([\"plan\", \"work\", \"review\", \"test\", \"ship\", \"flywheel\", \"strike\", \"sequencer\", \"knowledge\"]);\nconst VALID_RESOLUTIONS = new Set<AgentResolution>([\"working\", \"done\", \"needs_input\", \"stuck\", \"completed\", \"unclear\", \"abandoned\", \"api_error\"]);\ntype SpecialistAgentName = 'review-agent' | 'test-agent' | 'merge-agent' | 'inspect-agent' | 'uat-agent';\ntype SpecialistLifecycleState = 'active' | 'sleeping' | 'uninitialized';\n\nconst VALID_SPECIALIST_NAMES = new Set<SpecialistAgentName>([\"review-agent\", \"test-agent\", \"merge-agent\", \"inspect-agent\", \"uat-agent\"]);\nconst VALID_SPECIALIST_LIFECYCLE_STATES = new Set<SpecialistLifecycleState>([\"active\", \"sleeping\", \"uninitialized\"]);\nconst VALID_REVIEW_STATUSES = new Set<ReviewStatusValue>([\"pending\", \"reviewing\", \"passed\", \"failed\", \"blocked\", \"skipped\"]);\nconst VALID_TEST_STATUSES = new Set<TestStatusValue>([\"pending\", \"testing\", \"passed\", \"failed\", \"skipped\", \"dispatch_failed\"]);\nconst VALID_UAT_STATUSES = new Set<UatStatusValue>([\"pending\", \"testing\", \"passed\", \"failed\"]);\nconst VALID_MERGE_STATUSES = new Set<MergeStatusValue>([\"pending\", \"queued\", \"merging\", \"verifying\", \"merged\", \"failed\"]);\nconst VALID_VERIFICATION_STATUSES = new Set<VerificationStatusValue>([\"pending\", \"running\", \"passed\", \"failed\", \"skipped\"]);\n\nexport function toAgentStatus(v: unknown): AgentStatus {\n return VALID_AGENT_STATUSES.has(v as AgentStatus) ? v as AgentStatus : \"unknown\";\n}\n\n/** Pending-input fields carried by an agent snapshot (PAN-1591). */\ntype PendingInputFields = Pick<\n AgentSnapshot,\n | 'hasPendingQuestion'\n | 'pendingQuestionCount'\n | 'pendingQuestionPrompt'\n | 'pendingQuestionReason'\n | 'pendingInputCount'\n | 'pendingInputKinds'\n | 'pendingAskUserQuestion'\n>;\n\n/**\n * PAN-1591 — a non-running agent cannot be awaiting interactive input. The\n * enrichment poller only emits for live agents, so a `hasPendingQuestion: true`\n * computed while the agent was last alive lingers in the cache forever; the\n * `agent.status_changed` reducer already strips it on a RUNTIME stop, but the\n * bootstrap projection of an agent that was ALREADY stopped at server start\n * never did — surfacing phantom \"Waiting on your input\" rows that report \"no\n * longer waiting\" on click. This applies the same rule at projection time:\n * clear every pending-input field unless the agent is running/starting.\n */\nexport function projectPendingInput(status: AgentStatus, src: PendingInputFields): PendingInputFields {\n if (status !== 'running' && status !== 'starting') {\n return {\n hasPendingQuestion: undefined,\n pendingQuestionCount: undefined,\n pendingQuestionPrompt: undefined,\n pendingQuestionReason: undefined,\n pendingInputCount: undefined,\n pendingInputKinds: undefined,\n pendingAskUserQuestion: undefined,\n };\n }\n return src;\n}\nexport function toRole(v: unknown): Role | undefined {\n return v && VALID_ROLES.has(v as Role) ? v as Role : undefined;\n}\n\nexport function toAgentResolution(v: unknown): AgentResolution | undefined {\n return v && VALID_RESOLUTIONS.has(v as AgentResolution) ? v as AgentResolution : undefined;\n}\nexport function toSpecialistAgentName(v: unknown): SpecialistAgentName | undefined {\n return VALID_SPECIALIST_NAMES.has(v as SpecialistAgentName) ? v as SpecialistAgentName : undefined;\n}\nexport function toSpecialistLifecycleState(v: unknown): SpecialistLifecycleState {\n return VALID_SPECIALIST_LIFECYCLE_STATES.has(v as SpecialistLifecycleState) ? v as SpecialistLifecycleState : \"uninitialized\";\n}\nexport function toReviewStatus(v: unknown): ReviewStatusValue | undefined {\n return v && VALID_REVIEW_STATUSES.has(v as ReviewStatusValue) ? v as ReviewStatusValue : undefined;\n}\nexport function toTestStatus(v: unknown): TestStatusValue | undefined {\n return v && VALID_TEST_STATUSES.has(v as TestStatusValue) ? v as TestStatusValue : undefined;\n}\nexport function toUatStatus(v: unknown): UatStatusValue | undefined {\n return v && VALID_UAT_STATUSES.has(v as UatStatusValue) ? v as UatStatusValue : undefined;\n}\nexport function toMergeStatus(v: unknown): MergeStatusValue | undefined {\n return v && VALID_MERGE_STATUSES.has(v as MergeStatusValue) ? v as MergeStatusValue : undefined;\n}\nexport function toVerificationStatus(v: unknown): VerificationStatusValue | undefined {\n return v && VALID_VERIFICATION_STATUSES.has(v as VerificationStatusValue) ? v as VerificationStatusValue : undefined;\n}\n\ntype ReviewStatusSnapshotInput = ReviewStatus & {\n reviewCoordinatorSessionName?: string;\n reviewSessionNames?: string[];\n reviewSubStatuses?: Record<string, 'running' | 'done'>;\n activeSpecialist?: string;\n};\n\nexport function toReviewStatusSnapshot(status: ReviewStatusSnapshotInput): ReviewStatusSnapshot {\n return {\n issueId: status.issueId,\n reviewStatus: toReviewStatus(status.reviewStatus),\n testStatus: toTestStatus(status.testStatus),\n uatStatus: toUatStatus(status.uatStatus),\n uatNotes: status.uatNotes || undefined,\n mergeStatus: toMergeStatus(status.mergeStatus),\n verificationStatus: toVerificationStatus(status.verificationStatus),\n verificationNotes: status.verificationNotes || undefined,\n verificationCycleCount: typeof status.verificationCycleCount === 'number' ? status.verificationCycleCount : undefined,\n readyForMerge: !!status.readyForMerge,\n updatedAt: status.updatedAt,\n prUrl: status.prUrl || undefined,\n stuck: !!status.stuck ? true : undefined,\n stuckReason: status.stuckReason || undefined,\n stuckAt: status.stuckAt || undefined,\n stuckDetails: status.stuckDetails || undefined,\n reviewedAtCommit: status.reviewedAtCommit || undefined,\n reviewSpawnedAt: status.reviewSpawnedAt || undefined,\n testRetryCount: typeof status.testRetryCount === 'number' ? status.testRetryCount : undefined,\n reviewRetryCount: typeof status.reviewRetryCount === 'number' ? status.reviewRetryCount : undefined,\n recoveryStartedAt: status.recoveryStartedAt || undefined,\n deaconIgnored: !!status.deaconIgnored ? true : undefined,\n deaconIgnoredAt: status.deaconIgnoredAt || undefined,\n deaconIgnoredReason: status.deaconIgnoredReason || undefined,\n // PAN-1691: tri-state routing key — preserve true/false/undefined as-is.\n autoMerge: status.autoMerge,\n reviewCoordinatorSessionName: status.reviewCoordinatorSessionName || undefined,\n reviewSessionNames: status.reviewSessionNames && status.reviewSessionNames.length > 0 ? status.reviewSessionNames : undefined,\n reviewSubStatuses: status.reviewSubStatuses,\n queuePosition: computeQueuePositionFromStatusSync(status).queuePosition ?? undefined,\n activeSpecialist: status.activeSpecialist || undefined,\n mergeRetryCount: typeof status.mergeRetryCount === 'number' ? status.mergeRetryCount : undefined,\n mergeNotes: status.mergeNotes || undefined,\n blockerReasons: status.blockerReasons && status.blockerReasons.length > 0 ? status.blockerReasons : undefined,\n autoRequeueCount: typeof status.autoRequeueCount === 'number' ? status.autoRequeueCount : undefined,\n };\n}\n\n// ─── ReadModelService ────────────────────────────────────────────────────────\n\nexport interface ReadModelServiceShape {\n /** Return the current read model state as a DashboardSnapshot. */\n readonly getSnapshot: Effect.Effect<DashboardSnapshotWithActivity>;\n /** Return a single pending channel permission request without rebuilding a full snapshot. */\n readonly getChannelPermissionRequest: (\n requestId: string,\n ) => Effect.Effect<import('@overdeck/contracts').ChannelPermissionRequestSnapshot | null>;\n /** Return a recent resolved channel permission decision for safe delivery retries. */\n readonly getResolvedChannelPermissionDecision: (\n requestId: string,\n ) => Effect.Effect<import('@overdeck/contracts').ResolvedChannelPermissionDecision | null>;\n /** Return in-memory turn diff summaries for a single agent. */\n readonly getTurnDiffSummaries: (agentId: string) => Effect.Effect<TurnDiffSummary[]>;\n /** Return the agentId for a given sessionId (from agent snapshot or runtime claudeSessionId). */\n readonly getAgentIdBySessionId: (sessionId: string) => Effect.Effect<string | null>;\n /** Apply a domain event to the read model (called by event store on append). */\n readonly applyEvent: (event: DomainEvent) => void;\n /** Bootstrap the read model from existing lib module state. */\n readonly bootstrap: Effect.Effect<void>;\n}\n\n// ─── Overdeck → legacy AgentSnapshot adapter ─────────────────────────────────\n//\n// Maps overdeck's 18-field Agent (durable config only) to the legacy\n// AgentSnapshot wire format. Runtime/ephemeral fields (lastActivity, branch,\n// costSoFar, phase, hasPendingQuestion, etc.) start undefined and are filled\n// by in-memory events from the enrichment poller and domain event stream.\n\nfunction overdeckStatusToLegacy(\n status: OverdeckAgent['status'],\n): AgentStatus {\n if (status === 'crashed') return 'error';\n // 'idle'/'waiting' = agent is alive but waiting (tool-call paused, AUQ, etc.)\n if (status === 'idle' || status === 'waiting') return 'running';\n return status; // 'starting' | 'running' | 'stopped' are 1:1\n}\n\nfunction agentSnapshotFromOverdeck(agent: OverdeckAgent): AgentSnapshot {\n return {\n id: agent.id,\n issueId: agent.issueId,\n workspace: agent.workspace || undefined,\n runtime: agent.harness,\n model: agent.model,\n status: overdeckStatusToLegacy(agent.status),\n startedAt: agent.startedAt?.toISOString(),\n sessionId: agent.sessionId ?? undefined,\n role: agent.role,\n stoppedByUser: agent.stoppedByUser ?? undefined,\n paused: agent.paused ?? undefined,\n pausedReason: agent.pausedReason ?? undefined,\n troubled: agent.troubled ?? undefined,\n consecutiveFailures: agent.consecutiveFailures,\n firstFailureInRunAt: agent.firstFailureInRunAt?.toISOString(),\n lastFailureNextRetryAt: agent.lastFailureNextRetryAt?.toISOString(),\n };\n}\n\nexport class ReadModelService extends Context.Service<\n ReadModelService,\n ReadModelServiceShape\n>()('overdeck/dashboard/ReadModelService') {}\n\n// ─── Live implementation ─────────────────────────────────────────────────────\n\nexport const ReadModelServiceLive = Layer.effect(\n ReadModelService,\n Effect.gen(function* () {\n let state: ReadModelState = { ...INITIAL_READ_MODEL_STATE };\n\n function cloneTurnDiffSummaries(summaries: TurnDiffSummary[] | undefined): TurnDiffSummary[] {\n if (!summaries || summaries.length === 0) return [];\n return summaries.map(summary => ({\n ...summary,\n files: summary.files.map(file => ({ ...file })),\n assistantMessageId: summary.assistantMessageId ?? undefined,\n checkpointRef: summary.checkpointRef ?? undefined,\n }));\n }\n\n function buildSnapshot(): DashboardSnapshotWithActivity {\n // turnDiffSummariesByAgentId is intentionally excluded from the snapshot.\n //\n // Per-agent checkpoint history can grow to thousands of turns × hundreds\n // of files; in production we measured 484 MB across 44 agents, which the\n // browser's WebSocket client rejects as \"Max payload size exceeded\" and\n // closes the socket with code 1006 — leaving the kanban and command deck\n // perpetually empty. The data is still maintained in `state` and served\n // on-demand via GET /api/agents/:id/diffs, so chat-timeline components\n // fetch it only for the agent the user is actually viewing.\n return {\n sequence: state.sequence,\n agents: Object.values(state.agentsById),\n // PAN-1048 — specialistsByName projection retired. The DashboardSnapshot\n // schema still has a `specialists` field for backward compat with the\n // wire format; we always send an empty array and clients derive the\n // same data from agentsById filtered by role.\n specialists: [],\n reviewStatuses: Object.values(state.reviewStatusByIssueId),\n agentRuntimeById: state.agentRuntimeById,\n channelPermissionRequests: Object.values(state.channelPermissionRequestsById ?? {}),\n issues: state.issuesRaw,\n recentActivity: state.recentActivity,\n resources: state.resources ?? undefined,\n memory: {\n observationsByIssueId: state.observationsByIssueId,\n statusByIssueId: state.statusByIssueId,\n rollupsByIssueId: state.rollupsByIssueId,\n resetMarkersByScopeId: state.resetMarkersByScopeId,\n healthByIssueId: state.healthByIssueId,\n },\n scanProgress: state.scanProgress,\n enrichStats: state.enrichStats,\n enrichProgressBySessionId: state.enrichProgressBySessionId,\n embedProgressBySessionId: state.embedProgressBySessionId,\n timestamp: new Date().toISOString(),\n };\n }\n\n const applyEvent = (event: DomainEvent): void => {\n state = applyEventReducer(state, event);\n };\n\n const getSnapshot: Effect.Effect<DashboardSnapshotWithActivity> = Effect.gen(function* () {\n // Refresh issues from the shared issue service before building snapshot.\n // IssueDataService polls trackers in the background; its cached issues are\n // the freshest available without blocking on API calls.\n //\n // PAN-1510: merge issueService's view with the current state.issuesRaw\n // so a hard browser reload always reflects the union of (projection\n // cache + freshest issueService data). issueService entries win on\n // identifier conflicts (fresher status, labels, etc.), and cached\n // entries that issueService is missing (transient empty fetch,\n // partial poll, single-tracker failure) are preserved instead of\n // dropped. Identifier-based merge is the same shape PAN-1506 used to\n // surface newly-spawned agents through the bootstrap fast-path.\n try {\n const { getSharedIssueService } = yield* Effect.promise(\n () => import('./services/issue-service-singleton.js'),\n );\n const issueService = getSharedIssueService();\n const currentIssues = cleanIssues(issueService.getIssues());\n if (currentIssues.length > 0 || state.issuesRaw.length === 0) {\n state = {\n ...state,\n issuesRaw: mergeIssuesByIdentifier(state.issuesRaw, currentIssues),\n };\n }\n } catch (err) {\n console.error('[ReadModel] Failed to refresh issues for snapshot:', err);\n }\n\n const pruned = pruneAgentsForReadSource(state.agentsById, state.issuesRaw);\n if (pruned.prunedCount > 0) {\n state = { ...state, agentsById: pruned.agentsById };\n console.log(`[ReadModel] Pruned ${pruned.prunedCount} stale agent${pruned.prunedCount === 1 ? '' : 's'} from read source`);\n }\n\n return buildSnapshot();\n });\n\n const getChannelPermissionRequest = (\n requestId: string,\n ): Effect.Effect<import('@overdeck/contracts').ChannelPermissionRequestSnapshot | null> =>\n Effect.succeed(state.channelPermissionRequestsById?.[requestId] ?? null);\n\n const getResolvedChannelPermissionDecision = (\n requestId: string,\n ): Effect.Effect<import('@overdeck/contracts').ResolvedChannelPermissionDecision | null> =>\n Effect.succeed(state.resolvedChannelPermissionDecisionsById?.[requestId] ?? null);\n\n const getTurnDiffSummaries = (agentId: string): Effect.Effect<TurnDiffSummary[]> =>\n Effect.sync(() => cloneTurnDiffSummaries(state.turnDiffSummariesByAgentId[agentId]));\n\n const getAgentIdBySessionId = (sessionId: string): Effect.Effect<string | null> =>\n Effect.sync(() => state.agentIdBySessionId[sessionId] ?? null);\n\n // ── Bootstrap inline during layer construction ───────────────────────────\n const agentsResolver = yield* AgentsResolver;\n yield* Effect.gen(function* () {\n // PAN-1938 source-swap: agents now come from overdeck.db via AgentsResolver.\n // reconstructCache still runs for reviewStatusByIssueId (which reads\n // git-backed per-issue records, NOT panopticon.db SQLite cache tables)\n // and for its side effects (agent-backfill sync, checkpoint cleanup).\n const { reconstructCacheAuto } = yield* Effect.promise(() =>\n import('../../lib/reconstruct/reconstruct-cache.js'),\n );\n\n const [overdeckAgents, result] = yield* Effect.all([\n agentsResolver.list({}),\n Effect.promise(() => reconstructCacheAuto()),\n ]);\n\n const agentsById: Record<string, AgentSnapshot> = Object.fromEntries(\n overdeckAgents.map((a) => [a.id, agentSnapshotFromOverdeck(a)]),\n );\n\n // ── Sequence from event store (labels the snapshot, not a replay source) ─\n let sequence = 0;\n let recentActivity: unknown[] = [];\n try {\n const { getEventStore } = yield* Effect.promise(\n () => import('./event-store.js'),\n );\n const eventStore = getEventStore();\n sequence = eventStore.getLatestSequence();\n recentActivity = activityEntriesFromStoredEvents(\n eventStore.queryByType('activity.entry', MAX_SNAPSHOT_ACTIVITY_ENTRIES),\n );\n } catch {\n // Event store may not be initialized yet\n }\n\n state = {\n ...INITIAL_READ_MODEL_STATE,\n sequence,\n agentsById,\n reviewStatusByIssueId: result.reviewStatusByIssueId,\n issuesRaw: [],\n recentActivity,\n };\n\n console.log(\n `[ReadModel] Bootstrapped from local database: ` +\n `${Object.keys(agentsById).length} agents, ` +\n `${Object.keys(result.reviewStatusByIssueId).length} review statuses, ` +\n `${result.issuesEnumerated} in-flight issue(s), seq=${sequence}`,\n );\n\n // ── Checkpoint reconciliation (deferred — non-blocking) ──────────────────\n // Fire-and-forget: scan workspaces for git checkpoints in the background\n // so the ReadModel layer resolves immediately and the dashboard starts fast.\n void (async () => {\n try {\n const { listCheckpoints, diffCheckpointFiles, getCheckpointTimestamp, deleteLegacyCheckpointRefs } = await import('../../lib/checkpoint/checkpoint-manager.js');\n\n const agents = Object.values(state.agentsById);\n\n // One-time: clean up unscoped legacy refs from before per-agent namespacing.\n // Run against the first agent's workspace (all worktrees share the same parent .git).\n const firstAgentWithWorkspace = agents.find(a => a.workspace);\n if (firstAgentWithWorkspace?.workspace) {\n const deleted = await Effect.runPromise(deleteLegacyCheckpointRefs(firstAgentWithWorkspace.workspace));\n if (deleted > 0) {\n console.log(`[ReadModel] Deleted ${deleted} legacy unscoped checkpoint refs`);\n }\n }\n let reconciled = 0;\n for (const agent of agents) {\n if (shouldSkipCheckpointReconciliation(agent)) continue;\n\n const workspace = agent.workspace;\n if (!workspace) continue;\n const existingSummaries = state.turnDiffSummariesByAgentId[agent.id];\n if (existingSummaries && existingSummaries.length > 0) continue;\n\n try {\n const checkpoints = await Effect.runPromise(listCheckpoints(workspace, agent.id));\n if (checkpoints.length === 0) continue;\n\n const maxRetainedSummaries = getMaxTurnDiffSummariesPerAgent();\n const retainedCheckpoints = checkpoints.length > maxRetainedSummaries\n ? checkpoints.slice(-maxRetainedSummaries)\n : checkpoints;\n const checkpointOffset = checkpoints.length - retainedCheckpoints.length;\n\n const summaries: Array<{\n turnId: string;\n completedAt: string;\n files: Array<{ path: string; kind?: string; additions?: number; deletions?: number }>;\n checkpointRef?: string;\n assistantMessageId?: string;\n checkpointTurnCount?: number;\n }> = [];\n\n for (let i = 0; i < retainedCheckpoints.length; i++) {\n const absoluteIndex = checkpointOffset + i;\n const turnId = retainedCheckpoints[i];\n if (!turnId) continue;\n const prevTurnId = absoluteIndex > 0 ? checkpoints[absoluteIndex - 1] ?? null : null;\n let files: Array<{ path: string; kind?: string; additions?: number; deletions?: number }> = [];\n if (prevTurnId) {\n try {\n files = await Effect.runPromise(diffCheckpointFiles(workspace, agent.id, prevTurnId, turnId));\n } catch { /* checkpoint might be stale */ }\n }\n const completedAt = await Effect.runPromise(getCheckpointTimestamp(workspace, agent.id, turnId));\n summaries.push({\n turnId,\n completedAt,\n files,\n checkpointRef: `refs/pan/turn/${agent.id}/${turnId}`,\n checkpointTurnCount: absoluteIndex + 1,\n });\n }\n\n if (summaries.length > 0) {\n state = {\n ...state,\n turnDiffSummariesByAgentId: {\n ...state.turnDiffSummariesByAgentId,\n [agent.id]: trimTurnDiffSummaries(summaries),\n },\n };\n reconciled++;\n }\n } catch { /* agent workspace may not be a git repo */ }\n }\n\n if (reconciled > 0) {\n console.log(`[ReadModel] Reconciled checkpoints for ${reconciled} agent(s)`);\n }\n } catch (err) {\n console.warn('[ReadModel] Checkpoint reconciliation failed:', err);\n }\n })();\n\n // ── Issue listener (always) ──────────────────────────────────────────────\n // Issues come from external trackers (Linear/GitHub) with unpredictable shapes.\n // JSON round-trip strips undefined values that can't be serialized over WebSocket.\n try {\n const { getSharedIssueService } = yield* Effect.promise(\n () => import('./services/issue-service-singleton.js'),\n );\n const issueService = getSharedIssueService();\n\n // PAN-1510: merge issueService's view with whatever the projection\n // cache loaded so newly-filed issues (filed during the previous\n // session's debounced-flush window, or already loaded into\n // IssueDataService's in-memory cache before the read model bootstrap\n // wired its onIssuesChanged callback) reach `issuesRaw`. Without the\n // merge, the bootstrap window between issueService.start() loading\n // its SQLite cache and read-model wiring `onIssuesChanged` could\n // strand fresh issues — the subsequent `pushSnapshot` would either\n // hit a null callback or be skipped by `issuesChanged()` because\n // `lastFetchedIssues` already matched the new GitHub fetch.\n const mergeT0 = performance.now();\n const currentIssues = cleanIssues(issueService.getIssues());\n if (currentIssues.length > 0 || state.issuesRaw.length === 0) {\n const newIssues = discoverNewIssues(state.issuesRaw, currentIssues);\n if (newIssues.length > 0) {\n const sample = newIssues\n .slice(0, 5)\n .map(i => getIssueIdentifierKey(i) ?? 'unknown')\n .join(', ');\n const more = newIssues.length > 5 ? `, +${newIssues.length - 5} more` : '';\n console.log(\n `[ReadModel] Bootstrap: merging ${newIssues.length} new issue(s) from issueService not in projection cache (${sample}${more})`,\n );\n }\n state = {\n ...state,\n issuesRaw: mergeIssuesByIdentifier(state.issuesRaw, currentIssues),\n };\n }\n console.log(`[boot-timing] ReadModel bootstrap merge completed at +${Math.round(performance.now() - mergeT0)}ms`);\n\n // Wire live issue updates — when IssueDataService polls new data,\n // update the read model directly AND emit to event store for\n // WebSocket subscribers (PAN-433).\n issueService.onIssuesChanged((issues) => {\n const cleaned = cleanIssues(issues);\n state = { ...state, issuesRaw: cleaned };\n\n // Fan-out issues.snapshot to live WebSocket subscribers via in-memory PubSub.\n // Uses emitOnly (NOT append) — issues.snapshot is ~1.5 MB and must never be\n // persisted to the event log. Persisting it causes startup OOM on replay.\n // Uses cached reference to avoid async dynamic import delay\n // (delay caused frontend to miss updates after patchIssue)\n try {\n if (!_cachedEventStore) {\n import('./event-store.js').then(({ getEventStore }) => {\n _cachedEventStore = getEventStore();\n try {\n _cachedEventStore.emitOnly({\n type: 'issues.snapshot',\n timestamp: new Date().toISOString(),\n payload: { issues: cleaned },\n } as any);\n } catch { /* event store not ready */ }\n }).catch(() => {});\n } else {\n _cachedEventStore.emitOnly({\n type: 'issues.snapshot',\n timestamp: new Date().toISOString(),\n payload: { issues: cleaned },\n } as any);\n }\n } catch { /* event store not ready yet */ }\n\n // PAN-1866: debounced incremental sequencer pass on every backlog delta\n import('../../lib/backlog/backlog-auto-trigger.js').then(({ triggerDebouncedIncrementalPass }) => {\n triggerDebouncedIncrementalPass(process.cwd());\n }).catch(() => {});\n });\n } catch {\n console.warn('[ReadModel] IssueDataService not available at bootstrap, starting with empty issues');\n }\n });\n\n return {\n getSnapshot,\n getChannelPermissionRequest,\n getResolvedChannelPermissionDecision,\n getTurnDiffSummaries,\n getAgentIdBySessionId,\n applyEvent,\n bootstrap: Effect.void,\n };\n }),\n);\n","import { readFile, stat } from 'fs/promises';\nimport { basename, dirname, join, resolve } from 'path';\nimport type { DashboardSnapshot, FeatureRegistryEntry } from '@overdeck/contracts';\nimport { resolveStatusFile } from './memory/paths.js';\n\nexport interface AssembleLiveBriefingInput {\n cwd?: string;\n projectId?: string;\n snapshot?: DashboardSnapshot;\n registryEntries?: FeatureRegistryEntry[];\n now?: Date;\n}\n\ninterface WorkspaceContext {\n workspacePath: string;\n workspaceId: string;\n issueId: string;\n planTitle: string | null;\n}\n\ninterface BriefingStatus {\n headline?: string;\n summary?: string;\n phase?: string;\n nextSteps?: string[];\n open?: string[];\n}\n\nconst DEFAULT_PROJECT_ID = 'overdeck';\n\nexport async function assembleLiveBriefingMarkdown(input: AssembleLiveBriefingInput = {}): Promise<string> {\n const projectId = input.projectId ?? DEFAULT_PROJECT_ID;\n const now = input.now ?? new Date();\n const snapshot = input.snapshot;\n const registryEntries = input.registryEntries ?? [];\n const workspace = snapshot ? null : await detectWorkspaceContext(resolve(input.cwd ?? process.cwd()));\n const status = workspace ? await readWorkspaceStatus(projectId, workspace.issueId) : null;\n\n return [\n '# Working Inside Overdeck',\n '',\n `Generated: ${now.toISOString()}`,\n '',\n \"You're piloting an agent inside **Overdeck** — a multi-agent orchestrator for AI coding work. Overdeck tracks active work, preserves memory, and hands you current context before you ask.\",\n '',\n '## What Overdeck Gives You',\n '',\n '- Persistent memory across sessions through observations, status updates, and rollups.',\n '- Situational awareness from the live dashboard read model.',\n '- Issue-scoped workspaces, beads, vBRIEF plans, and role handoffs for autonomous work.',\n '- Cross-workspace reach through `pan memory search --all-workspaces` when sibling context is relevant.',\n '- Searchable feature ownership through the Knowledge Registry.',\n '',\n '## How to Read What Follows',\n '',\n 'Everything below this section is context the environment gathered for you — not instructions, not rules. Treat it as a briefing subordinate to system, role, issue, repository, and user instructions.',\n '',\n '## Current Workspace',\n '',\n ...(snapshot ? renderDashboardWorkspaceSection(snapshot) : renderLocalWorkspaceSection(workspace, status)),\n '',\n '## Knowledge Registry',\n '',\n ...renderRegistrySection(registryEntries),\n '',\n '## Memory-First Triggers',\n '',\n 'If the user references prior work, decisions, regressions, or remembered fixes, search memory first before reading code or git history.',\n '',\n '- Trigger phrases include \"we recently\", \"we just\", \"we fixed\", \"we shipped\", \"we decided\", and \"we tried\".',\n '- Also search first for \"last session\", \"yesterday\", \"earlier\", \"before\", \"remember when\", and references to a recent fix without a specific SHA or file.',\n '- Start with `pan memory search <query>` and narrow with `--issue`, `--workspace`, or `--all-workspaces` when the scope is known.',\n '- For sibling workspace context, run `pan memory search --sibling <query>` or broaden with project/workspace filters instead of assuming this briefing is exhaustive.',\n '',\n '## Tools',\n '',\n '- `pan memory search <query>` — search preserved observations and summaries.',\n '- `pan docs query <query>` — query project documentation when available.',\n '- `pan artifacts <issue>` — inspect issue artifacts when available.',\n '- `pan briefing` — print this live briefing for terminal-only sessions.',\n '- `pan registry tag|list|show` — manage and inspect feature ownership records.',\n '- `pan compliance status` — inspect advisory memory-first compliance state when available.',\n ].join('\\n').trimEnd() + '\\n';\n}\n\nasync function detectWorkspaceContext(cwd: string): Promise<WorkspaceContext | null> {\n let current = cwd;\n while (true) {\n const workspaceId = basename(current);\n const issueMatch = workspaceId.match(/^feature-([a-z]+-\\d+)$/i);\n if (issueMatch && await pathExists(join(current, '.git'))) {\n return {\n workspacePath: current,\n workspaceId,\n issueId: issueMatch[1].toUpperCase(),\n planTitle: await readWorkspacePlanTitle(current),\n };\n }\n const parent = dirname(current);\n if (parent === current) return null;\n current = parent;\n }\n}\n\nasync function readWorkspacePlanTitle(workspacePath: string): Promise<string | null> {\n const path = join(workspacePath, '.pan', 'spec.vbrief.json');\n try {\n const parsed = JSON.parse(await readFile(path, 'utf8')) as { plan?: { title?: unknown } };\n return typeof parsed.plan?.title === 'string' ? parsed.plan.title : null;\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n}\n\nasync function readWorkspaceStatus(projectId: string, issueId: string): Promise<BriefingStatus | null> {\n try {\n const parsed = JSON.parse(await readFile(resolveStatusFile(projectId, issueId), 'utf8')) as BriefingStatus;\n return parsed && typeof parsed === 'object' ? parsed : null;\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n}\n\nfunction renderLocalWorkspaceSection(workspace: WorkspaceContext | null, status: BriefingStatus | null): string[] {\n if (!workspace) {\n return [\n '- No Overdeck workspace was detected from the current directory.',\n '- Run `pan briefing` from inside a `workspaces/feature-<issue>` checkout to include workspace-specific context.',\n ];\n }\n\n const lines = [\n `- Workspace: ${workspace.workspaceId}`,\n `- Issue: ${workspace.issueId}`,\n `- Path: ${workspace.workspacePath}`,\n ];\n if (workspace.planTitle) lines.push(`- Plan: ${workspace.planTitle}`);\n if (status?.phase) lines.push(`- Phase: ${status.phase}`);\n if (status?.headline) lines.push(`- Headline: ${status.headline}`);\n if (status?.summary) lines.push(`- Summary: ${status.summary}`);\n if (status?.nextSteps?.length) lines.push(`- Next steps: ${status.nextSteps.join('; ')}`);\n if (status?.open?.length) lines.push(`- Open questions: ${status.open.join('; ')}`);\n return lines;\n}\n\nfunction renderDashboardWorkspaceSection(snapshot: DashboardSnapshot): string[] {\n const runningAgents = snapshot.agents.filter((agent) => agent.status === 'running' || agent.hasLiveTmuxSession);\n const pausedAgents = snapshot.agents.filter((agent) => agent.paused);\n const troubledAgents = snapshot.agents.filter((agent) => agent.troubled || (agent.consecutiveFailures ?? 0) > 0);\n const activeIssues = new Set(snapshot.agents.map((agent) => agent.issueId).filter(Boolean));\n const failedReviews = snapshot.reviewStatuses.filter((status) =>\n status.testStatus === 'failed' ||\n status.uatStatus === 'failed' ||\n status.verificationStatus === 'failed' ||\n status.reviewStatus === 'failed' ||\n (status.blockerReasons?.length ?? 0) > 0,\n );\n\n const lines = [\n `- Running agents: ${runningAgents.length}`,\n `- Active issues with agents: ${activeIssues.size}`,\n `- Paused gates: ${pausedAgents.length}`,\n `- Troubled agents: ${troubledAgents.length}`,\n `- Failed verification/review states needing attention: ${failedReviews.length}`,\n ];\n\n const recentStatuses = Object.entries(readStatusByIssue(snapshot))\n .slice(0, 8)\n .map(([issueId, status]) => {\n const headline = typeof status.headline === 'string' && status.headline.trim()\n ? status.headline.trim()\n : 'No headline recorded';\n const phase = typeof status.phase === 'string' && status.phase.trim()\n ? ` (${status.phase.trim()})`\n : '';\n return `- ${issueId}${phase}: ${headline}`;\n });\n\n if (recentStatuses.length > 0) {\n lines.push('', 'Recent workspace statuses:', ...recentStatuses);\n } else {\n lines.push('', '- No workspace status summaries are currently available.');\n }\n\n return lines;\n}\n\nfunction readStatusByIssue(snapshot: DashboardSnapshot): Record<string, { headline?: unknown; phase?: unknown }> {\n const memory = (snapshot as { memory?: { statusByIssueId?: unknown } }).memory;\n return memory?.statusByIssueId && typeof memory.statusByIssueId === 'object'\n ? memory.statusByIssueId as Record<string, { headline?: unknown; phase?: unknown }>\n : {};\n}\n\nfunction renderRegistrySection(entries: FeatureRegistryEntry[]): string[] {\n if (entries.length === 0) {\n return [\n '- No feature registry entries are currently available.',\n '- Use `pan registry tag <issueId> <feature>` to add manual ownership records.',\n '- Use `pan registry list` and `pan registry show <feature>` to inspect ownership from terminal-only sessions.',\n ];\n }\n\n return [\n '| Feature | Issue | Workspace | Agent | Status | Tags |',\n '|---|---|---|---|---|---|',\n ...entries.slice(0, 25).map((entry) => [\n entry.featureName,\n entry.owningIssueId ?? '—',\n entry.owningWorkspaceId ?? '—',\n entry.owningAgentId ?? '—',\n entry.status,\n entry.tags.length > 0 ? entry.tags.join(', ') : '—',\n ].map(markdownCell).join(' | ')).map((row) => `| ${row} |`),\n ];\n}\n\nfunction markdownCell(value: string): string {\n return value.replaceAll('|', '\\\\|').replace(/\\s+/g, ' ').trim();\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\nfunction isNotFound(error: unknown): boolean {\n return typeof error === 'object' && error !== null && 'code' in error && error.code === 'ENOENT';\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport type { DashboardSnapshot, FeatureRegistryEntry } from '@overdeck/contracts';\nimport { assembleLiveBriefingMarkdown } from '../../../lib/briefing-assembler.js';\nimport { resolveSessionContextBriefingPath } from '../../../lib/briefing-freshness.js';\nimport { listFeatureRegistryEntries } from '../../../lib/registry/feature-registry-storage.js';\n\nexport interface SessionContextWriter {\n schedule(): void;\n writeNow(): Promise<void>;\n stop(): void;\n}\n\nexport interface StartSessionContextWriterOptions {\n readSnapshot: () => Promise<DashboardSnapshot>;\n subscribe: (listener: () => void) => () => void;\n debounceMs?: number;\n path?: string;\n now?: () => Date;\n mkdir?: typeof mkdir;\n writeFile?: typeof writeFile;\n listRegistryEntries?: () => Promise<FeatureRegistryEntry[]>;\n setTimeout?: typeof setTimeout;\n clearTimeout?: typeof clearTimeout;\n logger?: Pick<Console, 'error' | 'warn'>;\n}\n\nexport function startSessionContextWriter(options: StartSessionContextWriterOptions): SessionContextWriter {\n const debounceMs = options.debounceMs ?? 500;\n const path = options.path ?? resolveSessionContextBriefingPath();\n const mkdirFn = options.mkdir ?? mkdir;\n const writeFileFn = options.writeFile ?? writeFile;\n const listRegistryEntriesFn = options.listRegistryEntries ?? (() => listFeatureRegistryEntries({ limit: 50 }));\n const setTimeoutFn = options.setTimeout ?? setTimeout;\n const clearTimeoutFn = options.clearTimeout ?? clearTimeout;\n const now = options.now ?? (() => new Date());\n const logger = options.logger ?? console;\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n let stopped = false;\n let writing = false;\n let pendingAfterWrite = false;\n\n const writeLatest = async (): Promise<void> => {\n if (stopped) return;\n if (writing) {\n pendingAfterWrite = true;\n return;\n }\n\n writing = true;\n try {\n const [snapshot, registryEntries] = await Promise.all([\n options.readSnapshot(),\n readRegistryEntries(listRegistryEntriesFn, logger),\n ]);\n const content = await assembleLiveBriefingMarkdown({ snapshot, registryEntries, now: now() });\n await mkdirFn(dirname(path), { recursive: true });\n await writeFileFn(path, content, 'utf8');\n } catch (error) {\n logger.error('[session-context] Failed to write live briefing:', error);\n } finally {\n writing = false;\n if (pendingAfterWrite && !stopped) {\n pendingAfterWrite = false;\n schedule();\n }\n }\n };\n\n const schedule = (): void => {\n if (stopped) return;\n if (timer) clearTimeoutFn(timer);\n timer = setTimeoutFn(() => {\n timer = null;\n void writeLatest();\n }, debounceMs);\n };\n\n const unsubscribe = options.subscribe(schedule);\n void writeLatest();\n\n return {\n schedule,\n writeNow: writeLatest,\n stop() {\n stopped = true;\n if (timer) {\n clearTimeoutFn(timer);\n timer = null;\n }\n unsubscribe();\n },\n };\n}\n\nasync function readRegistryEntries(\n listRegistryEntries: () => Promise<FeatureRegistryEntry[]>,\n logger: Pick<Console, 'warn'>,\n): Promise<FeatureRegistryEntry[]> {\n try {\n return await listRegistryEntries();\n } catch (error) {\n logger.warn('[session-context] Failed to load feature registry entries:', error);\n return [];\n }\n}\n","/**\n * Domain service wrappers — Effect services over existing lib modules (PAN-428 B5, PAN-433)\n *\n * EventStoreService: wraps the SQLite event store in Effect.\n * SnapshotService was removed in PAN-433 — replaced by ReadModelService (read-model.ts).\n *\n * The EventStoreServiceLive layer also wires event store → read model: every\n * appended event is pushed to ReadModelService.applyEvent() so the in-memory\n * projection stays current.\n */\n\nimport { Effect, Layer, Queue, Context, Stream } from 'effect';\nimport { initEventStore } from '../event-store.js';\nimport type { StoredEvent } from '../event-store.js';\nimport { ReadModelService } from '../read-model.js';\nimport { startSessionContextWriter } from './session-context-writer.js';\nimport { emitActivityDetailedSync } from '../../../lib/activity-logger.js';\nimport { captureCheckpoint, diffCheckpointFiles, listCheckpoints } from '../../../lib/checkpoint/checkpoint-manager.js';\nimport { randomUUID } from 'crypto';\n\n// ─── EventStoreService ────────────────────────────────────────────────────────\n\nexport interface EventStoreServiceShape {\n /** Append a domain event; returns the assigned sequence number. */\n readonly append: (event: Record<string, unknown>) => Effect.Effect<number>;\n /** Queue a domain event append without blocking the HTTP/WebSocket hot path. */\n readonly appendAsync: (event: Record<string, unknown>) => Effect.Effect<number>;\n /** Return all stored events with sequence > fromSequence. */\n readonly readFrom: (fromSequence: number) => Effect.Effect<StoredEvent[]>;\n /** Return events of a given type, most recent first, capped at limit. */\n readonly queryByType: (type: string, limit?: number) => Effect.Effect<StoredEvent[]>;\n /** Return the latest sequence number (0 if empty). */\n readonly getLatestSequence: Effect.Effect<number>;\n /** Subscribe to live events as an Effect Stream. */\n readonly streamEvents: Stream.Stream<StoredEvent>;\n}\n\nexport class EventStoreService extends Context.Service<\n EventStoreService,\n EventStoreServiceShape\n>()('overdeck/dashboard/EventStoreService') {}\n\n/** Map a domain event to a detailed activity log entry. Returns null for uninteresting events. */\nfunction shouldRefreshSessionContext(type: string): boolean {\n return type === 'issues.snapshot' ||\n type.startsWith('agent.') ||\n type.startsWith('review.') ||\n type.startsWith('pipeline.') ||\n type.startsWith('planning.') ||\n type.startsWith('plan.') ||\n type.startsWith('workspace.') ||\n type.startsWith('issue.') ||\n type.startsWith('memory.') ||\n type.startsWith('cost.') ||\n type.startsWith('merge.') ||\n type.startsWith('system.health_');\n}\n\nexport function mapDomainEventToDetailed(event: StoredEvent): {\n source: string;\n level: 'info' | 'warn' | 'error' | 'success';\n message: string;\n issueId?: string;\n triggeringEvent: string;\n} | null {\n const p = (event.payload ?? {}) as Record<string, unknown>;\n const issueId = (p['issueId'] as string) ?? undefined;\n\n switch (event.type) {\n case 'agent.created':\n return { source: 'cloister', level: 'info', message: `Agent created for ${p['agentId']}`, issueId, triggeringEvent: event.type };\n case 'agent.started':\n return { source: 'cloister', level: 'info', message: `Agent started: ${p['agentId']}`, issueId, triggeringEvent: event.type };\n case 'agent.stopped':\n return { source: 'cloister', level: 'info', message: `Agent stopped: ${p['agentId']}`, issueId, triggeringEvent: event.type };\n case 'agent.status_changed': {\n const prev = p['previousStatus'] ? ` (was ${p['previousStatus']})` : '';\n return { source: 'cloister', level: 'info', message: `Agent ${p['agentId']} status → ${p['status']}${prev}`, issueId, triggeringEvent: event.type };\n }\n case 'agent.enrichment_changed': {\n const parts: string[] = [];\n if (p['agentPhase']) parts.push(`phase=${p['agentPhase']}`);\n if (p['resolution']) parts.push(`resolution=${p['resolution']}`);\n if (p['hasPendingQuestion']) parts.push(`questions=${p['pendingQuestionCount']}`);\n if (parts.length === 0) return null;\n return { source: 'cloister', level: 'info', message: `Agent ${p['agentId']} enrichment: ${parts.join(', ')}`, issueId, triggeringEvent: event.type };\n }\n case 'planning.started':\n return { source: 'planning', level: 'info', message: `Planning started: ${p['sessionName']}`, issueId, triggeringEvent: event.type };\n case 'planning.failed':\n return { source: 'planning', level: 'error', message: `Planning failed: ${p['error']}`, issueId, triggeringEvent: event.type };\n case 'planning.sync':\n return { source: 'planning', level: 'info', message: `Planning sync: ${p['status']}${p['progress'] !== undefined ? ` (${p['progress']}%)` : ''}`, issueId, triggeringEvent: event.type };\n case 'plan.item_status_changed':\n return { source: 'plan', level: 'info', message: `Plan item ${p['itemId']} → ${p['status']}`, issueId, triggeringEvent: event.type };\n case 'plan.subitem_status_changed':\n return { source: 'plan', level: 'info', message: `Plan sub-item ${p['subItemId']} → ${p['status']}`, issueId, triggeringEvent: event.type };\n case 'plan.items_unblocked': {\n const items = p['items'] as string[] | undefined;\n if (!items || items.length === 0) return null;\n return { source: 'plan', level: 'success', message: `${items.length} plan item(s) unblocked`, issueId, triggeringEvent: event.type };\n }\n case 'pipeline.status_changed':\n return { source: 'pipeline', level: 'info', message: `Pipeline status updated for ${issueId}`, issueId, triggeringEvent: event.type };\n case 'review.status_changed':\n return { source: 'review', level: 'info', message: `Review status updated for ${issueId}`, issueId, triggeringEvent: event.type };\n case 'pipeline.verification-started':\n return { source: 'verification', level: 'info', message: `Verification started for ${issueId}`, issueId, triggeringEvent: event.type };\n case 'pipeline.verification-failed':\n return { source: 'verification', level: 'error', message: `Verification failed for ${issueId}${p['failedCheck'] ? `: ${p['failedCheck']}` : ''}`, issueId, triggeringEvent: event.type };\n case 'pipeline.review-started':\n return { source: 'review', level: 'info', message: `Review specialist started for ${issueId}`, issueId, triggeringEvent: event.type };\n case 'pipeline.review-completed':\n return { source: 'review', level: p['passed'] ? 'success' : 'error', message: `Review ${p['passed'] ? 'passed' : 'failed'} for ${issueId}`, issueId, triggeringEvent: event.type };\n case 'pipeline.test-started':\n return { source: 'test', level: 'info', message: `Test specialist started for ${issueId}`, issueId, triggeringEvent: event.type };\n case 'pipeline.test-completed':\n return { source: 'test', level: p['passed'] ? 'success' : 'error', message: `Tests ${p['passed'] ? 'passed' : 'failed'} for ${issueId}`, issueId, triggeringEvent: event.type };\n case 'specialist.started':\n return { source: 'cloister', level: 'info', message: `Specialist ${p['name'] ?? '?'} started${p['currentIssue'] ? ` for ${p['currentIssue']}` : ''}`, issueId, triggeringEvent: event.type };\n case 'specialist.completed':\n return { source: 'cloister', level: 'success', message: `Specialist ${p['name']} completed`, issueId, triggeringEvent: event.type };\n case 'specialist.failed':\n return { source: 'cloister', level: 'error', message: `Specialist ${p['name']} failed: ${p['error']}`, issueId, triggeringEvent: event.type };\n case 'workspace.created':\n return { source: 'workspace', level: 'info', message: `Workspace created at ${p['workspacePath']}`, issueId, triggeringEvent: event.type };\n case 'workspace.wipe_started':\n return { source: 'workspace', level: 'warn', message: `Deep-wipe started for ${issueId}`, issueId, triggeringEvent: event.type };\n case 'workspace.destroyed':\n return { source: 'workspace', level: 'info', message: `Workspace destroyed for ${issueId}`, issueId, triggeringEvent: event.type };\n case 'workspace.deleted':\n return { source: 'workspace', level: 'info', message: `Workspace deleted for ${issueId}`, issueId, triggeringEvent: event.type };\n case 'workspace.aborted':\n return { source: 'workspace', level: 'warn', message: `Workspace aborted for ${issueId}`, issueId, triggeringEvent: event.type };\n case 'issue.statusChanged':\n return { source: 'tracker', level: 'info', message: `Issue ${p['issueId']} status → ${p['canonicalStatus']}`, issueId: p['issueId'] as string, triggeringEvent: event.type };\n case 'dashboard.lifecycle_started':\n return { source: 'dashboard', level: 'info', message: `Dashboard lifecycle started: ${p['reason']}`, issueId, triggeringEvent: event.type };\n case 'dashboard.lifecycle_completed':\n return { source: 'dashboard', level: 'success', message: `Dashboard lifecycle completed: ${p['reason']}`, issueId, triggeringEvent: event.type };\n case 'dashboard.lifecycle_failed':\n return { source: 'dashboard', level: 'error', message: `Dashboard lifecycle failed: ${p['reason']} — ${p['error']}`, issueId, triggeringEvent: event.type };\n case 'merge.ready':\n return { source: 'ship', level: 'success', message: `Issue ${issueId} ready for merge`, issueId, triggeringEvent: event.type };\n case 'system.health_severity_changed':\n return {\n source: 'system-health',\n level: p['severity'] === 'critical' ? 'error' : p['severity'] === 'warning' ? 'warn' : 'info',\n message: `System health ${p['previousSeverity']} → ${p['severity']}`,\n issueId,\n triggeringEvent: event.type,\n };\n case 'cost.event_recorded': {\n const cost = Number(p['cost']);\n if (Number.isFinite(cost) && cost <= 0) return null;\n return { source: 'costs', level: 'info', message: `Cost event: $${(p['cost'] as number)?.toFixed?.(2) ?? p['cost']} for ${p['agentId']}`, issueId, triggeringEvent: event.type };\n }\n default:\n return null;\n }\n}\n\nexport const EventStoreServiceLive = Layer.effect(\n EventStoreService,\n Effect.gen(function* () {\n const store = yield* Effect.promise(() => initEventStore());\n const readModel = yield* ReadModelService;\n\n // Wire event store → read model: every appended event updates the projection.\n // The EventEmitter subscription is sync, so applyEvent runs inline on append.\n store.subscribe((event) => {\n readModel.applyEvent({\n type: event.type,\n sequence: event.sequence,\n timestamp: event.timestamp,\n payload: event.payload,\n } as any);\n });\n\n startSessionContextWriter({\n readSnapshot: () => Effect.runPromise(readModel.getSnapshot),\n subscribe: (listener) => store.subscribe((event) => {\n if (shouldRefreshSessionContext(event.type)) listener();\n }),\n });\n\n // Auto-emit detailed activity entries for state-change domain events.\n // Skip activity.* events to avoid infinite loops.\n store.subscribe((event) => {\n if (event.type.startsWith('activity.')) return;\n const detailed = mapDomainEventToDetailed(event);\n if (detailed) emitActivityDetailedSync(detailed);\n });\n\n // Capture checkpoints when agent activity changes.\n // Fires async (non-blocking) on every agent.activity_changed event for agents\n // with a workspace. Creates a git checkpoint, computes file changes since the\n // previous checkpoint, and emits agent.turn_diff_completed.\n // Rate-limited to one checkpoint per agent per 10 seconds.\n const checkpointCooldown = new Map<string, number>(); // agentId → last checkpoint timestamp\n store.subscribe((event) => {\n if (event.type !== 'agent.activity_changed') return;\n const p = (event.payload ?? {}) as Record<string, unknown>;\n const agentId = p['agentId'] as string | undefined;\n if (!agentId) return;\n\n // Rate limit: skip if last checkpoint was <10s ago\n const lastCheckpoint = checkpointCooldown.get(agentId) ?? 0;\n if (Date.now() - lastCheckpoint < 10_000) return;\n checkpointCooldown.set(agentId, Date.now());\n\n // Fire-and-forget async checkpoint — don't block the event loop\n (async () => {\n try {\n // Look up agent workspace from read model snapshot\n const snapshot = await Effect.runPromise(readModel.getSnapshot);\n const agent = (snapshot.agents as any[]).find((a: any) => a.id === agentId);\n if (!agent?.workspace) return;\n\n const workspace: string = agent.workspace;\n const turnId = `turn-${Date.now()}-${randomUUID().slice(0, 8)}`;\n\n // Capture checkpoint (git tag at current working tree state)\n await Effect.runPromise(captureCheckpoint(workspace, agentId, turnId));\n\n // Compute file changes from previous checkpoint\n const checkpoints = await Effect.runPromise(listCheckpoints(workspace, agentId));\n const prevCheckpoint = checkpoints.length >= 2 ? checkpoints[checkpoints.length - 2] : null;\n let files: Array<{ path: string; kind?: string; additions?: number; deletions?: number }> = [];\n if (prevCheckpoint) {\n files = await Effect.runPromise(diffCheckpointFiles(workspace, agentId, prevCheckpoint, turnId));\n }\n\n // Only emit if there are actual changes\n if (files.length === 0) return;\n\n // Emit turn_diff_completed event\n store.appendAsync({\n type: 'agent.turn_diff_completed',\n timestamp: new Date().toISOString(),\n payload: {\n agentId,\n turnId,\n completedAt: new Date().toISOString(),\n files,\n checkpointRef: `refs/pan/turn/${agentId}/${turnId}`,\n assistantMessageId: undefined,\n checkpointTurnCount: checkpoints.length,\n },\n } as any);\n } catch (err) {\n // Checkpoint capture is best-effort — log but don't crash\n console.error('[checkpoint] Failed to capture checkpoint for', agentId, err);\n }\n })();\n });\n\n // PAN-1635: bridge Claude-native compaction to the conversation \"compacting\"\n // flag. Only the Overdeck-native path emits conversation.compacting_changed\n // directly; a built-in Claude Code /compact is invisible to it. But the\n // PreCompact hook emits activity{tool:\"compact\"} and PostCompact emits\n // activity{idle} for *both* paths, so mirror that signal here. This gives the\n // dashboard a live \"Compacting…\" indicator during Claude's own /compact (and\n // visibly explains a prompt eaten by submit-time compaction). Conversation\n // agentIds (`conv-*`) double as the conversation name.\n const compactingConvs = new Set<string>();\n store.subscribe((event) => {\n if (event.type !== 'agent.activity_changed') return;\n const p = (event.payload ?? {}) as Record<string, unknown>;\n const agentId = p['agentId'] as string | undefined;\n if (!agentId || !agentId.startsWith('conv-')) return;\n const compacting = p['currentTool'] === 'compact';\n if (compacting === compactingConvs.has(agentId)) return;\n if (compacting) compactingConvs.add(agentId);\n else compactingConvs.delete(agentId);\n store.emitOnly({\n type: 'conversation.compacting_changed',\n timestamp: event.timestamp ?? new Date().toISOString(),\n payload: { conversationName: agentId, compacting },\n } as never);\n });\n\n const streamEvents = Stream.callback<StoredEvent>((queue) =>\n Effect.acquireRelease(\n Effect.sync(() =>\n store.subscribe((event) => Queue.offerUnsafe(queue, event)),\n ),\n (unsubscribe) => Effect.sync(unsubscribe),\n ),\n );\n\n return {\n append: (event) => Effect.sync(() => store.append(event as never)),\n appendAsync: (event) => Effect.promise(() => store.appendAsync(event as never)),\n readFrom: (fromSequence) => Effect.sync(() => store.readFrom(fromSequence)),\n queryByType: (type, limit) => Effect.sync(() => store.queryByType(type, limit)),\n getLatestSequence: Effect.sync(() => store.getLatestSequence()),\n streamEvents,\n };\n }),\n);\n","/**\n * IssueLifecycle Effect service (PAN-449)\n *\n * High-level issue state management that dispatches to the correct tracker\n * client (Linear, GitHub, Rally) based on the issue ID prefix.\n *\n * Routes should use IssueLifecycle instead of calling tracker clients directly.\n * The service handles:\n * - State transitions (In Planning, In Progress, In Review, Done)\n * - Label add/remove (GitHub only; no-op for Linear/Rally)\n * - Issue close with label cleanup\n */\n\nimport { Effect, Layer, Option, Context } from 'effect';\nimport { resolveGitHubIssueSync, resolveTrackerTypeSync } from '../../../lib/tracker-utils.js';\nimport { GitHubClient } from './github-client.js';\nimport { GitHubClientOptionalLive } from './github-client.js';\nimport { LinearClient } from './linear-client.js';\nimport { LinearClientOptionalLive } from './linear-client.js';\nimport { RallyClient } from './rally-client.js';\nimport { RallyClientLive } from './rally-client.js';\nimport type { RallyClientShape } from './rally-client.js';\nimport type { LinearState } from './linear-client.js';\nimport { TrackerNotConfigured, TrackerApiError, IssueNotFound, RateLimited } from './typed-errors.js';\nimport { EventStoreService } from './domain-services.js';\nimport { getSharedIssueService } from './issue-service-singleton.js';\n\n// ─── Event emission helper ────────────────────────────────────────────────────\n\n/**\n * Attempt to emit a domain event via the shared event store.\n * Non-fatal: silently no-ops if EventStoreService is absent (e.g. in unit tests).\n */\nfunction emitEvent(event: Record<string, unknown>): Effect.Effect<void, never> {\n return Effect.gen(function* () {\n const storeOption = yield* Effect.serviceOption(EventStoreService);\n if (Option.isSome(storeOption)) {\n yield* storeOption.value.append(event);\n }\n });\n}\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type IssueState = 'open' | 'in_planning' | 'in_progress' | 'in_review' | 'verifying_on_main' | 'closed' | 'canceled';\n\n// ─── Service interface ────────────────────────────────────────────────────────\n\nexport interface IssueLifecycleShape {\n /**\n * Transition an issue to a new lifecycle state.\n *\n * For Linear: resolves the Linear state ID from team workflow.\n * For GitHub: adds/removes workflow labels (state change via labels).\n * For Rally: delegates to RallyClient.updateState.\n */\n readonly transitionTo: (\n issueId: string,\n state: IssueState,\n ) => Effect.Effect<\n void,\n TrackerNotConfigured | IssueNotFound | TrackerApiError | RateLimited\n >;\n\n /**\n * Add a label to an issue.\n * GitHub only: creates the label if it doesn't exist, then adds it.\n * Linear / Rally: no-op (trackers don't use workflow labels the same way).\n */\n readonly addLabel: (\n issueId: string,\n label: string,\n ) => Effect.Effect<void, TrackerNotConfigured | TrackerApiError | RateLimited>;\n\n /**\n * Remove a label from an issue.\n * GitHub only; non-fatal if label is not present.\n * Linear / Rally: no-op.\n */\n readonly removeLabel: (\n issueId: string,\n label: string,\n ) => Effect.Effect<void, TrackerNotConfigured | TrackerApiError | RateLimited>;\n\n /**\n * Close an issue and clean up workflow labels.\n *\n * - Transitions issue to 'closed' state.\n * - For GitHub: removes in-progress/in-review/planned labels.\n */\n readonly close: (\n issueId: string,\n ) => Effect.Effect<\n void,\n TrackerNotConfigured | IssueNotFound | TrackerApiError | RateLimited\n >;\n}\n\n// ─── Service tag ──────────────────────────────────────────────────────────────\n\nexport class IssueLifecycle extends Context.Service<IssueLifecycle, IssueLifecycleShape>()(\n 'overdeck/dashboard/IssueLifecycle',\n) {}\n\n// ─── Linear state helpers ─────────────────────────────────────────────────────\n\n/** Map a normalized IssueState to a Linear state type string. */\nfunction linearStateType(state: IssueState): string {\n switch (state) {\n case 'open':\n return 'unstarted';\n case 'in_planning':\n case 'in_progress':\n case 'in_review':\n case 'verifying_on_main':\n return 'started';\n case 'closed':\n return 'completed';\n case 'canceled':\n return 'canceled';\n }\n}\n\n/** Find the best matching Linear state from a list given a target normalized state. */\nfunction findLinearState(states: ReadonlyArray<LinearState>, state: IssueState): LinearState | null {\n if (state === 'in_review' || state === 'verifying_on_main') {\n const names = state === 'verifying_on_main'\n ? ['verifying on main', 'verifying', 'in review']\n : ['in review'];\n const explicit = states.find((s) => names.includes(s.name.toLowerCase()));\n if (explicit) return explicit;\n // Fall back to first \"started\" state\n return states.filter((s) => s.type === 'started')[0] ?? null;\n }\n\n if (state === 'in_planning') {\n // Prefer a state named \"In Planning\" or \"Planning\"\n const explicit = states.find(\n (s) => s.name.toLowerCase() === 'in planning' || s.name.toLowerCase() === 'planning',\n );\n if (explicit) return explicit;\n // Fall back to first \"started\" state\n return states.filter((s) => s.type === 'started')[0] ?? null;\n }\n\n const targetType = linearStateType(state);\n const matching = states.filter((s) => s.type === targetType);\n return matching[0] ?? null;\n}\n\n// ─── GitHub label helpers ─────────────────────────────────────────────────────\n\nconst GITHUB_STATE_LABELS: Record<IssueState, { add: string[]; remove: string[] }> = {\n open: { add: [], remove: ['in-progress', 'in-review', 'planned', 'in-planning', 'review-ready', 'done', 'merged', 'verifying-on-main', 'needs-close-out', 'closed-out', 'wontfix', 'duplicate'] },\n in_planning: { add: ['planned'], remove: ['in-progress', 'in-review', 'review-ready', 'done', 'merged', 'verifying-on-main', 'needs-close-out', 'closed-out', 'wontfix', 'duplicate'] },\n in_progress: { add: ['in-progress'], remove: ['planned', 'in-planning', 'in-review', 'review-ready', 'done', 'merged', 'verifying-on-main', 'needs-close-out', 'closed-out', 'wontfix', 'duplicate'] },\n in_review: { add: ['in-review'], remove: ['in-progress', 'planned', 'in-planning', 'done', 'merged', 'verifying-on-main', 'needs-close-out', 'closed-out', 'wontfix', 'duplicate'] },\n verifying_on_main: { add: ['verifying-on-main'], remove: ['in-progress', 'in-review', 'planned', 'in-planning', 'review-ready', 'ready-for-merge', 'done', 'needs-close-out', 'closed-out', 'wontfix', 'duplicate'] },\n closed: { add: [], remove: ['in-progress', 'in-review', 'planned', 'in-planning', 'review-ready', 'done', 'merged', 'verifying-on-main', 'needs-close-out', 'closed-out', 'wontfix', 'duplicate'] },\n canceled: { add: ['wontfix'], remove: ['in-progress', 'in-review', 'planned', 'in-planning', 'review-ready', 'done', 'merged', 'verifying-on-main', 'needs-close-out', 'closed-out', 'duplicate'] },\n};\n\nconst GITHUB_LABEL_METADATA: Record<string, { color: string; description: string }> = {\n 'planned': { color: 'cfd3d7', description: 'Planning complete' },\n 'in-progress': { color: 'fbca04', description: 'Implementation in progress' },\n 'in-review': { color: 'd4c5f9', description: 'Under review' },\n 'verifying-on-main': { color: 'fbca04', description: 'Merged — awaiting verification on main' },\n 'wontfix': { color: 'ffffff', description: 'Will not be fixed' },\n};\n\nfunction githubLabelMetadata(label: string): { color: string; description: string } {\n return GITHUB_LABEL_METADATA[label] ?? { color: '0075ca', description: '' };\n}\n\n// ─── Live layer implementation ────────────────────────────────────────────────\n\n/** Map IssueState to canonical status string for cache patching. */\nfunction canonicalStatus(state: IssueState): string {\n switch (state) {\n case 'open': return 'open';\n case 'in_planning': return 'in_planning';\n case 'in_progress': return 'in_progress';\n case 'in_review': return 'in_review';\n case 'verifying_on_main': return 'verifying_on_main';\n case 'closed': return 'closed';\n case 'canceled': return 'canceled';\n }\n}\n\nexport const IssueLifecycleLive = Layer.effect(\n IssueLifecycle,\n Effect.gen(function* () {\n const linear = yield* LinearClient;\n const github = yield* GitHubClient;\n const rally = yield* RallyClient;\n\n const impl: IssueLifecycleShape = {\n transitionTo: (issueId, state) =>\n Effect.gen(function* () {\n const trackerType = resolveTrackerTypeSync(issueId);\n\n if (trackerType === 'github') {\n // GitHub state is managed via labels\n const ghInfo = resolveGitHubIssueSync(issueId);\n if (!ghInfo.isGitHub) return;\n const labelOps = GITHUB_STATE_LABELS[state];\n for (const label of labelOps.add) {\n const metadata = githubLabelMetadata(label);\n yield* github.ensureLabel(ghInfo.owner, ghInfo.repo, label, metadata.color, metadata.description);\n yield* github.addLabel(ghInfo.owner, ghInfo.repo, ghInfo.number, label);\n }\n for (const label of labelOps.remove) {\n yield* github.removeLabel(ghInfo.owner, ghInfo.repo, ghInfo.number, label);\n }\n if (state === 'closed' || state === 'canceled') {\n yield* github.closeIssue(ghInfo.owner, ghInfo.repo, ghInfo.number);\n } else {\n // Reopen the issue if it's currently closed (e.g. reopening after incorrect merge)\n yield* github.reopenIssue(ghInfo.owner, ghInfo.repo, ghInfo.number).pipe(\n Effect.catch(() => Effect.void) // Non-fatal if already open\n );\n }\n } else if (trackerType === 'rally') {\n const normalizedState =\n state === 'in_planning' || state === 'open' ? 'open'\n : state === 'in_progress' || state === 'in_review' || state === 'verifying_on_main' ? 'in_progress'\n : 'closed';\n yield* rally.updateState(issueId, normalizedState);\n } else {\n // Linear\n const issue = yield* linear.getIssue(issueId);\n const states = yield* linear.getTeamStates(issue.team.id);\n const target = findLinearState(states, state);\n // No-op if the issue is already in the target state\n if (target && issue.state?.id !== target.id) {\n yield* linear.updateState(issue.id, target.id);\n }\n }\n\n // Patch the in-memory cache and emit a domain event (non-fatal)\n yield* Effect.try({ try: () => getSharedIssueService().patchIssue(issueId, { canonicalStatus: canonicalStatus(state) }), catch: () => void 0 }).pipe(Effect.ignore);\n yield* emitEvent({\n type: 'issue.transitioned',\n timestamp: new Date().toISOString(),\n payload: { issueId, state },\n });\n }),\n\n addLabel: (issueId, label) =>\n Effect.gen(function* () {\n const trackerType = resolveTrackerTypeSync(issueId);\n if (trackerType !== 'github') return; // no-op for Linear/Rally\n\n const ghInfo = resolveGitHubIssueSync(issueId);\n if (!ghInfo.isGitHub) return;\n yield* github.addLabel(ghInfo.owner, ghInfo.repo, ghInfo.number, label);\n }),\n\n removeLabel: (issueId, label) =>\n Effect.gen(function* () {\n const trackerType = resolveTrackerTypeSync(issueId);\n if (trackerType !== 'github') return; // no-op for Linear/Rally\n\n const ghInfo = resolveGitHubIssueSync(issueId);\n if (!ghInfo.isGitHub) return;\n yield* github.removeLabel(ghInfo.owner, ghInfo.repo, ghInfo.number, label);\n }),\n\n close: (issueId) =>\n Effect.gen(function* () {\n const trackerType = resolveTrackerTypeSync(issueId);\n\n if (trackerType === 'github') {\n const ghInfo = resolveGitHubIssueSync(issueId);\n if (!ghInfo.isGitHub) return;\n // Remove workflow labels then close\n for (const label of ['in-progress', 'in-review', 'planned', 'in-planning', 'verifying-on-main', 'ready-for-merge']) {\n yield* github.removeLabel(ghInfo.owner, ghInfo.repo, ghInfo.number, label);\n }\n yield* github.closeIssue(ghInfo.owner, ghInfo.repo, ghInfo.number);\n } else if (trackerType === 'rally') {\n yield* rally.updateState(issueId, 'closed');\n } else {\n // Linear\n const issue = yield* linear.getIssue(issueId);\n const states = yield* linear.getTeamStates(issue.team.id);\n const target = findLinearState(states, 'closed');\n if (target) {\n yield* linear.updateState(issue.id, target.id);\n }\n }\n\n // Patch the in-memory cache and emit issue.closed domain event (non-fatal)\n yield* Effect.try({ try: () => getSharedIssueService().patchIssue(issueId, { canonicalStatus: 'closed' }), catch: () => void 0 }).pipe(Effect.ignore);\n yield* emitEvent({\n type: 'issue.closed',\n timestamp: new Date().toISOString(),\n payload: { issueId },\n });\n }),\n };\n\n return impl;\n }),\n);\n\n/**\n * Convenience layer that wires all tracker client dependencies.\n * Use this to provide IssueLifecycle without manually composing layers.\n */\nexport const IssueLifecycleWithClientLive = IssueLifecycleLive.pipe(\n Layer.provide(LinearClientOptionalLive),\n Layer.provide(GitHubClientOptionalLive),\n Layer.provide(\n // RallyClient is optional — provide a fallback that fails with TrackerNotConfigured\n Layer.effect(\n RallyClient,\n Effect.gen(function* () {\n // Dynamic: try to build RallyClientLive, fall back to stub if not configured\n return yield* Effect.gen(function* () {\n const { getRallyConfig } = yield* Effect.promise(() =>\n import('./tracker-config.js'),\n );\n const config = getRallyConfig();\n if (!config) {\n const fail = <A>(): Effect.Effect<A, TrackerNotConfigured> => Effect.fail(new TrackerNotConfigured({ tracker: 'rally' }));\n const fallback: RallyClientShape = {\n getIssue: () => fail(),\n getChildIssues: () => fail(),\n updateState: () => fail(),\n addComment: () => fail(),\n };\n return fallback;\n }\n // Delegate to RallyClientLive's logic\n return yield* RallyClient;\n }).pipe(Effect.provide(RallyClientLive));\n }),\n ),\n ),\n);\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import { i as init_issue_lifecycle, n as IssueLifecycleLive, r as IssueLifecycleWithClientLive, t as IssueLifecycle } from "./issue-lifecycle-DnhQgddB.js";
1
+ import { i as init_issue_lifecycle, n as IssueLifecycleLive, r as IssueLifecycleWithClientLive, t as IssueLifecycle } from "./issue-lifecycle-5LbOcATP.js";
2
2
  init_issue_lifecycle();
3
3
  export { IssueLifecycle, IssueLifecycleLive, IssueLifecycleWithClientLive };
@@ -1,3 +1,3 @@
1
- import { i as startSharedIssueService, n as init_issue_service_singleton, t as getSharedIssueService } from "./issue-service-singleton-_AquSBTl.js";
1
+ import { i as startSharedIssueService, n as init_issue_service_singleton, t as getSharedIssueService } from "./issue-service-singleton-CDGt__oQ.js";
2
2
  init_issue_service_singleton();
3
3
  export { getSharedIssueService, startSharedIssueService };
@@ -1,7 +1,7 @@
1
1
  import { n as __esmMin, r as __exportAll } from "./rolldown-runtime-DNCzzKnI.js";
2
2
  import { Gn as resolveProjectFromIssueSync, Nn as init_projects, f as loadConfigSync, t as init_config_yaml } from "./config-yaml-DPAieL_G.js";
3
3
  import { n as openDatabase, t as init_driver } from "./driver-D-HBOQa_.js";
4
- import { c as init_review_status, d as loadReviewStatusesForIssues } from "./review-status-BQjqQXZG.js";
4
+ import { c as init_review_status, d as loadReviewStatusesForIssues } from "./review-status-B5ZASOo4.js";
5
5
  import { a as init_tracker_config, n as getLinearApiKey, o as validateRallyConfig, r as getRallyConfig, t as getGitHubConfig } from "./tracker-config-C3hiPVAM.js";
6
6
  import { n as init_dist_src, t as Octokit } from "./dist-src-Byxt1ifL.js";
7
7
  import { n as mapGitHubStateToCanonical, t as init_state_mapping } from "./state-mapping-BvAgFK8n.js";
@@ -1504,4 +1504,4 @@ var init_issue_service_singleton = __esmMin((() => {
1504
1504
  //#endregion
1505
1505
  export { CacheService as a, startSharedIssueService as i, init_issue_service_singleton as n, init_cache_service as o, issue_service_singleton_exports as r, parseIntegerHeader as s, getSharedIssueService as t };
1506
1506
 
1507
- //# sourceMappingURL=issue-service-singleton-_AquSBTl.js.map
1507
+ //# sourceMappingURL=issue-service-singleton-CDGt__oQ.js.map