@overdeck/core 0.50.3 → 0.50.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/dist/agents-DNMRPC8n.js.map +1 -1
  2. package/dist/{build-info-BJuEVWOh.js → build-info-ioRdOmqT.js} +3 -3
  3. package/dist/{build-info-BJuEVWOh.js.map → build-info-ioRdOmqT.js.map} +1 -1
  4. package/dist/build-info-kMMitDOP.js +2 -0
  5. package/dist/cli/index.js +4 -4
  6. package/dist/conversation-compaction-GFfO5UZL.js.map +1 -1
  7. package/dist/dashboard/{advancing-selfheal-Bq8V0QUa.js → advancing-selfheal-Dp5_Gai3.js} +3 -3
  8. package/dist/dashboard/{advancing-selfheal-Bq8V0QUa.js.map → advancing-selfheal-Dp5_Gai3.js.map} +1 -1
  9. package/dist/dashboard/{agent-gc-Coz8zQt_.js → agent-gc-JzoSs5Pi.js} +3 -3
  10. package/dist/dashboard/{agent-gc-Coz8zQt_.js.map → agent-gc-JzoSs5Pi.js.map} +1 -1
  11. package/dist/dashboard/{agent-idle-CAwB-K6M.js → agent-idle-D-Qy5BN_.js} +2 -2
  12. package/dist/dashboard/{agent-idle-CAwB-K6M.js.map → agent-idle-D-Qy5BN_.js.map} +1 -1
  13. package/dist/dashboard/{agent-state-Ci6PhjOE.js → agent-state-B196fj1h.js} +38 -16
  14. package/dist/dashboard/agent-state-B196fj1h.js.map +1 -0
  15. package/dist/dashboard/{agent-state-CbUEwUEQ.js → agent-state-IpWnw8lv.js} +1 -1
  16. package/dist/dashboard/{agent-state-service-D6IVrX3B.js → agent-state-service-CCo5-9DR.js} +3 -3
  17. package/dist/dashboard/{agent-state-service-D6IVrX3B.js.map → agent-state-service-CCo5-9DR.js.map} +1 -1
  18. package/dist/dashboard/{agent-state-service-DcN1j6eK.js → agent-state-service-D_F3VZ23.js} +1 -1
  19. package/dist/dashboard/{agents-BVvL4o_4.js → agents-CkGhnAmu.js} +1 -1
  20. package/dist/dashboard/{backlog-auto-trigger-BuSBqG2V.js → backlog-auto-trigger-B5Z2zBtC.js} +2 -2
  21. package/dist/dashboard/{backlog-auto-trigger-BuSBqG2V.js.map → backlog-auto-trigger-B5Z2zBtC.js.map} +1 -1
  22. package/dist/dashboard/build-info-BTjOB0Xr.js +2 -0
  23. package/dist/dashboard/{build-info-Cj3Y0K87.js → build-info-D0EJNj6C.js} +3 -3
  24. package/dist/dashboard/{build-info-Cj3Y0K87.js.map → build-info-D0EJNj6C.js.map} +1 -1
  25. package/dist/dashboard/cli-CIznFrcE.js +2 -0
  26. package/dist/dashboard/{cli-DIhQieif.js → cli-DCbVrSsg.js} +2 -2
  27. package/dist/dashboard/{cli-DIhQieif.js.map → cli-DCbVrSsg.js.map} +1 -1
  28. package/dist/dashboard/{close-issue-CRlmrE5g.js → close-issue-Bmu9aOIx.js} +2 -2
  29. package/dist/dashboard/{close-issue-CRlmrE5g.js.map → close-issue-Bmu9aOIx.js.map} +1 -1
  30. package/dist/dashboard/{closed-issue-reaper-DrHyYgJR.js → closed-issue-reaper-COxfTtLz.js} +5 -5
  31. package/dist/dashboard/{closed-issue-reaper-DrHyYgJR.js.map → closed-issue-reaper-COxfTtLz.js.map} +1 -1
  32. package/dist/dashboard/{closed-issue-reaper-DWlw2EtL.js → closed-issue-reaper-DwMwG_Pz.js} +1 -1
  33. package/dist/dashboard/{compaction-continuation-DhYgsXmF.js → compaction-continuation-Bqc212Zm.js} +2 -2
  34. package/dist/dashboard/{compaction-continuation-DhYgsXmF.js.map → compaction-continuation-Bqc212Zm.js.map} +1 -1
  35. package/dist/dashboard/{conflict-gate-vczgdVTa.js → conflict-gate-9mPVg7-5.js} +1 -1
  36. package/dist/dashboard/{conflict-gate-Bt-lLgYf.js → conflict-gate-CGQTcR0I.js} +3 -3
  37. package/dist/dashboard/{conflict-gate-Bt-lLgYf.js.map → conflict-gate-CGQTcR0I.js.map} +1 -1
  38. package/dist/dashboard/{conversation-compaction-CsdCbcQB.js → conversation-compaction-C7P6V8Iw.js} +5 -5
  39. package/dist/dashboard/{conversation-compaction-CsdCbcQB.js.map → conversation-compaction-C7P6V8Iw.js.map} +1 -1
  40. package/dist/dashboard/{conversation-compaction-D0zgWM1f.js → conversation-compaction-Cck5oi-V.js} +1 -1
  41. package/dist/dashboard/{conversation-service-6LaymUV-.js → conversation-service-CsOpwI9Y.js} +37 -5
  42. package/dist/dashboard/{conversation-service-6LaymUV-.js.map → conversation-service-CsOpwI9Y.js.map} +1 -1
  43. package/dist/dashboard/conversation-service-plOFkoMo.js +3 -0
  44. package/dist/dashboard/dashboard-db-worker.js +1 -1
  45. package/dist/dashboard/{deacon-CU47dU7U.js → deacon-B8oMMIgN.js} +62 -62
  46. package/dist/dashboard/{deacon-CU47dU7U.js.map → deacon-B8oMMIgN.js.map} +1 -1
  47. package/dist/dashboard/{deacon-DYsOa_ss.js → deacon-b7J57Svt.js} +2 -2
  48. package/dist/dashboard/{deacon-pending-lifecycle-ijGX8wLb.js → deacon-pending-lifecycle-3NIJ7ew5.js} +2 -2
  49. package/dist/dashboard/{deacon-pending-lifecycle-ijGX8wLb.js.map → deacon-pending-lifecycle-3NIJ7ew5.js.map} +1 -1
  50. package/dist/dashboard/deacon.js +5 -5
  51. package/dist/dashboard/{delivery-DMYxIE0J.js → delivery-lnI-gQKo.js} +1 -1
  52. package/dist/dashboard/{deploy-patrol-C4eXco1S.js → deploy-patrol-u3v2krg9.js} +2 -2
  53. package/dist/dashboard/{deploy-patrol-C4eXco1S.js.map → deploy-patrol-u3v2krg9.js.map} +1 -1
  54. package/dist/dashboard/{feedback-stuck-retirement-Dd5XNrMr.js → feedback-stuck-retirement-CoFNqjCc.js} +2 -2
  55. package/dist/dashboard/{feedback-stuck-retirement-Dd5XNrMr.js.map → feedback-stuck-retirement-CoFNqjCc.js.map} +1 -1
  56. package/dist/dashboard/{feedback-target-BzdJ2wIR.js → feedback-target-BBCh3RRq.js} +1 -1
  57. package/dist/dashboard/{feedback-target-BcsA_GRV.js → feedback-target-CeCq6EhW.js} +5 -5
  58. package/dist/dashboard/{feedback-target-BcsA_GRV.js.map → feedback-target-CeCq6EhW.js.map} +1 -1
  59. package/dist/dashboard/{flywheel-Is3w2lcT.js → flywheel-DKldI63J.js} +7 -7
  60. package/dist/dashboard/{flywheel-Is3w2lcT.js.map → flywheel-DKldI63J.js.map} +1 -1
  61. package/dist/dashboard/{flywheel-D0guNqkh.js → flywheel-DpRPLErh.js} +7 -7
  62. package/dist/dashboard/{flywheel-D0guNqkh.js.map → flywheel-DpRPLErh.js.map} +1 -1
  63. package/dist/dashboard/{flywheel-B34cdfbd.js → flywheel-jeZFYvm-.js} +1 -1
  64. package/dist/dashboard/{flywheel-merge-order-6HiPR2WG.js → flywheel-merge-order-0OnoosaO.js} +1 -1
  65. package/dist/dashboard/{flywheel-merge-order-BsNPMu0S.js → flywheel-merge-order-PG5op4j7.js} +2 -2
  66. package/dist/dashboard/{flywheel-merge-order-BsNPMu0S.js.map → flywheel-merge-order-PG5op4j7.js.map} +1 -1
  67. package/dist/dashboard/{harness-resolve-CzTFVZ5l.js → harness-resolve-C0HWj3H9.js} +2 -2
  68. package/dist/dashboard/{harness-resolve-CzTFVZ5l.js.map → harness-resolve-C0HWj3H9.js.map} +1 -1
  69. package/dist/dashboard/{identity-Dc7rQLln.js → identity-Cot1Hg4G.js} +2 -2
  70. package/dist/dashboard/{identity-Dc7rQLln.js.map → identity-Cot1Hg4G.js.map} +1 -1
  71. package/dist/dashboard/{inspect-agent-CP7Bu47O.js → inspect-agent-BDmLWbGx.js} +3 -3
  72. package/dist/dashboard/{inspect-agent-CP7Bu47O.js.map → inspect-agent-BDmLWbGx.js.map} +1 -1
  73. package/dist/dashboard/inspect-agent-BcOkA-hC.js +2 -0
  74. package/dist/dashboard/{issue-lifecycle-BPbczKyy.js → issue-lifecycle-1B_v8Rf5.js} +6 -6
  75. package/dist/dashboard/{issue-lifecycle-BPbczKyy.js.map → issue-lifecycle-1B_v8Rf5.js.map} +1 -1
  76. package/dist/dashboard/{issue-lifecycle-FaugTi39.js → issue-lifecycle-CIrfbIbj.js} +1 -1
  77. package/dist/dashboard/{issue-service-singleton-Dsad9NTt.js → issue-service-singleton-BVwPzjwF.js} +1 -1
  78. package/dist/dashboard/{issue-service-singleton-BrZQJLRI.js → issue-service-singleton-KcRrOiFD.js} +2 -2
  79. package/dist/dashboard/{issue-service-singleton-BrZQJLRI.js.map → issue-service-singleton-KcRrOiFD.js.map} +1 -1
  80. package/dist/dashboard/{jsonl-resolver-DQEe0DHM.js → jsonl-resolver-jxbif7MD.js} +3 -3
  81. package/dist/dashboard/{jsonl-resolver-DQEe0DHM.js.map → jsonl-resolver-jxbif7MD.js.map} +1 -1
  82. package/dist/dashboard/{kimi-code-Cx5YUJpS.js → kimi-code-BZh3NgcN.js} +1 -1
  83. package/dist/dashboard/{lifecycle-DZFHmpGI.js → lifecycle-CvcZ4_fA.js} +4 -4
  84. package/dist/dashboard/{lifecycle-DZFHmpGI.js.map → lifecycle-CvcZ4_fA.js.map} +1 -1
  85. package/dist/dashboard/{linear-mcp-auth-DylPJABZ.js → linear-mcp-auth-CZoj_-d7.js} +1 -1
  86. package/dist/dashboard/{linear-mcp-auth-BFlpMvWh.js → linear-mcp-auth-Dx74yCAY.js} +2 -2
  87. package/dist/dashboard/{linear-mcp-auth-BFlpMvWh.js.map → linear-mcp-auth-Dx74yCAY.js.map} +1 -1
  88. package/dist/dashboard/{memory-governor-B7uDWRo9.js → memory-governor-Bv3LAqk3.js} +5 -5
  89. package/dist/dashboard/{memory-governor-B7uDWRo9.js.map → memory-governor-Bv3LAqk3.js.map} +1 -1
  90. package/dist/dashboard/{merge-agent-ddEcZDZD.js → merge-agent-1zVLl6k_.js} +1 -1
  91. package/dist/dashboard/{merge-agent-ttRY053V.js → merge-agent-8zrQu48t.js} +8 -8
  92. package/dist/dashboard/{merge-agent-ttRY053V.js.map → merge-agent-8zrQu48t.js.map} +1 -1
  93. package/dist/dashboard/merge-ops-CFJaMPLu.js +2 -0
  94. package/dist/dashboard/{merge-train-f_un38UJ.js → merge-train-BYH_cF7G.js} +2 -2
  95. package/dist/dashboard/{merge-train-f_un38UJ.js.map → merge-train-BYH_cF7G.js.map} +1 -1
  96. package/dist/dashboard/{merge-train-deps-GPlHOjK6.js → merge-train-deps-BTfXM5AR.js} +2 -2
  97. package/dist/dashboard/{merge-train-deps-GPlHOjK6.js.map → merge-train-deps-BTfXM5AR.js.map} +1 -1
  98. package/dist/dashboard/{messaging-BasbftDu.js → messaging-Bk6vGsMw.js} +1 -1
  99. package/dist/dashboard/{orphan-proposed-reconciler-DFHdQSEg.js → orphan-proposed-reconciler-B898fk26.js} +6 -6
  100. package/dist/dashboard/{orphan-proposed-reconciler-DFHdQSEg.js.map → orphan-proposed-reconciler-B898fk26.js.map} +1 -1
  101. package/dist/dashboard/{orphan-proposed-reconciler-DzAxrM99.js → orphan-proposed-reconciler-Bx_aWS4B.js} +1 -1
  102. package/dist/dashboard/{parked-residue-DMtbxJml.js → parked-residue-q2kzrYSm.js} +9 -9
  103. package/dist/dashboard/{parked-residue-DMtbxJml.js.map → parked-residue-q2kzrYSm.js.map} +1 -1
  104. package/dist/dashboard/{post-merge-lifecycle-worker-jzN2g_EY.js → post-merge-lifecycle-worker-Cl7-uf4g.js} +2 -2
  105. package/dist/dashboard/{post-merge-lifecycle-worker-jzN2g_EY.js.map → post-merge-lifecycle-worker-Cl7-uf4g.js.map} +1 -1
  106. package/dist/dashboard/{project-resource-refresh-queue-cqOeJ0fB.js → project-resource-refresh-queue-O_dRfPHn.js} +1 -1
  107. package/dist/dashboard/{project-resource-refresh-queue-DebwjPPA.js → project-resource-refresh-queue-se3T9IYP.js} +4 -4
  108. package/dist/dashboard/{project-resource-refresh-queue-DebwjPPA.js.map → project-resource-refresh-queue-se3T9IYP.js.map} +1 -1
  109. package/dist/dashboard/{reconstruct-cache-Bb2Jg2DZ.js → reconstruct-cache-XpWC9L0F.js} +2 -2
  110. package/dist/dashboard/{reconstruct-cache-Bb2Jg2DZ.js.map → reconstruct-cache-XpWC9L0F.js.map} +1 -1
  111. package/dist/dashboard/{release-engine-78iCajIc.js → release-engine-BuzxOamf.js} +2 -2
  112. package/dist/dashboard/{release-engine-78iCajIc.js.map → release-engine-BuzxOamf.js.map} +1 -1
  113. package/dist/dashboard/{remote-completion-D5wzZCte.js → remote-completion-BCZgeKPS.js} +3 -3
  114. package/dist/dashboard/{remote-completion-D5wzZCte.js.map → remote-completion-BCZgeKPS.js.map} +1 -1
  115. package/dist/dashboard/{resolver-BGZh3lXA.js → resolver-BeNrqNou.js} +5 -5
  116. package/dist/dashboard/{resolver-BGZh3lXA.js.map → resolver-BeNrqNou.js.map} +1 -1
  117. package/dist/dashboard/{resource-pressure-patrol-Bg23rPV-.js → resource-pressure-patrol-Ccg_aCgO.js} +3 -3
  118. package/dist/dashboard/{resource-pressure-patrol-Bg23rPV-.js.map → resource-pressure-patrol-Ccg_aCgO.js.map} +1 -1
  119. package/dist/dashboard/{resume-BKyB4hA1.js → resume-KGCrn0DV.js} +1 -1
  120. package/dist/dashboard/{review-agent-ZdP-HmwY.js → review-agent-BmM6YMZW.js} +1 -1
  121. package/dist/dashboard/{review-agent-DHs-JngN.js → review-agent-DRWVU8fP.js} +13 -13
  122. package/dist/dashboard/{review-agent-DHs-JngN.js.map → review-agent-DRWVU8fP.js.map} +1 -1
  123. package/dist/dashboard/{review-convoy-liveness-BTMvU4CA.js → review-convoy-liveness-EvCDv8Q_.js} +3 -3
  124. package/dist/dashboard/{review-convoy-liveness-BTMvU4CA.js.map → review-convoy-liveness-EvCDv8Q_.js.map} +1 -1
  125. package/dist/dashboard/{review-run-recovery-BYalQzbh.js → review-run-recovery-Ctm48Snt.js} +1 -1
  126. package/dist/dashboard/{review-run-recovery-BvVG3oUr.js → review-run-recovery-DNDI58cM.js} +2 -2
  127. package/dist/dashboard/{review-run-recovery-BvVG3oUr.js.map → review-run-recovery-DNDI58cM.js.map} +1 -1
  128. package/dist/dashboard/{review-status-DXTFzCmR.js → review-status-FnlvQuPI.js} +1 -1
  129. package/dist/dashboard/{review-status-emit-C9YW6SQL.js → review-status-emit-CeMS31DX.js} +2 -2
  130. package/dist/dashboard/{review-status-emit-C9YW6SQL.js.map → review-status-emit-CeMS31DX.js.map} +1 -1
  131. package/dist/dashboard/{review-status-BegAr86G.js → review-status-yY5qt7Rl.js} +5 -5
  132. package/dist/dashboard/{review-status-BegAr86G.js.map → review-status-yY5qt7Rl.js.map} +1 -1
  133. package/dist/dashboard/{review-verdict-feedback-qlVBi19-.js → review-verdict-feedback-BNgRj1e3.js} +4 -4
  134. package/dist/dashboard/{review-verdict-feedback-qlVBi19-.js.map → review-verdict-feedback-BNgRj1e3.js.map} +1 -1
  135. package/dist/dashboard/{review-verdict-feedback-Dp139Lmv.js → review-verdict-feedback-D39cuSV1.js} +1 -1
  136. package/dist/dashboard/{review-verdict-writer-CJr_Ob0Y.js → review-verdict-writer-DvFfKAn-.js} +2 -2
  137. package/dist/dashboard/{review-verdict-writer-CJr_Ob0Y.js.map → review-verdict-writer-DvFfKAn-.js.map} +1 -1
  138. package/dist/dashboard/{review-verdict-writer-BVco3WoI.js → review-verdict-writer-PcGBkvJ4.js} +1 -1
  139. package/dist/dashboard/{sequence-io-iljWlslY.js → sequence-io-DZyschsf.js} +2 -2
  140. package/dist/dashboard/{sequence-io-iljWlslY.js.map → sequence-io-DZyschsf.js.map} +1 -1
  141. package/dist/dashboard/{sequencer-agent-BGGVKxtw.js → sequencer-agent-BXDw5cNx.js} +5 -5
  142. package/dist/dashboard/{sequencer-agent-BGGVKxtw.js.map → sequencer-agent-BXDw5cNx.js.map} +1 -1
  143. package/dist/dashboard/server.js +79 -72
  144. package/dist/dashboard/server.js.map +1 -1
  145. package/dist/dashboard/{service-kzhN0DLg.js → service-DP50Omvn.js} +33 -33
  146. package/dist/dashboard/{service-kzhN0DLg.js.map → service-DP50Omvn.js.map} +1 -1
  147. package/dist/dashboard/{smart-compaction-owKHJDbL.js → smart-compaction-944GL8PN.js} +1 -1
  148. package/dist/dashboard/{smart-compaction-C96F2aFQ.js → smart-compaction-BzDfF6yb.js} +2 -2
  149. package/dist/dashboard/{smart-compaction-C96F2aFQ.js.map → smart-compaction-BzDfF6yb.js.map} +1 -1
  150. package/dist/dashboard/{spawn-planning-session-CiJdOHTD.js → spawn-planning-session-BcVnHJjm.js} +1 -1
  151. package/dist/dashboard/{spawn-planning-session-FmJa6PCS.js → spawn-planning-session-CH8IYli7.js} +3 -3
  152. package/dist/dashboard/{spawn-planning-session-FmJa6PCS.js.map → spawn-planning-session-CH8IYli7.js.map} +1 -1
  153. package/dist/dashboard/{specialist-context-_ootmzn2.js → specialist-context-DhDTXVht.js} +3 -3
  154. package/dist/dashboard/{specialist-context-_ootmzn2.js.map → specialist-context-DhDTXVht.js.map} +1 -1
  155. package/dist/dashboard/{specialist-logs-BbAX221-.js → specialist-logs-C1bcnZ-G.js} +2 -2
  156. package/dist/dashboard/{specialist-logs-BbAX221-.js.map → specialist-logs-C1bcnZ-G.js.map} +1 -1
  157. package/dist/dashboard/{specialists-COZoYaiz.js → specialists-BKmReEWT.js} +53 -53
  158. package/dist/dashboard/{specialists-COZoYaiz.js.map → specialists-BKmReEWT.js.map} +1 -1
  159. package/dist/dashboard/{specialists-RPLJnYc1.js → specialists-Bvrvt0br.js} +12 -12
  160. package/dist/dashboard/{specialists-RPLJnYc1.js.map → specialists-Bvrvt0br.js.map} +1 -1
  161. package/dist/dashboard/{specialists-DTAOVZpU.js → specialists-CD6GEKXk.js} +1 -1
  162. package/dist/dashboard/{specialists-D8sYZnPb.js → specialists-eFWpJRBc.js} +1 -1
  163. package/dist/dashboard/{stall-sweeper-9bRBZhrQ.js → stall-sweeper-D3fZvg7O.js} +3 -3
  164. package/dist/dashboard/{stall-sweeper-9bRBZhrQ.js.map → stall-sweeper-D3fZvg7O.js.map} +1 -1
  165. package/dist/dashboard/{state-migrate-J3tFJnbd.js → state-migrate-KZ2goscQ.js} +2 -2
  166. package/dist/dashboard/{state-migrate-J3tFJnbd.js.map → state-migrate-KZ2goscQ.js.map} +1 -1
  167. package/dist/dashboard/{stuck-remediation-BvTK_NYN.js → stuck-remediation-Cb2V1roj.js} +1 -1
  168. package/dist/dashboard/{stuck-remediation-4Rf_xMEb.js → stuck-remediation-CnoV0kcv.js} +5 -5
  169. package/dist/dashboard/{stuck-remediation-4Rf_xMEb.js.map → stuck-remediation-CnoV0kcv.js.map} +1 -1
  170. package/dist/dashboard/{summary-fork-PLcckTRE.js → summary-fork-CxuNBM0F.js} +2 -2
  171. package/dist/dashboard/{summary-fork-D-cylknJ.js → summary-fork-lC2vMXly.js} +4 -4
  172. package/dist/dashboard/{summary-fork-D-cylknJ.js.map → summary-fork-lC2vMXly.js.map} +1 -1
  173. package/dist/dashboard/{teardown-workspace-CTL16rnG.js → teardown-workspace-C-9murBw.js} +4 -4
  174. package/dist/dashboard/{teardown-workspace-CTL16rnG.js.map → teardown-workspace-C-9murBw.js.map} +1 -1
  175. package/dist/dashboard/{test-agent-queue-CMgYZiR_.js → test-agent-queue-YCBRzaAB.js} +3 -3
  176. package/dist/dashboard/{test-agent-queue-CMgYZiR_.js.map → test-agent-queue-YCBRzaAB.js.map} +1 -1
  177. package/dist/dashboard/{uat-failure-feedback-DS6W9X3I.js → uat-failure-feedback-B-1mL8Zp.js} +3 -3
  178. package/dist/dashboard/{uat-failure-feedback-DS6W9X3I.js.map → uat-failure-feedback-B-1mL8Zp.js.map} +1 -1
  179. package/dist/dashboard/{uat-promote-verification-CRsIHE_q.js → uat-promote-verification-BEBPcpPg.js} +2 -2
  180. package/dist/dashboard/{uat-promote-verification-CRsIHE_q.js.map → uat-promote-verification-BEBPcpPg.js.map} +1 -1
  181. package/dist/dashboard/{uat-train-BoA9r6_I.js → uat-train-BFo3OQUB.js} +1 -1
  182. package/dist/dashboard/{uat-train-Cd8CJeNe.js → uat-train-COgoJomz.js} +8 -8
  183. package/dist/dashboard/{uat-train-Cd8CJeNe.js.map → uat-train-COgoJomz.js.map} +1 -1
  184. package/dist/dashboard/{validate-BdmvTIym.js → validate-069_N1e2.js} +2 -2
  185. package/dist/dashboard/{validate-BdmvTIym.js.map → validate-069_N1e2.js.map} +1 -1
  186. package/dist/dashboard/{verification-runner-BO0K3xNA.js → verification-runner-CQnZyC5A.js} +1 -1
  187. package/dist/dashboard/{verification-runner-fEY1Qj-l.js → verification-runner-fy3wNrWn.js} +4 -4
  188. package/dist/dashboard/{verification-runner-fEY1Qj-l.js.map → verification-runner-fy3wNrWn.js.map} +1 -1
  189. package/dist/dashboard/workflows-Bk6Kt0ZQ.js +2 -0
  190. package/dist/dashboard/{workflows-CxVnygYU.js → workflows-N4QMkDzN.js} +10 -10
  191. package/dist/dashboard/{workflows-CxVnygYU.js.map → workflows-N4QMkDzN.js.map} +1 -1
  192. package/dist/dashboard/{workspaces-Cpz0xJWQ.js → workspaces-DmoyfHoU.js} +33 -33
  193. package/dist/dashboard/{workspaces-Cpz0xJWQ.js.map → workspaces-DmoyfHoU.js.map} +1 -1
  194. package/dist/{lifecycle-C6k0HWEO.js → lifecycle-DCFcjg7y.js} +1 -1
  195. package/dist/{lifecycle-D5oRpvvB.js → lifecycle-GkaHQJNK.js} +2 -2
  196. package/dist/{lifecycle-D5oRpvvB.js.map → lifecycle-GkaHQJNK.js.map} +1 -1
  197. package/dist/{merge-agent-Drufc7tU.js → merge-agent-CogNFasY.js} +2 -2
  198. package/dist/{merge-agent-Drufc7tU.js.map → merge-agent-CogNFasY.js.map} +1 -1
  199. package/dist/{merge-agent-CBOVY_eC.js → merge-agent-eteKQ9Rb.js} +1 -1
  200. package/package.json +1 -1
  201. package/dist/build-info-BgrwLUy6.js +0 -2
  202. package/dist/dashboard/agent-state-Ci6PhjOE.js.map +0 -1
  203. package/dist/dashboard/build-info-CYlxvZSQ.js +0 -2
  204. package/dist/dashboard/cli-ZAS502uC.js +0 -2
  205. package/dist/dashboard/conversation-service-Dlks_hSS.js +0 -3
  206. package/dist/dashboard/inspect-agent-DTuSg95i.js +0 -2
  207. package/dist/dashboard/merge-ops-Bgc-EScO.js +0 -2
  208. package/dist/dashboard/workflows-CgGNyl1z.js +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"flywheel-D0guNqkh.js","names":["setOrderBookStatus","execAsync","nodeServicesLayer"],"sources":["../../src/lib/flywheel-state-retention.ts","../../src/cli/commands/flywheel-merge-backend.ts","../../src/cli/commands/flywheel-orders.ts","../../src/cli/commands/flywheel-complete.ts","../../src/cli/commands/flywheel-surfaces.ts","../../src/cli/commands/flywheel.ts"],"sourcesContent":["import { readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport const FLYWHEEL_STATE_MAX_BYTES = 120 * 1024;\nexport const FLYWHEEL_STATE_MAX_LINES = 1_000;\nexport const FLYWHEEL_STATE_VERBATIM_RUNS = 3;\n\ninterface MarkdownBlock {\n heading: string;\n text: string;\n runNumber: number | null;\n}\n\nexport interface FlywheelStateCompactionResult {\n content: string;\n compactedRunNumbers: number[];\n preservedRunNumbers: number[];\n changed: boolean;\n}\n\nexport interface FlywheelStateFileCompactionResult {\n compacted: boolean;\n compactedRunNumbers: number[];\n preservedRunNumbers: number[];\n}\n\nfunction parseBlocks(content: string): { preamble: string; blocks: MarkdownBlock[] } {\n const matches = [...content.matchAll(/^## .+$/gm)];\n if (matches.length === 0) return { preamble: content, blocks: [] };\n\n const firstIndex = matches[0].index ?? 0;\n return {\n preamble: content.slice(0, firstIndex),\n blocks: matches.map((match, index) => {\n const start = match.index ?? 0;\n const end = matches[index + 1]?.index ?? content.length;\n const runMatch = match[0].match(/^## RUN-(\\d+)\\b/i);\n const isCompactedSummary = /^## RUN-\\d+\\s*…/i.test(match[0]) || /compacted summary/i.test(match[0]);\n return {\n heading: match[0],\n text: content.slice(start, end),\n runNumber: runMatch && !isCompactedSummary ? Number(runMatch[1]) : null,\n };\n }),\n };\n}\n\nfunction normalizeOneLine(value: string): string {\n return value.replace(/\\s+/g, ' ').trim();\n}\n\nfunction truncate(value: string, maxLength: number): string {\n if (value.length <= maxLength) return value;\n return `${value.slice(0, maxLength - 1).trimEnd()}…`;\n}\n\nfunction tickNumber(heading: string): number {\n const match = heading.match(/\\bticks?\\s+(\\d+)/i);\n return match ? Number(match[1]) : -1;\n}\n\nfunction summarizeRun(runNumber: number, blocks: MarkdownBlock[]): string {\n const selected = blocks.reduce((latest, candidate) => (\n tickNumber(candidate.heading) >= tickNumber(latest.heading) ? candidate : latest\n ));\n const dates = blocks.flatMap((block) => block.text.match(/\\b\\d{4}-\\d{2}-\\d{2}\\b/g) ?? []).sort();\n const date = dates.at(-1);\n const dashIndex = selected.heading.indexOf('—');\n const rawSummary = dashIndex >= 0\n ? selected.heading.slice(dashIndex + 1)\n : selected.heading.replace(new RegExp(`^## RUN-${runNumber}\\\\b`, 'i'), '');\n const summary = truncate(normalizeOneLine(rawSummary) || 'run completed', 220);\n const issueIds: string[] = [];\n const seen = new Set<string>();\n for (const text of [selected.text, ...blocks.map((block) => block.text)]) {\n for (const match of text.matchAll(/\\bPAN-\\d+\\b/g)) {\n if (seen.has(match[0])) continue;\n seen.add(match[0]);\n issueIds.push(match[0]);\n if (issueIds.length === 8) break;\n }\n if (issueIds.length === 8) break;\n }\n const issues = issueIds.length > 0 ? `; key issues: ${issueIds.join(', ')}` : '';\n return `- **RUN-${runNumber}${date ? ` (${date})` : ''}** — ${summary}${issues}`;\n}\n\nfunction readExistingSummaries(block: MarkdownBlock | undefined): Map<number, string> {\n const summaries = new Map<number, string>();\n if (!block) return summaries;\n\n const lines = block.text.split('\\n');\n let current: string[] = [];\n const flush = (): void => {\n if (current.length === 0) return;\n const summary = normalizeOneLine(current.join(' '));\n const match = summary.match(/^- \\*\\*RUN-(\\d+)\\b/);\n if (match) summaries.set(Number(match[1]), summary);\n current = [];\n };\n\n for (const line of lines) {\n if (/^- \\*\\*RUN-\\d+\\b/.test(line)) {\n flush();\n current.push(line);\n } else if (current.length > 0) {\n if (line.startsWith(' ') || line.trim() === '') current.push(line);\n else flush();\n }\n }\n flush();\n return summaries;\n}\n\nfunction buildCompactedLog(summaries: Map<number, string>): string {\n const runLines = [...summaries.entries()]\n .sort(([left], [right]) => left - right)\n .map(([, summary]) => summary);\n return [\n '## Compacted run log (older than the latest 3 runs)',\n '',\n 'One terse line per older run. The original tick-by-tick detail remains available in git history',\n '(`git log --follow docs/FLYWHEEL-STATE.md`); compaction never deletes repository history.',\n '',\n ...runLines,\n '',\n '',\n ].join('\\n');\n}\n\nexport function shouldCompactFlywheelState(content: string): boolean {\n return Buffer.byteLength(content, 'utf8') > FLYWHEEL_STATE_MAX_BYTES\n || content.split('\\n').length > FLYWHEEL_STATE_MAX_LINES;\n}\n\nexport function compactFlywheelState(content: string): FlywheelStateCompactionResult {\n if (!shouldCompactFlywheelState(content)) {\n return { content, compactedRunNumbers: [], preservedRunNumbers: [], changed: false };\n }\n\n const { preamble, blocks } = parseBlocks(content);\n const detailedRunNumbers = [...new Set(\n blocks.flatMap((block) => block.runNumber === null ? [] : [block.runNumber]),\n )].sort((left, right) => left - right);\n const preservedRunNumbers = detailedRunNumbers.slice(-FLYWHEEL_STATE_VERBATIM_RUNS);\n const preserved = new Set(preservedRunNumbers);\n const compactedRunNumbers = detailedRunNumbers.filter((runNumber) => !preserved.has(runNumber));\n if (compactedRunNumbers.length === 0) {\n return { content, compactedRunNumbers: [], preservedRunNumbers, changed: false };\n }\n\n const compactionBlocks = blocks.filter((block) => block.heading.startsWith('## Compacted run log'));\n const compactionBlock = compactionBlocks[0];\n const summaries = new Map<number, string>();\n for (const block of compactionBlocks) {\n for (const [runNumber, summary] of readExistingSummaries(block)) summaries.set(runNumber, summary);\n }\n for (const runNumber of compactedRunNumbers) {\n const runBlocks = blocks.filter((block) => block.runNumber === runNumber);\n summaries.set(runNumber, summarizeRun(runNumber, runBlocks));\n }\n for (const runNumber of preservedRunNumbers) summaries.delete(runNumber);\n const compactedLog = buildCompactedLog(summaries);\n\n const output: string[] = [preamble];\n let insertedCompactedLog = false;\n const insertionBlock = blocks.find((block) => block.heading.startsWith('## Recent runs'))\n ?? blocks.find((block) => block.runNumber !== null);\n for (const block of blocks) {\n if (compactionBlocks.includes(block)) {\n if (block === compactionBlock) {\n output.push(compactedLog);\n insertedCompactedLog = true;\n }\n continue;\n }\n if (!insertedCompactedLog && !compactionBlock && block === insertionBlock) {\n output.push(compactedLog);\n insertedCompactedLog = true;\n }\n if (block.runNumber !== null && !preserved.has(block.runNumber)) continue;\n output.push(block.text);\n }\n if (!insertedCompactedLog) output.push(compactedLog);\n\n const compacted = output.join('');\n return {\n content: compacted,\n compactedRunNumbers,\n preservedRunNumbers,\n changed: compacted !== content,\n };\n}\n\nexport async function compactFlywheelStateFile(cwd: string): Promise<FlywheelStateFileCompactionResult> {\n const path = join(cwd, 'docs', 'FLYWHEEL-STATE.md');\n let content: string;\n try {\n content = await readFile(path, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return { compacted: false, compactedRunNumbers: [], preservedRunNumbers: [] };\n }\n throw error;\n }\n\n const result = compactFlywheelState(content);\n if (result.changed) await writeFile(path, result.content, 'utf8');\n return {\n compacted: result.changed,\n compactedRunNumbers: result.compactedRunNumbers,\n preservedRunNumbers: result.preservedRunNumbers,\n };\n}\n","import { getMergeBackendStatus, type MergeBackendStatus } from '../../lib/github-app.js';\n\nexport function formatMergeBackendStatus(status: MergeBackendStatus): string {\n if (status.available) {\n return `Merge backend: ${status.mode} (${status.detail})`;\n }\n return `Merge backend: UNAVAILABLE - ${status.detail}`;\n}\n\nexport async function loadMergeBackendStatusForCli(): Promise<MergeBackendStatus> {\n try {\n return await getMergeBackendStatus();\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n available: false,\n mode: 'none',\n detail: `Unable to determine merge backend: ${message}`,\n };\n }\n}\n","import { readFile } from 'node:fs/promises';\nimport type { OrderBook } from '@overdeck/contracts';\n\nimport { abortSpawnedFlywheel } from '../../lib/cloister/flywheel.js';\nimport { requireFlywheelBrief } from '../../lib/flywheel-start.js';\nimport { ensureOrderIssueStore, getBook } from '../../lib/orders/resolver.js';\nimport { validateBookForStart } from '../../lib/orders/validate.js';\nimport { setStatus as setOrderBookStatus } from '../../lib/orders/writer.js';\nimport { findProjectByPathSync } from '../../lib/projects.js';\nimport { resolveStateReadHomeSync } from '../../lib/state-read-home.js';\n\nexport interface FlywheelOrderStartDeps {\n orderStateRoot?: (cwd: string) => string;\n getOrderBook?: typeof getBook;\n prepareIssueStore?: () => Promise<unknown>;\n validateOrderBook?: typeof validateBookForStart;\n setOrderStatus?: typeof setOrderBookStatus;\n requireBrief?: typeof requireFlywheelBrief;\n readBrief?: (path: string) => Promise<string>;\n cleanupSpawnedRun?: (runId: string) => Promise<void>;\n}\n\nexport interface FlywheelOrderStartContext {\n stateRoot: string;\n book: OrderBook;\n}\n\nfunction orderStateRootFor(cwd: string): string {\n const project = findProjectByPathSync(cwd);\n if (!project) throw new Error(`No configured project contains ${cwd}`);\n return resolveStateReadHomeSync(project).root;\n}\n\nfunction validationError(bookId: string, findings: readonly { code: string; issue: string; message: string }[]): Error {\n return new Error([\n `Order book ${bookId} cannot start:`,\n ...findings.map((finding) => `- [${finding.code}] ${finding.issue}: ${finding.message}`),\n ].join('\\n'));\n}\n\nexport async function resolveFlywheelOrderStart(\n cwd: string,\n bookId: string,\n deps: FlywheelOrderStartDeps = {},\n): Promise<FlywheelOrderStartContext> {\n const stateRoot = (deps.orderStateRoot ?? orderStateRootFor)(cwd);\n const book = (deps.getOrderBook ?? getBook)(stateRoot, bookId);\n if (!book) throw new Error(`Order book not found: ${bookId}`);\n if (book.status !== 'ready') {\n throw new Error(`Order book ${book.id} must be ready before start (current status: ${book.status})`);\n }\n await (deps.prepareIssueStore ?? ensureOrderIssueStore)();\n const validation = (deps.validateOrderBook ?? validateBookForStart)(stateRoot, book);\n if (validation.blocks.length > 0) throw validationError(book.id, validation.blocks);\n return { stateRoot, book };\n}\n\nexport async function resolveFlywheelOrderBriefOverlay(\n cwd: string,\n book: OrderBook | null,\n deps: FlywheelOrderStartDeps = {},\n): Promise<{ briefOverlayPath?: string; briefOverlayContent?: string }> {\n const path = book?.settings.briefOverlay?.trim();\n if (!path) return {};\n const brief = await (deps.requireBrief ?? requireFlywheelBrief)(cwd, path);\n return {\n briefOverlayPath: brief.displayPath,\n briefOverlayContent: await (deps.readBrief ?? ((absolutePath) => readFile(absolutePath, 'utf8')))(brief.absolutePath),\n };\n}\n\nexport async function compensateFailedFlywheelStart(\n context: FlywheelOrderStartContext | null,\n runId: string,\n cause: unknown,\n deps: FlywheelOrderStartDeps = {},\n): Promise<never> {\n const failures = [cause];\n try {\n await (deps.cleanupSpawnedRun ?? abortSpawnedFlywheel)(runId);\n } catch (error) {\n failures.push(error);\n }\n if (context) {\n try {\n await setFlywheelOrderStatus(context, 'ready', null, deps);\n } catch (error) {\n failures.push(error);\n }\n }\n if (failures.length > 1) {\n throw new AggregateError(failures, `Flywheel run ${runId} failed to start and compensation was incomplete`);\n }\n throw cause;\n}\n\nexport async function setFlywheelOrderStatus(\n context: FlywheelOrderStartContext,\n status: 'ready' | 'running',\n runId: string | null | undefined,\n deps: FlywheelOrderStartDeps = {},\n): Promise<void> {\n await (deps.setOrderStatus ?? setOrderBookStatus)(\n context.stateRoot,\n context.book.id,\n status,\n runId === undefined ? {} : { runId },\n );\n}\n","import { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { FlywheelStatus, OrderBook } from '@overdeck/contracts';\n\nimport {\n getFlywheelRunDir,\n readFlywheelLaunchMetadata,\n} from '../../dashboard/server/services/flywheel-run-state.js';\nimport { isFlywheelAutoPickupBacklog } from '../../lib/overdeck/control-settings.js';\nimport { computeBookProgress, firstReadyBookInQueue, getBook } from '../../lib/orders/resolver.js';\nimport type { OrderBookProgress } from '../../lib/orders/types.js';\nimport { advanceQueue } from '../../lib/orders/writer.js';\nimport { findProjectByPathSync, listProjectsSync, resolveProjectPath } from '../../lib/projects.js';\nimport { resolveStateReadHomeSync } from '../../lib/state-read-home.js';\n\nexport interface CompleteFlywheelOptions {\n cwd?: string;\n force?: boolean;\n}\n\ninterface StartContinuationOptions {\n cwd: string;\n brief?: string;\n orders?: string;\n}\n\ninterface StartContinuationResult {\n runId: string;\n}\n\nexport interface CompleteFlywheelDeps {\n loadStatus: () => Promise<FlywheelStatus | null>;\n buildReport: (status: FlywheelStatus, cwd: string) => Promise<string>;\n persistReport: (status: FlywheelStatus, cwd: string, report: string) => Promise<void>;\n clearGate: (runId: string) => void;\n start: (options: StartContinuationOptions) => Promise<StartContinuationResult>;\n readLaunch?: typeof readFlywheelLaunchMetadata;\n stateRoot?: (workspace: string) => string;\n getOrderBook?: typeof getBook;\n computeProgress?: (book: OrderBook) => OrderBookProgress;\n advance?: typeof advanceQueue;\n autoPickupBacklog?: () => boolean;\n readRetro?: (path: string) => Promise<string | null>;\n listProjects?: typeof listProjectsSync;\n firstReady?: typeof firstReadyBookInQueue;\n}\n\nexport interface CompleteFlywheelResult {\n runId: string;\n reportPath: string;\n retrospectiveIncluded: boolean;\n continuation: 'next-book' | 'backlog' | 'needs-you';\n nextRunId?: string;\n nextBookId?: string;\n nextProjectKey?: string;\n}\n\nfunction stateRootFor(workspace: string): string {\n const project = findProjectByPathSync(workspace);\n if (!project) throw new Error(`No configured project contains ${workspace}`);\n return resolveStateReadHomeSync(project).root;\n}\n\nasync function readOptionalFile(path: string): Promise<string | null> {\n try {\n return await readFile(path, 'utf8');\n } catch (error) {\n const code = typeof error === 'object' && error !== null && 'code' in error ? error.code : undefined;\n if (code === 'ENOENT') return null;\n throw error;\n }\n}\n\nexport function appendFlywheelRetrospective(report: string, retro: string | null): {\n report: string;\n retrospectiveIncluded: boolean;\n} {\n const finding = retro?.trim();\n if (!finding) {\n return {\n report: `${report.trimEnd()}\\n\\nRetrospective: no findings recognized.\\n`,\n retrospectiveIncluded: false,\n };\n }\n return {\n report: `${report.trimEnd()}\\n\\n---\\n\\n## Retrospective\\n\\n${finding}\\n`,\n retrospectiveIncluded: true,\n };\n}\n\nfunction nonTerminalIssues(progress: OrderBookProgress): string[] {\n return progress.items.filter((item) => !item.terminal).map((item) => item.issue);\n}\n\n/**\n * A ready book is an explicit operator release, so continuation looks past the\n * completing project: every tracked project's ready queue is scanned in registry\n * order and the first hit wins. Run scope governs backlog pickup, not this.\n */\nfunction findCrossProjectContinuation(\n stateRoot: string,\n deps: CompleteFlywheelDeps,\n): { projectKey: string; cwd: string; book: OrderBook } | null {\n const firstReady = deps.firstReady ?? firstReadyBookInQueue;\n for (const { key, config } of (deps.listProjects ?? listProjectsSync)()) {\n const projectStateRoot = resolveStateReadHomeSync(config, key).root;\n if (projectStateRoot === stateRoot) continue;\n const book = firstReady(projectStateRoot);\n // resolveProjectPath returns the project's primary root, which the Flywheel\n // start path requires — it uses options.cwd verbatim.\n if (book) return { projectKey: key, cwd: resolveProjectPath(config), book };\n }\n return null;\n}\n\nexport async function completeFlywheelRun(\n options: CompleteFlywheelOptions,\n deps: CompleteFlywheelDeps,\n): Promise<CompleteFlywheelResult> {\n const cwd = options.cwd ?? process.cwd();\n const status = await deps.loadStatus();\n if (!status) throw new Error('no flywheel run to complete');\n\n const launch = await (deps.readLaunch ?? readFlywheelLaunchMetadata)(status.runId);\n if (!launch) throw new Error(`Flywheel run ${status.runId} is missing launch metadata`);\n const bookId = launch.orders?.bookId;\n if (!bookId) throw new Error(`Flywheel run ${status.runId} is not bound to an order book; use pan flywheel report`);\n\n const stateRoot = (deps.stateRoot ?? stateRootFor)(launch.workspace);\n const book = (deps.getOrderBook ?? getBook)(stateRoot, bookId);\n if (!book) throw new Error(`Flywheel run ${status.runId} references missing order book ${bookId}`);\n const progress = (deps.computeProgress ?? computeBookProgress)(book);\n const remaining = nonTerminalIssues(progress);\n if (!progress.drained && !options.force) {\n throw new Error(`Order book ${book.id} is not drained; non-terminal items: ${remaining.join(', ')}. Pass --force to complete anyway.`);\n }\n\n const runDir = getFlywheelRunDir(status.runId);\n const retro = await (deps.readRetro ?? readOptionalFile)(join(runDir, 'retro.md'));\n const report = appendFlywheelRetrospective(await deps.buildReport(status, cwd), retro);\n try {\n await deps.persistReport(status, cwd, report.report);\n } finally {\n deps.clearGate(status.runId);\n }\n\n const nextBook = await (deps.advance ?? advanceQueue)(stateRoot, book.id);\n const startOptions = { cwd: launch.workspace, brief: launch.briefPath };\n if (nextBook) {\n const next = await deps.start({ ...startOptions, orders: nextBook.id });\n return {\n runId: status.runId,\n reportPath: join(runDir, 'report.md'),\n retrospectiveIncluded: report.retrospectiveIncluded,\n continuation: 'next-book',\n nextRunId: next.runId,\n nextBookId: nextBook.id,\n };\n }\n const crossProject = findCrossProjectContinuation(stateRoot, deps);\n if (crossProject) {\n // No brief: requireFlywheelBrief resolves the target project's own default\n // docs/flywheel-brief.md, so the completing project's brief never leaks over.\n const next = await deps.start({ cwd: crossProject.cwd, orders: crossProject.book.id });\n return {\n runId: status.runId,\n reportPath: join(runDir, 'report.md'),\n retrospectiveIncluded: report.retrospectiveIncluded,\n continuation: 'next-book',\n nextRunId: next.runId,\n nextBookId: crossProject.book.id,\n nextProjectKey: crossProject.projectKey,\n };\n }\n if ((deps.autoPickupBacklog ?? isFlywheelAutoPickupBacklog)()) {\n const next = await deps.start(startOptions);\n return {\n runId: status.runId,\n reportPath: join(runDir, 'report.md'),\n retrospectiveIncluded: report.retrospectiveIncluded,\n continuation: 'backlog',\n nextRunId: next.runId,\n };\n }\n return {\n runId: status.runId,\n reportPath: join(runDir, 'report.md'),\n retrospectiveIncluded: report.retrospectiveIncluded,\n continuation: 'needs-you',\n };\n}\n\nexport function createFlywheelCompleteCommand(deps: CompleteFlywheelDeps) {\n return async (options: CompleteFlywheelOptions = {}): Promise<void> => {\n try {\n const result = await completeFlywheelRun(options, deps);\n if (result.continuation === 'next-book') {\n const project = result.nextProjectKey ? ` (project: ${result.nextProjectKey})` : '';\n console.log(`Completed ${result.runId}; started ${result.nextRunId} with order book ${result.nextBookId}${project}.`);\n } else if (result.continuation === 'backlog') {\n console.log(`Completed ${result.runId}; started ${result.nextRunId} in backlog mode.`);\n } else {\n console.log('needs-you: pipeline idle — no order book queued and auto-pickup is off');\n }\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n };\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { exec, execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport type { Command } from 'commander';\nimport { parseSequenceMd } from '../../lib/backlog/sequence-io.js';\nimport { buildClassifyLookups } from '../../lib/backlog/lookups.js';\nimport {\n classifyIssue,\n computeWaves,\n computeCohort,\n computeStats,\n selectNeedsPlanning,\n} from '../../lib/backlog/pickup.js';\nimport { isFlywheelAutoPickupBacklog } from '../../lib/overdeck/control-settings.js';\nimport { getMergeBlockersPayload } from '../../lib/cloister/merge-blockers.js';\nimport { activeOrderBookIssues } from '../../lib/cloister/flywheel.js';\nimport { listSubstrateBugWeights, type WeightedSubstrateBug } from '../../lib/overdeck/substrate-bug-weights-service.js';\n\n/**\n * Sandbox-safe Flywheel data surfaces. These read state DIRECTLY (sequence.md + SQLite) and\n * emit no HTTP, so they work inside a network-isolated harness sandbox (e.g. codex's bwrap)\n * where the orchestrator cannot `curl http://127.0.0.1:3011/api/...`. They share the pickup\n * model (PAN-2006) and the merge-blocker model (PAN-1620) with the dashboard routes, so the\n * CLI and the dashboard can never disagree.\n */\n\nconst execAsync = promisify(exec);\nconst execFileAsync = promisify(execFile);\n\nfunction parseGitHubRemoteUrl(remoteUrl: string): { owner: string; repo: string } | null {\n const trimmed = remoteUrl.trim();\n const match = trimmed.match(/github\\.com[:/]([^/]+)\\/([^/\\s]+?)(?:\\.git)?$/i);\n if (!match) return null;\n return { owner: match[1]!, repo: match[2]! };\n}\n\nasync function resolveGitHubOwnerRepo(): Promise<{ owner: string; repo: string }> {\n const { stdout } = await execAsync('git remote get-url origin', { encoding: 'utf8' });\n const repo = parseGitHubRemoteUrl(stdout);\n if (!repo) throw new Error('origin is not a GitHub repository');\n return repo;\n}\n\n/** Labels for all open issues, keyed by bare number. This must fail rather than make a\n * label-dependent forecast indistinguishable from an empty backlog. */\nexport async function fetchOpenIssueLabels(): Promise<Map<string, string[]>> {\n const repo = await resolveGitHubOwnerRepo();\n const { stdout } = await execFileAsync(\n 'gh',\n ['api', '--paginate', '--slurp', `repos/${repo.owner}/${repo.repo}/issues?state=open&per_page=100`],\n { encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 },\n );\n const pages = JSON.parse(stdout || '[]') as Array<Array<{\n number: number;\n pull_request?: unknown;\n labels?: Array<{ name?: string | null } | string>;\n }>>;\n const byNumber = new Map<string, string[]>();\n for (const issue of pages.flat()) {\n if (issue.pull_request) continue;\n byNumber.set(String(issue.number), (issue.labels ?? [])\n .map((label) => typeof label === 'string' ? label : label.name)\n .filter((name): name is string => typeof name === 'string' && name.length > 0));\n }\n return byNumber;\n}\n\n/** `pan backlog forecast` — the pickup forecast (pickable waves, needs-planning, cohort, stats). */\nexport async function backlogForecastCommand(opts: { n?: string } = {}): Promise<void> {\n const projectRoot = process.cwd();\n const n = Math.max(1, Math.min(20, Number.parseInt(opts.n ?? '5', 10) || 5));\n const seqPath = join(projectRoot, '.pan', 'backlog', 'sequence.md');\n if (!existsSync(seqPath)) {\n console.log(JSON.stringify({ n, stats: null, inFlight: [], needsPlanning: [], waves: [], cohort: [] }, null, 2));\n return;\n }\n const parsed = parseSequenceMd(readFileSync(seqPath, 'utf-8'));\n if (!parsed.ok) {\n console.error(`parse error: ${parsed.error}`);\n process.exitCode = 1;\n return;\n }\n const nodes = parsed.doc.nodes;\n const labelsByNumber = await fetchOpenIssueLabels();\n const lk = buildClassifyLookups(projectRoot, {\n labels: (id) => labelsByNumber.get(id.replace(/^[A-Za-z]+-/, '')) ?? [],\n });\n const autoPickupBacklog = isFlywheelAutoPickupBacklog();\n const inFlight = nodes\n .map((x) => ({ issue: x.issue, rank: x.rank, state: classifyIssue(x, lk) }))\n .filter((x) => x.state.inPipeline)\n .sort((a, b) => a.rank - b.rank)\n .map((x) => x.issue);\n const needsPlanning = selectNeedsPlanning(nodes, lk, { cap: n * 2 }).map((x) => x.issue);\n const orderBookIssues = await activeOrderBookIssues(projectRoot);\n const waves = computeWaves(nodes, lk, n, autoPickupBacklog, orderBookIssues).map((w) => w.map((x) => x.issue));\n const cohort = computeCohort(nodes, lk, n, autoPickupBacklog, orderBookIssues);\n const stats = computeStats(nodes, lk, autoPickupBacklog, orderBookIssues);\n console.log(JSON.stringify({ n, autoPickupBacklog, stats, inFlight, needsPlanning, waves, cohort }, null, 2));\n}\n\n/** `pan flywheel merge-blockers` — PRs that passed review but cannot merge (GitHub-native reasons). */\nexport function flywheelMergeBlockersCommand(opts: { json?: boolean } = {}): void {\n const blockers = getMergeBlockersPayload();\n if (opts.json) {\n console.log(JSON.stringify(blockers, null, 2));\n return;\n }\n if (blockers.length === 0) {\n console.log('No merge-blocked PRs.');\n return;\n }\n for (const b of blockers) {\n const reasonTypes = b.reasons.map((r) => r.type).join(', ');\n console.log(`${b.issueId} [${reasonTypes}]${b.prUrl ? ` ${b.prUrl}` : ''}`);\n for (const r of b.reasons) console.log(` - ${r.type}: ${r.summary}`);\n }\n}\n\n/** `pan flywheel weights` — substrate bugs ranked by affected v1.0 criterion weight. */\nexport async function flywheelWeightsCommand(opts: { issue?: string; window?: string; json?: boolean } = {}): Promise<void> {\n const rows = await listSubstrateBugWeights(opts.window ?? '30d');\n const targetIssue = opts.issue?.toUpperCase();\n const filtered = targetIssue ? rows.filter((r) => r.issueId === targetIssue) : rows;\n\n if (opts.json) {\n console.log(JSON.stringify(filtered, null, 2));\n return;\n }\n\n if (filtered.length === 0) {\n console.log('No weighted substrate bugs in window.');\n return;\n }\n\n const issueWidth = Math.max(...filtered.map((r) => r.issueId.length));\n const severityWidth = Math.max(...filtered.map((r) => r.severity.length));\n const weightWidth = Math.max(...filtered.map((r) => String(r.weight).length));\n\n for (const r of filtered) {\n console.log(\n `${r.issueId.padEnd(issueWidth)} ${r.severity.padEnd(severityWidth)} ${String(r.weight).padStart(weightWidth)} ${r.weightReason}`,\n );\n }\n}\n\n/**\n * Attaches the sandbox-safe surface subcommands onto the already-registered `flywheel` and\n * `backlog` commands. Kept here (not in flywheel.ts) so flywheel.ts stays under the file-size\n * ceiling. Call AFTER registerFlywheelCommands(program) and the backlog command are set up.\n */\nexport function registerFlywheelSurfaceCommands(program: Command): void {\n const flywheel = program.commands.find((c) => c.name() === 'flywheel');\n flywheel\n ?.command('merge-blockers')\n .description('PRs that passed review but cannot merge (GitHub-native reasons) — reads SQLite directly, no HTTP (sandbox-safe)')\n .option('--json', 'Output JSON')\n .action((o: { json?: boolean }) => flywheelMergeBlockersCommand(o));\n\n flywheel\n ?.command('weights')\n .description('Weighted substrate bugs by affected v1.0 criterion — reads SQLite directly, no HTTP (sandbox-safe)')\n .option('--issue <id>', 'Filter to a single issue')\n .option('--window <dur>', 'Stats window like 30d, 7d (default 30d)')\n .option('--json', 'Output JSON')\n .action((o: { issue?: string; window?: string; json?: boolean }) => flywheelWeightsCommand(o));\n\n const backlog = program.commands.find((c) => c.name() === 'backlog');\n backlog\n ?.command('forecast')\n .description('Pickup forecast (waves / needs-planning / cohort / stats) from sequence.md + SQLite — no HTTP (sandbox-safe)')\n .option('-n, --n <n>', 'Wave size (default 5)')\n .action((o: { n?: string }) => backlogForecastCommand(o));\n}\n","import { exec, spawn } from 'node:child_process';\nimport { readFile, writeFile } from 'node:fs/promises';\nimport { freemem, totalmem } from 'node:os';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\nimport { Effect, Schema } from 'effect';\nimport { layer as nodeServicesLayer } from '@effect/platform-node/NodeServices';\nimport { Command } from 'commander';\nimport {\n FlywheelStats,\n FlywheelStatus,\n type FlywheelStats as FlywheelStatsPayload,\n type FlywheelStatsCriteria,\n type FlywheelStatsCriterion,\n type FlywheelStatsCriterionStatus,\n type FlywheelStatsTrend,\n} from '@overdeck/contracts';\nimport { abortFlywheelRun, clearFlywheelGate, getFlywheelRunDetail, getFlywheelRunDir, listFlywheelRuns, nextFlywheelRunId, readFlywheelLaunchMetadata, resolveLiveFlywheelRunId, writeFlywheelLaunchMetadata, writeLatestFlywheelStatus } from '../../dashboard/server/services/flywheel-run-state.js';\nimport { loadConfigSync, resolveModel, type FlywheelScope, type RoleEffort } from '../../lib/config-yaml.js';\nimport { FLYWHEEL_ORCHESTRATOR_AGENT_ID, pauseFlywheel, resumeFlywheel, spawnFlywheel } from '../../lib/cloister/flywheel.js';\nimport { stopAgent } from '../../lib/agents.js';\nimport type { RuntimeName } from '../../lib/runtimes/types.js';\nimport { resolveHarness } from '../../lib/harness-resolve.js';\nimport {\n FLYWHEEL_AUTO_PICKUP_BACKLOG_KEY,\n FLYWHEEL_REQUIRE_UAT_BEFORE_MERGE_KEY,\n getFlywheelActiveRunId,\n isFlywheelAutoPickupBacklog,\n isFlywheelGloballyPaused,\n isFlywheelRequireUatBeforeMerge,\n setFlywheelAutoPickupBacklog,\n setFlywheelRequireUatBeforeMerge,\n} from '../../lib/overdeck/control-settings.js';\nimport { sessionExists } from '../../lib/tmux.js';\nimport { ensureInternalTokenSync, INTERNAL_TOKEN_HEADER } from '../../lib/internal-token.js';\nimport { computeMergeQueueFromCandidates, listEligibleCandidatesByProject, type MergeQueueItem } from '../../lib/flywheel-merge-order.js';\nimport { DEFAULT_BRIEF_PATH, requireFlywheelBrief, resolvePrimaryWorktreeRoot } from '../../lib/flywheel-start.js';\nimport { compactFlywheelStateFile } from '../../lib/flywheel-state-retention.js';\nimport { formatMergeBackendStatus, loadMergeBackendStatusForCli } from './flywheel-merge-backend.js';\nimport { compensateFailedFlywheelStart, resolveFlywheelOrderBriefOverlay, resolveFlywheelOrderStart, setFlywheelOrderStatus, type FlywheelOrderStartDeps } from './flywheel-orders.js';\nimport { createFlywheelCompleteCommand } from './flywheel-complete.js';\nimport { registerFlywheelSurfaceCommands } from './flywheel-surfaces.js';\n\ntype InputStream = AsyncIterable<string | Buffer | Uint8Array>;\n\ninterface EmitStatusOptions {\n file: string;\n}\n\ninterface StatusOptions {\n json?: boolean;\n}\n\ninterface StatsOptions {\n window?: string;\n json?: boolean;\n}\n\ninterface FormatStatsOptions {\n color?: boolean;\n}\n\ninterface ConfigOptions {\n get?: true | string;\n set?: string;\n}\n\ninterface StartOptions { brief?: string; cwd?: string; orders?: string }\n\nexport interface StartFlywheelRunDeps extends FlywheelOrderStartDeps {\n resolvePrimaryRoot?: typeof resolvePrimaryWorktreeRoot;\n nextRunId?: typeof nextFlywheelRunId;\n writeLaunchMetadata?: typeof writeFlywheelLaunchMetadata;\n resolveRoleConfig?: () => Promise<ResolvedFlywheelRoleConfig>;\n spawn?: typeof spawnFlywheel;\n writeStatus?: typeof writeLatestFlywheelStatus;\n}\n\ninterface StartFlywheelRunResult {\n runId: string;\n briefDisplayPath: string;\n agentModel?: string;\n}\n\ninterface ReportOptions { cwd?: string; force?: boolean }\n\ninterface ReportOpenOptions {\n runId?: string;\n opener?: (path: string) => void | Promise<void>;\n}\n\ninterface FlywheelGateSnapshot {\n paused: boolean;\n activeRunId: string | null;\n}\n\ninterface ResolvedFlywheelRoleConfig {\n harness: 'claude-code' | 'ohmypi' | 'codex' | 'acp' | 'kimi-code' | 'opencode' | 'muse';\n model: string;\n effort: RoleEffort;\n minAgents: number;\n maxAgents: number;\n scope: FlywheelScope;\n autoPickupBacklog: boolean;\n requireUatBeforeMerge: boolean;\n}\n\nconst decodeFlywheelStatus = Schema.decodeUnknownSync(FlywheelStatus);\nconst decodeFlywheelStats = Schema.decodeUnknownSync(FlywheelStats);\nconst execAsync = promisify(exec);\nconst DEFAULT_STATS_WINDOW = '30d';\n\nfunction dashboardBaseUrl(): string {\n return (process.env.OVERDECK_DASHBOARD_URL || process.env.DASHBOARD_URL || 'http://localhost:3011').replace(/\\/$/, '');\n}\n\nexport async function readFlywheelStatusJson(file: string, input: InputStream = process.stdin): Promise<string> {\n if (file !== '-') return readFile(file, 'utf8');\n\n const chunks: string[] = [];\n for await (const chunk of input) {\n chunks.push(typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8'));\n }\n return chunks.join('');\n}\n\nexport function parseFlywheelStatusJson(raw: string): unknown {\n try {\n return JSON.parse(raw);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Invalid JSON: ${message}`);\n }\n}\n\nexport function validateFlywheelStatusPayload(payload: unknown): FlywheelStatus {\n try {\n return decodeFlywheelStatus(payload);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Invalid FlywheelStatus: ${message}`);\n }\n}\n\nfunction isFlywheelConfigKey(key: string): key is FlywheelConfigKey {\n return FLYWHEEL_CONFIG_KEYS.includes(key as FlywheelConfigKey);\n}\n\nfunction parseFlywheelConfigKey(key: string): FlywheelConfigKey {\n if (!isFlywheelConfigKey(key)) throw new Error(`Unknown flywheel config key: ${key}`);\n return key;\n}\n\nfunction readFlywheelConfigValue(key: FlywheelConfigKey): boolean {\n switch (key) {\n case FLYWHEEL_AUTO_PICKUP_BACKLOG_KEY:\n return isFlywheelAutoPickupBacklog();\n case FLYWHEEL_REQUIRE_UAT_BEFORE_MERGE_KEY:\n return isFlywheelRequireUatBeforeMerge();\n }\n}\n\nfunction writeFlywheelConfigValue(key: FlywheelConfigKey, value: boolean): void {\n switch (key) {\n case FLYWHEEL_AUTO_PICKUP_BACKLOG_KEY:\n setFlywheelAutoPickupBacklog(value);\n return;\n case FLYWHEEL_REQUIRE_UAT_BEFORE_MERGE_KEY:\n setFlywheelRequireUatBeforeMerge(value);\n return;\n }\n}\n\nfunction formatFlywheelConfigValue(key: FlywheelConfigKey): string {\n return `${key}=${readFlywheelConfigValue(key)}`;\n}\n\nfunction parseConfigBoolean(key: string, rawValue: string): boolean {\n if (rawValue === 'true') return true;\n if (rawValue === 'false') return false;\n throw new Error(`Boolean value required for ${key}: ${rawValue}`);\n}\n\nfunction parseFlywheelConfigAssignment(assignment: string): { key: FlywheelConfigKey; value: boolean } {\n const separator = assignment.indexOf('=');\n if (separator === -1) throw new Error('Flywheel config assignment must use <key>=<bool>');\n const key = parseFlywheelConfigKey(assignment.slice(0, separator));\n const value = parseConfigBoolean(key, assignment.slice(separator + 1));\n return { key, value };\n}\n\nexport async function flywheelConfigCommand(options: ConfigOptions = {}): Promise<void> {\n try {\n if (options.get !== undefined && options.set !== undefined) {\n throw new Error('Use either --get or --set, not both');\n }\n\n if (options.set !== undefined) {\n const { key, value } = parseFlywheelConfigAssignment(options.set);\n writeFlywheelConfigValue(key, value);\n console.log(`${key}=${value}`);\n return;\n }\n\n if (typeof options.get === 'string') {\n console.log(formatFlywheelConfigValue(parseFlywheelConfigKey(options.get)));\n return;\n }\n\n console.log(FLYWHEEL_CONFIG_KEYS.map(formatFlywheelConfigValue).join('\\n'));\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n\nconst FLYWHEEL_CONFIG_KEYS = [\n FLYWHEEL_AUTO_PICKUP_BACKLOG_KEY,\n FLYWHEEL_REQUIRE_UAT_BEFORE_MERGE_KEY,\n] as const;\n\ntype FlywheelConfigKey = typeof FLYWHEEL_CONFIG_KEYS[number];\n\nfunction mb(bytes: number): number {\n return Math.round(bytes / 1024 / 1024);\n}\n\nasync function resolveFlywheelRoleConfig(): Promise<ResolvedFlywheelRoleConfig> {\n const { config } = loadConfigSync();\n const flywheel = config.roles?.flywheel;\n const model = resolveModel('flywheel', undefined, config);\n // PAN-1984/PAN-1865: derive the harness from the model provider; never pin\n // flywheel to claude-code or a hardcoded fallback.\n const harness = await resolveHarness({ model });\n return {\n harness,\n model,\n effort: flywheel?.effort ?? 'high',\n minAgents: flywheel?.minAgents ?? 20,\n maxAgents: flywheel?.maxAgents ?? 30,\n scope: flywheel?.scope ?? 'pan-only',\n autoPickupBacklog: isFlywheelAutoPickupBacklog(),\n requireUatBeforeMerge: isFlywheelRequireUatBeforeMerge(),\n };\n}\n\nasync function createInitialFlywheelStatus(\n runId: string,\n startedAt: string,\n cwd: string,\n agentModel: string | undefined,\n agentHarness: RuntimeName | undefined,\n roleConfig: ResolvedFlywheelRoleConfig,\n): Promise<FlywheelStatus> {\n const ramTotalMb = mb(totalmem());\n return {\n runId,\n startedAt,\n elapsedMs: 0,\n orchestrator: {\n harness: agentHarness ?? roleConfig.harness,\n model: agentModel ?? roleConfig.model,\n effort: roleConfig.effort,\n ctxPercent: 0,\n },\n headline: {\n bugsFixed: 0,\n swarmItemsMerged: 0,\n swarmItemsTotal: 0,\n prsMerged: 0,\n awaitingUat: 0,\n },\n activePipeline: [],\n substrateBugs: [],\n agents: [{\n id: FLYWHEEL_ORCHESTRATOR_AGENT_ID,\n label: 'flywheel-orchestrator',\n status: 'running',\n role: 'flywheel',\n model: agentModel,\n }],\n parked: [],\n suggestions: [],\n system: {\n mainHead: await gitOutput('git rev-parse --short HEAD', cwd).catch(() => 'unknown'),\n ramUsedMb: Math.max(0, ramTotalMb - mb(freemem())),\n ramTotalMb,\n swapUsedMb: 0,\n swapTotalMb: 0,\n agentsActive: 1,\n agentsCap: roleConfig.maxAgents,\n },\n openQuestions: [],\n ticks: 0,\n lastTickAt: startedAt,\n };\n}\n\nexport async function postFlywheelStatus(status: FlywheelStatus, fetchImpl: typeof fetch = fetch): Promise<void> {\n const internalToken = ensureInternalTokenSync();\n const res = await fetchImpl(`${dashboardBaseUrl()}/api/flywheel/status`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n [INTERNAL_TOKEN_HEADER]: internalToken,\n },\n body: JSON.stringify(status),\n });\n\n if (!res.ok) {\n const body = await res.text().catch(() => '');\n throw new Error(`Dashboard rejected FlywheelStatus (${res.status})${body ? `: ${body}` : ''}`);\n }\n}\n\nexport async function emitStatusCommand(options: EmitStatusOptions): Promise<void> {\n try {\n const raw = await readFlywheelStatusJson(options.file);\n const payload = parseFlywheelStatusJson(raw);\n const status = validateFlywheelStatusPayload(payload);\n await postFlywheelStatus(status);\n console.log(`Flywheel status emitted for ${status.runId}`);\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n\nexport async function startFlywheelRun(\n options: StartOptions = {},\n deps: StartFlywheelRunDeps = {},\n): Promise<StartFlywheelRunResult> {\n const cwd = options.cwd ?? await (deps.resolvePrimaryRoot ?? resolvePrimaryWorktreeRoot)(process.cwd());\n const brief = await (deps.requireBrief ?? requireFlywheelBrief)(cwd, options.brief);\n const orderContext = options.orders ? await resolveFlywheelOrderStart(cwd, options.orders, deps) : null;\n const book = orderContext?.book ?? null;\n const overlay = await resolveFlywheelOrderBriefOverlay(cwd, book, deps);\n const runId = await (deps.nextRunId ?? nextFlywheelRunId)();\n const startedAt = new Date().toISOString();\n const launchMetadata = {\n version: 1 as const,\n runId,\n workspace: cwd,\n briefPath: brief.absolutePath,\n briefDisplayPath: brief.displayPath,\n ...(overlay.briefOverlayPath ? { briefOverlayPath: overlay.briefOverlayPath } : {}),\n ...(book ? { orders: { bookId: book.id } } : {}),\n };\n await (deps.writeLaunchMetadata ?? writeFlywheelLaunchMetadata)(launchMetadata);\n const roleConfig = await (deps.resolveRoleConfig ?? resolveFlywheelRoleConfig)();\n const agent = await (deps.spawn ?? spawnFlywheel)({\n runId,\n briefPath: brief.absolutePath,\n ...overlay,\n workspace: cwd,\n model: roleConfig.model,\n harness: roleConfig.harness,\n effort: roleConfig.effort,\n minAgents: roleConfig.minAgents,\n maxAgents: roleConfig.maxAgents,\n scope: roleConfig.scope,\n autoPickupBacklog: roleConfig.autoPickupBacklog,\n requireUatBeforeMerge: roleConfig.requireUatBeforeMerge,\n });\n try {\n if (orderContext) await setFlywheelOrderStatus(orderContext, 'running', runId, deps);\n await (deps.writeStatus ?? writeLatestFlywheelStatus)(await createInitialFlywheelStatus(\n runId,\n startedAt,\n cwd,\n agent.model,\n agent.harness,\n roleConfig,\n ));\n return { runId, briefDisplayPath: brief.displayPath, agentModel: agent.model };\n } catch (error) {\n return compensateFailedFlywheelStart(orderContext, runId, error, deps);\n }\n}\n\nexport async function flywheelStartCommand(options: StartOptions = {}): Promise<void> {\n try {\n const result = await startFlywheelRun(options);\n\n console.log(`Flywheel started: ${result.runId}`);\n console.log(`Brief: ${result.briefDisplayPath}`);\n console.log(`Run URL: ${dashboardBaseUrl()}/flywheel`);\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n\nfunction formatElapsed(ms: number): string {\n const totalSeconds = Math.max(0, Math.floor(ms / 1000));\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n if (hours > 0) return `${hours}h ${minutes}m`;\n if (minutes > 0) return `${minutes}m ${seconds}s`;\n return `${seconds}s`;\n}\n\nasync function loadActiveFlywheelStatus(): Promise<FlywheelStatus | null> {\n // resolveLiveFlywheelRunId self-heals the SQLite gate when the prior run\n // has ended or its on-disk state is gone (PAN-1245). Status and start now\n // consult the same source of truth.\n const activeRunId = await resolveLiveFlywheelRunId();\n if (!activeRunId) return null;\n const detail = await getFlywheelRunDetail(activeRunId);\n return detail?.status === 'running' ? detail.latest : null;\n}\n\nexport function formatFlywheelStatus(status: FlywheelStatus): string {\n return [\n `Run: ${status.runId}`,\n `Elapsed: ${formatElapsed(status.elapsedMs)}`,\n `Bugs fixed: ${status.headline.bugsFixed}`,\n `SWARM items: ${status.headline.swarmItemsMerged}/${status.headline.swarmItemsTotal}`,\n `PRs merged: ${status.headline.prsMerged}`,\n `Awaiting UAT: ${status.headline.awaitingUat}`,\n `Active agents: ${status.system.agentsActive}/${status.system.agentsCap}`,\n `RAM: ${status.system.ramUsedMb} MiB used / ${status.system.ramTotalMb} MiB total`,\n `Main HEAD: ${status.system.mainHead.slice(0, 7)}`,\n `Last tick: ${status.lastTickAt}`,\n ].join('\\n');\n}\n\nasync function formatMergeQueueForCli(cwd: string, json?: boolean): Promise<string> {\n const q = await Effect.runPromise(computeMergeQueueFromCandidates(await listEligibleCandidatesByProject(cwd), cwd).pipe(Effect.provide(nodeServicesLayer)));\n return json ? JSON.stringify({ mergeQueue: q }, null, 2) : `Merge queue:\\n${q.map(i => ` ${i.pr ? `#${i.pr}` : 'no-pr'} ${i.issueId} ${i.title}`).join('\\n') || ' (empty)'}`;\n}\n\nexport async function flywheelStatusCommand(options: StatusOptions): Promise<void> {\n try {\n const status = await loadActiveFlywheelStatus();\n if (!status) {\n console.log(await formatMergeQueueForCli(process.cwd(), options.json));\n return;\n }\n const mergeBackend = await loadMergeBackendStatusForCli();\n console.log(options.json\n ? JSON.stringify({ ...status, mergeBackend }, null, 2)\n : `${formatFlywheelStatus(status)}\\n${formatMergeBackendStatus(mergeBackend)}`);\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n\nconst STATS_CRITERION_KEYS = [\n 'c1_bugRate',\n 'c2_p0Bugs',\n 'c3_passRate',\n 'c4_mttr',\n 'c5_intervention',\n 'c6_timeConsistency',\n 'c7_flake',\n] as const satisfies readonly (keyof FlywheelStatsCriteria)[];\n\nconst STATUS_GLYPH: Record<FlywheelStatsCriterionStatus, string> = {\n green: '● green',\n yellow: '● yellow',\n red: '● red',\n insufficient_data: '○ insufficient_data',\n};\n\nconst STATUS_COLOR: Record<FlywheelStatsCriterionStatus, string> = {\n green: '\\x1b[32m',\n yellow: '\\x1b[33m',\n red: '\\x1b[31m',\n insufficient_data: '\\x1b[90m',\n};\n\nconst TREND_LABEL: Record<FlywheelStatsTrend, string> = {\n up: '↗ up',\n down: '↘ down',\n flat: '→ flat',\n};\n\nexport async function fetchFlywheelStats(window: string, fetchImpl: typeof fetch = fetch): Promise<FlywheelStatsPayload> {\n const internalToken = ensureInternalTokenSync();\n const url = new URL(`${dashboardBaseUrl()}/api/flywheel/stats`);\n url.searchParams.set('window', window);\n const res = await fetchImpl(url.toString(), {\n headers: {\n [INTERNAL_TOKEN_HEADER]: internalToken,\n },\n });\n\n if (!res.ok) {\n const body = await res.text().catch(() => '');\n throw new Error(`Dashboard rejected Flywheel stats request (${res.status})${body ? `: ${body}` : ''}`);\n }\n\n return decodeFlywheelStats(await res.json());\n}\n\nfunction formatScalar(value: number): string {\n if (!Number.isFinite(value)) return '—';\n if (value > 0 && Math.abs(value) < 1) return `${(value * 100).toFixed(1)}%`;\n return Number.isInteger(value) ? String(value) : value.toFixed(2);\n}\n\nfunction formatDuration(ms: number): string {\n const abs = Math.abs(ms);\n if (abs >= 24 * 60 * 60 * 1000) return `${(ms / (24 * 60 * 60 * 1000)).toFixed(1)}d`;\n if (abs >= 60 * 60 * 1000) return `${(ms / (60 * 60 * 1000)).toFixed(1)}h`;\n if (abs >= 60 * 1000) return `${(ms / (60 * 1000)).toFixed(1)}m`;\n return `${Math.round(ms)}ms`;\n}\n\nfunction formatObjectValue(value: Record<string, unknown>): string {\n return Object.entries(value)\n .map(([key, entry]) => {\n if (typeof entry === 'number' && key.toLowerCase().endsWith('ms')) return `${key}: ${formatDuration(entry)}`;\n if (typeof entry === 'number') return `${key}: ${formatScalar(entry)}`;\n if (typeof entry === 'string' || typeof entry === 'boolean' || entry === null) return `${key}: ${String(entry)}`;\n return `${key}: ${JSON.stringify(entry)}`;\n })\n .join(', ');\n}\n\nfunction formatCriterionValue(value: FlywheelStatsCriterion['value']): string {\n return typeof value === 'number' ? formatScalar(value) : formatObjectValue(value as Record<string, unknown>);\n}\n\nfunction colorStatus(status: FlywheelStatsCriterionStatus, color: boolean): string {\n if (!color) return STATUS_GLYPH[status];\n const [glyph, label] = STATUS_GLYPH[status].split(' ');\n return `${STATUS_COLOR[status]}${glyph}\\x1b[0m ${label}`;\n}\n\nfunction tableStatsCell(value: string | number | undefined): string {\n return String(value ?? '—').replace(/\\|/g, '\\\\|').replace(/\\n/g, ' ');\n}\n\nexport function formatFlywheelStats(stats: FlywheelStatsPayload, options: FormatStatsOptions = {}): string {\n const color = options.color ?? process.stdout.isTTY === true;\n const rows = STATS_CRITERION_KEYS.map((key) => {\n const criterion = stats.criteria[key];\n return `| ${tableStatsCell(criterion.label)} | ${tableStatsCell(formatCriterionValue(criterion.value))} | ${tableStatsCell(formatCriterionValue(criterion.target))} | ${tableStatsCell(colorStatus(criterion.status, color))} | ${tableStatsCell(criterion.trend ? TREND_LABEL[criterion.trend] : '—')} | ${criterion.sampleSize} |`;\n });\n return [\n `Flywheel stats (${stats.window})`,\n `Generated: ${stats.generatedAt}`,\n '',\n '| Criterion | Value | Target | Status | Trend | Sample |',\n '|---|---:|---:|---|---|---:|',\n ...rows,\n ].join('\\n');\n}\n\nexport async function flywheelStatsCommand(options: StatsOptions = {}): Promise<void> {\n try {\n const stats = await fetchFlywheelStats(options.window ?? DEFAULT_STATS_WINDOW);\n console.log(options.json ? JSON.stringify(stats, null, 2) : formatFlywheelStats(stats));\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n\nfunction runNumberFromRunId(runId: string): number {\n const match = /^RUN-(\\d+)$/.exec(runId);\n if (!match) throw new Error(`Invalid Flywheel run id for report: ${runId}`);\n return Number(match[1]);\n}\n\nfunction reportDate(status: FlywheelStatus): string {\n return status.lastTickAt.slice(0, 10);\n}\n\nfunction tableCell(value: string | number | undefined): string {\n return String(value ?? '').replace(/\\|/g, '\\\\|').replace(/\\n/g, ' ');\n}\n\nexport function formatFlywheelStateReport(status: FlywheelStatus, mergeQueue: MergeQueueItem[] = []): string {\n const runNumber = runNumberFromRunId(status.runId);\n const suggestionRows = status.suggestions.length > 0\n ? status.suggestions.map((suggestion) => `| ${tableCell(suggestion.priority)} | ${tableCell(suggestion.action)} | ${tableCell(suggestion.issueId)} | ${tableCell(suggestion.rationale)} |`).join('\\n')\n : 'No suggestions emitted this run.';\n const activePipelineRows = status.activePipeline.length > 0\n ? status.activePipeline.map((item) => `| ${tableCell(item.issueId)} | ${tableCell(item.verb)} | ${tableCell(item.status)} | ${tableCell(item.title)} | ${tableCell(item.progressPercent)} | ${tableCell(item.pr)} |`).join('\\n')\n : '| _None_ | | | | | |';\n const substrateRows = status.substrateBugs.length > 0\n ? status.substrateBugs.map((bug) => `| ${tableCell(bug.issueId)} | ${tableCell(bug.status)} | ${tableCell(bug.title)} | ${tableCell(bug.commitSha?.slice(0, 10))} |`).join('\\n')\n : '| _None_ | | | |';\n const patternLines = status.parked.length > 0\n ? status.parked.map((item) => `- **${item.issueId}** (${item.reason}) — ${item.title}`).join('\\n')\n : '- None recorded this run.';\n const questionLines = status.openQuestions.length > 0\n ? status.openQuestions.map((question) => `- ${question}`).join('\\n')\n : '- None.';\n const mergeQueueSection = mergeQueue.length > 0\n ? `## Merge Queue\n\nMerge ship-ready PRs in this order to avoid rebase conflicts. Branches with overlapping file changes must be merged in sequence; non-overlapping branches can be merged in any order.\n\n| # | Issue | PR | Conflicts With |\n|---|---|---|---|\n${mergeQueue.map((item) => {\n const prCell = item.pr != null ? `#${item.pr}` : '—';\n const conflictsCell = item.conflictsWith.length > 0 ? item.conflictsWith.join(', ') : '—';\n return `| ${item.mergeOrder} | ${item.issueId} | ${prCell} | ${conflictsCell} |`;\n}).join('\\n')}\n\n---\n\n`\n : '';\n\n return `# Flywheel Run ${runNumber} Report — ${reportDate(status)}\n\nPer-run report derived from the last \\`FlywheelStatus\\` snapshot. Durable cumulative memory across runs lives in \\`docs/FLYWHEEL-STATE.md\\`.\n\n**Run window:** ${status.startedAt} → ${status.lastTickAt} (${status.orchestrator.harness}, ${status.orchestrator.model}, ${status.orchestrator.effort})\n\n**Headline result:** ${status.headline.bugsFixed} substrate bugs fixed; ${status.headline.swarmItemsMerged}/${status.headline.swarmItemsTotal} SWARM items merged; ${status.headline.prsMerged} PRs merged; ${status.headline.awaitingUat} awaiting UAT.\n\n**Run counters:** ticks ${status.ticks}; last tick ${status.lastTickAt}; orchestrator context ${status.orchestrator.ctxPercent}%.\n\n---\n\n${mergeQueueSection}## Suggestions\n\n${status.suggestions.length > 0 ? `| Priority | Action | Issue | Rationale |\n|---|---|---|---|\n${suggestionRows}` : suggestionRows}\n\n---\n\n## Active Pipeline\n\n| Issue | Verb | Status | Title | Progress | PR |\n|---|---|---|---|---:|---:|\n${activePipelineRows}\n\n---\n\n## Cycling Alerts\n\n${questionLines}\n\n---\n\n## Infrastructure Gaps\n\n| Issue | Status | Notes | Commit |\n|---|---|---|---|\n${substrateRows}\n\n---\n\n## Pattern Ledger\n\n${patternLines}\n\n---\n\n## Skill Gaps\n\n- Keep \\`pan flywheel report\\` in the closeout path so run summaries stay archived under the run directory.\n`;\n}\n\nasync function isFlywheelStateDirty(cwd: string): Promise<boolean> {\n // Tolerate non-git cwd / missing file: a non-zero git exit means there are\n // no orchestrator-authored changes worth committing here (PAN-1245). The\n // gate clear in flywheelReportCommand must not be blocked by this check.\n try {\n const { stdout } = await execAsync(\n 'git status --porcelain docs/FLYWHEEL-STATE.md',\n { cwd, encoding: 'utf8' },\n );\n return stdout.trim().length > 0;\n } catch {\n return false;\n }\n}\n\nasync function loadReportFlywheelStatus(): Promise<FlywheelStatus | null> {\n const activeRunId = getFlywheelActiveRunId();\n if (activeRunId) {\n const activeDetail = await getFlywheelRunDetail(activeRunId);\n if (activeDetail?.latest) return activeDetail.latest;\n }\n const runs = await listFlywheelRuns();\n const run = runs.find((candidate) => candidate.status === 'running') ?? runs[0];\n if (!run) return null;\n const detail = await getFlywheelRunDetail(run.id);\n return detail?.latest ?? null;\n}\n\nasync function gitOutput(command: string, cwd: string): Promise<string> {\n const { stdout } = await execAsync(command, { cwd, encoding: 'utf8' });\n return stdout.trim();\n}\n\nasync function commitFlywheelStateChanges(cwd: string, runNumber: number): Promise<void> {\n const subject = `docs(flywheel): run ${runNumber}`;\n await execAsync('git add docs/FLYWHEEL-STATE.md', { cwd });\n const headSubject = await gitOutput('git log -1 --format=%s', cwd).catch(() => '');\n const command = headSubject === subject\n ? `git commit --amend -m ${JSON.stringify(subject)}`\n : `git commit -m ${JSON.stringify(subject)}`;\n await execAsync(command, { cwd, encoding: 'utf8' });\n}\n\nfunction readFlywheelGateSnapshot(): FlywheelGateSnapshot {\n return {\n paused: isFlywheelGloballyPaused(),\n activeRunId: getFlywheelActiveRunId(),\n };\n}\n\nfunction formatGateSnapshot(snapshot: FlywheelGateSnapshot): string {\n return `paused=${snapshot.paused ? 'true' : 'false'} active_run_id=${snapshot.activeRunId ?? 'none'}`;\n}\n\nexport async function pauseFlywheelRun(): Promise<{ before: FlywheelGateSnapshot; after: FlywheelGateSnapshot; changed: boolean }> {\n const before = readFlywheelGateSnapshot();\n if (before.paused) return { before, after: before, changed: false };\n await pauseFlywheel();\n return { before, after: readFlywheelGateSnapshot(), changed: true };\n}\n\nexport async function flywheelPauseCommand(): Promise<void> {\n try {\n const result = await pauseFlywheelRun();\n if (!result.changed) {\n console.log(`Flywheel already paused (${formatGateSnapshot(result.before)})`);\n return;\n }\n\n console.log(`Flywheel paused: before ${formatGateSnapshot(result.before)}; after ${formatGateSnapshot(result.after)}`);\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n\nexport async function resumeFlywheelRun(): Promise<{ before: FlywheelGateSnapshot; after: FlywheelGateSnapshot; changed: boolean }> {\n const before = readFlywheelGateSnapshot();\n if (!before.paused && await Effect.runPromise(sessionExists(FLYWHEEL_ORCHESTRATOR_AGENT_ID))) {\n return { before, after: before, changed: false };\n }\n if (!before.activeRunId) throw new Error('No active flywheel run to resume');\n const launch = await readFlywheelLaunchMetadata(before.activeRunId);\n if (!launch) {\n throw new Error(`Flywheel run ${before.activeRunId} is missing launch metadata; cannot resume safely`);\n }\n const brief = await requireFlywheelBrief(launch.workspace, launch.briefPath);\n const roleConfig = await resolveFlywheelRoleConfig();\n await resumeFlywheel({\n resumeCause: 'operator',\n workspace: launch.workspace,\n briefPath: brief.absolutePath,\n model: roleConfig.model,\n harness: roleConfig.harness,\n effort: roleConfig.effort,\n minAgents: roleConfig.minAgents,\n maxAgents: roleConfig.maxAgents,\n scope: roleConfig.scope,\n autoPickupBacklog: roleConfig.autoPickupBacklog,\n requireUatBeforeMerge: roleConfig.requireUatBeforeMerge,\n });\n return { before, after: readFlywheelGateSnapshot(), changed: true };\n}\n\nexport async function flywheelResumeCommand(): Promise<void> {\n try {\n const result = await resumeFlywheelRun();\n if (!result.changed) {\n console.log(`Flywheel already running (${formatGateSnapshot(result.before)})`);\n return;\n }\n\n console.log(`Flywheel resumed: before ${formatGateSnapshot(result.before)}; after ${formatGateSnapshot(result.after)}`);\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n\nfunction clearFlywheelRunGate(runId: string): void {\n if (getFlywheelActiveRunId() === runId) {\n clearFlywheelGate();\n }\n}\n\n// Stop the orchestrator, write the report, commit state changes, and clear the gate.\nexport async function flywheelStopCommand(options: Pick<ReportOptions, 'cwd'> = {}): Promise<void> {\n try {\n const sessionAlive = await Effect.runPromise(sessionExists(FLYWHEEL_ORCHESTRATOR_AGENT_ID));\n if (sessionAlive) {\n await Effect.runPromise(stopAgent(FLYWHEEL_ORCHESTRATOR_AGENT_ID, 'operator'));\n }\n\n const status = await loadReportFlywheelStatus();\n if (!status) {\n console.log('No flywheel run is active and nothing is left to report.');\n return;\n }\n\n // The orchestrator has already been stopped, so force the report path to\n // bypass its alive-session guard and finalize the run. Thread cwd through\n // explicitly — the report path commits docs/FLYWHEEL-STATE.md in its cwd,\n // and an implicit process.cwd() here has committed into the wrong repo\n // when invoked from tests or another checkout (PAN-2658).\n await flywheelReportCommand({ force: true, cwd: options.cwd });\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n\nasync function buildFlywheelStateReport(status: FlywheelStatus, cwd: string): Promise<string> {\n // PAN-1696: Compute the merge queue from the pipeline ready set, not flywheel activePipeline.\n const candidates = await listEligibleCandidatesByProject(cwd);\n const mergeQueue = await Effect.runPromise(computeMergeQueueFromCandidates(candidates, cwd).pipe(Effect.provide(nodeServicesLayer)));\n return formatFlywheelStateReport(status, mergeQueue);\n}\n\nasync function persistFlywheelStateReport(status: FlywheelStatus, cwd: string, report: string): Promise<void> {\n const runNumber = runNumberFromRunId(status.runId);\n await writeFile(join(getFlywheelRunDir(status.runId), 'report.md'), report, 'utf8');\n await compactFlywheelStateFile(cwd);\n const stateChanged = await isFlywheelStateDirty(cwd);\n if (stateChanged) {\n await commitFlywheelStateChanges(cwd, runNumber);\n console.log(`Wrote per-run report and committed FLYWHEEL-STATE.md changes for run ${runNumber}.`);\n } else {\n console.log(`Wrote per-run report for run ${runNumber}. No FLYWHEEL-STATE.md changes to commit.`);\n }\n}\n\nexport async function flywheelReportCommand(options: ReportOptions = {}): Promise<void> {\n try {\n const cwd = options.cwd ?? process.cwd();\n\n // Writing report.md finalizes the run. Refuse while the orchestrator is alive;\n // its own end-of-run call passes --force to bypass this guard.\n if (!options.force && await Effect.runPromise(sessionExists(FLYWHEEL_ORCHESTRATOR_AGENT_ID))) {\n console.error('Refusing to write report — flywheel orchestrator session is still alive.');\n console.error('This command finalizes the run (writes report.md and clears the active-run gate).');\n console.error('Run `pan flywheel pause` (or `pan flywheel abort`) first, or pass --force to override.');\n process.exitCode = 1;\n return;\n }\n\n const status = await loadReportFlywheelStatus();\n if (!status) {\n console.error('no flywheel run to report');\n process.exitCode = 1;\n return;\n }\n\n try {\n await persistFlywheelStateReport(status, cwd, await buildFlywheelStateReport(status, cwd));\n } finally {\n clearFlywheelRunGate(status.runId);\n }\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n\nconst flywheelCompleteCommand = createFlywheelCompleteCommand({ loadStatus: loadReportFlywheelStatus, buildReport: buildFlywheelStateReport, persistReport: persistFlywheelStateReport, clearGate: clearFlywheelRunGate, start: startFlywheelRun });\nfunction getPlatformOpenCommand(): string {\n switch (process.platform) {\n case 'linux': return 'xdg-open';\n case 'darwin': return 'open';\n case 'win32': return 'explorer';\n default: throw new Error(`Opening files is not supported on ${process.platform}`);\n }\n}\n\nfunction openPathDetached(path: string): void {\n const child = spawn(getPlatformOpenCommand(), [path], {\n detached: true,\n stdio: 'ignore',\n });\n child.unref();\n}\n\nasync function resolveReportOpenRunId(runId: string | undefined): Promise<string> {\n if (runId) return runId;\n const runs = await listFlywheelRuns();\n const run = runs.find((candidate) => candidate.status === 'complete') ?? runs[0];\n if (!run) throw new Error('no flywheel run report to open');\n return run.id;\n}\n\nexport async function openFlywheelRunReport(options: ReportOpenOptions = {}): Promise<{ runId: string; path: string }> {\n const runId = await resolveReportOpenRunId(options.runId);\n const detail = await getFlywheelRunDetail(runId);\n if (!detail) throw new Error(`Flywheel run not found: ${runId}`);\n if (!detail.paths.report) throw new Error(`No report exists for ${runId}`);\n await (options.opener ?? openPathDetached)(detail.paths.report);\n return { runId, path: detail.paths.report };\n}\n\nexport async function flywheelReportOpenCommand(options: ReportOpenOptions = {}): Promise<void> {\n try {\n const result = await openFlywheelRunReport(options);\n console.log(`Opened Flywheel report for ${result.runId}: ${result.path}`);\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n\n// Discard the current run without writing a report (PAN-1245).\nexport async function flywheelAbortCommand(): Promise<void> {\n try {\n const candidate = getFlywheelActiveRunId();\n if (!candidate) {\n console.log('No active flywheel run to abort.');\n return;\n }\n await Effect.runPromise(stopAgent(FLYWHEEL_ORCHESTRATOR_AGENT_ID, 'operator'));\n await abortFlywheelRun(candidate);\n console.log(`Aborted flywheel run ${candidate}.`);\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n\nexport function registerFlywheelCommands(program: Command): void {\n const flywheel = program\n .command('flywheel')\n .description('Flywheel orchestrator lifecycle and status helpers');\n registerFlywheelSurfaceCommands(program);\n\n flywheel\n .command('start')\n .description('Start the Flywheel orchestrator')\n .option('--brief <path>', 'Path to the Flywheel brief', DEFAULT_BRIEF_PATH)\n .option('--orders <book-id>', 'Bind the run to a validated order book')\n .action(flywheelStartCommand);\n\n flywheel\n .command('emit-status')\n .description('Validate and publish a FlywheelStatus JSON snapshot to the local dashboard')\n .requiredOption('--file <path>', 'Path to FlywheelStatus JSON, or - to read from stdin')\n .action(emitStatusCommand);\n\n flywheel\n .command('config')\n .description('Get or set Flywheel autonomy configuration')\n .option('--get [key]', 'Print Flywheel config values')\n .option('--set <key=value>', 'Set a Flywheel config boolean')\n .action(flywheelConfigCommand);\n\n flywheel\n .command('status')\n .description('Show the active Flywheel run status')\n .option('--json', 'Emit the raw FlywheelStatus JSON')\n .action(flywheelStatusCommand);\n\n flywheel\n .command('stats')\n .description('Show Flywheel v1.0 readiness stats')\n .option('--window <duration>', 'Stats window duration', DEFAULT_STATS_WINDOW)\n .option('--json', 'Emit the raw FlywheelStats JSON')\n .action(flywheelStatsCommand);\n\n flywheel\n .command('pause')\n .description('Pause the active Flywheel orchestrator run')\n .action(flywheelPauseCommand);\n\n flywheel\n .command('resume')\n .description('Resume the paused Flywheel orchestrator run')\n .action(flywheelResumeCommand);\n\n flywheel\n .command('report')\n .description('Finalize the active Flywheel run: write report.md, compact over-threshold FLYWHEEL-STATE.md history, commit State changes, and clear the active-run gate. Refuses to run while the orchestrator session is alive (pause or abort first).')\n .option('--force', 'Bypass the orchestrator-alive guard. Intended for the orchestrator role\\'s own end-of-run call.')\n .action(flywheelReportCommand);\n\n flywheel.command('complete')\n .description('Complete a drained order-book run, write its report and retrospective, then continue mechanically')\n .option('--force', 'Complete even when the order book still has non-terminal items').action(flywheelCompleteCommand);\n\n flywheel\n .command('stop')\n .description('Stop the Flywheel orchestrator gracefully: kill any live session, write report.md, commit FLYWHEEL-STATE.md changes, and clear the active-run gate')\n .action(flywheelStopCommand);\n\n flywheel\n .command('abort')\n .description('Discard the active Flywheel run without writing a report')\n .action(flywheelAbortCommand);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"flywheel-DpRPLErh.js","names":["setOrderBookStatus","execAsync","nodeServicesLayer"],"sources":["../../src/lib/flywheel-state-retention.ts","../../src/cli/commands/flywheel-merge-backend.ts","../../src/cli/commands/flywheel-orders.ts","../../src/cli/commands/flywheel-complete.ts","../../src/cli/commands/flywheel-surfaces.ts","../../src/cli/commands/flywheel.ts"],"sourcesContent":["import { readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport const FLYWHEEL_STATE_MAX_BYTES = 120 * 1024;\nexport const FLYWHEEL_STATE_MAX_LINES = 1_000;\nexport const FLYWHEEL_STATE_VERBATIM_RUNS = 3;\n\ninterface MarkdownBlock {\n heading: string;\n text: string;\n runNumber: number | null;\n}\n\nexport interface FlywheelStateCompactionResult {\n content: string;\n compactedRunNumbers: number[];\n preservedRunNumbers: number[];\n changed: boolean;\n}\n\nexport interface FlywheelStateFileCompactionResult {\n compacted: boolean;\n compactedRunNumbers: number[];\n preservedRunNumbers: number[];\n}\n\nfunction parseBlocks(content: string): { preamble: string; blocks: MarkdownBlock[] } {\n const matches = [...content.matchAll(/^## .+$/gm)];\n if (matches.length === 0) return { preamble: content, blocks: [] };\n\n const firstIndex = matches[0].index ?? 0;\n return {\n preamble: content.slice(0, firstIndex),\n blocks: matches.map((match, index) => {\n const start = match.index ?? 0;\n const end = matches[index + 1]?.index ?? content.length;\n const runMatch = match[0].match(/^## RUN-(\\d+)\\b/i);\n const isCompactedSummary = /^## RUN-\\d+\\s*…/i.test(match[0]) || /compacted summary/i.test(match[0]);\n return {\n heading: match[0],\n text: content.slice(start, end),\n runNumber: runMatch && !isCompactedSummary ? Number(runMatch[1]) : null,\n };\n }),\n };\n}\n\nfunction normalizeOneLine(value: string): string {\n return value.replace(/\\s+/g, ' ').trim();\n}\n\nfunction truncate(value: string, maxLength: number): string {\n if (value.length <= maxLength) return value;\n return `${value.slice(0, maxLength - 1).trimEnd()}…`;\n}\n\nfunction tickNumber(heading: string): number {\n const match = heading.match(/\\bticks?\\s+(\\d+)/i);\n return match ? Number(match[1]) : -1;\n}\n\nfunction summarizeRun(runNumber: number, blocks: MarkdownBlock[]): string {\n const selected = blocks.reduce((latest, candidate) => (\n tickNumber(candidate.heading) >= tickNumber(latest.heading) ? candidate : latest\n ));\n const dates = blocks.flatMap((block) => block.text.match(/\\b\\d{4}-\\d{2}-\\d{2}\\b/g) ?? []).sort();\n const date = dates.at(-1);\n const dashIndex = selected.heading.indexOf('—');\n const rawSummary = dashIndex >= 0\n ? selected.heading.slice(dashIndex + 1)\n : selected.heading.replace(new RegExp(`^## RUN-${runNumber}\\\\b`, 'i'), '');\n const summary = truncate(normalizeOneLine(rawSummary) || 'run completed', 220);\n const issueIds: string[] = [];\n const seen = new Set<string>();\n for (const text of [selected.text, ...blocks.map((block) => block.text)]) {\n for (const match of text.matchAll(/\\bPAN-\\d+\\b/g)) {\n if (seen.has(match[0])) continue;\n seen.add(match[0]);\n issueIds.push(match[0]);\n if (issueIds.length === 8) break;\n }\n if (issueIds.length === 8) break;\n }\n const issues = issueIds.length > 0 ? `; key issues: ${issueIds.join(', ')}` : '';\n return `- **RUN-${runNumber}${date ? ` (${date})` : ''}** — ${summary}${issues}`;\n}\n\nfunction readExistingSummaries(block: MarkdownBlock | undefined): Map<number, string> {\n const summaries = new Map<number, string>();\n if (!block) return summaries;\n\n const lines = block.text.split('\\n');\n let current: string[] = [];\n const flush = (): void => {\n if (current.length === 0) return;\n const summary = normalizeOneLine(current.join(' '));\n const match = summary.match(/^- \\*\\*RUN-(\\d+)\\b/);\n if (match) summaries.set(Number(match[1]), summary);\n current = [];\n };\n\n for (const line of lines) {\n if (/^- \\*\\*RUN-\\d+\\b/.test(line)) {\n flush();\n current.push(line);\n } else if (current.length > 0) {\n if (line.startsWith(' ') || line.trim() === '') current.push(line);\n else flush();\n }\n }\n flush();\n return summaries;\n}\n\nfunction buildCompactedLog(summaries: Map<number, string>): string {\n const runLines = [...summaries.entries()]\n .sort(([left], [right]) => left - right)\n .map(([, summary]) => summary);\n return [\n '## Compacted run log (older than the latest 3 runs)',\n '',\n 'One terse line per older run. The original tick-by-tick detail remains available in git history',\n '(`git log --follow docs/FLYWHEEL-STATE.md`); compaction never deletes repository history.',\n '',\n ...runLines,\n '',\n '',\n ].join('\\n');\n}\n\nexport function shouldCompactFlywheelState(content: string): boolean {\n return Buffer.byteLength(content, 'utf8') > FLYWHEEL_STATE_MAX_BYTES\n || content.split('\\n').length > FLYWHEEL_STATE_MAX_LINES;\n}\n\nexport function compactFlywheelState(content: string): FlywheelStateCompactionResult {\n if (!shouldCompactFlywheelState(content)) {\n return { content, compactedRunNumbers: [], preservedRunNumbers: [], changed: false };\n }\n\n const { preamble, blocks } = parseBlocks(content);\n const detailedRunNumbers = [...new Set(\n blocks.flatMap((block) => block.runNumber === null ? [] : [block.runNumber]),\n )].sort((left, right) => left - right);\n const preservedRunNumbers = detailedRunNumbers.slice(-FLYWHEEL_STATE_VERBATIM_RUNS);\n const preserved = new Set(preservedRunNumbers);\n const compactedRunNumbers = detailedRunNumbers.filter((runNumber) => !preserved.has(runNumber));\n if (compactedRunNumbers.length === 0) {\n return { content, compactedRunNumbers: [], preservedRunNumbers, changed: false };\n }\n\n const compactionBlocks = blocks.filter((block) => block.heading.startsWith('## Compacted run log'));\n const compactionBlock = compactionBlocks[0];\n const summaries = new Map<number, string>();\n for (const block of compactionBlocks) {\n for (const [runNumber, summary] of readExistingSummaries(block)) summaries.set(runNumber, summary);\n }\n for (const runNumber of compactedRunNumbers) {\n const runBlocks = blocks.filter((block) => block.runNumber === runNumber);\n summaries.set(runNumber, summarizeRun(runNumber, runBlocks));\n }\n for (const runNumber of preservedRunNumbers) summaries.delete(runNumber);\n const compactedLog = buildCompactedLog(summaries);\n\n const output: string[] = [preamble];\n let insertedCompactedLog = false;\n const insertionBlock = blocks.find((block) => block.heading.startsWith('## Recent runs'))\n ?? blocks.find((block) => block.runNumber !== null);\n for (const block of blocks) {\n if (compactionBlocks.includes(block)) {\n if (block === compactionBlock) {\n output.push(compactedLog);\n insertedCompactedLog = true;\n }\n continue;\n }\n if (!insertedCompactedLog && !compactionBlock && block === insertionBlock) {\n output.push(compactedLog);\n insertedCompactedLog = true;\n }\n if (block.runNumber !== null && !preserved.has(block.runNumber)) continue;\n output.push(block.text);\n }\n if (!insertedCompactedLog) output.push(compactedLog);\n\n const compacted = output.join('');\n return {\n content: compacted,\n compactedRunNumbers,\n preservedRunNumbers,\n changed: compacted !== content,\n };\n}\n\nexport async function compactFlywheelStateFile(cwd: string): Promise<FlywheelStateFileCompactionResult> {\n const path = join(cwd, 'docs', 'FLYWHEEL-STATE.md');\n let content: string;\n try {\n content = await readFile(path, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return { compacted: false, compactedRunNumbers: [], preservedRunNumbers: [] };\n }\n throw error;\n }\n\n const result = compactFlywheelState(content);\n if (result.changed) await writeFile(path, result.content, 'utf8');\n return {\n compacted: result.changed,\n compactedRunNumbers: result.compactedRunNumbers,\n preservedRunNumbers: result.preservedRunNumbers,\n };\n}\n","import { getMergeBackendStatus, type MergeBackendStatus } from '../../lib/github-app.js';\n\nexport function formatMergeBackendStatus(status: MergeBackendStatus): string {\n if (status.available) {\n return `Merge backend: ${status.mode} (${status.detail})`;\n }\n return `Merge backend: UNAVAILABLE - ${status.detail}`;\n}\n\nexport async function loadMergeBackendStatusForCli(): Promise<MergeBackendStatus> {\n try {\n return await getMergeBackendStatus();\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n available: false,\n mode: 'none',\n detail: `Unable to determine merge backend: ${message}`,\n };\n }\n}\n","import { readFile } from 'node:fs/promises';\nimport type { OrderBook } from '@overdeck/contracts';\n\nimport { abortSpawnedFlywheel } from '../../lib/cloister/flywheel.js';\nimport { requireFlywheelBrief } from '../../lib/flywheel-start.js';\nimport { ensureOrderIssueStore, getBook } from '../../lib/orders/resolver.js';\nimport { validateBookForStart } from '../../lib/orders/validate.js';\nimport { setStatus as setOrderBookStatus } from '../../lib/orders/writer.js';\nimport { findProjectByPathSync } from '../../lib/projects.js';\nimport { resolveStateReadHomeSync } from '../../lib/state-read-home.js';\n\nexport interface FlywheelOrderStartDeps {\n orderStateRoot?: (cwd: string) => string;\n getOrderBook?: typeof getBook;\n prepareIssueStore?: () => Promise<unknown>;\n validateOrderBook?: typeof validateBookForStart;\n setOrderStatus?: typeof setOrderBookStatus;\n requireBrief?: typeof requireFlywheelBrief;\n readBrief?: (path: string) => Promise<string>;\n cleanupSpawnedRun?: (runId: string) => Promise<void>;\n}\n\nexport interface FlywheelOrderStartContext {\n stateRoot: string;\n book: OrderBook;\n}\n\nfunction orderStateRootFor(cwd: string): string {\n const project = findProjectByPathSync(cwd);\n if (!project) throw new Error(`No configured project contains ${cwd}`);\n return resolveStateReadHomeSync(project).root;\n}\n\nfunction validationError(bookId: string, findings: readonly { code: string; issue: string; message: string }[]): Error {\n return new Error([\n `Order book ${bookId} cannot start:`,\n ...findings.map((finding) => `- [${finding.code}] ${finding.issue}: ${finding.message}`),\n ].join('\\n'));\n}\n\nexport async function resolveFlywheelOrderStart(\n cwd: string,\n bookId: string,\n deps: FlywheelOrderStartDeps = {},\n): Promise<FlywheelOrderStartContext> {\n const stateRoot = (deps.orderStateRoot ?? orderStateRootFor)(cwd);\n const book = (deps.getOrderBook ?? getBook)(stateRoot, bookId);\n if (!book) throw new Error(`Order book not found: ${bookId}`);\n if (book.status !== 'ready') {\n throw new Error(`Order book ${book.id} must be ready before start (current status: ${book.status})`);\n }\n await (deps.prepareIssueStore ?? ensureOrderIssueStore)();\n const validation = (deps.validateOrderBook ?? validateBookForStart)(stateRoot, book);\n if (validation.blocks.length > 0) throw validationError(book.id, validation.blocks);\n return { stateRoot, book };\n}\n\nexport async function resolveFlywheelOrderBriefOverlay(\n cwd: string,\n book: OrderBook | null,\n deps: FlywheelOrderStartDeps = {},\n): Promise<{ briefOverlayPath?: string; briefOverlayContent?: string }> {\n const path = book?.settings.briefOverlay?.trim();\n if (!path) return {};\n const brief = await (deps.requireBrief ?? requireFlywheelBrief)(cwd, path);\n return {\n briefOverlayPath: brief.displayPath,\n briefOverlayContent: await (deps.readBrief ?? ((absolutePath) => readFile(absolutePath, 'utf8')))(brief.absolutePath),\n };\n}\n\nexport async function compensateFailedFlywheelStart(\n context: FlywheelOrderStartContext | null,\n runId: string,\n cause: unknown,\n deps: FlywheelOrderStartDeps = {},\n): Promise<never> {\n const failures = [cause];\n try {\n await (deps.cleanupSpawnedRun ?? abortSpawnedFlywheel)(runId);\n } catch (error) {\n failures.push(error);\n }\n if (context) {\n try {\n await setFlywheelOrderStatus(context, 'ready', null, deps);\n } catch (error) {\n failures.push(error);\n }\n }\n if (failures.length > 1) {\n throw new AggregateError(failures, `Flywheel run ${runId} failed to start and compensation was incomplete`);\n }\n throw cause;\n}\n\nexport async function setFlywheelOrderStatus(\n context: FlywheelOrderStartContext,\n status: 'ready' | 'running',\n runId: string | null | undefined,\n deps: FlywheelOrderStartDeps = {},\n): Promise<void> {\n await (deps.setOrderStatus ?? setOrderBookStatus)(\n context.stateRoot,\n context.book.id,\n status,\n runId === undefined ? {} : { runId },\n );\n}\n","import { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { FlywheelStatus, OrderBook } from '@overdeck/contracts';\n\nimport {\n getFlywheelRunDir,\n readFlywheelLaunchMetadata,\n} from '../../dashboard/server/services/flywheel-run-state.js';\nimport { isFlywheelAutoPickupBacklog } from '../../lib/overdeck/control-settings.js';\nimport { computeBookProgress, firstReadyBookInQueue, getBook } from '../../lib/orders/resolver.js';\nimport type { OrderBookProgress } from '../../lib/orders/types.js';\nimport { advanceQueue } from '../../lib/orders/writer.js';\nimport { findProjectByPathSync, listProjectsSync, resolveProjectPath } from '../../lib/projects.js';\nimport { resolveStateReadHomeSync } from '../../lib/state-read-home.js';\n\nexport interface CompleteFlywheelOptions {\n cwd?: string;\n force?: boolean;\n}\n\ninterface StartContinuationOptions {\n cwd: string;\n brief?: string;\n orders?: string;\n}\n\ninterface StartContinuationResult {\n runId: string;\n}\n\nexport interface CompleteFlywheelDeps {\n loadStatus: () => Promise<FlywheelStatus | null>;\n buildReport: (status: FlywheelStatus, cwd: string) => Promise<string>;\n persistReport: (status: FlywheelStatus, cwd: string, report: string) => Promise<void>;\n clearGate: (runId: string) => void;\n start: (options: StartContinuationOptions) => Promise<StartContinuationResult>;\n readLaunch?: typeof readFlywheelLaunchMetadata;\n stateRoot?: (workspace: string) => string;\n getOrderBook?: typeof getBook;\n computeProgress?: (book: OrderBook) => OrderBookProgress;\n advance?: typeof advanceQueue;\n autoPickupBacklog?: () => boolean;\n readRetro?: (path: string) => Promise<string | null>;\n listProjects?: typeof listProjectsSync;\n firstReady?: typeof firstReadyBookInQueue;\n}\n\nexport interface CompleteFlywheelResult {\n runId: string;\n reportPath: string;\n retrospectiveIncluded: boolean;\n continuation: 'next-book' | 'backlog' | 'needs-you';\n nextRunId?: string;\n nextBookId?: string;\n nextProjectKey?: string;\n}\n\nfunction stateRootFor(workspace: string): string {\n const project = findProjectByPathSync(workspace);\n if (!project) throw new Error(`No configured project contains ${workspace}`);\n return resolveStateReadHomeSync(project).root;\n}\n\nasync function readOptionalFile(path: string): Promise<string | null> {\n try {\n return await readFile(path, 'utf8');\n } catch (error) {\n const code = typeof error === 'object' && error !== null && 'code' in error ? error.code : undefined;\n if (code === 'ENOENT') return null;\n throw error;\n }\n}\n\nexport function appendFlywheelRetrospective(report: string, retro: string | null): {\n report: string;\n retrospectiveIncluded: boolean;\n} {\n const finding = retro?.trim();\n if (!finding) {\n return {\n report: `${report.trimEnd()}\\n\\nRetrospective: no findings recognized.\\n`,\n retrospectiveIncluded: false,\n };\n }\n return {\n report: `${report.trimEnd()}\\n\\n---\\n\\n## Retrospective\\n\\n${finding}\\n`,\n retrospectiveIncluded: true,\n };\n}\n\nfunction nonTerminalIssues(progress: OrderBookProgress): string[] {\n return progress.items.filter((item) => !item.terminal).map((item) => item.issue);\n}\n\n/**\n * A ready book is an explicit operator release, so continuation looks past the\n * completing project: every tracked project's ready queue is scanned in registry\n * order and the first hit wins. Run scope governs backlog pickup, not this.\n */\nfunction findCrossProjectContinuation(\n stateRoot: string,\n deps: CompleteFlywheelDeps,\n): { projectKey: string; cwd: string; book: OrderBook } | null {\n const firstReady = deps.firstReady ?? firstReadyBookInQueue;\n for (const { key, config } of (deps.listProjects ?? listProjectsSync)()) {\n const projectStateRoot = resolveStateReadHomeSync(config, key).root;\n if (projectStateRoot === stateRoot) continue;\n const book = firstReady(projectStateRoot);\n // resolveProjectPath returns the project's primary root, which the Flywheel\n // start path requires — it uses options.cwd verbatim.\n if (book) return { projectKey: key, cwd: resolveProjectPath(config), book };\n }\n return null;\n}\n\nexport async function completeFlywheelRun(\n options: CompleteFlywheelOptions,\n deps: CompleteFlywheelDeps,\n): Promise<CompleteFlywheelResult> {\n const cwd = options.cwd ?? process.cwd();\n const status = await deps.loadStatus();\n if (!status) throw new Error('no flywheel run to complete');\n\n const launch = await (deps.readLaunch ?? readFlywheelLaunchMetadata)(status.runId);\n if (!launch) throw new Error(`Flywheel run ${status.runId} is missing launch metadata`);\n const bookId = launch.orders?.bookId;\n if (!bookId) throw new Error(`Flywheel run ${status.runId} is not bound to an order book; use pan flywheel report`);\n\n const stateRoot = (deps.stateRoot ?? stateRootFor)(launch.workspace);\n const book = (deps.getOrderBook ?? getBook)(stateRoot, bookId);\n if (!book) throw new Error(`Flywheel run ${status.runId} references missing order book ${bookId}`);\n const progress = (deps.computeProgress ?? computeBookProgress)(book);\n const remaining = nonTerminalIssues(progress);\n if (!progress.drained && !options.force) {\n throw new Error(`Order book ${book.id} is not drained; non-terminal items: ${remaining.join(', ')}. Pass --force to complete anyway.`);\n }\n\n const runDir = getFlywheelRunDir(status.runId);\n const retro = await (deps.readRetro ?? readOptionalFile)(join(runDir, 'retro.md'));\n const report = appendFlywheelRetrospective(await deps.buildReport(status, cwd), retro);\n try {\n await deps.persistReport(status, cwd, report.report);\n } finally {\n deps.clearGate(status.runId);\n }\n\n const nextBook = await (deps.advance ?? advanceQueue)(stateRoot, book.id);\n const startOptions = { cwd: launch.workspace, brief: launch.briefPath };\n if (nextBook) {\n const next = await deps.start({ ...startOptions, orders: nextBook.id });\n return {\n runId: status.runId,\n reportPath: join(runDir, 'report.md'),\n retrospectiveIncluded: report.retrospectiveIncluded,\n continuation: 'next-book',\n nextRunId: next.runId,\n nextBookId: nextBook.id,\n };\n }\n const crossProject = findCrossProjectContinuation(stateRoot, deps);\n if (crossProject) {\n // No brief: requireFlywheelBrief resolves the target project's own default\n // docs/flywheel-brief.md, so the completing project's brief never leaks over.\n const next = await deps.start({ cwd: crossProject.cwd, orders: crossProject.book.id });\n return {\n runId: status.runId,\n reportPath: join(runDir, 'report.md'),\n retrospectiveIncluded: report.retrospectiveIncluded,\n continuation: 'next-book',\n nextRunId: next.runId,\n nextBookId: crossProject.book.id,\n nextProjectKey: crossProject.projectKey,\n };\n }\n if ((deps.autoPickupBacklog ?? isFlywheelAutoPickupBacklog)()) {\n const next = await deps.start(startOptions);\n return {\n runId: status.runId,\n reportPath: join(runDir, 'report.md'),\n retrospectiveIncluded: report.retrospectiveIncluded,\n continuation: 'backlog',\n nextRunId: next.runId,\n };\n }\n return {\n runId: status.runId,\n reportPath: join(runDir, 'report.md'),\n retrospectiveIncluded: report.retrospectiveIncluded,\n continuation: 'needs-you',\n };\n}\n\nexport function createFlywheelCompleteCommand(deps: CompleteFlywheelDeps) {\n return async (options: CompleteFlywheelOptions = {}): Promise<void> => {\n try {\n const result = await completeFlywheelRun(options, deps);\n if (result.continuation === 'next-book') {\n const project = result.nextProjectKey ? ` (project: ${result.nextProjectKey})` : '';\n console.log(`Completed ${result.runId}; started ${result.nextRunId} with order book ${result.nextBookId}${project}.`);\n } else if (result.continuation === 'backlog') {\n console.log(`Completed ${result.runId}; started ${result.nextRunId} in backlog mode.`);\n } else {\n console.log('needs-you: pipeline idle — no order book queued and auto-pickup is off');\n }\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n };\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { exec, execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport type { Command } from 'commander';\nimport { parseSequenceMd } from '../../lib/backlog/sequence-io.js';\nimport { buildClassifyLookups } from '../../lib/backlog/lookups.js';\nimport {\n classifyIssue,\n computeWaves,\n computeCohort,\n computeStats,\n selectNeedsPlanning,\n} from '../../lib/backlog/pickup.js';\nimport { isFlywheelAutoPickupBacklog } from '../../lib/overdeck/control-settings.js';\nimport { getMergeBlockersPayload } from '../../lib/cloister/merge-blockers.js';\nimport { activeOrderBookIssues } from '../../lib/cloister/flywheel.js';\nimport { listSubstrateBugWeights, type WeightedSubstrateBug } from '../../lib/overdeck/substrate-bug-weights-service.js';\n\n/**\n * Sandbox-safe Flywheel data surfaces. These read state DIRECTLY (sequence.md + SQLite) and\n * emit no HTTP, so they work inside a network-isolated harness sandbox (e.g. codex's bwrap)\n * where the orchestrator cannot `curl http://127.0.0.1:3011/api/...`. They share the pickup\n * model (PAN-2006) and the merge-blocker model (PAN-1620) with the dashboard routes, so the\n * CLI and the dashboard can never disagree.\n */\n\nconst execAsync = promisify(exec);\nconst execFileAsync = promisify(execFile);\n\nfunction parseGitHubRemoteUrl(remoteUrl: string): { owner: string; repo: string } | null {\n const trimmed = remoteUrl.trim();\n const match = trimmed.match(/github\\.com[:/]([^/]+)\\/([^/\\s]+?)(?:\\.git)?$/i);\n if (!match) return null;\n return { owner: match[1]!, repo: match[2]! };\n}\n\nasync function resolveGitHubOwnerRepo(): Promise<{ owner: string; repo: string }> {\n const { stdout } = await execAsync('git remote get-url origin', { encoding: 'utf8' });\n const repo = parseGitHubRemoteUrl(stdout);\n if (!repo) throw new Error('origin is not a GitHub repository');\n return repo;\n}\n\n/** Labels for all open issues, keyed by bare number. This must fail rather than make a\n * label-dependent forecast indistinguishable from an empty backlog. */\nexport async function fetchOpenIssueLabels(): Promise<Map<string, string[]>> {\n const repo = await resolveGitHubOwnerRepo();\n const { stdout } = await execFileAsync(\n 'gh',\n ['api', '--paginate', '--slurp', `repos/${repo.owner}/${repo.repo}/issues?state=open&per_page=100`],\n { encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 },\n );\n const pages = JSON.parse(stdout || '[]') as Array<Array<{\n number: number;\n pull_request?: unknown;\n labels?: Array<{ name?: string | null } | string>;\n }>>;\n const byNumber = new Map<string, string[]>();\n for (const issue of pages.flat()) {\n if (issue.pull_request) continue;\n byNumber.set(String(issue.number), (issue.labels ?? [])\n .map((label) => typeof label === 'string' ? label : label.name)\n .filter((name): name is string => typeof name === 'string' && name.length > 0));\n }\n return byNumber;\n}\n\n/** `pan backlog forecast` — the pickup forecast (pickable waves, needs-planning, cohort, stats). */\nexport async function backlogForecastCommand(opts: { n?: string } = {}): Promise<void> {\n const projectRoot = process.cwd();\n const n = Math.max(1, Math.min(20, Number.parseInt(opts.n ?? '5', 10) || 5));\n const seqPath = join(projectRoot, '.pan', 'backlog', 'sequence.md');\n if (!existsSync(seqPath)) {\n console.log(JSON.stringify({ n, stats: null, inFlight: [], needsPlanning: [], waves: [], cohort: [] }, null, 2));\n return;\n }\n const parsed = parseSequenceMd(readFileSync(seqPath, 'utf-8'));\n if (!parsed.ok) {\n console.error(`parse error: ${parsed.error}`);\n process.exitCode = 1;\n return;\n }\n const nodes = parsed.doc.nodes;\n const labelsByNumber = await fetchOpenIssueLabels();\n const lk = buildClassifyLookups(projectRoot, {\n labels: (id) => labelsByNumber.get(id.replace(/^[A-Za-z]+-/, '')) ?? [],\n });\n const autoPickupBacklog = isFlywheelAutoPickupBacklog();\n const inFlight = nodes\n .map((x) => ({ issue: x.issue, rank: x.rank, state: classifyIssue(x, lk) }))\n .filter((x) => x.state.inPipeline)\n .sort((a, b) => a.rank - b.rank)\n .map((x) => x.issue);\n const needsPlanning = selectNeedsPlanning(nodes, lk, { cap: n * 2 }).map((x) => x.issue);\n const orderBookIssues = await activeOrderBookIssues(projectRoot);\n const waves = computeWaves(nodes, lk, n, autoPickupBacklog, orderBookIssues).map((w) => w.map((x) => x.issue));\n const cohort = computeCohort(nodes, lk, n, autoPickupBacklog, orderBookIssues);\n const stats = computeStats(nodes, lk, autoPickupBacklog, orderBookIssues);\n console.log(JSON.stringify({ n, autoPickupBacklog, stats, inFlight, needsPlanning, waves, cohort }, null, 2));\n}\n\n/** `pan flywheel merge-blockers` — PRs that passed review but cannot merge (GitHub-native reasons). */\nexport function flywheelMergeBlockersCommand(opts: { json?: boolean } = {}): void {\n const blockers = getMergeBlockersPayload();\n if (opts.json) {\n console.log(JSON.stringify(blockers, null, 2));\n return;\n }\n if (blockers.length === 0) {\n console.log('No merge-blocked PRs.');\n return;\n }\n for (const b of blockers) {\n const reasonTypes = b.reasons.map((r) => r.type).join(', ');\n console.log(`${b.issueId} [${reasonTypes}]${b.prUrl ? ` ${b.prUrl}` : ''}`);\n for (const r of b.reasons) console.log(` - ${r.type}: ${r.summary}`);\n }\n}\n\n/** `pan flywheel weights` — substrate bugs ranked by affected v1.0 criterion weight. */\nexport async function flywheelWeightsCommand(opts: { issue?: string; window?: string; json?: boolean } = {}): Promise<void> {\n const rows = await listSubstrateBugWeights(opts.window ?? '30d');\n const targetIssue = opts.issue?.toUpperCase();\n const filtered = targetIssue ? rows.filter((r) => r.issueId === targetIssue) : rows;\n\n if (opts.json) {\n console.log(JSON.stringify(filtered, null, 2));\n return;\n }\n\n if (filtered.length === 0) {\n console.log('No weighted substrate bugs in window.');\n return;\n }\n\n const issueWidth = Math.max(...filtered.map((r) => r.issueId.length));\n const severityWidth = Math.max(...filtered.map((r) => r.severity.length));\n const weightWidth = Math.max(...filtered.map((r) => String(r.weight).length));\n\n for (const r of filtered) {\n console.log(\n `${r.issueId.padEnd(issueWidth)} ${r.severity.padEnd(severityWidth)} ${String(r.weight).padStart(weightWidth)} ${r.weightReason}`,\n );\n }\n}\n\n/**\n * Attaches the sandbox-safe surface subcommands onto the already-registered `flywheel` and\n * `backlog` commands. Kept here (not in flywheel.ts) so flywheel.ts stays under the file-size\n * ceiling. Call AFTER registerFlywheelCommands(program) and the backlog command are set up.\n */\nexport function registerFlywheelSurfaceCommands(program: Command): void {\n const flywheel = program.commands.find((c) => c.name() === 'flywheel');\n flywheel\n ?.command('merge-blockers')\n .description('PRs that passed review but cannot merge (GitHub-native reasons) — reads SQLite directly, no HTTP (sandbox-safe)')\n .option('--json', 'Output JSON')\n .action((o: { json?: boolean }) => flywheelMergeBlockersCommand(o));\n\n flywheel\n ?.command('weights')\n .description('Weighted substrate bugs by affected v1.0 criterion — reads SQLite directly, no HTTP (sandbox-safe)')\n .option('--issue <id>', 'Filter to a single issue')\n .option('--window <dur>', 'Stats window like 30d, 7d (default 30d)')\n .option('--json', 'Output JSON')\n .action((o: { issue?: string; window?: string; json?: boolean }) => flywheelWeightsCommand(o));\n\n const backlog = program.commands.find((c) => c.name() === 'backlog');\n backlog\n ?.command('forecast')\n .description('Pickup forecast (waves / needs-planning / cohort / stats) from sequence.md + SQLite — no HTTP (sandbox-safe)')\n .option('-n, --n <n>', 'Wave size (default 5)')\n .action((o: { n?: string }) => backlogForecastCommand(o));\n}\n","import { exec, spawn } from 'node:child_process';\nimport { readFile, writeFile } from 'node:fs/promises';\nimport { freemem, totalmem } from 'node:os';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\nimport { Effect, Schema } from 'effect';\nimport { layer as nodeServicesLayer } from '@effect/platform-node/NodeServices';\nimport { Command } from 'commander';\nimport {\n FlywheelStats,\n FlywheelStatus,\n type FlywheelStats as FlywheelStatsPayload,\n type FlywheelStatsCriteria,\n type FlywheelStatsCriterion,\n type FlywheelStatsCriterionStatus,\n type FlywheelStatsTrend,\n} from '@overdeck/contracts';\nimport { abortFlywheelRun, clearFlywheelGate, getFlywheelRunDetail, getFlywheelRunDir, listFlywheelRuns, nextFlywheelRunId, readFlywheelLaunchMetadata, resolveLiveFlywheelRunId, writeFlywheelLaunchMetadata, writeLatestFlywheelStatus } from '../../dashboard/server/services/flywheel-run-state.js';\nimport { loadConfigSync, resolveModel, type FlywheelScope, type RoleEffort } from '../../lib/config-yaml.js';\nimport { FLYWHEEL_ORCHESTRATOR_AGENT_ID, pauseFlywheel, resumeFlywheel, spawnFlywheel } from '../../lib/cloister/flywheel.js';\nimport { stopAgent } from '../../lib/agents.js';\nimport type { RuntimeName } from '../../lib/runtimes/types.js';\nimport { resolveHarness } from '../../lib/harness-resolve.js';\nimport {\n FLYWHEEL_AUTO_PICKUP_BACKLOG_KEY,\n FLYWHEEL_REQUIRE_UAT_BEFORE_MERGE_KEY,\n getFlywheelActiveRunId,\n isFlywheelAutoPickupBacklog,\n isFlywheelGloballyPaused,\n isFlywheelRequireUatBeforeMerge,\n setFlywheelAutoPickupBacklog,\n setFlywheelRequireUatBeforeMerge,\n} from '../../lib/overdeck/control-settings.js';\nimport { sessionExists } from '../../lib/tmux.js';\nimport { ensureInternalTokenSync, INTERNAL_TOKEN_HEADER } from '../../lib/internal-token.js';\nimport { computeMergeQueueFromCandidates, listEligibleCandidatesByProject, type MergeQueueItem } from '../../lib/flywheel-merge-order.js';\nimport { DEFAULT_BRIEF_PATH, requireFlywheelBrief, resolvePrimaryWorktreeRoot } from '../../lib/flywheel-start.js';\nimport { compactFlywheelStateFile } from '../../lib/flywheel-state-retention.js';\nimport { formatMergeBackendStatus, loadMergeBackendStatusForCli } from './flywheel-merge-backend.js';\nimport { compensateFailedFlywheelStart, resolveFlywheelOrderBriefOverlay, resolveFlywheelOrderStart, setFlywheelOrderStatus, type FlywheelOrderStartDeps } from './flywheel-orders.js';\nimport { createFlywheelCompleteCommand } from './flywheel-complete.js';\nimport { registerFlywheelSurfaceCommands } from './flywheel-surfaces.js';\n\ntype InputStream = AsyncIterable<string | Buffer | Uint8Array>;\n\ninterface EmitStatusOptions {\n file: string;\n}\n\ninterface StatusOptions {\n json?: boolean;\n}\n\ninterface StatsOptions {\n window?: string;\n json?: boolean;\n}\n\ninterface FormatStatsOptions {\n color?: boolean;\n}\n\ninterface ConfigOptions {\n get?: true | string;\n set?: string;\n}\n\ninterface StartOptions { brief?: string; cwd?: string; orders?: string }\n\nexport interface StartFlywheelRunDeps extends FlywheelOrderStartDeps {\n resolvePrimaryRoot?: typeof resolvePrimaryWorktreeRoot;\n nextRunId?: typeof nextFlywheelRunId;\n writeLaunchMetadata?: typeof writeFlywheelLaunchMetadata;\n resolveRoleConfig?: () => Promise<ResolvedFlywheelRoleConfig>;\n spawn?: typeof spawnFlywheel;\n writeStatus?: typeof writeLatestFlywheelStatus;\n}\n\ninterface StartFlywheelRunResult {\n runId: string;\n briefDisplayPath: string;\n agentModel?: string;\n}\n\ninterface ReportOptions { cwd?: string; force?: boolean }\n\ninterface ReportOpenOptions {\n runId?: string;\n opener?: (path: string) => void | Promise<void>;\n}\n\ninterface FlywheelGateSnapshot {\n paused: boolean;\n activeRunId: string | null;\n}\n\ninterface ResolvedFlywheelRoleConfig {\n harness: 'claude-code' | 'ohmypi' | 'codex' | 'acp' | 'kimi-code' | 'opencode' | 'muse';\n model: string;\n effort: RoleEffort;\n minAgents: number;\n maxAgents: number;\n scope: FlywheelScope;\n autoPickupBacklog: boolean;\n requireUatBeforeMerge: boolean;\n}\n\nconst decodeFlywheelStatus = Schema.decodeUnknownSync(FlywheelStatus);\nconst decodeFlywheelStats = Schema.decodeUnknownSync(FlywheelStats);\nconst execAsync = promisify(exec);\nconst DEFAULT_STATS_WINDOW = '30d';\n\nfunction dashboardBaseUrl(): string {\n return (process.env.OVERDECK_DASHBOARD_URL || process.env.DASHBOARD_URL || 'http://localhost:3011').replace(/\\/$/, '');\n}\n\nexport async function readFlywheelStatusJson(file: string, input: InputStream = process.stdin): Promise<string> {\n if (file !== '-') return readFile(file, 'utf8');\n\n const chunks: string[] = [];\n for await (const chunk of input) {\n chunks.push(typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8'));\n }\n return chunks.join('');\n}\n\nexport function parseFlywheelStatusJson(raw: string): unknown {\n try {\n return JSON.parse(raw);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Invalid JSON: ${message}`);\n }\n}\n\nexport function validateFlywheelStatusPayload(payload: unknown): FlywheelStatus {\n try {\n return decodeFlywheelStatus(payload);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Invalid FlywheelStatus: ${message}`);\n }\n}\n\nfunction isFlywheelConfigKey(key: string): key is FlywheelConfigKey {\n return FLYWHEEL_CONFIG_KEYS.includes(key as FlywheelConfigKey);\n}\n\nfunction parseFlywheelConfigKey(key: string): FlywheelConfigKey {\n if (!isFlywheelConfigKey(key)) throw new Error(`Unknown flywheel config key: ${key}`);\n return key;\n}\n\nfunction readFlywheelConfigValue(key: FlywheelConfigKey): boolean {\n switch (key) {\n case FLYWHEEL_AUTO_PICKUP_BACKLOG_KEY:\n return isFlywheelAutoPickupBacklog();\n case FLYWHEEL_REQUIRE_UAT_BEFORE_MERGE_KEY:\n return isFlywheelRequireUatBeforeMerge();\n }\n}\n\nfunction writeFlywheelConfigValue(key: FlywheelConfigKey, value: boolean): void {\n switch (key) {\n case FLYWHEEL_AUTO_PICKUP_BACKLOG_KEY:\n setFlywheelAutoPickupBacklog(value);\n return;\n case FLYWHEEL_REQUIRE_UAT_BEFORE_MERGE_KEY:\n setFlywheelRequireUatBeforeMerge(value);\n return;\n }\n}\n\nfunction formatFlywheelConfigValue(key: FlywheelConfigKey): string {\n return `${key}=${readFlywheelConfigValue(key)}`;\n}\n\nfunction parseConfigBoolean(key: string, rawValue: string): boolean {\n if (rawValue === 'true') return true;\n if (rawValue === 'false') return false;\n throw new Error(`Boolean value required for ${key}: ${rawValue}`);\n}\n\nfunction parseFlywheelConfigAssignment(assignment: string): { key: FlywheelConfigKey; value: boolean } {\n const separator = assignment.indexOf('=');\n if (separator === -1) throw new Error('Flywheel config assignment must use <key>=<bool>');\n const key = parseFlywheelConfigKey(assignment.slice(0, separator));\n const value = parseConfigBoolean(key, assignment.slice(separator + 1));\n return { key, value };\n}\n\nexport async function flywheelConfigCommand(options: ConfigOptions = {}): Promise<void> {\n try {\n if (options.get !== undefined && options.set !== undefined) {\n throw new Error('Use either --get or --set, not both');\n }\n\n if (options.set !== undefined) {\n const { key, value } = parseFlywheelConfigAssignment(options.set);\n writeFlywheelConfigValue(key, value);\n console.log(`${key}=${value}`);\n return;\n }\n\n if (typeof options.get === 'string') {\n console.log(formatFlywheelConfigValue(parseFlywheelConfigKey(options.get)));\n return;\n }\n\n console.log(FLYWHEEL_CONFIG_KEYS.map(formatFlywheelConfigValue).join('\\n'));\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n\nconst FLYWHEEL_CONFIG_KEYS = [\n FLYWHEEL_AUTO_PICKUP_BACKLOG_KEY,\n FLYWHEEL_REQUIRE_UAT_BEFORE_MERGE_KEY,\n] as const;\n\ntype FlywheelConfigKey = typeof FLYWHEEL_CONFIG_KEYS[number];\n\nfunction mb(bytes: number): number {\n return Math.round(bytes / 1024 / 1024);\n}\n\nasync function resolveFlywheelRoleConfig(): Promise<ResolvedFlywheelRoleConfig> {\n const { config } = loadConfigSync();\n const flywheel = config.roles?.flywheel;\n const model = resolveModel('flywheel', undefined, config);\n // PAN-1984/PAN-1865: derive the harness from the model provider; never pin\n // flywheel to claude-code or a hardcoded fallback.\n const harness = await resolveHarness({ model });\n return {\n harness,\n model,\n effort: flywheel?.effort ?? 'high',\n minAgents: flywheel?.minAgents ?? 20,\n maxAgents: flywheel?.maxAgents ?? 30,\n scope: flywheel?.scope ?? 'pan-only',\n autoPickupBacklog: isFlywheelAutoPickupBacklog(),\n requireUatBeforeMerge: isFlywheelRequireUatBeforeMerge(),\n };\n}\n\nasync function createInitialFlywheelStatus(\n runId: string,\n startedAt: string,\n cwd: string,\n agentModel: string | undefined,\n agentHarness: RuntimeName | undefined,\n roleConfig: ResolvedFlywheelRoleConfig,\n): Promise<FlywheelStatus> {\n const ramTotalMb = mb(totalmem());\n return {\n runId,\n startedAt,\n elapsedMs: 0,\n orchestrator: {\n harness: agentHarness ?? roleConfig.harness,\n model: agentModel ?? roleConfig.model,\n effort: roleConfig.effort,\n ctxPercent: 0,\n },\n headline: {\n bugsFixed: 0,\n swarmItemsMerged: 0,\n swarmItemsTotal: 0,\n prsMerged: 0,\n awaitingUat: 0,\n },\n activePipeline: [],\n substrateBugs: [],\n agents: [{\n id: FLYWHEEL_ORCHESTRATOR_AGENT_ID,\n label: 'flywheel-orchestrator',\n status: 'running',\n role: 'flywheel',\n model: agentModel,\n }],\n parked: [],\n suggestions: [],\n system: {\n mainHead: await gitOutput('git rev-parse --short HEAD', cwd).catch(() => 'unknown'),\n ramUsedMb: Math.max(0, ramTotalMb - mb(freemem())),\n ramTotalMb,\n swapUsedMb: 0,\n swapTotalMb: 0,\n agentsActive: 1,\n agentsCap: roleConfig.maxAgents,\n },\n openQuestions: [],\n ticks: 0,\n lastTickAt: startedAt,\n };\n}\n\nexport async function postFlywheelStatus(status: FlywheelStatus, fetchImpl: typeof fetch = fetch): Promise<void> {\n const internalToken = ensureInternalTokenSync();\n const res = await fetchImpl(`${dashboardBaseUrl()}/api/flywheel/status`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n [INTERNAL_TOKEN_HEADER]: internalToken,\n },\n body: JSON.stringify(status),\n });\n\n if (!res.ok) {\n const body = await res.text().catch(() => '');\n throw new Error(`Dashboard rejected FlywheelStatus (${res.status})${body ? `: ${body}` : ''}`);\n }\n}\n\nexport async function emitStatusCommand(options: EmitStatusOptions): Promise<void> {\n try {\n const raw = await readFlywheelStatusJson(options.file);\n const payload = parseFlywheelStatusJson(raw);\n const status = validateFlywheelStatusPayload(payload);\n await postFlywheelStatus(status);\n console.log(`Flywheel status emitted for ${status.runId}`);\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n\nexport async function startFlywheelRun(\n options: StartOptions = {},\n deps: StartFlywheelRunDeps = {},\n): Promise<StartFlywheelRunResult> {\n const cwd = options.cwd ?? await (deps.resolvePrimaryRoot ?? resolvePrimaryWorktreeRoot)(process.cwd());\n const brief = await (deps.requireBrief ?? requireFlywheelBrief)(cwd, options.brief);\n const orderContext = options.orders ? await resolveFlywheelOrderStart(cwd, options.orders, deps) : null;\n const book = orderContext?.book ?? null;\n const overlay = await resolveFlywheelOrderBriefOverlay(cwd, book, deps);\n const runId = await (deps.nextRunId ?? nextFlywheelRunId)();\n const startedAt = new Date().toISOString();\n const launchMetadata = {\n version: 1 as const,\n runId,\n workspace: cwd,\n briefPath: brief.absolutePath,\n briefDisplayPath: brief.displayPath,\n ...(overlay.briefOverlayPath ? { briefOverlayPath: overlay.briefOverlayPath } : {}),\n ...(book ? { orders: { bookId: book.id } } : {}),\n };\n await (deps.writeLaunchMetadata ?? writeFlywheelLaunchMetadata)(launchMetadata);\n const roleConfig = await (deps.resolveRoleConfig ?? resolveFlywheelRoleConfig)();\n const agent = await (deps.spawn ?? spawnFlywheel)({\n runId,\n briefPath: brief.absolutePath,\n ...overlay,\n workspace: cwd,\n model: roleConfig.model,\n harness: roleConfig.harness,\n effort: roleConfig.effort,\n minAgents: roleConfig.minAgents,\n maxAgents: roleConfig.maxAgents,\n scope: roleConfig.scope,\n autoPickupBacklog: roleConfig.autoPickupBacklog,\n requireUatBeforeMerge: roleConfig.requireUatBeforeMerge,\n });\n try {\n if (orderContext) await setFlywheelOrderStatus(orderContext, 'running', runId, deps);\n await (deps.writeStatus ?? writeLatestFlywheelStatus)(await createInitialFlywheelStatus(\n runId,\n startedAt,\n cwd,\n agent.model,\n agent.harness,\n roleConfig,\n ));\n return { runId, briefDisplayPath: brief.displayPath, agentModel: agent.model };\n } catch (error) {\n return compensateFailedFlywheelStart(orderContext, runId, error, deps);\n }\n}\n\nexport async function flywheelStartCommand(options: StartOptions = {}): Promise<void> {\n try {\n const result = await startFlywheelRun(options);\n\n console.log(`Flywheel started: ${result.runId}`);\n console.log(`Brief: ${result.briefDisplayPath}`);\n console.log(`Run URL: ${dashboardBaseUrl()}/flywheel`);\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n\nfunction formatElapsed(ms: number): string {\n const totalSeconds = Math.max(0, Math.floor(ms / 1000));\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n if (hours > 0) return `${hours}h ${minutes}m`;\n if (minutes > 0) return `${minutes}m ${seconds}s`;\n return `${seconds}s`;\n}\n\nasync function loadActiveFlywheelStatus(): Promise<FlywheelStatus | null> {\n // resolveLiveFlywheelRunId self-heals the SQLite gate when the prior run\n // has ended or its on-disk state is gone (PAN-1245). Status and start now\n // consult the same source of truth.\n const activeRunId = await resolveLiveFlywheelRunId();\n if (!activeRunId) return null;\n const detail = await getFlywheelRunDetail(activeRunId);\n return detail?.status === 'running' ? detail.latest : null;\n}\n\nexport function formatFlywheelStatus(status: FlywheelStatus): string {\n return [\n `Run: ${status.runId}`,\n `Elapsed: ${formatElapsed(status.elapsedMs)}`,\n `Bugs fixed: ${status.headline.bugsFixed}`,\n `SWARM items: ${status.headline.swarmItemsMerged}/${status.headline.swarmItemsTotal}`,\n `PRs merged: ${status.headline.prsMerged}`,\n `Awaiting UAT: ${status.headline.awaitingUat}`,\n `Active agents: ${status.system.agentsActive}/${status.system.agentsCap}`,\n `RAM: ${status.system.ramUsedMb} MiB used / ${status.system.ramTotalMb} MiB total`,\n `Main HEAD: ${status.system.mainHead.slice(0, 7)}`,\n `Last tick: ${status.lastTickAt}`,\n ].join('\\n');\n}\n\nasync function formatMergeQueueForCli(cwd: string, json?: boolean): Promise<string> {\n const q = await Effect.runPromise(computeMergeQueueFromCandidates(await listEligibleCandidatesByProject(cwd), cwd).pipe(Effect.provide(nodeServicesLayer)));\n return json ? JSON.stringify({ mergeQueue: q }, null, 2) : `Merge queue:\\n${q.map(i => ` ${i.pr ? `#${i.pr}` : 'no-pr'} ${i.issueId} ${i.title}`).join('\\n') || ' (empty)'}`;\n}\n\nexport async function flywheelStatusCommand(options: StatusOptions): Promise<void> {\n try {\n const status = await loadActiveFlywheelStatus();\n if (!status) {\n console.log(await formatMergeQueueForCli(process.cwd(), options.json));\n return;\n }\n const mergeBackend = await loadMergeBackendStatusForCli();\n console.log(options.json\n ? JSON.stringify({ ...status, mergeBackend }, null, 2)\n : `${formatFlywheelStatus(status)}\\n${formatMergeBackendStatus(mergeBackend)}`);\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n\nconst STATS_CRITERION_KEYS = [\n 'c1_bugRate',\n 'c2_p0Bugs',\n 'c3_passRate',\n 'c4_mttr',\n 'c5_intervention',\n 'c6_timeConsistency',\n 'c7_flake',\n] as const satisfies readonly (keyof FlywheelStatsCriteria)[];\n\nconst STATUS_GLYPH: Record<FlywheelStatsCriterionStatus, string> = {\n green: '● green',\n yellow: '● yellow',\n red: '● red',\n insufficient_data: '○ insufficient_data',\n};\n\nconst STATUS_COLOR: Record<FlywheelStatsCriterionStatus, string> = {\n green: '\\x1b[32m',\n yellow: '\\x1b[33m',\n red: '\\x1b[31m',\n insufficient_data: '\\x1b[90m',\n};\n\nconst TREND_LABEL: Record<FlywheelStatsTrend, string> = {\n up: '↗ up',\n down: '↘ down',\n flat: '→ flat',\n};\n\nexport async function fetchFlywheelStats(window: string, fetchImpl: typeof fetch = fetch): Promise<FlywheelStatsPayload> {\n const internalToken = ensureInternalTokenSync();\n const url = new URL(`${dashboardBaseUrl()}/api/flywheel/stats`);\n url.searchParams.set('window', window);\n const res = await fetchImpl(url.toString(), {\n headers: {\n [INTERNAL_TOKEN_HEADER]: internalToken,\n },\n });\n\n if (!res.ok) {\n const body = await res.text().catch(() => '');\n throw new Error(`Dashboard rejected Flywheel stats request (${res.status})${body ? `: ${body}` : ''}`);\n }\n\n return decodeFlywheelStats(await res.json());\n}\n\nfunction formatScalar(value: number): string {\n if (!Number.isFinite(value)) return '—';\n if (value > 0 && Math.abs(value) < 1) return `${(value * 100).toFixed(1)}%`;\n return Number.isInteger(value) ? String(value) : value.toFixed(2);\n}\n\nfunction formatDuration(ms: number): string {\n const abs = Math.abs(ms);\n if (abs >= 24 * 60 * 60 * 1000) return `${(ms / (24 * 60 * 60 * 1000)).toFixed(1)}d`;\n if (abs >= 60 * 60 * 1000) return `${(ms / (60 * 60 * 1000)).toFixed(1)}h`;\n if (abs >= 60 * 1000) return `${(ms / (60 * 1000)).toFixed(1)}m`;\n return `${Math.round(ms)}ms`;\n}\n\nfunction formatObjectValue(value: Record<string, unknown>): string {\n return Object.entries(value)\n .map(([key, entry]) => {\n if (typeof entry === 'number' && key.toLowerCase().endsWith('ms')) return `${key}: ${formatDuration(entry)}`;\n if (typeof entry === 'number') return `${key}: ${formatScalar(entry)}`;\n if (typeof entry === 'string' || typeof entry === 'boolean' || entry === null) return `${key}: ${String(entry)}`;\n return `${key}: ${JSON.stringify(entry)}`;\n })\n .join(', ');\n}\n\nfunction formatCriterionValue(value: FlywheelStatsCriterion['value']): string {\n return typeof value === 'number' ? formatScalar(value) : formatObjectValue(value as Record<string, unknown>);\n}\n\nfunction colorStatus(status: FlywheelStatsCriterionStatus, color: boolean): string {\n if (!color) return STATUS_GLYPH[status];\n const [glyph, label] = STATUS_GLYPH[status].split(' ');\n return `${STATUS_COLOR[status]}${glyph}\\x1b[0m ${label}`;\n}\n\nfunction tableStatsCell(value: string | number | undefined): string {\n return String(value ?? '—').replace(/\\|/g, '\\\\|').replace(/\\n/g, ' ');\n}\n\nexport function formatFlywheelStats(stats: FlywheelStatsPayload, options: FormatStatsOptions = {}): string {\n const color = options.color ?? process.stdout.isTTY === true;\n const rows = STATS_CRITERION_KEYS.map((key) => {\n const criterion = stats.criteria[key];\n return `| ${tableStatsCell(criterion.label)} | ${tableStatsCell(formatCriterionValue(criterion.value))} | ${tableStatsCell(formatCriterionValue(criterion.target))} | ${tableStatsCell(colorStatus(criterion.status, color))} | ${tableStatsCell(criterion.trend ? TREND_LABEL[criterion.trend] : '—')} | ${criterion.sampleSize} |`;\n });\n return [\n `Flywheel stats (${stats.window})`,\n `Generated: ${stats.generatedAt}`,\n '',\n '| Criterion | Value | Target | Status | Trend | Sample |',\n '|---|---:|---:|---|---|---:|',\n ...rows,\n ].join('\\n');\n}\n\nexport async function flywheelStatsCommand(options: StatsOptions = {}): Promise<void> {\n try {\n const stats = await fetchFlywheelStats(options.window ?? DEFAULT_STATS_WINDOW);\n console.log(options.json ? JSON.stringify(stats, null, 2) : formatFlywheelStats(stats));\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n\nfunction runNumberFromRunId(runId: string): number {\n const match = /^RUN-(\\d+)$/.exec(runId);\n if (!match) throw new Error(`Invalid Flywheel run id for report: ${runId}`);\n return Number(match[1]);\n}\n\nfunction reportDate(status: FlywheelStatus): string {\n return status.lastTickAt.slice(0, 10);\n}\n\nfunction tableCell(value: string | number | undefined): string {\n return String(value ?? '').replace(/\\|/g, '\\\\|').replace(/\\n/g, ' ');\n}\n\nexport function formatFlywheelStateReport(status: FlywheelStatus, mergeQueue: MergeQueueItem[] = []): string {\n const runNumber = runNumberFromRunId(status.runId);\n const suggestionRows = status.suggestions.length > 0\n ? status.suggestions.map((suggestion) => `| ${tableCell(suggestion.priority)} | ${tableCell(suggestion.action)} | ${tableCell(suggestion.issueId)} | ${tableCell(suggestion.rationale)} |`).join('\\n')\n : 'No suggestions emitted this run.';\n const activePipelineRows = status.activePipeline.length > 0\n ? status.activePipeline.map((item) => `| ${tableCell(item.issueId)} | ${tableCell(item.verb)} | ${tableCell(item.status)} | ${tableCell(item.title)} | ${tableCell(item.progressPercent)} | ${tableCell(item.pr)} |`).join('\\n')\n : '| _None_ | | | | | |';\n const substrateRows = status.substrateBugs.length > 0\n ? status.substrateBugs.map((bug) => `| ${tableCell(bug.issueId)} | ${tableCell(bug.status)} | ${tableCell(bug.title)} | ${tableCell(bug.commitSha?.slice(0, 10))} |`).join('\\n')\n : '| _None_ | | | |';\n const patternLines = status.parked.length > 0\n ? status.parked.map((item) => `- **${item.issueId}** (${item.reason}) — ${item.title}`).join('\\n')\n : '- None recorded this run.';\n const questionLines = status.openQuestions.length > 0\n ? status.openQuestions.map((question) => `- ${question}`).join('\\n')\n : '- None.';\n const mergeQueueSection = mergeQueue.length > 0\n ? `## Merge Queue\n\nMerge ship-ready PRs in this order to avoid rebase conflicts. Branches with overlapping file changes must be merged in sequence; non-overlapping branches can be merged in any order.\n\n| # | Issue | PR | Conflicts With |\n|---|---|---|---|\n${mergeQueue.map((item) => {\n const prCell = item.pr != null ? `#${item.pr}` : '—';\n const conflictsCell = item.conflictsWith.length > 0 ? item.conflictsWith.join(', ') : '—';\n return `| ${item.mergeOrder} | ${item.issueId} | ${prCell} | ${conflictsCell} |`;\n}).join('\\n')}\n\n---\n\n`\n : '';\n\n return `# Flywheel Run ${runNumber} Report — ${reportDate(status)}\n\nPer-run report derived from the last \\`FlywheelStatus\\` snapshot. Durable cumulative memory across runs lives in \\`docs/FLYWHEEL-STATE.md\\`.\n\n**Run window:** ${status.startedAt} → ${status.lastTickAt} (${status.orchestrator.harness}, ${status.orchestrator.model}, ${status.orchestrator.effort})\n\n**Headline result:** ${status.headline.bugsFixed} substrate bugs fixed; ${status.headline.swarmItemsMerged}/${status.headline.swarmItemsTotal} SWARM items merged; ${status.headline.prsMerged} PRs merged; ${status.headline.awaitingUat} awaiting UAT.\n\n**Run counters:** ticks ${status.ticks}; last tick ${status.lastTickAt}; orchestrator context ${status.orchestrator.ctxPercent}%.\n\n---\n\n${mergeQueueSection}## Suggestions\n\n${status.suggestions.length > 0 ? `| Priority | Action | Issue | Rationale |\n|---|---|---|---|\n${suggestionRows}` : suggestionRows}\n\n---\n\n## Active Pipeline\n\n| Issue | Verb | Status | Title | Progress | PR |\n|---|---|---|---|---:|---:|\n${activePipelineRows}\n\n---\n\n## Cycling Alerts\n\n${questionLines}\n\n---\n\n## Infrastructure Gaps\n\n| Issue | Status | Notes | Commit |\n|---|---|---|---|\n${substrateRows}\n\n---\n\n## Pattern Ledger\n\n${patternLines}\n\n---\n\n## Skill Gaps\n\n- Keep \\`pan flywheel report\\` in the closeout path so run summaries stay archived under the run directory.\n`;\n}\n\nasync function isFlywheelStateDirty(cwd: string): Promise<boolean> {\n // Tolerate non-git cwd / missing file: a non-zero git exit means there are\n // no orchestrator-authored changes worth committing here (PAN-1245). The\n // gate clear in flywheelReportCommand must not be blocked by this check.\n try {\n const { stdout } = await execAsync(\n 'git status --porcelain docs/FLYWHEEL-STATE.md',\n { cwd, encoding: 'utf8' },\n );\n return stdout.trim().length > 0;\n } catch {\n return false;\n }\n}\n\nasync function loadReportFlywheelStatus(): Promise<FlywheelStatus | null> {\n const activeRunId = getFlywheelActiveRunId();\n if (activeRunId) {\n const activeDetail = await getFlywheelRunDetail(activeRunId);\n if (activeDetail?.latest) return activeDetail.latest;\n }\n const runs = await listFlywheelRuns();\n const run = runs.find((candidate) => candidate.status === 'running') ?? runs[0];\n if (!run) return null;\n const detail = await getFlywheelRunDetail(run.id);\n return detail?.latest ?? null;\n}\n\nasync function gitOutput(command: string, cwd: string): Promise<string> {\n const { stdout } = await execAsync(command, { cwd, encoding: 'utf8' });\n return stdout.trim();\n}\n\nasync function commitFlywheelStateChanges(cwd: string, runNumber: number): Promise<void> {\n const subject = `docs(flywheel): run ${runNumber}`;\n await execAsync('git add docs/FLYWHEEL-STATE.md', { cwd });\n const headSubject = await gitOutput('git log -1 --format=%s', cwd).catch(() => '');\n const command = headSubject === subject\n ? `git commit --amend -m ${JSON.stringify(subject)}`\n : `git commit -m ${JSON.stringify(subject)}`;\n await execAsync(command, { cwd, encoding: 'utf8' });\n}\n\nfunction readFlywheelGateSnapshot(): FlywheelGateSnapshot {\n return {\n paused: isFlywheelGloballyPaused(),\n activeRunId: getFlywheelActiveRunId(),\n };\n}\n\nfunction formatGateSnapshot(snapshot: FlywheelGateSnapshot): string {\n return `paused=${snapshot.paused ? 'true' : 'false'} active_run_id=${snapshot.activeRunId ?? 'none'}`;\n}\n\nexport async function pauseFlywheelRun(): Promise<{ before: FlywheelGateSnapshot; after: FlywheelGateSnapshot; changed: boolean }> {\n const before = readFlywheelGateSnapshot();\n if (before.paused) return { before, after: before, changed: false };\n await pauseFlywheel();\n return { before, after: readFlywheelGateSnapshot(), changed: true };\n}\n\nexport async function flywheelPauseCommand(): Promise<void> {\n try {\n const result = await pauseFlywheelRun();\n if (!result.changed) {\n console.log(`Flywheel already paused (${formatGateSnapshot(result.before)})`);\n return;\n }\n\n console.log(`Flywheel paused: before ${formatGateSnapshot(result.before)}; after ${formatGateSnapshot(result.after)}`);\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n\nexport async function resumeFlywheelRun(): Promise<{ before: FlywheelGateSnapshot; after: FlywheelGateSnapshot; changed: boolean }> {\n const before = readFlywheelGateSnapshot();\n if (!before.paused && await Effect.runPromise(sessionExists(FLYWHEEL_ORCHESTRATOR_AGENT_ID))) {\n return { before, after: before, changed: false };\n }\n if (!before.activeRunId) throw new Error('No active flywheel run to resume');\n const launch = await readFlywheelLaunchMetadata(before.activeRunId);\n if (!launch) {\n throw new Error(`Flywheel run ${before.activeRunId} is missing launch metadata; cannot resume safely`);\n }\n const brief = await requireFlywheelBrief(launch.workspace, launch.briefPath);\n const roleConfig = await resolveFlywheelRoleConfig();\n await resumeFlywheel({\n resumeCause: 'operator',\n workspace: launch.workspace,\n briefPath: brief.absolutePath,\n model: roleConfig.model,\n harness: roleConfig.harness,\n effort: roleConfig.effort,\n minAgents: roleConfig.minAgents,\n maxAgents: roleConfig.maxAgents,\n scope: roleConfig.scope,\n autoPickupBacklog: roleConfig.autoPickupBacklog,\n requireUatBeforeMerge: roleConfig.requireUatBeforeMerge,\n });\n return { before, after: readFlywheelGateSnapshot(), changed: true };\n}\n\nexport async function flywheelResumeCommand(): Promise<void> {\n try {\n const result = await resumeFlywheelRun();\n if (!result.changed) {\n console.log(`Flywheel already running (${formatGateSnapshot(result.before)})`);\n return;\n }\n\n console.log(`Flywheel resumed: before ${formatGateSnapshot(result.before)}; after ${formatGateSnapshot(result.after)}`);\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n\nfunction clearFlywheelRunGate(runId: string): void {\n if (getFlywheelActiveRunId() === runId) {\n clearFlywheelGate();\n }\n}\n\n// Stop the orchestrator, write the report, commit state changes, and clear the gate.\nexport async function flywheelStopCommand(options: Pick<ReportOptions, 'cwd'> = {}): Promise<void> {\n try {\n const sessionAlive = await Effect.runPromise(sessionExists(FLYWHEEL_ORCHESTRATOR_AGENT_ID));\n if (sessionAlive) {\n await Effect.runPromise(stopAgent(FLYWHEEL_ORCHESTRATOR_AGENT_ID, 'operator'));\n }\n\n const status = await loadReportFlywheelStatus();\n if (!status) {\n console.log('No flywheel run is active and nothing is left to report.');\n return;\n }\n\n // The orchestrator has already been stopped, so force the report path to\n // bypass its alive-session guard and finalize the run. Thread cwd through\n // explicitly — the report path commits docs/FLYWHEEL-STATE.md in its cwd,\n // and an implicit process.cwd() here has committed into the wrong repo\n // when invoked from tests or another checkout (PAN-2658).\n await flywheelReportCommand({ force: true, cwd: options.cwd });\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n\nasync function buildFlywheelStateReport(status: FlywheelStatus, cwd: string): Promise<string> {\n // PAN-1696: Compute the merge queue from the pipeline ready set, not flywheel activePipeline.\n const candidates = await listEligibleCandidatesByProject(cwd);\n const mergeQueue = await Effect.runPromise(computeMergeQueueFromCandidates(candidates, cwd).pipe(Effect.provide(nodeServicesLayer)));\n return formatFlywheelStateReport(status, mergeQueue);\n}\n\nasync function persistFlywheelStateReport(status: FlywheelStatus, cwd: string, report: string): Promise<void> {\n const runNumber = runNumberFromRunId(status.runId);\n await writeFile(join(getFlywheelRunDir(status.runId), 'report.md'), report, 'utf8');\n await compactFlywheelStateFile(cwd);\n const stateChanged = await isFlywheelStateDirty(cwd);\n if (stateChanged) {\n await commitFlywheelStateChanges(cwd, runNumber);\n console.log(`Wrote per-run report and committed FLYWHEEL-STATE.md changes for run ${runNumber}.`);\n } else {\n console.log(`Wrote per-run report for run ${runNumber}. No FLYWHEEL-STATE.md changes to commit.`);\n }\n}\n\nexport async function flywheelReportCommand(options: ReportOptions = {}): Promise<void> {\n try {\n const cwd = options.cwd ?? process.cwd();\n\n // Writing report.md finalizes the run. Refuse while the orchestrator is alive;\n // its own end-of-run call passes --force to bypass this guard.\n if (!options.force && await Effect.runPromise(sessionExists(FLYWHEEL_ORCHESTRATOR_AGENT_ID))) {\n console.error('Refusing to write report — flywheel orchestrator session is still alive.');\n console.error('This command finalizes the run (writes report.md and clears the active-run gate).');\n console.error('Run `pan flywheel pause` (or `pan flywheel abort`) first, or pass --force to override.');\n process.exitCode = 1;\n return;\n }\n\n const status = await loadReportFlywheelStatus();\n if (!status) {\n console.error('no flywheel run to report');\n process.exitCode = 1;\n return;\n }\n\n try {\n await persistFlywheelStateReport(status, cwd, await buildFlywheelStateReport(status, cwd));\n } finally {\n clearFlywheelRunGate(status.runId);\n }\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n\nconst flywheelCompleteCommand = createFlywheelCompleteCommand({ loadStatus: loadReportFlywheelStatus, buildReport: buildFlywheelStateReport, persistReport: persistFlywheelStateReport, clearGate: clearFlywheelRunGate, start: startFlywheelRun });\nfunction getPlatformOpenCommand(): string {\n switch (process.platform) {\n case 'linux': return 'xdg-open';\n case 'darwin': return 'open';\n case 'win32': return 'explorer';\n default: throw new Error(`Opening files is not supported on ${process.platform}`);\n }\n}\n\nfunction openPathDetached(path: string): void {\n const child = spawn(getPlatformOpenCommand(), [path], {\n detached: true,\n stdio: 'ignore',\n });\n child.unref();\n}\n\nasync function resolveReportOpenRunId(runId: string | undefined): Promise<string> {\n if (runId) return runId;\n const runs = await listFlywheelRuns();\n const run = runs.find((candidate) => candidate.status === 'complete') ?? runs[0];\n if (!run) throw new Error('no flywheel run report to open');\n return run.id;\n}\n\nexport async function openFlywheelRunReport(options: ReportOpenOptions = {}): Promise<{ runId: string; path: string }> {\n const runId = await resolveReportOpenRunId(options.runId);\n const detail = await getFlywheelRunDetail(runId);\n if (!detail) throw new Error(`Flywheel run not found: ${runId}`);\n if (!detail.paths.report) throw new Error(`No report exists for ${runId}`);\n await (options.opener ?? openPathDetached)(detail.paths.report);\n return { runId, path: detail.paths.report };\n}\n\nexport async function flywheelReportOpenCommand(options: ReportOpenOptions = {}): Promise<void> {\n try {\n const result = await openFlywheelRunReport(options);\n console.log(`Opened Flywheel report for ${result.runId}: ${result.path}`);\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n\n// Discard the current run without writing a report (PAN-1245).\nexport async function flywheelAbortCommand(): Promise<void> {\n try {\n const candidate = getFlywheelActiveRunId();\n if (!candidate) {\n console.log('No active flywheel run to abort.');\n return;\n }\n await Effect.runPromise(stopAgent(FLYWHEEL_ORCHESTRATOR_AGENT_ID, 'operator'));\n await abortFlywheelRun(candidate);\n console.log(`Aborted flywheel run ${candidate}.`);\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n\nexport function registerFlywheelCommands(program: Command): void {\n const flywheel = program\n .command('flywheel')\n .description('Flywheel orchestrator lifecycle and status helpers');\n registerFlywheelSurfaceCommands(program);\n\n flywheel\n .command('start')\n .description('Start the Flywheel orchestrator')\n .option('--brief <path>', 'Path to the Flywheel brief', DEFAULT_BRIEF_PATH)\n .option('--orders <book-id>', 'Bind the run to a validated order book')\n .action(flywheelStartCommand);\n\n flywheel\n .command('emit-status')\n .description('Validate and publish a FlywheelStatus JSON snapshot to the local dashboard')\n .requiredOption('--file <path>', 'Path to FlywheelStatus JSON, or - to read from stdin')\n .action(emitStatusCommand);\n\n flywheel\n .command('config')\n .description('Get or set Flywheel autonomy configuration')\n .option('--get [key]', 'Print Flywheel config values')\n .option('--set <key=value>', 'Set a Flywheel config boolean')\n .action(flywheelConfigCommand);\n\n flywheel\n .command('status')\n .description('Show the active Flywheel run status')\n .option('--json', 'Emit the raw FlywheelStatus JSON')\n .action(flywheelStatusCommand);\n\n flywheel\n .command('stats')\n .description('Show Flywheel v1.0 readiness stats')\n .option('--window <duration>', 'Stats window duration', DEFAULT_STATS_WINDOW)\n .option('--json', 'Emit the raw FlywheelStats JSON')\n .action(flywheelStatsCommand);\n\n flywheel\n .command('pause')\n .description('Pause the active Flywheel orchestrator run')\n .action(flywheelPauseCommand);\n\n flywheel\n .command('resume')\n .description('Resume the paused Flywheel orchestrator run')\n .action(flywheelResumeCommand);\n\n flywheel\n .command('report')\n .description('Finalize the active Flywheel run: write report.md, compact over-threshold FLYWHEEL-STATE.md history, commit State changes, and clear the active-run gate. Refuses to run while the orchestrator session is alive (pause or abort first).')\n .option('--force', 'Bypass the orchestrator-alive guard. Intended for the orchestrator role\\'s own end-of-run call.')\n .action(flywheelReportCommand);\n\n flywheel.command('complete')\n .description('Complete a drained order-book run, write its report and retrospective, then continue mechanically')\n .option('--force', 'Complete even when the order book still has non-terminal items').action(flywheelCompleteCommand);\n\n flywheel\n .command('stop')\n .description('Stop the Flywheel orchestrator gracefully: kill any live session, write report.md, commit FLYWHEEL-STATE.md changes, and clear the active-run gate')\n .action(flywheelStopCommand);\n\n flywheel\n .command('abort')\n .description('Discard the active Flywheel run without writing a report')\n .action(flywheelAbortCommand);\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { c as pauseFlywheel, l as resumeFlywheel } from "./flywheel-Is3w2lcT.js";
1
+ import { c as pauseFlywheel, l as resumeFlywheel } from "./flywheel-DKldI63J.js";
2
2
  export { pauseFlywheel, resumeFlywheel };
@@ -1,2 +1,2 @@
1
- import { a as computePredictedConflictSignals, c as orderMergeCandidates, d as planUatCandidate, f as resolveMergeQueuePrUrl, i as computePolyrepoMergeQueueFromCandidates, l as pickFromSequence, n as changedFilesVsMain, o as declaredIssueFootprint, p as reviewRecordEligibility, r as computeMergeQueueFromCandidates, s as listEligibleCandidatesByProject, t as MERGE_QUEUE_GIT_CONCURRENCY, u as planMergeTrain } from "./flywheel-merge-order-BsNPMu0S.js";
1
+ import { a as computePredictedConflictSignals, c as orderMergeCandidates, d as planUatCandidate, f as resolveMergeQueuePrUrl, i as computePolyrepoMergeQueueFromCandidates, l as pickFromSequence, n as changedFilesVsMain, o as declaredIssueFootprint, p as reviewRecordEligibility, r as computeMergeQueueFromCandidates, s as listEligibleCandidatesByProject, t as MERGE_QUEUE_GIT_CONCURRENCY, u as planMergeTrain } from "./flywheel-merge-order-PG5op4j7.js";
2
2
  export { MERGE_QUEUE_GIT_CONCURRENCY, changedFilesVsMain, computeMergeQueueFromCandidates, computePolyrepoMergeQueueFromCandidates, computePredictedConflictSignals, declaredIssueFootprint, listEligibleCandidatesByProject, orderMergeCandidates, pickFromSequence, planMergeTrain, planUatCandidate, resolveMergeQueuePrUrl, reviewRecordEligibility };
@@ -2,7 +2,7 @@ import { $t as findProjectByPathSync, On as resolveProjectFromIssueSync, an as g
2
2
  import { c as init_dag, n as compileGlob } from "./dag-Drz6Nrnf.js";
3
3
  import { n as computeIssueFootprint, r as init_swarm_readiness } from "./swarm-readiness-BkVvnCdr.js";
4
4
  import { n as resolveGitHubIssueSync, t as init_tracker_utils } from "./tracker-utils-McMENyKj.js";
5
- import { d as getReviewStatusSync, h as loadReviewStatuses, p as init_review_status, v as mergeGateEligibility } from "./review-status-BegAr86G.js";
5
+ import { d as getReviewStatusSync, h as loadReviewStatuses, p as init_review_status, v as mergeGateEligibility } from "./review-status-yY5qt7Rl.js";
6
6
  import { g as isAutoPickable, h as init_pickup, l as classifyIssue } from "./pickup-TwHZNJn4.js";
7
7
  import { Effect } from "effect";
8
8
  import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
@@ -461,4 +461,4 @@ function pickFromSequence(nodes, opts) {
461
461
  //#endregion
462
462
  export { computePredictedConflictSignals as a, orderMergeCandidates as c, planUatCandidate as d, resolveMergeQueuePrUrl as f, computePolyrepoMergeQueueFromCandidates as i, pickFromSequence as l, changedFilesVsMain as n, declaredIssueFootprint as o, reviewRecordEligibility as p, computeMergeQueueFromCandidates as r, listEligibleCandidatesByProject as s, MERGE_QUEUE_GIT_CONCURRENCY as t, planMergeTrain as u };
463
463
 
464
- //# sourceMappingURL=flywheel-merge-order-BsNPMu0S.js.map
464
+ //# sourceMappingURL=flywheel-merge-order-PG5op4j7.js.map