@overdeck/desktop 0.45.23 → 0.45.24
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/drizzle/overdeck/0000_overdeck_init.sql +2 -0
- package/package.json +1 -1
- package/server/{acceptance-criteria-DOA0KgXx.js → acceptance-criteria-DDCnXF58.js} +2 -2
- package/server/{advancing-selfheal-BBu6DSNm.js → advancing-selfheal-BVwW4qRn.js} +6 -6
- package/server/{agent-gc-BV1q_2EV.js → agent-gc-CHcnvonI.js} +4 -4
- package/server/agent-state-Bc3tzUC8.js +3 -0
- package/server/{agent-state-service-HoK8U4jW.js → agent-state-service-BsgWxZuB.js} +5 -5
- package/server/{agent-state-service-CnzFKSAQ.js → agent-state-service-D5UGKIJL.js} +1 -1
- package/server/{agents-B4Bjbkfv.js → agents-EbI0zCJl.js} +4 -4
- package/server/agents-wi9NWzs2.js +3 -0
- package/server/{archive-planning-zkJb3BeM.js → archive-planning-BZDgzoEj.js} +1 -1
- package/server/{archive-planning-DYrYojxl.js → archive-planning-Du25Jtst.js} +4 -4
- package/server/{artifact-index-jobs-C-xy7Z5v.js → artifact-index-jobs-CbEDfCDu.js} +2 -2
- package/server/assemble-J2ZZ2C15.js +339 -0
- package/server/{backlog-auto-trigger-GtG71X8H.js → backlog-auto-trigger-2R2jog7n.js} +3 -3
- package/server/{behavior-B_-4EKF3.js → behavior-B3exJ42O.js} +2 -2
- package/server/{briefing-freshness-DWdW7vUm.js → briefing-freshness-D1wzUIWv.js} +2 -2
- package/server/{build-info-DgTqZkL5.js → build-info-CZk_TITh.js} +1 -1
- package/server/{build-info-BT096lEl.js → build-info-Ie_D0_BT.js} +3 -3
- package/server/{checkpoint-manager-DDqcGyu8.js → checkpoint-manager-BI1Wxoku.js} +2 -2
- package/server/{checkpoint-manager-B7Bt29J0.js → checkpoint-manager-DwbQoqrC.js} +1 -1
- package/server/checkpoint-worker.js +1 -1
- package/server/{checkpoints-CpgCjAlN.js → checkpoints-BJIs5QIA.js} +1 -1
- package/server/{checkpoints-C-AXLfYT.js → checkpoints-CQB1h765.js} +2 -2
- package/server/{claude-hooks-provision-C8wdRnN1.js → claude-hooks-provision-8Ngo8TpT.js} +1 -1
- package/server/{claude-hooks-provision-but-_qGY.js → claude-hooks-provision-D8GXF3_s.js} +2 -2
- package/server/{claude-permissions-H_5wHQZQ.js → claude-permissions-DNWEVV6z.js} +2 -2
- package/server/{claude-settings-overlay-CbSz0Dvb.js → claude-settings-overlay-CEdaqpWI.js} +2 -2
- package/server/{claude-settings-overlay-BTENVmYf.js → claude-settings-overlay-DWy-Bb4Y.js} +1 -1
- package/server/cli-B86rejVV.js +3 -0
- package/server/{cli-vBeXbPrq.js → cli-RoOx5DUf.js} +9 -9
- package/server/{cliproxy-Bgb2XCqi.js → cliproxy-C8a1vtTU.js} +2 -2
- package/server/{cliproxy-DW0V86F8.js → cliproxy-fHfIeiGd.js} +1 -1
- package/server/{close-issue-Dn_4c4G8.js → close-issue-DoOifFIK.js} +5 -5
- package/server/{closed-issue-reaper-hANY_ehE.js → closed-issue-reaper-Bk3SU4RC.js} +9 -9
- package/server/{closed-issue-reaper-CWpY2uBw.js → closed-issue-reaper-CktPXo8m.js} +1 -1
- package/server/{codex-TmSi-yTv.js → codex-BZXGhUVB.js} +7 -7
- package/server/{codex-Dg7Is5yb.js → codex-GcHuUF-k.js} +1 -1
- package/server/{codex-parser-C4gSH-IR.js → codex-parser-CFZ1_1Jl.js} +2 -2
- package/server/{config-CCvjQf27.js → config-BZ0sHTju.js} +2 -2
- package/server/{config-DjLOdbgT.js → config-CKxYiCmX.js} +2 -2
- package/server/{config-DUIhMN_T.js → config-DzV30TOm.js} +1 -1
- package/server/{config-yaml-DvyMuOle.js → config-yaml-DySPNmKN.js} +23 -9
- package/server/{conflict-gate-CmrYpa47.js → conflict-gate-BaCVrH-N.js} +3 -3
- package/server/{conflict-gate-BSJkNTdp.js → conflict-gate-BhAY7TUd.js} +1 -1
- package/server/context-layers-BrPmE__h.js +4 -0
- package/server/context-layers-igZ4FI4_.js +37 -0
- package/server/{control-settings-aP1ANIlI.js → control-settings-BQp7_4uD.js} +4 -4
- package/server/{conversation-compaction-CNbTLB86.js → conversation-compaction-CgrR8YYp.js} +8 -8
- package/server/{conversation-compaction-DiG5zWQq.js → conversation-compaction-eQB5CkOe.js} +1 -1
- package/server/{conversation-service-Up7FEzIC.js → conversation-service-BLd3MYsG.js} +1 -1
- package/server/{conversation-service-Du-MTNpJ.js → conversation-service-DPJ71F33.js} +329 -19
- package/server/{conversations-DbOs1e9Q.js → conversations-6AfUfbwc.js} +5 -5
- package/server/{conversations-6mPANqvL.js → conversations-CAsJF9kh.js} +1 -1
- package/server/{cost-Ca0YTWDw.js → cost-B2ddP6wq.js} +2 -2
- package/server/{cost-CW4WSe72.js → cost-R-m6Yi-W.js} +3 -3
- package/server/{cost-sync-C0VdGme1.js → cost-sync-BsN6ER0n.js} +3 -3
- package/server/{dag-CdOjJVDT.js → dag-B1ucnjDU.js} +2 -2
- package/server/{dangerous-git-ops-CT-IypG3.js → dangerous-git-ops-BXqeWVSf.js} +1 -1
- package/server/dashboard-db-worker.js +7 -7
- package/server/{deacon-pRnyD5w9.js → deacon-CU_cAYSR.js} +254 -144
- package/server/{deacon-DYSEJb48.js → deacon-kiQrb4ZT.js} +3 -3
- package/server/{deacon-nudge-log-Bjg8U4Y9.js → deacon-nudge-log-6wVqAaqz.js} +3 -3
- package/server/{deacon-swarm-DftYRDo8.js → deacon-swarm-BXZZ1Z2-.js} +13 -13
- package/server/deacon-swarm-C-GwfjJy.js +2 -0
- package/server/deacon.js +8 -8
- package/server/{deploy-patrol-Bjsho_ha.js → deploy-patrol-AiFoP8dw.js} +6 -6
- package/server/{deploy-window-DeP_OIRk.js → deploy-window-C3F-tihF.js} +6 -6
- package/server/{deploy-window-CM6W1-c6.js → deploy-window-DkZraDyj.js} +1 -1
- package/server/{dev-supervisor-BpMoHlkR.js → dev-supervisor-bcOWqdQE.js} +2 -2
- package/server/{devcontainer-renderer-CtUgrVKW.js → devcontainer-renderer-_d6XhyND.js} +3 -3
- package/server/dist-Bzy0y69j.js +3 -0
- package/server/{dist-DyRx4cI7.js → dist-Cb_0uvpS.js} +1 -1
- package/server/{docker-BK7HIcBj.js → docker-DcMTFTUC.js} +2 -2
- package/server/{dod-BrSfUHm0.js → dod-C6n9RV3G.js} +1 -1
- package/server/{ensure-devcontainer-DRqaAkBR.js → ensure-devcontainer-BZgOhRo7.js} +1 -1
- package/server/{ensure-devcontainer-DseivYEy.js → ensure-devcontainer-DNi-ono3.js} +4 -4
- package/server/{esm-shims-CbI3BVkl.js → esm-shims-OSHAYaS1.js} +1 -1
- package/server/{event-store-tdiY5eDZ.js → event-store-CQL6NeqB.js} +3 -3
- package/server/{event-store-Cr5PQwFC.js → event-store-EV2cMiXj.js} +1 -1
- package/server/{events-CvvGK6LT.js → events-BTdrdEq8.js} +1 -1
- package/server/{events-CC1dgUpx.js → events-DTfAe4My.js} +3 -3
- package/server/{factory-CynerjXO.js → factory-CFibJuaw.js} +4 -4
- package/server/{feature-registry-population-B0pmAz0a.js → feature-registry-population-DKZFMbFt.js} +4 -4
- package/server/{feature-registry-storage-_wUBRD3J.js → feature-registry-storage-BdPuLcBX.js} +2 -2
- package/server/{features-Dhxz-wLc.js → features-ez2271oH.js} +2 -2
- package/server/{feedback-target-CPS15Vte.js → feedback-target-7mqqwrX6.js} +7 -7
- package/server/{feedback-target-iygZwmMc.js → feedback-target-CoFDHiM8.js} +1 -1
- package/server/{feedback-writer-C9b6ccN1.js → feedback-writer-CY5L1lB7.js} +5 -5
- package/server/{feedback-writer-qmiMbqTZ.js → feedback-writer-D3a22RCd.js} +1 -1
- package/server/flywheel-DHrgCphM.js +1084 -0
- package/server/flywheel-DzGtWnJ1.js +477 -0
- package/server/flywheel-SVF02Jpq.js +2 -0
- package/server/{flywheel-merge-order-DxbQKE1d.js → flywheel-merge-order-UU0tiUld.js} +9 -8
- package/server/{flywheel-merge-order-C-6zRLvM.js → flywheel-merge-order-UUcsCDb6.js} +1 -1
- package/server/{forge-Ch9gCFod.js → forge-CNFnyqps.js} +2 -2
- package/server/forge-DXnS_YFH.js +2 -0
- package/server/{from-CJ_aH5jD.js → from-kNxT0bZC.js} +1 -1
- package/server/{fts-db-CKgg0VKg.js → fts-db-CE46vDkZ.js} +2 -2
- package/server/{fts-operations-BrL_VTRr.js → fts-operations-CqY6zl-i.js} +2 -2
- package/server/{git-utils-BVa07a4a.js → git-utils-CRw2zLPC.js} +68 -3
- package/server/git-utils-D0_yhCgF.js +3 -0
- package/server/{github-app-CMBdIFBQ.js → github-app-C3QrAUd0.js} +18 -7
- package/server/{github-app-CPfoFAdV.js → github-app-ClZ2lk8D.js} +1 -1
- package/server/{github-client--XVlDpwM.js → github-client-BnIsSrEN.js} +2 -2
- package/server/{github-client-B4QsdE-X.js → github-client-DfQYVUaw.js} +1 -1
- package/server/{github-pr-lookup-BRbDW-qk.js → github-pr-lookup-D16NGsSO.js} +2 -2
- package/server/{harness-binary-CVio2m-u.js → harness-binary-DPUi-Pv8.js} +32 -6
- package/server/{harness-resolve-qHJBhy3Z.js → harness-resolve-3lNZkyT4.js} +18 -9
- package/server/{health-events-C9B8y1Hm.js → health-events-BBcTYQKU.js} +2 -2
- package/server/{health-events-CpfvuVmm.js → health-events-BDOHby5Q.js} +1 -1
- package/server/{hooks-DUXw8A0-.js → hooks-CKZgXlJb.js} +2 -2
- package/server/{hooks-ltecuFqZ.js → hooks-DPVX1rwQ.js} +1 -1
- package/server/{hume-BEq0bUDg.js → hume-Cz8igP9E.js} +2 -2
- package/server/hume-ccTf4wTX.js +3 -0
- package/server/{idle-stack-reaper-D7w9t3fE.js → idle-stack-reaper-CuLF0j2H.js} +1 -1
- package/server/{idle-stack-reaper-w1ISSlhs.js → idle-stack-reaper-DDIRwxUY.js} +2 -2
- package/server/{in-flight-guard-aQ1IxeI3.js → in-flight-guard-CAAkqPLu.js} +2 -2
- package/server/{infra-CzaSOgul.js → infra-BEqFxlJc.js} +14 -4
- package/server/{injection-DZ0PfYa4.js → injection-C8aiVJrF.js} +191 -183
- package/server/{injection-oNZeoDhL.js → injection-CPUIqNhI.js} +1 -1
- package/server/{inspect-agent-DJKLWcVT.js → inspect-agent-BLuJ1AkT.js} +135 -79
- package/server/inspect-agent-Brtz0URj.js +2 -0
- package/server/{internal-token-DXdfprGr.js → internal-token-BrcU6qfe.js} +1 -1
- package/server/{internal-token-Bb4XeV2x.js → internal-token-s0-oKybJ.js} +2 -2
- package/server/{io-B3_AHDfr.js → io-C9kF1wtJ.js} +3 -3
- package/server/{issue-closed-zhdvQicO.js → issue-closed-CDJk4pIp.js} +7 -7
- package/server/{issue-lifecycle-Dy4vd0Oa.js → issue-lifecycle-B8sZHVHh.js} +1 -1
- package/server/{issue-lifecycle-CreaCkjW.js → issue-lifecycle-ojZAzDBP.js} +21 -21
- package/server/{issue-service-singleton-DabvBWUp.js → issue-service-singleton-BvJ09SaR.js} +1 -1
- package/server/{issue-service-singleton-B5ushCvE.js → issue-service-singleton-Yx5DdmVJ.js} +8 -8
- package/server/{issues-B6ToV6x1.js → issues-BMmP_77e.js} +2 -2
- package/server/{jsonl-parser-Dhflo1SH.js → jsonl-parser-BXSlHwEh.js} +3 -3
- package/server/{label-cleanup-BKPJm4Hv.js → label-cleanup-B0UJwnv5.js} +3 -3
- package/server/{label-reconciler-DdWbHEBu.js → label-reconciler-B4iOAjlC.js} +4 -4
- package/server/{launcher-generator-CHVm7bGT.js → launcher-generator-BBWG_SiS.js} +101 -7
- package/server/{lifecycle-io-D8iNBcux.js → lifecycle-io-BREGpnLk.js} +5 -5
- package/server/{lifecycle-io-Bsnr2K4F.js → lifecycle-io-DPo7x11I.js} +1 -1
- package/server/{lifecycle-sycO16hj.js → lifecycle-xmBfK2GJ.js} +6 -6
- package/server/{lifecycle-DfeyBDwA.js → lifecycle-yPd___5E.js} +2 -2
- package/server/{linear-client-D4zqMnX7.js → linear-client-BsuUvkdA.js} +2 -2
- package/server/{linear-client-DqhDQO9k.js → linear-client-CTskEeT_.js} +1 -1
- package/server/memory-fts-worker.js +1 -1
- package/server/{merge-DK23JEdS.js → merge-B1DiziGU.js} +1 -1
- package/server/{merge-C_ZEhbqI.js → merge-CefQNAnq.js} +3 -3
- package/server/{merge-agent-XrB80vfT.js → merge-agent-4WOWlSYA.js} +28 -28
- package/server/{merge-agent-BQM3a8z5.js → merge-agent-CKkgIBPB.js} +1 -1
- package/server/{merge-batch-Bwoyr8Je.js → merge-batch-ClFn2T-y.js} +1 -1
- package/server/merge-ops-CR44oU9-.js +2 -0
- package/server/{merge-rebase-C1KIVr0s.js → merge-rebase-BfaTyXsw.js} +2 -2
- package/server/merge-set-DJ8-UErs.js +83 -0
- package/server/{merge-set-DnSYdBEp.js → merge-set-bUqbm3yc.js} +1 -1
- package/server/{merge-sync-SSv3E-pl.js → merge-sync-BJYeOODr.js} +1 -1
- package/server/{merge-sync-DZ9dojJn.js → merge-sync-VPV28g0g.js} +2 -2
- package/server/{merge-train-DxDfBTjr.js → merge-train-CHopycP0.js} +3 -3
- package/server/{merge-train-deps-CyYQUksx.js → merge-train-deps-D1cOZzy2.js} +4 -4
- package/server/{merge-verification-CSizH3HY.js → merge-verification-BkaOqPsw.js} +4 -4
- package/server/{multipart-parser-Dt8YPN5S.js → multipart-parser-duPAfSBw.js} +2 -2
- package/server/{no-resume-mode-Cx3K0hHi.js → no-resume-mode-Nw29vaFb.js} +1 -1
- package/server/node_modules/.package-lock.json +8 -8
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/package.json +3 -3
- package/server/node_modules/undici/README.md +5 -0
- package/server/node_modules/undici/docs/docs/api/Client.md +4 -0
- package/server/node_modules/undici/docs/docs/api/Dispatcher.md +6 -0
- package/server/node_modules/undici/docs/docs/api/Interceptors.md +59 -0
- package/server/node_modules/undici/docs/docs/api/MockAgent.md +27 -0
- package/server/node_modules/undici/docs/docs/api/MockPool.md +28 -1
- package/server/node_modules/undici/docs/docs/index.md +5 -0
- package/server/node_modules/undici/lib/core/request.js +1 -1
- package/server/node_modules/undici/lib/core/util.js +6 -1
- package/server/node_modules/undici/lib/dispatcher/agent.js +3 -3
- package/server/node_modules/undici/lib/dispatcher/client-h1.js +31 -6
- package/server/node_modules/undici/lib/dispatcher/client-h2.js +34 -43
- package/server/node_modules/undici/lib/handler/cache-handler.js +93 -5
- package/server/node_modules/undici/lib/handler/cache-revalidation-handler.js +10 -0
- package/server/node_modules/undici/lib/handler/redirect-handler.js +5 -2
- package/server/node_modules/undici/lib/handler/retry-handler.js +6 -4
- package/server/node_modules/undici/lib/interceptor/cache.js +21 -4
- package/server/node_modules/undici/lib/interceptor/decompress.js +4 -0
- package/server/node_modules/undici/lib/mock/mock-interceptor.js +25 -7
- package/server/node_modules/undici/lib/mock/mock-utils.js +39 -10
- package/server/node_modules/undici/lib/util/cache.js +49 -7
- package/server/node_modules/undici/lib/web/fetch/body.js +2 -1
- package/server/node_modules/undici/lib/web/fetch/response.js +1 -3
- package/server/node_modules/undici/package.json +2 -2
- package/server/node_modules/undici/types/client.d.ts +2 -2
- package/server/node_modules/undici/types/dispatcher.d.ts +3 -3
- package/server/node_modules/undici/types/mock-interceptor.d.ts +5 -1
- package/server/{orphan-proposed-reconciler-neK8dHQk.js → orphan-proposed-reconciler-BvKkWMJQ.js} +12 -12
- package/server/{orphan-proposed-reconciler-BTkDrl4R.js → orphan-proposed-reconciler-CTSGcK8p.js} +1 -1
- package/server/package-lock.json +9 -9
- package/server/package.json +1 -1
- package/server/{pan-dir-DeVUgwA7.js → pan-dir-B0c3w0Ya.js} +3 -3
- package/server/{paths-_zEts6wc.js → paths-D-xWGHdh.js} +2 -2
- package/server/{pickup-Dd0Znxxy.js → pickup-DUCLWrJY.js} +17 -16
- package/server/{pipeline-membership-0HaEGDlH.js → pipeline-membership-mY3GprlK.js} +7 -7
- package/server/{pipeline-notifier-6iCJplFZ.js → pipeline-notifier-BJr8gQX6.js} +1 -1
- package/server/{pipeline-notifier-D0ykk59O.js → pipeline-notifier-BXkJzDC4.js} +2 -2
- package/server/{project-pipelines-2sK1apsO.js → project-pipelines-C0MWUeCU.js} +2 -2
- package/server/project-repos-B6GycDh9.js +94 -0
- package/server/project-repos-DXKQsc_m.js +3 -0
- package/server/{projects-9SkRpES9.js → projects-vjjcmiN8.js} +1 -1
- package/server/{prompts-Dp4Tpk39.js → prompts-DxzFXuLl.js} +2 -2
- package/server/{providers-DV7ec8l7.js → providers-qQAwGQDW.js} +7 -7
- package/server/{providers-DKkBXhxU.js → providers-u4Ga0jOr.js} +1 -1
- package/server/public/assets/{chunk-BO2N2NFS-CltymLRg.js → chunk-BO2N2NFS-BamJdinX.js} +4 -4
- package/server/public/assets/{diagram-OG6HWLK6-D2f6pjgA.js → diagram-OG6HWLK6-DfOB8-w4.js} +1 -1
- package/server/public/assets/{dist-DEmlBBNQ.js → dist-ByIuzMQW.js} +3 -3
- package/server/public/assets/{dist-BWR4qFiV.js → dist-D7zYSEA6.js} +1 -1
- package/server/public/assets/{ganttDiagram-6RSMTGT7-DnfiViPK.js → ganttDiagram-6RSMTGT7-EdmRyXL3.js} +1 -1
- package/server/public/assets/{highlighted-body-OFNGDK62-B8Wrmj9H.js → highlighted-body-OFNGDK62-MerOfTX8.js} +1 -1
- package/server/public/assets/{index-CsSuZQBI.js → index-CWeMerGO.js} +146 -144
- package/server/public/assets/{index-CUr1xv8w.css → index-CenuJ3Cw.css} +1 -1
- package/server/public/assets/{linear-C9zFQpkX.js → linear-BBbArIcK.js} +1 -1
- package/server/public/assets/mermaid-GHXKKRXX-CpPWAFwE.js +1 -0
- package/server/public/assets/{pieDiagram-4H26LBE5-lR8H7Yjd.js → pieDiagram-4H26LBE5-BKIddiJN.js} +1 -1
- package/server/public/assets/{prod-B1jCQihw.js → prod-B8vYiu52.js} +4 -4
- package/server/public/assets/{quadrantDiagram-W4KKPZXB-CVQ2guem.js → quadrantDiagram-W4KKPZXB-D6RtoULk.js} +1 -1
- package/server/public/assets/{sankeyDiagram-5OEKKPKP-eD1RB4gm.js → sankeyDiagram-5OEKKPKP-CLrCedZX.js} +1 -1
- package/server/public/assets/{xychartDiagram-2RQKCTM6-ChEC6dQn.js → xychartDiagram-2RQKCTM6-hznR8DYl.js} +1 -1
- package/server/public/index.html +2 -2
- package/server/{pull-requests--Ib2jTgy.js → pull-requests-7N8WKseP.js} +1 -1
- package/server/{pull-requests-CXIhdCYD.js → pull-requests-_lHueOHW.js} +3 -3
- package/server/{queries-6d7Cdtm7.js → queries-CjNIf-Ub.js} +576 -166
- package/server/{rally-B708-nZn.js → rally-CyWT_lNO.js} +3 -3
- package/server/{rally-api-nY0ZANw3.js → rally-api-C8e_1JRi.js} +2 -2
- package/server/{rally-api-yjQ0TfHd.js → rally-api-ClOSd-Tt.js} +1 -1
- package/server/{rally-client-B1EJhfV0.js → rally-client-lc-nokqr.js} +4 -4
- package/server/{rally-client-CULK2XAY.js → rally-client-sgtVqzNU.js} +1 -1
- package/server/rally-zhhtXuDP.js +3 -0
- package/server/{reap-terminal-sessions-MWnqdLCJ.js → reap-terminal-sessions-7KeHv1R1.js} +1 -1
- package/server/{reap-terminal-sessions-N3BTkmX_.js → reap-terminal-sessions-BtHPo6Xc.js} +1 -1
- package/server/rebuild-stack-CFvcBHsS.js +4 -0
- package/server/{rebuild-stack-Dp4eLkt4.js → rebuild-stack-lwtsW0GO.js} +26 -35
- package/server/{reconstruct-cache-uWbZfjms.js → reconstruct-cache-C3Ui7ojV.js} +6 -6
- package/server/{record-t6V-3Dcj.js → record-CN0pj4_5.js} +1 -1
- package/server/{record-update-DACtKh6_.js → record-update-bLJxAXH4.js} +1 -1
- package/server/{record-update-Dq0t2uBG.js → record-update-dE1Aw83j.js} +2 -2
- package/server/{recovery-trip-BJtIQuag.js → recovery-trip-BvRWfUXM.js} +2 -2
- package/server/{release-engine-DZpmGGzb.js → release-engine-Cx48W7z8.js} +5 -5
- package/server/{release-set-hnXd_kga.js → release-set-CQfuN9-v.js} +3 -3
- package/server/{remote-Br-ZWVIP.js → remote-C_2hasl7.js} +3 -3
- package/server/{remote-4tiENDaI.js → remote-ChgimyYa.js} +3 -3
- package/server/{remote-agents-nmty1mLu.js → remote-agents-By1ige5O.js} +6 -6
- package/server/{remote-agents-CRCg1L4H.js → remote-agents-CA3Ihlxj.js} +1 -1
- package/server/{remote-completion-BbWNd10K.js → remote-completion-fVe9e_3u.js} +12 -12
- package/server/resume-BFv1PGpA.js +3 -0
- package/server/{review-agent-sKkE1FEL.js → review-agent-CIGOQYBD.js} +29 -25
- package/server/{review-agent-DJRO8EG8.js → review-agent-DNboMyW4.js} +1 -1
- package/server/{review-artifacts-BLbSC4ke.js → review-artifacts-nRDPuMxd.js} +5 -5
- package/server/{review-context-BgX3lcCx.js → review-context-2uj-YQ8J.js} +69 -25
- package/server/{review-status-C6n6-9f_.js → review-status-btEYlaxA.js} +1 -1
- package/server/{review-status-enrichment-tbOn_A_u.js → review-status-enrichment-Dci-ZwEt.js} +3 -3
- package/server/{review-status-A7quS5KJ.js → review-status-kZj_KyCk.js} +42 -26
- package/server/{review-status-sync-BhSQ0d0l.js → review-status-sync-BTOpxtTC.js} +10 -5
- package/server/{review-status-sync-To3-iS-N.js → review-status-sync-DSDK3OMy.js} +1 -1
- package/server/{review-verdict-feedback-CTRNtzXO.js → review-verdict-feedback-CCjVA7Uk.js} +6 -6
- package/server/{review-verdict-feedback-C1Kg8n7t.js → review-verdict-feedback-CTd4QMBu.js} +1 -1
- package/server/{rollup-B53Y81xK.js → rollup-BKQC7jp4.js} +1 -1
- package/server/{rollup-WA0VD-Rr.js → rollup-FGsKki9R.js} +8 -8
- package/server/{scanner-D2743cp8.js → scanner-CnqEZeDy.js} +41 -14
- package/server/{sequence-io-Cg0zYj_E.js → sequence-io-D909zgeH.js} +4 -4
- package/server/{sequencer-agent-BGY8gjg-.js → sequencer-agent-CzkcK6P5.js} +5 -5
- package/server/server.js +2194 -341
- package/server/{service-DCpB_SGd.js → service-CLeAoU6G.js} +51 -50
- package/server/{session-fork-BQ8_6H32.js → session-fork-CHI1XvL9.js} +2 -2
- package/server/{session-history-CVZDpS3c.js → session-history-BT3IvqhC.js} +4 -4
- package/server/settings-66A6uvIA.js +2 -0
- package/server/{settings-BZ5m1UCl.js → settings-BTMvpNFU.js} +3 -3
- package/server/{settings-ylxwqwQG.js → settings-C0zgxOGf.js} +2 -2
- package/server/{shadow-engineering-BlxG6Xye.js → shadow-engineering-BJfgm-hX.js} +2 -2
- package/server/{shadow-state-Dh-HzF2J.js → shadow-state-7a-524T8.js} +2 -2
- package/server/{shadow-state-C2_epgx2.js → shadow-state-DnDyjujH.js} +1 -1
- package/server/{shadow-utils-4bvjn9b3.js → shadow-utils-Db3oroFG.js} +2 -2
- package/server/{ship-log-ClSMIzZX.js → ship-log-D9MlVzYU.js} +1 -1
- package/server/{smart-compaction-CHIxLr_d.js → smart-compaction-CV2ukKND.js} +1 -1
- package/server/{smart-compaction-r4pHQBIr.js → smart-compaction-KCYScAU1.js} +7 -7
- package/server/{spawn-planning-session-DWAorh2B.js → spawn-planning-session-DKpdFyOC.js} +30 -23
- package/server/{spawn-planning-session-KC_UduxD.js → spawn-planning-session-qUC2VWu3.js} +1 -1
- package/server/{specialist-completion-BXhav38U.js → specialist-completion-Ky9wCDpb.js} +1 -1
- package/server/{specialist-context-DtrYvGJ-.js → specialist-context-Bn3dCGS2.js} +5 -5
- package/server/{specialist-handoff-logger-C45ouoZ6.js → specialist-handoff-logger-CMEwYxa2.js} +2 -2
- package/server/{specialist-logs-DJ3RzKxW.js → specialist-logs-C5p8G7tx.js} +3 -3
- package/server/{specialists-JVO00bs7.js → specialists-CC4r-c2s.js} +776 -347
- package/server/{specialists-D5fRZXmN.js → specialists-CShuVGWT.js} +1 -1
- package/server/{specialists-DVj0hvaL.js → specialists-DM6bWjsi.js} +1 -1
- package/server/{specialists-DyFeedjn.js → specialists-jiuuyVkp.js} +21 -19
- package/server/specs-C8Wsn2XO.js +3 -0
- package/server/{src-CG9mCkP6.js → src-CXmtj0im.js} +6 -6
- package/server/{src-CMTQGyCK.js → src-CdmnTM2m.js} +5 -5
- package/server/{src-U7_kTPEE.js → src-DtcUOZCq.js} +111 -18
- package/server/stack-health-Bn3o8FBX.js +3 -0
- package/server/{stack-health-puygdaMd.js → stack-health-DxSdG9cW.js} +63 -9
- package/server/{stale-task-claims-BbFwCTb0.js → stale-task-claims-KJKxxiiQ.js} +4 -4
- package/server/{staleness-Cbl_w4Bw.js → staleness-CT34nwr9.js} +1 -1
- package/server/{staleness-Bpy41ngS.js → staleness-DPm7ClwA.js} +1 -1
- package/server/{state-home-CIGjwNfJ.js → state-home-Cn_Qu8IX.js} +2 -2
- package/server/{state-migrate-Dkzaxvny.js → state-migrate-BGg2ZaOt.js} +4 -4
- package/server/{stuck-remediation-CzEov_fq.js → stuck-remediation-DYuPRYYw.js} +300 -23
- package/server/{stuck-remediation-sY_MHHgQ.js → stuck-remediation-s9_2qfi6.js} +1 -1
- package/server/{substrate-bug-weights-service-BH66lhm7.js → substrate-bug-weights-service-B-FwCs5V.js} +3 -3
- package/server/{summary-fork-DHhJIsAL.js → summary-fork-D1RAk9rJ.js} +78 -12
- package/server/{summary-fork-D8bXjCr7.js → summary-fork-DpcxN9tf.js} +2 -2
- package/server/supervisor/{injection-budget-C1iPirFS.js → injection-budget-Csw5tqfJ.js} +2 -41
- package/server/supervisor/pty-supervisor.js +2 -1
- package/server/supervisor/pty-token-x-ea28Us.js +43 -0
- package/server/supervisor-FTJAIsAK.js +2 -0
- package/server/{supervisor-BO-Zk_h-.js → supervisor-wM4ooB5O.js} +3 -3
- package/server/{swarm-readiness-CQ_fwUWF.js → swarm-readiness-DlP_UjHq.js} +3 -3
- package/server/{system-prerequisites-Bza0ffAh.js → system-prerequisites-DUDj1YHf.js} +56 -29
- package/server/{teardown-workspace-CEgauF4U.js → teardown-workspace-DeTTImn-.js} +15 -15
- package/server/{test-agent-queue-DXDt0Xa-.js → test-agent-queue-MbxX5vqq.js} +6 -6
- package/server/{test-verdict-CmUBK5cT.js → test-verdict-bWYBtqBg.js} +1 -1
- package/server/{test-verdict-DFjjIHR5.js → test-verdict-gscuksPv.js} +1 -1
- package/server/{tldr-daemon-DL566HbH.js → tldr-daemon-BYFkts95.js} +1 -1
- package/server/{tldr-daemon-BDtLOIlO.js → tldr-daemon-C-69c3OI.js} +2 -2
- package/server/{tmux-DoThH6aj.js → tmux-B6z7h_vZ.js} +1 -1
- package/server/{tmux-ftABRAmc.js → tmux-R7N7y-Mh.js} +2 -2
- package/server/{token-CsOhvw42.js → token-BK6t8BIT.js} +2 -2
- package/server/token-util-DuPuNriz.js +3 -0
- package/server/{token-util-CTmczIfW.js → token-util-DxXu-vd1.js} +1 -1
- package/server/{tracker-config-B4itFzDo.js → tracker-config-G6latr8C.js} +2 -2
- package/server/{tracker-utils-BePq_oj-.js → tracker-utils-B4ylXZQK.js} +1 -1
- package/server/{tracker-utils-tnxxqROg.js → tracker-utils-DHv5pSDT.js} +2 -2
- package/server/{traefik-connect-m73vGebA.js → traefik-connect-Df2uXPsQ.js} +1 -1
- package/server/{tunnel-B2vxjx_b.js → tunnel-CDVKJpRv.js} +3 -3
- package/server/{tunnel-Cgj-IGIl.js → tunnel-DGdUkGoZ.js} +1 -1
- package/server/{types-BBNjPcqU.js → types-5M0lupud.js} +1 -1
- package/server/{uat-train-cKfM-_WW.js → uat-train-B05BWkp-.js} +1 -1
- package/server/{uat-train-CuEn0t9M.js → uat-train-DDSrSoV1.js} +25 -25
- package/server/validate-IfDGkaVR.js +177 -0
- package/server/{validation-CIi6pnI2.js → validation-CC9OPqJZ.js} +1 -1
- package/server/{validation-B5JxoPUS.js → validation-Dw1pzDzr.js} +4 -4
- package/server/{verification-artifact-C-lIHfUa.js → verification-artifact-B3itKTxu.js} +1 -1
- package/server/{verification-artifact-BcnIbigL.js → verification-artifact-TVFSz_q1.js} +1 -1
- package/server/{verification-runner-CDj4lD4t.js → verification-runner-BW4HOKAz.js} +1 -1
- package/server/{verification-runner-Br0S_Ous.js → verification-runner-CkgX6EtI.js} +91 -106
- package/server/{vtt-parser-DKpNNW5i.js → vtt-parser-DYPbKcIJ.js} +1 -1
- package/server/{work-agent-prompt-Yb2bd3Fq.js → work-agent-prompt-Du8Vh2aJ.js} +9 -9
- package/server/workflows-BQu4RFQ-.js +2 -0
- package/server/{workflows-C9WmILql.js → workflows-mbQzh6mQ.js} +18 -18
- package/server/{workspace-DuUdndHe.js → workspace--8tnhDI0.js} +1 -1
- package/server/{workspace-BVl4rihN.js → workspace-BPUws1-_.js} +3 -3
- package/server/{workspace-config-cEGcO6UT.js → workspace-config-CIVWZOQr.js} +1 -1
- package/server/{workspace-manager-hLUv3ADC.js → workspace-manager-CBhNdzE4.js} +66 -15
- package/server/workspace-manager-CWT55qeI.js +3 -0
- package/server/{workspace-metadata-DNpMixRq.js → workspace-metadata-BzHnwwX4.js} +1 -1
- package/server/{workspace-metadata-CskwzzhL.js → workspace-metadata-_WygnyoX.js} +2 -2
- package/server/{workspaces-hWvy9eKs.js → workspaces-Do_9J0LQ.js} +130 -111
- package/sync-sources/hooks/record-cost-event.js +6 -4
- package/sync-sources/skills/okf/README.md +11 -3
- package/sync-sources/skills/okf/SKILL.md +12 -1
- package/sync-sources/skills/okf/docs/USAGE.md +15 -0
- package/sync-sources/skills/okf/references/overdeck.md +28 -0
- package/sync-sources/skills/pan-flywheel/SKILL.md +18 -1
- package/sync-sources/skills/pan-knowledge/SKILL.md +10 -0
- package/sync-sources/skills/pan-orders/SKILL.md +33 -0
- package/sync-sources/skills/pan-start/SKILL.md +4 -0
- package/sync-sources/templates/traefik/dynamic/overdeck.yml.template +10 -0
- package/server/agent-state-Bg0lhqwj.js +0 -3
- package/server/agents-FlIgQ3ql.js +0 -3
- package/server/cli-BsVIHwzC.js +0 -3
- package/server/context-layers-B3Nt58jl.js +0 -4
- package/server/context-layers-ZYFcWiyc.js +0 -165
- package/server/deacon-swarm-9jRLS5Ke.js +0 -2
- package/server/dist-Bd3vn0Hw.js +0 -3
- package/server/flywheel-DMCUzDWg.js +0 -2
- package/server/flywheel-DSSpWmqF.js +0 -522
- package/server/flywheel-DcBkQvgE.js +0 -207
- package/server/forge-B-ZWyTRm.js +0 -2
- package/server/git-utils-Dry_Ka_D.js +0 -3
- package/server/hume-B_DbflNM.js +0 -3
- package/server/inspect-agent-ch-F-5Ci.js +0 -2
- package/server/layers-DzxSTGNA.js +0 -93
- package/server/merge-blockers-BsFjGrQ8.js +0 -41
- package/server/merge-ops-DlB1IM0Y.js +0 -2
- package/server/merge-set-DLz0MkGT.js +0 -139
- package/server/public/assets/mermaid-GHXKKRXX-BUAB8hqt.js +0 -1
- package/server/rally-njs_mJrt.js +0 -3
- package/server/rebuild-stack-kA7XngID.js +0 -3
- package/server/resume-CcKqnTvE.js +0 -3
- package/server/settings-D0OSwos5.js +0 -2
- package/server/specs-nN45DG2X.js +0 -3
- package/server/stack-health-Df0YEZKa.js +0 -3
- package/server/supervisor-BPy-o1A8.js +0 -2
- package/server/token-util-DrHg8abn.js +0 -3
- package/server/workflows-D90rMOya.js +0 -2
- package/server/workspace-manager-ZTdJAy9M.js +0 -3
|
@@ -0,0 +1,1084 @@
|
|
|
1
|
+
import { a as __toCommonJS } from "./rolldown-runtime-DNCzzKnI.js";
|
|
2
|
+
import { Ht as flushAutoCommits, Tn as init_projects, Ut as init_auto_commit, Wt as queueAutoCommit, fn as findProjectByPathSync, nn as init_state_read_home, rn as resolveStateReadHomeSync, vn as getProjectSwarmHotspots } from "./config-yaml-DySPNmKN.js";
|
|
3
|
+
import { S as FlywheelStatus, d as OrderBookIndexEntry, t as init_src, u as OrderBook, x as FlywheelRunId } from "./src-DtcUOZCq.js";
|
|
4
|
+
import { c as getReviewStatusSync, l as init_review_status } from "./review-status-kZj_KyCk.js";
|
|
5
|
+
import { a as countAgentsByStatusRole, o as init_agents } from "./agents-EbI0zCJl.js";
|
|
6
|
+
import { Gn as getAgentDir, b as init_agents$1, ft as stopAgent, ut as spawnRun } from "./queries-CjNIf-Ub.js";
|
|
7
|
+
import { n as init_issue_service_singleton, r as issue_service_singleton_exports } from "./issue-service-singleton-Yx5DdmVJ.js";
|
|
8
|
+
import { o as getInternalTokenSync, s as init_internal_token, t as INTERNAL_TOKEN_HEADER } from "./internal-token-s0-oKybJ.js";
|
|
9
|
+
import { d as isFlywheelAutoPickupBacklog, f as isFlywheelGloballyPaused, i as getFlywheelActiveRunId, p as isFlywheelRequireUatBeforeMerge, s as init_control_settings, x as setFlywheelGloballyPaused, y as setFlywheelActiveRunId } from "./control-settings-BQp7_4uD.js";
|
|
10
|
+
import { n as parseSequenceMd, t as init_sequence_io } from "./sequence-io-D909zgeH.js";
|
|
11
|
+
import { l as computeCohort, m as init_pickup, n as LEGACY_PARKED_LABELS } from "./pickup-DUCLWrJY.js";
|
|
12
|
+
import { a as declaredIssueFootprint, i as computePredictedConflictSignals, s as pickFromSequence } from "./flywheel-merge-order-UU0tiUld.js";
|
|
13
|
+
import { i as gatherProjectLensSignals, n as init_pipeline_membership, o as init_pipeline_membership_gather, r as resolvePipelineMembership } from "./pipeline-membership-mY3GprlK.js";
|
|
14
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
15
|
+
import { Effect, Schema } from "effect";
|
|
16
|
+
import { homedir } from "node:os";
|
|
17
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
18
|
+
import { mkdir, readFile, readdir, realpath, rename, stat as stat$1, writeFile } from "node:fs/promises";
|
|
19
|
+
import { exec } from "node:child_process";
|
|
20
|
+
import { promisify } from "node:util";
|
|
21
|
+
//#region ../../lib/orders/types.ts
|
|
22
|
+
init_projects();
|
|
23
|
+
init_state_read_home();
|
|
24
|
+
init_auto_commit();
|
|
25
|
+
init_internal_token();
|
|
26
|
+
init_review_status();
|
|
27
|
+
init_agents$1();
|
|
28
|
+
init_sequence_io();
|
|
29
|
+
init_pickup();
|
|
30
|
+
init_src();
|
|
31
|
+
const decodeOrderBook = Schema.decodeUnknownSync(OrderBook);
|
|
32
|
+
function parseOrderBookJson(value) {
|
|
33
|
+
try {
|
|
34
|
+
return {
|
|
35
|
+
ok: true,
|
|
36
|
+
book: decodeOrderBook(value)
|
|
37
|
+
};
|
|
38
|
+
} catch (error) {
|
|
39
|
+
return {
|
|
40
|
+
ok: false,
|
|
41
|
+
error: `Invalid order book: ${error instanceof Error ? error.message : String(error)}`
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region ../../lib/orders/io.ts
|
|
47
|
+
init_src();
|
|
48
|
+
const BOOK_ID_PATTERN = /^\d{4}-\d{2}-\d{2}-[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
49
|
+
const decodeIndex = Schema.decodeUnknownSync(Schema.Array(OrderBookIndexEntry));
|
|
50
|
+
function ordersDirectory(stateRoot) {
|
|
51
|
+
return join(stateRoot, "orders");
|
|
52
|
+
}
|
|
53
|
+
function orderBookPath(stateRoot, bookId) {
|
|
54
|
+
if (!BOOK_ID_PATTERN.test(bookId)) throw new Error(`Invalid order book id: ${bookId}`);
|
|
55
|
+
return join(ordersDirectory(stateRoot), `${bookId}.json`);
|
|
56
|
+
}
|
|
57
|
+
function orderBookIndexPath(stateRoot) {
|
|
58
|
+
return join(ordersDirectory(stateRoot), "index.json");
|
|
59
|
+
}
|
|
60
|
+
function backlogSequencePath(stateRoot) {
|
|
61
|
+
return join(stateRoot, "backlog", "sequence.md");
|
|
62
|
+
}
|
|
63
|
+
function readOrderBook(stateRoot, bookId) {
|
|
64
|
+
const path = orderBookPath(stateRoot, bookId);
|
|
65
|
+
if (!existsSync(path)) return null;
|
|
66
|
+
let value;
|
|
67
|
+
try {
|
|
68
|
+
value = JSON.parse(readFileSync(path, "utf8"));
|
|
69
|
+
} catch (error) {
|
|
70
|
+
throw new Error(`Could not parse order book ${bookId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
71
|
+
}
|
|
72
|
+
const parsed = parseOrderBookJson(value);
|
|
73
|
+
if (!parsed.ok) throw new Error(`Could not parse order book ${bookId}: ${parsed.error}`);
|
|
74
|
+
if (parsed.book.id !== bookId) throw new Error(`Order book ${bookId} contains mismatched id ${parsed.book.id}`);
|
|
75
|
+
return parsed.book;
|
|
76
|
+
}
|
|
77
|
+
function readOrderBookIndex(stateRoot) {
|
|
78
|
+
const path = orderBookIndexPath(stateRoot);
|
|
79
|
+
if (!existsSync(path)) return [];
|
|
80
|
+
try {
|
|
81
|
+
return decodeIndex(JSON.parse(readFileSync(path, "utf8")));
|
|
82
|
+
} catch (error) {
|
|
83
|
+
throw new Error(`Could not parse order book index: ${error instanceof Error ? error.message : String(error)}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function listOrderBookIds(stateRoot) {
|
|
87
|
+
const indexed = readOrderBookIndex(stateRoot).map((entry) => entry.id);
|
|
88
|
+
if (indexed.length > 0 || !existsSync(ordersDirectory(stateRoot))) return [...indexed];
|
|
89
|
+
return readdirSync(ordersDirectory(stateRoot)).filter((name) => name !== "index.json" && name.endsWith(".json")).map((name) => name.slice(0, -5)).filter((id) => BOOK_ID_PATTERN.test(id)).sort();
|
|
90
|
+
}
|
|
91
|
+
function preserveOperatorOwnedState(prior, next) {
|
|
92
|
+
if (!prior) return next;
|
|
93
|
+
if (prior.id !== next.id) throw new Error(`Order book id is immutable: ${prior.id} cannot become ${next.id}`);
|
|
94
|
+
const priorItems = new Map(prior.items.map((item) => [item.issue.toUpperCase(), item]));
|
|
95
|
+
const items = next.items.map((item) => {
|
|
96
|
+
const existing = priorItems.get(item.issue.toUpperCase());
|
|
97
|
+
if (!existing) return item;
|
|
98
|
+
return {
|
|
99
|
+
...item,
|
|
100
|
+
addedAt: existing.addedAt,
|
|
101
|
+
addedBy: existing.addedBy
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
return {
|
|
105
|
+
...next,
|
|
106
|
+
createdAt: prior.createdAt,
|
|
107
|
+
items
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function indexEntry(book) {
|
|
111
|
+
return {
|
|
112
|
+
id: book.id,
|
|
113
|
+
name: book.name,
|
|
114
|
+
status: book.status,
|
|
115
|
+
runId: book.runId,
|
|
116
|
+
updatedAt: book.updatedAt
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Physical state-store primitive used only by the orders writer. Domain callers
|
|
121
|
+
* must mutate order books through writer.ts so the book and queue stay aligned.
|
|
122
|
+
*/
|
|
123
|
+
async function writeOrderBookState(stateRoot, nextBook, queueOrder) {
|
|
124
|
+
const book = preserveOperatorOwnedState(readOrderBook(stateRoot, nextBook.id), nextBook);
|
|
125
|
+
const bookPath = orderBookPath(stateRoot, book.id);
|
|
126
|
+
const indexPath = orderBookIndexPath(stateRoot);
|
|
127
|
+
const existingIndex = [...readOrderBookIndex(stateRoot)];
|
|
128
|
+
const byId = new Map(existingIndex.map((entry) => [entry.id, entry]));
|
|
129
|
+
byId.set(book.id, indexEntry(book));
|
|
130
|
+
const ids = [...queueOrder ?? existingIndex.map((entry) => entry.id)];
|
|
131
|
+
if (!ids.includes(book.id)) ids.push(book.id);
|
|
132
|
+
for (const entry of existingIndex) if (!ids.includes(entry.id)) ids.push(entry.id);
|
|
133
|
+
const index = ids.map((id) => byId.get(id)).filter((entry) => Boolean(entry));
|
|
134
|
+
mkdirSync(dirname(bookPath), { recursive: true });
|
|
135
|
+
writeFileSync(bookPath, `${JSON.stringify(book, null, 2)}\n`, "utf8");
|
|
136
|
+
writeFileSync(indexPath, `${JSON.stringify(index, null, 2)}\n`, "utf8");
|
|
137
|
+
queueAutoCommit({
|
|
138
|
+
projectRoot: stateRoot,
|
|
139
|
+
repoRoot: stateRoot,
|
|
140
|
+
paths: [bookPath, indexPath],
|
|
141
|
+
subject: `chore(state): update order book ${book.id}`
|
|
142
|
+
});
|
|
143
|
+
const flushed = await Effect.runPromise(flushAutoCommits(stateRoot));
|
|
144
|
+
if (flushed.errored || flushed.pushed === false) throw new Error(`Failed to persist order book ${book.id}: ${flushed.reason ?? "state commit failed"}`);
|
|
145
|
+
return book;
|
|
146
|
+
}
|
|
147
|
+
//#endregion
|
|
148
|
+
//#region ../../lib/orders/resolver.ts
|
|
149
|
+
const COMPLETE_STATUS = "complete";
|
|
150
|
+
function labelNames(issue) {
|
|
151
|
+
return (Array.isArray(issue.labels) ? issue.labels : []).map((label) => typeof label === "string" ? label : typeof label === "object" && label !== null && typeof label.name === "string" ? label.name : "").filter(Boolean).map((label) => label.toLowerCase());
|
|
152
|
+
}
|
|
153
|
+
function issueIdentifier(issue) {
|
|
154
|
+
for (const key of [
|
|
155
|
+
"identifier",
|
|
156
|
+
"issueId",
|
|
157
|
+
"id"
|
|
158
|
+
]) {
|
|
159
|
+
const value = issue[key];
|
|
160
|
+
if (typeof value === "string" && value) return value.toUpperCase();
|
|
161
|
+
}
|
|
162
|
+
return "";
|
|
163
|
+
}
|
|
164
|
+
function issueIsClosed(issue) {
|
|
165
|
+
return [
|
|
166
|
+
issue.canonicalStatus,
|
|
167
|
+
issue.status,
|
|
168
|
+
issue.state
|
|
169
|
+
].map((value) => typeof value === "string" ? value.toLowerCase().replace(/[ -]/g, "_") : "").some((value) => [
|
|
170
|
+
"done",
|
|
171
|
+
"completed",
|
|
172
|
+
"closed",
|
|
173
|
+
"canceled",
|
|
174
|
+
"cancelled"
|
|
175
|
+
].includes(value));
|
|
176
|
+
}
|
|
177
|
+
const liveOrderIssueLookup = (issueIds) => {
|
|
178
|
+
const wanted = new Set(issueIds.map((id) => id.toUpperCase()));
|
|
179
|
+
const result = /* @__PURE__ */ new Map();
|
|
180
|
+
try {
|
|
181
|
+
const { getSharedIssueService } = (init_issue_service_singleton(), __toCommonJS(issue_service_singleton_exports));
|
|
182
|
+
const issues = getSharedIssueService().getIssues({
|
|
183
|
+
cycle: "all",
|
|
184
|
+
includeCompleted: true
|
|
185
|
+
});
|
|
186
|
+
for (const issue of issues) {
|
|
187
|
+
const id = issueIdentifier(issue);
|
|
188
|
+
if (!wanted.has(id)) continue;
|
|
189
|
+
const labels = labelNames(issue);
|
|
190
|
+
const closed = issueIsClosed(issue);
|
|
191
|
+
result.set(id, {
|
|
192
|
+
issue: id,
|
|
193
|
+
open: !closed,
|
|
194
|
+
parked: labels.includes("parked") || LEGACY_PARKED_LABELS.some((label) => labels.includes(label))
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
} catch {}
|
|
198
|
+
return result;
|
|
199
|
+
};
|
|
200
|
+
/** The sole order-book read door. */
|
|
201
|
+
function listBooks(stateRoot) {
|
|
202
|
+
return listOrderBookIds(stateRoot).map((id) => {
|
|
203
|
+
const book = readOrderBook(stateRoot, id);
|
|
204
|
+
if (!book) throw new Error(`Order book index references missing book ${id}`);
|
|
205
|
+
return book;
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
function getBook(stateRoot, bookId) {
|
|
209
|
+
return readOrderBook(stateRoot, bookId);
|
|
210
|
+
}
|
|
211
|
+
function membership(stateRoot) {
|
|
212
|
+
const result = /* @__PURE__ */ new Map();
|
|
213
|
+
for (const book of listBooks(stateRoot)) {
|
|
214
|
+
if (book.status === COMPLETE_STATUS) continue;
|
|
215
|
+
for (const item of book.items) {
|
|
216
|
+
const issue = item.issue.toUpperCase();
|
|
217
|
+
const existing = result.get(issue);
|
|
218
|
+
if (existing && existing !== book.id) throw new Error(`Issue ${issue} belongs to multiple non-complete order books: ${existing}, ${book.id}`);
|
|
219
|
+
result.set(issue, book.id);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return result;
|
|
223
|
+
}
|
|
224
|
+
function backlogCandidates(stateRoot, limit) {
|
|
225
|
+
if (limit <= 0) return [];
|
|
226
|
+
const path = backlogSequencePath(stateRoot);
|
|
227
|
+
if (!existsSync(path)) return [];
|
|
228
|
+
const parsed = parseSequenceMd(readFileSync(path, "utf8"));
|
|
229
|
+
if (!parsed.ok) throw new Error(`Could not parse backlog sequence: ${parsed.error}`);
|
|
230
|
+
const assigned = membership(stateRoot);
|
|
231
|
+
return [...parsed.doc.nodes].sort((a, b) => a.rank - b.rank).filter((node) => !assigned.has(node.issue.toUpperCase())).slice(0, limit);
|
|
232
|
+
}
|
|
233
|
+
function computeBookProgress(book, issueLookup = liveOrderIssueLookup) {
|
|
234
|
+
const state = issueLookup(book.items.map((item) => item.issue));
|
|
235
|
+
const items = book.items.map((item) => {
|
|
236
|
+
const issue = state.get(item.issue.toUpperCase());
|
|
237
|
+
const closed = issue ? !issue.open : false;
|
|
238
|
+
const parked = issue?.parked ?? false;
|
|
239
|
+
return {
|
|
240
|
+
issue: item.issue,
|
|
241
|
+
lane: item.lane,
|
|
242
|
+
order: item.order,
|
|
243
|
+
closed,
|
|
244
|
+
parked,
|
|
245
|
+
terminal: closed || parked
|
|
246
|
+
};
|
|
247
|
+
});
|
|
248
|
+
return {
|
|
249
|
+
bookId: book.id,
|
|
250
|
+
total: items.length,
|
|
251
|
+
landed: items.filter((item) => item.closed).length,
|
|
252
|
+
items,
|
|
253
|
+
drained: items.every((item) => item.terminal)
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
//#endregion
|
|
257
|
+
//#region ../../lib/orders/writer.ts
|
|
258
|
+
init_agents();
|
|
259
|
+
init_control_settings();
|
|
260
|
+
function timestamp(value) {
|
|
261
|
+
return value ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
262
|
+
}
|
|
263
|
+
function requireBook(stateRoot, bookId) {
|
|
264
|
+
const book = getBook(stateRoot, bookId);
|
|
265
|
+
if (!book) throw new Error(`Order book not found: ${bookId}`);
|
|
266
|
+
return book;
|
|
267
|
+
}
|
|
268
|
+
function normalizeLaneOrder(items) {
|
|
269
|
+
const laneA = items.filter((item) => item.lane === "A").sort((a, b) => a.order - b.order);
|
|
270
|
+
const laneB = items.filter((item) => item.lane === "B").sort((a, b) => a.order - b.order);
|
|
271
|
+
return [...laneA.map((item, index) => ({
|
|
272
|
+
...item,
|
|
273
|
+
order: index + 1
|
|
274
|
+
})), ...laneB.map((item, index) => ({
|
|
275
|
+
...item,
|
|
276
|
+
order: index + 1
|
|
277
|
+
}))];
|
|
278
|
+
}
|
|
279
|
+
function updated(book, changes, at) {
|
|
280
|
+
return {
|
|
281
|
+
...book,
|
|
282
|
+
...changes,
|
|
283
|
+
id: book.id,
|
|
284
|
+
createdAt: book.createdAt,
|
|
285
|
+
updatedAt: timestamp(at)
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
/** The sole order-book write door. */
|
|
289
|
+
async function createBook(stateRoot, input) {
|
|
290
|
+
if (getBook(stateRoot, input.id)) throw new Error(`Order book already exists: ${input.id}`);
|
|
291
|
+
const at = timestamp(input.createdAt);
|
|
292
|
+
const settings = {
|
|
293
|
+
laneAConcurrency: input.settings?.laneAConcurrency ?? 1,
|
|
294
|
+
briefOverlay: input.settings?.briefOverlay,
|
|
295
|
+
posture: input.settings?.posture ?? "open",
|
|
296
|
+
postureSetAt: input.settings?.postureSetAt,
|
|
297
|
+
postureSetBy: input.settings?.postureSetBy,
|
|
298
|
+
postureReason: input.settings?.postureReason
|
|
299
|
+
};
|
|
300
|
+
if (!Number.isInteger(settings.laneAConcurrency) || settings.laneAConcurrency < 1) throw new Error("laneAConcurrency must be a positive integer");
|
|
301
|
+
return writeOrderBookState(stateRoot, {
|
|
302
|
+
id: input.id,
|
|
303
|
+
name: input.name,
|
|
304
|
+
status: "draft",
|
|
305
|
+
settings,
|
|
306
|
+
items: [],
|
|
307
|
+
createdAt: at,
|
|
308
|
+
updatedAt: at
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
async function renameBook(stateRoot, bookId, name, at) {
|
|
312
|
+
if (!name.trim()) throw new Error("Order book name cannot be empty");
|
|
313
|
+
return writeOrderBookState(stateRoot, updated(requireBook(stateRoot, bookId), { name: name.trim() }, at));
|
|
314
|
+
}
|
|
315
|
+
async function addItems(stateRoot, bookId, items, actor, at) {
|
|
316
|
+
const book = requireBook(stateRoot, bookId);
|
|
317
|
+
const existing = new Set(book.items.map((item) => item.issue.toUpperCase()));
|
|
318
|
+
const assigned = membership(stateRoot);
|
|
319
|
+
const addedAt = timestamp(at);
|
|
320
|
+
const additions = [];
|
|
321
|
+
for (const item of items) {
|
|
322
|
+
const issue = item.issue.toUpperCase();
|
|
323
|
+
if (existing.has(issue) || additions.some((candidate) => candidate.issue.toUpperCase() === issue)) throw new Error(`Issue ${issue} is already in order book ${bookId}`);
|
|
324
|
+
const otherBook = assigned.get(issue);
|
|
325
|
+
if (otherBook && otherBook !== bookId) throw new Error(`Issue ${issue} already belongs to non-complete order book ${otherBook}`);
|
|
326
|
+
additions.push({
|
|
327
|
+
...item,
|
|
328
|
+
issue,
|
|
329
|
+
prereqs: item.prereqs.map((prereq) => prereq.toUpperCase()),
|
|
330
|
+
addedAt: item.addedAt ?? addedAt,
|
|
331
|
+
addedBy: item.addedBy ?? actor
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
return writeOrderBookState(stateRoot, updated(book, { items: normalizeLaneOrder([...book.items, ...additions]) }, at));
|
|
335
|
+
}
|
|
336
|
+
async function removeItem(stateRoot, bookId, issueId, at) {
|
|
337
|
+
const book = requireBook(stateRoot, bookId);
|
|
338
|
+
const issue = issueId.toUpperCase();
|
|
339
|
+
if (!book.items.some((item) => item.issue.toUpperCase() === issue)) throw new Error(`Issue ${issue} is not in order book ${bookId}`);
|
|
340
|
+
return writeOrderBookState(stateRoot, updated(book, { items: normalizeLaneOrder(book.items.filter((item) => item.issue.toUpperCase() !== issue)) }, at));
|
|
341
|
+
}
|
|
342
|
+
async function moveItem(stateRoot, bookId, issueId, lane, order, at) {
|
|
343
|
+
const book = requireBook(stateRoot, bookId);
|
|
344
|
+
const issue = issueId.toUpperCase();
|
|
345
|
+
const moving = book.items.find((item) => item.issue.toUpperCase() === issue);
|
|
346
|
+
if (!moving) throw new Error(`Issue ${issue} is not in order book ${bookId}`);
|
|
347
|
+
if (!Number.isInteger(order) || order < 1) throw new Error("Order must be a positive integer");
|
|
348
|
+
const remaining = book.items.filter((item) => item !== moving);
|
|
349
|
+
const targetLane = remaining.filter((item) => item.lane === lane).sort((a, b) => a.order - b.order);
|
|
350
|
+
targetLane.splice(Math.min(order - 1, targetLane.length), 0, {
|
|
351
|
+
...moving,
|
|
352
|
+
lane,
|
|
353
|
+
order
|
|
354
|
+
});
|
|
355
|
+
const otherLane = remaining.filter((item) => item.lane !== lane).sort((a, b) => a.order - b.order);
|
|
356
|
+
return writeOrderBookState(stateRoot, updated(book, { items: normalizeLaneOrder([...targetLane, ...otherLane]) }, at));
|
|
357
|
+
}
|
|
358
|
+
async function setItemRequirements(stateRoot, bookId, issueId, requirements, at) {
|
|
359
|
+
const book = requireBook(stateRoot, bookId);
|
|
360
|
+
const issue = issueId.toUpperCase();
|
|
361
|
+
let found = false;
|
|
362
|
+
const items = book.items.map((item) => {
|
|
363
|
+
if (item.issue.toUpperCase() !== issue) return item;
|
|
364
|
+
found = true;
|
|
365
|
+
return {
|
|
366
|
+
...item,
|
|
367
|
+
prereqs: requirements.prereqs === void 0 ? item.prereqs : [...new Set(requirements.prereqs.map((prereq) => prereq.toUpperCase()))],
|
|
368
|
+
reVerify: requirements.reVerify ?? item.reVerify,
|
|
369
|
+
planAtPickup: requirements.planAtPickup ?? item.planAtPickup
|
|
370
|
+
};
|
|
371
|
+
});
|
|
372
|
+
if (!found) throw new Error(`Issue ${issue} is not in order book ${bookId}`);
|
|
373
|
+
return writeOrderBookState(stateRoot, updated(book, { items }, at));
|
|
374
|
+
}
|
|
375
|
+
async function setSettings(stateRoot, bookId, settings, at) {
|
|
376
|
+
const book = requireBook(stateRoot, bookId);
|
|
377
|
+
const next = {
|
|
378
|
+
...book.settings,
|
|
379
|
+
...settings
|
|
380
|
+
};
|
|
381
|
+
if (!Number.isInteger(next.laneAConcurrency) || next.laneAConcurrency < 1) throw new Error("laneAConcurrency must be a positive integer");
|
|
382
|
+
return writeOrderBookState(stateRoot, updated(book, { settings: next }, at));
|
|
383
|
+
}
|
|
384
|
+
async function setStatus(stateRoot, bookId, status, options = {}) {
|
|
385
|
+
const book = requireBook(stateRoot, bookId);
|
|
386
|
+
if (status === "running") {
|
|
387
|
+
const running = listBooks(stateRoot).find((candidate) => candidate.status === "running" && candidate.id !== bookId);
|
|
388
|
+
if (running) throw new Error(`Order book ${running.id} is already running`);
|
|
389
|
+
}
|
|
390
|
+
return writeOrderBookState(stateRoot, updated(book, {
|
|
391
|
+
status,
|
|
392
|
+
runId: options.runId === void 0 ? book.runId : options.runId ?? void 0
|
|
393
|
+
}, options.at));
|
|
394
|
+
}
|
|
395
|
+
async function advanceQueue(stateRoot, completedBookId, at) {
|
|
396
|
+
if (requireBook(stateRoot, completedBookId).status !== "complete") await setStatus(stateRoot, completedBookId, "complete", { at });
|
|
397
|
+
const order = readOrderBookIndex(stateRoot).map((entry) => entry.id);
|
|
398
|
+
for (const id of order) {
|
|
399
|
+
const book = getBook(stateRoot, id);
|
|
400
|
+
if (book?.status === "ready") return book;
|
|
401
|
+
}
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
//#endregion
|
|
405
|
+
//#region services/flywheel-run-state.ts
|
|
406
|
+
init_src();
|
|
407
|
+
const decodeFlywheelStatus = Schema.decodeUnknownSync(FlywheelStatus);
|
|
408
|
+
const decodeFlywheelRunId$1 = Schema.decodeUnknownSync(FlywheelRunId);
|
|
409
|
+
const RUN_ID_PATTERN = /^RUN-(\d+)$/;
|
|
410
|
+
const DEFAULT_RUNS_LIMIT = 20;
|
|
411
|
+
const MAX_RUNS_LIMIT = 100;
|
|
412
|
+
const RUN_SUMMARY_CONCURRENCY = 4;
|
|
413
|
+
const flywheelStatusListeners = /* @__PURE__ */ new Set();
|
|
414
|
+
function subscribeLatestFlywheelStatus(listener) {
|
|
415
|
+
flywheelStatusListeners.add(listener);
|
|
416
|
+
return () => {
|
|
417
|
+
flywheelStatusListeners.delete(listener);
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
function publishLatestFlywheelStatus(status) {
|
|
421
|
+
for (const listener of flywheelStatusListeners) listener(status);
|
|
422
|
+
}
|
|
423
|
+
function publishFlywheelStatusCleared() {
|
|
424
|
+
publishLatestFlywheelStatus(null);
|
|
425
|
+
}
|
|
426
|
+
function parseFlywheelRunId(runId) {
|
|
427
|
+
return decodeFlywheelRunId$1(runId);
|
|
428
|
+
}
|
|
429
|
+
function isFlywheelRunId(runId) {
|
|
430
|
+
return RUN_ID_PATTERN.test(runId);
|
|
431
|
+
}
|
|
432
|
+
function runNumber(runId) {
|
|
433
|
+
const match = RUN_ID_PATTERN.exec(runId);
|
|
434
|
+
return match ? Number(match[1]) : 0;
|
|
435
|
+
}
|
|
436
|
+
function normalizeLimit(limit) {
|
|
437
|
+
if (limit === void 0) return DEFAULT_RUNS_LIMIT;
|
|
438
|
+
if (!Number.isFinite(limit) || limit <= 0) return DEFAULT_RUNS_LIMIT;
|
|
439
|
+
return Math.min(Math.floor(limit), MAX_RUNS_LIMIT);
|
|
440
|
+
}
|
|
441
|
+
async function mapWithConcurrency(items, concurrency, mapper) {
|
|
442
|
+
const results = new Array(items.length);
|
|
443
|
+
let index = 0;
|
|
444
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, async () => {
|
|
445
|
+
while (index < items.length) {
|
|
446
|
+
const current = index;
|
|
447
|
+
index += 1;
|
|
448
|
+
results[current] = await mapper(items[current]);
|
|
449
|
+
}
|
|
450
|
+
}));
|
|
451
|
+
return results;
|
|
452
|
+
}
|
|
453
|
+
function getFlywheelHome(options = {}) {
|
|
454
|
+
return join(options.overdeckHome ?? process.env["OVERDECK_HOME"] ?? join(homedir(), ".overdeck"), "flywheel");
|
|
455
|
+
}
|
|
456
|
+
function getFlywheelRunsDir(options = {}) {
|
|
457
|
+
return join(getFlywheelHome(options), "runs");
|
|
458
|
+
}
|
|
459
|
+
function getFlywheelRunDir(runId, options = {}) {
|
|
460
|
+
return join(getFlywheelRunsDir(options), parseFlywheelRunId(runId));
|
|
461
|
+
}
|
|
462
|
+
/** Persist the run's cohort (issue ids) at run start. */
|
|
463
|
+
function saveRunCohort(runId, issueIds, options = {}) {
|
|
464
|
+
const dir = getFlywheelRunDir(runId, options);
|
|
465
|
+
mkdirSync(dir, { recursive: true });
|
|
466
|
+
writeFileSync(join(dir, "cohort.json"), JSON.stringify({
|
|
467
|
+
issueIds: [...issueIds],
|
|
468
|
+
snapshotAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
469
|
+
}, null, 2), "utf-8");
|
|
470
|
+
}
|
|
471
|
+
async function nextFlywheelRunId(options = {}) {
|
|
472
|
+
const runsDir = getFlywheelRunsDir(options);
|
|
473
|
+
await mkdir(runsDir, { recursive: true });
|
|
474
|
+
return parseFlywheelRunId(`RUN-${(await readdir(runsDir, { withFileTypes: true })).reduce((max, entry) => {
|
|
475
|
+
if (!entry.isDirectory()) return max;
|
|
476
|
+
const match = RUN_ID_PATTERN.exec(entry.name);
|
|
477
|
+
if (!match) return max;
|
|
478
|
+
return Math.max(max, Number(match[1]));
|
|
479
|
+
}, 0) + 1}`);
|
|
480
|
+
}
|
|
481
|
+
async function writeJsonAtomic(path, payload) {
|
|
482
|
+
const tmpPath = join(dirname(path), `.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`);
|
|
483
|
+
await mkdir(dirname(path), { recursive: true });
|
|
484
|
+
await writeFile(tmpPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
485
|
+
await rename(tmpPath, path);
|
|
486
|
+
return path;
|
|
487
|
+
}
|
|
488
|
+
function enrichFlywheelStatusWithOrders(status, book, progress) {
|
|
489
|
+
const terminalPipelineStatuses = new Set([
|
|
490
|
+
"failed",
|
|
491
|
+
"merged",
|
|
492
|
+
"parked"
|
|
493
|
+
]);
|
|
494
|
+
const inFlight = new Set(status.activePipeline.filter((item) => !terminalPipelineStatuses.has(item.status)).map((item) => item.issueId.toUpperCase()));
|
|
495
|
+
const laneAInFlight = book.items.filter((item) => item.lane === "A" && inFlight.has(item.issue.toUpperCase())).sort((left, right) => left.order - right.order).map((item) => item.issue);
|
|
496
|
+
const laneBInFlight = book.items.filter((item) => item.lane === "B" && inFlight.has(item.issue.toUpperCase())).sort((left, right) => left.order - right.order)[0]?.issue;
|
|
497
|
+
return {
|
|
498
|
+
...status,
|
|
499
|
+
orders: {
|
|
500
|
+
bookId: book.id,
|
|
501
|
+
bookName: book.name,
|
|
502
|
+
landed: progress.landed,
|
|
503
|
+
total: progress.total,
|
|
504
|
+
laneAInFlight,
|
|
505
|
+
...laneBInFlight ? { laneBInFlight } : {},
|
|
506
|
+
drained: progress.drained
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
async function mechanicallyEnrichOrdersStatus(status, options) {
|
|
511
|
+
const launch = await readFlywheelLaunchMetadata(status.runId, options);
|
|
512
|
+
if (!launch?.orders) {
|
|
513
|
+
const { orders: _untrustedOrders, ...booklessStatus } = status;
|
|
514
|
+
return booklessStatus;
|
|
515
|
+
}
|
|
516
|
+
const stateRoot = options.orderStateRoot?.(launch.workspace) ?? (() => {
|
|
517
|
+
const project = findProjectByPathSync(launch.workspace);
|
|
518
|
+
if (!project) throw new Error(`No configured project contains orders-bound Flywheel workspace ${launch.workspace}`);
|
|
519
|
+
return resolveStateReadHomeSync(project).root;
|
|
520
|
+
})();
|
|
521
|
+
const book = (options.getOrderBook ?? getBook)(stateRoot, launch.orders.bookId);
|
|
522
|
+
if (!book) throw new Error(`Flywheel run ${status.runId} references missing order book ${launch.orders.bookId}`);
|
|
523
|
+
const progress = (options.computeOrderProgress ?? computeBookProgress)(book);
|
|
524
|
+
if (progress.drained && book.status === "running") await (options.setOrderStatus ?? setStatus)(stateRoot, book.id, "drained", { runId: book.runId });
|
|
525
|
+
return enrichFlywheelStatusWithOrders(status, book, progress);
|
|
526
|
+
}
|
|
527
|
+
async function writeLatestFlywheelStatus(status, options = {}) {
|
|
528
|
+
const enriched = await mechanicallyEnrichOrdersStatus(status, options);
|
|
529
|
+
const latestPath = join(getFlywheelRunDir(status.runId, options), "latest.json");
|
|
530
|
+
await writeJsonAtomic(latestPath, enriched);
|
|
531
|
+
publishLatestFlywheelStatus(enriched);
|
|
532
|
+
return latestPath;
|
|
533
|
+
}
|
|
534
|
+
function decodeLaunchMetadata(payload) {
|
|
535
|
+
if (typeof payload !== "object" || payload === null) throw new Error("Invalid Flywheel launch metadata");
|
|
536
|
+
const record = payload;
|
|
537
|
+
if (record.version !== 1) throw new Error("Invalid Flywheel launch metadata version");
|
|
538
|
+
for (const key of [
|
|
539
|
+
"runId",
|
|
540
|
+
"workspace",
|
|
541
|
+
"briefPath",
|
|
542
|
+
"briefDisplayPath"
|
|
543
|
+
]) if (typeof record[key] !== "string" || !record[key]) throw new Error(`Invalid Flywheel launch metadata: ${key}`);
|
|
544
|
+
if (record.briefOverlayPath !== void 0 && (typeof record.briefOverlayPath !== "string" || !record.briefOverlayPath)) throw new Error("Invalid Flywheel launch metadata: briefOverlayPath");
|
|
545
|
+
if (record.orders !== void 0) {
|
|
546
|
+
const orders = record.orders;
|
|
547
|
+
if (!orders || typeof orders !== "object" || typeof orders.bookId !== "string" || !orders.bookId) throw new Error("Invalid Flywheel launch metadata: orders.bookId");
|
|
548
|
+
}
|
|
549
|
+
return record;
|
|
550
|
+
}
|
|
551
|
+
async function writeFlywheelLaunchMetadata(metadata, options = {}) {
|
|
552
|
+
const runId = parseFlywheelRunId(metadata.runId);
|
|
553
|
+
return writeJsonAtomic(join(getFlywheelRunDir(runId, options), "launch.json"), {
|
|
554
|
+
...metadata,
|
|
555
|
+
runId
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
async function readFlywheelLaunchMetadata(runId, options = {}) {
|
|
559
|
+
const launchPath = join(getFlywheelRunDir(runId, options), "launch.json");
|
|
560
|
+
try {
|
|
561
|
+
return decodeLaunchMetadata(JSON.parse(await readFile(launchPath, "utf8")));
|
|
562
|
+
} catch (error) {
|
|
563
|
+
if ((typeof error === "object" && error !== null && "code" in error ? error.code : void 0) === "ENOENT") return null;
|
|
564
|
+
throw error;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
async function readLatestFlywheelStatus(runId, options = {}) {
|
|
568
|
+
const latestPath = join(getFlywheelRunDir(runId, options), "latest.json");
|
|
569
|
+
try {
|
|
570
|
+
return decodeFlywheelStatus(JSON.parse(await readFile(latestPath, "utf8")));
|
|
571
|
+
} catch (error) {
|
|
572
|
+
if ((typeof error === "object" && error !== null && "code" in error ? error.code : void 0) === "ENOENT") return null;
|
|
573
|
+
throw error;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
async function fileExists(path) {
|
|
577
|
+
try {
|
|
578
|
+
return (await stat$1(path)).isFile();
|
|
579
|
+
} catch (error) {
|
|
580
|
+
if ((typeof error === "object" && error !== null && "code" in error ? error.code : void 0) === "ENOENT") return false;
|
|
581
|
+
throw error;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
async function deriveRunStatus(runDir, runId, activeRunId) {
|
|
585
|
+
if (await fileExists(join(runDir, "aborted.json"))) return "aborted";
|
|
586
|
+
if (await fileExists(join(runDir, "report.md"))) return "complete";
|
|
587
|
+
if (activeRunId !== runId) return "aborted";
|
|
588
|
+
return isFlywheelGloballyPaused() ? "paused" : "running";
|
|
589
|
+
}
|
|
590
|
+
async function summarizeRun(runId, options, activeRunId) {
|
|
591
|
+
const runDir = getFlywheelRunDir(runId, options);
|
|
592
|
+
const latest = await readLatestFlywheelStatus(runId, options);
|
|
593
|
+
const status = await deriveRunStatus(runDir, runId, activeRunId);
|
|
594
|
+
return {
|
|
595
|
+
id: runId,
|
|
596
|
+
startedAt: latest?.startedAt ?? "",
|
|
597
|
+
status
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
async function readCurrentLatestFlywheelStatus(options = {}) {
|
|
601
|
+
const activeRunId = options.activeRunId === void 0 ? getFlywheelActiveRunId() : options.activeRunId;
|
|
602
|
+
if (!activeRunId || !isFlywheelRunId(activeRunId)) return null;
|
|
603
|
+
const activeRun = await getFlywheelRunDetail(activeRunId, options);
|
|
604
|
+
return activeRun?.status === "running" || activeRun?.status === "paused" ? activeRun.latest : null;
|
|
605
|
+
}
|
|
606
|
+
function selectLatestRunIds(entries, limit) {
|
|
607
|
+
const selected = [];
|
|
608
|
+
for (const entry of entries) {
|
|
609
|
+
if (!entry.isDirectory() || !isFlywheelRunId(entry.name)) continue;
|
|
610
|
+
selected.push(entry.name);
|
|
611
|
+
selected.sort((a, b) => runNumber(b) - runNumber(a));
|
|
612
|
+
if (selected.length > limit) selected.length = limit;
|
|
613
|
+
}
|
|
614
|
+
return selected;
|
|
615
|
+
}
|
|
616
|
+
async function listFlywheelRuns(options = {}) {
|
|
617
|
+
const runsDir = getFlywheelRunsDir(options);
|
|
618
|
+
let entries;
|
|
619
|
+
try {
|
|
620
|
+
entries = await readdir(runsDir, { withFileTypes: true });
|
|
621
|
+
} catch (error) {
|
|
622
|
+
if ((typeof error === "object" && error !== null && "code" in error ? error.code : void 0) === "ENOENT") return [];
|
|
623
|
+
throw error;
|
|
624
|
+
}
|
|
625
|
+
const runIds = selectLatestRunIds(entries, normalizeLimit(options.limit));
|
|
626
|
+
const activeRunId = getFlywheelActiveRunId();
|
|
627
|
+
return (await mapWithConcurrency(runIds, RUN_SUMMARY_CONCURRENCY, (runId) => summarizeRun(runId, options, activeRunId))).sort((a, b) => b.startedAt.localeCompare(a.startedAt));
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* PAN-1528/PAN-1908: overlay the flywheel capacity gauge from the indexed
|
|
631
|
+
* agents table. This status path must not enumerate state.json files.
|
|
632
|
+
*/
|
|
633
|
+
async function countLiveWorkAgents(_options = {}) {
|
|
634
|
+
return countAgentsByStatusRole("running", "work");
|
|
635
|
+
}
|
|
636
|
+
function withLiveAgentsActive(latest, liveCount) {
|
|
637
|
+
if (latest.system.agentsActive === liveCount) return latest;
|
|
638
|
+
return {
|
|
639
|
+
...latest,
|
|
640
|
+
system: {
|
|
641
|
+
...latest.system,
|
|
642
|
+
agentsActive: liveCount
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
async function getFlywheelRunDetail(runId, options = {}) {
|
|
647
|
+
const runDir = getFlywheelRunDir(runId, options);
|
|
648
|
+
const latest = await readLatestFlywheelStatus(runId, options);
|
|
649
|
+
if (!latest) return null;
|
|
650
|
+
const status = await deriveRunStatus(runDir, runId, options.activeRunId !== void 0 ? options.activeRunId : getFlywheelActiveRunId());
|
|
651
|
+
const reportPath = join(runDir, "report.md");
|
|
652
|
+
const openedPrPath = join(runDir, "opened-pr.json");
|
|
653
|
+
const liveCount = await countLiveWorkAgents(options);
|
|
654
|
+
return {
|
|
655
|
+
id: runId,
|
|
656
|
+
startedAt: latest.startedAt,
|
|
657
|
+
status,
|
|
658
|
+
latest: withLiveAgentsActive(latest, liveCount),
|
|
659
|
+
...latest.orders ? { orders: latest.orders } : {},
|
|
660
|
+
paths: {
|
|
661
|
+
latest: join(runDir, "latest.json"),
|
|
662
|
+
...await fileExists(reportPath) ? { report: reportPath } : {},
|
|
663
|
+
...await fileExists(openedPrPath) ? { openedPr: openedPrPath } : {}
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
function clearFlywheelGate() {
|
|
668
|
+
setFlywheelActiveRunId(null);
|
|
669
|
+
setFlywheelGloballyPaused(false);
|
|
670
|
+
publishFlywheelStatusCleared();
|
|
671
|
+
}
|
|
672
|
+
async function resolveLiveFlywheelRunId(options = {}) {
|
|
673
|
+
const candidate = getFlywheelActiveRunId();
|
|
674
|
+
if (!candidate) return null;
|
|
675
|
+
if (!isFlywheelRunId(candidate)) {
|
|
676
|
+
clearFlywheelGate();
|
|
677
|
+
return null;
|
|
678
|
+
}
|
|
679
|
+
if (!await readLatestFlywheelStatus(candidate, options)) {
|
|
680
|
+
clearFlywheelGate();
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
const status = await deriveRunStatus(getFlywheelRunDir(candidate, options), candidate, candidate);
|
|
684
|
+
if (status === "complete" || status === "aborted") {
|
|
685
|
+
clearFlywheelGate();
|
|
686
|
+
return null;
|
|
687
|
+
}
|
|
688
|
+
return candidate;
|
|
689
|
+
}
|
|
690
|
+
async function abortFlywheelRun(runId, options = {}) {
|
|
691
|
+
const parsed = parseFlywheelRunId(runId);
|
|
692
|
+
await writeJsonAtomic(join(getFlywheelRunDir(parsed, options), "aborted.json"), {
|
|
693
|
+
runId: parsed,
|
|
694
|
+
abortedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
695
|
+
});
|
|
696
|
+
if (getFlywheelActiveRunId() === parsed) clearFlywheelGate();
|
|
697
|
+
}
|
|
698
|
+
//#endregion
|
|
699
|
+
//#region ../../lib/flywheel-start.ts
|
|
700
|
+
const execAsync = promisify(exec);
|
|
701
|
+
function isInsideRoot(projectRoot, candidate) {
|
|
702
|
+
const relativePath = relative(projectRoot, candidate);
|
|
703
|
+
return relativePath === "" || !relativePath.startsWith("..") && !isAbsolute(relativePath);
|
|
704
|
+
}
|
|
705
|
+
function resolveFlywheelStartBriefPath(cwd, requestedPath) {
|
|
706
|
+
const rawPath = requestedPath?.trim() || "docs/flywheel-brief.md";
|
|
707
|
+
if (rawPath.includes("\0")) throw new Error("Brief path is invalid");
|
|
708
|
+
const root = resolve(cwd);
|
|
709
|
+
const absolutePath = isAbsolute(rawPath) ? resolve(rawPath) : resolve(root, rawPath);
|
|
710
|
+
if (!isInsideRoot(root, absolutePath)) throw new Error("Brief path must stay inside the project root");
|
|
711
|
+
const normalizedRoot = root.endsWith(sep) ? root : `${root}${sep}`;
|
|
712
|
+
const displayPath = absolutePath === root ? "." : relative(root, absolutePath);
|
|
713
|
+
return {
|
|
714
|
+
absolutePath,
|
|
715
|
+
displayPath: absolutePath.startsWith(normalizedRoot) ? displayPath : absolutePath
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
async function assertExistingPathInsideRoot(projectRoot, candidate) {
|
|
719
|
+
const [realRoot, realCandidate] = await Promise.all([realpath(projectRoot), realpath(candidate)]);
|
|
720
|
+
if (!isInsideRoot(realRoot, realCandidate)) throw new Error("Brief path must stay inside the project root");
|
|
721
|
+
}
|
|
722
|
+
async function requireFlywheelBrief(cwd, requestedPath) {
|
|
723
|
+
const resolved = resolveFlywheelStartBriefPath(cwd, requestedPath);
|
|
724
|
+
try {
|
|
725
|
+
await assertExistingPathInsideRoot(cwd, resolved.absolutePath);
|
|
726
|
+
await readFile(resolved.absolutePath, "utf8");
|
|
727
|
+
return resolved;
|
|
728
|
+
} catch (error) {
|
|
729
|
+
if ((typeof error === "object" && error !== null && "code" in error ? error.code : void 0) === "ENOENT") throw new Error(`Flywheel brief not found: ${resolved.displayPath}`);
|
|
730
|
+
throw error;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* The flywheel is a project-scoped singleton: it must run from the PRIMARY
|
|
735
|
+
* worktree root, never a linked feature worktree. Every worktree contains
|
|
736
|
+
* docs/flywheel-brief.md, so the brief check alone cannot catch a caller whose
|
|
737
|
+
* shell happens to sit inside a workspaces/feature-* checkout, and an
|
|
738
|
+
* orchestrator spawned there operates inside a live work agent's worktree.
|
|
739
|
+
* A linked worktree is
|
|
740
|
+
* detected by `git rev-parse --git-common-dir` resolving to the primary's
|
|
741
|
+
* .git directory; outside a repo (or on failure) the cwd is kept and the
|
|
742
|
+
* brief check reports its own error.
|
|
743
|
+
*/
|
|
744
|
+
async function resolvePrimaryWorktreeRoot(cwd) {
|
|
745
|
+
try {
|
|
746
|
+
const { stdout } = await execAsync("git rev-parse --path-format=absolute --git-common-dir", {
|
|
747
|
+
cwd,
|
|
748
|
+
timeout: 5e3
|
|
749
|
+
});
|
|
750
|
+
const commonDir = stdout.trim();
|
|
751
|
+
if (commonDir.endsWith(`${sep}.git`)) return dirname(commonDir);
|
|
752
|
+
return cwd;
|
|
753
|
+
} catch {
|
|
754
|
+
return cwd;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
//#endregion
|
|
758
|
+
//#region ../../lib/backlog/lookups.ts
|
|
759
|
+
/**
|
|
760
|
+
* Build the {@link ClassifyLookups} the shared pickup module needs from live project
|
|
761
|
+
* state: labels (in-memory issue service), planned (readable xBRIEF spec), and
|
|
762
|
+
* in-pipeline (review status / live workspace). Shared by the dashboard forecast
|
|
763
|
+
* route and the Flywheel run-cohort snapshot so both classify issues identically
|
|
764
|
+
* (PAN-2006 single source of truth). The issue service is lazy-required to avoid a
|
|
765
|
+
* static lib → dashboard layering edge.
|
|
766
|
+
*/
|
|
767
|
+
function buildClassifyLookups(projectRoot, opts = {}) {
|
|
768
|
+
const labelsByIssue = /* @__PURE__ */ new Map();
|
|
769
|
+
if (!opts?.labels) try {
|
|
770
|
+
const { getSharedIssueService } = (init_issue_service_singleton(), __toCommonJS(issue_service_singleton_exports));
|
|
771
|
+
for (const issue of getSharedIssueService().getIssues()) {
|
|
772
|
+
const id = typeof issue["identifier"] === "string" ? issue["identifier"].toUpperCase() : "";
|
|
773
|
+
if (!id) continue;
|
|
774
|
+
const names = (Array.isArray(issue["labels"]) ? issue["labels"] : []).map((l) => typeof l === "string" ? l : l?.name ?? "").filter((s) => Boolean(s));
|
|
775
|
+
labelsByIssue.set(id, names);
|
|
776
|
+
}
|
|
777
|
+
} catch {}
|
|
778
|
+
const specsDir = join(projectRoot, ".pan", "specs");
|
|
779
|
+
const specIssues = /* @__PURE__ */ new Set();
|
|
780
|
+
if (existsSync(specsDir)) for (const f of readdirSync(specsDir)) {
|
|
781
|
+
const m = /^[\d-]+-([A-Z]+-\d+)-/i.exec(f);
|
|
782
|
+
if (m) specIssues.add(m[1].toUpperCase());
|
|
783
|
+
}
|
|
784
|
+
const workspacesDir = join(projectRoot, "workspaces");
|
|
785
|
+
return {
|
|
786
|
+
labels: opts?.labels ?? ((id) => labelsByIssue.get(id.toUpperCase()) ?? []),
|
|
787
|
+
isPlanned: (id) => {
|
|
788
|
+
const u = id.toUpperCase();
|
|
789
|
+
return specIssues.has(u);
|
|
790
|
+
},
|
|
791
|
+
isInPipeline: (id) => {
|
|
792
|
+
const rs = getReviewStatusSync(id.toUpperCase());
|
|
793
|
+
return rs !== null && rs.reviewStatus !== "pending" || existsSync(join(workspacesDir, `feature-${id.toLowerCase()}`));
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
//#endregion
|
|
798
|
+
//#region ../../lib/cloister/flywheel.ts
|
|
799
|
+
init_sequence_io();
|
|
800
|
+
init_projects();
|
|
801
|
+
init_state_read_home();
|
|
802
|
+
init_control_settings();
|
|
803
|
+
init_pickup();
|
|
804
|
+
init_pipeline_membership_gather();
|
|
805
|
+
init_pipeline_membership();
|
|
806
|
+
const FLYWHEEL_ORCHESTRATOR_AGENT_ID = "flywheel-orchestrator";
|
|
807
|
+
function isIssueInResolvedPipeline(issueId, memberships) {
|
|
808
|
+
return memberships.some((membership) => membership.issueId.toUpperCase() === issueId.toUpperCase() && membership.inPipeline);
|
|
809
|
+
}
|
|
810
|
+
async function activeOrderBookIssues(projectRoot, runId, deps = {}) {
|
|
811
|
+
const activeRunId = runId ?? (deps.activeRunId ?? getFlywheelActiveRunId)();
|
|
812
|
+
if (!activeRunId) return /* @__PURE__ */ new Set();
|
|
813
|
+
const bookId = (await (deps.readLaunch ?? readFlywheelLaunchMetadata)(activeRunId))?.orders?.bookId;
|
|
814
|
+
if (!bookId) return /* @__PURE__ */ new Set();
|
|
815
|
+
const stateRoot = deps.stateRoot?.(projectRoot) ?? (() => {
|
|
816
|
+
const project = findProjectByPathSync(projectRoot);
|
|
817
|
+
return project ? resolveStateReadHomeSync(project).root : null;
|
|
818
|
+
})();
|
|
819
|
+
if (!stateRoot) return /* @__PURE__ */ new Set();
|
|
820
|
+
const book = (deps.getBook ?? getBook)(stateRoot, bookId);
|
|
821
|
+
return new Set(book?.items.map((item) => item.issue.toUpperCase()) ?? []);
|
|
822
|
+
}
|
|
823
|
+
const FlywheelRunIdSchema = Schema.String.check(Schema.isPattern(/^RUN-\d+$/));
|
|
824
|
+
const decodeFlywheelRunId = Schema.decodeUnknownSync(FlywheelRunIdSchema);
|
|
825
|
+
function parseRunId(runId) {
|
|
826
|
+
return decodeFlywheelRunId(runId);
|
|
827
|
+
}
|
|
828
|
+
function defaultFlywheelRunId() {
|
|
829
|
+
return parseRunId(`RUN-${Date.now()}`);
|
|
830
|
+
}
|
|
831
|
+
function internalDashboardOrigin() {
|
|
832
|
+
const port = Number.parseInt(process.env["API_PORT"] ?? process.env["PORT"] ?? "3011", 10);
|
|
833
|
+
return process.env["OVERDECK_INTERNAL_DASHBOARD_URL"] ?? `http://127.0.0.1:${port}`;
|
|
834
|
+
}
|
|
835
|
+
async function fetchIssueRowsFromDashboard() {
|
|
836
|
+
const dashboardOrigin = internalDashboardOrigin();
|
|
837
|
+
const token = getInternalTokenSync();
|
|
838
|
+
if (!token) {
|
|
839
|
+
console.error("[flywheel] Cannot load issue metadata: internal dashboard token is unavailable");
|
|
840
|
+
return [];
|
|
841
|
+
}
|
|
842
|
+
try {
|
|
843
|
+
const response = await fetch(new URL("/api/issues?cycle=all&includeCompleted=true", dashboardOrigin), { headers: {
|
|
844
|
+
[INTERNAL_TOKEN_HEADER]: token,
|
|
845
|
+
origin: dashboardOrigin
|
|
846
|
+
} });
|
|
847
|
+
if (!response.ok) {
|
|
848
|
+
console.error(`[flywheel] Cannot load issue metadata from dashboard: HTTP ${response.status}`);
|
|
849
|
+
return [];
|
|
850
|
+
}
|
|
851
|
+
const body = await response.json();
|
|
852
|
+
if (!Array.isArray(body)) {
|
|
853
|
+
console.error("[flywheel] Cannot load issue metadata from dashboard: response was not an array");
|
|
854
|
+
return [];
|
|
855
|
+
}
|
|
856
|
+
return body;
|
|
857
|
+
} catch (error) {
|
|
858
|
+
console.error("[flywheel] Cannot load issue metadata from dashboard:", error);
|
|
859
|
+
return [];
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
async function flywheelRunConfigurationSection(options, runId) {
|
|
863
|
+
const configLines = [
|
|
864
|
+
options.harness ? `Harness: ${options.harness}` : void 0,
|
|
865
|
+
options.effort ? `Effort: ${options.effort}` : void 0,
|
|
866
|
+
options.minAgents ? `Min concurrent agents (target): ${options.minAgents}` : void 0,
|
|
867
|
+
options.maxAgents ? `Max concurrent agents (ceiling): ${options.maxAgents}` : void 0,
|
|
868
|
+
options.scope ? `Scope: ${options.scope}` : void 0,
|
|
869
|
+
typeof options.autoPickupBacklog === "boolean" ? `Auto-pickup backlog: ${options.autoPickupBacklog}` : void 0,
|
|
870
|
+
typeof options.requireUatBeforeMerge === "boolean" ? `Require UAT before merge: ${options.requireUatBeforeMerge}` : void 0
|
|
871
|
+
].filter(Boolean).join("\n");
|
|
872
|
+
let sequenceSection = "";
|
|
873
|
+
const projectRoot = process.cwd();
|
|
874
|
+
const orderBookIssues = await activeOrderBookIssues(projectRoot, runId);
|
|
875
|
+
if (options.autoPickupBacklog || orderBookIssues.size > 0) {
|
|
876
|
+
const seqPath = join(projectRoot, ".pan", "backlog", "sequence.md");
|
|
877
|
+
if (existsSync(seqPath)) try {
|
|
878
|
+
const parsed = parseSequenceMd(readFileSync(seqPath, "utf-8"));
|
|
879
|
+
if (parsed.ok) {
|
|
880
|
+
const getIssueRows = async () => fetchIssueRowsFromDashboard();
|
|
881
|
+
const issueRowMap = new Map((await getIssueRows()).map((i) => [i.ref ?? i.identifier ?? "", i]));
|
|
882
|
+
const issueLabelsLookup = (issueId) => issueRowMap.get(issueId)?.labels ?? [];
|
|
883
|
+
const AUTHORIZED_AUTHORS = new Set(["eltmon", "panopticon-agent[bot]"]);
|
|
884
|
+
const isAuthorizedIssue = (issueId) => {
|
|
885
|
+
const row = issueRowMap.get(issueId);
|
|
886
|
+
if (!row) return false;
|
|
887
|
+
if (row.author && AUTHORIZED_AUTHORS.has(row.author)) return true;
|
|
888
|
+
return (typeof row.assignee === "string" ? row.assignee : row.assignee?.name) === "eltmon";
|
|
889
|
+
};
|
|
890
|
+
const specsDir = join(projectRoot, ".pan", "specs");
|
|
891
|
+
const issuesWithSpecs = /* @__PURE__ */ new Set();
|
|
892
|
+
const declaredFootprints = [];
|
|
893
|
+
if (existsSync(specsDir)) for (const f of readdirSync(specsDir)) {
|
|
894
|
+
const match = /^[\d-]+-([A-Z]+-\d+)-/i.exec(f);
|
|
895
|
+
if (!match) continue;
|
|
896
|
+
const issueId = match[1].toUpperCase();
|
|
897
|
+
issuesWithSpecs.add(issueId);
|
|
898
|
+
try {
|
|
899
|
+
const doc = JSON.parse(readFileSync(join(specsDir, f), "utf-8"));
|
|
900
|
+
declaredFootprints.push(declaredIssueFootprint(issueId, doc));
|
|
901
|
+
} catch {}
|
|
902
|
+
}
|
|
903
|
+
const projectConfig = findProjectByPathSync(projectRoot);
|
|
904
|
+
const predictedConflictSignals = computePredictedConflictSignals(declaredFootprints, { hotspots: getProjectSwarmHotspots(projectConfig) });
|
|
905
|
+
const isReadyOrHasPrd = (issueId) => {
|
|
906
|
+
const id = issueId.toUpperCase();
|
|
907
|
+
if (issuesWithSpecs.has(id)) return true;
|
|
908
|
+
return existsSync(join(projectRoot, ".pan", "drafts", `${id}.md`));
|
|
909
|
+
};
|
|
910
|
+
const membershipAvailable = Boolean(projectConfig?.github_repo);
|
|
911
|
+
const memberships = projectConfig?.github_repo ? (await gatherProjectLensSignals(projectConfig)).map(resolvePipelineMembership) : [];
|
|
912
|
+
const isInPipeline = (issueId) => membershipAvailable ? isIssueInResolvedPipeline(issueId, memberships) : true;
|
|
913
|
+
const top10 = parsed.doc.nodes.slice(0, 10).map((n) => ` #${n.rank} ${n.issue}: ${n.why.slice(0, 100)} [gate:${n.gate}]`);
|
|
914
|
+
const nextPick = pickFromSequence(parsed.doc.nodes, {
|
|
915
|
+
issueLabels: issueLabelsLookup,
|
|
916
|
+
isAuthorizedIssue,
|
|
917
|
+
isReadyOrHasPrd,
|
|
918
|
+
isInPipeline,
|
|
919
|
+
requireReady: true,
|
|
920
|
+
autoPickupBacklog: options.autoPickupBacklog,
|
|
921
|
+
activeBookMembership: orderBookIssues,
|
|
922
|
+
predictedConflictSignals
|
|
923
|
+
});
|
|
924
|
+
let nextLine;
|
|
925
|
+
let pickInstruction;
|
|
926
|
+
if (!membershipAvailable) {
|
|
927
|
+
nextLine = "MEMBERSHIP UNAVAILABLE: project has no GitHub repository; auto-pickup is disabled";
|
|
928
|
+
pickInstruction = "\n\nIMPORTANT: Canonical pipeline membership is unavailable for this project. Do NOT auto-start backlog work; surface the missing github_repo configuration to the operator.";
|
|
929
|
+
} else if (!nextPick) {
|
|
930
|
+
nextLine = "No eligible issue found in sequence — fall back to normal priority";
|
|
931
|
+
pickInstruction = "";
|
|
932
|
+
} else if (nextPick.planning === "interactive") {
|
|
933
|
+
nextLine = `NEEDS OPERATOR ACTION: ${nextPick.issueId} (rank ${nextPick.rank}) has planning=interactive — do NOT auto-start; operator must run 'pan plan ${nextPick.issueId}'`;
|
|
934
|
+
pickInstruction = `\n\nIMPORTANT: auto_pickup_backlog=true. The top-ranked issue requires interactive planning and MUST NOT be auto-started. Surface it to the operator for manual 'pan plan' invocation instead of auto-picking it.`;
|
|
935
|
+
} else {
|
|
936
|
+
const conflictSuffix = typeof nextPick.predictedConflictCount === "number" ? `, predictedConflicts=${nextPick.predictedConflictCount}${nextPick.predictedConflictsWith?.length ? ` with ${nextPick.predictedConflictsWith.join(", ")}` : ""}` : "";
|
|
937
|
+
nextLine = `MUST start next: ${nextPick.issueId} (rank ${nextPick.rank}, planning=${nextPick.planning}${conflictSuffix})`;
|
|
938
|
+
pickInstruction = `\n\nIMPORTANT: auto_pickup_backlog=true. You MUST pick the "MUST start next" issue above as your next startup target. Do NOT apply your own P0-P3/oldest-first ranking while a sequence is available.`;
|
|
939
|
+
}
|
|
940
|
+
sequenceSection = `\n\nBacklog sequence (${parsed.doc.nodes.length} issues ranked):\n${top10.join("\n")}\n${nextLine}${pickInstruction}`;
|
|
941
|
+
}
|
|
942
|
+
} catch {}
|
|
943
|
+
}
|
|
944
|
+
return (configLines ? `\n\nRun configuration:\n${configLines}` : "") + sequenceSection;
|
|
945
|
+
}
|
|
946
|
+
async function defaultFlywheelPrompt(runId, options, briefContent) {
|
|
947
|
+
return `FLYWHEEL ORCHESTRATOR TASK for ${runId}:
|
|
948
|
+
|
|
949
|
+
Run the Fix-All Flywheel loop. Keep status snapshots current, coordinate Overdeck roles through the normal pipeline surfaces, respect the configured run scope and agent cap, and wait for explicit lifecycle instructions when the run is paused or complete.${await flywheelRunConfigurationSection(options, runId)}${options.briefPath ? `\n\nBrief path: ${options.briefPath}\n\n${briefContent ?? ""}` : ""}`;
|
|
950
|
+
}
|
|
951
|
+
function getLocalFlywheelRunDir(runId) {
|
|
952
|
+
return join(process.env.OVERDECK_HOME ?? join(homedir(), ".overdeck"), "flywheel", "runs", runId);
|
|
953
|
+
}
|
|
954
|
+
async function saveResumeSessionId(runId, sessionId) {
|
|
955
|
+
const runDir = getLocalFlywheelRunDir(runId);
|
|
956
|
+
await mkdir(runDir, { recursive: true });
|
|
957
|
+
await writeFile(join(runDir, "resume-session.json"), JSON.stringify({
|
|
958
|
+
sessionId,
|
|
959
|
+
pausedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
960
|
+
}), "utf-8");
|
|
961
|
+
}
|
|
962
|
+
async function loadResumeSessionId(runId) {
|
|
963
|
+
try {
|
|
964
|
+
const raw = await readFile(join(getLocalFlywheelRunDir(runId), "resume-session.json"), "utf-8");
|
|
965
|
+
const parsed = JSON.parse(raw);
|
|
966
|
+
return typeof parsed.sessionId === "string" ? parsed.sessionId : null;
|
|
967
|
+
} catch {
|
|
968
|
+
return null;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
function isFlywheelDevcontainerRuntime(env = process.env) {
|
|
972
|
+
const disabledDeacon = env.OVERDECK_DISABLE_DEACON?.toLowerCase();
|
|
973
|
+
if (disabledDeacon === "1" || disabledDeacon === "true") return true;
|
|
974
|
+
const hostname = env.HOSTNAME?.toLowerCase() ?? "";
|
|
975
|
+
return hostname.includes("devcontainer") || hostname.startsWith("api-feature-") || hostname.startsWith("workspace-");
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* Resume prompt. PAN-2006 FR-8: re-attach the standing brief on resume so its
|
|
979
|
+
* directives (pipeline-unblock override, never-block, red-main-first) survive a
|
|
980
|
+
* resume + context compaction — the pre-PAN-2006 resume prompt only pointed at
|
|
981
|
+
* FLYWHEEL-STATE.md and a long-running orchestrator could drift off-brief.
|
|
982
|
+
*/
|
|
983
|
+
function buildFlywheelResumePrompt(configSection, briefContent) {
|
|
984
|
+
return `FLYWHEEL RESUME: You were paused by the operator. Resume the tick loop from your prior state. Check \`docs/FLYWHEEL-STATE.md\` and the latest status snapshot for context.${configSection}${briefContent ? `\n\n--- Standing brief (re-read it — it governs pickup, unblocking, and never-block) ---\n\n${briefContent}` : ""}`;
|
|
985
|
+
}
|
|
986
|
+
function withOrderBookBriefOverlay(briefContent, overlayPath, overlayContent) {
|
|
987
|
+
if (!overlayContent) return briefContent;
|
|
988
|
+
const standard = briefContent?.trimEnd() ?? "";
|
|
989
|
+
const overlay = `--- Order-book brief overlay${overlayPath ? `: ${overlayPath}` : ""} ---\n\n${overlayContent.trim()}`;
|
|
990
|
+
return standard ? `${standard}\n\n${overlay}` : overlay;
|
|
991
|
+
}
|
|
992
|
+
async function spawnFlywheelAgent(runId, options = {}) {
|
|
993
|
+
const workspace = options.workspace ?? process.cwd();
|
|
994
|
+
const briefPath = options.briefPath ?? join(workspace, "docs", "flywheel-brief.md");
|
|
995
|
+
let briefOverlayPath = options.briefOverlayPath;
|
|
996
|
+
let briefOverlayContent = options.briefOverlayContent;
|
|
997
|
+
if (briefOverlayPath && briefOverlayContent === void 0) {
|
|
998
|
+
const resolvedOverlay = await requireFlywheelBrief(workspace, briefOverlayPath);
|
|
999
|
+
briefOverlayPath = resolvedOverlay.displayPath;
|
|
1000
|
+
briefOverlayContent = await readFile(resolvedOverlay.absolutePath, "utf8");
|
|
1001
|
+
}
|
|
1002
|
+
const briefContent = withOrderBookBriefOverlay(await readFile(briefPath, "utf8").catch(() => void 0), briefOverlayPath, briefOverlayContent);
|
|
1003
|
+
return spawnRun(runId, "flywheel", {
|
|
1004
|
+
agentId: FLYWHEEL_ORCHESTRATOR_AGENT_ID,
|
|
1005
|
+
workspace,
|
|
1006
|
+
prompt: options.resumeSessionId ? buildFlywheelResumePrompt(await flywheelRunConfigurationSection(options, runId), briefContent) : options.prompt ?? await defaultFlywheelPrompt(runId, {
|
|
1007
|
+
...options,
|
|
1008
|
+
briefPath
|
|
1009
|
+
}, briefContent),
|
|
1010
|
+
model: options.model,
|
|
1011
|
+
harness: options.harness,
|
|
1012
|
+
effort: options.effort,
|
|
1013
|
+
allowHost: true,
|
|
1014
|
+
registerConversation: true,
|
|
1015
|
+
resumeSessionId: options.resumeSessionId,
|
|
1016
|
+
flywheelRunId: runId
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
function withFlywheelAutonomyOptions(options) {
|
|
1020
|
+
return {
|
|
1021
|
+
...options,
|
|
1022
|
+
autoPickupBacklog: options.autoPickupBacklog ?? isFlywheelAutoPickupBacklog(),
|
|
1023
|
+
requireUatBeforeMerge: options.requireUatBeforeMerge ?? isFlywheelRequireUatBeforeMerge()
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
async function spawnFlywheel(options = {}) {
|
|
1027
|
+
if (isFlywheelDevcontainerRuntime(options.env)) throw new Error("Refusing to spawn flywheel-orchestrator inside a workspace devcontainer");
|
|
1028
|
+
const activeRunId = await resolveLiveFlywheelRunId();
|
|
1029
|
+
if (activeRunId) throw new Error(`Flywheel run ${activeRunId} is already active; pause, resume, or report it before starting another run`);
|
|
1030
|
+
const runId = options.runId ? parseRunId(options.runId) : defaultFlywheelRunId();
|
|
1031
|
+
const agent = await spawnFlywheelAgent(runId, withFlywheelAutonomyOptions(options));
|
|
1032
|
+
setFlywheelActiveRunId(runId);
|
|
1033
|
+
setFlywheelGloballyPaused(false);
|
|
1034
|
+
try {
|
|
1035
|
+
const workspace = options.workspace ?? process.cwd();
|
|
1036
|
+
const seqPath = join(workspace, ".pan", "backlog", "sequence.md");
|
|
1037
|
+
if (existsSync(seqPath)) {
|
|
1038
|
+
const parsed = parseSequenceMd(readFileSync(seqPath, "utf-8"));
|
|
1039
|
+
if (parsed.ok) saveRunCohort(runId, computeCohort(parsed.doc.nodes, buildClassifyLookups(workspace), options.maxAgents ?? 5, options.autoPickupBacklog ?? isFlywheelAutoPickupBacklog(), await activeOrderBookIssues(workspace, runId)));
|
|
1040
|
+
}
|
|
1041
|
+
} catch {}
|
|
1042
|
+
return agent;
|
|
1043
|
+
}
|
|
1044
|
+
async function abortSpawnedFlywheel(runId) {
|
|
1045
|
+
try {
|
|
1046
|
+
await Effect.runPromise(stopAgent(FLYWHEEL_ORCHESTRATOR_AGENT_ID));
|
|
1047
|
+
} finally {
|
|
1048
|
+
await abortFlywheelRun(runId);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
async function pauseFlywheel() {
|
|
1052
|
+
const activeRunId = getFlywheelActiveRunId();
|
|
1053
|
+
if (activeRunId) try {
|
|
1054
|
+
const sessionId = (await readFile(join(getAgentDir(FLYWHEEL_ORCHESTRATOR_AGENT_ID), "session.id"), "utf-8")).trim();
|
|
1055
|
+
if (sessionId) await saveResumeSessionId(activeRunId, sessionId);
|
|
1056
|
+
} catch {}
|
|
1057
|
+
setFlywheelGloballyPaused(true);
|
|
1058
|
+
await Effect.runPromise(stopAgent(FLYWHEEL_ORCHESTRATOR_AGENT_ID));
|
|
1059
|
+
return { activeRunId };
|
|
1060
|
+
}
|
|
1061
|
+
async function resumeFlywheel(options = {}) {
|
|
1062
|
+
if (isFlywheelDevcontainerRuntime(options.env)) throw new Error("Refusing to resume flywheel-orchestrator inside a workspace devcontainer");
|
|
1063
|
+
const activeRunId = getFlywheelActiveRunId();
|
|
1064
|
+
if (!activeRunId) throw new Error("No active flywheel run to resume");
|
|
1065
|
+
const runId = parseRunId(activeRunId);
|
|
1066
|
+
const resumeSessionId = options.resumeSessionId ?? await loadResumeSessionId(runId) ?? void 0;
|
|
1067
|
+
const launch = await readFlywheelLaunchMetadata(runId);
|
|
1068
|
+
const agent = await spawnFlywheelAgent(runId, withFlywheelAutonomyOptions({
|
|
1069
|
+
...options,
|
|
1070
|
+
workspace: options.workspace ?? launch?.workspace,
|
|
1071
|
+
briefPath: options.briefPath ?? launch?.briefPath,
|
|
1072
|
+
briefOverlayPath: options.briefOverlayPath ?? launch?.briefOverlayPath,
|
|
1073
|
+
resumeSessionId
|
|
1074
|
+
}));
|
|
1075
|
+
setFlywheelGloballyPaused(false);
|
|
1076
|
+
return {
|
|
1077
|
+
activeRunId,
|
|
1078
|
+
agent
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
//#endregion
|
|
1082
|
+
export { advanceQueue as A, getBook as B, readCurrentLatestFlywheelStatus as C, writeFlywheelLaunchMetadata as D, subscribeLatestFlywheelStatus as E, setItemRequirements as F, liveOrderIssueLookup as H, setSettings as I, setStatus as L, moveItem as M, removeItem as N, writeLatestFlywheelStatus as O, renameBook as P, backlogCandidates as R, nextFlywheelRunId as S, resolveLiveFlywheelRunId as T, listBooks as V, clearFlywheelGate as _, isFlywheelDevcontainerRuntime as a, isFlywheelRunId as b, pauseFlywheel as c, spawnFlywheel as d, spawnFlywheelAgent as f, abortFlywheelRun as g, resolvePrimaryWorktreeRoot as h, buildFlywheelResumePrompt as i, createBook as j, addItems as k, resumeFlywheel as l, requireFlywheelBrief as m, abortSpawnedFlywheel as n, isIssueInResolvedPipeline as o, buildClassifyLookups as p, activeOrderBookIssues as r, loadResumeSessionId as s, FLYWHEEL_ORCHESTRATOR_AGENT_ID as t, saveResumeSessionId as u, getFlywheelRunDetail as v, readFlywheelLaunchMetadata as w, listFlywheelRuns as x, getFlywheelRunDir as y, computeBookProgress as z };
|
|
1083
|
+
|
|
1084
|
+
//# sourceMappingURL=flywheel-DHrgCphM.js.map
|