@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.
- package/dist/agents-DNMRPC8n.js.map +1 -1
- package/dist/{build-info-BJuEVWOh.js → build-info-ioRdOmqT.js} +3 -3
- package/dist/{build-info-BJuEVWOh.js.map → build-info-ioRdOmqT.js.map} +1 -1
- package/dist/build-info-kMMitDOP.js +2 -0
- package/dist/cli/index.js +4 -4
- package/dist/conversation-compaction-GFfO5UZL.js.map +1 -1
- package/dist/dashboard/{advancing-selfheal-Bq8V0QUa.js → advancing-selfheal-Dp5_Gai3.js} +3 -3
- package/dist/dashboard/{advancing-selfheal-Bq8V0QUa.js.map → advancing-selfheal-Dp5_Gai3.js.map} +1 -1
- package/dist/dashboard/{agent-gc-Coz8zQt_.js → agent-gc-JzoSs5Pi.js} +3 -3
- package/dist/dashboard/{agent-gc-Coz8zQt_.js.map → agent-gc-JzoSs5Pi.js.map} +1 -1
- package/dist/dashboard/{agent-idle-CAwB-K6M.js → agent-idle-D-Qy5BN_.js} +2 -2
- package/dist/dashboard/{agent-idle-CAwB-K6M.js.map → agent-idle-D-Qy5BN_.js.map} +1 -1
- package/dist/dashboard/{agent-state-Ci6PhjOE.js → agent-state-B196fj1h.js} +38 -16
- package/dist/dashboard/agent-state-B196fj1h.js.map +1 -0
- package/dist/dashboard/{agent-state-CbUEwUEQ.js → agent-state-IpWnw8lv.js} +1 -1
- package/dist/dashboard/{agent-state-service-D6IVrX3B.js → agent-state-service-CCo5-9DR.js} +3 -3
- package/dist/dashboard/{agent-state-service-D6IVrX3B.js.map → agent-state-service-CCo5-9DR.js.map} +1 -1
- package/dist/dashboard/{agent-state-service-DcN1j6eK.js → agent-state-service-D_F3VZ23.js} +1 -1
- package/dist/dashboard/{agents-BVvL4o_4.js → agents-CkGhnAmu.js} +1 -1
- package/dist/dashboard/{backlog-auto-trigger-BuSBqG2V.js → backlog-auto-trigger-B5Z2zBtC.js} +2 -2
- package/dist/dashboard/{backlog-auto-trigger-BuSBqG2V.js.map → backlog-auto-trigger-B5Z2zBtC.js.map} +1 -1
- package/dist/dashboard/build-info-BTjOB0Xr.js +2 -0
- package/dist/dashboard/{build-info-Cj3Y0K87.js → build-info-D0EJNj6C.js} +3 -3
- package/dist/dashboard/{build-info-Cj3Y0K87.js.map → build-info-D0EJNj6C.js.map} +1 -1
- package/dist/dashboard/cli-CIznFrcE.js +2 -0
- package/dist/dashboard/{cli-DIhQieif.js → cli-DCbVrSsg.js} +2 -2
- package/dist/dashboard/{cli-DIhQieif.js.map → cli-DCbVrSsg.js.map} +1 -1
- package/dist/dashboard/{close-issue-CRlmrE5g.js → close-issue-Bmu9aOIx.js} +2 -2
- package/dist/dashboard/{close-issue-CRlmrE5g.js.map → close-issue-Bmu9aOIx.js.map} +1 -1
- package/dist/dashboard/{closed-issue-reaper-DrHyYgJR.js → closed-issue-reaper-COxfTtLz.js} +5 -5
- package/dist/dashboard/{closed-issue-reaper-DrHyYgJR.js.map → closed-issue-reaper-COxfTtLz.js.map} +1 -1
- package/dist/dashboard/{closed-issue-reaper-DWlw2EtL.js → closed-issue-reaper-DwMwG_Pz.js} +1 -1
- package/dist/dashboard/{compaction-continuation-DhYgsXmF.js → compaction-continuation-Bqc212Zm.js} +2 -2
- package/dist/dashboard/{compaction-continuation-DhYgsXmF.js.map → compaction-continuation-Bqc212Zm.js.map} +1 -1
- package/dist/dashboard/{conflict-gate-vczgdVTa.js → conflict-gate-9mPVg7-5.js} +1 -1
- package/dist/dashboard/{conflict-gate-Bt-lLgYf.js → conflict-gate-CGQTcR0I.js} +3 -3
- package/dist/dashboard/{conflict-gate-Bt-lLgYf.js.map → conflict-gate-CGQTcR0I.js.map} +1 -1
- package/dist/dashboard/{conversation-compaction-CsdCbcQB.js → conversation-compaction-C7P6V8Iw.js} +5 -5
- package/dist/dashboard/{conversation-compaction-CsdCbcQB.js.map → conversation-compaction-C7P6V8Iw.js.map} +1 -1
- package/dist/dashboard/{conversation-compaction-D0zgWM1f.js → conversation-compaction-Cck5oi-V.js} +1 -1
- package/dist/dashboard/{conversation-service-6LaymUV-.js → conversation-service-CsOpwI9Y.js} +37 -5
- package/dist/dashboard/{conversation-service-6LaymUV-.js.map → conversation-service-CsOpwI9Y.js.map} +1 -1
- package/dist/dashboard/conversation-service-plOFkoMo.js +3 -0
- package/dist/dashboard/dashboard-db-worker.js +1 -1
- package/dist/dashboard/{deacon-CU47dU7U.js → deacon-B8oMMIgN.js} +62 -62
- package/dist/dashboard/{deacon-CU47dU7U.js.map → deacon-B8oMMIgN.js.map} +1 -1
- package/dist/dashboard/{deacon-DYsOa_ss.js → deacon-b7J57Svt.js} +2 -2
- package/dist/dashboard/{deacon-pending-lifecycle-ijGX8wLb.js → deacon-pending-lifecycle-3NIJ7ew5.js} +2 -2
- package/dist/dashboard/{deacon-pending-lifecycle-ijGX8wLb.js.map → deacon-pending-lifecycle-3NIJ7ew5.js.map} +1 -1
- package/dist/dashboard/deacon.js +5 -5
- package/dist/dashboard/{delivery-DMYxIE0J.js → delivery-lnI-gQKo.js} +1 -1
- package/dist/dashboard/{deploy-patrol-C4eXco1S.js → deploy-patrol-u3v2krg9.js} +2 -2
- package/dist/dashboard/{deploy-patrol-C4eXco1S.js.map → deploy-patrol-u3v2krg9.js.map} +1 -1
- package/dist/dashboard/{feedback-stuck-retirement-Dd5XNrMr.js → feedback-stuck-retirement-CoFNqjCc.js} +2 -2
- package/dist/dashboard/{feedback-stuck-retirement-Dd5XNrMr.js.map → feedback-stuck-retirement-CoFNqjCc.js.map} +1 -1
- package/dist/dashboard/{feedback-target-BzdJ2wIR.js → feedback-target-BBCh3RRq.js} +1 -1
- package/dist/dashboard/{feedback-target-BcsA_GRV.js → feedback-target-CeCq6EhW.js} +5 -5
- package/dist/dashboard/{feedback-target-BcsA_GRV.js.map → feedback-target-CeCq6EhW.js.map} +1 -1
- package/dist/dashboard/{flywheel-Is3w2lcT.js → flywheel-DKldI63J.js} +7 -7
- package/dist/dashboard/{flywheel-Is3w2lcT.js.map → flywheel-DKldI63J.js.map} +1 -1
- package/dist/dashboard/{flywheel-D0guNqkh.js → flywheel-DpRPLErh.js} +7 -7
- package/dist/dashboard/{flywheel-D0guNqkh.js.map → flywheel-DpRPLErh.js.map} +1 -1
- package/dist/dashboard/{flywheel-B34cdfbd.js → flywheel-jeZFYvm-.js} +1 -1
- package/dist/dashboard/{flywheel-merge-order-6HiPR2WG.js → flywheel-merge-order-0OnoosaO.js} +1 -1
- package/dist/dashboard/{flywheel-merge-order-BsNPMu0S.js → flywheel-merge-order-PG5op4j7.js} +2 -2
- package/dist/dashboard/{flywheel-merge-order-BsNPMu0S.js.map → flywheel-merge-order-PG5op4j7.js.map} +1 -1
- package/dist/dashboard/{harness-resolve-CzTFVZ5l.js → harness-resolve-C0HWj3H9.js} +2 -2
- package/dist/dashboard/{harness-resolve-CzTFVZ5l.js.map → harness-resolve-C0HWj3H9.js.map} +1 -1
- package/dist/dashboard/{identity-Dc7rQLln.js → identity-Cot1Hg4G.js} +2 -2
- package/dist/dashboard/{identity-Dc7rQLln.js.map → identity-Cot1Hg4G.js.map} +1 -1
- package/dist/dashboard/{inspect-agent-CP7Bu47O.js → inspect-agent-BDmLWbGx.js} +3 -3
- package/dist/dashboard/{inspect-agent-CP7Bu47O.js.map → inspect-agent-BDmLWbGx.js.map} +1 -1
- package/dist/dashboard/inspect-agent-BcOkA-hC.js +2 -0
- package/dist/dashboard/{issue-lifecycle-BPbczKyy.js → issue-lifecycle-1B_v8Rf5.js} +6 -6
- package/dist/dashboard/{issue-lifecycle-BPbczKyy.js.map → issue-lifecycle-1B_v8Rf5.js.map} +1 -1
- package/dist/dashboard/{issue-lifecycle-FaugTi39.js → issue-lifecycle-CIrfbIbj.js} +1 -1
- package/dist/dashboard/{issue-service-singleton-Dsad9NTt.js → issue-service-singleton-BVwPzjwF.js} +1 -1
- package/dist/dashboard/{issue-service-singleton-BrZQJLRI.js → issue-service-singleton-KcRrOiFD.js} +2 -2
- package/dist/dashboard/{issue-service-singleton-BrZQJLRI.js.map → issue-service-singleton-KcRrOiFD.js.map} +1 -1
- package/dist/dashboard/{jsonl-resolver-DQEe0DHM.js → jsonl-resolver-jxbif7MD.js} +3 -3
- package/dist/dashboard/{jsonl-resolver-DQEe0DHM.js.map → jsonl-resolver-jxbif7MD.js.map} +1 -1
- package/dist/dashboard/{kimi-code-Cx5YUJpS.js → kimi-code-BZh3NgcN.js} +1 -1
- package/dist/dashboard/{lifecycle-DZFHmpGI.js → lifecycle-CvcZ4_fA.js} +4 -4
- package/dist/dashboard/{lifecycle-DZFHmpGI.js.map → lifecycle-CvcZ4_fA.js.map} +1 -1
- package/dist/dashboard/{linear-mcp-auth-DylPJABZ.js → linear-mcp-auth-CZoj_-d7.js} +1 -1
- package/dist/dashboard/{linear-mcp-auth-BFlpMvWh.js → linear-mcp-auth-Dx74yCAY.js} +2 -2
- package/dist/dashboard/{linear-mcp-auth-BFlpMvWh.js.map → linear-mcp-auth-Dx74yCAY.js.map} +1 -1
- package/dist/dashboard/{memory-governor-B7uDWRo9.js → memory-governor-Bv3LAqk3.js} +5 -5
- package/dist/dashboard/{memory-governor-B7uDWRo9.js.map → memory-governor-Bv3LAqk3.js.map} +1 -1
- package/dist/dashboard/{merge-agent-ddEcZDZD.js → merge-agent-1zVLl6k_.js} +1 -1
- package/dist/dashboard/{merge-agent-ttRY053V.js → merge-agent-8zrQu48t.js} +8 -8
- package/dist/dashboard/{merge-agent-ttRY053V.js.map → merge-agent-8zrQu48t.js.map} +1 -1
- package/dist/dashboard/merge-ops-CFJaMPLu.js +2 -0
- package/dist/dashboard/{merge-train-f_un38UJ.js → merge-train-BYH_cF7G.js} +2 -2
- package/dist/dashboard/{merge-train-f_un38UJ.js.map → merge-train-BYH_cF7G.js.map} +1 -1
- package/dist/dashboard/{merge-train-deps-GPlHOjK6.js → merge-train-deps-BTfXM5AR.js} +2 -2
- package/dist/dashboard/{merge-train-deps-GPlHOjK6.js.map → merge-train-deps-BTfXM5AR.js.map} +1 -1
- package/dist/dashboard/{messaging-BasbftDu.js → messaging-Bk6vGsMw.js} +1 -1
- package/dist/dashboard/{orphan-proposed-reconciler-DFHdQSEg.js → orphan-proposed-reconciler-B898fk26.js} +6 -6
- package/dist/dashboard/{orphan-proposed-reconciler-DFHdQSEg.js.map → orphan-proposed-reconciler-B898fk26.js.map} +1 -1
- package/dist/dashboard/{orphan-proposed-reconciler-DzAxrM99.js → orphan-proposed-reconciler-Bx_aWS4B.js} +1 -1
- package/dist/dashboard/{parked-residue-DMtbxJml.js → parked-residue-q2kzrYSm.js} +9 -9
- package/dist/dashboard/{parked-residue-DMtbxJml.js.map → parked-residue-q2kzrYSm.js.map} +1 -1
- package/dist/dashboard/{post-merge-lifecycle-worker-jzN2g_EY.js → post-merge-lifecycle-worker-Cl7-uf4g.js} +2 -2
- package/dist/dashboard/{post-merge-lifecycle-worker-jzN2g_EY.js.map → post-merge-lifecycle-worker-Cl7-uf4g.js.map} +1 -1
- package/dist/dashboard/{project-resource-refresh-queue-cqOeJ0fB.js → project-resource-refresh-queue-O_dRfPHn.js} +1 -1
- package/dist/dashboard/{project-resource-refresh-queue-DebwjPPA.js → project-resource-refresh-queue-se3T9IYP.js} +4 -4
- package/dist/dashboard/{project-resource-refresh-queue-DebwjPPA.js.map → project-resource-refresh-queue-se3T9IYP.js.map} +1 -1
- package/dist/dashboard/{reconstruct-cache-Bb2Jg2DZ.js → reconstruct-cache-XpWC9L0F.js} +2 -2
- package/dist/dashboard/{reconstruct-cache-Bb2Jg2DZ.js.map → reconstruct-cache-XpWC9L0F.js.map} +1 -1
- package/dist/dashboard/{release-engine-78iCajIc.js → release-engine-BuzxOamf.js} +2 -2
- package/dist/dashboard/{release-engine-78iCajIc.js.map → release-engine-BuzxOamf.js.map} +1 -1
- package/dist/dashboard/{remote-completion-D5wzZCte.js → remote-completion-BCZgeKPS.js} +3 -3
- package/dist/dashboard/{remote-completion-D5wzZCte.js.map → remote-completion-BCZgeKPS.js.map} +1 -1
- package/dist/dashboard/{resolver-BGZh3lXA.js → resolver-BeNrqNou.js} +5 -5
- package/dist/dashboard/{resolver-BGZh3lXA.js.map → resolver-BeNrqNou.js.map} +1 -1
- package/dist/dashboard/{resource-pressure-patrol-Bg23rPV-.js → resource-pressure-patrol-Ccg_aCgO.js} +3 -3
- package/dist/dashboard/{resource-pressure-patrol-Bg23rPV-.js.map → resource-pressure-patrol-Ccg_aCgO.js.map} +1 -1
- package/dist/dashboard/{resume-BKyB4hA1.js → resume-KGCrn0DV.js} +1 -1
- package/dist/dashboard/{review-agent-ZdP-HmwY.js → review-agent-BmM6YMZW.js} +1 -1
- package/dist/dashboard/{review-agent-DHs-JngN.js → review-agent-DRWVU8fP.js} +13 -13
- package/dist/dashboard/{review-agent-DHs-JngN.js.map → review-agent-DRWVU8fP.js.map} +1 -1
- package/dist/dashboard/{review-convoy-liveness-BTMvU4CA.js → review-convoy-liveness-EvCDv8Q_.js} +3 -3
- package/dist/dashboard/{review-convoy-liveness-BTMvU4CA.js.map → review-convoy-liveness-EvCDv8Q_.js.map} +1 -1
- package/dist/dashboard/{review-run-recovery-BYalQzbh.js → review-run-recovery-Ctm48Snt.js} +1 -1
- package/dist/dashboard/{review-run-recovery-BvVG3oUr.js → review-run-recovery-DNDI58cM.js} +2 -2
- package/dist/dashboard/{review-run-recovery-BvVG3oUr.js.map → review-run-recovery-DNDI58cM.js.map} +1 -1
- package/dist/dashboard/{review-status-DXTFzCmR.js → review-status-FnlvQuPI.js} +1 -1
- package/dist/dashboard/{review-status-emit-C9YW6SQL.js → review-status-emit-CeMS31DX.js} +2 -2
- package/dist/dashboard/{review-status-emit-C9YW6SQL.js.map → review-status-emit-CeMS31DX.js.map} +1 -1
- package/dist/dashboard/{review-status-BegAr86G.js → review-status-yY5qt7Rl.js} +5 -5
- package/dist/dashboard/{review-status-BegAr86G.js.map → review-status-yY5qt7Rl.js.map} +1 -1
- package/dist/dashboard/{review-verdict-feedback-qlVBi19-.js → review-verdict-feedback-BNgRj1e3.js} +4 -4
- package/dist/dashboard/{review-verdict-feedback-qlVBi19-.js.map → review-verdict-feedback-BNgRj1e3.js.map} +1 -1
- package/dist/dashboard/{review-verdict-feedback-Dp139Lmv.js → review-verdict-feedback-D39cuSV1.js} +1 -1
- package/dist/dashboard/{review-verdict-writer-CJr_Ob0Y.js → review-verdict-writer-DvFfKAn-.js} +2 -2
- package/dist/dashboard/{review-verdict-writer-CJr_Ob0Y.js.map → review-verdict-writer-DvFfKAn-.js.map} +1 -1
- package/dist/dashboard/{review-verdict-writer-BVco3WoI.js → review-verdict-writer-PcGBkvJ4.js} +1 -1
- package/dist/dashboard/{sequence-io-iljWlslY.js → sequence-io-DZyschsf.js} +2 -2
- package/dist/dashboard/{sequence-io-iljWlslY.js.map → sequence-io-DZyschsf.js.map} +1 -1
- package/dist/dashboard/{sequencer-agent-BGGVKxtw.js → sequencer-agent-BXDw5cNx.js} +5 -5
- package/dist/dashboard/{sequencer-agent-BGGVKxtw.js.map → sequencer-agent-BXDw5cNx.js.map} +1 -1
- package/dist/dashboard/server.js +79 -72
- package/dist/dashboard/server.js.map +1 -1
- package/dist/dashboard/{service-kzhN0DLg.js → service-DP50Omvn.js} +33 -33
- package/dist/dashboard/{service-kzhN0DLg.js.map → service-DP50Omvn.js.map} +1 -1
- package/dist/dashboard/{smart-compaction-owKHJDbL.js → smart-compaction-944GL8PN.js} +1 -1
- package/dist/dashboard/{smart-compaction-C96F2aFQ.js → smart-compaction-BzDfF6yb.js} +2 -2
- package/dist/dashboard/{smart-compaction-C96F2aFQ.js.map → smart-compaction-BzDfF6yb.js.map} +1 -1
- package/dist/dashboard/{spawn-planning-session-CiJdOHTD.js → spawn-planning-session-BcVnHJjm.js} +1 -1
- package/dist/dashboard/{spawn-planning-session-FmJa6PCS.js → spawn-planning-session-CH8IYli7.js} +3 -3
- package/dist/dashboard/{spawn-planning-session-FmJa6PCS.js.map → spawn-planning-session-CH8IYli7.js.map} +1 -1
- package/dist/dashboard/{specialist-context-_ootmzn2.js → specialist-context-DhDTXVht.js} +3 -3
- package/dist/dashboard/{specialist-context-_ootmzn2.js.map → specialist-context-DhDTXVht.js.map} +1 -1
- package/dist/dashboard/{specialist-logs-BbAX221-.js → specialist-logs-C1bcnZ-G.js} +2 -2
- package/dist/dashboard/{specialist-logs-BbAX221-.js.map → specialist-logs-C1bcnZ-G.js.map} +1 -1
- package/dist/dashboard/{specialists-COZoYaiz.js → specialists-BKmReEWT.js} +53 -53
- package/dist/dashboard/{specialists-COZoYaiz.js.map → specialists-BKmReEWT.js.map} +1 -1
- package/dist/dashboard/{specialists-RPLJnYc1.js → specialists-Bvrvt0br.js} +12 -12
- package/dist/dashboard/{specialists-RPLJnYc1.js.map → specialists-Bvrvt0br.js.map} +1 -1
- package/dist/dashboard/{specialists-DTAOVZpU.js → specialists-CD6GEKXk.js} +1 -1
- package/dist/dashboard/{specialists-D8sYZnPb.js → specialists-eFWpJRBc.js} +1 -1
- package/dist/dashboard/{stall-sweeper-9bRBZhrQ.js → stall-sweeper-D3fZvg7O.js} +3 -3
- package/dist/dashboard/{stall-sweeper-9bRBZhrQ.js.map → stall-sweeper-D3fZvg7O.js.map} +1 -1
- package/dist/dashboard/{state-migrate-J3tFJnbd.js → state-migrate-KZ2goscQ.js} +2 -2
- package/dist/dashboard/{state-migrate-J3tFJnbd.js.map → state-migrate-KZ2goscQ.js.map} +1 -1
- package/dist/dashboard/{stuck-remediation-BvTK_NYN.js → stuck-remediation-Cb2V1roj.js} +1 -1
- package/dist/dashboard/{stuck-remediation-4Rf_xMEb.js → stuck-remediation-CnoV0kcv.js} +5 -5
- package/dist/dashboard/{stuck-remediation-4Rf_xMEb.js.map → stuck-remediation-CnoV0kcv.js.map} +1 -1
- package/dist/dashboard/{summary-fork-PLcckTRE.js → summary-fork-CxuNBM0F.js} +2 -2
- package/dist/dashboard/{summary-fork-D-cylknJ.js → summary-fork-lC2vMXly.js} +4 -4
- package/dist/dashboard/{summary-fork-D-cylknJ.js.map → summary-fork-lC2vMXly.js.map} +1 -1
- package/dist/dashboard/{teardown-workspace-CTL16rnG.js → teardown-workspace-C-9murBw.js} +4 -4
- package/dist/dashboard/{teardown-workspace-CTL16rnG.js.map → teardown-workspace-C-9murBw.js.map} +1 -1
- package/dist/dashboard/{test-agent-queue-CMgYZiR_.js → test-agent-queue-YCBRzaAB.js} +3 -3
- package/dist/dashboard/{test-agent-queue-CMgYZiR_.js.map → test-agent-queue-YCBRzaAB.js.map} +1 -1
- package/dist/dashboard/{uat-failure-feedback-DS6W9X3I.js → uat-failure-feedback-B-1mL8Zp.js} +3 -3
- package/dist/dashboard/{uat-failure-feedback-DS6W9X3I.js.map → uat-failure-feedback-B-1mL8Zp.js.map} +1 -1
- package/dist/dashboard/{uat-promote-verification-CRsIHE_q.js → uat-promote-verification-BEBPcpPg.js} +2 -2
- package/dist/dashboard/{uat-promote-verification-CRsIHE_q.js.map → uat-promote-verification-BEBPcpPg.js.map} +1 -1
- package/dist/dashboard/{uat-train-BoA9r6_I.js → uat-train-BFo3OQUB.js} +1 -1
- package/dist/dashboard/{uat-train-Cd8CJeNe.js → uat-train-COgoJomz.js} +8 -8
- package/dist/dashboard/{uat-train-Cd8CJeNe.js.map → uat-train-COgoJomz.js.map} +1 -1
- package/dist/dashboard/{validate-BdmvTIym.js → validate-069_N1e2.js} +2 -2
- package/dist/dashboard/{validate-BdmvTIym.js.map → validate-069_N1e2.js.map} +1 -1
- package/dist/dashboard/{verification-runner-BO0K3xNA.js → verification-runner-CQnZyC5A.js} +1 -1
- package/dist/dashboard/{verification-runner-fEY1Qj-l.js → verification-runner-fy3wNrWn.js} +4 -4
- package/dist/dashboard/{verification-runner-fEY1Qj-l.js.map → verification-runner-fy3wNrWn.js.map} +1 -1
- package/dist/dashboard/workflows-Bk6Kt0ZQ.js +2 -0
- package/dist/dashboard/{workflows-CxVnygYU.js → workflows-N4QMkDzN.js} +10 -10
- package/dist/dashboard/{workflows-CxVnygYU.js.map → workflows-N4QMkDzN.js.map} +1 -1
- package/dist/dashboard/{workspaces-Cpz0xJWQ.js → workspaces-DmoyfHoU.js} +33 -33
- package/dist/dashboard/{workspaces-Cpz0xJWQ.js.map → workspaces-DmoyfHoU.js.map} +1 -1
- package/dist/{lifecycle-C6k0HWEO.js → lifecycle-DCFcjg7y.js} +1 -1
- package/dist/{lifecycle-D5oRpvvB.js → lifecycle-GkaHQJNK.js} +2 -2
- package/dist/{lifecycle-D5oRpvvB.js.map → lifecycle-GkaHQJNK.js.map} +1 -1
- package/dist/{merge-agent-Drufc7tU.js → merge-agent-CogNFasY.js} +2 -2
- package/dist/{merge-agent-Drufc7tU.js.map → merge-agent-CogNFasY.js.map} +1 -1
- package/dist/{merge-agent-CBOVY_eC.js → merge-agent-eteKQ9Rb.js} +1 -1
- package/package.json +1 -1
- package/dist/build-info-BgrwLUy6.js +0 -2
- package/dist/dashboard/agent-state-Ci6PhjOE.js.map +0 -1
- package/dist/dashboard/build-info-CYlxvZSQ.js +0 -2
- package/dist/dashboard/cli-ZAS502uC.js +0 -2
- package/dist/dashboard/conversation-service-Dlks_hSS.js +0 -3
- package/dist/dashboard/inspect-agent-DTuSg95i.js +0 -2
- package/dist/dashboard/merge-ops-Bgc-EScO.js +0 -2
- package/dist/dashboard/workflows-CgGNyl1z.js +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflows-CxVnygYU.js","names":["execFileAsync","promisify","execFile","promisify","exec","join"],"sources":["../../src/lib/lifecycle/dod-gate.ts","../../src/lib/lifecycle/residue.ts","../../src/lib/lifecycle/workflows.ts"],"sourcesContent":["/** Mechanical Definition-of-Done row checks. */\n\nimport { execFile } from 'node:child_process';\nimport { userInfo } from 'node:os';\nimport { resolve } from 'node:path';\nimport { promisify } from 'node:util';\nimport type { CanonicalState } from '../../core/state-mapping.js';\nimport { Effect } from 'effect';\nimport { listRunningAgents, type AgentState } from '../agents.js';\nimport { getDashboardApiUrlSync } from '../config.js';\nimport { rehydrateHeadAnchor } from '../git-utils.js';\nimport { getReviewStatus, setReviewStatusSync, type ReviewStatus, type ReviewStatusUpdate } from '../review-status.js';\nimport type { StrikeLandingStatus } from '../strike-landing.js';\nimport {\n getProjectConfigFromWorkspacePath,\n readIssueRecord,\n resolveProjectForIssue,\n type PanIssuePipelineRecord,\n type PanIssueShipRecord,\n} from '../pan-dir/record.js';\nimport type { ProjectConfig } from '../projects.js';\nimport { getAutoCloseOutCanonicalState } from '../cloister/deacon-canonical-state.js';\nimport { aggregateGenerationShipStatus, loadShipRecords } from '../cloister/ship-status.js';\nimport { isTrackerIssueClosed } from '../cloister/issue-closed.js';\nimport {\n fetchCommitCheckRuns,\n fetchIssuePullRequest,\n fetchRequiredStatusChecks,\n type CommitCheckRuns,\n} from '../overdeck/pull-requests.js';\nimport { listUatGenerationsSync, type UatGeneration } from '../overdeck/merge-sync.js';\nimport { getForgeAdapter } from '../forge.js';\nimport { resolveProjectReposForIssueSync } from '../project-repos.js';\nimport {\n gatherIssueBranchContainment,\n type IssueBranchContainment,\n} from '../pipeline-membership-gather.js';\nimport {\n acceptFlagFor,\n BRANCH_ABSENT_MERGE_ERROR,\n canAcceptDodMisses,\n DOD_ROWS,\n type DodGateResult,\n type DodRowId,\n type DodRowResult,\n} from './dod.js';\nimport type { LifecycleContext, StepResult } from './types.js';\n\nconst execFileAsync = promisify(execFile);\n\ntype StatusSource =\n | { source: 'live'; status: ReviewStatus }\n | { source: 'journal'; status: PanIssuePipelineRecord }\n | null;\ntype Awaitable<T> = T | Promise<T>;\n\nexport interface DodStatusRowDeps {\n getReviewStatus: (issueId: string) => Awaitable<ReviewStatus | null>;\n getJournalStatus: (issueId: string) => Awaitable<PanIssuePipelineRecord | null>;\n}\n\nexport interface MergedDodRowResult extends DodRowResult {\n mergedAt?: string;\n mergeCommit?: string;\n evidence?: 'branch-containment';\n containedStrikeHead?: string;\n}\n\ninterface MergedForgeArtifact {\n forge: string;\n url?: string;\n id?: string;\n}\n\ninterface MergedRowDeps {\n verifyMerged: (ctx: LifecycleContext) => Promise<StepResult>;\n readPullRequest: (ctx: LifecycleContext, branchName: string) => Promise<{\n number?: number;\n state?: string;\n mergedAt?: string;\n mergeCommit?: { oid?: string } | string | null;\n }>;\n readDurableMerges?: (ctx: LifecycleContext) => Promise<string[]>;\n readMergedForgeArtifacts?: (ctx: LifecycleContext) => Promise<MergedForgeArtifact[]>;\n readBranchContainment?: (ctx: LifecycleContext) => Promise<IssueBranchContainment>;\n}\n\nconst defaultMergedRowDeps: MergedRowDeps = {\n verifyMerged: async () => ({\n step: 'close-out:verify-merged',\n success: false,\n skipped: false,\n error: 'merge verifier was not supplied',\n }),\n readPullRequest: async (ctx, branchName) => {\n if (!ctx.github) return {};\n void branchName;\n const response = await fetchIssuePullRequest(ctx.issueId);\n if (response.error) throw new Error(response.error);\n return response.pr ?? {};\n },\n readDurableMerges: async ctx => {\n const project = resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath);\n return (await readIssueRecord(project, ctx.issueId))?.closeOut.merges ?? [];\n },\n readMergedForgeArtifacts: async ctx => {\n const repos = resolveProjectReposForIssueSync(ctx.issueId)?.filter(repo => repo.required) ?? [];\n const artifacts = await Promise.all(repos.map(repo => getForgeAdapter(repo.forge).findMergedArtifact({\n sourceBranch: repo.sourceBranch,\n targetBranch: repo.targetBranch,\n cwd: repo.repoPath,\n })));\n return artifacts.filter((artifact): artifact is NonNullable<typeof artifact> => artifact !== null);\n },\n readBranchContainment: async ctx => {\n const project = resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath);\n if (!project) return { unmergedRefs: [], mergedWorkRefs: [], pointerRefs: [] };\n return gatherIssueBranchContainment(project, ctx.issueId);\n },\n};\n\ninterface PostMergeRowDeps {\n readCanonicalState: (ctx: LifecycleContext) => Promise<CanonicalState | null>;\n readMergeStatus: (issueId: string) => Awaitable<string | undefined>;\n listAgents: () => Awaitable<Array<Pick<AgentState, 'id' | 'issueId' | 'role' | 'status'>>>;\n readStrikeLanded?: (issueId: string) => Awaitable<boolean>;\n}\n\nconst defaultPostMergeRowDeps: PostMergeRowDeps = {\n readStrikeLanded: async issueId => strikeLanded((await loadStatus(issueId, defaultDeps))?.status),\n readCanonicalState: async ctx => {\n return getAutoCloseOutCanonicalState(ctx.issueId) as Promise<CanonicalState | null>;\n },\n // PAN-3025: same live→journal fallback as the verdict rows — after close-out\n // clears live status, the durable record is the only place mergeStatus survives.\n readMergeStatus: async issueId => (await loadStatus(issueId, defaultDeps))?.status.mergeStatus,\n listAgents: async () => Effect.runPromise(listRunningAgents()),\n};\n\nexport const DEFAULT_MAIN_VERIFY_REQUIRED_CHECKS = ['test', 'lint', 'build (22)', 'guard'];\n\ninterface MainVerifyRowDeps {\n readCheckRuns: (ctx: LifecycleContext, commit: string) => Promise<CommitCheckRuns>;\n readRequiredChecks?: (ctx: LifecycleContext) => Promise<string[]>;\n /**\n * PAN-3202: default-branch commits that contain `mergeCommit`, newest first.\n * These are the candidate heads for the later-green-run evidence form.\n */\n readContainingDefaultBranchCommits?: (ctx: LifecycleContext, mergeCommit: string) => Promise<string[]>;\n}\n\nfunction configuredMainVerifyChecks(ctx: LifecycleContext): string[] {\n const project = resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath);\n return project?.main_verify_required_checks?.length\n ? project.main_verify_required_checks\n : DEFAULT_MAIN_VERIFY_REQUIRED_CHECKS;\n}\n\nexport async function readContainingDefaultBranchCommits(\n ctx: LifecycleContext,\n mergeCommit: string,\n): Promise<string[]> {\n const options = { cwd: ctx.projectPath, encoding: 'utf-8' as const, timeout: 10000, maxBuffer: 8 * 1024 * 1024 };\n try {\n await execFileAsync('git', ['merge-base', '--is-ancestor', mergeCommit, 'origin/main'], options);\n } catch (error) {\n if (typeof error === 'object' && error !== null && 'code' in error && error.code === 1) return [];\n throw error;\n }\n\n const { stdout } = await execFileAsync(\n 'git',\n ['rev-list', '--first-parent', `${mergeCommit}..origin/main`],\n options,\n );\n return stdout.split('\\n').map(line => line.trim()).filter(Boolean);\n}\n\nconst defaultMainVerifyRowDeps: MainVerifyRowDeps = {\n readCheckRuns: async (ctx, commit) => {\n if (!ctx.github) return { total: 0, names: [], successful: [], failed: [], pending: [] };\n return fetchCommitCheckRuns(ctx.github.owner, ctx.github.repo, commit);\n },\n readRequiredChecks: async ctx => {\n if (!ctx.github) return configuredMainVerifyChecks(ctx);\n return fetchRequiredStatusChecks(ctx.github.owner, ctx.github.repo, 'main', configuredMainVerifyChecks(ctx));\n },\n readContainingDefaultBranchCommits,\n};\n\ninterface ShipRowDeps {\n readProject: (ctx: LifecycleContext) => ProjectConfig | null;\n readPipeline: (ctx: LifecycleContext) => Promise<PanIssuePipelineRecord | null>;\n findPromotedBatch: (ctx: LifecycleContext) => UatGeneration | null;\n readBatchShip: (project: ProjectConfig, generation: UatGeneration) => Promise<PanIssueShipRecord | null>;\n}\n\nconst defaultShipRowDeps: ShipRowDeps = {\n readProject: ctx => resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath),\n readPipeline: async ctx => {\n const project = resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath);\n return (await readIssueRecord(project, ctx.issueId))?.pipeline ?? null;\n },\n findPromotedBatch: ctx => {\n const project = resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath);\n return listUatGenerationsSync({ projectRoot: resolve(project.path), statuses: ['promoted'] })\n .find(generation => generation.members.some(member => member.issueId.toUpperCase() === ctx.issueId.toUpperCase())) ?? null;\n },\n readBatchShip: async (project, generation) => aggregateGenerationShipStatus(\n generation,\n await loadShipRecords(project, [generation]),\n ),\n};\n\ninterface DeployRowDeps {\n dashboardUrl: () => string;\n readJson: (url: string) => Promise<Record<string, unknown>>;\n commitContains: (repoRoot: string, mergeCommit: string, buildCommit: string) => Promise<boolean>;\n}\n\nexport interface TerminalVerdictSettlement {\n trackerClosed: boolean;\n landedWork: boolean;\n mainVerifyStatus: DodRowResult['status'];\n}\n\nexport interface EvaluateDodGateDeps {\n review: (issueId: string, settlement?: TerminalVerdictSettlement) => DodRowResult | Promise<DodRowResult>;\n tests: (issueId: string, settlement?: TerminalVerdictSettlement) => DodRowResult | Promise<DodRowResult>;\n verification: (issueId: string, settlement?: TerminalVerdictSettlement) => DodRowResult | Promise<DodRowResult>;\n merged: (ctx: LifecycleContext) => MergedDodRowResult | Promise<MergedDodRowResult>;\n postMerge: (ctx: LifecycleContext, merged?: MergedDodRowResult) => DodRowResult | Promise<DodRowResult>;\n mainVerify: (ctx: LifecycleContext, mergeCommit?: string) => DodRowResult | Promise<DodRowResult>;\n ship: (ctx: LifecycleContext) => DodRowResult | Promise<DodRowResult>;\n deploy: (ctx: LifecycleContext, merge: {\n mergedAt?: string;\n mergeCommit?: string;\n mergedRowStatus?: DodRowResult['status'];\n mainVerifyRowStatus?: DodRowResult['status'];\n }) => DodRowResult | Promise<DodRowResult>;\n trackerClosed?: (issueId: string) => Awaitable<boolean>;\n reconcileContainedStrike?: (ctx: LifecycleContext, merged: MergedDodRowResult) => Awaitable<void>;\n now: () => string;\n}\n\nconst defaultEvaluateDodGateDeps: EvaluateDodGateDeps = {\n review: (issueId, settlement) => checkReviewRow(issueId, defaultDeps, settlement),\n tests: (issueId, settlement) => checkTestsRow(issueId, defaultDeps, settlement),\n verification: (issueId, settlement) => checkVerificationRow(issueId, defaultDeps, settlement),\n merged: checkMergedRow,\n postMerge: checkPostMergeRow,\n mainVerify: checkMainVerifyRow,\n ship: checkShipRow,\n deploy: checkDeployRow,\n trackerClosed: isTrackerIssueClosed,\n reconcileContainedStrike,\n now: () => new Date().toISOString(),\n};\n\nconst defaultDeployRowDeps: DeployRowDeps = {\n dashboardUrl: getDashboardApiUrlSync,\n readJson: async url => {\n const response = await fetch(url, { signal: AbortSignal.timeout(3000) });\n if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);\n return response.json() as Promise<Record<string, unknown>>;\n },\n commitContains: async (repoRoot, mergeCommit, buildCommit) => {\n try {\n await execFileAsync('git', ['merge-base', '--is-ancestor', mergeCommit, buildCommit], {\n cwd: repoRoot,\n encoding: 'utf-8',\n timeout: 10000,\n });\n return true;\n } catch {\n return false;\n }\n },\n};\n\nconst defaultDeps: DodStatusRowDeps = {\n getReviewStatus: issueId => Effect.runPromise(getReviewStatus(issueId)),\n getJournalStatus: async issueId => {\n const project = resolveProjectForIssue(issueId) ?? getProjectConfigFromWorkspacePath(process.cwd());\n return (await readIssueRecord(project, issueId))?.pipeline ?? null;\n },\n};\n\nfunction rowDefinition(id: DodRowId) {\n const row = DOD_ROWS.find(candidate => candidate.id === id);\n if (!row) throw new Error(`Unknown DoD row: ${id}`);\n return row;\n}\n\nfunction result(id: DodRowId, status: DodRowResult['status'], observed: string): DodRowResult {\n const row = rowDefinition(id);\n return { id, num: row.num, title: row.title, expected: row.expected, observed, status };\n}\n\nasync function loadStatus(issueId: string, deps: DodStatusRowDeps): Promise<StatusSource> {\n try {\n const live = await deps.getReviewStatus(issueId);\n if (live) return { source: 'live', status: live };\n const journal = await deps.getJournalStatus(issueId);\n return journal ? { source: 'journal', status: journal } : null;\n } catch {\n return null;\n }\n}\n\n/**\n * PAN-3180: `landed` is the strike path's own durable statement that this issue's\n * work reached main through `strike/<id>`. It is written by the server merge door\n * (`mergeCompletionStatus` in `merge-strike.ts`) and mirrored into the per-issue\n * record's `pipeline` block, so it outlives review-status clearing. Every earlier\n * landing state (`ready`/`landing`/`recovering`/`needs_you`) is a strike still in\n * flight and earns no waiver.\n */\nfunction strikeLanded(status: StrikeLandingStatus | null | undefined): boolean {\n return status?.strikeLandingState === 'landed';\n}\n\n/**\n * PAN-3180: a strike dispatches neither a review nor a test specialist — that\n * bypass is the whole point of the path. So for a strike-landed issue these rows\n * are `skip` (deliberately not run), never `pass` (ran and succeeded) and never\n * `miss` (outstanding). A reader of the close-out record still sees the real\n * verdict and the reason it was never produced.\n */\nconst STRIKE_BYPASS_NOTE: Record<'review' | 'tests', string> = {\n review: 'no review specialist is dispatched for a strike',\n tests: 'no test specialist is dispatched for a strike',\n};\n\n/**\n * A specialist that ran and returned a negative verdict is a different fact from\n * one that was never dispatched, so the strike waiver never covers it — those\n * still block until an operator records an explicit `--accept-<row>` override.\n */\nconst NEGATIVE_VERDICTS = new Set(['failed', 'blocked', 'dispatch_failed']);\n\n/**\n * PAN-3187: a tracker-closed issue with landed work cannot produce a verdict it\n * never produced while active. Negative test/verification verdicts settle only\n * when main verification proves the landed state green; review negatives remain.\n */\nfunction terminalVerdictSettlement(\n id: 'review' | 'tests' | 'verification',\n value: string | undefined,\n observed: string,\n settlement?: TerminalVerdictSettlement,\n): DodRowResult | null {\n if (!settlement?.trackerClosed || !settlement.landedWork) return null;\n if (!NEGATIVE_VERDICTS.has(value ?? '')) {\n return result(\n id,\n 'skip',\n `${observed}; settled because the tracker issue is closed and merged work is landed`,\n );\n }\n if (id !== 'review' && settlement.mainVerifyStatus === 'pass') {\n return result(\n id,\n 'skip',\n `${observed}; superseded because the tracker issue is closed, merged work is landed, and main verification passed`,\n );\n }\n return null;\n}\n\nasync function checkVerdict(\n issueId: string,\n id: 'review' | 'tests',\n field: 'reviewStatus' | 'testStatus',\n deps: DodStatusRowDeps,\n settlement?: TerminalVerdictSettlement,\n): Promise<DodRowResult> {\n const loaded = await loadStatus(issueId, deps);\n if (!loaded) return result(id, 'miss', 'no review status or journal record found');\n\n const value = loaded.status[field];\n const source = loaded.source === 'journal' ? ' from pipeline journal' : '';\n const policy = value === 'skipped' ? ' (skipped per issue policy)' : '';\n const observed = `${field}: ${value ?? 'missing'}${source}${policy}`;\n if (value === 'passed' || value === 'skipped') return result(id, 'pass', observed);\n if (strikeLanded(loaded.status) && !NEGATIVE_VERDICTS.has(value ?? '')) {\n return result(\n id,\n 'skip',\n `${observed}; skipped by the strike path (strikeLandingState: landed) — ${STRIKE_BYPASS_NOTE[id]}`,\n );\n }\n return terminalVerdictSettlement(id, value, observed, settlement) ?? result(id, 'miss', observed);\n}\n\nexport function checkReviewRow(\n issueId: string,\n deps: DodStatusRowDeps = defaultDeps,\n settlement?: TerminalVerdictSettlement,\n): Promise<DodRowResult> {\n return checkVerdict(issueId, 'review', 'reviewStatus', deps, settlement);\n}\n\nexport function checkTestsRow(\n issueId: string,\n deps: DodStatusRowDeps = defaultDeps,\n settlement?: TerminalVerdictSettlement,\n): Promise<DodRowResult> {\n return checkVerdict(issueId, 'tests', 'testStatus', deps, settlement);\n}\n\nexport async function checkVerificationRow(\n issueId: string,\n deps: DodStatusRowDeps = defaultDeps,\n settlement?: TerminalVerdictSettlement,\n): Promise<DodRowResult> {\n const loaded = await loadStatus(issueId, deps);\n if (!loaded) return result('verification', 'miss', 'no review status or journal record found');\n\n // PAN-3067: the verdict alone decides this row, exactly like rows 1 and 2.\n // `lastVerifiedCommit` is a best-effort optimization anchor — verification-runner.ts\n // snapshots it inside a try/catch and spreads the field conditionally, and a\n // `skipped` verdict never has one at all — so its absence says nothing about\n // whether verification ran. Requiring it made every issue whose anchor was\n // never written un-closable. The observed string always names the anchor's\n // presence or absence so a reader can never mistake it for a hidden condition.\n const value = loaded.status.verificationStatus;\n const commit = loaded.status.lastVerifiedCommit;\n const accepted = value === 'passed' || value === 'skipped';\n const notes: string[] = [];\n if (loaded.source === 'journal') notes.push('from pipeline journal');\n if (value === 'skipped') notes.push('skipped per issue policy');\n if (accepted && !commit) notes.push('no lastVerifiedCommit recorded');\n const observed = `verificationStatus: ${value ?? 'missing'}${commit ? ` at ${commit}` : ''}${\n notes.length > 0 ? ` (${notes.join('; ')})` : ''\n }`;\n if (accepted) return result('verification', 'pass', observed);\n // An out-of-band merge never enters merge-ops, so the CI-green skip cannot\n // record its normal verification verdict. Once rows 4 and 6 prove the landed\n // work and main CI green, that evidence satisfies row 3 without an override.\n if (value === undefined && settlement?.landedWork && settlement.mainVerifyStatus === 'pass') {\n return result('verification', 'pass', `${observed}; verification satisfied by green main CI after landing`);\n }\n return terminalVerdictSettlement('verification', value, observed, settlement) ??\n result('verification', 'miss', observed);\n}\n\nexport async function checkMergedRow(\n ctx: LifecycleContext,\n deps: MergedRowDeps = defaultMergedRowDeps,\n): Promise<MergedDodRowResult> {\n const verified: StepResult = await deps.verifyMerged(ctx).catch(error => ({\n step: 'close-out:verify-merged',\n success: false,\n skipped: false,\n error: error instanceof Error ? error.message : String(error),\n }));\n const detail = verified.details?.join('; ');\n const observed = detail || verified.error || (verified.skipped ? 'issue already closed on forge' : 'merge not verified');\n const merged = result('merged', verified.success || verified.skipped ? 'pass' : 'miss', observed) as MergedDodRowResult;\n const branchAbsent = verified.error === BRANCH_ABSENT_MERGE_ERROR;\n let durableMerges: string[] = [];\n let forgeArtifacts: MergedForgeArtifact[] = [];\n\n if (branchAbsent) {\n try {\n durableMerges = await (deps.readDurableMerges ?? defaultMergedRowDeps.readDurableMerges!)(ctx);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n merged.observed = `${merged.observed}; durable merge evidence unavailable: ${message}`;\n }\n if (!ctx.github && deps.readMergedForgeArtifacts) {\n try {\n forgeArtifacts = await deps.readMergedForgeArtifacts(ctx);\n const labels = forgeArtifacts.map(artifact => {\n const label = artifact.forge === 'gitlab'\n ? (artifact.id ? `MR !${artifact.id}` : 'GitLab MR')\n : (artifact.id ? `PR #${artifact.id}` : 'GitHub PR');\n return `${label} merged${artifact.url ? ` (${artifact.url})` : ''}`;\n });\n if (labels.length > 0) merged.observed = `${merged.observed}; ${labels.join('; ')}`;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n merged.observed = `${merged.observed}; forge artifact evidence unavailable: ${message}`;\n }\n }\n }\n\n let pullRequestState: string | undefined;\n if (ctx.github) {\n try {\n const branchName = `feature/${ctx.issueId.toLowerCase()}`;\n const pullRequest = await deps.readPullRequest(ctx, branchName);\n pullRequestState = pullRequest.state;\n merged.mergedAt = pullRequest.mergedAt;\n merged.mergeCommit = typeof pullRequest.mergeCommit === 'string'\n ? pullRequest.mergeCommit\n : pullRequest.mergeCommit?.oid;\n const number = pullRequest.number ? `PR #${pullRequest.number}` : 'PR';\n const state = pullRequest.state ?? 'state unknown';\n const at = pullRequest.mergedAt ? ` at ${pullRequest.mergedAt}` : '';\n merged.observed = `${merged.observed}; ${number} ${state}${at}`;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n merged.observed = `${merged.observed}; forge metadata unavailable: ${message}`;\n }\n }\n\n if (branchAbsent) {\n const forgeMerged = pullRequestState?.toUpperCase() === 'MERGED';\n if (durableMerges.length > 0 || forgeMerged || forgeArtifacts.length > 0) {\n merged.status = 'pass';\n if (durableMerges.length > 0) {\n merged.observed = `${merged.observed}; durable close-out record contains ${durableMerges.length} merge artifact(s)`;\n }\n } else {\n merged.observed = `${merged.observed}; no merged forge artifact or durable close-out merge record found`;\n }\n }\n\n // A successful ancestry check proves that the branch head reached main, but\n // only containment can identify that result as a non-PR landing for row 5.\n const needsContainmentEvidence = merged.status === 'miss' || (\n verified.success &&\n ctx.github !== undefined &&\n pullRequestState?.toUpperCase() !== 'MERGED'\n );\n if (needsContainmentEvidence) {\n try {\n const containment = await (deps.readBranchContainment ?? defaultMergedRowDeps.readBranchContainment!)(ctx);\n if (containment.mergedWorkRefs.length > 0 && containment.unmergedRefs.length === 0) {\n merged.status = 'pass';\n merged.evidence = 'branch-containment';\n merged.observed = `${merged.observed}; branch work contained in default branch with no merged PR — non-PR landing (membership L2-work lens): ${containment.mergedWorkRefs.join(', ')}`;\n const strikeSuffix = `:strike/${ctx.issueId.toLowerCase()}`;\n merged.containedStrikeHead = containment.mergedWorkHeads\n ?.find(candidate => candidate.ref.endsWith(strikeSuffix))?.head;\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n merged.observed = `${merged.observed}; branch containment evidence unavailable: ${message}`;\n }\n }\n\n return merged;\n}\n\nconst NEGATIVE_STRIKE_VERDICTS = new Set(['failed', 'blocked', 'dispatch_failed']);\n\n/**\n * Reconcile an out-of-band strike landing only when its durable ready marker\n * names the exact strike tip proven contained in main. This is the evidence a\n * normal merge-door landing consumes before it clears the marker.\n */\nexport async function reconcileContainedStrike(\n ctx: LifecycleContext,\n merged: MergedDodRowResult,\n deps: {\n getStatus?: (issueId: string) => Awaitable<ReviewStatus | null>;\n setStatus?: typeof setReviewStatusSync;\n } = {},\n): Promise<void> {\n const head = merged.evidence === 'branch-containment' ? merged.containedStrikeHead : undefined;\n if (!head) return;\n const current = await (deps.getStatus ?? (issueId => Effect.runPromise(getReviewStatus(issueId))))(ctx.issueId);\n if (!current || current.strikeReadyHead !== head) return;\n\n const update: ReviewStatusUpdate = {};\n if (!NEGATIVE_STRIKE_VERDICTS.has(current.reviewStatus)) update.reviewStatus = 'passed';\n if (!NEGATIVE_STRIKE_VERDICTS.has(current.testStatus)) update.testStatus = 'passed';\n if (!NEGATIVE_STRIKE_VERDICTS.has(current.verificationStatus ?? '')) {\n update.verificationStatus = 'passed';\n update.verificationNotes = `Contained strike ${head} matched durable strike readiness evidence`;\n update.lastVerifiedCommit = rehydrateHeadAnchor(head);\n }\n update.mergeStatus = 'merged';\n update.strikeLandingState = 'landed';\n update.strikeReadyHead = undefined;\n update.strikeReadyAt = undefined;\n\n const changed = Object.entries(update).some(([key, value]) => current[key as keyof ReviewStatus] !== value);\n if (changed) (deps.setStatus ?? setReviewStatusSync)(ctx.issueId, update);\n}\n\nexport async function checkPostMergeRow(\n ctx: LifecycleContext,\n merged?: MergedDodRowResult,\n deps: PostMergeRowDeps = defaultPostMergeRowDeps,\n): Promise<DodRowResult> {\n try {\n const canonicalState = await deps.readCanonicalState(ctx);\n const mergeStatus = await deps.readMergeStatus(ctx.issueId);\n const issueId = ctx.issueId.toUpperCase();\n const runningAgents = (await deps.listAgents()).filter(agent =>\n agent.issueId.toUpperCase() === issueId &&\n (agent.role === 'work' || agent.role === 'plan') &&\n (agent.status === 'starting' || agent.status === 'running'),\n );\n const stateObserved = canonicalState\n ? `canonical state: ${canonicalState}`\n : `canonical state unavailable; mergeStatus: ${mergeStatus ?? 'missing'}`;\n const agentsObserved = runningAgents.length > 0\n ? `running agents: ${runningAgents.map(agent => agent.id).join(', ')}`\n : 'no running work/planning agents';\n // PAN-3188 (row 5): terminal canonical states settle this row. 'done'\n // proves the post-merge lifecycle already ran to close-out; 'canceled'\n // makes it moot. Requiring the transient verifying_on_main marker here\n // wedges every re-evaluated terminal issue (the 11 closed issues that\n // blocked close-out sweeps on 'canonical state: done').\n if (canonicalState === 'done') {\n return result(\n 'post-merge',\n runningAgents.length === 0 ? 'pass' : 'miss',\n `terminal canonical state: done — the post-merge lifecycle already ran to close-out; ${agentsObserved}`,\n );\n }\n if (canonicalState === 'canceled') {\n return result(\n 'post-merge',\n runningAgents.length === 0 ? 'skip' : 'miss',\n `terminal canonical state: canceled — post-merge lifecycle not applicable; ${agentsObserved}`,\n );\n }\n const lifecycleObserved = canonicalState === 'verifying_on_main' || mergeStatus === 'merged';\n // PAN-3180: `postMergeLifecycle()` is the work-agent handoff — it pauses the\n // work/planning agents, stops the workspace stack, and applies\n // `verifying-on-main`. A strike has no work agent to pause and its landing is\n // owned by the Deacon's merge door, so the marker this row looks for is never\n // written and its absence proves nothing. What a strike does still owe is\n // quiescence, so a live work/planning agent remains a real miss.\n if (!lifecycleObserved && await (deps.readStrikeLanded ?? defaultPostMergeRowDeps.readStrikeLanded!)(ctx.issueId)) {\n return result(\n 'post-merge',\n runningAgents.length === 0 ? 'skip' : 'miss',\n `strike landing (strikeLandingState: landed) — the work-agent post-merge handoff is not the strike path's lifecycle; ${stateObserved}; ${agentsObserved}`,\n );\n }\n if (!lifecycleObserved && merged?.evidence === 'branch-containment') {\n return result(\n 'post-merge',\n runningAgents.length === 0 ? 'pass' : 'miss',\n `non-PR landing (branch-containment evidence) — post-merge lifecycle not applicable; ${agentsObserved}`,\n );\n }\n return result(\n 'post-merge',\n lifecycleObserved && runningAgents.length === 0 ? 'pass' : 'miss',\n `${stateObserved}; ${agentsObserved}`,\n );\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return result('post-merge', 'miss', `post-merge evidence unavailable: ${message}`);\n }\n}\n\n/**\n * PAN-3202: how many default-branch heads above the merge commit are probed for\n * the later-green-run evidence form. The tip of main is often mid-CI, so a single\n * candidate would usually read as pending; five reaches back far enough to find\n * the newest concluded green head without turning one row into a check-run storm.\n */\nconst LATER_GREEN_CANDIDATE_LIMIT = 5;\n\n/**\n * PAN-3202: the fallback evidence form for DoD row 6. When a merge lands inside a\n * red-main window, the merge commit's own check-runs can never turn green, so the\n * row was permanently unsatisfiable even after main went green hundreds of times\n * with the commit included. A later default-branch head whose check-runs all\n * concluded green, and which contains the merge commit, is strictly stronger\n * evidence than the original run: it proves main is healthy *with* the merge in it,\n * which is exactly what \"verified on main\" means. Row 7 (deploy) already reasons\n * this way through the same containment relation.\n */\nfunction requiredChecksOutcome(checks: CommitCheckRuns, required: string[]): {\n missing: string[];\n unsuccessful: string[];\n} {\n return {\n missing: required.filter(check => !checks.names.includes(check)),\n unsuccessful: required.filter(check => checks.names.includes(check) && !checks.successful.includes(check)),\n };\n}\n\nfunction hasRequiredChecksGreen(checks: CommitCheckRuns, required: string[]): boolean {\n const outcome = requiredChecksOutcome(checks, required);\n return outcome.missing.length === 0 && outcome.unsuccessful.length === 0;\n}\n\nasync function findLaterGreenDefaultBranchRun(\n ctx: LifecycleContext,\n mergeCommit: string,\n required: string[],\n deps: MainVerifyRowDeps,\n): Promise<{ run: { sha: string; total: number } | null; note: string }> {\n const readCandidates =\n deps.readContainingDefaultBranchCommits ?? defaultMainVerifyRowDeps.readContainingDefaultBranchCommits!;\n let candidates: string[];\n try {\n candidates = await readCandidates(ctx, mergeCommit);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { run: null, note: `later-run evidence unavailable: ${message}` };\n }\n\n const probed = candidates.slice(0, LATER_GREEN_CANDIDATE_LIMIT);\n for (const sha of probed) {\n try {\n const checks = await deps.readCheckRuns(ctx, sha);\n if (hasRequiredChecksGreen(checks, required)) {\n return { run: { sha, total: checks.total }, note: '' };\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { run: null, note: `later-run evidence unavailable: ${message}` };\n }\n }\n return {\n run: null,\n note: probed.length === 0\n ? 'no later default-branch commit contains the merge'\n : `no green CI run among the ${probed.length} newest default-branch commit(s) containing the merge`,\n };\n}\n\nexport async function checkMainVerifyRow(\n ctx: LifecycleContext,\n mergeCommit?: string,\n deps: MainVerifyRowDeps = defaultMainVerifyRowDeps,\n): Promise<DodRowResult> {\n if (!ctx.github || !mergeCommit) {\n return result(\n 'main-verify',\n 'skip',\n 'no merge commit resolvable — verified-on-main has no durable marker (see DoD row 6)',\n );\n }\n\n try {\n const requiredChecks = await (deps.readRequiredChecks ?? defaultMainVerifyRowDeps.readRequiredChecks!)(ctx);\n const checks = await deps.readCheckRuns(ctx, mergeCommit);\n const outcome = requiredChecksOutcome(checks, requiredChecks);\n if (hasRequiredChecksGreen(checks, requiredChecks)) {\n return result('main-verify', 'pass', `required checks concluded successfully on ${mergeCommit}: ${requiredChecks.join(', ')}`);\n }\n\n // The merge commit's own run stays the primary evidence, so its outcome is\n // always recorded first; the later-green-run form only appends to it.\n const parts = [\n ...(outcome.missing.length > 0 ? [`missing required checks on ${mergeCommit}: ${outcome.missing.join(', ')}`] : []),\n ...(outcome.unsuccessful.length > 0 ? [`required checks not successful: ${outcome.unsuccessful.join(', ')}`] : []),\n ];\n const primary = parts.join('; ');\n\n const later = await findLaterGreenDefaultBranchRun(ctx, mergeCommit, requiredChecks, deps);\n if (later.run) {\n return result(\n 'main-verify',\n 'pass',\n `${primary}; verified on main by later green CI run ${later.run.sha} containing the merge (required checks concluded successfully: ${requiredChecks.join(', ')})`,\n );\n }\n return result('main-verify', 'miss', `${primary}; ${later.note}`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return result('main-verify', 'miss', `could not read merge-commit check-runs: ${message}`);\n }\n}\n\nexport async function checkShipRow(\n ctx: LifecycleContext,\n deps: ShipRowDeps = defaultShipRowDeps,\n): Promise<DodRowResult> {\n const project = deps.readProject(ctx);\n if (!project?.version_sync) {\n return result('ship', 'skip', 'project declares no version_sync; ship step not applicable');\n }\n\n const promotedBatch = deps.findPromotedBatch(ctx);\n const ship = promotedBatch\n ? await deps.readBatchShip(project, promotedBatch)\n : (await deps.readPipeline(ctx))?.ship;\n if (!ship) {\n if (promotedBatch) {\n return result(\n 'ship',\n 'miss',\n `batch ${promotedBatch.name} includes this issue but no durable ship settlement was recorded`,\n );\n }\n return result('ship', 'skip', 'merged outside a batch; ship is batch-scoped');\n }\n if (ship.status === 'passed') {\n return result(\n 'ship',\n 'pass',\n `version ${ship.version ?? 'unknown'} shipped for batch ${ship.batch}; ${ship.paths?.length ?? 0} path(s) verified`,\n );\n }\n if (ship.status === 'pending') {\n return result(\n 'ship',\n 'miss',\n `batch ${ship.batch} merged but no version was shipped — use the Ship version action on the batch card for ${ship.batch}`,\n );\n }\n if (ship.status === 'partial') {\n const failing = ship.paths?.filter(path => !path.ok).map(path => path.path) ?? [];\n return result(\n 'ship',\n 'miss',\n `batch ${ship.batch} partially propagated version ${ship.version ?? 'unknown'}; failing paths: ${failing.join(', ') || 'unknown'}`,\n );\n }\n return result(\n 'ship',\n 'miss',\n `batch ${ship.batch} version ship failed (${ship.errorCode ?? 'unknown-failure'}): ${ship.error ?? ship.reason ?? 'inspect the local dashboard log'}`,\n );\n}\n\nexport async function checkDeployRow(\n ctx: LifecycleContext,\n merge: {\n mergedAt?: string;\n mergeCommit?: string;\n mergedRowStatus?: DodRowResult['status'];\n mainVerifyRowStatus?: DodRowResult['status'];\n },\n deps: DeployRowDeps = defaultDeployRowDeps,\n): Promise<DodRowResult> {\n if (!merge.mergeCommit) {\n if (merge.mergedRowStatus === 'miss') {\n return result(\n 'deploy',\n 'skip',\n 'no merge commit resolved because the merged row missed — deploy ancestry depends on row 4; build ancestry unchecked',\n );\n }\n // PAN-3188: row 6 (main-verify) skips whenever no merge commit is\n // resolvable — the no-durable-anchor landing class (e.g. GitLab-backed\n // landings whose row 4 evidence is the merge specialist's confirmation).\n // Row 7 must agree: same missing anchor, same skip. Only when main-verify\n // did NOT skip is the absent commit an integrity problem worth a miss.\n if (merge.mainVerifyRowStatus === 'skip') {\n return result(\n 'deploy',\n 'skip',\n 'verified-on-main skipped — no merge commit resolvable, so deploy build-ancestry has no durable anchor either (same landing class as DoD row 6 skip)',\n );\n }\n return result('deploy', 'miss', 'merged row passed without a resolvable merge commit; build ancestry cannot be checked');\n }\n\n const baseUrl = deps.dashboardUrl().replace(/\\/$/, '');\n let health: Record<string, unknown>;\n try {\n health = await deps.readJson(`${baseUrl}/api/health`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return result('deploy', 'miss', `dashboard not reachable at ${baseUrl} — a merged fix is not live if no server is serving it: ${message}`);\n }\n\n const repoRoot = typeof health.repoRoot === 'string' ? health.repoRoot : '';\n if (!repoRoot) return result('deploy', 'miss', `dashboard at ${baseUrl} did not report repoRoot`);\n if (resolve(repoRoot) !== resolve(ctx.projectPath)) {\n return result('deploy', 'skip', `live dashboard serves ${repoRoot}, not this project — deploy semantics undefined for ${ctx.projectPath}`);\n }\n\n const buildCommit = typeof health.buildCommit === 'string' && health.buildCommit.trim()\n ? health.buildCommit\n : undefined;\n if (!buildCommit) {\n return result('deploy', 'miss', `dashboard at ${baseUrl} did not report buildCommit; live deployment cannot be proven`);\n }\n try {\n if (health.buildDirty === true) {\n return result(\n 'deploy',\n 'miss',\n `live build ${buildCommit.slice(0, 8)} was built from a dirty working tree — uncommitted changes may be serving; redeploy canonically with \\`pan reload\\``,\n );\n }\n\n const canonical = await deps.commitContains(repoRoot, buildCommit, 'origin/main');\n if (!canonical) {\n return result(\n 'deploy',\n 'miss',\n `live build commit ${buildCommit.slice(0, 8)} is not an ancestor of origin/main — the server is running a build of local-only commits; redeploy with \\`pan reload\\``,\n );\n }\n\n const contains = await deps.commitContains(repoRoot, merge.mergeCommit, buildCommit);\n const observed = `build commit ${buildCommit.slice(0, 8)} ${contains ? 'contains' : 'does not contain'} merge ${merge.mergeCommit.slice(0, 8)}`;\n return result('deploy', contains ? 'pass' : 'miss', observed);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return result('deploy', 'miss', `deploy evidence unavailable: ${message}`);\n }\n}\n\nexport async function evaluateDodGate(\n ctx: LifecycleContext,\n opts: {\n acceptedRows?: DodRowId[];\n acceptedBy?: string;\n verifyMerged?: MergedRowDeps['verifyMerged'];\n } = {},\n deps: EvaluateDodGateDeps = defaultEvaluateDodGateDeps,\n): Promise<DodGateResult> {\n const acceptedRows = new Set(opts.acceptedRows ?? []);\n const overridable = new Set(DOD_ROWS.filter(row => row.overridable).map(row => row.id));\n for (const id of acceptedRows) {\n if (!overridable.has(id)) throw new TypeError(`DoD row \"${id}\" cannot be accepted; valid rows: ${[...overridable].join(', ')}`);\n }\n const by = opts.acceptedBy ?? process.env.OVERDECK_AGENT_ID ?? userInfo().username;\n if (acceptedRows.size > 0 && !canAcceptDodMisses(by)) {\n throw new TypeError('The flywheel orchestrator cannot accept missed Definition-of-Done rows; an operator must apply --accept-<row> overrides.');\n }\n\n const merged = opts.verifyMerged\n ? await checkMergedRow(ctx, { ...defaultMergedRowDeps, verifyMerged: opts.verifyMerged })\n : await deps.merged(ctx);\n await (deps.reconcileContainedStrike ?? defaultEvaluateDodGateDeps.reconcileContainedStrike!)(ctx, merged);\n // Main-verify computes before deploy because deploy's no-merge-commit\n // branch keys on main-verify's outcome (PAN-3188: row 7 skips when row 6\n // skips — both mean \"no durable anchor\" for this landing class). The verdict\n // rows also need this landed-state evidence before they can settle terminal issues.\n const mainVerify = await deps.mainVerify(ctx, merged.mergeCommit);\n const trackerClosed = await (deps.trackerClosed ?? defaultEvaluateDodGateDeps.trackerClosed!)(ctx.issueId);\n const settlement: TerminalVerdictSettlement = {\n trackerClosed,\n landedWork: merged.status === 'pass',\n mainVerifyStatus: mainVerify.status,\n };\n const [review, tests, verification, postMerge, ship, deploy] = await Promise.all([\n deps.review(ctx.issueId, settlement),\n deps.tests(ctx.issueId, settlement),\n deps.verification(ctx.issueId, settlement),\n deps.postMerge(ctx, merged),\n deps.ship(ctx),\n deps.deploy(ctx, {\n mergedAt: merged.mergedAt,\n mergeCommit: merged.mergeCommit,\n mergedRowStatus: merged.status,\n mainVerifyRowStatus: mainVerify.status,\n }),\n ]);\n const rows = [review, tests, verification, merged, postMerge, mainVerify, ship, deploy];\n for (const row of rows) {\n if (row.status === 'miss' && acceptedRows.has(row.id)) {\n row.acceptedBy = { flag: acceptFlagFor(rowDefinition(row.id)), by, at: deps.now() };\n }\n }\n const misses = rows.filter(row => row.status === 'miss').map(row => row.id);\n const accepted = rows.filter(row => row.acceptedBy).map(row => row.id);\n return {\n rows,\n misses,\n accepted,\n passed: rows.every(row => row.status !== 'miss' || Boolean(row.acceptedBy)),\n };\n}\n\n/**\n * PAN-3025: completion witness for the close-out ceremony. Returns closedOutAt\n * only when the journal is terminal AND the live row is gone (the ceremony's\n * final mutating step). closedOut with live status present means a prior run\n * aborted mid-ceremony and must fall through to complete it.\n */\nexport async function readCompletedCloseOut(issueId: string, projectPath: string): Promise<string | null> {\n try {\n const project = resolveProjectForIssue(issueId) ?? getProjectConfigFromWorkspacePath(projectPath);\n const record = await readIssueRecord(project, issueId.toUpperCase());\n if (!record?.pipeline.closedOut) return null;\n // Fail closed: a read error means we cannot confirm absence, so return null (incomplete)\n const live = await Effect.runPromise(getReviewStatus(issueId)).catch(() => 'unknown');\n if (live === 'unknown') return null; // Reject the idempotent path on any read failure\n return live ? null : (record.pipeline.closedOutAt ?? 'unknown');\n } catch {\n // On any error (record read, etc.), fail closed\n return null;\n }\n}\n","/**\n * Residue disposition: close stale convention PRs/MRs for tracker-closed\n * pre-record-era issues with no landing evidence.\n */\n\nimport { execFile } from 'child_process';\nimport { promisify } from 'util';\nimport type { StepResult } from './types.js';\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * Extract GitHub owner/repo from a repository checkout path.\n * Reads the git remote URL to resolve forge coordinates.\n */\nexport async function extractGitHubCoordinates(repoPath: string): Promise<string | null> {\n try {\n const { stdout } = await execFileAsync('git', [\n '-C', repoPath,\n 'remote', 'get-url', 'origin',\n ], { encoding: 'utf-8', timeout: 5_000 });\n\n const remoteUrl = stdout.trim();\n const match = remoteUrl.match(/github\\.com[:/]([^/]+)\\/(.+?)(\\.git)?$/);\n if (match) {\n const owner = match[1];\n const repo = match[2];\n return `${owner}/${repo}`;\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Extract GitLab project path from a repository checkout path.\n * Reads the git remote URL to resolve forge coordinates.\n */\nexport async function extractGitLabProject(repoPath: string): Promise<string | null> {\n try {\n const { stdout } = await execFileAsync('git', [\n '-C', repoPath,\n 'remote', 'get-url', 'origin',\n ], { encoding: 'utf-8', timeout: 5_000 });\n\n const remoteUrl = stdout.trim();\n const match = remoteUrl.match(/gitlab\\.com[:/](.+?)(\\.git)?$/);\n if (match) {\n return match[1];\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport interface CloseResidueConventionPrsContext {\n issueId: string;\n projectPath: string;\n github?: { repos: string[] }; // owner/repo paths for GitHub\n gitlab?: { projects: string[] }; // GitLab project paths\n}\n\nexport async function closeResidueConventionPrs(\n ctx: CloseResidueConventionPrsContext,\n): Promise<StepResult> {\n const evidence: string[] = [];\n const fatalErrors: string[] = [];\n const issueIdLower = ctx.issueId.toLowerCase();\n const possibleHeads = [`feature/${issueIdLower}`, `strike/${issueIdLower}`];\n\n // GitHub PR close — handle all configured repositories\n if (ctx.github?.repos) {\n for (const githubRepo of ctx.github.repos) {\n for (const head of possibleHeads) {\n try {\n const { stdout } = await execFileAsync('gh', [\n 'pr', 'list',\n '--repo', githubRepo,\n '--head', head,\n '--state', 'open',\n '--json', 'number',\n ], { encoding: 'utf-8', timeout: 15_000 });\n\n const prs = JSON.parse(stdout) as Array<{ number: number }>;\n if (prs.length === 0) {\n evidence.push(`No open GitHub PRs found on ${githubRepo}/${head}`);\n }\n for (const pr of prs) {\n try {\n await execFileAsync('gh', [\n 'pr', 'close',\n `${pr.number}`,\n '--repo', githubRepo,\n '--comment', 'Closing stale residue PR with no merge claim — issue closed out without merge landing.',\n ], { encoding: 'utf-8', timeout: 15_000 });\n evidence.push(`Closed GitHub PR #${pr.number} on ${githubRepo}/${head}`);\n } catch (err) {\n fatalErrors.push(`GitHub PR #${pr.number} close failed on ${githubRepo}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n } catch (err) {\n fatalErrors.push(`GitHub PR lookup for ${githubRepo}/${head} failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n }\n }\n\n // GitLab MR close — write note BEFORE closing to ensure auditability on retry\n if (ctx.gitlab?.projects) {\n for (const glProject of ctx.gitlab.projects) {\n for (const head of possibleHeads) {\n try {\n const { stdout } = await execFileAsync('glab', [\n 'mr', 'list',\n '--repo', glProject,\n '--source-branch', head,\n '--state', 'opened',\n '--output', 'json',\n ], { encoding: 'utf-8', timeout: 15_000 });\n\n const mrs = JSON.parse(stdout) as Array<{ iid: number }>;\n if (mrs.length === 0) {\n evidence.push(`No open GitLab MRs found on ${glProject}/${head}`);\n }\n for (const mr of mrs) {\n try {\n // Write the audit note BEFORE closing to ensure it's recorded\n await execFileAsync('glab', [\n 'mr', 'note',\n '--repo', glProject,\n String(mr.iid),\n '--message', 'Closing stale residue MR with no merge claim — issue closed out without merge landing.',\n ], { encoding: 'utf-8', timeout: 15_000 });\n\n // Now close the MR\n await execFileAsync('glab', [\n 'mr', 'close',\n '--repo', glProject,\n String(mr.iid),\n ], { encoding: 'utf-8', timeout: 15_000 });\n\n evidence.push(`Closed GitLab MR !${mr.iid} on ${glProject}/${head}`);\n } catch (err) {\n fatalErrors.push(`GitLab MR !${mr.iid} operation failed on ${glProject}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n } catch (err) {\n fatalErrors.push(`GitLab MR lookup for ${glProject}/${head} failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n }\n }\n\n // Fail on any repository/operation errors — even if some PRs/MRs closed\n if (fatalErrors.length > 0) {\n return {\n step: 'Close stale convention PRs/MRs',\n success: false,\n skipped: false,\n error: `Failed to list/close convention PRs/MRs: ${fatalErrors.join('; ')}`,\n details: evidence.length > 0 ? [`Partial progress before failure: ${evidence.join('; ')}`] : undefined,\n };\n }\n\n if (evidence.length === 0) {\n return {\n step: 'Close stale convention PRs/MRs',\n success: true,\n skipped: true,\n details: ['No open convention PRs/MRs found on any configured repository'],\n };\n }\n\n return {\n step: 'Close stale convention PRs/MRs',\n success: true,\n skipped: false,\n details: evidence,\n };\n}\n","/**\n * Lifecycle workflows — Compose atomic operations into complete workflows.\n *\n * approve() — Post-merge: archive + close + teardown\n * close() — Simple close: close-issue + teardown\n * closeOut() — Full ceremony: verify-merged + archive + teardown + close + label + clear-status\n * deepWipe() — Destructive: teardown(deleteBranches) + delete agent state + reset issue\n */\n\nimport { existsSync } from 'fs';\nimport { copyFile, readFile, writeFile } from 'fs/promises';\nimport { join } from 'path';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport { Effect } from 'effect';\nimport { OVERDECK_HOME } from '../paths.js';\nimport type {\n LifecycleContext,\n WorkflowResult,\n StepResult,\n ApproveOptions,\n DeepWipeOptions,\n ArchiveOptions,\n CloseOutOptions,\n} from './types.js';\nimport { stepOk, stepSkipped, stepFailed, getLinearApiKey } from './types.js';\nimport { archivePlanning, findWorkspacePath } from './archive-planning.js';\nimport { closeIssue, type CloseIssueOptions } from './close-issue.js';\nimport { teardownWorkspace } from './teardown-workspace.js';\nimport { loadCloisterConfig } from '../cloister/config.js';\nimport { extractNumberSync, extractPrefixSync } from '../issue-id.js';\nimport { recordFeatureRegistryLifecycle } from '../registry/feature-registry-population.js';\nimport { getForgeAdapter } from '../forge.js';\nimport { resolveProjectReposForIssueSync } from '../project-repos.js';\nimport {\n getProjectConfigFromWorkspacePath,\n markRecordPipelineClosedOutSync,\n markRecordPipelineResidueClosedOutSync,\n writeCloseOutDodGate,\n} from '../pan-dir/record.js';\nimport { pruneStoppedAgentsForIssue } from '../cloister/agent-gc.js';\nimport { isTrackerIssueClosed } from '../cloister/issue-closed.js';\nimport { acknowledgeAllOpenRecoveryTrips } from '../cloister/recovery-trip.js';\nimport { clearAgentOperatorGatesForIssueSync } from '../agents/agent-state.js';\nimport { evaluateDodGate, readCompletedCloseOut } from './dod-gate.js';\nimport { closeResidueConventionPrs, extractGitHubCoordinates, extractGitLabProject } from './residue.js';\nimport {\n capturePipelineStage,\n resolvePipelineTelemetryContext,\n type PipelineTelemetryContext,\n} from '../telemetry/pipeline.js';\nimport { acceptFlagFor, BRANCH_ABSENT_MERGE_ERROR, buildAbandonedDodGate, buildResidueDodGate, DOD_ROWS, type DodGateResult, type DodRowId } from './dod.js';\n\nconst execAsync = promisify(exec);\n\nfunction trackerName(ctx: LifecycleContext, fallback: string): string {\n const name = ctx.tracker?.name ?? fallback;\n return name.charAt(0).toUpperCase() + name.slice(1);\n}\n\n/**\n * Build a WorkflowResult from collected steps.\n */\nfunction buildResult(\n workflow: WorkflowResult['workflow'],\n issueId: string,\n steps: StepResult[],\n startTime: number,\n dodGate?: DodGateResult,\n): WorkflowResult {\n return {\n workflow,\n issueId,\n success: steps.every(s => s.success),\n steps,\n duration: Date.now() - startTime,\n ...(dodGate ? { dodGate } : {}),\n };\n}\n\nfunction hasBlockingFailure(steps: StepResult[]): boolean {\n return steps.some(s => !s.success && !s.skipped);\n}\n\n/**\n * approve() — Post-merge lifecycle.\n */\nexport function approve(\n ctx: LifecycleContext,\n opts: ApproveOptions & CloseIssueOptions & ArchiveOptions = {},\n): Effect.Effect<WorkflowResult> {\n return Effect.gen(function* () {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n\n // 1. Archive planning\n const archiveSteps = yield* archivePlanning(ctx, opts);\n allSteps.push(...archiveSteps);\n\n // If archive failed, stop — don't destroy unarchived artifacts\n const archiveFailed = archiveSteps.some(s => !s.success && !s.skipped);\n if (archiveFailed) {\n allSteps.push(stepFailed('approve:abort', 'Stopped — archiving failed, workspace preserved'));\n return buildResult('approve', ctx.issueId, allSteps, start);\n }\n\n // 2. Close issue\n const closeSteps = yield* closeIssue(ctx, {\n tracker: opts.tracker,\n comment: 'Merged to main via Overdeck lifecycle',\n applyLabel: true,\n });\n allSteps.push(...closeSteps);\n\n // 3. Teardown workspace (delete branches — merge is complete)\n const teardownSteps = yield* teardownWorkspace(ctx, { deleteBranches: true });\n allSteps.push(...teardownSteps);\n\n // 5. Clear review status\n const clearResult = yield* clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n\n return buildResult('approve', ctx.issueId, allSteps, start);\n });\n}\n\n/**\n * close() — Simple issue close with teardown.\n */\nexport function close(\n ctx: LifecycleContext,\n opts: CloseIssueOptions = {},\n): Effect.Effect<WorkflowResult> {\n return Effect.gen(function* () {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n\n // 1. Close issue\n const closeSteps = yield* closeIssue(ctx, {\n tracker: opts.tracker,\n reason: opts.reason,\n applyLabel: false,\n });\n allSteps.push(...closeSteps);\n\n // 2. Teardown workspace\n const teardownSteps = yield* teardownWorkspace(ctx);\n allSteps.push(...teardownSteps);\n\n // 3. Clear review status\n const clearResult = yield* clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n\n return buildResult('close', ctx.issueId, allSteps, start);\n });\n}\n\n/**\n * closeOut() — Full close-out ceremony.\n *\n * This is the human-gated verification and cleanup workflow.\n * Replaces the monolithic executeCloseOut() function.\n *\n * 1. Verify branch merged (hard fail if not — must pass before any cleanup)\n * 2. Move PRD + archive workspace artifacts (hard fail if archiving fails)\n * 3. Mark xBRIEF completed\n * 4. Clean up workspace (tmux, TLDR, Docker, worktree)\n * 5. Clean up agent state\n * 6. Close issue on tracker\n * 7. Apply closed-out label\n * 8. Clear review status\n */\nexport function closeOut(\n ctx: LifecycleContext,\n opts: CloseIssueOptions & ArchiveOptions & CloseOutOptions = {},\n): Effect.Effect<WorkflowResult> {\n return Effect.gen(function* () {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n\n // PAN-3025: idempotent short-circuit for already-completed close-out ceremony.\n // Fail closed: on any error, proceed to the gate (null = not-confirmed-complete).\n const closedOutAt = yield* Effect.promise(() =>\n readCompletedCloseOut(ctx.issueId, ctx.projectPath).catch(() => null)\n );\n if (closedOutAt) {\n allSteps.push(stepSkipped('close-out:idempotent', [\n `Issue already closed out at ${closedOutAt} — skipping the Definition-of-Done gate and ceremony`,\n ]));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // Recover UAT-promotion evidence before the gate reads the verification verdict.\n const uatEvidenceStep = yield* Effect.promise(async () => {\n try {\n const { healUatPromotionVerification } = await import('../cloister/uat-promote-verification.js');\n const evidence = await healUatPromotionVerification(ctx.issueId);\n if (!evidence) {\n return stepSkipped('dod:uat-promotion-evidence', ['No missing UAT-promotion verification evidence found']);\n }\n return stepOk('dod:uat-promotion-evidence', [\n `Recorded verification from ${evidence.generation}`,\n ...(evidence.mergeSha ? [`Promoted to main at ${evidence.mergeSha.slice(0, 9)}`] : []),\n ]);\n } catch (err) {\n return stepSkipped('dod:uat-promotion-evidence', [\n `Evidence recovery unavailable: ${err instanceof Error ? err.message : String(err)}`,\n ]);\n }\n });\n allSteps.push(uatEvidenceStep);\n\n // 1. Collect residue evidence BEFORE building the gate (if residue disposition)\n const abandon = opts.abandonDisposition;\n const residue = opts.residueDisposition;\n let residueEvidence: string[] = [];\n let trackerClosedEvidence: string[] = [];\n\n if (residue) {\n // Pre-verify tracker is closed for residue disposition\n try {\n const isClosed = yield* Effect.promise(() => isTrackerIssueClosed(ctx.issueId));\n if (!isClosed) {\n allSteps.push(stepFailed('close-out:residue-precondition', 'Residue disposition requires the tracker issue to be already closed'));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n trackerClosedEvidence.push('Tracker issue verified closed');\n } catch (err) {\n allSteps.push(stepFailed('close-out:residue-precondition', `Could not verify tracker closure: ${err instanceof Error ? err.message : String(err)}`));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // Resolve forge coordinates and close stale PRs/MRs\n const prRepos = resolveProjectReposForIssueSync(ctx.issueId);\n if (!prRepos || prRepos.length === 0) {\n allSteps.push(stepFailed('close-out:residue-precondition', 'Could not resolve any configured repositories for residue cleanup'));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // Resolve all GitHub and GitLab coordinates\n const githubPaths = prRepos.filter(r => r.forge === 'github').map(r => r.repoPath);\n const gitlabPaths = prRepos.filter(r => r.forge === 'gitlab').map(r => r.repoPath);\n\n const resolveCoords = yield* Effect.promise(async () => {\n const githubRepos: string[] = [];\n const gitlabRepos: string[] = [];\n const errors: string[] = [];\n\n for (const repoPath of githubPaths) {\n const coords = await extractGitHubCoordinates(repoPath);\n if (coords) {\n githubRepos.push(coords);\n } else {\n errors.push(`GitHub coordinate extraction failed for ${repoPath}`);\n }\n }\n\n for (const repoPath of gitlabPaths) {\n const proj = await extractGitLabProject(repoPath);\n if (proj) {\n gitlabRepos.push(proj);\n } else {\n errors.push(`GitLab project extraction failed for ${repoPath}`);\n }\n }\n\n return { githubRepos, gitlabRepos, errors };\n });\n\n // Fail if any configured repository could not be resolved\n if (resolveCoords.errors.length > 0) {\n allSteps.push(stepFailed('close-out:residue-precondition', `Could not resolve all forge coordinates: ${resolveCoords.errors.join('; ')}`));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // Fail if no repositories were successfully resolved\n if (resolveCoords.githubRepos.length === 0 && resolveCoords.gitlabRepos.length === 0) {\n allSteps.push(stepFailed('close-out:residue-precondition', 'No forge coordinates were resolved for residue cleanup'));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // Execute residue cleanup\n const residueStep = yield* Effect.promise(() => closeResidueConventionPrs({\n issueId: ctx.issueId,\n projectPath: ctx.projectPath,\n github: resolveCoords.githubRepos.length > 0 ? { repos: resolveCoords.githubRepos } : undefined,\n gitlab: resolveCoords.gitlabRepos.length > 0 ? { projects: resolveCoords.gitlabRepos } : undefined,\n }));\n allSteps.push(residueStep);\n if (!residueStep.success && !residueStep.skipped) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — residue PR/MR close failed'));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n residueEvidence = residueStep.details ?? [];\n }\n\n // 2. Build the Definition-of-Done gate with collected evidence\n let dodGate: DodGateResult = abandon\n ? buildAbandonedDodGate(abandon.reason, abandon.by)\n : residue\n ? buildResidueDodGate(residue.reason, residue.by, [\n ...trackerClosedEvidence,\n ...residueEvidence,\n ])\n : yield* Effect.promise(() => evaluateDodGate(ctx, {\n acceptedRows: opts.dodAcceptedRows,\n acceptedBy: opts.dodAcceptedBy,\n verifyMerged: verifyBranchMergedImpl,\n }));\n\n for (const row of dodGate.rows) {\n const details = [`expected: ${row.expected}`, `observed: ${row.observed}`];\n if (row.acceptedBy) {\n allSteps.push(stepSkipped(`dod:${row.id}`, [\n ...details,\n `MISS accepted via ${row.acceptedBy.flag} by ${row.acceptedBy.by} at ${row.acceptedBy.at}`,\n ]));\n } else if (row.status === 'skip') {\n allSteps.push(stepSkipped(`dod:${row.id}`, details));\n } else if (row.status === 'pass') {\n allSteps.push(stepOk(`dod:${row.id}`, details));\n } else {\n allSteps.push(stepFailed(`dod:${row.id}`, row.observed, details));\n }\n }\n if (!dodGate.passed) {\n const flags = dodGate.rows\n .filter(row => row.status === 'miss' && !row.acceptedBy)\n .map(row => `${row.id} (${acceptFlagFor(DOD_ROWS.find(def => def.id === row.id)!)})`);\n allSteps.push(stepFailed(\n 'close-out:dod-gate',\n `Definition-of-Done gate blocked close-out: ${flags.join(', ')}. Re-run with the named --accept-<row> flag to record an explicit override.`,\n ));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n\n // 3. Move PRD + archive workspace artifacts\n // (Note: step numbering adjusted due to upfront residue cleanup)\n\n // 2. Move PRD + archive workspace artifacts\n const archiveSteps = yield* archivePlanning(ctx, opts);\n allSteps.push(...archiveSteps);\n\n // Hard fail on archive failure — don't destroy unarchived artifacts\n const archiveFailed = archiveSteps.some(s => !s.success && !s.skipped);\n if (archiveFailed) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — archiving failed, workspace preserved'));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n\n // 3. Mark the xBRIEF completed on main before teardown removes local state.\n const xbriefStep = yield* Effect.promise(() => completeXBriefStep(ctx));\n allSteps.push(xbriefStep);\n if (!xbriefStep.success && !xbriefStep.skipped) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — xBRIEF completion failed, workspace preserved'));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n\n // 5+6. Teardown workspace + agent state\n const telemetryContext: PipelineTelemetryContext | null = yield* Effect.promise(async () => {\n try {\n return await resolvePipelineTelemetryContext(ctx.issueId);\n } catch {\n // Membership and agent attribution are best-effort and must not abort close-out.\n return null;\n }\n });\n const closeOutConfig = (yield* Effect.promise(() => Effect.runPromise(loadCloisterConfig()))).close_out;\n const teardownSteps = yield* teardownWorkspace(ctx, {\n deleteWorkspace: closeOutConfig?.remove_workspace ?? false,\n deleteBranches: closeOutConfig?.delete_feature_branch ?? false,\n });\n allSteps.push(...teardownSteps);\n if (hasBlockingFailure(teardownSteps)) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — teardown failed, tracker issue and review status preserved'));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n\n const teardownDef = DOD_ROWS.find(row => row.id === 'teardown')!;\n dodGate.rows.push({\n ...teardownDef,\n status: 'pass',\n observed: teardownSteps.flatMap(step => step.details ?? []).join('; ') || 'close-out teardown completed',\n });\n\n // 6+7. Close issue + apply label\n const closeSteps = yield* closeIssue(ctx, {\n tracker: opts.tracker,\n comment: abandon\n ? `Closed without landing evidence — disposition recorded: ${abandon.reason}`\n : ctx.auto ? 'Closed via automatic close-out ceremony' : 'Closed via close-out ceremony',\n applyLabel: true,\n });\n allSteps.push(...closeSteps);\n if (hasBlockingFailure(closeSteps)) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — issue close failed, review status preserved'));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n\n // 8. Mark durable pipeline terminal before clearing the DB cache.\n const markTerminal = yield* markPipelineClosedOutStep(ctx, residue);\n allSteps.push(markTerminal);\n\n // Update gate with verified residue evidence before recording if a residue row exists\n if (residue && residueEvidence.length > 0) {\n const residueRow = dodGate.rows.find(row => (row.id as string) === 'residue');\n if (residueRow) {\n residueRow.observed = residueEvidence.join('; ');\n }\n }\n\n const recordDodGate = yield* recordDodGateStep(ctx, dodGate, abandon, residue);\n allSteps.push(recordDodGate);\n if (!recordDodGate.success) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — Definition-of-Done audit could not be persisted; review status preserved'));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n if (markTerminal.success) {\n const pruned = yield* Effect.promise(() => pruneStoppedAgentsForIssue(ctx.issueId));\n allSteps.push(pruned.preserved.length > 0\n ? stepSkipped('close-out:prune-agent-rows', [`Preserved live agents or terminal rows with retained transcripts: ${pruned.preserved.join(', ')}`])\n : stepOk('close-out:prune-agent-rows', [`Pruned ${pruned.removed.length} stopped agent row(s)`]));\n\n // PAN-3727: acknowledge open recovery trips and clear operator-gate\n // residue (stoppedByUser/paused/troubled) so a terminal issue's\n // preserved agent rows and record stop reappearing in the parked\n // population. The two doors are independent residue — run and catch\n // each separately (review finding) so a trip-ack failure can never\n // suppress gate clearing, or vice versa. Non-blocking overall — a\n // bookkeeping failure must never strand an already-merged close-out.\n allSteps.push(yield* Effect.promise(async () => {\n let trips = 0;\n let tripsError: string | undefined;\n try {\n trips = await acknowledgeAllOpenRecoveryTrips(ctx.issueId);\n } catch (err) {\n tripsError = (err as Error).message ?? String(err);\n }\n let gates: string[] = [];\n let gatesError: string | undefined;\n try {\n gates = clearAgentOperatorGatesForIssueSync(ctx.issueId);\n } catch (err) {\n gatesError = (err as Error).message ?? String(err);\n }\n const summary = `Acked ${trips} open trip(s); cleared operator gates on ${gates.length} agent row(s)`;\n if (!tripsError && !gatesError) {\n return stepOk('close-out:ack-parked-residue', [summary]);\n }\n return stepSkipped('close-out:ack-parked-residue', [\n summary,\n ...(tripsError ? [`trip acknowledgement failed: ${tripsError}`] : []),\n ...(gatesError ? [`gate clearing failed: ${gatesError}`] : []),\n ]);\n }));\n }\n\n // 9. Clear review status\n const clearResult = yield* clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n\n yield* Effect.promise(() => resetPostMergeStateForIssue(ctx.issueId));\n yield* Effect.promise(() => recordFeatureRegistryLifecycle({ issueId: ctx.issueId, status: 'archived' }));\n\n const result = buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n if (result.success && !markTerminal.skipped) capturePipelineStage('closed_out', telemetryContext);\n return result;\n });\n}\n\nfunction markPipelineClosedOutStep(ctx: LifecycleContext, residue?: { reason: string; by: string }): Effect.Effect<StepResult> {\n const step = 'close-out:mark-pipeline-terminal';\n return Effect.try({\n try: () => {\n const project = getProjectConfigFromWorkspacePath(ctx.projectPath);\n if (residue) {\n markRecordPipelineResidueClosedOutSync(project, ctx.issueId.toUpperCase());\n } else {\n markRecordPipelineClosedOutSync(project, ctx.issueId.toUpperCase());\n }\n return stepOk(step, ['Marked durable pipeline journal closed-out']);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepSkipped(step, [`Pipeline terminal marker failed (non-fatal): ${(err as Error).message ?? String(err)}`])),\n ),\n );\n}\n\nfunction recordDodGateStep(ctx: LifecycleContext, dodGate: DodGateResult, abandonDisposition?: { reason: string; by: string }, residueDisposition?: { reason: string; by: string }): Effect.Effect<StepResult> {\n const step = 'close-out:record-dod-gate';\n return Effect.tryPromise({\n try: async () => {\n const project = getProjectConfigFromWorkspacePath(ctx.projectPath);\n await writeCloseOutDodGate(project, ctx.issueId.toUpperCase(), {\n evaluatedAt: new Date().toISOString(),\n rows: dodGate.rows,\n accepted: dodGate.accepted,\n ...(abandonDisposition ? { disposition: abandonDisposition } : {}),\n ...(residueDisposition ? { disposition: residueDisposition } : {}),\n });\n return stepOk(step, ['Recorded Definition-of-Done gate with 8 rows']);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed(step, `Definition-of-Done gate record failed: ${(err as Error).message ?? String(err)}`)),\n ),\n );\n}\n\n/**\n * deepWipe() — Destructive cleanup for abandoned workspaces.\n */\nfunction destructiveResetWorkflow(\n workflow: 'deep-wipe' | 'reset' | 'cancel',\n ctx: LifecycleContext,\n opts: DeepWipeOptions,\n resetStep: (ctx: LifecycleContext) => Effect.Effect<StepResult>,\n progressLabel: string,\n progressSuccessDetail: string,\n): Effect.Effect<WorkflowResult> {\n return Effect.gen(function* () {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n const { deleteWorkspace = true, deleteBranches = true, resetIssue = true, onProgress } = opts;\n const resetContext = opts.tracker ? { ...ctx, tracker: opts.tracker } : ctx;\n\n const TOTAL_STEPS = 3 + (resetIssue ? 1 : 0);\n let stepNum = 0;\n\n const progress = (label: string, detail: string, status: 'active' | 'complete' | 'error' = 'active') => {\n onProgress?.({ step: stepNum, total: TOTAL_STEPS, label, detail, status });\n };\n\n stepNum = 1;\n\n // Preserve PRD before workspace teardown so it survives reset/cancel.\n const issueLower = ctx.issueId.toLowerCase();\n const workspacePath = findWorkspacePath(ctx.projectPath, issueLower);\n if (workspacePath && existsSync(workspacePath)) {\n const prdPath = join(workspacePath, '.pan', 'prd.md');\n if (existsSync(prdPath)) {\n yield* Effect.tryPromise({\n try: async () => {\n const activeDir = join(ctx.projectPath, 'docs', 'prds', 'active', issueLower);\n const { mkdir } = await import('fs/promises');\n await mkdir(activeDir, { recursive: true });\n await copyFile(prdPath, join(activeDir, 'prd.md'));\n },\n catch: () => null,\n }).pipe(Effect.catch(() => Effect.void));\n }\n }\n\n progress('Tearing down workspace', 'Killing agents, stopping services, removing files');\n const teardownSteps = yield* teardownWorkspace(ctx, {\n deleteWorkspace,\n deleteBranches,\n workspaceConfig: opts.workspaceConfig,\n projectName: opts.projectName,\n });\n allSteps.push(...teardownSteps);\n const teardownFailed = teardownSteps.some(s => !s.success && !s.skipped);\n progress('Tearing down workspace', teardownFailed ? 'Some steps failed' : 'Workspace torn down', teardownFailed ? 'error' : 'complete');\n\n stepNum = 2;\n progress('Deleting git branches', `feature/${ctx.issueId.toLowerCase()}`);\n progress('Deleting git branches', deleteBranches ? 'Branches removed' : 'Skipped', 'complete');\n\n if (resetIssue) {\n stepNum = 3;\n progress(progressLabel, `${ctx.issueId}`);\n const resetResult = yield* resetStep(resetContext);\n allSteps.push(resetResult);\n progress(progressLabel, resetResult.success ? progressSuccessDetail : (resetResult.error || 'Failed'), resetResult.success ? 'complete' : 'error');\n }\n\n stepNum = resetIssue ? 4 : 3;\n progress('Clearing review status', 'Removing specialist state');\n const clearResult = yield* clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n progress('Clearing review status', 'Review status cleared', 'complete');\n\n return buildResult(workflow, ctx.issueId, allSteps, start);\n });\n}\n\nexport function deepWipe(\n ctx: LifecycleContext,\n opts: DeepWipeOptions = {},\n): Effect.Effect<WorkflowResult> {\n return destructiveResetWorkflow(\n 'deep-wipe',\n ctx,\n opts,\n resetIssueToTodo,\n 'Resetting issue status',\n 'Issue reset to Todo',\n );\n}\n\nexport function resetToTodo(\n ctx: LifecycleContext,\n opts: DeepWipeOptions = {},\n): Effect.Effect<WorkflowResult> {\n return destructiveResetWorkflow(\n 'reset',\n ctx,\n opts,\n resetIssueToTodo,\n 'Resetting issue status',\n 'Issue reset to Todo',\n );\n}\n\nexport function cancelIssueWorkflow(\n ctx: LifecycleContext,\n opts: DeepWipeOptions = {},\n): Effect.Effect<WorkflowResult> {\n return destructiveResetWorkflow(\n 'cancel',\n ctx,\n opts,\n resetIssueToCanceled,\n 'Canceling issue',\n 'Issue moved to Canceled',\n );\n}\n\n// --- Internal helpers ---\n\nasync function completeXBriefStep(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'close-out:vbrief-completed';\n try {\n const { transitionXBriefOnMain } = await import('../xbrief/lifecycle-io.js');\n const result = await Effect.runPromise(transitionXBriefOnMain(\n ctx.projectPath,\n ctx.issueId,\n 'completed',\n 'completed',\n `scope: complete ${ctx.issueId.toUpperCase()} xBRIEF`,\n ));\n const details = [\n result.moved ? 'Updated xBRIEF lifecycle to completed' : 'xBRIEF lifecycle already completed',\n result.statusUpdated ? 'Updated plan.status to completed' : 'plan.status already completed',\n ];\n if (result.committed) details.push('Committed xBRIEF completion on main');\n return stepOk(step, details);\n } catch (err) {\n const cause = (err as { cause?: unknown }).cause ?? err;\n const message = cause instanceof Error ? cause.message : String(cause);\n if (message.includes('No xBRIEF found')) {\n return stepSkipped(step, [`No xBRIEF found for ${ctx.issueId}`]);\n }\n return stepFailed(step, `xBRIEF completion failed: ${message}`);\n }\n}\n\n/**\n * Verify feature branch is merged into main.\n */\nfunction verifyBranchMerged(ctx: LifecycleContext): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => verifyBranchMergedImpl(ctx),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('close-out:verify-merged', `Could not verify merge: ${(err as Error).message}`)),\n ),\n );\n}\n\nexport interface MergeVerificationRoot { repoKey: string; dir: string; sourceBranch: string; targetBranch: string; forge: 'github' | 'gitlab' }\n\nasync function verifyRootConventionBranches(ctx: LifecycleContext, root: MergeVerificationRoot, issueLower: string): Promise<StepResult | null> {\n const featureResult = await verifyConventionBranchMerged(ctx, root);\n if (featureResult?.success) return featureResult;\n\n const strikeResult = await verifyConventionBranchMerged(ctx, { ...root, sourceBranch: `strike/${issueLower}` });\n if (strikeResult?.success && !strikeResult.skipped) {\n return stepOk('close-out:verify-merged', [\n ...(strikeResult.details ?? []),\n `Superseded ${root.sourceBranch} residual: ${featureResult?.error ?? BRANCH_ABSENT_MERGE_ERROR}`,\n ]);\n }\n return featureResult;\n}\n\nexport async function verifyBranchMergedImpl(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'close-out:verify-merged';\n const issueLower = ctx.issueId.toLowerCase();\n const defaultRoot: MergeVerificationRoot = { repoKey: issueLower, dir: ctx.projectPath, sourceBranch: `feature/${issueLower}`, targetBranch: 'main', forge: 'github' };\n\n try {\n // Check review-status first — the merge specialist validates before marking merged\n try {\n const { loadReviewStatuses } = await import('../review-status.js');\n const statuses = loadReviewStatuses();\n const issueKey = ctx.issueId.toUpperCase();\n if (statuses[issueKey]?.mergeStatus === 'merged') {\n return stepOk(step, ['Merge specialist confirmed merge completed']);\n }\n } catch {\n // review-status.json may not exist, continue with git checks\n }\n\n const resolvedRoots = resolveProjectReposForIssueSync(ctx.issueId)\n ?.filter((repo) => repo.required)\n .map((repo): MergeVerificationRoot => ({\n repoKey: repo.repoKey,\n dir: repo.repoPath,\n sourceBranch: repo.sourceBranch,\n targetBranch: repo.targetBranch,\n forge: repo.forge,\n }));\n const roots = resolvedRoots?.length ? resolvedRoots : [defaultRoot];\n if (roots.length === 1) {\n return await verifyRootConventionBranches(ctx, roots[0], issueLower)\n ?? stepFailed(step, BRANCH_ABSENT_MERGE_ERROR);\n }\n\n const results = await Promise.all(roots.map(async (root) => ({\n root,\n result: await verifyRootConventionBranches(ctx, root, issueLower),\n })));\n const failures = results.filter(({ result }) => result && !result.success);\n if (failures.length > 0) {\n return stepFailed(step, failures.map(({ root, result }) => `${root.repoKey}: ${result?.error}`).join('; '));\n }\n const successes = results.filter(({ result }) => result?.success);\n if (successes.length > 0) {\n return stepOk(step, successes.flatMap(({ root, result }) =>\n (result?.details ?? []).map((detail) => `${root.repoKey}: ${detail}`)));\n }\n return stepFailed(step, BRANCH_ABSENT_MERGE_ERROR);\n } catch (err) {\n return stepFailed(step, `Could not verify merge: ${(err as Error).message}`);\n }\n}\n\ninterface GitLabMergeLookupCache {\n attempted: boolean;\n headSha?: string;\n artifact?: { id?: string; url?: string } | null;\n}\n\ninterface GitLabMergeMatch {\n result: StepResult;\n reused: boolean;\n}\n\nexport async function verifyConventionBranchMerged(ctx: LifecycleContext, root: MergeVerificationRoot): Promise<StepResult | null> {\n const step = 'close-out:verify-merged';\n const gitlabLookup: GitLabMergeLookupCache = { attempted: false };\n const { stdout: branchExists } = await execAsync(\n `git branch --list \"${root.sourceBranch}\" 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n\n if (branchExists.trim()) {\n // Use merge-base --is-ancestor: checks if the branch tip is reachable from main\n try {\n await execAsync(\n `git merge-base --is-ancestor ${root.sourceBranch} ${root.targetBranch}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const remoteCheck = await verifyRemoteBranchIfPresent(ctx, root, gitlabLookup);\n if (remoteCheck && !remoteCheck.success) return remoteCheck;\n return stepOk(step, ['All commits merged to main', ...(remoteCheck?.details ?? [])]);\n } catch {\n // --is-ancestor fails for squash merges where the branch still exists.\n const gitlabMerged = await verifySquashMergedMrByBranch(root, root.sourceBranch, gitlabLookup);\n if (gitlabMerged) {\n const remoteCheck = await verifyRemoteBranchIfPresent(ctx, root, gitlabLookup);\n if (remoteCheck && !remoteCheck.success) return remoteCheck;\n return stepOk(step, [...(gitlabMerged.result.details ?? []), ...(remoteCheck?.details ?? [])]);\n }\n\n try {\n const { stdout: codeDiff } = await execAsync(\n `git diff ${root.targetBranch}...${root.sourceBranch} -- ':!.planning' ':!docs/prds' ':!.overdeck/prompts' 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n if (!codeDiff.trim()) {\n const remoteCheck = await verifyRemoteBranchIfPresent(ctx, root, gitlabLookup);\n if (remoteCheck && !remoteCheck.success) return remoteCheck;\n return stepOk(step, [\n 'Code changes squash-merged to main (only planning artifacts remain on branch)',\n ...(remoteCheck?.details ?? []),\n ]);\n }\n } catch {\n // diff failed — fall through to unmerged report\n }\n\n const githubMerged = root.forge === 'github'\n ? await verifySquashMergedPrByBranch(ctx, root, root.sourceBranch)\n : null;\n if (githubMerged?.success) {\n const remoteCheck = await verifyRemoteBranchIfPresent(ctx, root, gitlabLookup);\n if (remoteCheck && !remoteCheck.success) return remoteCheck;\n return stepOk(step, [\n ...(githubMerged.details ?? []),\n ...(remoteCheck?.details ?? []),\n ]);\n }\n if (githubMerged) return githubMerged;\n\n const { stdout: unmerged } = await execAsync(\n `git log ${root.targetBranch}..${root.sourceBranch} --oneline 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const count = unmerged.trim() ? unmerged.trim().split('\\n').length : 0;\n\n if (ctx.github && root.forge === 'github') {\n try {\n const { stdout: issueState } = await execAsync(\n `gh issue view ${ctx.github.number} --repo ${ctx.github.owner}/${ctx.github.repo} --json state --jq '.state'`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n if (issueState.trim().toUpperCase() === 'CLOSED') {\n return stepSkipped(step, [`Issue already closed on GitHub; ${count} unmerged commit(s) remain on ${root.sourceBranch}`]);\n }\n } catch {\n // gh check failed — fall through to hard fail\n }\n }\n\n return stepFailed(step, `${count} unmerged commit(s) on ${root.sourceBranch}. Merge before closing out.`);\n }\n }\n\n // Check remote\n const { stdout: remoteBranch } = await execAsync(\n `git ls-remote --heads origin \"${root.sourceBranch}\" 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n\n if (remoteBranch.trim()) {\n await execAsync(`git fetch origin ${root.sourceBranch}`, { cwd: root.dir }).catch(() => {});\n const remoteCheck = await verifyRemoteBranchIfPresent(ctx, root, gitlabLookup);\n if (remoteCheck) return remoteCheck;\n }\n\n return null;\n}\n\nasync function verifySquashMergedMrByBranch(\n root: MergeVerificationRoot,\n branchRef: string,\n lookup: GitLabMergeLookupCache,\n): Promise<GitLabMergeMatch | null> {\n if (root.forge !== 'gitlab') return null;\n try {\n const { stdout } = await execAsync(`git rev-parse ${branchRef} 2>/dev/null`, { cwd: root.dir, encoding: 'utf-8' });\n const headSha = stdout.trim();\n if (!headSha) return null;\n const reused = lookup.attempted;\n if (!reused) {\n lookup.attempted = true;\n lookup.headSha = headSha;\n lookup.artifact = await getForgeAdapter('gitlab').findMergedArtifact({\n sourceBranch: root.sourceBranch,\n targetBranch: root.targetBranch,\n headSha,\n cwd: root.dir,\n });\n }\n if (!lookup.artifact || lookup.headSha !== headSha) return null;\n const label = lookup.artifact.id ? `MR !${lookup.artifact.id}` : 'GitLab MR';\n const url = lookup.artifact.url ? ` (${lookup.artifact.url})` : '';\n return {\n result: stepOk('close-out:verify-merged', [`${label} is merged and ${branchRef} matches the merged MR head${url}`]),\n reused,\n };\n } catch {\n return null;\n }\n}\n\ntype GitHubMergedPr = {\n number?: number;\n mergedAt?: string | null;\n headRefOid?: string | null;\n url?: string | null;\n};\n\nasync function verifyRemoteBranchIfPresent(\n ctx: LifecycleContext,\n root: MergeVerificationRoot,\n gitlabLookup: GitLabMergeLookupCache,\n): Promise<StepResult | null> {\n const step = 'close-out:verify-merged';\n const remoteRef = `origin/${root.sourceBranch}`;\n\n const { stdout: remoteBranch } = await execAsync(\n `git ls-remote --heads origin \"${root.sourceBranch}\" 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n if (!remoteBranch.trim()) return null;\n\n await execAsync(`git fetch origin ${root.sourceBranch}`, { cwd: root.dir }).catch(() => {});\n\n try {\n await execAsync(\n `git merge-base --is-ancestor ${remoteRef} ${root.targetBranch}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n return stepOk(step, ['Remote branch fully merged']);\n } catch {\n // Squash-merge detection for remote branch\n try {\n const { stdout: codeDiff } = await execAsync(\n `git diff ${root.targetBranch}...${remoteRef} -- ':!.planning' ':!docs/prds' ':!.overdeck/prompts' 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n if (!codeDiff.trim()) {\n return stepOk(step, ['Remote code changes squash-merged to main (only planning artifacts remain on branch)']);\n }\n } catch {\n // diff failed — fall through\n }\n\n const gitlabMerged = await verifySquashMergedMrByBranch(root, remoteRef, gitlabLookup);\n if (gitlabMerged) {\n return gitlabMerged.reused\n ? stepOk(step, [`Remote ${remoteRef} matches the merged MR head`])\n : gitlabMerged.result;\n }\n\n const githubMerged = root.forge === 'github'\n ? await verifySquashMergedPrByBranch(ctx, root, remoteRef)\n : null;\n if (githubMerged) return githubMerged;\n\n const { stdout: remoteUnmerged } = await execAsync(\n `git log ${root.targetBranch}..${remoteRef} --oneline 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const count = remoteUnmerged.trim() ? remoteUnmerged.trim().split('\\n').length : 0;\n\n if (ctx.github && root.forge === 'github') {\n try {\n const { stdout: issueState } = await execAsync(\n `gh issue view ${ctx.github.number} --repo ${ctx.github.owner}/${ctx.github.repo} --json state --jq '.state'`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n if (issueState.trim().toUpperCase() === 'CLOSED') {\n return stepSkipped(step, [`Issue already closed on GitHub; ${count} unmerged commit(s) remain on remote ${root.sourceBranch}`]);\n }\n } catch {\n // gh check failed — fall through to hard fail\n }\n }\n\n return stepFailed(step, `${count} unmerged commit(s) on remote ${root.sourceBranch}.`);\n }\n}\n\nasync function verifySquashMergedPrByBranch(\n ctx: LifecycleContext,\n root: MergeVerificationRoot,\n branchRef: string,\n): Promise<StepResult | null> {\n if (!ctx.github) return null;\n\n const step = 'close-out:verify-merged';\n const { owner, repo } = ctx.github;\n const remoteTargetRef = `origin/${root.targetBranch}`;\n\n try {\n const { stdout: prJson } = await execAsync(\n `gh pr list --repo ${owner}/${repo} --state merged --head ${JSON.stringify(root.sourceBranch)} --json number,mergedAt,headRefOid,url`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const prs = JSON.parse(prJson) as GitHubMergedPr[];\n const mergedPr = prs\n .filter((pr) => (\n typeof pr.mergedAt === 'string'\n && pr.mergedAt.length > 0\n && typeof pr.headRefOid === 'string'\n && pr.headRefOid.length > 0\n ))\n .sort((a, b) => Date.parse(String(b.mergedAt)) - Date.parse(String(a.mergedAt)))[0];\n if (!mergedPr?.headRefOid) return null;\n\n const { stdout: tipShaRaw } = await execAsync(\n `git rev-parse ${branchRef} 2>/dev/null`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const tipSha = tipShaRaw.trim();\n if (!tipSha) return null;\n\n if (tipSha === mergedPr.headRefOid) {\n const prLabel = typeof mergedPr.number === 'number' ? `PR #${mergedPr.number}` : 'GitHub PR';\n return stepOk(step, [`${prLabel} is squash-merged and ${branchRef} matches the merged PR head`]);\n }\n\n // A workspace may merge a newer target branch after its PR head was pushed.\n // Ignore those merge commits and allow close-out when every post-PR commit\n // is already on the configured remote target; only branch-unique work is unsafe.\n let commitsNotOnTarget: string[] | null = null;\n try {\n const { stdout: commitsRaw } = await execAsync(\n `git log --no-merges --format=%H ${mergedPr.headRefOid}..${tipSha}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const commits = commitsRaw.split('\\n').map((sha) => sha.trim()).filter(Boolean);\n const unmerged: string[] = [];\n for (const commit of commits) {\n try {\n await execAsync(\n `git merge-base --is-ancestor ${commit} ${remoteTargetRef}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n } catch {\n unmerged.push(commit);\n }\n }\n if (unmerged.length === 0) {\n const prLabel = typeof mergedPr.number === 'number' ? `PR #${mergedPr.number}` : 'GitHub PR';\n return stepOk(step, [\n `${prLabel} is squash-merged; all ${commits.length} post-PR non-merge commit(s) on ${branchRef} are already on ${remoteTargetRef}`,\n ]);\n }\n\n commitsNotOnTarget = unmerged;\n } catch { /* containment check failure falls through to the state-plane policy */ }\n\n // PAN-2406 / state-plane policy rule 3: commits after the merged head that\n // touch ONLY legacy state-plane paths under .pan/ are pipeline exhaust —\n // e.g. 'chore: record merge status' — and must not block close-out.\n try {\n const { stdout: deltaRaw } = await execAsync(\n `git diff --name-only ${mergedPr.headRefOid}..${tipSha}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n let deltaFiles = deltaRaw.split('\\n').map((f) => f.trim()).filter(Boolean);\n let statePlaneOnly = deltaFiles.length > 0 && deltaFiles.every((f) =>\n f.startsWith('.pan/'));\n if (!statePlaneOnly) {\n // Branch may have merged its target INTO itself after the PR merged —\n // the two-dot delta then contains target-branch files. Judge only changes\n // UNIQUE to the branch (three-dot vs the remote target): if those are\n // state-plane-only, everything real is already on the target.\n const { stdout: uniqueRaw } = await execAsync(\n `git diff --name-only ${remoteTargetRef}...${tipSha}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n deltaFiles = uniqueRaw.split('\\n').map((f) => f.trim()).filter(Boolean);\n statePlaneOnly = deltaFiles.every((f) =>\n f.startsWith('.pan/'));\n }\n if (statePlaneOnly) {\n const prLabel = typeof mergedPr.number === 'number' ? `PR #${mergedPr.number}` : 'GitHub PR';\n return stepOk(step, [\n `${prLabel} is squash-merged; ${branchRef} is ahead only by state-plane commits (${deltaFiles.length} file(s): .pan) — accepted per state-plane policy`,\n ]);\n }\n } catch { /* diff failure falls through to the strict rejection below */ }\n\n const prLabel = typeof mergedPr.number === 'number' ? `PR #${mergedPr.number}` : 'merged GitHub PR';\n if (commitsNotOnTarget) {\n return stepFailed(step, `${branchRef} has ${commitsNotOnTarget.length} commit(s) after merged ${prLabel} that are not on ${remoteTargetRef}: ${commitsNotOnTarget.join(', ')}`);\n }\n return stepFailed(step, `${branchRef} does not match the head commit of merged ${prLabel}; inspect before closing out.`);\n } catch {\n return null;\n }\n}\n\n/**\n * Reset issue back to open/backlog state (for destructive reset).\n */\nfunction resetIssueToTodo(ctx: LifecycleContext): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => resetIssueToTodoImpl(ctx),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('reset:reset-issue', `Failed to reset issue: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function resetIssueToTodoImpl(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'reset:reset-issue';\n try {\n if (ctx.github) {\n const { owner, repo, number } = ctx.github;\n // Reopen the issue\n await execAsync(\n `gh issue reopen ${number} --repo ${owner}/${repo}`,\n { encoding: 'utf-8' },\n ).catch(() => {}); // May already be open\n // Remove lifecycle labels\n const labelsToRemove = ['in-review', 'in-progress', 'planned', 'planning', 'Review: Approved', 'Review: Failed', 'ready-for-merge'];\n for (const label of labelsToRemove) {\n await execAsync(\n `gh issue edit ${number} --repo ${owner}/${repo} --remove-label \"${label}\"`,\n { encoding: 'utf-8' },\n ).catch(() => {}); // Label may not exist\n }\n return stepOk(step, [`Reset GitHub issue #${number}: reopened and cleared labels`]);\n }\n\n // Linear: reopen to Todo\n const linearApiKey = await getLinearApiKey();\n if (linearApiKey) {\n const { LinearClient } = await import('@linear/sdk');\n const client = new LinearClient({ apiKey: linearApiKey });\n const issueNum = extractNumberSync(ctx.issueId);\n const teamKey = extractPrefixSync(ctx.issueId);\n if (issueNum === null || teamKey === null) {\n return stepFailed(step, `Could not parse issue ID: ${ctx.issueId}`);\n }\n const results = await client.issues({\n filter: {\n number: { eq: issueNum },\n team: { key: { eq: teamKey } },\n },\n first: 1,\n });\n if (results.nodes.length > 0) {\n const issue = results.nodes[0];\n const team = await issue.team;\n if (team) {\n const states = await team.states();\n const todoState = states.nodes.find(s => s.type === 'unstarted' && s.name === 'Todo') ||\n states.nodes.find(s => s.type === 'unstarted');\n if (todoState) {\n await issue.update({ stateId: todoState.id });\n }\n }\n }\n return stepOk(step, [`Reset ${trackerName(ctx, 'linear')} issue ${ctx.issueId} to Todo`]);\n }\n\n return stepSkipped(step, ['No tracker available to reset issue']);\n } catch (err) {\n return stepFailed(step, `Failed to reset issue: ${(err as Error).message}`);\n }\n}\n\n/**\n * Clear review status for an issue.\n */\nasync function resetPostMergeStateForIssue(issueId: string): Promise<void> {\n try {\n const { resetPostMergeState } = await import('../cloister/merge-agent.js');\n resetPostMergeState(issueId);\n resetPostMergeState(issueId.toUpperCase());\n } catch {\n return;\n }\n}\n\nfunction clearReviewStatusStep(issueId: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => clearReviewStatusStepImpl(issueId),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepSkipped('clear-review-status', [`Failed to clear review status (non-fatal): ${(err as Error).message}`])),\n ),\n );\n}\n\nasync function clearReviewStatusStepImpl(issueId: string): Promise<StepResult> {\n const step = 'clear-review-status';\n try {\n const { clearReviewStatus } = await import('../review-status.js');\n clearReviewStatus(issueId.toUpperCase());\n return stepOk(step, ['Review status cleared']);\n } catch {\n // Fallback: direct file manipulation\n try {\n const statusFile = join(OVERDECK_HOME, 'review-status.json');\n if (existsSync(statusFile)) {\n const data = JSON.parse(await readFile(statusFile, 'utf-8'));\n const upperKey = issueId.toUpperCase();\n if (data[upperKey]) {\n delete data[upperKey];\n await writeFile(statusFile, JSON.stringify(data, null, 2));\n }\n }\n return stepOk(step, ['Review status cleared (direct)']);\n } catch (innerErr) {\n return stepSkipped(step, [`Failed to clear review status (non-fatal): ${(innerErr as Error).message}`]);\n }\n }\n}\n\nexport const __testInternals = {\n completeXBriefStep,\n verifyBranchMerged,\n};\n\nfunction resetIssueToCanceled(ctx: LifecycleContext): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => resetIssueToCanceledImpl(ctx),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('cancel:reset-issue', `Failed to cancel issue: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function resetIssueToCanceledImpl(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'cancel:reset-issue';\n try {\n if (ctx.github) {\n const { owner, repo, number } = ctx.github;\n await execAsync(\n `gh issue edit ${number} --repo ${owner}/${repo} --add-label \"wontfix\"`,\n { encoding: 'utf-8' },\n ).catch(() => {});\n return stepOk(step, [`Marked GitHub issue #${number} as canceled/wontfix`]);\n }\n\n const linearApiKey = await getLinearApiKey();\n if (linearApiKey) {\n const { LinearClient } = await import('@linear/sdk');\n const client = new LinearClient({ apiKey: linearApiKey });\n const issueNum = extractNumberSync(ctx.issueId);\n const teamKey = extractPrefixSync(ctx.issueId);\n if (issueNum === null || teamKey === null) {\n return stepFailed(step, `Could not parse issue ID: ${ctx.issueId}`);\n }\n const results = await client.issues({\n filter: {\n number: { eq: issueNum },\n team: { key: { eq: teamKey } },\n },\n first: 1,\n });\n if (results.nodes.length > 0) {\n const issue = results.nodes[0];\n const team = await issue.team;\n if (team) {\n const states = await team.states();\n const canceledState = states.nodes.find(s => s.type === 'canceled') ||\n states.nodes.find(s => s.name.toLowerCase() === 'canceled');\n if (canceledState) {\n await issue.update({ stateId: canceledState.id });\n }\n }\n }\n return stepOk(step, [`Reset ${trackerName(ctx, 'linear')} issue ${ctx.issueId} to Canceled`]);\n }\n\n return stepSkipped(step, ['No tracker available to cancel issue']);\n } catch (err) {\n return stepFailed(step, `Failed to cancel issue: ${(err as Error).message}`);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"workflows-N4QMkDzN.js","names":["execFileAsync","promisify","execFile","promisify","exec","join"],"sources":["../../src/lib/lifecycle/dod-gate.ts","../../src/lib/lifecycle/residue.ts","../../src/lib/lifecycle/workflows.ts"],"sourcesContent":["/** Mechanical Definition-of-Done row checks. */\n\nimport { execFile } from 'node:child_process';\nimport { userInfo } from 'node:os';\nimport { resolve } from 'node:path';\nimport { promisify } from 'node:util';\nimport type { CanonicalState } from '../../core/state-mapping.js';\nimport { Effect } from 'effect';\nimport { listRunningAgents, type AgentState } from '../agents.js';\nimport { getDashboardApiUrlSync } from '../config.js';\nimport { rehydrateHeadAnchor } from '../git-utils.js';\nimport { getReviewStatus, setReviewStatusSync, type ReviewStatus, type ReviewStatusUpdate } from '../review-status.js';\nimport type { StrikeLandingStatus } from '../strike-landing.js';\nimport {\n getProjectConfigFromWorkspacePath,\n readIssueRecord,\n resolveProjectForIssue,\n type PanIssuePipelineRecord,\n type PanIssueShipRecord,\n} from '../pan-dir/record.js';\nimport type { ProjectConfig } from '../projects.js';\nimport { getAutoCloseOutCanonicalState } from '../cloister/deacon-canonical-state.js';\nimport { aggregateGenerationShipStatus, loadShipRecords } from '../cloister/ship-status.js';\nimport { isTrackerIssueClosed } from '../cloister/issue-closed.js';\nimport {\n fetchCommitCheckRuns,\n fetchIssuePullRequest,\n fetchRequiredStatusChecks,\n type CommitCheckRuns,\n} from '../overdeck/pull-requests.js';\nimport { listUatGenerationsSync, type UatGeneration } from '../overdeck/merge-sync.js';\nimport { getForgeAdapter } from '../forge.js';\nimport { resolveProjectReposForIssueSync } from '../project-repos.js';\nimport {\n gatherIssueBranchContainment,\n type IssueBranchContainment,\n} from '../pipeline-membership-gather.js';\nimport {\n acceptFlagFor,\n BRANCH_ABSENT_MERGE_ERROR,\n canAcceptDodMisses,\n DOD_ROWS,\n type DodGateResult,\n type DodRowId,\n type DodRowResult,\n} from './dod.js';\nimport type { LifecycleContext, StepResult } from './types.js';\n\nconst execFileAsync = promisify(execFile);\n\ntype StatusSource =\n | { source: 'live'; status: ReviewStatus }\n | { source: 'journal'; status: PanIssuePipelineRecord }\n | null;\ntype Awaitable<T> = T | Promise<T>;\n\nexport interface DodStatusRowDeps {\n getReviewStatus: (issueId: string) => Awaitable<ReviewStatus | null>;\n getJournalStatus: (issueId: string) => Awaitable<PanIssuePipelineRecord | null>;\n}\n\nexport interface MergedDodRowResult extends DodRowResult {\n mergedAt?: string;\n mergeCommit?: string;\n evidence?: 'branch-containment';\n containedStrikeHead?: string;\n}\n\ninterface MergedForgeArtifact {\n forge: string;\n url?: string;\n id?: string;\n}\n\ninterface MergedRowDeps {\n verifyMerged: (ctx: LifecycleContext) => Promise<StepResult>;\n readPullRequest: (ctx: LifecycleContext, branchName: string) => Promise<{\n number?: number;\n state?: string;\n mergedAt?: string;\n mergeCommit?: { oid?: string } | string | null;\n }>;\n readDurableMerges?: (ctx: LifecycleContext) => Promise<string[]>;\n readMergedForgeArtifacts?: (ctx: LifecycleContext) => Promise<MergedForgeArtifact[]>;\n readBranchContainment?: (ctx: LifecycleContext) => Promise<IssueBranchContainment>;\n}\n\nconst defaultMergedRowDeps: MergedRowDeps = {\n verifyMerged: async () => ({\n step: 'close-out:verify-merged',\n success: false,\n skipped: false,\n error: 'merge verifier was not supplied',\n }),\n readPullRequest: async (ctx, branchName) => {\n if (!ctx.github) return {};\n void branchName;\n const response = await fetchIssuePullRequest(ctx.issueId);\n if (response.error) throw new Error(response.error);\n return response.pr ?? {};\n },\n readDurableMerges: async ctx => {\n const project = resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath);\n return (await readIssueRecord(project, ctx.issueId))?.closeOut.merges ?? [];\n },\n readMergedForgeArtifacts: async ctx => {\n const repos = resolveProjectReposForIssueSync(ctx.issueId)?.filter(repo => repo.required) ?? [];\n const artifacts = await Promise.all(repos.map(repo => getForgeAdapter(repo.forge).findMergedArtifact({\n sourceBranch: repo.sourceBranch,\n targetBranch: repo.targetBranch,\n cwd: repo.repoPath,\n })));\n return artifacts.filter((artifact): artifact is NonNullable<typeof artifact> => artifact !== null);\n },\n readBranchContainment: async ctx => {\n const project = resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath);\n if (!project) return { unmergedRefs: [], mergedWorkRefs: [], pointerRefs: [] };\n return gatherIssueBranchContainment(project, ctx.issueId);\n },\n};\n\ninterface PostMergeRowDeps {\n readCanonicalState: (ctx: LifecycleContext) => Promise<CanonicalState | null>;\n readMergeStatus: (issueId: string) => Awaitable<string | undefined>;\n listAgents: () => Awaitable<Array<Pick<AgentState, 'id' | 'issueId' | 'role' | 'status'>>>;\n readStrikeLanded?: (issueId: string) => Awaitable<boolean>;\n}\n\nconst defaultPostMergeRowDeps: PostMergeRowDeps = {\n readStrikeLanded: async issueId => strikeLanded((await loadStatus(issueId, defaultDeps))?.status),\n readCanonicalState: async ctx => {\n return getAutoCloseOutCanonicalState(ctx.issueId) as Promise<CanonicalState | null>;\n },\n // PAN-3025: same live→journal fallback as the verdict rows — after close-out\n // clears live status, the durable record is the only place mergeStatus survives.\n readMergeStatus: async issueId => (await loadStatus(issueId, defaultDeps))?.status.mergeStatus,\n listAgents: async () => Effect.runPromise(listRunningAgents()),\n};\n\nexport const DEFAULT_MAIN_VERIFY_REQUIRED_CHECKS = ['test', 'lint', 'build (22)', 'guard'];\n\ninterface MainVerifyRowDeps {\n readCheckRuns: (ctx: LifecycleContext, commit: string) => Promise<CommitCheckRuns>;\n readRequiredChecks?: (ctx: LifecycleContext) => Promise<string[]>;\n /**\n * PAN-3202: default-branch commits that contain `mergeCommit`, newest first.\n * These are the candidate heads for the later-green-run evidence form.\n */\n readContainingDefaultBranchCommits?: (ctx: LifecycleContext, mergeCommit: string) => Promise<string[]>;\n}\n\nfunction configuredMainVerifyChecks(ctx: LifecycleContext): string[] {\n const project = resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath);\n return project?.main_verify_required_checks?.length\n ? project.main_verify_required_checks\n : DEFAULT_MAIN_VERIFY_REQUIRED_CHECKS;\n}\n\nexport async function readContainingDefaultBranchCommits(\n ctx: LifecycleContext,\n mergeCommit: string,\n): Promise<string[]> {\n const options = { cwd: ctx.projectPath, encoding: 'utf-8' as const, timeout: 10000, maxBuffer: 8 * 1024 * 1024 };\n try {\n await execFileAsync('git', ['merge-base', '--is-ancestor', mergeCommit, 'origin/main'], options);\n } catch (error) {\n if (typeof error === 'object' && error !== null && 'code' in error && error.code === 1) return [];\n throw error;\n }\n\n const { stdout } = await execFileAsync(\n 'git',\n ['rev-list', '--first-parent', `${mergeCommit}..origin/main`],\n options,\n );\n return stdout.split('\\n').map(line => line.trim()).filter(Boolean);\n}\n\nconst defaultMainVerifyRowDeps: MainVerifyRowDeps = {\n readCheckRuns: async (ctx, commit) => {\n if (!ctx.github) return { total: 0, names: [], successful: [], failed: [], pending: [] };\n return fetchCommitCheckRuns(ctx.github.owner, ctx.github.repo, commit);\n },\n readRequiredChecks: async ctx => {\n if (!ctx.github) return configuredMainVerifyChecks(ctx);\n return fetchRequiredStatusChecks(ctx.github.owner, ctx.github.repo, 'main', configuredMainVerifyChecks(ctx));\n },\n readContainingDefaultBranchCommits,\n};\n\ninterface ShipRowDeps {\n readProject: (ctx: LifecycleContext) => ProjectConfig | null;\n readPipeline: (ctx: LifecycleContext) => Promise<PanIssuePipelineRecord | null>;\n findPromotedBatch: (ctx: LifecycleContext) => UatGeneration | null;\n readBatchShip: (project: ProjectConfig, generation: UatGeneration) => Promise<PanIssueShipRecord | null>;\n}\n\nconst defaultShipRowDeps: ShipRowDeps = {\n readProject: ctx => resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath),\n readPipeline: async ctx => {\n const project = resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath);\n return (await readIssueRecord(project, ctx.issueId))?.pipeline ?? null;\n },\n findPromotedBatch: ctx => {\n const project = resolveProjectForIssue(ctx.issueId) ?? getProjectConfigFromWorkspacePath(ctx.projectPath);\n return listUatGenerationsSync({ projectRoot: resolve(project.path), statuses: ['promoted'] })\n .find(generation => generation.members.some(member => member.issueId.toUpperCase() === ctx.issueId.toUpperCase())) ?? null;\n },\n readBatchShip: async (project, generation) => aggregateGenerationShipStatus(\n generation,\n await loadShipRecords(project, [generation]),\n ),\n};\n\ninterface DeployRowDeps {\n dashboardUrl: () => string;\n readJson: (url: string) => Promise<Record<string, unknown>>;\n commitContains: (repoRoot: string, mergeCommit: string, buildCommit: string) => Promise<boolean>;\n}\n\nexport interface TerminalVerdictSettlement {\n trackerClosed: boolean;\n landedWork: boolean;\n mainVerifyStatus: DodRowResult['status'];\n}\n\nexport interface EvaluateDodGateDeps {\n review: (issueId: string, settlement?: TerminalVerdictSettlement) => DodRowResult | Promise<DodRowResult>;\n tests: (issueId: string, settlement?: TerminalVerdictSettlement) => DodRowResult | Promise<DodRowResult>;\n verification: (issueId: string, settlement?: TerminalVerdictSettlement) => DodRowResult | Promise<DodRowResult>;\n merged: (ctx: LifecycleContext) => MergedDodRowResult | Promise<MergedDodRowResult>;\n postMerge: (ctx: LifecycleContext, merged?: MergedDodRowResult) => DodRowResult | Promise<DodRowResult>;\n mainVerify: (ctx: LifecycleContext, mergeCommit?: string) => DodRowResult | Promise<DodRowResult>;\n ship: (ctx: LifecycleContext) => DodRowResult | Promise<DodRowResult>;\n deploy: (ctx: LifecycleContext, merge: {\n mergedAt?: string;\n mergeCommit?: string;\n mergedRowStatus?: DodRowResult['status'];\n mainVerifyRowStatus?: DodRowResult['status'];\n }) => DodRowResult | Promise<DodRowResult>;\n trackerClosed?: (issueId: string) => Awaitable<boolean>;\n reconcileContainedStrike?: (ctx: LifecycleContext, merged: MergedDodRowResult) => Awaitable<void>;\n now: () => string;\n}\n\nconst defaultEvaluateDodGateDeps: EvaluateDodGateDeps = {\n review: (issueId, settlement) => checkReviewRow(issueId, defaultDeps, settlement),\n tests: (issueId, settlement) => checkTestsRow(issueId, defaultDeps, settlement),\n verification: (issueId, settlement) => checkVerificationRow(issueId, defaultDeps, settlement),\n merged: checkMergedRow,\n postMerge: checkPostMergeRow,\n mainVerify: checkMainVerifyRow,\n ship: checkShipRow,\n deploy: checkDeployRow,\n trackerClosed: isTrackerIssueClosed,\n reconcileContainedStrike,\n now: () => new Date().toISOString(),\n};\n\nconst defaultDeployRowDeps: DeployRowDeps = {\n dashboardUrl: getDashboardApiUrlSync,\n readJson: async url => {\n const response = await fetch(url, { signal: AbortSignal.timeout(3000) });\n if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);\n return response.json() as Promise<Record<string, unknown>>;\n },\n commitContains: async (repoRoot, mergeCommit, buildCommit) => {\n try {\n await execFileAsync('git', ['merge-base', '--is-ancestor', mergeCommit, buildCommit], {\n cwd: repoRoot,\n encoding: 'utf-8',\n timeout: 10000,\n });\n return true;\n } catch {\n return false;\n }\n },\n};\n\nconst defaultDeps: DodStatusRowDeps = {\n getReviewStatus: issueId => Effect.runPromise(getReviewStatus(issueId)),\n getJournalStatus: async issueId => {\n const project = resolveProjectForIssue(issueId) ?? getProjectConfigFromWorkspacePath(process.cwd());\n return (await readIssueRecord(project, issueId))?.pipeline ?? null;\n },\n};\n\nfunction rowDefinition(id: DodRowId) {\n const row = DOD_ROWS.find(candidate => candidate.id === id);\n if (!row) throw new Error(`Unknown DoD row: ${id}`);\n return row;\n}\n\nfunction result(id: DodRowId, status: DodRowResult['status'], observed: string): DodRowResult {\n const row = rowDefinition(id);\n return { id, num: row.num, title: row.title, expected: row.expected, observed, status };\n}\n\nasync function loadStatus(issueId: string, deps: DodStatusRowDeps): Promise<StatusSource> {\n try {\n const live = await deps.getReviewStatus(issueId);\n if (live) return { source: 'live', status: live };\n const journal = await deps.getJournalStatus(issueId);\n return journal ? { source: 'journal', status: journal } : null;\n } catch {\n return null;\n }\n}\n\n/**\n * PAN-3180: `landed` is the strike path's own durable statement that this issue's\n * work reached main through `strike/<id>`. It is written by the server merge door\n * (`mergeCompletionStatus` in `merge-strike.ts`) and mirrored into the per-issue\n * record's `pipeline` block, so it outlives review-status clearing. Every earlier\n * landing state (`ready`/`landing`/`recovering`/`needs_you`) is a strike still in\n * flight and earns no waiver.\n */\nfunction strikeLanded(status: StrikeLandingStatus | null | undefined): boolean {\n return status?.strikeLandingState === 'landed';\n}\n\n/**\n * PAN-3180: a strike dispatches neither a review nor a test specialist — that\n * bypass is the whole point of the path. So for a strike-landed issue these rows\n * are `skip` (deliberately not run), never `pass` (ran and succeeded) and never\n * `miss` (outstanding). A reader of the close-out record still sees the real\n * verdict and the reason it was never produced.\n */\nconst STRIKE_BYPASS_NOTE: Record<'review' | 'tests', string> = {\n review: 'no review specialist is dispatched for a strike',\n tests: 'no test specialist is dispatched for a strike',\n};\n\n/**\n * A specialist that ran and returned a negative verdict is a different fact from\n * one that was never dispatched, so the strike waiver never covers it — those\n * still block until an operator records an explicit `--accept-<row>` override.\n */\nconst NEGATIVE_VERDICTS = new Set(['failed', 'blocked', 'dispatch_failed']);\n\n/**\n * PAN-3187: a tracker-closed issue with landed work cannot produce a verdict it\n * never produced while active. Negative test/verification verdicts settle only\n * when main verification proves the landed state green; review negatives remain.\n */\nfunction terminalVerdictSettlement(\n id: 'review' | 'tests' | 'verification',\n value: string | undefined,\n observed: string,\n settlement?: TerminalVerdictSettlement,\n): DodRowResult | null {\n if (!settlement?.trackerClosed || !settlement.landedWork) return null;\n if (!NEGATIVE_VERDICTS.has(value ?? '')) {\n return result(\n id,\n 'skip',\n `${observed}; settled because the tracker issue is closed and merged work is landed`,\n );\n }\n if (id !== 'review' && settlement.mainVerifyStatus === 'pass') {\n return result(\n id,\n 'skip',\n `${observed}; superseded because the tracker issue is closed, merged work is landed, and main verification passed`,\n );\n }\n return null;\n}\n\nasync function checkVerdict(\n issueId: string,\n id: 'review' | 'tests',\n field: 'reviewStatus' | 'testStatus',\n deps: DodStatusRowDeps,\n settlement?: TerminalVerdictSettlement,\n): Promise<DodRowResult> {\n const loaded = await loadStatus(issueId, deps);\n if (!loaded) return result(id, 'miss', 'no review status or journal record found');\n\n const value = loaded.status[field];\n const source = loaded.source === 'journal' ? ' from pipeline journal' : '';\n const policy = value === 'skipped' ? ' (skipped per issue policy)' : '';\n const observed = `${field}: ${value ?? 'missing'}${source}${policy}`;\n if (value === 'passed' || value === 'skipped') return result(id, 'pass', observed);\n if (strikeLanded(loaded.status) && !NEGATIVE_VERDICTS.has(value ?? '')) {\n return result(\n id,\n 'skip',\n `${observed}; skipped by the strike path (strikeLandingState: landed) — ${STRIKE_BYPASS_NOTE[id]}`,\n );\n }\n return terminalVerdictSettlement(id, value, observed, settlement) ?? result(id, 'miss', observed);\n}\n\nexport function checkReviewRow(\n issueId: string,\n deps: DodStatusRowDeps = defaultDeps,\n settlement?: TerminalVerdictSettlement,\n): Promise<DodRowResult> {\n return checkVerdict(issueId, 'review', 'reviewStatus', deps, settlement);\n}\n\nexport function checkTestsRow(\n issueId: string,\n deps: DodStatusRowDeps = defaultDeps,\n settlement?: TerminalVerdictSettlement,\n): Promise<DodRowResult> {\n return checkVerdict(issueId, 'tests', 'testStatus', deps, settlement);\n}\n\nexport async function checkVerificationRow(\n issueId: string,\n deps: DodStatusRowDeps = defaultDeps,\n settlement?: TerminalVerdictSettlement,\n): Promise<DodRowResult> {\n const loaded = await loadStatus(issueId, deps);\n if (!loaded) return result('verification', 'miss', 'no review status or journal record found');\n\n // PAN-3067: the verdict alone decides this row, exactly like rows 1 and 2.\n // `lastVerifiedCommit` is a best-effort optimization anchor — verification-runner.ts\n // snapshots it inside a try/catch and spreads the field conditionally, and a\n // `skipped` verdict never has one at all — so its absence says nothing about\n // whether verification ran. Requiring it made every issue whose anchor was\n // never written un-closable. The observed string always names the anchor's\n // presence or absence so a reader can never mistake it for a hidden condition.\n const value = loaded.status.verificationStatus;\n const commit = loaded.status.lastVerifiedCommit;\n const accepted = value === 'passed' || value === 'skipped';\n const notes: string[] = [];\n if (loaded.source === 'journal') notes.push('from pipeline journal');\n if (value === 'skipped') notes.push('skipped per issue policy');\n if (accepted && !commit) notes.push('no lastVerifiedCommit recorded');\n const observed = `verificationStatus: ${value ?? 'missing'}${commit ? ` at ${commit}` : ''}${\n notes.length > 0 ? ` (${notes.join('; ')})` : ''\n }`;\n if (accepted) return result('verification', 'pass', observed);\n // An out-of-band merge never enters merge-ops, so the CI-green skip cannot\n // record its normal verification verdict. Once rows 4 and 6 prove the landed\n // work and main CI green, that evidence satisfies row 3 without an override.\n if (value === undefined && settlement?.landedWork && settlement.mainVerifyStatus === 'pass') {\n return result('verification', 'pass', `${observed}; verification satisfied by green main CI after landing`);\n }\n return terminalVerdictSettlement('verification', value, observed, settlement) ??\n result('verification', 'miss', observed);\n}\n\nexport async function checkMergedRow(\n ctx: LifecycleContext,\n deps: MergedRowDeps = defaultMergedRowDeps,\n): Promise<MergedDodRowResult> {\n const verified: StepResult = await deps.verifyMerged(ctx).catch(error => ({\n step: 'close-out:verify-merged',\n success: false,\n skipped: false,\n error: error instanceof Error ? error.message : String(error),\n }));\n const detail = verified.details?.join('; ');\n const observed = detail || verified.error || (verified.skipped ? 'issue already closed on forge' : 'merge not verified');\n const merged = result('merged', verified.success || verified.skipped ? 'pass' : 'miss', observed) as MergedDodRowResult;\n const branchAbsent = verified.error === BRANCH_ABSENT_MERGE_ERROR;\n let durableMerges: string[] = [];\n let forgeArtifacts: MergedForgeArtifact[] = [];\n\n if (branchAbsent) {\n try {\n durableMerges = await (deps.readDurableMerges ?? defaultMergedRowDeps.readDurableMerges!)(ctx);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n merged.observed = `${merged.observed}; durable merge evidence unavailable: ${message}`;\n }\n if (!ctx.github && deps.readMergedForgeArtifacts) {\n try {\n forgeArtifacts = await deps.readMergedForgeArtifacts(ctx);\n const labels = forgeArtifacts.map(artifact => {\n const label = artifact.forge === 'gitlab'\n ? (artifact.id ? `MR !${artifact.id}` : 'GitLab MR')\n : (artifact.id ? `PR #${artifact.id}` : 'GitHub PR');\n return `${label} merged${artifact.url ? ` (${artifact.url})` : ''}`;\n });\n if (labels.length > 0) merged.observed = `${merged.observed}; ${labels.join('; ')}`;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n merged.observed = `${merged.observed}; forge artifact evidence unavailable: ${message}`;\n }\n }\n }\n\n let pullRequestState: string | undefined;\n if (ctx.github) {\n try {\n const branchName = `feature/${ctx.issueId.toLowerCase()}`;\n const pullRequest = await deps.readPullRequest(ctx, branchName);\n pullRequestState = pullRequest.state;\n merged.mergedAt = pullRequest.mergedAt;\n merged.mergeCommit = typeof pullRequest.mergeCommit === 'string'\n ? pullRequest.mergeCommit\n : pullRequest.mergeCommit?.oid;\n const number = pullRequest.number ? `PR #${pullRequest.number}` : 'PR';\n const state = pullRequest.state ?? 'state unknown';\n const at = pullRequest.mergedAt ? ` at ${pullRequest.mergedAt}` : '';\n merged.observed = `${merged.observed}; ${number} ${state}${at}`;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n merged.observed = `${merged.observed}; forge metadata unavailable: ${message}`;\n }\n }\n\n if (branchAbsent) {\n const forgeMerged = pullRequestState?.toUpperCase() === 'MERGED';\n if (durableMerges.length > 0 || forgeMerged || forgeArtifacts.length > 0) {\n merged.status = 'pass';\n if (durableMerges.length > 0) {\n merged.observed = `${merged.observed}; durable close-out record contains ${durableMerges.length} merge artifact(s)`;\n }\n } else {\n merged.observed = `${merged.observed}; no merged forge artifact or durable close-out merge record found`;\n }\n }\n\n // A successful ancestry check proves that the branch head reached main, but\n // only containment can identify that result as a non-PR landing for row 5.\n const needsContainmentEvidence = merged.status === 'miss' || (\n verified.success &&\n ctx.github !== undefined &&\n pullRequestState?.toUpperCase() !== 'MERGED'\n );\n if (needsContainmentEvidence) {\n try {\n const containment = await (deps.readBranchContainment ?? defaultMergedRowDeps.readBranchContainment!)(ctx);\n if (containment.mergedWorkRefs.length > 0 && containment.unmergedRefs.length === 0) {\n merged.status = 'pass';\n merged.evidence = 'branch-containment';\n merged.observed = `${merged.observed}; branch work contained in default branch with no merged PR — non-PR landing (membership L2-work lens): ${containment.mergedWorkRefs.join(', ')}`;\n const strikeSuffix = `:strike/${ctx.issueId.toLowerCase()}`;\n merged.containedStrikeHead = containment.mergedWorkHeads\n ?.find(candidate => candidate.ref.endsWith(strikeSuffix))?.head;\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n merged.observed = `${merged.observed}; branch containment evidence unavailable: ${message}`;\n }\n }\n\n return merged;\n}\n\nconst NEGATIVE_STRIKE_VERDICTS = new Set(['failed', 'blocked', 'dispatch_failed']);\n\n/**\n * Reconcile an out-of-band strike landing only when its durable ready marker\n * names the exact strike tip proven contained in main. This is the evidence a\n * normal merge-door landing consumes before it clears the marker.\n */\nexport async function reconcileContainedStrike(\n ctx: LifecycleContext,\n merged: MergedDodRowResult,\n deps: {\n getStatus?: (issueId: string) => Awaitable<ReviewStatus | null>;\n setStatus?: typeof setReviewStatusSync;\n } = {},\n): Promise<void> {\n const head = merged.evidence === 'branch-containment' ? merged.containedStrikeHead : undefined;\n if (!head) return;\n const current = await (deps.getStatus ?? (issueId => Effect.runPromise(getReviewStatus(issueId))))(ctx.issueId);\n if (!current || current.strikeReadyHead !== head) return;\n\n const update: ReviewStatusUpdate = {};\n if (!NEGATIVE_STRIKE_VERDICTS.has(current.reviewStatus)) update.reviewStatus = 'passed';\n if (!NEGATIVE_STRIKE_VERDICTS.has(current.testStatus)) update.testStatus = 'passed';\n if (!NEGATIVE_STRIKE_VERDICTS.has(current.verificationStatus ?? '')) {\n update.verificationStatus = 'passed';\n update.verificationNotes = `Contained strike ${head} matched durable strike readiness evidence`;\n update.lastVerifiedCommit = rehydrateHeadAnchor(head);\n }\n update.mergeStatus = 'merged';\n update.strikeLandingState = 'landed';\n update.strikeReadyHead = undefined;\n update.strikeReadyAt = undefined;\n\n const changed = Object.entries(update).some(([key, value]) => current[key as keyof ReviewStatus] !== value);\n if (changed) (deps.setStatus ?? setReviewStatusSync)(ctx.issueId, update);\n}\n\nexport async function checkPostMergeRow(\n ctx: LifecycleContext,\n merged?: MergedDodRowResult,\n deps: PostMergeRowDeps = defaultPostMergeRowDeps,\n): Promise<DodRowResult> {\n try {\n const canonicalState = await deps.readCanonicalState(ctx);\n const mergeStatus = await deps.readMergeStatus(ctx.issueId);\n const issueId = ctx.issueId.toUpperCase();\n const runningAgents = (await deps.listAgents()).filter(agent =>\n agent.issueId.toUpperCase() === issueId &&\n (agent.role === 'work' || agent.role === 'plan') &&\n (agent.status === 'starting' || agent.status === 'running'),\n );\n const stateObserved = canonicalState\n ? `canonical state: ${canonicalState}`\n : `canonical state unavailable; mergeStatus: ${mergeStatus ?? 'missing'}`;\n const agentsObserved = runningAgents.length > 0\n ? `running agents: ${runningAgents.map(agent => agent.id).join(', ')}`\n : 'no running work/planning agents';\n // PAN-3188 (row 5): terminal canonical states settle this row. 'done'\n // proves the post-merge lifecycle already ran to close-out; 'canceled'\n // makes it moot. Requiring the transient verifying_on_main marker here\n // wedges every re-evaluated terminal issue (the 11 closed issues that\n // blocked close-out sweeps on 'canonical state: done').\n if (canonicalState === 'done') {\n return result(\n 'post-merge',\n runningAgents.length === 0 ? 'pass' : 'miss',\n `terminal canonical state: done — the post-merge lifecycle already ran to close-out; ${agentsObserved}`,\n );\n }\n if (canonicalState === 'canceled') {\n return result(\n 'post-merge',\n runningAgents.length === 0 ? 'skip' : 'miss',\n `terminal canonical state: canceled — post-merge lifecycle not applicable; ${agentsObserved}`,\n );\n }\n const lifecycleObserved = canonicalState === 'verifying_on_main' || mergeStatus === 'merged';\n // PAN-3180: `postMergeLifecycle()` is the work-agent handoff — it pauses the\n // work/planning agents, stops the workspace stack, and applies\n // `verifying-on-main`. A strike has no work agent to pause and its landing is\n // owned by the Deacon's merge door, so the marker this row looks for is never\n // written and its absence proves nothing. What a strike does still owe is\n // quiescence, so a live work/planning agent remains a real miss.\n if (!lifecycleObserved && await (deps.readStrikeLanded ?? defaultPostMergeRowDeps.readStrikeLanded!)(ctx.issueId)) {\n return result(\n 'post-merge',\n runningAgents.length === 0 ? 'skip' : 'miss',\n `strike landing (strikeLandingState: landed) — the work-agent post-merge handoff is not the strike path's lifecycle; ${stateObserved}; ${agentsObserved}`,\n );\n }\n if (!lifecycleObserved && merged?.evidence === 'branch-containment') {\n return result(\n 'post-merge',\n runningAgents.length === 0 ? 'pass' : 'miss',\n `non-PR landing (branch-containment evidence) — post-merge lifecycle not applicable; ${agentsObserved}`,\n );\n }\n return result(\n 'post-merge',\n lifecycleObserved && runningAgents.length === 0 ? 'pass' : 'miss',\n `${stateObserved}; ${agentsObserved}`,\n );\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return result('post-merge', 'miss', `post-merge evidence unavailable: ${message}`);\n }\n}\n\n/**\n * PAN-3202: how many default-branch heads above the merge commit are probed for\n * the later-green-run evidence form. The tip of main is often mid-CI, so a single\n * candidate would usually read as pending; five reaches back far enough to find\n * the newest concluded green head without turning one row into a check-run storm.\n */\nconst LATER_GREEN_CANDIDATE_LIMIT = 5;\n\n/**\n * PAN-3202: the fallback evidence form for DoD row 6. When a merge lands inside a\n * red-main window, the merge commit's own check-runs can never turn green, so the\n * row was permanently unsatisfiable even after main went green hundreds of times\n * with the commit included. A later default-branch head whose check-runs all\n * concluded green, and which contains the merge commit, is strictly stronger\n * evidence than the original run: it proves main is healthy *with* the merge in it,\n * which is exactly what \"verified on main\" means. Row 7 (deploy) already reasons\n * this way through the same containment relation.\n */\nfunction requiredChecksOutcome(checks: CommitCheckRuns, required: string[]): {\n missing: string[];\n unsuccessful: string[];\n} {\n return {\n missing: required.filter(check => !checks.names.includes(check)),\n unsuccessful: required.filter(check => checks.names.includes(check) && !checks.successful.includes(check)),\n };\n}\n\nfunction hasRequiredChecksGreen(checks: CommitCheckRuns, required: string[]): boolean {\n const outcome = requiredChecksOutcome(checks, required);\n return outcome.missing.length === 0 && outcome.unsuccessful.length === 0;\n}\n\nasync function findLaterGreenDefaultBranchRun(\n ctx: LifecycleContext,\n mergeCommit: string,\n required: string[],\n deps: MainVerifyRowDeps,\n): Promise<{ run: { sha: string; total: number } | null; note: string }> {\n const readCandidates =\n deps.readContainingDefaultBranchCommits ?? defaultMainVerifyRowDeps.readContainingDefaultBranchCommits!;\n let candidates: string[];\n try {\n candidates = await readCandidates(ctx, mergeCommit);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { run: null, note: `later-run evidence unavailable: ${message}` };\n }\n\n const probed = candidates.slice(0, LATER_GREEN_CANDIDATE_LIMIT);\n for (const sha of probed) {\n try {\n const checks = await deps.readCheckRuns(ctx, sha);\n if (hasRequiredChecksGreen(checks, required)) {\n return { run: { sha, total: checks.total }, note: '' };\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { run: null, note: `later-run evidence unavailable: ${message}` };\n }\n }\n return {\n run: null,\n note: probed.length === 0\n ? 'no later default-branch commit contains the merge'\n : `no green CI run among the ${probed.length} newest default-branch commit(s) containing the merge`,\n };\n}\n\nexport async function checkMainVerifyRow(\n ctx: LifecycleContext,\n mergeCommit?: string,\n deps: MainVerifyRowDeps = defaultMainVerifyRowDeps,\n): Promise<DodRowResult> {\n if (!ctx.github || !mergeCommit) {\n return result(\n 'main-verify',\n 'skip',\n 'no merge commit resolvable — verified-on-main has no durable marker (see DoD row 6)',\n );\n }\n\n try {\n const requiredChecks = await (deps.readRequiredChecks ?? defaultMainVerifyRowDeps.readRequiredChecks!)(ctx);\n const checks = await deps.readCheckRuns(ctx, mergeCommit);\n const outcome = requiredChecksOutcome(checks, requiredChecks);\n if (hasRequiredChecksGreen(checks, requiredChecks)) {\n return result('main-verify', 'pass', `required checks concluded successfully on ${mergeCommit}: ${requiredChecks.join(', ')}`);\n }\n\n // The merge commit's own run stays the primary evidence, so its outcome is\n // always recorded first; the later-green-run form only appends to it.\n const parts = [\n ...(outcome.missing.length > 0 ? [`missing required checks on ${mergeCommit}: ${outcome.missing.join(', ')}`] : []),\n ...(outcome.unsuccessful.length > 0 ? [`required checks not successful: ${outcome.unsuccessful.join(', ')}`] : []),\n ];\n const primary = parts.join('; ');\n\n const later = await findLaterGreenDefaultBranchRun(ctx, mergeCommit, requiredChecks, deps);\n if (later.run) {\n return result(\n 'main-verify',\n 'pass',\n `${primary}; verified on main by later green CI run ${later.run.sha} containing the merge (required checks concluded successfully: ${requiredChecks.join(', ')})`,\n );\n }\n return result('main-verify', 'miss', `${primary}; ${later.note}`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return result('main-verify', 'miss', `could not read merge-commit check-runs: ${message}`);\n }\n}\n\nexport async function checkShipRow(\n ctx: LifecycleContext,\n deps: ShipRowDeps = defaultShipRowDeps,\n): Promise<DodRowResult> {\n const project = deps.readProject(ctx);\n if (!project?.version_sync) {\n return result('ship', 'skip', 'project declares no version_sync; ship step not applicable');\n }\n\n const promotedBatch = deps.findPromotedBatch(ctx);\n const ship = promotedBatch\n ? await deps.readBatchShip(project, promotedBatch)\n : (await deps.readPipeline(ctx))?.ship;\n if (!ship) {\n if (promotedBatch) {\n return result(\n 'ship',\n 'miss',\n `batch ${promotedBatch.name} includes this issue but no durable ship settlement was recorded`,\n );\n }\n return result('ship', 'skip', 'merged outside a batch; ship is batch-scoped');\n }\n if (ship.status === 'passed') {\n return result(\n 'ship',\n 'pass',\n `version ${ship.version ?? 'unknown'} shipped for batch ${ship.batch}; ${ship.paths?.length ?? 0} path(s) verified`,\n );\n }\n if (ship.status === 'pending') {\n return result(\n 'ship',\n 'miss',\n `batch ${ship.batch} merged but no version was shipped — use the Ship version action on the batch card for ${ship.batch}`,\n );\n }\n if (ship.status === 'partial') {\n const failing = ship.paths?.filter(path => !path.ok).map(path => path.path) ?? [];\n return result(\n 'ship',\n 'miss',\n `batch ${ship.batch} partially propagated version ${ship.version ?? 'unknown'}; failing paths: ${failing.join(', ') || 'unknown'}`,\n );\n }\n return result(\n 'ship',\n 'miss',\n `batch ${ship.batch} version ship failed (${ship.errorCode ?? 'unknown-failure'}): ${ship.error ?? ship.reason ?? 'inspect the local dashboard log'}`,\n );\n}\n\nexport async function checkDeployRow(\n ctx: LifecycleContext,\n merge: {\n mergedAt?: string;\n mergeCommit?: string;\n mergedRowStatus?: DodRowResult['status'];\n mainVerifyRowStatus?: DodRowResult['status'];\n },\n deps: DeployRowDeps = defaultDeployRowDeps,\n): Promise<DodRowResult> {\n if (!merge.mergeCommit) {\n if (merge.mergedRowStatus === 'miss') {\n return result(\n 'deploy',\n 'skip',\n 'no merge commit resolved because the merged row missed — deploy ancestry depends on row 4; build ancestry unchecked',\n );\n }\n // PAN-3188: row 6 (main-verify) skips whenever no merge commit is\n // resolvable — the no-durable-anchor landing class (e.g. GitLab-backed\n // landings whose row 4 evidence is the merge specialist's confirmation).\n // Row 7 must agree: same missing anchor, same skip. Only when main-verify\n // did NOT skip is the absent commit an integrity problem worth a miss.\n if (merge.mainVerifyRowStatus === 'skip') {\n return result(\n 'deploy',\n 'skip',\n 'verified-on-main skipped — no merge commit resolvable, so deploy build-ancestry has no durable anchor either (same landing class as DoD row 6 skip)',\n );\n }\n return result('deploy', 'miss', 'merged row passed without a resolvable merge commit; build ancestry cannot be checked');\n }\n\n const baseUrl = deps.dashboardUrl().replace(/\\/$/, '');\n let health: Record<string, unknown>;\n try {\n health = await deps.readJson(`${baseUrl}/api/health`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return result('deploy', 'miss', `dashboard not reachable at ${baseUrl} — a merged fix is not live if no server is serving it: ${message}`);\n }\n\n const repoRoot = typeof health.repoRoot === 'string' ? health.repoRoot : '';\n if (!repoRoot) return result('deploy', 'miss', `dashboard at ${baseUrl} did not report repoRoot`);\n if (resolve(repoRoot) !== resolve(ctx.projectPath)) {\n return result('deploy', 'skip', `live dashboard serves ${repoRoot}, not this project — deploy semantics undefined for ${ctx.projectPath}`);\n }\n\n const buildCommit = typeof health.buildCommit === 'string' && health.buildCommit.trim()\n ? health.buildCommit\n : undefined;\n if (!buildCommit) {\n return result('deploy', 'miss', `dashboard at ${baseUrl} did not report buildCommit; live deployment cannot be proven`);\n }\n try {\n if (health.buildDirty === true) {\n return result(\n 'deploy',\n 'miss',\n `live build ${buildCommit.slice(0, 8)} was built from a dirty working tree — uncommitted changes may be serving; redeploy canonically with \\`pan reload\\``,\n );\n }\n\n const canonical = await deps.commitContains(repoRoot, buildCommit, 'origin/main');\n if (!canonical) {\n return result(\n 'deploy',\n 'miss',\n `live build commit ${buildCommit.slice(0, 8)} is not an ancestor of origin/main — the server is running a build of local-only commits; redeploy with \\`pan reload\\``,\n );\n }\n\n const contains = await deps.commitContains(repoRoot, merge.mergeCommit, buildCommit);\n const observed = `build commit ${buildCommit.slice(0, 8)} ${contains ? 'contains' : 'does not contain'} merge ${merge.mergeCommit.slice(0, 8)}`;\n return result('deploy', contains ? 'pass' : 'miss', observed);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return result('deploy', 'miss', `deploy evidence unavailable: ${message}`);\n }\n}\n\nexport async function evaluateDodGate(\n ctx: LifecycleContext,\n opts: {\n acceptedRows?: DodRowId[];\n acceptedBy?: string;\n verifyMerged?: MergedRowDeps['verifyMerged'];\n } = {},\n deps: EvaluateDodGateDeps = defaultEvaluateDodGateDeps,\n): Promise<DodGateResult> {\n const acceptedRows = new Set(opts.acceptedRows ?? []);\n const overridable = new Set(DOD_ROWS.filter(row => row.overridable).map(row => row.id));\n for (const id of acceptedRows) {\n if (!overridable.has(id)) throw new TypeError(`DoD row \"${id}\" cannot be accepted; valid rows: ${[...overridable].join(', ')}`);\n }\n const by = opts.acceptedBy ?? process.env.OVERDECK_AGENT_ID ?? userInfo().username;\n if (acceptedRows.size > 0 && !canAcceptDodMisses(by)) {\n throw new TypeError('The flywheel orchestrator cannot accept missed Definition-of-Done rows; an operator must apply --accept-<row> overrides.');\n }\n\n const merged = opts.verifyMerged\n ? await checkMergedRow(ctx, { ...defaultMergedRowDeps, verifyMerged: opts.verifyMerged })\n : await deps.merged(ctx);\n await (deps.reconcileContainedStrike ?? defaultEvaluateDodGateDeps.reconcileContainedStrike!)(ctx, merged);\n // Main-verify computes before deploy because deploy's no-merge-commit\n // branch keys on main-verify's outcome (PAN-3188: row 7 skips when row 6\n // skips — both mean \"no durable anchor\" for this landing class). The verdict\n // rows also need this landed-state evidence before they can settle terminal issues.\n const mainVerify = await deps.mainVerify(ctx, merged.mergeCommit);\n const trackerClosed = await (deps.trackerClosed ?? defaultEvaluateDodGateDeps.trackerClosed!)(ctx.issueId);\n const settlement: TerminalVerdictSettlement = {\n trackerClosed,\n landedWork: merged.status === 'pass',\n mainVerifyStatus: mainVerify.status,\n };\n const [review, tests, verification, postMerge, ship, deploy] = await Promise.all([\n deps.review(ctx.issueId, settlement),\n deps.tests(ctx.issueId, settlement),\n deps.verification(ctx.issueId, settlement),\n deps.postMerge(ctx, merged),\n deps.ship(ctx),\n deps.deploy(ctx, {\n mergedAt: merged.mergedAt,\n mergeCommit: merged.mergeCommit,\n mergedRowStatus: merged.status,\n mainVerifyRowStatus: mainVerify.status,\n }),\n ]);\n const rows = [review, tests, verification, merged, postMerge, mainVerify, ship, deploy];\n for (const row of rows) {\n if (row.status === 'miss' && acceptedRows.has(row.id)) {\n row.acceptedBy = { flag: acceptFlagFor(rowDefinition(row.id)), by, at: deps.now() };\n }\n }\n const misses = rows.filter(row => row.status === 'miss').map(row => row.id);\n const accepted = rows.filter(row => row.acceptedBy).map(row => row.id);\n return {\n rows,\n misses,\n accepted,\n passed: rows.every(row => row.status !== 'miss' || Boolean(row.acceptedBy)),\n };\n}\n\n/**\n * PAN-3025: completion witness for the close-out ceremony. Returns closedOutAt\n * only when the journal is terminal AND the live row is gone (the ceremony's\n * final mutating step). closedOut with live status present means a prior run\n * aborted mid-ceremony and must fall through to complete it.\n */\nexport async function readCompletedCloseOut(issueId: string, projectPath: string): Promise<string | null> {\n try {\n const project = resolveProjectForIssue(issueId) ?? getProjectConfigFromWorkspacePath(projectPath);\n const record = await readIssueRecord(project, issueId.toUpperCase());\n if (!record?.pipeline.closedOut) return null;\n // Fail closed: a read error means we cannot confirm absence, so return null (incomplete)\n const live = await Effect.runPromise(getReviewStatus(issueId)).catch(() => 'unknown');\n if (live === 'unknown') return null; // Reject the idempotent path on any read failure\n return live ? null : (record.pipeline.closedOutAt ?? 'unknown');\n } catch {\n // On any error (record read, etc.), fail closed\n return null;\n }\n}\n","/**\n * Residue disposition: close stale convention PRs/MRs for tracker-closed\n * pre-record-era issues with no landing evidence.\n */\n\nimport { execFile } from 'child_process';\nimport { promisify } from 'util';\nimport type { StepResult } from './types.js';\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * Extract GitHub owner/repo from a repository checkout path.\n * Reads the git remote URL to resolve forge coordinates.\n */\nexport async function extractGitHubCoordinates(repoPath: string): Promise<string | null> {\n try {\n const { stdout } = await execFileAsync('git', [\n '-C', repoPath,\n 'remote', 'get-url', 'origin',\n ], { encoding: 'utf-8', timeout: 5_000 });\n\n const remoteUrl = stdout.trim();\n const match = remoteUrl.match(/github\\.com[:/]([^/]+)\\/(.+?)(\\.git)?$/);\n if (match) {\n const owner = match[1];\n const repo = match[2];\n return `${owner}/${repo}`;\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Extract GitLab project path from a repository checkout path.\n * Reads the git remote URL to resolve forge coordinates.\n */\nexport async function extractGitLabProject(repoPath: string): Promise<string | null> {\n try {\n const { stdout } = await execFileAsync('git', [\n '-C', repoPath,\n 'remote', 'get-url', 'origin',\n ], { encoding: 'utf-8', timeout: 5_000 });\n\n const remoteUrl = stdout.trim();\n const match = remoteUrl.match(/gitlab\\.com[:/](.+?)(\\.git)?$/);\n if (match) {\n return match[1];\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport interface CloseResidueConventionPrsContext {\n issueId: string;\n projectPath: string;\n github?: { repos: string[] }; // owner/repo paths for GitHub\n gitlab?: { projects: string[] }; // GitLab project paths\n}\n\nexport async function closeResidueConventionPrs(\n ctx: CloseResidueConventionPrsContext,\n): Promise<StepResult> {\n const evidence: string[] = [];\n const fatalErrors: string[] = [];\n const issueIdLower = ctx.issueId.toLowerCase();\n const possibleHeads = [`feature/${issueIdLower}`, `strike/${issueIdLower}`];\n\n // GitHub PR close — handle all configured repositories\n if (ctx.github?.repos) {\n for (const githubRepo of ctx.github.repos) {\n for (const head of possibleHeads) {\n try {\n const { stdout } = await execFileAsync('gh', [\n 'pr', 'list',\n '--repo', githubRepo,\n '--head', head,\n '--state', 'open',\n '--json', 'number',\n ], { encoding: 'utf-8', timeout: 15_000 });\n\n const prs = JSON.parse(stdout) as Array<{ number: number }>;\n if (prs.length === 0) {\n evidence.push(`No open GitHub PRs found on ${githubRepo}/${head}`);\n }\n for (const pr of prs) {\n try {\n await execFileAsync('gh', [\n 'pr', 'close',\n `${pr.number}`,\n '--repo', githubRepo,\n '--comment', 'Closing stale residue PR with no merge claim — issue closed out without merge landing.',\n ], { encoding: 'utf-8', timeout: 15_000 });\n evidence.push(`Closed GitHub PR #${pr.number} on ${githubRepo}/${head}`);\n } catch (err) {\n fatalErrors.push(`GitHub PR #${pr.number} close failed on ${githubRepo}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n } catch (err) {\n fatalErrors.push(`GitHub PR lookup for ${githubRepo}/${head} failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n }\n }\n\n // GitLab MR close — write note BEFORE closing to ensure auditability on retry\n if (ctx.gitlab?.projects) {\n for (const glProject of ctx.gitlab.projects) {\n for (const head of possibleHeads) {\n try {\n const { stdout } = await execFileAsync('glab', [\n 'mr', 'list',\n '--repo', glProject,\n '--source-branch', head,\n '--state', 'opened',\n '--output', 'json',\n ], { encoding: 'utf-8', timeout: 15_000 });\n\n const mrs = JSON.parse(stdout) as Array<{ iid: number }>;\n if (mrs.length === 0) {\n evidence.push(`No open GitLab MRs found on ${glProject}/${head}`);\n }\n for (const mr of mrs) {\n try {\n // Write the audit note BEFORE closing to ensure it's recorded\n await execFileAsync('glab', [\n 'mr', 'note',\n '--repo', glProject,\n String(mr.iid),\n '--message', 'Closing stale residue MR with no merge claim — issue closed out without merge landing.',\n ], { encoding: 'utf-8', timeout: 15_000 });\n\n // Now close the MR\n await execFileAsync('glab', [\n 'mr', 'close',\n '--repo', glProject,\n String(mr.iid),\n ], { encoding: 'utf-8', timeout: 15_000 });\n\n evidence.push(`Closed GitLab MR !${mr.iid} on ${glProject}/${head}`);\n } catch (err) {\n fatalErrors.push(`GitLab MR !${mr.iid} operation failed on ${glProject}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n } catch (err) {\n fatalErrors.push(`GitLab MR lookup for ${glProject}/${head} failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n }\n }\n\n // Fail on any repository/operation errors — even if some PRs/MRs closed\n if (fatalErrors.length > 0) {\n return {\n step: 'Close stale convention PRs/MRs',\n success: false,\n skipped: false,\n error: `Failed to list/close convention PRs/MRs: ${fatalErrors.join('; ')}`,\n details: evidence.length > 0 ? [`Partial progress before failure: ${evidence.join('; ')}`] : undefined,\n };\n }\n\n if (evidence.length === 0) {\n return {\n step: 'Close stale convention PRs/MRs',\n success: true,\n skipped: true,\n details: ['No open convention PRs/MRs found on any configured repository'],\n };\n }\n\n return {\n step: 'Close stale convention PRs/MRs',\n success: true,\n skipped: false,\n details: evidence,\n };\n}\n","/**\n * Lifecycle workflows — Compose atomic operations into complete workflows.\n *\n * approve() — Post-merge: archive + close + teardown\n * close() — Simple close: close-issue + teardown\n * closeOut() — Full ceremony: verify-merged + archive + teardown + close + label + clear-status\n * deepWipe() — Destructive: teardown(deleteBranches) + delete agent state + reset issue\n */\n\nimport { existsSync } from 'fs';\nimport { copyFile, readFile, writeFile } from 'fs/promises';\nimport { join } from 'path';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport { Effect } from 'effect';\nimport { OVERDECK_HOME } from '../paths.js';\nimport type {\n LifecycleContext,\n WorkflowResult,\n StepResult,\n ApproveOptions,\n DeepWipeOptions,\n ArchiveOptions,\n CloseOutOptions,\n} from './types.js';\nimport { stepOk, stepSkipped, stepFailed, getLinearApiKey } from './types.js';\nimport { archivePlanning, findWorkspacePath } from './archive-planning.js';\nimport { closeIssue, type CloseIssueOptions } from './close-issue.js';\nimport { teardownWorkspace } from './teardown-workspace.js';\nimport { loadCloisterConfig } from '../cloister/config.js';\nimport { extractNumberSync, extractPrefixSync } from '../issue-id.js';\nimport { recordFeatureRegistryLifecycle } from '../registry/feature-registry-population.js';\nimport { getForgeAdapter } from '../forge.js';\nimport { resolveProjectReposForIssueSync } from '../project-repos.js';\nimport {\n getProjectConfigFromWorkspacePath,\n markRecordPipelineClosedOutSync,\n markRecordPipelineResidueClosedOutSync,\n writeCloseOutDodGate,\n} from '../pan-dir/record.js';\nimport { pruneStoppedAgentsForIssue } from '../cloister/agent-gc.js';\nimport { isTrackerIssueClosed } from '../cloister/issue-closed.js';\nimport { acknowledgeAllOpenRecoveryTrips } from '../cloister/recovery-trip.js';\nimport { clearAgentOperatorGatesForIssueSync } from '../agents/agent-state.js';\nimport { evaluateDodGate, readCompletedCloseOut } from './dod-gate.js';\nimport { closeResidueConventionPrs, extractGitHubCoordinates, extractGitLabProject } from './residue.js';\nimport {\n capturePipelineStage,\n resolvePipelineTelemetryContext,\n type PipelineTelemetryContext,\n} from '../telemetry/pipeline.js';\nimport { acceptFlagFor, BRANCH_ABSENT_MERGE_ERROR, buildAbandonedDodGate, buildResidueDodGate, DOD_ROWS, type DodGateResult, type DodRowId } from './dod.js';\n\nconst execAsync = promisify(exec);\n\nfunction trackerName(ctx: LifecycleContext, fallback: string): string {\n const name = ctx.tracker?.name ?? fallback;\n return name.charAt(0).toUpperCase() + name.slice(1);\n}\n\n/**\n * Build a WorkflowResult from collected steps.\n */\nfunction buildResult(\n workflow: WorkflowResult['workflow'],\n issueId: string,\n steps: StepResult[],\n startTime: number,\n dodGate?: DodGateResult,\n): WorkflowResult {\n return {\n workflow,\n issueId,\n success: steps.every(s => s.success),\n steps,\n duration: Date.now() - startTime,\n ...(dodGate ? { dodGate } : {}),\n };\n}\n\nfunction hasBlockingFailure(steps: StepResult[]): boolean {\n return steps.some(s => !s.success && !s.skipped);\n}\n\n/**\n * approve() — Post-merge lifecycle.\n */\nexport function approve(\n ctx: LifecycleContext,\n opts: ApproveOptions & CloseIssueOptions & ArchiveOptions = {},\n): Effect.Effect<WorkflowResult> {\n return Effect.gen(function* () {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n\n // 1. Archive planning\n const archiveSteps = yield* archivePlanning(ctx, opts);\n allSteps.push(...archiveSteps);\n\n // If archive failed, stop — don't destroy unarchived artifacts\n const archiveFailed = archiveSteps.some(s => !s.success && !s.skipped);\n if (archiveFailed) {\n allSteps.push(stepFailed('approve:abort', 'Stopped — archiving failed, workspace preserved'));\n return buildResult('approve', ctx.issueId, allSteps, start);\n }\n\n // 2. Close issue\n const closeSteps = yield* closeIssue(ctx, {\n tracker: opts.tracker,\n comment: 'Merged to main via Overdeck lifecycle',\n applyLabel: true,\n });\n allSteps.push(...closeSteps);\n\n // 3. Teardown workspace (delete branches — merge is complete)\n const teardownSteps = yield* teardownWorkspace(ctx, { deleteBranches: true });\n allSteps.push(...teardownSteps);\n\n // 5. Clear review status\n const clearResult = yield* clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n\n return buildResult('approve', ctx.issueId, allSteps, start);\n });\n}\n\n/**\n * close() — Simple issue close with teardown.\n */\nexport function close(\n ctx: LifecycleContext,\n opts: CloseIssueOptions = {},\n): Effect.Effect<WorkflowResult> {\n return Effect.gen(function* () {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n\n // 1. Close issue\n const closeSteps = yield* closeIssue(ctx, {\n tracker: opts.tracker,\n reason: opts.reason,\n applyLabel: false,\n });\n allSteps.push(...closeSteps);\n\n // 2. Teardown workspace\n const teardownSteps = yield* teardownWorkspace(ctx);\n allSteps.push(...teardownSteps);\n\n // 3. Clear review status\n const clearResult = yield* clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n\n return buildResult('close', ctx.issueId, allSteps, start);\n });\n}\n\n/**\n * closeOut() — Full close-out ceremony.\n *\n * This is the human-gated verification and cleanup workflow.\n * Replaces the monolithic executeCloseOut() function.\n *\n * 1. Verify branch merged (hard fail if not — must pass before any cleanup)\n * 2. Move PRD + archive workspace artifacts (hard fail if archiving fails)\n * 3. Mark xBRIEF completed\n * 4. Clean up workspace (tmux, TLDR, Docker, worktree)\n * 5. Clean up agent state\n * 6. Close issue on tracker\n * 7. Apply closed-out label\n * 8. Clear review status\n */\nexport function closeOut(\n ctx: LifecycleContext,\n opts: CloseIssueOptions & ArchiveOptions & CloseOutOptions = {},\n): Effect.Effect<WorkflowResult> {\n return Effect.gen(function* () {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n\n // PAN-3025: idempotent short-circuit for already-completed close-out ceremony.\n // Fail closed: on any error, proceed to the gate (null = not-confirmed-complete).\n const closedOutAt = yield* Effect.promise(() =>\n readCompletedCloseOut(ctx.issueId, ctx.projectPath).catch(() => null)\n );\n if (closedOutAt) {\n allSteps.push(stepSkipped('close-out:idempotent', [\n `Issue already closed out at ${closedOutAt} — skipping the Definition-of-Done gate and ceremony`,\n ]));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // Recover UAT-promotion evidence before the gate reads the verification verdict.\n const uatEvidenceStep = yield* Effect.promise(async () => {\n try {\n const { healUatPromotionVerification } = await import('../cloister/uat-promote-verification.js');\n const evidence = await healUatPromotionVerification(ctx.issueId);\n if (!evidence) {\n return stepSkipped('dod:uat-promotion-evidence', ['No missing UAT-promotion verification evidence found']);\n }\n return stepOk('dod:uat-promotion-evidence', [\n `Recorded verification from ${evidence.generation}`,\n ...(evidence.mergeSha ? [`Promoted to main at ${evidence.mergeSha.slice(0, 9)}`] : []),\n ]);\n } catch (err) {\n return stepSkipped('dod:uat-promotion-evidence', [\n `Evidence recovery unavailable: ${err instanceof Error ? err.message : String(err)}`,\n ]);\n }\n });\n allSteps.push(uatEvidenceStep);\n\n // 1. Collect residue evidence BEFORE building the gate (if residue disposition)\n const abandon = opts.abandonDisposition;\n const residue = opts.residueDisposition;\n let residueEvidence: string[] = [];\n let trackerClosedEvidence: string[] = [];\n\n if (residue) {\n // Pre-verify tracker is closed for residue disposition\n try {\n const isClosed = yield* Effect.promise(() => isTrackerIssueClosed(ctx.issueId));\n if (!isClosed) {\n allSteps.push(stepFailed('close-out:residue-precondition', 'Residue disposition requires the tracker issue to be already closed'));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n trackerClosedEvidence.push('Tracker issue verified closed');\n } catch (err) {\n allSteps.push(stepFailed('close-out:residue-precondition', `Could not verify tracker closure: ${err instanceof Error ? err.message : String(err)}`));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // Resolve forge coordinates and close stale PRs/MRs\n const prRepos = resolveProjectReposForIssueSync(ctx.issueId);\n if (!prRepos || prRepos.length === 0) {\n allSteps.push(stepFailed('close-out:residue-precondition', 'Could not resolve any configured repositories for residue cleanup'));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // Resolve all GitHub and GitLab coordinates\n const githubPaths = prRepos.filter(r => r.forge === 'github').map(r => r.repoPath);\n const gitlabPaths = prRepos.filter(r => r.forge === 'gitlab').map(r => r.repoPath);\n\n const resolveCoords = yield* Effect.promise(async () => {\n const githubRepos: string[] = [];\n const gitlabRepos: string[] = [];\n const errors: string[] = [];\n\n for (const repoPath of githubPaths) {\n const coords = await extractGitHubCoordinates(repoPath);\n if (coords) {\n githubRepos.push(coords);\n } else {\n errors.push(`GitHub coordinate extraction failed for ${repoPath}`);\n }\n }\n\n for (const repoPath of gitlabPaths) {\n const proj = await extractGitLabProject(repoPath);\n if (proj) {\n gitlabRepos.push(proj);\n } else {\n errors.push(`GitLab project extraction failed for ${repoPath}`);\n }\n }\n\n return { githubRepos, gitlabRepos, errors };\n });\n\n // Fail if any configured repository could not be resolved\n if (resolveCoords.errors.length > 0) {\n allSteps.push(stepFailed('close-out:residue-precondition', `Could not resolve all forge coordinates: ${resolveCoords.errors.join('; ')}`));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // Fail if no repositories were successfully resolved\n if (resolveCoords.githubRepos.length === 0 && resolveCoords.gitlabRepos.length === 0) {\n allSteps.push(stepFailed('close-out:residue-precondition', 'No forge coordinates were resolved for residue cleanup'));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // Execute residue cleanup\n const residueStep = yield* Effect.promise(() => closeResidueConventionPrs({\n issueId: ctx.issueId,\n projectPath: ctx.projectPath,\n github: resolveCoords.githubRepos.length > 0 ? { repos: resolveCoords.githubRepos } : undefined,\n gitlab: resolveCoords.gitlabRepos.length > 0 ? { projects: resolveCoords.gitlabRepos } : undefined,\n }));\n allSteps.push(residueStep);\n if (!residueStep.success && !residueStep.skipped) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — residue PR/MR close failed'));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n residueEvidence = residueStep.details ?? [];\n }\n\n // 2. Build the Definition-of-Done gate with collected evidence\n let dodGate: DodGateResult = abandon\n ? buildAbandonedDodGate(abandon.reason, abandon.by)\n : residue\n ? buildResidueDodGate(residue.reason, residue.by, [\n ...trackerClosedEvidence,\n ...residueEvidence,\n ])\n : yield* Effect.promise(() => evaluateDodGate(ctx, {\n acceptedRows: opts.dodAcceptedRows,\n acceptedBy: opts.dodAcceptedBy,\n verifyMerged: verifyBranchMergedImpl,\n }));\n\n for (const row of dodGate.rows) {\n const details = [`expected: ${row.expected}`, `observed: ${row.observed}`];\n if (row.acceptedBy) {\n allSteps.push(stepSkipped(`dod:${row.id}`, [\n ...details,\n `MISS accepted via ${row.acceptedBy.flag} by ${row.acceptedBy.by} at ${row.acceptedBy.at}`,\n ]));\n } else if (row.status === 'skip') {\n allSteps.push(stepSkipped(`dod:${row.id}`, details));\n } else if (row.status === 'pass') {\n allSteps.push(stepOk(`dod:${row.id}`, details));\n } else {\n allSteps.push(stepFailed(`dod:${row.id}`, row.observed, details));\n }\n }\n if (!dodGate.passed) {\n const flags = dodGate.rows\n .filter(row => row.status === 'miss' && !row.acceptedBy)\n .map(row => `${row.id} (${acceptFlagFor(DOD_ROWS.find(def => def.id === row.id)!)})`);\n allSteps.push(stepFailed(\n 'close-out:dod-gate',\n `Definition-of-Done gate blocked close-out: ${flags.join(', ')}. Re-run with the named --accept-<row> flag to record an explicit override.`,\n ));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n\n // 3. Move PRD + archive workspace artifacts\n // (Note: step numbering adjusted due to upfront residue cleanup)\n\n // 2. Move PRD + archive workspace artifacts\n const archiveSteps = yield* archivePlanning(ctx, opts);\n allSteps.push(...archiveSteps);\n\n // Hard fail on archive failure — don't destroy unarchived artifacts\n const archiveFailed = archiveSteps.some(s => !s.success && !s.skipped);\n if (archiveFailed) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — archiving failed, workspace preserved'));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n\n // 3. Mark the xBRIEF completed on main before teardown removes local state.\n const xbriefStep = yield* Effect.promise(() => completeXBriefStep(ctx));\n allSteps.push(xbriefStep);\n if (!xbriefStep.success && !xbriefStep.skipped) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — xBRIEF completion failed, workspace preserved'));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n\n // 5+6. Teardown workspace + agent state\n const telemetryContext: PipelineTelemetryContext | null = yield* Effect.promise(async () => {\n try {\n return await resolvePipelineTelemetryContext(ctx.issueId);\n } catch {\n // Membership and agent attribution are best-effort and must not abort close-out.\n return null;\n }\n });\n const closeOutConfig = (yield* Effect.promise(() => Effect.runPromise(loadCloisterConfig()))).close_out;\n const teardownSteps = yield* teardownWorkspace(ctx, {\n deleteWorkspace: closeOutConfig?.remove_workspace ?? false,\n deleteBranches: closeOutConfig?.delete_feature_branch ?? false,\n });\n allSteps.push(...teardownSteps);\n if (hasBlockingFailure(teardownSteps)) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — teardown failed, tracker issue and review status preserved'));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n\n const teardownDef = DOD_ROWS.find(row => row.id === 'teardown')!;\n dodGate.rows.push({\n ...teardownDef,\n status: 'pass',\n observed: teardownSteps.flatMap(step => step.details ?? []).join('; ') || 'close-out teardown completed',\n });\n\n // 6+7. Close issue + apply label\n const closeSteps = yield* closeIssue(ctx, {\n tracker: opts.tracker,\n comment: abandon\n ? `Closed without landing evidence — disposition recorded: ${abandon.reason}`\n : ctx.auto ? 'Closed via automatic close-out ceremony' : 'Closed via close-out ceremony',\n applyLabel: true,\n });\n allSteps.push(...closeSteps);\n if (hasBlockingFailure(closeSteps)) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — issue close failed, review status preserved'));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n\n // 8. Mark durable pipeline terminal before clearing the DB cache.\n const markTerminal = yield* markPipelineClosedOutStep(ctx, residue);\n allSteps.push(markTerminal);\n\n // Update gate with verified residue evidence before recording if a residue row exists\n if (residue && residueEvidence.length > 0) {\n const residueRow = dodGate.rows.find(row => (row.id as string) === 'residue');\n if (residueRow) {\n residueRow.observed = residueEvidence.join('; ');\n }\n }\n\n const recordDodGate = yield* recordDodGateStep(ctx, dodGate, abandon, residue);\n allSteps.push(recordDodGate);\n if (!recordDodGate.success) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — Definition-of-Done audit could not be persisted; review status preserved'));\n return buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n }\n if (markTerminal.success) {\n const pruned = yield* Effect.promise(() => pruneStoppedAgentsForIssue(ctx.issueId));\n allSteps.push(pruned.preserved.length > 0\n ? stepSkipped('close-out:prune-agent-rows', [`Preserved live agents or terminal rows with retained transcripts: ${pruned.preserved.join(', ')}`])\n : stepOk('close-out:prune-agent-rows', [`Pruned ${pruned.removed.length} stopped agent row(s)`]));\n\n // PAN-3727: acknowledge open recovery trips and clear operator-gate\n // residue (stoppedByUser/paused/troubled) so a terminal issue's\n // preserved agent rows and record stop reappearing in the parked\n // population. The two doors are independent residue — run and catch\n // each separately (review finding) so a trip-ack failure can never\n // suppress gate clearing, or vice versa. Non-blocking overall — a\n // bookkeeping failure must never strand an already-merged close-out.\n allSteps.push(yield* Effect.promise(async () => {\n let trips = 0;\n let tripsError: string | undefined;\n try {\n trips = await acknowledgeAllOpenRecoveryTrips(ctx.issueId);\n } catch (err) {\n tripsError = (err as Error).message ?? String(err);\n }\n let gates: string[] = [];\n let gatesError: string | undefined;\n try {\n gates = clearAgentOperatorGatesForIssueSync(ctx.issueId);\n } catch (err) {\n gatesError = (err as Error).message ?? String(err);\n }\n const summary = `Acked ${trips} open trip(s); cleared operator gates on ${gates.length} agent row(s)`;\n if (!tripsError && !gatesError) {\n return stepOk('close-out:ack-parked-residue', [summary]);\n }\n return stepSkipped('close-out:ack-parked-residue', [\n summary,\n ...(tripsError ? [`trip acknowledgement failed: ${tripsError}`] : []),\n ...(gatesError ? [`gate clearing failed: ${gatesError}`] : []),\n ]);\n }));\n }\n\n // 9. Clear review status\n const clearResult = yield* clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n\n yield* Effect.promise(() => resetPostMergeStateForIssue(ctx.issueId));\n yield* Effect.promise(() => recordFeatureRegistryLifecycle({ issueId: ctx.issueId, status: 'archived' }));\n\n const result = buildResult('close-out', ctx.issueId, allSteps, start, dodGate);\n if (result.success && !markTerminal.skipped) capturePipelineStage('closed_out', telemetryContext);\n return result;\n });\n}\n\nfunction markPipelineClosedOutStep(ctx: LifecycleContext, residue?: { reason: string; by: string }): Effect.Effect<StepResult> {\n const step = 'close-out:mark-pipeline-terminal';\n return Effect.try({\n try: () => {\n const project = getProjectConfigFromWorkspacePath(ctx.projectPath);\n if (residue) {\n markRecordPipelineResidueClosedOutSync(project, ctx.issueId.toUpperCase());\n } else {\n markRecordPipelineClosedOutSync(project, ctx.issueId.toUpperCase());\n }\n return stepOk(step, ['Marked durable pipeline journal closed-out']);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepSkipped(step, [`Pipeline terminal marker failed (non-fatal): ${(err as Error).message ?? String(err)}`])),\n ),\n );\n}\n\nfunction recordDodGateStep(ctx: LifecycleContext, dodGate: DodGateResult, abandonDisposition?: { reason: string; by: string }, residueDisposition?: { reason: string; by: string }): Effect.Effect<StepResult> {\n const step = 'close-out:record-dod-gate';\n return Effect.tryPromise({\n try: async () => {\n const project = getProjectConfigFromWorkspacePath(ctx.projectPath);\n await writeCloseOutDodGate(project, ctx.issueId.toUpperCase(), {\n evaluatedAt: new Date().toISOString(),\n rows: dodGate.rows,\n accepted: dodGate.accepted,\n ...(abandonDisposition ? { disposition: abandonDisposition } : {}),\n ...(residueDisposition ? { disposition: residueDisposition } : {}),\n });\n return stepOk(step, ['Recorded Definition-of-Done gate with 8 rows']);\n },\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed(step, `Definition-of-Done gate record failed: ${(err as Error).message ?? String(err)}`)),\n ),\n );\n}\n\n/**\n * deepWipe() — Destructive cleanup for abandoned workspaces.\n */\nfunction destructiveResetWorkflow(\n workflow: 'deep-wipe' | 'reset' | 'cancel',\n ctx: LifecycleContext,\n opts: DeepWipeOptions,\n resetStep: (ctx: LifecycleContext) => Effect.Effect<StepResult>,\n progressLabel: string,\n progressSuccessDetail: string,\n): Effect.Effect<WorkflowResult> {\n return Effect.gen(function* () {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n const { deleteWorkspace = true, deleteBranches = true, resetIssue = true, onProgress } = opts;\n const resetContext = opts.tracker ? { ...ctx, tracker: opts.tracker } : ctx;\n\n const TOTAL_STEPS = 3 + (resetIssue ? 1 : 0);\n let stepNum = 0;\n\n const progress = (label: string, detail: string, status: 'active' | 'complete' | 'error' = 'active') => {\n onProgress?.({ step: stepNum, total: TOTAL_STEPS, label, detail, status });\n };\n\n stepNum = 1;\n\n // Preserve PRD before workspace teardown so it survives reset/cancel.\n const issueLower = ctx.issueId.toLowerCase();\n const workspacePath = findWorkspacePath(ctx.projectPath, issueLower);\n if (workspacePath && existsSync(workspacePath)) {\n const prdPath = join(workspacePath, '.pan', 'prd.md');\n if (existsSync(prdPath)) {\n yield* Effect.tryPromise({\n try: async () => {\n const activeDir = join(ctx.projectPath, 'docs', 'prds', 'active', issueLower);\n const { mkdir } = await import('fs/promises');\n await mkdir(activeDir, { recursive: true });\n await copyFile(prdPath, join(activeDir, 'prd.md'));\n },\n catch: () => null,\n }).pipe(Effect.catch(() => Effect.void));\n }\n }\n\n progress('Tearing down workspace', 'Killing agents, stopping services, removing files');\n const teardownSteps = yield* teardownWorkspace(ctx, {\n deleteWorkspace,\n deleteBranches,\n workspaceConfig: opts.workspaceConfig,\n projectName: opts.projectName,\n });\n allSteps.push(...teardownSteps);\n const teardownFailed = teardownSteps.some(s => !s.success && !s.skipped);\n progress('Tearing down workspace', teardownFailed ? 'Some steps failed' : 'Workspace torn down', teardownFailed ? 'error' : 'complete');\n\n stepNum = 2;\n progress('Deleting git branches', `feature/${ctx.issueId.toLowerCase()}`);\n progress('Deleting git branches', deleteBranches ? 'Branches removed' : 'Skipped', 'complete');\n\n if (resetIssue) {\n stepNum = 3;\n progress(progressLabel, `${ctx.issueId}`);\n const resetResult = yield* resetStep(resetContext);\n allSteps.push(resetResult);\n progress(progressLabel, resetResult.success ? progressSuccessDetail : (resetResult.error || 'Failed'), resetResult.success ? 'complete' : 'error');\n }\n\n stepNum = resetIssue ? 4 : 3;\n progress('Clearing review status', 'Removing specialist state');\n const clearResult = yield* clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n progress('Clearing review status', 'Review status cleared', 'complete');\n\n return buildResult(workflow, ctx.issueId, allSteps, start);\n });\n}\n\nexport function deepWipe(\n ctx: LifecycleContext,\n opts: DeepWipeOptions = {},\n): Effect.Effect<WorkflowResult> {\n return destructiveResetWorkflow(\n 'deep-wipe',\n ctx,\n opts,\n resetIssueToTodo,\n 'Resetting issue status',\n 'Issue reset to Todo',\n );\n}\n\nexport function resetToTodo(\n ctx: LifecycleContext,\n opts: DeepWipeOptions = {},\n): Effect.Effect<WorkflowResult> {\n return destructiveResetWorkflow(\n 'reset',\n ctx,\n opts,\n resetIssueToTodo,\n 'Resetting issue status',\n 'Issue reset to Todo',\n );\n}\n\nexport function cancelIssueWorkflow(\n ctx: LifecycleContext,\n opts: DeepWipeOptions = {},\n): Effect.Effect<WorkflowResult> {\n return destructiveResetWorkflow(\n 'cancel',\n ctx,\n opts,\n resetIssueToCanceled,\n 'Canceling issue',\n 'Issue moved to Canceled',\n );\n}\n\n// --- Internal helpers ---\n\nasync function completeXBriefStep(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'close-out:vbrief-completed';\n try {\n const { transitionXBriefOnMain } = await import('../xbrief/lifecycle-io.js');\n const result = await Effect.runPromise(transitionXBriefOnMain(\n ctx.projectPath,\n ctx.issueId,\n 'completed',\n 'completed',\n `scope: complete ${ctx.issueId.toUpperCase()} xBRIEF`,\n ));\n const details = [\n result.moved ? 'Updated xBRIEF lifecycle to completed' : 'xBRIEF lifecycle already completed',\n result.statusUpdated ? 'Updated plan.status to completed' : 'plan.status already completed',\n ];\n if (result.committed) details.push('Committed xBRIEF completion on main');\n return stepOk(step, details);\n } catch (err) {\n const cause = (err as { cause?: unknown }).cause ?? err;\n const message = cause instanceof Error ? cause.message : String(cause);\n if (message.includes('No xBRIEF found')) {\n return stepSkipped(step, [`No xBRIEF found for ${ctx.issueId}`]);\n }\n return stepFailed(step, `xBRIEF completion failed: ${message}`);\n }\n}\n\n/**\n * Verify feature branch is merged into main.\n */\nfunction verifyBranchMerged(ctx: LifecycleContext): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => verifyBranchMergedImpl(ctx),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('close-out:verify-merged', `Could not verify merge: ${(err as Error).message}`)),\n ),\n );\n}\n\nexport interface MergeVerificationRoot { repoKey: string; dir: string; sourceBranch: string; targetBranch: string; forge: 'github' | 'gitlab' }\n\nasync function verifyRootConventionBranches(ctx: LifecycleContext, root: MergeVerificationRoot, issueLower: string): Promise<StepResult | null> {\n const featureResult = await verifyConventionBranchMerged(ctx, root);\n if (featureResult?.success) return featureResult;\n\n const strikeResult = await verifyConventionBranchMerged(ctx, { ...root, sourceBranch: `strike/${issueLower}` });\n if (strikeResult?.success && !strikeResult.skipped) {\n return stepOk('close-out:verify-merged', [\n ...(strikeResult.details ?? []),\n `Superseded ${root.sourceBranch} residual: ${featureResult?.error ?? BRANCH_ABSENT_MERGE_ERROR}`,\n ]);\n }\n return featureResult;\n}\n\nexport async function verifyBranchMergedImpl(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'close-out:verify-merged';\n const issueLower = ctx.issueId.toLowerCase();\n const defaultRoot: MergeVerificationRoot = { repoKey: issueLower, dir: ctx.projectPath, sourceBranch: `feature/${issueLower}`, targetBranch: 'main', forge: 'github' };\n\n try {\n // Check review-status first — the merge specialist validates before marking merged\n try {\n const { loadReviewStatuses } = await import('../review-status.js');\n const statuses = loadReviewStatuses();\n const issueKey = ctx.issueId.toUpperCase();\n if (statuses[issueKey]?.mergeStatus === 'merged') {\n return stepOk(step, ['Merge specialist confirmed merge completed']);\n }\n } catch {\n // review-status.json may not exist, continue with git checks\n }\n\n const resolvedRoots = resolveProjectReposForIssueSync(ctx.issueId)\n ?.filter((repo) => repo.required)\n .map((repo): MergeVerificationRoot => ({\n repoKey: repo.repoKey,\n dir: repo.repoPath,\n sourceBranch: repo.sourceBranch,\n targetBranch: repo.targetBranch,\n forge: repo.forge,\n }));\n const roots = resolvedRoots?.length ? resolvedRoots : [defaultRoot];\n if (roots.length === 1) {\n return await verifyRootConventionBranches(ctx, roots[0], issueLower)\n ?? stepFailed(step, BRANCH_ABSENT_MERGE_ERROR);\n }\n\n const results = await Promise.all(roots.map(async (root) => ({\n root,\n result: await verifyRootConventionBranches(ctx, root, issueLower),\n })));\n const failures = results.filter(({ result }) => result && !result.success);\n if (failures.length > 0) {\n return stepFailed(step, failures.map(({ root, result }) => `${root.repoKey}: ${result?.error}`).join('; '));\n }\n const successes = results.filter(({ result }) => result?.success);\n if (successes.length > 0) {\n return stepOk(step, successes.flatMap(({ root, result }) =>\n (result?.details ?? []).map((detail) => `${root.repoKey}: ${detail}`)));\n }\n return stepFailed(step, BRANCH_ABSENT_MERGE_ERROR);\n } catch (err) {\n return stepFailed(step, `Could not verify merge: ${(err as Error).message}`);\n }\n}\n\ninterface GitLabMergeLookupCache {\n attempted: boolean;\n headSha?: string;\n artifact?: { id?: string; url?: string } | null;\n}\n\ninterface GitLabMergeMatch {\n result: StepResult;\n reused: boolean;\n}\n\nexport async function verifyConventionBranchMerged(ctx: LifecycleContext, root: MergeVerificationRoot): Promise<StepResult | null> {\n const step = 'close-out:verify-merged';\n const gitlabLookup: GitLabMergeLookupCache = { attempted: false };\n const { stdout: branchExists } = await execAsync(\n `git branch --list \"${root.sourceBranch}\" 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n\n if (branchExists.trim()) {\n // Use merge-base --is-ancestor: checks if the branch tip is reachable from main\n try {\n await execAsync(\n `git merge-base --is-ancestor ${root.sourceBranch} ${root.targetBranch}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const remoteCheck = await verifyRemoteBranchIfPresent(ctx, root, gitlabLookup);\n if (remoteCheck && !remoteCheck.success) return remoteCheck;\n return stepOk(step, ['All commits merged to main', ...(remoteCheck?.details ?? [])]);\n } catch {\n // --is-ancestor fails for squash merges where the branch still exists.\n const gitlabMerged = await verifySquashMergedMrByBranch(root, root.sourceBranch, gitlabLookup);\n if (gitlabMerged) {\n const remoteCheck = await verifyRemoteBranchIfPresent(ctx, root, gitlabLookup);\n if (remoteCheck && !remoteCheck.success) return remoteCheck;\n return stepOk(step, [...(gitlabMerged.result.details ?? []), ...(remoteCheck?.details ?? [])]);\n }\n\n try {\n const { stdout: codeDiff } = await execAsync(\n `git diff ${root.targetBranch}...${root.sourceBranch} -- ':!.planning' ':!docs/prds' ':!.overdeck/prompts' 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n if (!codeDiff.trim()) {\n const remoteCheck = await verifyRemoteBranchIfPresent(ctx, root, gitlabLookup);\n if (remoteCheck && !remoteCheck.success) return remoteCheck;\n return stepOk(step, [\n 'Code changes squash-merged to main (only planning artifacts remain on branch)',\n ...(remoteCheck?.details ?? []),\n ]);\n }\n } catch {\n // diff failed — fall through to unmerged report\n }\n\n const githubMerged = root.forge === 'github'\n ? await verifySquashMergedPrByBranch(ctx, root, root.sourceBranch)\n : null;\n if (githubMerged?.success) {\n const remoteCheck = await verifyRemoteBranchIfPresent(ctx, root, gitlabLookup);\n if (remoteCheck && !remoteCheck.success) return remoteCheck;\n return stepOk(step, [\n ...(githubMerged.details ?? []),\n ...(remoteCheck?.details ?? []),\n ]);\n }\n if (githubMerged) return githubMerged;\n\n const { stdout: unmerged } = await execAsync(\n `git log ${root.targetBranch}..${root.sourceBranch} --oneline 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const count = unmerged.trim() ? unmerged.trim().split('\\n').length : 0;\n\n if (ctx.github && root.forge === 'github') {\n try {\n const { stdout: issueState } = await execAsync(\n `gh issue view ${ctx.github.number} --repo ${ctx.github.owner}/${ctx.github.repo} --json state --jq '.state'`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n if (issueState.trim().toUpperCase() === 'CLOSED') {\n return stepSkipped(step, [`Issue already closed on GitHub; ${count} unmerged commit(s) remain on ${root.sourceBranch}`]);\n }\n } catch {\n // gh check failed — fall through to hard fail\n }\n }\n\n return stepFailed(step, `${count} unmerged commit(s) on ${root.sourceBranch}. Merge before closing out.`);\n }\n }\n\n // Check remote\n const { stdout: remoteBranch } = await execAsync(\n `git ls-remote --heads origin \"${root.sourceBranch}\" 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n\n if (remoteBranch.trim()) {\n await execAsync(`git fetch origin ${root.sourceBranch}`, { cwd: root.dir }).catch(() => {});\n const remoteCheck = await verifyRemoteBranchIfPresent(ctx, root, gitlabLookup);\n if (remoteCheck) return remoteCheck;\n }\n\n return null;\n}\n\nasync function verifySquashMergedMrByBranch(\n root: MergeVerificationRoot,\n branchRef: string,\n lookup: GitLabMergeLookupCache,\n): Promise<GitLabMergeMatch | null> {\n if (root.forge !== 'gitlab') return null;\n try {\n const { stdout } = await execAsync(`git rev-parse ${branchRef} 2>/dev/null`, { cwd: root.dir, encoding: 'utf-8' });\n const headSha = stdout.trim();\n if (!headSha) return null;\n const reused = lookup.attempted;\n if (!reused) {\n lookup.attempted = true;\n lookup.headSha = headSha;\n lookup.artifact = await getForgeAdapter('gitlab').findMergedArtifact({\n sourceBranch: root.sourceBranch,\n targetBranch: root.targetBranch,\n headSha,\n cwd: root.dir,\n });\n }\n if (!lookup.artifact || lookup.headSha !== headSha) return null;\n const label = lookup.artifact.id ? `MR !${lookup.artifact.id}` : 'GitLab MR';\n const url = lookup.artifact.url ? ` (${lookup.artifact.url})` : '';\n return {\n result: stepOk('close-out:verify-merged', [`${label} is merged and ${branchRef} matches the merged MR head${url}`]),\n reused,\n };\n } catch {\n return null;\n }\n}\n\ntype GitHubMergedPr = {\n number?: number;\n mergedAt?: string | null;\n headRefOid?: string | null;\n url?: string | null;\n};\n\nasync function verifyRemoteBranchIfPresent(\n ctx: LifecycleContext,\n root: MergeVerificationRoot,\n gitlabLookup: GitLabMergeLookupCache,\n): Promise<StepResult | null> {\n const step = 'close-out:verify-merged';\n const remoteRef = `origin/${root.sourceBranch}`;\n\n const { stdout: remoteBranch } = await execAsync(\n `git ls-remote --heads origin \"${root.sourceBranch}\" 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n if (!remoteBranch.trim()) return null;\n\n await execAsync(`git fetch origin ${root.sourceBranch}`, { cwd: root.dir }).catch(() => {});\n\n try {\n await execAsync(\n `git merge-base --is-ancestor ${remoteRef} ${root.targetBranch}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n return stepOk(step, ['Remote branch fully merged']);\n } catch {\n // Squash-merge detection for remote branch\n try {\n const { stdout: codeDiff } = await execAsync(\n `git diff ${root.targetBranch}...${remoteRef} -- ':!.planning' ':!docs/prds' ':!.overdeck/prompts' 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n if (!codeDiff.trim()) {\n return stepOk(step, ['Remote code changes squash-merged to main (only planning artifacts remain on branch)']);\n }\n } catch {\n // diff failed — fall through\n }\n\n const gitlabMerged = await verifySquashMergedMrByBranch(root, remoteRef, gitlabLookup);\n if (gitlabMerged) {\n return gitlabMerged.reused\n ? stepOk(step, [`Remote ${remoteRef} matches the merged MR head`])\n : gitlabMerged.result;\n }\n\n const githubMerged = root.forge === 'github'\n ? await verifySquashMergedPrByBranch(ctx, root, remoteRef)\n : null;\n if (githubMerged) return githubMerged;\n\n const { stdout: remoteUnmerged } = await execAsync(\n `git log ${root.targetBranch}..${remoteRef} --oneline 2>/dev/null || true`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const count = remoteUnmerged.trim() ? remoteUnmerged.trim().split('\\n').length : 0;\n\n if (ctx.github && root.forge === 'github') {\n try {\n const { stdout: issueState } = await execAsync(\n `gh issue view ${ctx.github.number} --repo ${ctx.github.owner}/${ctx.github.repo} --json state --jq '.state'`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n if (issueState.trim().toUpperCase() === 'CLOSED') {\n return stepSkipped(step, [`Issue already closed on GitHub; ${count} unmerged commit(s) remain on remote ${root.sourceBranch}`]);\n }\n } catch {\n // gh check failed — fall through to hard fail\n }\n }\n\n return stepFailed(step, `${count} unmerged commit(s) on remote ${root.sourceBranch}.`);\n }\n}\n\nasync function verifySquashMergedPrByBranch(\n ctx: LifecycleContext,\n root: MergeVerificationRoot,\n branchRef: string,\n): Promise<StepResult | null> {\n if (!ctx.github) return null;\n\n const step = 'close-out:verify-merged';\n const { owner, repo } = ctx.github;\n const remoteTargetRef = `origin/${root.targetBranch}`;\n\n try {\n const { stdout: prJson } = await execAsync(\n `gh pr list --repo ${owner}/${repo} --state merged --head ${JSON.stringify(root.sourceBranch)} --json number,mergedAt,headRefOid,url`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const prs = JSON.parse(prJson) as GitHubMergedPr[];\n const mergedPr = prs\n .filter((pr) => (\n typeof pr.mergedAt === 'string'\n && pr.mergedAt.length > 0\n && typeof pr.headRefOid === 'string'\n && pr.headRefOid.length > 0\n ))\n .sort((a, b) => Date.parse(String(b.mergedAt)) - Date.parse(String(a.mergedAt)))[0];\n if (!mergedPr?.headRefOid) return null;\n\n const { stdout: tipShaRaw } = await execAsync(\n `git rev-parse ${branchRef} 2>/dev/null`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const tipSha = tipShaRaw.trim();\n if (!tipSha) return null;\n\n if (tipSha === mergedPr.headRefOid) {\n const prLabel = typeof mergedPr.number === 'number' ? `PR #${mergedPr.number}` : 'GitHub PR';\n return stepOk(step, [`${prLabel} is squash-merged and ${branchRef} matches the merged PR head`]);\n }\n\n // A workspace may merge a newer target branch after its PR head was pushed.\n // Ignore those merge commits and allow close-out when every post-PR commit\n // is already on the configured remote target; only branch-unique work is unsafe.\n let commitsNotOnTarget: string[] | null = null;\n try {\n const { stdout: commitsRaw } = await execAsync(\n `git log --no-merges --format=%H ${mergedPr.headRefOid}..${tipSha}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n const commits = commitsRaw.split('\\n').map((sha) => sha.trim()).filter(Boolean);\n const unmerged: string[] = [];\n for (const commit of commits) {\n try {\n await execAsync(\n `git merge-base --is-ancestor ${commit} ${remoteTargetRef}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n } catch {\n unmerged.push(commit);\n }\n }\n if (unmerged.length === 0) {\n const prLabel = typeof mergedPr.number === 'number' ? `PR #${mergedPr.number}` : 'GitHub PR';\n return stepOk(step, [\n `${prLabel} is squash-merged; all ${commits.length} post-PR non-merge commit(s) on ${branchRef} are already on ${remoteTargetRef}`,\n ]);\n }\n\n commitsNotOnTarget = unmerged;\n } catch { /* containment check failure falls through to the state-plane policy */ }\n\n // PAN-2406 / state-plane policy rule 3: commits after the merged head that\n // touch ONLY legacy state-plane paths under .pan/ are pipeline exhaust —\n // e.g. 'chore: record merge status' — and must not block close-out.\n try {\n const { stdout: deltaRaw } = await execAsync(\n `git diff --name-only ${mergedPr.headRefOid}..${tipSha}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n let deltaFiles = deltaRaw.split('\\n').map((f) => f.trim()).filter(Boolean);\n let statePlaneOnly = deltaFiles.length > 0 && deltaFiles.every((f) =>\n f.startsWith('.pan/'));\n if (!statePlaneOnly) {\n // Branch may have merged its target INTO itself after the PR merged —\n // the two-dot delta then contains target-branch files. Judge only changes\n // UNIQUE to the branch (three-dot vs the remote target): if those are\n // state-plane-only, everything real is already on the target.\n const { stdout: uniqueRaw } = await execAsync(\n `git diff --name-only ${remoteTargetRef}...${tipSha}`,\n { cwd: root.dir, encoding: 'utf-8' },\n );\n deltaFiles = uniqueRaw.split('\\n').map((f) => f.trim()).filter(Boolean);\n statePlaneOnly = deltaFiles.every((f) =>\n f.startsWith('.pan/'));\n }\n if (statePlaneOnly) {\n const prLabel = typeof mergedPr.number === 'number' ? `PR #${mergedPr.number}` : 'GitHub PR';\n return stepOk(step, [\n `${prLabel} is squash-merged; ${branchRef} is ahead only by state-plane commits (${deltaFiles.length} file(s): .pan) — accepted per state-plane policy`,\n ]);\n }\n } catch { /* diff failure falls through to the strict rejection below */ }\n\n const prLabel = typeof mergedPr.number === 'number' ? `PR #${mergedPr.number}` : 'merged GitHub PR';\n if (commitsNotOnTarget) {\n return stepFailed(step, `${branchRef} has ${commitsNotOnTarget.length} commit(s) after merged ${prLabel} that are not on ${remoteTargetRef}: ${commitsNotOnTarget.join(', ')}`);\n }\n return stepFailed(step, `${branchRef} does not match the head commit of merged ${prLabel}; inspect before closing out.`);\n } catch {\n return null;\n }\n}\n\n/**\n * Reset issue back to open/backlog state (for destructive reset).\n */\nfunction resetIssueToTodo(ctx: LifecycleContext): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => resetIssueToTodoImpl(ctx),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('reset:reset-issue', `Failed to reset issue: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function resetIssueToTodoImpl(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'reset:reset-issue';\n try {\n if (ctx.github) {\n const { owner, repo, number } = ctx.github;\n // Reopen the issue\n await execAsync(\n `gh issue reopen ${number} --repo ${owner}/${repo}`,\n { encoding: 'utf-8' },\n ).catch(() => {}); // May already be open\n // Remove lifecycle labels\n const labelsToRemove = ['in-review', 'in-progress', 'planned', 'planning', 'Review: Approved', 'Review: Failed', 'ready-for-merge'];\n for (const label of labelsToRemove) {\n await execAsync(\n `gh issue edit ${number} --repo ${owner}/${repo} --remove-label \"${label}\"`,\n { encoding: 'utf-8' },\n ).catch(() => {}); // Label may not exist\n }\n return stepOk(step, [`Reset GitHub issue #${number}: reopened and cleared labels`]);\n }\n\n // Linear: reopen to Todo\n const linearApiKey = await getLinearApiKey();\n if (linearApiKey) {\n const { LinearClient } = await import('@linear/sdk');\n const client = new LinearClient({ apiKey: linearApiKey });\n const issueNum = extractNumberSync(ctx.issueId);\n const teamKey = extractPrefixSync(ctx.issueId);\n if (issueNum === null || teamKey === null) {\n return stepFailed(step, `Could not parse issue ID: ${ctx.issueId}`);\n }\n const results = await client.issues({\n filter: {\n number: { eq: issueNum },\n team: { key: { eq: teamKey } },\n },\n first: 1,\n });\n if (results.nodes.length > 0) {\n const issue = results.nodes[0];\n const team = await issue.team;\n if (team) {\n const states = await team.states();\n const todoState = states.nodes.find(s => s.type === 'unstarted' && s.name === 'Todo') ||\n states.nodes.find(s => s.type === 'unstarted');\n if (todoState) {\n await issue.update({ stateId: todoState.id });\n }\n }\n }\n return stepOk(step, [`Reset ${trackerName(ctx, 'linear')} issue ${ctx.issueId} to Todo`]);\n }\n\n return stepSkipped(step, ['No tracker available to reset issue']);\n } catch (err) {\n return stepFailed(step, `Failed to reset issue: ${(err as Error).message}`);\n }\n}\n\n/**\n * Clear review status for an issue.\n */\nasync function resetPostMergeStateForIssue(issueId: string): Promise<void> {\n try {\n const { resetPostMergeState } = await import('../cloister/merge-agent.js');\n resetPostMergeState(issueId);\n resetPostMergeState(issueId.toUpperCase());\n } catch {\n return;\n }\n}\n\nfunction clearReviewStatusStep(issueId: string): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => clearReviewStatusStepImpl(issueId),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepSkipped('clear-review-status', [`Failed to clear review status (non-fatal): ${(err as Error).message}`])),\n ),\n );\n}\n\nasync function clearReviewStatusStepImpl(issueId: string): Promise<StepResult> {\n const step = 'clear-review-status';\n try {\n const { clearReviewStatus } = await import('../review-status.js');\n clearReviewStatus(issueId.toUpperCase());\n return stepOk(step, ['Review status cleared']);\n } catch {\n // Fallback: direct file manipulation\n try {\n const statusFile = join(OVERDECK_HOME, 'review-status.json');\n if (existsSync(statusFile)) {\n const data = JSON.parse(await readFile(statusFile, 'utf-8'));\n const upperKey = issueId.toUpperCase();\n if (data[upperKey]) {\n delete data[upperKey];\n await writeFile(statusFile, JSON.stringify(data, null, 2));\n }\n }\n return stepOk(step, ['Review status cleared (direct)']);\n } catch (innerErr) {\n return stepSkipped(step, [`Failed to clear review status (non-fatal): ${(innerErr as Error).message}`]);\n }\n }\n}\n\nexport const __testInternals = {\n completeXBriefStep,\n verifyBranchMerged,\n};\n\nfunction resetIssueToCanceled(ctx: LifecycleContext): Effect.Effect<StepResult> {\n return Effect.tryPromise({\n try: () => resetIssueToCanceledImpl(ctx),\n catch: (err) => err,\n }).pipe(\n Effect.catch((err) =>\n Effect.succeed(stepFailed('cancel:reset-issue', `Failed to cancel issue: ${(err as Error).message}`)),\n ),\n );\n}\n\nasync function resetIssueToCanceledImpl(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'cancel:reset-issue';\n try {\n if (ctx.github) {\n const { owner, repo, number } = ctx.github;\n await execAsync(\n `gh issue edit ${number} --repo ${owner}/${repo} --add-label \"wontfix\"`,\n { encoding: 'utf-8' },\n ).catch(() => {});\n return stepOk(step, [`Marked GitHub issue #${number} as canceled/wontfix`]);\n }\n\n const linearApiKey = await getLinearApiKey();\n if (linearApiKey) {\n const { LinearClient } = await import('@linear/sdk');\n const client = new LinearClient({ apiKey: linearApiKey });\n const issueNum = extractNumberSync(ctx.issueId);\n const teamKey = extractPrefixSync(ctx.issueId);\n if (issueNum === null || teamKey === null) {\n return stepFailed(step, `Could not parse issue ID: ${ctx.issueId}`);\n }\n const results = await client.issues({\n filter: {\n number: { eq: issueNum },\n team: { key: { eq: teamKey } },\n },\n first: 1,\n });\n if (results.nodes.length > 0) {\n const issue = results.nodes[0];\n const team = await issue.team;\n if (team) {\n const states = await team.states();\n const canceledState = states.nodes.find(s => s.type === 'canceled') ||\n states.nodes.find(s => s.name.toLowerCase() === 'canceled');\n if (canceledState) {\n await issue.update({ stateId: canceledState.id });\n }\n }\n }\n return stepOk(step, [`Reset ${trackerName(ctx, 'linear')} issue ${ctx.issueId} to Canceled`]);\n }\n\n return stepSkipped(step, ['No tracker available to cancel issue']);\n } catch (err) {\n return stepFailed(step, `Failed to cancel issue: ${(err as Error).message}`);\n }\n}\n"],"mappings":"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"}
|