@overdeck/desktop 0.49.1 → 0.49.2
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/package.json +1 -1
- package/server/{acceptance-criteria-Crw-xSJ3.js → acceptance-criteria-CHtDkHEL.js} +2 -2
- package/server/{active-dashboard-bundle-CLwfD-Om.js → active-dashboard-bundle-Byh6sP-q.js} +2 -2
- package/server/{activity-logger-BAVLrkR7.js → activity-logger-BWZhVphb.js} +22 -4
- package/server/activity-logger-Ck8qoluj.js +3 -0
- package/server/{advancing-selfheal-BQiJ7mIH.js → advancing-selfheal-65W4yUQi.js} +6 -6
- package/server/{agent-gc-5E0lgxI1.js → agent-gc-BNUudFnt.js} +10 -10
- package/server/agent-idle-ONBssBN_.js +745 -0
- package/server/{agent-state-Bb3UUxQm.js → agent-state-BJhGIJZe.js} +421 -135
- package/server/agent-state-CM8iVw5B.js +3 -0
- package/server/{agent-state-service-BFbttwRA.js → agent-state-service-CiIuwzIs.js} +5 -5
- package/server/{agent-state-service-keg-x1bT.js → agent-state-service-CzHfoqKQ.js} +1 -1
- package/server/agents-DpCuoF65.js +3 -0
- package/server/{agents-CqQJW75N.js → agents-jXgqpHca.js} +8 -8
- package/server/archive-planning-BW4MKNI7.js +2 -0
- package/server/{archive-planning-BwEhu9Oq.js → archive-planning-CGkU6UeL.js} +20 -135
- package/server/{artifact-index-jobs-C6Lg2rKm.js → artifact-index-jobs-Ck4NRRkY.js} +3 -3
- package/server/{assemble-DyTp2gSB.js → assemble-CyvEBmmq.js} +28 -9
- package/server/{auto-spawn-consent-BDfe4qz0.js → auto-spawn-consent-Dc4mtM-B.js} +8 -7
- package/server/{backlog-auto-trigger-DuOGpdDW.js → backlog-auto-trigger-CCEpr4YT.js} +3 -3
- package/server/{behavior-Bitko0mD.js → behavior-BiCQzqyT.js} +3 -3
- package/server/{bridge-pool-patrol-DiHEd13O.js → bridge-pool-patrol-Ce8rNPym.js} +2 -2
- package/server/{briefing-freshness-CDJcmHHZ.js → briefing-freshness-e_OEYZEe.js} +2 -2
- package/server/{build-info-DTX8JwVP.js → build-info-BRw4QlYO.js} +3 -3
- package/server/build-info-Bj2vQEMh.js +2 -0
- package/server/{checkpoint-manager-CKg1tn34.js → checkpoint-manager-CKr42Usw.js} +1 -1
- package/server/{checkpoint-manager-BlOOdzLF.js → checkpoint-manager-DOwRGFff.js} +26 -9
- package/server/checkpoint-worker.js +1 -1
- package/server/{checkpoints-DRALkfDS.js → checkpoints-Cb6CWT4S.js} +1 -1
- package/server/{checkpoints-BGfl0cHM.js → checkpoints-Cszp_0Np.js} +2 -2
- package/server/{claude-hooks-provision-B_tv3wzE.js → claude-hooks-provision-C5JHpxTS.js} +1 -1
- package/server/{claude-hooks-provision-BlGv6twJ.js → claude-hooks-provision-Cer2eFfj.js} +2 -2
- package/server/{claude-permissions-aHgDcZi5.js → claude-permissions-Bc5kjX1q.js} +2 -2
- package/server/{claude-settings-overlay-CYOvFE3A.js → claude-settings-overlay-C7rWsnya.js} +1 -1
- package/server/{claude-settings-overlay-T2LWLteV.js → claude-settings-overlay-m-VVZej1.js} +2 -2
- package/server/cli-BRuzUT_I.js +2 -0
- package/server/{cli-Do8UpLoI.js → cli-BmczsfD6.js} +11 -11
- package/server/{cliproxy-CX6jHRDP.js → cliproxy-BpYpkogS.js} +52 -17
- package/server/cliproxy-_yUU0HRY.js +3 -0
- package/server/{close-issue-J0_eWYT1.js → close-issue-CxvRD0UI.js} +5 -5
- package/server/{closed-issue-reaper-bFppuk6F.js → closed-issue-reaper-nMLRi5de.js} +13 -12
- package/server/{closed-issue-reaper-Cl2h9AfT.js → closed-issue-reaper-omOcUDBl.js} +1 -1
- package/server/{codex-icapA4om.js → codex-DZhFfcWu.js} +1 -1
- package/server/{codex-OOea7lXs.js → codex-KaSKE-R3.js} +16 -10
- package/server/{compaction-continuation-D5voR1ME.js → compaction-continuation-Bjfi70G-.js} +3 -3
- package/server/{config-Cjleh3_U.js → config-CH1AqqIQ.js} +1 -1
- package/server/{config-DC75T4Mr.js → config-Doq_tF-f.js} +2 -2
- package/server/{config-tucKBCCc.js → config-ZGjvuR6J.js} +2 -2
- package/server/{config-yaml-BwOEANMr.js → config-yaml-D9nfpEx1.js} +33 -8
- package/server/{conflict-gate-DlpWCMd7.js → conflict-gate-D4XCtrm2.js} +1 -1
- package/server/{conflict-gate-D2L1kZeL.js → conflict-gate-DcUTY4Gb.js} +4 -4
- package/server/{context-layers-CSPGc2Hb.js → context-layers-BBTKP-Y4.js} +3 -3
- package/server/context-layers-BGj2Y9l3.js +4 -0
- package/server/{continue-U3Af0jUq.js → continue-i7Revg8D.js} +3 -3
- package/server/{control-settings-5k6ixdFW.js → control-settings-BMoDHY1G.js} +6 -6
- package/server/{conversation-compaction-CSpVpxN6.js → conversation-compaction-D4VOmF7p.js} +8 -8
- package/server/{conversation-compaction-Ciit9O6k.js → conversation-compaction-DzIjYMfg.js} +1 -1
- package/server/{conversation-service-BxrB5lUT.js → conversation-service-CzZUJFEx.js} +1 -1
- package/server/{conversation-service-BteE4ELK.js → conversation-service-tQCIbBVZ.js} +6 -6
- package/server/{conversations-DhwxsOru.js → conversations-C75EFFjf.js} +12 -7
- package/server/conversations-bFDaD5YN.js +3 -0
- package/server/{cost-Bnf9NUC5.js → cost-BCWEkvro.js} +3 -3
- package/server/{cost-CQKI6OCu.js → cost-CiIwxH1D.js} +2 -2
- package/server/{cost-sync-B-ADeXKY.js → cost-sync-DOkdE_yB.js} +3 -3
- package/server/{dag-DI9LaK_q.js → dag-BPc74hwj.js} +6 -19
- package/server/{dangerous-git-ops-akhA3iyF.js → dangerous-git-ops-By9iguTP.js} +1 -1
- package/server/dashboard-db-worker.js +7 -7
- package/server/{deacon-TPYssFYt.js → deacon-B24dNzr8.js} +1774 -171
- package/server/deacon-CzSZMtb-.js +4 -0
- package/server/{deacon-canonical-state-DFYymrDw.js → deacon-canonical-state-CkvxRPck.js} +3 -3
- package/server/{deacon-event-client-Te5v10-b.js → deacon-event-client-BYIQBu0T.js} +1 -1
- package/server/{deacon-event-client-BKOL4AcL.js → deacon-event-client-Do6Z2gKl.js} +2 -2
- package/server/{deacon-nudge-log-BtlTWgWu.js → deacon-nudge-log-CzM9JA9t.js} +1 -1
- package/server/{deacon-pending-lifecycle-DVHjOLje.js → deacon-pending-lifecycle-BObEdT-L.js} +3 -3
- package/server/deacon.js +11 -11
- package/server/dead-end-trip-6V_CwveT.js +2 -0
- package/server/{dead-end-trip-P0IF1O0E.js → dead-end-trip-CnVmjnya.js} +3 -3
- package/server/delivery-gyce0vQ7.js +3 -0
- package/server/{deploy-patrol-BXjlvoV3.js → deploy-patrol-CEwwEYDM.js} +8 -8
- package/server/deploy-queue-CjLErpgw.js +2 -0
- package/server/{deploy-queue-o7shYg1j.js → deploy-queue-r96ojDI3.js} +2 -2
- package/server/{deploy-window-CNT6fOff.js → deploy-window-D_3hhfxt.js} +4 -4
- package/server/{deploy-window-BMk5_oX6.js → deploy-window-DeJK8mTK.js} +1 -1
- package/server/{dev-supervisor-SR1CLOAK.js → dev-supervisor-DZWwpENk.js} +2 -2
- package/server/{devcontainer-renderer-enMmNDFa.js → devcontainer-renderer-d-9dCV_2.js} +3 -3
- package/server/{dist-CPTO_zMR.js → dist-DsLAsFSh.js} +1 -1
- package/server/dist-fCA6y2bH.js +3 -0
- package/server/{docker-B7qUZptz.js → docker-B2mUBzGk.js} +3 -3
- package/server/{docker-KR3zSFKY.js → docker-CRPHBylA.js} +1 -1
- package/server/{dod-gK835UMf.js → dod-DWtilRLx.js} +1 -1
- package/server/{done-preflight-CksRvh49.js → done-preflight-BDVdWed5.js} +1 -1
- package/server/{done-preflight-Bk8aExAL.js → done-preflight-D4ukUf-S.js} +5 -5
- package/server/{driver-CmsY2RLl.js → driver-BtcFz-qu.js} +14 -3
- package/server/{durable-review-pipeline-DgYHucN2.js → durable-review-pipeline-B3OS2_He.js} +11 -2
- package/server/durable-review-pipeline-CaCbEJSt.js +3 -0
- package/server/{ensure-devcontainer-ojerEqQg.js → ensure-devcontainer-BUeBRjmm.js} +4 -4
- package/server/{ensure-devcontainer-BQ4YizSH.js → ensure-devcontainer-rGSh0JHK.js} +1 -1
- package/server/{esm-shims-1htPASWs.js → esm-shims-oAaKIa-a.js} +1 -1
- package/server/{event-store-D7qcJXuZ.js → event-store-BCSgMubf.js} +5 -5
- package/server/{event-store-BgEjNsvk.js → event-store-Db4w4YIY.js} +1 -1
- package/server/{event-store-provider-BKvk2rDj.js → event-store-provider-DlSQND3V.js} +1 -1
- package/server/{events-BIKGJZOT.js → events-B6zioJA_.js} +3 -3
- package/server/{dist-src-BM9eudEd.js → factory-BrdqXBeK.js} +714 -2
- package/server/{feature-registry-storage-ewNSIVsQ.js → feature-registry-storage-zh3ZXW6f.js} +3 -3
- package/server/{features-CGopJXdU.js → features-BGntOWdy.js} +2 -2
- package/server/{feedback-stuck-retirement-B8-fX7NA.js → feedback-stuck-retirement-DpA5u1kt.js} +2 -2
- package/server/{feedback-target-Ci6N5p4G.js → feedback-target-D7GxdUYG.js} +10 -10
- package/server/{feedback-target-CkyLYohm.js → feedback-target-Ddqk93NF.js} +1 -1
- package/server/{feedback-writer-D4ULO4qG.js → feedback-writer-C1gCLFmp.js} +5 -5
- package/server/{feedback-writer-DfuVjvH0.js → feedback-writer-CH2846DP.js} +1 -1
- package/server/{flywheel-C7yek87O.js → flywheel-CjW2L7BS.js} +79 -29
- package/server/{flywheel-DXI_WITn.js → flywheel-HJt7QdWP.js} +1 -1
- package/server/{flywheel-merge-order-C5m5bYBc.js → flywheel-merge-order-4fNRN2F2.js} +1 -1
- package/server/{flywheel-merge-order-CXfU4M6l.js → flywheel-merge-order-D_yyAlND.js} +7 -7
- package/server/{flywheel-BwYPDlhK.js → flywheel-v1cCOBx-.js} +14 -14
- package/server/{forge-DYX9sKzG.js → forge-RVTCK_5X.js} +2 -2
- package/server/forge-bvak6vXJ.js +2 -0
- package/server/{from-DAdMxC29.js → from-BnLgKYvw.js} +1 -1
- package/server/{fs-lock-cw25uO_E.js → fs-lock-C7J-ugIf.js} +2 -2
- package/server/{fts-operations-D_rzLxX5.js → fts-operations-CU0B5WUx.js} +3 -3
- package/server/git-info-BSrI9owC.js +148 -0
- package/server/{git-utils-Y0zG25ez.js → git-utils-CsKV0Dis.js} +1 -1
- package/server/{git-utils-DV4B9TRl.js → git-utils-DW8-jIUJ.js} +4 -4
- package/server/{github-app-4UX8VIr8.js → github-app-7ywLhuZO.js} +2 -2
- package/server/{github-app-C33lDk9Z.js → github-app-Oi5hdn1n.js} +1 -1
- package/server/{github-client-nPj5ckZO.js → github-client-Cy0fSj1a.js} +1 -1
- package/server/{github-client-DkJVR908.js → github-client-DAbuXLuk.js} +3 -3
- package/server/{github-pr-lookup-C9Vokb0W.js → github-pr-lookup-Dhh3ec1V.js} +2 -2
- package/server/{harness-binary-BgXYDo2Z.js → harness-binary-tMmgJfC1.js} +2 -2
- package/server/{harness-resolve-1NZcVx7n.js → harness-resolve-DusNpVEe.js} +4 -4
- package/server/{health-events-jMfxrElt.js → health-events-BYDZ9n2i.js} +1 -1
- package/server/{health-events-DlX_FVAB.js → health-events-DNw9Mzw9.js} +2 -2
- package/server/{hooks-W3REcb3h.js → hooks-B4wj32l2.js} +1 -1
- package/server/{hooks-NQ19vvlB.js → hooks-BjBjxqNh.js} +2 -2
- package/server/{hume-BFkid2PO.js → hume-BP9YbxaE.js} +2 -2
- package/server/hume-TYTb6fDU.js +3 -0
- package/server/{identity-Bwh6VGeL.js → identity-C23uY8dZ.js} +3 -3
- package/server/{idle-stack-reaper-BeU_KxWx.js → idle-stack-reaper-BSxb1bln.js} +4 -4
- package/server/{idle-stack-reaper-CHtCNpvd.js → idle-stack-reaper-Bw0rU9GC.js} +1 -1
- package/server/{in-flight-guard-pcwXKLGz.js → in-flight-guard-DuxabAHT.js} +1 -1
- package/server/{infra-KTp9KV9u.js → infra-CHIvOb9q.js} +30 -7
- package/server/{injection-67YyU6SC.js → injection-BYUt8ml1.js} +8 -8
- package/server/{injection-BxbZKPDh.js → injection-BviePcm3.js} +1 -1
- package/server/inspect-agent-JFEaYTRa.js +2 -0
- package/server/{inspect-agent-VHSihlb7.js → inspect-agent-sV_19NxZ.js} +12 -12
- package/server/{internal-token-B-z-sGBH.js → internal-token-BOtIRJU7.js} +1 -1
- package/server/{internal-token-zY75jrRJ.js → internal-token-DoVzb4b7.js} +2 -2
- package/server/{io-DfvO2xyh.js → io-DKrWd3gX.js} +5 -5
- package/server/{issue-closed-dvEWKO7Q.js → issue-closed-6RQsAjDX.js} +7 -7
- package/server/{issue-lifecycle-3N8XF7lP.js → issue-lifecycle-D9nLErvS.js} +206 -29
- package/server/{issue-lifecycle-BI6jAfp8.js → issue-lifecycle-DOPVv7xz.js} +1 -1
- package/server/{issue-service-singleton-BN_2MXp4.js → issue-service-singleton-B2xy-IS9.js} +124 -11
- package/server/{issue-service-singleton-CL6hESX2.js → issue-service-singleton-CvgNAWqK.js} +1 -1
- package/server/issue-title-fallback-DTSCQVu0.js +120 -0
- package/server/{issues-CkvUoAnq.js → issues-e_Ck1Ln2.js} +2 -2
- package/server/{jsonl-parser-CrEb2PWr.js → jsonl-parser-DqxVCZ1k.js} +3 -3
- package/server/jsonl-resolver-07OS3qOr.js +324 -0
- package/server/{kimi-code-C26gmGxM.js → kimi-code-BjtAHIz1.js} +14 -12
- package/server/{kimi-code-DoMoGqIu.js → kimi-code-vtXl2DR5.js} +1 -1
- package/server/{kimi-parser-Dmhy3UlR.js → kimi-parser-B1lgy2Vt.js} +2 -2
- package/server/{label-cleanup-X2lWfghH.js → label-cleanup-D2zUMasf.js} +3 -3
- package/server/{lifecycle-Bg44O-9x.js → lifecycle-0NOEzomy.js} +2 -2
- package/server/{lifecycle-B0rL38D3.js → lifecycle-DaCovZwn.js} +6 -6
- package/server/{lifecycle-io-CCBpRv5m.js → lifecycle-io-6WL46dsB.js} +5 -5
- package/server/{lifecycle-io-DQDU0apV.js → lifecycle-io-BBMTaP7S.js} +1 -1
- package/server/{linear-client-B0cIkTs4.js → linear-client-7fQThJTc.js} +3 -3
- package/server/{linear-client-6V2E-DSa.js → linear-client-BTGApNLo.js} +1 -1
- package/server/{linear-mcp-auth-LCo9AWUd.js → linear-mcp-auth-B8pYRsiU.js} +1 -1
- package/server/{linear-mcp-auth-Dsu9Io9r.js → linear-mcp-auth-Nk5s3L9M.js} +5 -5
- package/server/memory-fts-worker.js +1 -1
- package/server/{memory-governor-A4O6vI9d.js → memory-governor-Cqvbusr4.js} +14 -14
- package/server/{memory-pressure-patrol-BWlWywmA.js → memory-pressure-patrol-DPb8foam.js} +5 -5
- package/server/{merge-BGWcBxku.js → merge-BB_bjJ9a.js} +1 -1
- package/server/{merge-vz80RvSE.js → merge-FZ78_cEz.js} +3 -3
- package/server/{merge-agent-GWhmnxFk.js → merge-agent-B3jRrlS1.js} +37 -33
- package/server/{merge-agent-BRSz26Oh.js → merge-agent-BuoC_PRZ.js} +2 -2
- package/server/{merge-batch-CFMEtTxv.js → merge-batch-B8bVjFWS.js} +1 -1
- package/server/{merge-completeness-CP347umA.js → merge-completeness-DQt7Qa7i.js} +4 -4
- package/server/{merge-completeness-7ajHAIR-.js → merge-completeness-DkdabVch.js} +1 -1
- package/server/merge-ops-DabXHp2i.js +2 -0
- package/server/merge-rebase-CXq0qsgR.js +2 -0
- package/server/{merge-rebase-C5htyEEH.js → merge-rebase-qq40s91P.js} +2 -2
- package/server/{merge-set-DsGW8fmc.js → merge-set-BTNYvxM3.js} +1 -1
- package/server/{merge-set-eiE2nC6C.js → merge-set-BmRRflt0.js} +4 -4
- package/server/{merge-sync-CM2-myXu.js → merge-sync-0m2Q087q.js} +1 -1
- package/server/{merge-sync-Ct0-cxMX.js → merge-sync-CJpOa0cZ.js} +2 -2
- package/server/{merge-train-deps-LXHGK61n.js → merge-train-deps-CH5Mr3vN.js} +4 -4
- package/server/{merge-train-BOu3fN7O.js → merge-train-iwKqjtCF.js} +4 -4
- package/server/{merge-verification-BRp08IzI.js → merge-verification-JJc5GC2t.js} +6 -6
- package/server/{merged-docker-cleanup-worker-CwldDjHV.js → merged-docker-cleanup-worker-BOqWA373.js} +4 -4
- package/server/{merged-docker-cleanup-worker-C8eGn78a.js → merged-docker-cleanup-worker-CrhTdxZV.js} +1 -1
- package/server/messaging-nuwStbdQ.js +3 -0
- package/server/{multipart-parser-OAuW7824.js → multipart-parser-DxuW_rZV.js} +2 -2
- package/server/{no-resume-mode-MWI6pnrK.js → no-resume-mode-xuYLFhLw.js} +1 -1
- package/server/node_modules/.package-lock.json +11 -11
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/AGENTS.md +14 -0
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/CLAUDE.md +14 -0
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/README.md +10 -3
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/ai-docs/src/40_sql/10_basics.ts +175 -0
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/ai-docs/src/40_sql/index.md +6 -0
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/ai-docs/src/51_http-server/10_basics.ts +3 -1
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/ai-docs/src/51_http-server/20_testing.ts +100 -0
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/ai-docs/src/51_http-server/fixtures/api/Users.ts +30 -14
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/ai-docs/src/51_http-server/fixtures/domain/User.ts +15 -6
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/ai-docs/src/51_http-server/fixtures/server/Authorization.ts +11 -10
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/ai-docs/src/51_http-server/fixtures/server/Users/http.ts +41 -32
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/ai-docs/src/51_http-server/fixtures/server/Users.ts +141 -20
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/ai-docs/src/70_cli/10_basics.ts +27 -3
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/dist/NodeChildProcessSpawner.d.ts.map +1 -1
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/dist/NodeChildProcessSpawner.js +10 -9
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/dist/NodeChildProcessSpawner.js.map +1 -1
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/dist/NodeSocketServer.js +4 -4
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/dist/NodeSocketServer.js.map +1 -1
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/dist/NodeStream.d.ts.map +1 -1
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/dist/NodeStream.js +10 -4
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/dist/NodeStream.js.map +1 -1
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/dist/internal/nodeChildProcessSpawner.d.ts +4 -0
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/dist/internal/nodeChildProcessSpawner.d.ts.map +1 -0
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/dist/internal/nodeChildProcessSpawner.js +10 -0
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/dist/internal/nodeChildProcessSpawner.js.map +1 -0
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/package.json +4 -4
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/src/NodeChildProcessSpawner.ts +16 -11
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/src/NodeSocketServer.ts +4 -4
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/src/NodeStream.ts +10 -4
- package/server/node_modules/@effect/platform-node/node_modules/@effect/platform-node-shared/src/internal/nodeChildProcessSpawner.ts +16 -0
- package/server/node_modules/ansi-regex/index.js +3 -2
- package/server/node_modules/ansi-regex/package.json +1 -1
- package/server/node_modules/gaxios/build/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/server/node_modules/gaxios/build/esm/tsconfig.tsbuildinfo +1 -1
- package/server/node_modules/gaxios/package.json +3 -3
- package/server/{orphan-proposed-reconciler-cV3URUqk.js → orphan-proposed-reconciler-Dmk0kO_p.js} +24 -23
- package/server/{orphan-proposed-reconciler-BAE5AZ_e.js → orphan-proposed-reconciler-gf4TxfZb.js} +2 -2
- package/server/package-lock.json +12 -12
- package/server/package.json +1 -1
- package/server/{pan-dir-DDd91kMb.js → pan-dir-BMda7tQj.js} +5 -5
- package/server/{deacon-merge-BOGFEV8_.js → parked-residue-BiHb8cD7.js} +150 -35
- package/server/{paths-CRLnDgcI.js → paths-DEe9j-kw.js} +2 -2
- package/server/{pending-lifecycle-claim-CYgMiDAP.js → pending-lifecycle-claim-J5uIIJPu.js} +2 -2
- package/server/{pickup-BjJsSX12.js → pickup-D1UbSDPd.js} +1 -1
- package/server/{pipeline-agent-reader-BLc43dVJ.js → pipeline-agent-reader-D-6okryb.js} +5 -5
- package/server/{pipeline-membership-5qlRz5Qg.js → pipeline-membership-CCbgBzNC.js} +1 -1
- package/server/{pipeline-membership-BHZp_ShY.js → pipeline-membership-PGX3Pe8V.js} +1 -1
- package/server/{pipeline-membership-gather-CGFY1v-y.js → pipeline-membership-gather-BK4dzh0X.js} +17 -12
- package/server/{pipeline-membership-gather-WhRc1yke.js → pipeline-membership-gather-BRhU7XPx.js} +1 -1
- package/server/{pipeline-notifier-BmsLWM_2.js → pipeline-notifier-BNAV9kpa.js} +2 -2
- package/server/{pipeline-notifier-C4fZ2ito.js → pipeline-notifier-DvHLaXKb.js} +1 -1
- package/server/{post-merge-lifecycle-worker-KK1RTKRE.js → post-merge-lifecycle-worker-DAWgzXQA.js} +3 -3
- package/server/{post-merge-state-_Lj3LDLh.js → post-merge-state-DC2_0t12.js} +4 -4
- package/server/prd-locations-BbAvqQmT.js +124 -0
- package/server/{project-pipelines-CCkebHOu.js → project-pipelines-By_5WcT4.js} +2 -2
- package/server/{project-repos-B4MK48j4.js → project-repos--QeCg7yQ.js} +29 -3
- package/server/project-repos-3bI6t4fO.js +3 -0
- package/server/project-resource-refresh-queue-BaZC79S3.js +2 -0
- package/server/project-resource-refresh-queue-x-K9mvpP.js +865 -0
- package/server/{projects-DvPEAP18.js → projects-LftaVK99.js} +1 -1
- package/server/{prompts-BiS5I2tZ.js → prompts-BotGu-6W.js} +2 -2
- package/server/{providers-DrfK0Ip8.js → providers-BL1I66sM.js} +7 -7
- package/server/providers-Dqgz3MY5.js +3 -0
- package/server/public/assets/{chunk-BO2N2NFS-GtVe_o56.js → chunk-BO2N2NFS-D5Fz5jND.js} +4 -4
- package/server/public/assets/{diagram-OG6HWLK6-BvhcYYHc.js → diagram-OG6HWLK6-Eux0gWFq.js} +1 -1
- package/server/public/assets/{dist-D0ryefak.js → dist-C3zShwhA.js} +1 -1
- package/server/public/assets/{dist-CSmUXazd.js → dist-D-JX_u7m.js} +3 -3
- package/server/public/assets/{ganttDiagram-6RSMTGT7-BXA51DzM.js → ganttDiagram-6RSMTGT7-UH3aweia.js} +1 -1
- package/server/public/assets/{highlighted-body-OFNGDK62-mlV4EpWt.js → highlighted-body-OFNGDK62-ccIQ-WXS.js} +1 -1
- package/server/public/assets/index-2wV4ipy_.css +1 -0
- package/server/public/assets/index-BTVcdPve.js +860 -0
- package/server/public/assets/{linear-BPE7oswm.js → linear-Cnnu4YQy.js} +1 -1
- package/server/public/assets/mermaid-GHXKKRXX-Bjto8iJh.js +1 -0
- package/server/public/assets/{pieDiagram-4H26LBE5-BguGGV3S.js → pieDiagram-4H26LBE5-BD8-Tye6.js} +1 -1
- package/server/public/assets/{prod-aKDYUrXQ.js → prod-Ca6aKMts.js} +4 -4
- package/server/public/assets/{quadrantDiagram-W4KKPZXB-Bx3i4B6H.js → quadrantDiagram-W4KKPZXB-BmSrVyFD.js} +1 -1
- package/server/public/assets/{sankeyDiagram-5OEKKPKP-B5vuLjA4.js → sankeyDiagram-5OEKKPKP-CEGy8Das.js} +1 -1
- package/server/public/assets/{xychartDiagram-2RQKCTM6-HPlpkqsN.js → xychartDiagram-2RQKCTM6-VnQ8iY8s.js} +1 -1
- package/server/public/index.html +2 -2
- package/server/{pull-requests-BcHIsFJv.js → pull-requests-Cp_FDT8-.js} +3 -3
- package/server/pull-requests-jVGcVq5G.js +2 -0
- package/server/rally-CxhVgIQ6.js +3 -0
- package/server/{rally-api-ShZ4kvn0.js → rally-api-CqAIJ8RX.js} +2 -2
- package/server/{rally-api-u_vkr5c_.js → rally-api-Ij1Zt0NI.js} +1 -1
- package/server/{rally-B1OMtzch.js → rally-bQpBzUNf.js} +3 -3
- package/server/{rally-client-DgZvUZqX.js → rally-client-CN5vh4Ac.js} +1 -1
- package/server/{rally-client-D6Feq1Ka.js → rally-client-qKbCNRVW.js} +5 -5
- package/server/{reap-terminal-sessions-CRfiXLY0.js → reap-terminal-sessions-BaDSNR-k.js} +2 -2
- package/server/{reap-terminal-sessions-D7f3zSE9.js → reap-terminal-sessions-DgEIKMhH.js} +2 -2
- package/server/{rebuild-stack-DKxOnkpE.js → rebuild-stack-BNOMVh7C.js} +7 -7
- package/server/{rebuild-stack-CCwKHZb6.js → rebuild-stack-DrMyA_mA.js} +2 -2
- package/server/{reconstruct-cache-BP8UIKt7.js → reconstruct-cache-mxIYCnqs.js} +7 -7
- package/server/{record-By-wp6pg.js → record-Ug18Cp4V.js} +1 -1
- package/server/{record-update-ByGtIlUK.js → record-update-WyhsREoH.js} +1 -1
- package/server/{record-update-D23jPU6b.js → record-update-dBGxL9OC.js} +8 -6
- package/server/{recovery-trip-C4dTl8DO.js → recovery-trip-BKLrL6NX.js} +43 -10
- package/server/{recovery-trip-qloOYOFz.js → recovery-trip-wFAixKsg.js} +1 -1
- package/server/{release-engine-Crc0Tjx-.js → release-engine-D9gaCBn9.js} +5 -5
- package/server/{release-set-DDXwbIK7.js → release-set-DPHHzEGx.js} +3 -3
- package/server/{remote-CBpLi4Yy.js → remote-BU6ONLpv.js} +3 -3
- package/server/{remote-oEMfXlwT.js → remote-BUEeUic6.js} +3 -3
- package/server/{remote-agents-DQVGZVQ9.js → remote-agents-BVtASiOF.js} +1 -1
- package/server/{remote-agents-Bgtc2nOO.js → remote-agents-CSaTLi3u.js} +6 -6
- package/server/{remote-completion-DxUJ5HKR.js → remote-completion-CBV3b9TI.js} +13 -13
- package/server/{resolver-C8rooxoz.js → resolver-DvSMXty7.js} +31 -8
- package/server/{resolver-CuAULbLs.js → resolver-gxv97jsQ.js} +2 -2
- package/server/resume-DvHuF-bm.js +3 -0
- package/server/review-agent-DgkUPRx0.js +3 -0
- package/server/{review-agent-7C2w5CzU.js → review-agent-esZ0QXdY.js} +43 -55
- package/server/{review-artifacts-BJeEfm2r.js → review-artifacts-DGGEobLp.js} +6 -6
- package/server/{review-convoy-liveness-DfF9eXjI.js → review-convoy-liveness-DmO8dIBH.js} +20 -11
- package/server/{review-run-recovery-7BKt2Es0.js → review-run-recovery-Ct0q7FkE.js} +3 -3
- package/server/{review-run-recovery-ZjuDTNhZ.js → review-run-recovery-DHiiw7d8.js} +1 -1
- package/server/{review-status-CUnjjOUu.js → review-status-C_kQeeX0.js} +1 -1
- package/server/{review-status-emit-Cg4tOb8U.js → review-status-emit-BZz1Y_22.js} +3 -3
- package/server/{review-status-enrichment-D-rQshu6.js → review-status-enrichment-BWH0XW_m.js} +3 -3
- package/server/{review-status-source-DSn0glLa.js → review-status-source-D2PCWS6E.js} +1 -1
- package/server/{review-status-source-Ct912fv_.js → review-status-source-Jq0BXmn3.js} +1 -1
- package/server/{review-status-sync-B7V_Gg51.js → review-status-sync-2C9lk85r.js} +2 -2
- package/server/{review-status-sync-YthpOH_q.js → review-status-sync-Bq00Loey.js} +1 -1
- package/server/{review-status-DBnR5t4z.js → review-status-yOGD2MuK.js} +86 -27
- package/server/{review-verdict-feedback-DqXTuJk8.js → review-verdict-feedback-Brjaxoda.js} +1 -1
- package/server/{review-verdict-feedback-C-dBon_-.js → review-verdict-feedback-CE_tr2iQ.js} +8 -8
- package/server/{review-verdict-writer-jldSh3Bm.js → review-verdict-writer-CD-4kDVx.js} +1 -1
- package/server/{review-verdict-writer-CKaJWp75.js → review-verdict-writer-Ca2V7QSY.js} +7 -7
- package/server/{rollup-DZwH4CLs.js → rollup--g9ik1XQ.js} +1 -1
- package/server/{rollup-QCwYcBdP.js → rollup-B6g9Nr6L.js} +8 -8
- package/server/{scanner-2aHeLC3W.js → scanner-YxIv-fG1.js} +8 -8
- package/server/{search-DwHYaj66.js → search-DqMN4__-.js} +2 -2
- package/server/{secret-redaction-B5G1xZ3k.js → secret-redaction-DLZ-VqHq.js} +1 -1
- package/server/{sequence-io-POKWdusG.js → sequence-io--b3_PsWq.js} +4 -4
- package/server/{sequencer-agent-DS00KHVm.js → sequencer-agent-CqouKVZG.js} +6 -6
- package/server/server.js +1115 -1269
- package/server/{service-BLb_CyPL.js → service-CEp-3iow.js} +64 -63
- package/server/{session-fork-DcTzVwex.js → session-fork-GtoeCh2H.js} +2 -2
- package/server/{settings-Clw8UVNs.js → settings-CLQcdkoD.js} +2 -2
- package/server/{settings-B9-H2K_E.js → settings-Ch2pCVtt.js} +3 -3
- package/server/settings-DhtPDmmI.js +2 -0
- package/server/{shadow-engineering-C6oO_PiR.js → shadow-engineering-BxiFrIKv.js} +3 -3
- package/server/{shadow-state-CEGrrECe.js → shadow-state-CSA1vtxr.js} +2 -2
- package/server/{shadow-state-e7250VUx.js → shadow-state-nGhCNXT2.js} +1 -1
- package/server/{shadow-utils-xFk2Wusb.js → shadow-utils-7q5QDmMj.js} +2 -2
- package/server/{ship-log-7BnQHuKU.js → ship-log-ie0A_SqV.js} +1 -1
- package/server/{ship-record-CwUX6Afo.js → ship-record-BM3oVvnL.js} +7 -7
- package/server/{ship-record-BgUPnpT0.js → ship-record-DHWH1hLz.js} +1 -1
- package/server/{ship-status-Ce3_3edi.js → ship-status-BEkddXMT.js} +2 -2
- package/server/{smart-compaction-BmfIi3oL.js → smart-compaction-BkzjVpWL.js} +1 -1
- package/server/{smart-compaction-Dy0gw1VH.js → smart-compaction-D0TE1lBD.js} +7 -7
- package/server/{spawn-planning-session-nH3b4Drz.js → spawn-planning-session-CkGweSCC.js} +19 -19
- package/server/{spawn-planning-session-CfhxRJoY.js → spawn-planning-session-s406rw1N.js} +2 -2
- package/server/{specialist-completion-CC1XJbTx.js → specialist-completion-CTAjwH5j.js} +1 -1
- package/server/{specialist-context-cHwpcXSh.js → specialist-context-CKasRfYu.js} +5 -5
- package/server/{specialist-handoff-logger-GqnGSYoJ.js → specialist-handoff-logger-DrjrOmmn.js} +2 -2
- package/server/{specialist-logs-5OTnrV91.js → specialist-logs-Du6quZ0_.js} +3 -3
- package/server/{specialists-xXcJHAyg.js → specialists-B-4zyE4Y.js} +1 -1
- package/server/{specialists-8LcQi9Np.js → specialists-CuDWiNdB.js} +21 -272
- package/server/{specialists-DocAK-7A.js → specialists-b0dMMjWq.js} +148 -685
- package/server/specialists-registry-1y6Rt_F1.js +3 -0
- package/server/specialists-registry-BYjs04iK.js +277 -0
- package/server/specialists-x-kPMhrL.js +4 -0
- package/server/specs-IYsFlM-L.js +3 -0
- package/server/{src-DudQNYVw.js → src-CY2Wl8IX.js} +337 -6
- package/server/{src-C2TAxwki.js → src-CZsPs61V.js} +6 -6
- package/server/{src-DUVvEA8Q.js → src-DeQwpuh6.js} +3 -3
- package/server/{stack-health-DKT4ySnA.js → stack-health-BQFSJc0l.js} +3 -3
- package/server/{stack-health-CbyiYS9H.js → stack-health-ypIKtkbE.js} +1 -1
- package/server/{stale-task-claims-PzFee70R.js → stale-task-claims-C2e1zdMT.js} +5 -5
- package/server/{staleness-BjOAq9-d.js → staleness-CRDK6LFG.js} +1 -1
- package/server/{staleness-D8UTEXV5.js → staleness-Fzx_3_Hr.js} +1 -1
- package/server/{stall-sweeper-DOyIfeLR.js → stall-sweeper-QNk4h9m6.js} +8 -8
- package/server/{state-home-DhAZlk5k.js → state-home-DdG8erBI.js} +2 -2
- package/server/{state-mapping-BVSs84bO.js → state-mapping-D2yC81Vy.js} +1 -1
- package/server/{state-migrate-CJKEwpB0.js → state-migrate-C6H3jfHE.js} +6 -6
- package/server/{state-mirror-Do7pag7W.js → state-mirror-DF4Lfxm0.js} +3 -3
- package/server/stuck-remediation-BDa_tXgB.js +774 -0
- package/server/stuck-remediation-BVNd9txd.js +2 -0
- package/server/{substrate-bug-weights-service-BTNjHBnl.js → substrate-bug-weights-service-BbbCZEtJ.js} +3 -3
- package/server/{summary-fork-CZ88rhVx.js → summary-fork-BvcW6bq0.js} +113 -30
- package/server/{summary-fork-CvB7Oyq9.js → summary-fork-DIQmtOuV.js} +2 -2
- package/server/supervisor/{pane-composer-BaLLspSj.js → pane-composer-CoiVkGIt.js} +8 -1
- package/server/supervisor/{paths-D6XUcR_g.js → paths-DTGbtcaP.js} +8 -4
- package/server/supervisor/pty-supervisor.js +3 -3
- package/server/supervisor/{pty-token-DjjHxoCq.js → pty-token-GVqnvU3U.js} +2 -2
- package/server/{supervisor-D4v0WfiQ.js → supervisor-D3jC2c7A.js} +4 -4
- package/server/supervisor-v0cYN2Vp.js +2 -0
- package/server/{swarm-readiness-D8R-LWX5.js → swarm-readiness-CMWAQuWM.js} +3 -3
- package/server/{synthesis-verdict-B6wvLGcA.js → synthesis-verdict-C-pJOPsn.js} +1 -1
- package/server/{synthesis-verdict-N5Z11pg7.js → synthesis-verdict-DVlrLF9A.js} +2 -2
- package/server/{system-prerequisites-BbGZ_WHF.js → system-prerequisites-QKtIYXI2.js} +2 -2
- package/server/{teardown-workspace-CYend-KY.js → teardown-workspace-cJea1OLo.js} +16 -16
- package/server/{test-agent-queue-BUtWhNcc.js → test-agent-queue-C5DI_gWX.js} +6 -6
- package/server/{test-verdict-BWm3hgDj.js → test-verdict-C7kmd2_Y.js} +1 -1
- package/server/{test-verdict-BWtRmdiR.js → test-verdict-DUbDHqeD.js} +2 -2
- package/server/{tldr-daemon-CneJ24eL.js → tldr-daemon-BNQHwblU.js} +1 -1
- package/server/{tldr-daemon-CMVBT4kT.js → tldr-daemon-CO08YlfQ.js} +2 -2
- package/server/tmux-B8zsqD_6.js +4 -0
- package/server/{tmux-BqXzRuaH.js → tmux-DWEc4Ld9.js} +91 -19
- package/server/{token-CO3INKbh.js → token-Bez-qCB2.js} +2 -2
- package/server/{token-util-DyJJyX_B.js → token-util-Crcay9j_.js} +1 -1
- package/server/token-util-DfUS3Lpp.js +3 -0
- package/server/{tracker-config-DVzhaUxO.js → tracker-config-CJzPhSHv.js} +2 -2
- package/server/{tracker-utils-xnVJVrrO.js → tracker-utils-DJAOAuMN.js} +2 -2
- package/server/{traefik-connect-A5k54mCv.js → traefik-connect-D3H5I6b9.js} +1 -1
- package/server/{tunnel-DauSR-rs.js → tunnel-BSdKnW0U.js} +1 -1
- package/server/{tunnel-CwOnS-PB.js → tunnel-Cr3ctqxp.js} +3 -3
- package/server/{typed-errors-B-gfU2RB.js → typed-errors-CZ3GFBvo.js} +1 -1
- package/server/{typed-errors-DzC4zXbI.js → typed-errors-DXqf3RnP.js} +1 -1
- package/server/{types-DjpLVpsj.js → types-fzBp9cYi.js} +1 -1
- package/server/{types-DbxY2QB6.js → types-pLpbsNto.js} +1 -1
- package/server/{uat-failure-feedback-BVqsWlrr.js → uat-failure-feedback-IZQ-J8jt.js} +5 -5
- package/server/{uat-promote-verification-DmG097QS.js → uat-promote-verification-DXApKcc2.js} +5 -5
- package/server/{uat-train-COSy-vEX.js → uat-train-CycEzpbE.js} +1 -1
- package/server/{uat-train-DVUNW03P.js → uat-train-IIZlz7r3.js} +0 -0
- package/server/{validate-Dvv83iBx.js → validate-Ur4xH5bN.js} +3 -3
- package/server/{validation-BdCjzWLN.js → validation-CHovCoTT.js} +1 -1
- package/server/{validation-DlHafKzu.js → validation-Dr6V4PXx.js} +9 -6
- package/server/{verification-artifact-CaSbxUVg.js → verification-artifact-D3viYe_t.js} +1 -1
- package/server/{verification-artifact-0Zku8N-W.js → verification-artifact-k5Ftpg0c.js} +1 -1
- package/server/{verification-runner-C6wdZda2.js → verification-runner-BuqGwm2t.js} +1 -1
- package/server/{verification-runner-DwP2fUEF.js → verification-runner-Dl13mmsI.js} +53 -27
- package/server/{vtt-parser-luerJpYT.js → vtt-parser-BSnAB-Og.js} +1 -1
- package/server/work-agent-prompt-CJJ4tXci.js +3 -0
- package/server/{work-agent-prompt--9AWNYwZ.js → work-agent-prompt-CioeOuM4.js} +14 -14
- package/server/{work-agent-start-BNQO65W7.js → work-agent-start-B65Gqn9N.js} +2 -2
- package/server/{work-agent-start-DkzseQFl.js → work-agent-start-Ct1HVh6z.js} +1 -1
- package/server/{workflows-BYwR2aIN.js → workflows-AgFul1xc.js} +114 -41
- package/server/workflows-CfHm8lVd.js +2 -0
- package/server/{workspace-B-QJa3Ps.js → workspace-Cu3XlMXt.js} +1 -1
- package/server/{workspace-iTnTycOB.js → workspace-Cy4sDd3q.js} +3 -3
- package/server/{workspace-anchor-drift-DTt6h2Em.js → workspace-anchor-drift-BI0Hz6zI.js} +1 -1
- package/server/{workspace-anchor-drift-CHiGg8iu.js → workspace-anchor-drift-CMaqvizr.js} +4 -4
- package/server/{workspace-config-D0CavVtz.js → workspace-config-DRYDO7Zr.js} +1 -1
- package/server/{workspace-manager-W1OnbvU5.js → workspace-manager-CgovxDrO.js} +17 -17
- package/server/{workspace-manager-5BKPV6SY.js → workspace-manager-jfkKAEuO.js} +1 -1
- package/server/{workspace-metadata-tbbevEmP.js → workspace-metadata-BDqXzGGK.js} +1 -1
- package/server/{workspace-metadata-dJhXvZGQ.js → workspace-metadata-BNzODIv6.js} +2 -2
- package/server/{workspaces-BlpbOPZ7.js → workspaces-BJdPqx_J.js} +131 -127
- package/sync-sources/dev-skills/pan-style-guide/SKILL.md +90 -15
- package/sync-sources/hooks/linear-mcp-auth-hook +6 -1
- package/sync-sources/rules/handoff-isolate-cwd.md +4 -4
- package/sync-sources/rules/pan-handoff-focus-limit.md +12 -5
- package/sync-sources/rules/ste-writing.md +17 -0
- package/sync-sources/rules/work-agents-via-pan.md +2 -1
- package/sync-sources/rules/writing-style.md +3 -2
- package/sync-sources/skills/{skill-creator → claude-skill-creator}/SKILL.md +1 -1
- package/sync-sources/skills/{skill-creator → claude-skill-creator}/scripts/init_skill.py +1 -1
- package/sync-sources/skills/{skill-creator → claude-skill-creator}/scripts/package_skill.py +1 -1
- package/sync-sources/skills/pan-gauntlet-loop/SKILL.md +182 -0
- package/sync-sources/skills/pan-gauntlet-loop/assets/PROMPT.template.md +92 -0
- package/sync-sources/skills/pan-gauntlet-loop/assets/REFERENCE-BAR.template.md +53 -0
- package/sync-sources/skills/pan-gauntlet-loop/assets/STYLE.template.md +41 -0
- package/sync-sources/skills/pan-gauntlet-loop/assets/gauntlet-index.html +237 -0
- package/sync-sources/skills/pan-gauntlet-loop/references/domains.md +106 -0
- package/sync-sources/skills/pan-gauntlet-loop/references/lessons.md +60 -0
- package/sync-sources/skills/pan-handoff/SKILL.md +13 -6
- package/sync-sources/skills/pan-help/SKILL.md +1 -1
- package/sync-sources/skills/pan-peer-review/SKILL.md +3 -3
- package/sync-sources/skills/pan-restart/SKILL.md +6 -3
- package/sync-sources/skills/pan-start/SKILL.md +3 -0
- package/sync-sources/skills/pan-swarm/SKILL.md +32 -44
- package/sync-sources/skills/pan-sync/SKILL.md +1 -1
- package/server/activity-logger-BejO16Am.js +0 -3
- package/server/agent-state-DH_-Jkua.js +0 -3
- package/server/agents-megfmQHM.js +0 -3
- package/server/archive-planning-C-H9cM_J.js +0 -2
- package/server/build-info-PQlh_Rhb.js +0 -2
- package/server/cli-TaZspPHS.js +0 -2
- package/server/cliproxy-36ncAXlu.js +0 -3
- package/server/context-layers-CF6zHws4.js +0 -4
- package/server/conversations-AnGJ5rtX.js +0 -3
- package/server/deacon-BEH-IY0v.js +0 -5
- package/server/deacon-swarm-BHX-kR1b.js +0 -2
- package/server/deacon-swarm-BiLfNm_v.js +0 -1522
- package/server/dead-end-trip-BWpDg1Qd.js +0 -2
- package/server/delivery-cJM6zRjJ.js +0 -3
- package/server/deploy-queue-CmVBZEja.js +0 -2
- package/server/dist-VKBfkdi7.js +0 -3
- package/server/durable-review-pipeline-DgcVCYDL.js +0 -3
- package/server/factory-D4V2wd_V.js +0 -717
- package/server/forge-CrP1L3i6.js +0 -2
- package/server/hume-1ubD_6O7.js +0 -3
- package/server/inspect-agent-CfxERkNt.js +0 -2
- package/server/merge-ops-DtHx17Vd.js +0 -2
- package/server/merge-rebase-BvB4UuNS.js +0 -2
- package/server/project-repos-BJiShUsN.js +0 -3
- package/server/providers-CStNGwze.js +0 -3
- package/server/public/assets/index-BFEwfmqX.js +0 -860
- package/server/public/assets/index-_USwyTAT.css +0 -1
- package/server/public/assets/mermaid-GHXKKRXX-Dsln95SK.js +0 -1
- package/server/pull-requests-C1Cqp2yo.js +0 -2
- package/server/rally-CjayjUIl.js +0 -3
- package/server/resume-D7JuMCRQ.js +0 -3
- package/server/review-agent-BUNTVJK2.js +0 -2
- package/server/settings-DBWB1gsG.js +0 -2
- package/server/specialists-D8gsOCwh.js +0 -3
- package/server/specs-BqkrRnU9.js +0 -3
- package/server/stuck-remediation-Di5JYkoo.js +0 -1164
- package/server/stuck-remediation-TNM1VhZb.js +0 -2
- package/server/supervisor-DX8VsPnb.js +0 -2
- package/server/tmux-CqNapnrr.js +0 -4
- package/server/token-util-Bu8bPF9f.js +0 -3
- package/server/workflows-Cf4LAnhg.js +0 -2
|
@@ -1,58 +1,63 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { m as loadConfig$1, p as init_config } from "./config-
|
|
3
|
-
import {
|
|
4
|
-
import { a as recordLockPath, i as readOwner, n as init_fs_lock } from "./fs-lock-
|
|
5
|
-
import { a as updateIssueRecord, i as init_record_update, n as clearRecordPipelineClosedOut } from "./record-update-
|
|
6
|
-
import {
|
|
7
|
-
import { _ as logDeaconEventSync, c as init_agents, d as removeAgentRecordSync, g as logAgentLifecycleSync, h as init_persistent_logger, m as isTerminalIssueStage, p as getIssueStageSync, u as listAllAgentsSync } from "./agents-
|
|
8
|
-
import { A as
|
|
9
|
-
import { i as ensureInternalTokenSync, o as getInternalTokenSync, s as init_internal_token, t as INTERNAL_TOKEN_HEADER } from "./internal-token-
|
|
10
|
-
import {
|
|
11
|
-
import { O as stampBootReconciliation, _ as setBootReconciliationGrace, c as init_control_settings, g as setBootReconciliationDecision, o as getLastCleanShutdownAt, r as getBootReconciliationState, u as isDeaconGloballyPaused } from "./control-settings-
|
|
12
|
-
import { d as PENDING_PROMOTION_FILENAME, f as WORKSPACE_RUNTIME_DIRNAME, m as init_types } from "./types-
|
|
13
|
-
import { C as init_specs, S as findSpecByIssue, f as readPlanSync, h as readWorkspacePlanSync, o as findWorkspaceDraftPlanSync, s as init_io } from "./io-
|
|
14
|
-
import {
|
|
15
|
-
import { a as loadCloisterConfigSync, r as init_config$1 } from "./config-
|
|
16
|
-
import { a as init_state_home, o as inspectLegacyStatePaths } from "./state-home-
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import { t as
|
|
24
|
-
import {
|
|
25
|
-
import { t as
|
|
26
|
-
import { a as
|
|
27
|
-
import {
|
|
28
|
-
import { r as
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import { a as
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import { t as
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
42
|
-
import { t as
|
|
43
|
-
import {
|
|
44
|
-
import {
|
|
45
|
-
import {
|
|
46
|
-
import {
|
|
47
|
-
import {
|
|
1
|
+
import { E as init_tier_table, It as isStatePlaneOnlyStatus, J as getProjectConfigFromWorkspacePath, K as getIssueRecordPathForWorkspace, Mt as init_state_plane, O as resolveTieredExecutionEnabled, On as resolveProjectFromIssueSync, Ot as reconcileStatePlaneDrift, Ri as init_paths, Tt as init_auto_commit, Ui as sessionFilePath, Y as init_record, Yn as parseIssueIdSync, Zr as FsError, _n as listProjectsAsync, ci as init_errors, et as readIssueRecordForWorkspaceSync, fn as init_projects, gn as listProjects, jt as getMainDivergence, li as AGENTS_DIR, on as getProjectSync, p as loadConfigSync, qn as init_issue_id, rt as resolveProjectForIssue, t as init_config_yaml, tt as readIssueRecordSync, vi as OVERDECK_HOME, vn as listProjectsSync } from "./config-yaml-D9nfpEx1.js";
|
|
2
|
+
import { m as loadConfig$1, p as init_config } from "./config-ZGjvuR6J.js";
|
|
3
|
+
import { i as emitActivityEntryOnce, l as init_activity_logger, o as emitActivityEntrySync, s as emitActivityTtsSync } from "./activity-logger-BWZhVphb.js";
|
|
4
|
+
import { a as recordLockPath, i as readOwner, n as init_fs_lock } from "./fs-lock-C7J-ugIf.js";
|
|
5
|
+
import { a as updateIssueRecord, i as init_record_update, n as clearRecordPipelineClosedOut, o as updateIssueRecordForWorkspace } from "./record-update-dBGxL9OC.js";
|
|
6
|
+
import { B as sendKeys, C as init_tmux, E as killSession, I as querySessionSync, M as listSessionNamesSync, N as listSessions, T as isPaneDead, U as sessionExists, V as sendKeysAsync, W as sessionExistsSync, j as listSessionNames, k as listPaneValues, r as capturePane, v as getManagedTmuxSocketName } from "./tmux-DWEc4Ld9.js";
|
|
7
|
+
import { _ as logDeaconEventSync, c as init_agents, d as removeAgentRecordSync, g as logAgentLifecycleSync, h as init_persistent_logger, m as isTerminalIssueStage, p as getIssueStageSync, u as listAllAgentsSync } from "./agents-jXgqpHca.js";
|
|
8
|
+
import { $ as deliverCommitForReview, $n as isTerminalSwarmSlotAgent, A as saveAgentStateSync, Ai as getAgentRuntimeSnapshot, Bn as countRunningAgents, Cn as init_state_dir_removal, Cr as paneShowsCodexAuthBurn, Dr as init_spawn_prep, En as removeAgentStateDir, Er as defaultRunWorkspace, Fr as init_resolve_tier, Ft as resumeAgent, Gn as init_concurrency, Hn as describeRunningAgents, Ht as init_messaging, Ir as resolveTier, Jn as resetPatrolDispatchBudget, K as saveAgentRuntimeState, Kn as releaseAdvancingSlot, Kr as hasAgentRuntimeInSubtree, M as setAgentPausedSync, N as setAgentYieldedSync, O as resetAgentFailureCount, Qn as init_swarm_slot_lifecycle, Sr as isCodexAuthRouted, T as recordAgentFailure, Ut as messageAgent, Vn as countRunningSwarmSlotsForIssue, Vt as isContextOverflowTail, W as getAgentRuntimeStateSync, Wn as getConcurrencyLimits, Xn as tryReserveSwarmSlot, Yn as tryReserveAdvancingSlot, Zn as workResumeSlotsAvailable, _ as init_agent_state, _i as buildResumeContinueMessage, _n as listAgentStates, _t as init_pending_decision_gate, an as init_spawn, at as broadcastCommit, cn as ensureRegisteredSlotWorktree, d as decideResumeGate, dt as reconcileSlotState, er as getCachedMemoryVerdict, et as init_tier_supervisor, f as getAgentDir, fn as init_termination, g as getAgentStateSync, gt as detectPendingOperatorDecision, it as supervisorAgentId, ji as init_agent_runtime, k as saveAgentState, l as clearAgentTroubledSync, ln as init_registered_slot_spawn, lt as init_slot_reconcile, m as getAgentState, mr as init_transcript_landing, nt as shouldSupervise, ot as init_tier_feed, p as getAgentResumeGateBlockReason, pn as stopAgent, pr as captureTranscriptUserRecordSnapshot, q as init_agents$1, qn as releaseSwarmSlot, qr as init_runtime_command, rt as spawnTierSupervisor, sn as spawnRun, sr as deliverInitialPromptWithRetry, tr as init_memory_verdict_cache, tt as loadPrdDraft, u as clearYieldForResumeSync, ut as listSlotAssignments, vn as listRunningAgents, x as markAgentRunningState, xn as RETAINED_TRANSCRIPTS_MARKER, xr as init_codex_auth, yn as listRunningAgentsSync, yr as applyCodexAuthBurnFlag, zt as init_context_overflow } from "./agent-state-BJhGIJZe.js";
|
|
9
|
+
import { i as ensureInternalTokenSync, o as getInternalTokenSync, s as init_internal_token, t as INTERNAL_TOKEN_HEADER } from "./internal-token-DoVzb4b7.js";
|
|
10
|
+
import { A as init_conversations, M as listArchivedConversations, P as listConversations } from "./conversations-C75EFFjf.js";
|
|
11
|
+
import { O as stampBootReconciliation, _ as setBootReconciliationGrace, c as init_control_settings, d as isDeaconGloballyPausedSync, g as setBootReconciliationDecision, o as getLastCleanShutdownAt, r as getBootReconciliationState, u as isDeaconGloballyPaused } from "./control-settings-BMoDHY1G.js";
|
|
12
|
+
import { d as PENDING_PROMOTION_FILENAME, f as WORKSPACE_RUNTIME_DIRNAME, m as init_types } from "./types-pLpbsNto.js";
|
|
13
|
+
import { C as init_specs, S as findSpecByIssue, b as subItemsOf, f as readPlanSync, h as readWorkspacePlanSync, i as findPlanSync, n as applyStatusOverrides, o as findWorkspaceDraftPlanSync, s as init_io, y as init_types$1 } from "./io-DKrWd3gX.js";
|
|
14
|
+
import { a as getDispatchableItems, c as init_dag } from "./dag-BPc74hwj.js";
|
|
15
|
+
import { a as loadCloisterConfigSync, r as init_config$1 } from "./config-Doq_tF-f.js";
|
|
16
|
+
import { a as init_state_home, o as inspectLegacyStatePaths } from "./state-home-DdG8erBI.js";
|
|
17
|
+
import { d as resolveWorkspaceRepoRootsSync, i as init_project_repos, u as resolveSlotWorkspaceWorktreesSync } from "./project-repos--QeCg7yQ.js";
|
|
18
|
+
import { r as init_swarm_readiness, t as analyzeSwarmReadiness } from "./swarm-readiness-CMWAQuWM.js";
|
|
19
|
+
import { l as init_review_status_sync, u as markWorkspaceStuck } from "./review-status-sync-2C9lk85r.js";
|
|
20
|
+
import { B as needsReviewDispatch, G as findBlockingFindings, J as findWorkspaceVerdictConflicts, K as init_review_findings, Q as readWorkspaceVerdictFallback, Y as init_review_status_record_sync, d as getReviewStatusSync, h as loadReviewStatuses, p as init_review_status, q as drainWorkspaceVerdictFallback, w as setReviewStatusSync, z as init_review_dispatch_decision } from "./review-status-yOGD2MuK.js";
|
|
21
|
+
import { d as init_review_verdict_report, f as parseVerdictReport, l as findVerdictReport, u as findVerdictReportAsync } from "./synthesis-verdict-DVlrLF9A.js";
|
|
22
|
+
import { t as assessMemoryPressure } from "./memory-governor-Cqvbusr4.js";
|
|
23
|
+
import { r as isAgentIdleForNudge, t as getAgentEffectiveLastActivityMs } from "./agent-idle-ONBssBN_.js";
|
|
24
|
+
import { n as pruneTerminalStoppedAgents, r as pidsWithCwdUnder } from "./agent-gc-BNUudFnt.js";
|
|
25
|
+
import { t as recordDeaconNudge } from "./deacon-nudge-log-CzM9JA9t.js";
|
|
26
|
+
import { a as readLiveTrackerIssueState, n as init_issue_closed, r as isIssueClosed } from "./issue-closed-6RQsAjDX.js";
|
|
27
|
+
import { t as getAutoCloseOutCanonicalState } from "./deacon-canonical-state-CkvxRPck.js";
|
|
28
|
+
import { a as checkFailedMergeRetry, c as ciRetryMap, d as reconcileMergedButReviewing, f as reconcileStaleMergeBlockers, g as reconcileStuckMergingStates, h as reconcileAutoMergeRows, i as autoCloseOut, l as reconcileClosedPrReadyForMerge, m as reconcileStuckReadyForMerge, n as reconcileTerminalIssueResidue, o as checkFirstCompletionAgents, p as reconcileStaleMergeStatus, r as listIssueRecords, s as checkReadyForMergeStuck$1, u as reconcileFalseMerged } from "./parked-residue-BiHb8cD7.js";
|
|
29
|
+
import { a as writeFeedbackFile, i as init_feedback_writer } from "./feedback-writer-C1gCLFmp.js";
|
|
30
|
+
import { r as surfaceIssueFeedbackNeedsYou, t as init_feedback_target } from "./feedback-target-D7GxdUYG.js";
|
|
31
|
+
import { t as recordReviewVerdict } from "./review-verdict-writer-Ca2V7QSY.js";
|
|
32
|
+
import { a as observeActiveReviewArtifact, i as convergeRowFromVerdictOfRecord, n as reviewTimestampMs, r as REVIEW_SUB_ROLES, t as evaluateReviewConvoyLiveness } from "./review-convoy-liveness-DmO8dIBH.js";
|
|
33
|
+
import { g as getAllProjectSpecialistStatuses, t as init_specialists } from "./specialists-CuDWiNdB.js";
|
|
34
|
+
import { m as parseReviewerSessionName, s as getTmuxSessionName } from "./specialists-registry-BYjs04iK.js";
|
|
35
|
+
import { a as inferBranchFromWorkspace, i as findWorkspacePath } from "./archive-planning-CGkU6UeL.js";
|
|
36
|
+
import { n as shouldSkipDispatchAsMerged } from "./merge-verification-JJc5GC2t.js";
|
|
37
|
+
import { t as deliverReviewVerdictFeedback } from "./review-verdict-feedback-CE_tr2iQ.js";
|
|
38
|
+
import { n as reconcileTraefikNetworks, t as init_traefik_connect } from "./traefik-connect-D3H5I6b9.js";
|
|
39
|
+
import { i as recordRecoveryFailure, n as findRecoveryTrip, r as init_recovery_trip } from "./recovery-trip-BKLrL6NX.js";
|
|
40
|
+
import { a as init_work_agent_prompt, n as buildWorkAgentPrompt } from "./work-agent-prompt-CioeOuM4.js";
|
|
41
|
+
import { t as isExplicitNoResumeRequest } from "./no-resume-mode-xuYLFhLw.js";
|
|
42
|
+
import { t as createInFlightGuard } from "./in-flight-guard-DuxabAHT.js";
|
|
43
|
+
import { t as checkStuckAgentRemediation } from "./stuck-remediation-BDa_tXgB.js";
|
|
44
|
+
import { r as reconcileClosedIssueAgents } from "./closed-issue-reaper-nMLRi5de.js";
|
|
45
|
+
import { b as reconcilePipelineLabelsPatrol, y as init_label_reconciler } from "./pipeline-membership-gather-BK4dzh0X.js";
|
|
46
|
+
import { t as recordDeadEndNeedsYou } from "./dead-end-trip-CnVmjnya.js";
|
|
47
|
+
import { a as triggerRebuildAndStart, i as reconcileOrphanProposedSpecs } from "./orphan-proposed-reconciler-Dmk0kO_p.js";
|
|
48
|
+
import { n as spawnWorkAgentThroughAgentsEndpoint } from "./work-agent-start-B65Gqn9N.js";
|
|
49
|
+
import { n as reconcileIdleWorkspaceStacks } from "./idle-stack-reaper-BSxb1bln.js";
|
|
50
|
+
import { a as recordUnsignaledTestEscalation, i as readTestVerdictArtifact, n as decideUnsignaledTestAction, r as init_test_verdict } from "./test-verdict-DUbDHqeD.js";
|
|
51
|
+
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
48
52
|
import { Effect } from "effect";
|
|
49
53
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "fs";
|
|
50
54
|
import { basename as basename$1, dirname as dirname$1, join as join$1 } from "path";
|
|
51
55
|
import { cpus, loadavg } from "os";
|
|
52
|
-
import {
|
|
56
|
+
import { hostname } from "node:os";
|
|
57
|
+
import { existsSync as existsSync$1, readFileSync as readFileSync$1 } from "node:fs";
|
|
53
58
|
import { access, mkdir, readFile, readdir, rename, rm, rmdir, stat, writeFile } from "node:fs/promises";
|
|
54
59
|
import { readdir as readdir$1 } from "fs/promises";
|
|
55
|
-
import { execFile, execFileSync } from "node:child_process";
|
|
60
|
+
import { exec, execFile, execFileSync } from "node:child_process";
|
|
56
61
|
import { promisify } from "node:util";
|
|
57
62
|
import { exec as exec$1, execFile as execFile$1 } from "child_process";
|
|
58
63
|
import { promisify as promisify$1 } from "util";
|
|
@@ -61,10 +66,11 @@ init_paths();
|
|
|
61
66
|
init_errors();
|
|
62
67
|
init_issue_id();
|
|
63
68
|
init_projects();
|
|
64
|
-
init_state_read_home();
|
|
65
69
|
init_state_plane();
|
|
66
70
|
init_auto_commit();
|
|
67
71
|
init_record();
|
|
72
|
+
init_tier_table();
|
|
73
|
+
init_config_yaml();
|
|
68
74
|
init_config();
|
|
69
75
|
init_activity_logger();
|
|
70
76
|
init_fs_lock();
|
|
@@ -73,19 +79,36 @@ init_tmux();
|
|
|
73
79
|
init_persistent_logger();
|
|
74
80
|
init_agents();
|
|
75
81
|
init_internal_token();
|
|
82
|
+
init_agent_runtime();
|
|
76
83
|
init_conversations();
|
|
77
84
|
init_runtime_command();
|
|
85
|
+
init_control_settings();
|
|
78
86
|
init_types();
|
|
79
87
|
init_specs();
|
|
88
|
+
init_types$1();
|
|
80
89
|
init_io();
|
|
90
|
+
init_dag();
|
|
91
|
+
init_resolve_tier();
|
|
92
|
+
init_spawn_prep();
|
|
81
93
|
init_codex_auth();
|
|
82
94
|
init_transcript_landing();
|
|
83
95
|
init_config$1();
|
|
96
|
+
init_memory_verdict_cache();
|
|
84
97
|
init_swarm_slot_lifecycle();
|
|
85
98
|
init_concurrency();
|
|
86
99
|
init_state_dir_removal();
|
|
100
|
+
init_state_home();
|
|
101
|
+
init_termination();
|
|
102
|
+
init_project_repos();
|
|
103
|
+
init_registered_slot_spawn();
|
|
104
|
+
init_spawn();
|
|
87
105
|
init_messaging();
|
|
88
106
|
init_context_overflow();
|
|
107
|
+
init_pending_decision_gate();
|
|
108
|
+
init_swarm_readiness();
|
|
109
|
+
init_slot_reconcile();
|
|
110
|
+
init_tier_feed();
|
|
111
|
+
init_tier_supervisor();
|
|
89
112
|
init_agents$1();
|
|
90
113
|
init_agent_state();
|
|
91
114
|
init_review_status_sync();
|
|
@@ -142,7 +165,7 @@ async function checkInspectAgentTimeouts(now = /* @__PURE__ */ new Date()) {
|
|
|
142
165
|
console.warn(`[deacon] Failed to kill inspect session ${sessionName}: ${msg}`);
|
|
143
166
|
});
|
|
144
167
|
try {
|
|
145
|
-
const { messageAgent } = await import("./agents-
|
|
168
|
+
const { messageAgent } = await import("./agents-DpCuoF65.js");
|
|
146
169
|
await messageAgent(`agent-${issueId.toLowerCase()}`, verdict, "deacon:inspect-watchdog");
|
|
147
170
|
} catch (err) {
|
|
148
171
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -182,12 +205,12 @@ function paneShowsModelSwitch(pane, launchModel) {
|
|
|
182
205
|
}
|
|
183
206
|
return Boolean(target && target.toLowerCase() !== launchModel.toLowerCase());
|
|
184
207
|
}
|
|
185
|
-
const defaultDeps$
|
|
208
|
+
const defaultDeps$7 = {
|
|
186
209
|
capturePane: (agentId) => Effect.runPromise(capturePane(agentId, 100)),
|
|
187
210
|
sendKey: (agentId, key) => sendKeysAsync(agentId, key, "known-modal-handler"),
|
|
188
211
|
settle: () => new Promise((resolve) => setTimeout(resolve, 300))
|
|
189
212
|
};
|
|
190
|
-
async function handleKnownAgentModal(agentId, deps = defaultDeps$
|
|
213
|
+
async function handleKnownAgentModal(agentId, deps = defaultDeps$7, initialPane) {
|
|
191
214
|
const pane = initialPane ?? await deps.capturePane(agentId);
|
|
192
215
|
const modal = KNOWN_AGENT_MODALS.find((candidate) => candidate.matches(pane));
|
|
193
216
|
if (!modal) return "none";
|
|
@@ -280,7 +303,7 @@ const apiErrorRecoveryState = /* @__PURE__ */ new Map();
|
|
|
280
303
|
* backend's own limit produces a hard 400 instead.
|
|
281
304
|
*/
|
|
282
305
|
/** Continuation nudge sent after a successful harness /compact (specialist/planning sessions). */
|
|
283
|
-
const CONTEXT_OVERFLOW_CONTINUE_MSG = "Your context was compacted to recover from a context-window overflow. Continue from where you left off using the compacted summary and your
|
|
306
|
+
const CONTEXT_OVERFLOW_CONTINUE_MSG = "Your context was compacted to recover from a context-window overflow. Continue from where you left off using the compacted summary and your xBRIEF items / continue.json — do NOT start over.";
|
|
284
307
|
/**
|
|
285
308
|
* Let a recovery attempt finish before judging the result. MUST exceed the
|
|
286
309
|
* patrol interval (60s): at exactly one interval the guard expires on the very
|
|
@@ -405,9 +428,9 @@ async function checkApiErrorAgents() {
|
|
|
405
428
|
}
|
|
406
429
|
if (!tmuxOutput.includes("❯")) continue;
|
|
407
430
|
try {
|
|
408
|
-
const { maybeContinueCompactedAgent } = await import("./compaction-continuation-
|
|
409
|
-
const { findLastCompactBoundary } = await import("./conversation-service-
|
|
410
|
-
const { deliverAgentMessage } = await import("./delivery-
|
|
431
|
+
const { maybeContinueCompactedAgent } = await import("./compaction-continuation-Bjfi70G-.js");
|
|
432
|
+
const { findLastCompactBoundary } = await import("./conversation-service-CzZUJFEx.js");
|
|
433
|
+
const { deliverAgentMessage } = await import("./delivery-gyce0vQ7.js");
|
|
411
434
|
const continued = await maybeContinueCompactedAgent({
|
|
412
435
|
agentId: sessionName,
|
|
413
436
|
tmuxOutput,
|
|
@@ -458,13 +481,13 @@ async function checkApiErrorAgents() {
|
|
|
458
481
|
const st = getAgentStateSync(sessionName);
|
|
459
482
|
const sid = st?.sessionId ?? runtimeState?.claudeSessionId;
|
|
460
483
|
if (st?.workspace && sid && st.model) {
|
|
461
|
-
const { computeContextUsage } = await import("./conversation-service-
|
|
484
|
+
const { computeContextUsage } = await import("./conversation-service-CzZUJFEx.js");
|
|
462
485
|
const usage = await computeContextUsage(sessionFilePath(st.workspace, sid), st.model);
|
|
463
486
|
if (usage && usage.percentUsed < 85) recoveredPct = usage.percentUsed;
|
|
464
487
|
}
|
|
465
488
|
} catch {}
|
|
466
489
|
if (recoveredPct !== null) {
|
|
467
|
-
const { clearWorkspaceStuck } = await import("./review-status-sync-
|
|
490
|
+
const { clearWorkspaceStuck } = await import("./review-status-sync-Bq00Loey.js");
|
|
468
491
|
clearWorkspaceStuck(issueId);
|
|
469
492
|
stuckOverflowNativeRecoveryState.delete(sessionName);
|
|
470
493
|
actions.push(`Context overflow recovery: cleared stuck flag for ${sessionName} (context back to ${Math.round(recoveredPct)}%)`);
|
|
@@ -480,7 +503,7 @@ async function checkApiErrorAgents() {
|
|
|
480
503
|
rec.attempts += 1;
|
|
481
504
|
rec.lastAttempt = now;
|
|
482
505
|
stuckOverflowNativeRecoveryState.set(sessionName, rec);
|
|
483
|
-
const { resumeAgent } = await import("./agents-
|
|
506
|
+
const { resumeAgent } = await import("./agents-DpCuoF65.js");
|
|
484
507
|
const recovered = await resumeAgent(sessionName, void 0, { compact: true });
|
|
485
508
|
if (recovered.success) {
|
|
486
509
|
stuckOverflowNativeRecoveryState.delete(sessionName);
|
|
@@ -528,7 +551,7 @@ async function checkApiErrorAgents() {
|
|
|
528
551
|
}
|
|
529
552
|
const compactAttempts = (ov?.compactAttempts ?? 0) + 1;
|
|
530
553
|
if (issueId !== null) {
|
|
531
|
-
const { resumeAgent } = await import("./agents-
|
|
554
|
+
const { resumeAgent } = await import("./agents-DpCuoF65.js");
|
|
532
555
|
const resumeResult = await resumeAgent(sessionName, void 0, { compact: true });
|
|
533
556
|
contextOverflowRecoveryState.set(sessionName, {
|
|
534
557
|
lastAttempt: now,
|
|
@@ -783,7 +806,7 @@ async function checkPostReviewCommits() {
|
|
|
783
806
|
const actions = [];
|
|
784
807
|
try {
|
|
785
808
|
const statuses = loadReviewStatuses();
|
|
786
|
-
const { resolveProjectFromIssueSync } = await import("./projects-
|
|
809
|
+
const { resolveProjectFromIssueSync } = await import("./projects-LftaVK99.js");
|
|
787
810
|
for (const [issueId, status] of Object.entries(statuses)) {
|
|
788
811
|
const isBlocked = status.reviewStatus === "blocked";
|
|
789
812
|
if (!isBlocked) blockedReviewDriftObservations.delete(issueId);
|
|
@@ -815,8 +838,8 @@ async function checkPostReviewCommits() {
|
|
|
815
838
|
if (!project) continue;
|
|
816
839
|
const workspacePath = join$1(project.projectPath, "workspaces", `feature-${issueId.toLowerCase()}`);
|
|
817
840
|
if (!existsSync(workspacePath)) continue;
|
|
818
|
-
const { formatAnchorShort, rehydrateHeadAnchor } = await import("./git-utils-
|
|
819
|
-
const { evaluateWorkspaceAnchorDrift } = await import("./workspace-anchor-drift-
|
|
841
|
+
const { formatAnchorShort, rehydrateHeadAnchor } = await import("./git-utils-CsKV0Dis.js");
|
|
842
|
+
const { evaluateWorkspaceAnchorDrift } = await import("./workspace-anchor-drift-BI0Hz6zI.js");
|
|
820
843
|
const verdict = await evaluateWorkspaceAnchorDrift(issueId, workspacePath, rehydrateHeadAnchor(reviewedAnchor));
|
|
821
844
|
if (verdict.kind === "unreadable" || verdict.kind === "current") {
|
|
822
845
|
blockedReviewDriftObservations.delete(issueId);
|
|
@@ -834,7 +857,7 @@ async function checkPostReviewCommits() {
|
|
|
834
857
|
resetLoopEscalated.add(issueId);
|
|
835
858
|
const message = `${issueId} — review reset suppressed: drift against ${formatAnchorShort(currentHead)} already caused a reset and the workspace has not moved since. This indicates an anchor-producer disagreement, not new commits; review will not re-dispatch until the workspace head actually changes.`;
|
|
836
859
|
logDeaconEventSync(`checkPostReviewCommits: ${message}`);
|
|
837
|
-
const { recordDeadEndNeedsYou } = await import("./dead-end-trip-
|
|
860
|
+
const { recordDeadEndNeedsYou } = await import("./dead-end-trip-6V_CwveT.js");
|
|
838
861
|
await recordDeadEndNeedsYou(issueId, "review-reset-loop", currentHead, message);
|
|
839
862
|
}
|
|
840
863
|
continue;
|
|
@@ -878,7 +901,7 @@ async function checkPostReviewCommits() {
|
|
|
878
901
|
actions.push(`Deferred post-review re-dispatch for ${issueId} — advancing-role concurrency ceiling reached`);
|
|
879
902
|
logDeaconEventSync(`checkPostReviewCommits: deferred review for ${issueId} — advancing ceiling reached (PAN-1665) — ${describeRunningAgents()}`);
|
|
880
903
|
} else if (freshStatus?.reviewStatus === "pending") {
|
|
881
|
-
const { spawnReviewRoleForIssue } = await import("./review-agent-
|
|
904
|
+
const { spawnReviewRoleForIssue } = await import("./review-agent-DgkUPRx0.js");
|
|
882
905
|
const branch = `feature/${issueId.toLowerCase()}`;
|
|
883
906
|
const dispatchResult = await Effect.runPromise(spawnReviewRoleForIssue({
|
|
884
907
|
issueId,
|
|
@@ -920,7 +943,7 @@ async function checkPostReviewCommits() {
|
|
|
920
943
|
* of life regardless of tmux. The deacon's liveness arbiter consults this
|
|
921
944
|
* before declaring an agent orphaned.
|
|
922
945
|
*/
|
|
923
|
-
const execFileAsync$
|
|
946
|
+
const execFileAsync$5 = promisify(execFile);
|
|
924
947
|
const defaultPgrep = (pattern) => {
|
|
925
948
|
try {
|
|
926
949
|
execFileSync("pgrep", ["-f", pattern], { stdio: [
|
|
@@ -935,7 +958,7 @@ const defaultPgrep = (pattern) => {
|
|
|
935
958
|
};
|
|
936
959
|
const defaultAsyncPgrep = async (pattern) => {
|
|
937
960
|
try {
|
|
938
|
-
await execFileAsync$
|
|
961
|
+
await execFileAsync$5("pgrep", ["-f", pattern], { encoding: "utf8" });
|
|
939
962
|
return true;
|
|
940
963
|
} catch (error) {
|
|
941
964
|
if (typeof error === "object" && error !== null && "code" in error && error.code === 1) return false;
|
|
@@ -962,7 +985,7 @@ init_review_status();
|
|
|
962
985
|
init_tmux();
|
|
963
986
|
init_feedback_writer();
|
|
964
987
|
init_feedback_target();
|
|
965
|
-
const execFileAsync$
|
|
988
|
+
const execFileAsync$4 = promisify(execFile);
|
|
966
989
|
function internalDashboardUrl() {
|
|
967
990
|
const port = Number.parseInt(process.env.API_PORT ?? process.env.PORT ?? "3011", 10);
|
|
968
991
|
return process.env.OVERDECK_INTERNAL_DASHBOARD_URL ?? `http://127.0.0.1:${port}`;
|
|
@@ -1042,14 +1065,14 @@ async function defaultIsStrikeAgentAlive(agentId) {
|
|
|
1042
1065
|
return true;
|
|
1043
1066
|
}
|
|
1044
1067
|
}
|
|
1045
|
-
function defaultDeps$
|
|
1068
|
+
function defaultDeps$6() {
|
|
1046
1069
|
return {
|
|
1047
1070
|
loadStatuses: loadReviewStatuses,
|
|
1048
1071
|
getStatus: getReviewStatusSync,
|
|
1049
1072
|
setStatus: setReviewStatusSync,
|
|
1050
1073
|
resolveProject: resolveProjectFromIssueSync,
|
|
1051
1074
|
mergeIssue: requestStrikeMerge,
|
|
1052
|
-
getMainHead: async (projectPath) => (await execFileAsync$
|
|
1075
|
+
getMainHead: async (projectPath) => (await execFileAsync$4("git", ["rev-parse", "origin/main"], {
|
|
1053
1076
|
cwd: projectPath,
|
|
1054
1077
|
encoding: "utf8"
|
|
1055
1078
|
})).stdout.trim(),
|
|
@@ -1071,7 +1094,7 @@ function defaultDeps$2() {
|
|
|
1071
1094
|
isScheduled: (key) => strikeLandingSupervisor.has(key),
|
|
1072
1095
|
isPersistentlyOwned: () => false,
|
|
1073
1096
|
listProjects: listProjectsAsync,
|
|
1074
|
-
git: async (args, cwd) => (await execFileAsync$
|
|
1097
|
+
git: async (args, cwd) => (await execFileAsync$4("git", args, {
|
|
1075
1098
|
cwd,
|
|
1076
1099
|
encoding: "utf8"
|
|
1077
1100
|
})).stdout.trim(),
|
|
@@ -1273,7 +1296,7 @@ async function salvageStrandedStrikeBranches(deps) {
|
|
|
1273
1296
|
}
|
|
1274
1297
|
async function patrolStrikeLandings(overrides = {}) {
|
|
1275
1298
|
const deps = {
|
|
1276
|
-
...defaultDeps$
|
|
1299
|
+
...defaultDeps$6(),
|
|
1277
1300
|
...overrides
|
|
1278
1301
|
};
|
|
1279
1302
|
const actions = await salvageStrandedStrikeBranches(deps);
|
|
@@ -1450,7 +1473,7 @@ function reviewConvoyLiveness(issueId) {
|
|
|
1450
1473
|
*/
|
|
1451
1474
|
async function recoverUnhealthyTestStack(issueId, workspacePath) {
|
|
1452
1475
|
const key = issueId.toUpperCase();
|
|
1453
|
-
const { getWorkspaceStackHealth } = await import("./stack-health-
|
|
1476
|
+
const { getWorkspaceStackHealth } = await import("./stack-health-ypIKtkbE.js");
|
|
1454
1477
|
const health = await Effect.runPromise(getWorkspaceStackHealth(issueId, { workspacePath }));
|
|
1455
1478
|
if (health.healthy) {
|
|
1456
1479
|
testStackRebuildState.delete(key);
|
|
@@ -1482,7 +1505,7 @@ async function recoverUnhealthyTestStack(issueId, workspacePath) {
|
|
|
1482
1505
|
record.attempts += 1;
|
|
1483
1506
|
testStackRebuildState.set(key, record);
|
|
1484
1507
|
console.log(`[deacon] Test stack for ${key} unhealthy (${health.reasons.join("; ")}) — rebuilding (attempt ${record.attempts}/${TEST_STACK_REBUILD_MAX_ATTEMPTS})`);
|
|
1485
|
-
const { rebuildWorkspaceStack } = await import("./rebuild-stack-
|
|
1508
|
+
const { rebuildWorkspaceStack } = await import("./rebuild-stack-DrMyA_mA.js");
|
|
1486
1509
|
const result = await Effect.runPromise(rebuildWorkspaceStack(issueId, { onProgress: (m) => console.log(`[deacon] ${key} stack rebuild: ${m}`) }));
|
|
1487
1510
|
if (!result.success) {
|
|
1488
1511
|
console.warn(`[deacon] Test stack rebuild failed for ${key}: ${result.error}`);
|
|
@@ -1668,7 +1691,7 @@ async function handleReviewCoordinatorDied(issueId, _sessionName, _reason) {
|
|
|
1668
1691
|
return actions;
|
|
1669
1692
|
}
|
|
1670
1693
|
try {
|
|
1671
|
-
const { spawnReviewRoleForIssue } = await import("./review-agent-
|
|
1694
|
+
const { spawnReviewRoleForIssue } = await import("./review-agent-DgkUPRx0.js");
|
|
1672
1695
|
const dispatchResult = await Effect.runPromise(spawnReviewRoleForIssue({
|
|
1673
1696
|
issueId,
|
|
1674
1697
|
workspace,
|
|
@@ -1743,7 +1766,7 @@ async function reconcileReviewStatusOrphan(issueId, rawStatus, options = {}) {
|
|
|
1743
1766
|
const issueLower = issueId.toLowerCase();
|
|
1744
1767
|
const workspace = getAgentStateSync(`agent-${issueLower}`)?.workspace || (resolved ? findWorkspacePath(resolved.projectPath, issueLower) : null);
|
|
1745
1768
|
if (workspace) try {
|
|
1746
|
-
if ((options.readWorkspaceHead ? await options.readWorkspaceHead(workspace) : await (await import("./git-utils-
|
|
1769
|
+
if ((options.readWorkspaceHead ? await options.readWorkspaceHead(workspace) : await (await import("./git-utils-CsKV0Dis.js")).snapshotWorkspaceHeadsPromise(issueId, workspace)) === status.lastVerifiedCommit) {
|
|
1747
1770
|
setReviewStatusSync(issueId, {
|
|
1748
1771
|
reviewStatus: "pending",
|
|
1749
1772
|
reviewNotes: void 0
|
|
@@ -1765,7 +1788,7 @@ async function reconcileReviewStatusOrphan(issueId, rawStatus, options = {}) {
|
|
|
1765
1788
|
if (project) {
|
|
1766
1789
|
const workspacePath = join$1(project.projectPath, "workspaces", `feature-${issueId.toLowerCase()}`);
|
|
1767
1790
|
if (existsSync(workspacePath)) {
|
|
1768
|
-
const { snapshotWorkspaceHeadsPromise } = await import("./git-utils-
|
|
1791
|
+
const { snapshotWorkspaceHeadsPromise } = await import("./git-utils-CsKV0Dis.js");
|
|
1769
1792
|
reviewUpdate["reviewedAtCommit"] = await snapshotWorkspaceHeadsPromise(issueId, workspacePath);
|
|
1770
1793
|
}
|
|
1771
1794
|
}
|
|
@@ -1827,7 +1850,7 @@ async function reconcileReviewStatusOrphan(issueId, rawStatus, options = {}) {
|
|
|
1827
1850
|
const workspace = agentState?.workspace || (resolved ? findWorkspacePath(resolved.projectPath, issueLower) : null);
|
|
1828
1851
|
if (workspace && resolved && !tryReserveAdvancingSlot() && !await tryYieldForAdvancingDispatch("review", issueId)) actions.push(`Deferred review re-dispatch for ${issueId} — advancing-role concurrency ceiling reached`);
|
|
1829
1852
|
else if (workspace && resolved) try {
|
|
1830
|
-
const { spawnReviewRoleForIssue } = await import("./review-agent-
|
|
1853
|
+
const { spawnReviewRoleForIssue } = await import("./review-agent-DgkUPRx0.js");
|
|
1831
1854
|
const dispatchResult = await Effect.runPromise(spawnReviewRoleForIssue({
|
|
1832
1855
|
issueId,
|
|
1833
1856
|
workspace,
|
|
@@ -1852,8 +1875,8 @@ async function reconcileReviewStatusOrphan(issueId, rawStatus, options = {}) {
|
|
|
1852
1875
|
const workspace = agentState?.workspace || (resolved ? findWorkspacePath(resolved.projectPath, issueLower) : null);
|
|
1853
1876
|
if (workspace && resolved) {
|
|
1854
1877
|
const branch = `feature/${issueLower}`;
|
|
1855
|
-
const { spawnRun } = await import("./agents-
|
|
1856
|
-
const { buildTestRolePrompt } = await import("./test-agent-queue-
|
|
1878
|
+
const { spawnRun } = await import("./agents-DpCuoF65.js");
|
|
1879
|
+
const { buildTestRolePrompt } = await import("./test-agent-queue-C5DI_gWX.js");
|
|
1857
1880
|
const stackRecovery = await recoverUnhealthyTestStack(issueId, workspace);
|
|
1858
1881
|
if (stackRecovery === "cooldown" || stackRecovery === "exhausted") {
|
|
1859
1882
|
setReviewStatusSync(issueId, { testStatus: "dispatch_failed" });
|
|
@@ -1992,7 +2015,7 @@ async function recoverStalledReviewConvoys(getCanonicalState = getAutoCloseOutCa
|
|
|
1992
2015
|
record.lastAttempt = now;
|
|
1993
2016
|
record.attempts += 1;
|
|
1994
2017
|
stalledReviewConvoyRecoveryState.set(key, record);
|
|
1995
|
-
const { spawnReviewRoleForIssue } = await import("./review-agent-
|
|
2018
|
+
const { spawnReviewRoleForIssue } = await import("./review-agent-DgkUPRx0.js");
|
|
1996
2019
|
try {
|
|
1997
2020
|
const result = await Effect.runPromise(spawnReviewRoleForIssue({
|
|
1998
2021
|
issueId,
|
|
@@ -2063,7 +2086,7 @@ async function checkMissingReviewStatuses() {
|
|
|
2063
2086
|
continue;
|
|
2064
2087
|
}
|
|
2065
2088
|
try {
|
|
2066
|
-
const { spawnReviewRoleForIssue } = await import("./review-agent-
|
|
2089
|
+
const { spawnReviewRoleForIssue } = await import("./review-agent-DgkUPRx0.js");
|
|
2067
2090
|
await Effect.runPromise(spawnReviewRoleForIssue({
|
|
2068
2091
|
issueId,
|
|
2069
2092
|
workspace,
|
|
@@ -2114,7 +2137,7 @@ async function checkStuckReviewing() {
|
|
|
2114
2137
|
const actions = [];
|
|
2115
2138
|
const REVIEW_STUCK_THRESHOLD_MS = 1800 * 1e3;
|
|
2116
2139
|
try {
|
|
2117
|
-
const { loadReviewStatuses, setReviewStatusSync } = await import("./review-status-
|
|
2140
|
+
const { loadReviewStatuses, setReviewStatusSync } = await import("./review-status-C_kQeeX0.js");
|
|
2118
2141
|
const statuses = loadReviewStatuses();
|
|
2119
2142
|
const now = Date.now();
|
|
2120
2143
|
const activeReviewIssues = /* @__PURE__ */ new Set();
|
|
@@ -2273,7 +2296,7 @@ async function reconcileUnappliedReviewVerdicts() {
|
|
|
2273
2296
|
let currentHead;
|
|
2274
2297
|
let reviewedHead;
|
|
2275
2298
|
try {
|
|
2276
|
-
const { rehydrateHeadAnchor, snapshotWorkspaceHeadsPromise } = await import("./git-utils-
|
|
2299
|
+
const { rehydrateHeadAnchor, snapshotWorkspaceHeadsPromise } = await import("./git-utils-CsKV0Dis.js");
|
|
2277
2300
|
currentHead = await snapshotWorkspaceHeadsPromise(issueId, wsPath);
|
|
2278
2301
|
const persistedAnchor = reviewAnchorFromContext(latestContext);
|
|
2279
2302
|
reviewedHead = persistedAnchor ? rehydrateHeadAnchor(persistedAnchor) : void 0;
|
|
@@ -2297,7 +2320,7 @@ async function reconcileUnappliedReviewVerdicts() {
|
|
|
2297
2320
|
const notes = parsed.topBlocker || `See ${latestReport.filename}`;
|
|
2298
2321
|
const nudge = `Your review verdict is already written on disk but review status is still pending. Your ONLY remaining task is to execute this Bash command immediately — do not analyze, do not summarize, do not ask questions, just run it:\n\n${buildReviewCompletionCommand(issueId, parsed.verdict, notes, basename$1(latestDir))}\n\nRun this command NOW. Do not write any other response before executing it.`;
|
|
2299
2322
|
try {
|
|
2300
|
-
const { messageAgent } = await import("./agents-
|
|
2323
|
+
const { messageAgent } = await import("./agents-DpCuoF65.js");
|
|
2301
2324
|
await messageAgent(reviewSession, nudge);
|
|
2302
2325
|
pendingVerdictNudges.set(latestDir, now);
|
|
2303
2326
|
const action = `Nudged ${reviewSession} to apply pending ${parsed.verdict} verdict from ${latestReport.filename}`;
|
|
@@ -2416,7 +2439,7 @@ async function checkCompletedButUnsignaledReviews() {
|
|
|
2416
2439
|
const cmd = buildReviewCompletionCommand(issueId, verdict, topBlocker || `See ${latestReport.filename}`, basename$1(latestDir));
|
|
2417
2440
|
const nudge = `Your review verdict in ${latestReport.filename} is already written and saved. Your ONLY remaining task is to execute this Bash command immediately — do not analyze, do not summarize, do not ask questions, just run it:\n\n${cmd}\n\nRun this command NOW. Do not write any other response before executing it.`;
|
|
2418
2441
|
try {
|
|
2419
|
-
const { messageAgent } = await import("./agents-
|
|
2442
|
+
const { messageAgent } = await import("./agents-DpCuoF65.js");
|
|
2420
2443
|
await messageAgent(reviewSession, nudge);
|
|
2421
2444
|
unsignaledReviewNudges.set(latestDir, now);
|
|
2422
2445
|
actions.push(`Nudged ${reviewSession} to signal ${verdict} (${latestReport.filename} written ${Math.round((now - latestMtime) / 6e4)}min ago)`);
|
|
@@ -2521,7 +2544,7 @@ async function retryReviewer(state, agentId, trigger) {
|
|
|
2521
2544
|
return false;
|
|
2522
2545
|
}
|
|
2523
2546
|
try {
|
|
2524
|
-
const { spawnReviewSubRoleForIssue } = await import("./review-agent-
|
|
2547
|
+
const { spawnReviewSubRoleForIssue } = await import("./review-agent-DgkUPRx0.js");
|
|
2525
2548
|
const contextManifestPath = join$1(dirname$1(outputPath), "context.json");
|
|
2526
2549
|
const result = await Effect.runPromise(spawnReviewSubRoleForIssue({
|
|
2527
2550
|
issueId: state.issueId,
|
|
@@ -2607,11 +2630,11 @@ async function nudgeSynthesisForCompleteReviewerReports(states) {
|
|
|
2607
2630
|
writeFileSync(join$1(reviewDir, "synthesis.md"), synthesis.body);
|
|
2608
2631
|
let fallbackHead;
|
|
2609
2632
|
try {
|
|
2610
|
-
const { snapshotWorkspaceHeadsPromise } = await import("./git-utils-
|
|
2633
|
+
const { snapshotWorkspaceHeadsPromise } = await import("./git-utils-CsKV0Dis.js");
|
|
2611
2634
|
fallbackHead = await snapshotWorkspaceHeadsPromise(state.issueId, state.workspace);
|
|
2612
2635
|
} catch {}
|
|
2613
2636
|
const notes = synthesis.topBlocker || "Review approved by Deacon fallback from completed reviewer reports";
|
|
2614
|
-
const { recordReviewVerdict } = await import("./review-verdict-writer-
|
|
2637
|
+
const { recordReviewVerdict } = await import("./review-verdict-writer-CD-4kDVx.js");
|
|
2615
2638
|
const outcome = await recordReviewVerdict(state.issueId, {
|
|
2616
2639
|
verdict: synthesis.verdict,
|
|
2617
2640
|
notes,
|
|
@@ -2652,7 +2675,7 @@ async function nudgeSynthesisForCompleteReviewerReports(states) {
|
|
|
2652
2675
|
"Read the four reports, write synthesis.md, then signal review status exactly as roles/review.md instructs."
|
|
2653
2676
|
].join("\n");
|
|
2654
2677
|
try {
|
|
2655
|
-
const { messageAgent } = await import("./agents-
|
|
2678
|
+
const { messageAgent } = await import("./agents-DpCuoF65.js");
|
|
2656
2679
|
await messageAgent(state.id, message);
|
|
2657
2680
|
reviewReportsPresentNudges.set(nudgeKey, now);
|
|
2658
2681
|
const action = `Nudged ${state.id} to synthesize from ${REVIEW_SUB_ROLES.length} reviewer reports`;
|
|
@@ -2764,7 +2787,7 @@ async function monitorReviewConvoySignals() {
|
|
|
2764
2787
|
if (!signal) continue;
|
|
2765
2788
|
const message = signal === "ready" ? `REVIEWER_READY ${state.reviewSubRole} ${outputPath}` : signal === "timeout" ? `REVIEWER_TIMEOUT ${state.reviewSubRole} ${reason}` : `REVIEWER_FAILED ${state.reviewSubRole} ${reason}`;
|
|
2766
2789
|
try {
|
|
2767
|
-
const { messageAgent } = await import("./agents-
|
|
2790
|
+
const { messageAgent } = await import("./agents-DpCuoF65.js");
|
|
2768
2791
|
await messageAgent(state.reviewSynthesisAgentId, message);
|
|
2769
2792
|
state.reviewMonitorSignaled = signal;
|
|
2770
2793
|
saveAgentStateSync(state);
|
|
@@ -2772,7 +2795,7 @@ async function monitorReviewConvoySignals() {
|
|
|
2772
2795
|
actions.push(action);
|
|
2773
2796
|
logDeaconEventSync(`monitorReviewConvoySignals: ${action}`);
|
|
2774
2797
|
if (signal === "ready") try {
|
|
2775
|
-
const { notifyPipelineSync } = await import("./pipeline-notifier-
|
|
2798
|
+
const { notifyPipelineSync } = await import("./pipeline-notifier-DvHLaXKb.js");
|
|
2776
2799
|
notifyPipelineSync({
|
|
2777
2800
|
type: "reviewer_completed",
|
|
2778
2801
|
issueId: state.issueId,
|
|
@@ -2780,7 +2803,7 @@ async function monitorReviewConvoySignals() {
|
|
|
2780
2803
|
});
|
|
2781
2804
|
} catch {}
|
|
2782
2805
|
else if (signal === "timeout") try {
|
|
2783
|
-
const { notifyPipelineSync } = await import("./pipeline-notifier-
|
|
2806
|
+
const { notifyPipelineSync } = await import("./pipeline-notifier-DvHLaXKb.js");
|
|
2784
2807
|
notifyPipelineSync({
|
|
2785
2808
|
type: "reviewer_timed_out",
|
|
2786
2809
|
issueId: state.issueId,
|
|
@@ -2889,8 +2912,8 @@ async function cleanupOrphanedReviewSessions() {
|
|
|
2889
2912
|
async function checkStalledReviewParents() {
|
|
2890
2913
|
const actions = [];
|
|
2891
2914
|
try {
|
|
2892
|
-
const { listAgentStates } = await import("./agents-
|
|
2893
|
-
const { PARENT_REVIEW_TIMEOUT_MS } = await import("./review-agent-
|
|
2915
|
+
const { listAgentStates } = await import("./agents-DpCuoF65.js");
|
|
2916
|
+
const { PARENT_REVIEW_TIMEOUT_MS } = await import("./review-agent-DgkUPRx0.js");
|
|
2894
2917
|
const states = listAgentStates();
|
|
2895
2918
|
const now = Date.now();
|
|
2896
2919
|
for (const state of states) {
|
|
@@ -2914,7 +2937,7 @@ async function checkStalledReviewParents() {
|
|
|
2914
2937
|
paneTail: paneTail.split("\n").filter(Boolean).slice(-5).join(" | ").slice(0, 400)
|
|
2915
2938
|
};
|
|
2916
2939
|
try {
|
|
2917
|
-
const { markWorkspaceStuck } = await import("./review-status-
|
|
2940
|
+
const { markWorkspaceStuck } = await import("./review-status-C_kQeeX0.js");
|
|
2918
2941
|
markWorkspaceStuck(state.issueId, "review_parent_stalled_needs_you", {
|
|
2919
2942
|
reason: `Review parent ${state.id} is past its review deadline with no verdict on record`,
|
|
2920
2943
|
...evidence
|
|
@@ -2926,7 +2949,7 @@ async function checkStalledReviewParents() {
|
|
|
2926
2949
|
actions.push(message);
|
|
2927
2950
|
logDeaconEventSync(message);
|
|
2928
2951
|
try {
|
|
2929
|
-
const { emitActivityEntrySync } = await import("./activity-logger-
|
|
2952
|
+
const { emitActivityEntrySync } = await import("./activity-logger-Ck8qoluj.js");
|
|
2930
2953
|
emitActivityEntrySync({
|
|
2931
2954
|
source: "review",
|
|
2932
2955
|
level: "warn",
|
|
@@ -2957,9 +2980,1604 @@ function hasTerminalVerdictOfRecord(state) {
|
|
|
2957
2980
|
}
|
|
2958
2981
|
}
|
|
2959
2982
|
//#endregion
|
|
2960
|
-
//#region ../../lib/
|
|
2961
|
-
init_control_settings();
|
|
2983
|
+
//#region ../../lib/agents/slot-merge.ts
|
|
2962
2984
|
init_traefik_connect();
|
|
2985
|
+
const execAsync$5 = promisify(exec);
|
|
2986
|
+
async function verifyAndMergeSlot(issue, slotIndex, item, options = {}) {
|
|
2987
|
+
const issueId = typeof issue === "string" ? issue : issue.issueId;
|
|
2988
|
+
const featureWorkspace = typeof issue === "string" ? defaultRunWorkspace(issue) : issue.featureWorkspace ?? defaultRunWorkspace(issue.issueId);
|
|
2989
|
+
const slotWorkspace = typeof issue === "string" ? `${featureWorkspace}-slot-${slotIndex}` : issue.slotWorkspace ?? `${featureWorkspace}-slot-${slotIndex}`;
|
|
2990
|
+
const slotBranch = typeof issue === "string" ? `feature/${issueId.toLowerCase()}-slot-${slotIndex}` : issue.slotBranch ?? `feature/${issueId.toLowerCase()}-slot-${slotIndex}`;
|
|
2991
|
+
const deps = {
|
|
2992
|
+
run: async (command, cwd) => execAsync$5(command, { cwd }),
|
|
2993
|
+
resolveRepoRoots: resolveWorkspaceRepoRootsSync,
|
|
2994
|
+
...options.deps
|
|
2995
|
+
};
|
|
2996
|
+
const verifyCommands = item.metadata?.verify_commands ?? [];
|
|
2997
|
+
const expectedOutputs = item.metadata?.expected_outputs ?? [];
|
|
2998
|
+
const evidence = {
|
|
2999
|
+
verifyCommands,
|
|
3000
|
+
expectedOutputs,
|
|
3001
|
+
commandOutputs: []
|
|
3002
|
+
};
|
|
3003
|
+
if (!Number.isInteger(slotIndex) || slotIndex < 1) return {
|
|
3004
|
+
verified: false,
|
|
3005
|
+
merged: false,
|
|
3006
|
+
conflicts: false,
|
|
3007
|
+
evidence,
|
|
3008
|
+
failure: `Invalid slot index: ${slotIndex}`
|
|
3009
|
+
};
|
|
3010
|
+
if (verifyCommands.length === 0) return {
|
|
3011
|
+
verified: false,
|
|
3012
|
+
merged: false,
|
|
3013
|
+
conflicts: false,
|
|
3014
|
+
evidence,
|
|
3015
|
+
failure: `Item ${item.id} has no verify_commands`
|
|
3016
|
+
};
|
|
3017
|
+
if (expectedOutputs.length === 0) return {
|
|
3018
|
+
verified: false,
|
|
3019
|
+
merged: false,
|
|
3020
|
+
conflicts: false,
|
|
3021
|
+
evidence,
|
|
3022
|
+
failure: `Item ${item.id} has no expected_outputs`
|
|
3023
|
+
};
|
|
3024
|
+
for (const command of verifyCommands) try {
|
|
3025
|
+
const output = await deps.run(command, slotWorkspace);
|
|
3026
|
+
evidence.commandOutputs.push({
|
|
3027
|
+
command,
|
|
3028
|
+
stdout: output.stdout,
|
|
3029
|
+
stderr: output.stderr
|
|
3030
|
+
});
|
|
3031
|
+
} catch (error) {
|
|
3032
|
+
const failed = commandFailure(error);
|
|
3033
|
+
evidence.commandOutputs.push({
|
|
3034
|
+
command,
|
|
3035
|
+
stdout: failed.stdout,
|
|
3036
|
+
stderr: failed.stderr
|
|
3037
|
+
});
|
|
3038
|
+
return {
|
|
3039
|
+
verified: false,
|
|
3040
|
+
merged: false,
|
|
3041
|
+
conflicts: false,
|
|
3042
|
+
evidence,
|
|
3043
|
+
failure: `Verify command failed for ${item.id}: ${command}`
|
|
3044
|
+
};
|
|
3045
|
+
}
|
|
3046
|
+
const slotRoots = deps.resolveRepoRoots(issueId, slotWorkspace);
|
|
3047
|
+
if (slotRoots.some((root) => root.degradedPolyrepo)) return {
|
|
3048
|
+
verified: true,
|
|
3049
|
+
merged: false,
|
|
3050
|
+
conflicts: false,
|
|
3051
|
+
evidence,
|
|
3052
|
+
failure: `Could not resolve polyrepo roots for slot ${slotIndex} — refusing to infer a merge`
|
|
3053
|
+
};
|
|
3054
|
+
const aheadByRepo = /* @__PURE__ */ new Map();
|
|
3055
|
+
for (const root of slotRoots) aheadByRepo.set(root.repoKey, await aheadCount(deps.run, root.dir, root.sourceBranch, slotBranch));
|
|
3056
|
+
const rootsWithWork = slotRoots.filter((root) => (aheadByRepo.get(root.repoKey) ?? 0) > 0);
|
|
3057
|
+
if (rootsWithWork.length === 0) return {
|
|
3058
|
+
verified: true,
|
|
3059
|
+
merged: false,
|
|
3060
|
+
conflicts: false,
|
|
3061
|
+
evidence,
|
|
3062
|
+
failure: `Slot branch ${slotBranch} has no unmerged current-item changes in any repo — refusing to mark item ${item.id} merged`
|
|
3063
|
+
};
|
|
3064
|
+
if (!slotRoots.some((root) => root.isPolyrepo)) try {
|
|
3065
|
+
evidence.mergeOutput = await deps.run(`git merge --no-ff ${JSON.stringify(slotBranch)}`, featureWorkspace);
|
|
3066
|
+
return {
|
|
3067
|
+
verified: true,
|
|
3068
|
+
merged: true,
|
|
3069
|
+
conflicts: false,
|
|
3070
|
+
evidence
|
|
3071
|
+
};
|
|
3072
|
+
} catch (error) {
|
|
3073
|
+
const failed = commandFailure(error);
|
|
3074
|
+
evidence.mergeOutput = {
|
|
3075
|
+
stdout: failed.stdout,
|
|
3076
|
+
stderr: failed.stderr
|
|
3077
|
+
};
|
|
3078
|
+
await deps.run("git merge --abort", featureWorkspace).catch(() => {});
|
|
3079
|
+
return {
|
|
3080
|
+
verified: true,
|
|
3081
|
+
merged: false,
|
|
3082
|
+
conflicts: true,
|
|
3083
|
+
evidence,
|
|
3084
|
+
failure: `Slot branch ${slotBranch} did not merge cleanly`
|
|
3085
|
+
};
|
|
3086
|
+
}
|
|
3087
|
+
const baseRoots = new Map(deps.resolveRepoRoots(issueId, featureWorkspace).map((root) => [root.repoKey, root]));
|
|
3088
|
+
const repoMergeOutputs = [];
|
|
3089
|
+
for (const root of rootsWithWork) {
|
|
3090
|
+
const baseRoot = baseRoots.get(root.repoKey);
|
|
3091
|
+
if (!baseRoot) return {
|
|
3092
|
+
verified: true,
|
|
3093
|
+
merged: false,
|
|
3094
|
+
conflicts: false,
|
|
3095
|
+
evidence: {
|
|
3096
|
+
...evidence,
|
|
3097
|
+
repoMergeOutputs
|
|
3098
|
+
},
|
|
3099
|
+
failure: `No base feature-workspace checkout for repo ${root.repoKey} — cannot integrate slot ${slotIndex}`
|
|
3100
|
+
};
|
|
3101
|
+
try {
|
|
3102
|
+
const output = await deps.run(`git merge --no-ff ${JSON.stringify(slotBranch)}`, baseRoot.dir);
|
|
3103
|
+
repoMergeOutputs.push({
|
|
3104
|
+
repoKey: root.repoKey,
|
|
3105
|
+
stdout: output.stdout,
|
|
3106
|
+
stderr: output.stderr
|
|
3107
|
+
});
|
|
3108
|
+
} catch (error) {
|
|
3109
|
+
const failed = commandFailure(error);
|
|
3110
|
+
repoMergeOutputs.push({
|
|
3111
|
+
repoKey: root.repoKey,
|
|
3112
|
+
stdout: failed.stdout,
|
|
3113
|
+
stderr: failed.stderr
|
|
3114
|
+
});
|
|
3115
|
+
await deps.run("git merge --abort", baseRoot.dir).catch(() => {});
|
|
3116
|
+
return {
|
|
3117
|
+
verified: true,
|
|
3118
|
+
merged: false,
|
|
3119
|
+
conflicts: true,
|
|
3120
|
+
evidence: {
|
|
3121
|
+
...evidence,
|
|
3122
|
+
repoMergeOutputs
|
|
3123
|
+
},
|
|
3124
|
+
failure: `Slot branch ${slotBranch} did not merge cleanly into ${root.repoKey}`
|
|
3125
|
+
};
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
return {
|
|
3129
|
+
verified: true,
|
|
3130
|
+
merged: true,
|
|
3131
|
+
conflicts: false,
|
|
3132
|
+
evidence: {
|
|
3133
|
+
...evidence,
|
|
3134
|
+
repoMergeOutputs
|
|
3135
|
+
}
|
|
3136
|
+
};
|
|
3137
|
+
}
|
|
3138
|
+
/** Count base..branch commits in one repo; a missing branch or repo means zero. */
|
|
3139
|
+
async function aheadCount(run, cwd, baseBranch, branch) {
|
|
3140
|
+
try {
|
|
3141
|
+
const { stdout } = await run(`git rev-list --count ${JSON.stringify(baseBranch)}..${JSON.stringify(branch)}`, cwd);
|
|
3142
|
+
const count = Number(stdout.trim());
|
|
3143
|
+
return Number.isFinite(count) && count > 0 ? count : 0;
|
|
3144
|
+
} catch {
|
|
3145
|
+
return 0;
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
3148
|
+
function commandFailure(error) {
|
|
3149
|
+
const partial = error;
|
|
3150
|
+
return {
|
|
3151
|
+
stdout: typeof partial.stdout === "string" ? partial.stdout : "",
|
|
3152
|
+
stderr: typeof partial.stderr === "string" ? partial.stderr : typeof partial.message === "string" ? partial.message : String(error)
|
|
3153
|
+
};
|
|
3154
|
+
}
|
|
3155
|
+
//#endregion
|
|
3156
|
+
//#region ../../lib/pan-dir/task-door.ts
|
|
3157
|
+
/** Single task-mutation write door over the immutable xBRIEF specification. */
|
|
3158
|
+
var TaskStatusChangeError = class extends Error {
|
|
3159
|
+
constructor(message, details) {
|
|
3160
|
+
super(message);
|
|
3161
|
+
this.details = details;
|
|
3162
|
+
this.name = "TaskStatusChangeError";
|
|
3163
|
+
}
|
|
3164
|
+
};
|
|
3165
|
+
const TERMINAL = new Set(["completed", "cancelled"]);
|
|
3166
|
+
function statusFor(type) {
|
|
3167
|
+
if (type === "claim") return "running";
|
|
3168
|
+
if (type === "done") return "completed";
|
|
3169
|
+
if (type === "block") return "blocked";
|
|
3170
|
+
if (type === "unblock") return "pending";
|
|
3171
|
+
if (type === "reopen") return "pending";
|
|
3172
|
+
return "cancelled";
|
|
3173
|
+
}
|
|
3174
|
+
function subItemOverrideKey(itemId, subItemId) {
|
|
3175
|
+
return subItemId.includes(".") ? subItemId : `${itemId}.${subItemId}`;
|
|
3176
|
+
}
|
|
3177
|
+
function allowedFrom(type, status) {
|
|
3178
|
+
if (type === "claim") return status === "pending";
|
|
3179
|
+
if (type === "done") return status === "running";
|
|
3180
|
+
if (type === "block") return status === "pending" || status === "running";
|
|
3181
|
+
if (type === "unblock") return status === "blocked";
|
|
3182
|
+
if (type === "reopen") return status === "completed";
|
|
3183
|
+
return status === "pending" || status === "running" || status === "blocked";
|
|
3184
|
+
}
|
|
3185
|
+
function taskError(issueId, itemId, operation, currentStatus, owner, extra) {
|
|
3186
|
+
const ownerText = owner ? ` The current claim owner is ${owner}.` : "";
|
|
3187
|
+
const recovery = `Run \`pan task show ${issueId} ${itemId}\` to inspect the current task state before retrying.`;
|
|
3188
|
+
return new TaskStatusChangeError(`Task ${itemId} on ${issueId} is ${currentStatus}, so ${operation} is not allowed.${ownerText}${extra ? ` ${extra}` : ""} ${recovery}`, {
|
|
3189
|
+
issueId,
|
|
3190
|
+
itemId,
|
|
3191
|
+
operation,
|
|
3192
|
+
currentStatus,
|
|
3193
|
+
owner
|
|
3194
|
+
});
|
|
3195
|
+
}
|
|
3196
|
+
function archiveClaim(record, itemId, outcome, reason, forced, now) {
|
|
3197
|
+
const claim = record.tasks?.claims[itemId];
|
|
3198
|
+
if (!claim || !record.tasks) return;
|
|
3199
|
+
const entry = {
|
|
3200
|
+
...claim,
|
|
3201
|
+
itemId,
|
|
3202
|
+
outcome,
|
|
3203
|
+
reason,
|
|
3204
|
+
forced: forced || void 0,
|
|
3205
|
+
releasedAt: now
|
|
3206
|
+
};
|
|
3207
|
+
record.tasks.claimHistory = [...record.tasks.claimHistory ?? [], entry].slice(-50);
|
|
3208
|
+
delete record.tasks.claims[itemId];
|
|
3209
|
+
}
|
|
3210
|
+
async function applyTaskStatusChange(project, issueId, operation) {
|
|
3211
|
+
const normalizedIssueId = issueId.toUpperCase();
|
|
3212
|
+
const writerId = operation.writerId ?? process.env.OVERDECK_AGENT_ID ?? `cli-${process.pid}@${hostname()}`;
|
|
3213
|
+
let result;
|
|
3214
|
+
await updateIssueRecord(project, normalizedIssueId, (record) => {
|
|
3215
|
+
const planPath = findPlanSync(join(project.path, "workspaces", `feature-${normalizedIssueId.toLowerCase()}`));
|
|
3216
|
+
if (!planPath) throw new Error(`The xBRIEF for ${normalizedIssueId} is missing or unreadable. Return the issue to planning before changing task state.`);
|
|
3217
|
+
const item = applyStatusOverrides(readPlanSync(planPath), record.statusOverrides ?? {}).plan.items.find(({ id }) => id === operation.itemId);
|
|
3218
|
+
if (!item) throw new Error(`Task ${operation.itemId} does not exist in the immutable xBRIEF for ${normalizedIssueId}. Return the issue to planning to change scope.`);
|
|
3219
|
+
const currentStatus = item.status;
|
|
3220
|
+
const currentSequence = record.tasks?.sequence ?? 0;
|
|
3221
|
+
const existingClaim = record.tasks?.claims[operation.itemId];
|
|
3222
|
+
const nextStatus = statusFor(operation.type);
|
|
3223
|
+
const subItems = subItemsOf(item);
|
|
3224
|
+
const subIds = operation.subItemIds?.length ? new Set(operation.subItemIds) : new Set(operation.type === "done" || operation.type === "cancel" || operation.type === "reopen" ? subItems.map(({ id }) => id) : []);
|
|
3225
|
+
const repeatedCancellation = operation.type === "cancel" && currentStatus === "cancelled";
|
|
3226
|
+
if (operation.expectedSequence !== void 0 && operation.expectedSequence !== currentSequence) throw new TaskStatusChangeError(`Task sequence conflict for ${normalizedIssueId}: expected ${operation.expectedSequence}, but the current sequence is ${currentSequence}. Run \`pan task next ${normalizedIssueId}\` and retry with the new sequence.`, {
|
|
3227
|
+
issueId: normalizedIssueId,
|
|
3228
|
+
itemId: operation.itemId,
|
|
3229
|
+
operation: operation.type,
|
|
3230
|
+
currentStatus,
|
|
3231
|
+
currentSequence
|
|
3232
|
+
});
|
|
3233
|
+
if (TERMINAL.has(currentStatus) && !repeatedCancellation && operation.type !== "reopen") throw taskError(normalizedIssueId, operation.itemId, operation.type, currentStatus, existingClaim?.writerId);
|
|
3234
|
+
if (operation.force && !operation.reason) throw taskError(normalizedIssueId, operation.itemId, operation.type, currentStatus, existingClaim?.writerId, "A forced transition requires --reason.");
|
|
3235
|
+
if ((operation.type === "block" || operation.type === "cancel" || operation.type === "reopen") && !operation.reason) throw taskError(normalizedIssueId, operation.itemId, operation.type, currentStatus, existingClaim?.writerId, "This transition requires --reason.");
|
|
3236
|
+
if (repeatedCancellation && subItems.every((subItem) => subItem.status === "cancelled")) {
|
|
3237
|
+
result = {
|
|
3238
|
+
issueId: normalizedIssueId,
|
|
3239
|
+
itemId: operation.itemId,
|
|
3240
|
+
status: "cancelled",
|
|
3241
|
+
sequence: currentSequence,
|
|
3242
|
+
idempotent: true
|
|
3243
|
+
};
|
|
3244
|
+
return record;
|
|
3245
|
+
}
|
|
3246
|
+
if (operation.type === "claim" && currentStatus === "running" && existingClaim?.writerId === writerId) {
|
|
3247
|
+
result = {
|
|
3248
|
+
issueId: normalizedIssueId,
|
|
3249
|
+
itemId: operation.itemId,
|
|
3250
|
+
status: "running",
|
|
3251
|
+
sequence: currentSequence,
|
|
3252
|
+
claim: existingClaim,
|
|
3253
|
+
idempotent: true
|
|
3254
|
+
};
|
|
3255
|
+
return record;
|
|
3256
|
+
}
|
|
3257
|
+
const ownerRestricted = (operation.type === "done" || operation.type === "block" || operation.type === "cancel") && currentStatus === "running";
|
|
3258
|
+
if (!operation.force && ownerRestricted && existingClaim?.writerId !== writerId) throw taskError(normalizedIssueId, operation.itemId, operation.type, currentStatus, existingClaim?.writerId ?? "missing claim owner");
|
|
3259
|
+
if (!operation.force && !repeatedCancellation && !allowedFrom(operation.type, currentStatus)) throw taskError(normalizedIssueId, operation.itemId, operation.type, currentStatus, existingClaim?.writerId);
|
|
3260
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3261
|
+
record.tasks ??= {
|
|
3262
|
+
sequence: 0,
|
|
3263
|
+
claims: {}
|
|
3264
|
+
};
|
|
3265
|
+
record.statusOverrides = {
|
|
3266
|
+
...record.statusOverrides ?? {},
|
|
3267
|
+
[operation.itemId]: nextStatus
|
|
3268
|
+
};
|
|
3269
|
+
for (const subId of subIds) record.statusOverrides[subItemOverrideKey(operation.itemId, subId)] = nextStatus;
|
|
3270
|
+
if (operation.type === "claim") record.tasks.claims[operation.itemId] = {
|
|
3271
|
+
writerId,
|
|
3272
|
+
agentId: process.env.OVERDECK_AGENT_ID ?? null,
|
|
3273
|
+
pid: process.pid,
|
|
3274
|
+
host: hostname(),
|
|
3275
|
+
claimedAt: now
|
|
3276
|
+
};
|
|
3277
|
+
else if (existingClaim) archiveClaim(record, operation.itemId, nextStatus === "completed" ? "completed" : nextStatus === "blocked" ? "blocked" : "cancelled", operation.reason, operation.force ?? false, now);
|
|
3278
|
+
if (operation.reason) record.tasks.statusReasons = {
|
|
3279
|
+
...record.tasks.statusReasons ?? {},
|
|
3280
|
+
[operation.itemId]: {
|
|
3281
|
+
reason: operation.reason,
|
|
3282
|
+
updatedAt: now,
|
|
3283
|
+
forced: operation.force || void 0
|
|
3284
|
+
}
|
|
3285
|
+
};
|
|
3286
|
+
record.tasks.sequence = currentSequence + 1;
|
|
3287
|
+
result = {
|
|
3288
|
+
issueId: normalizedIssueId,
|
|
3289
|
+
itemId: operation.itemId,
|
|
3290
|
+
status: nextStatus,
|
|
3291
|
+
sequence: record.tasks.sequence,
|
|
3292
|
+
claim: record.tasks.claims[operation.itemId]
|
|
3293
|
+
};
|
|
3294
|
+
return record;
|
|
3295
|
+
}, { writerId });
|
|
3296
|
+
if (!result) throw new Error(`Task mutation for ${normalizedIssueId}/${operation.itemId} produced no result.`);
|
|
3297
|
+
return result;
|
|
3298
|
+
}
|
|
3299
|
+
//#endregion
|
|
3300
|
+
//#region ../../lib/swarm-policy.ts
|
|
3301
|
+
init_projects();
|
|
3302
|
+
function resolveSwarmPolicyLayers(global = {}, project = {}, issue = {}, cli = {}) {
|
|
3303
|
+
const resolve = (key, fallback) => {
|
|
3304
|
+
for (const [source, layer] of [
|
|
3305
|
+
["cli", cli],
|
|
3306
|
+
["issue", issue],
|
|
3307
|
+
["project", project],
|
|
3308
|
+
["global", global]
|
|
3309
|
+
]) if (layer[key] !== void 0) return {
|
|
3310
|
+
value: layer[key],
|
|
3311
|
+
source
|
|
3312
|
+
};
|
|
3313
|
+
return {
|
|
3314
|
+
value: fallback,
|
|
3315
|
+
source: "default"
|
|
3316
|
+
};
|
|
3317
|
+
};
|
|
3318
|
+
const mode = resolve("mode", "off");
|
|
3319
|
+
const maxSlots = resolve("maxSlots", 3);
|
|
3320
|
+
const autoAdvance = resolve("autoAdvance", true);
|
|
3321
|
+
return {
|
|
3322
|
+
mode: mode.value,
|
|
3323
|
+
maxSlots: maxSlots.value,
|
|
3324
|
+
autoAdvance: autoAdvance.value,
|
|
3325
|
+
source: {
|
|
3326
|
+
mode: mode.source,
|
|
3327
|
+
maxSlots: maxSlots.source,
|
|
3328
|
+
autoAdvance: autoAdvance.source
|
|
3329
|
+
}
|
|
3330
|
+
};
|
|
3331
|
+
}
|
|
3332
|
+
function resolveSwarmPolicy(issueId, cli = {}) {
|
|
3333
|
+
const global = loadConfigSync().config.swarm;
|
|
3334
|
+
if (!issueId) return resolveSwarmPolicyLayers(global, {}, {}, cli);
|
|
3335
|
+
const resolved = resolveProjectFromIssueSync(issueId);
|
|
3336
|
+
const project = resolved ? getProjectSync(resolved.projectKey) : void 0;
|
|
3337
|
+
const issue = project ? readIssueRecordSync(project, issueId)?.swarm?.policy : void 0;
|
|
3338
|
+
return resolveSwarmPolicyLayers(global, project?.swarm, issue, cli);
|
|
3339
|
+
}
|
|
3340
|
+
function resolveAutomaticSwarmPolicy(issueId, swarmEligible, manual = false) {
|
|
3341
|
+
const policy = resolveSwarmPolicy(issueId);
|
|
3342
|
+
return {
|
|
3343
|
+
policy,
|
|
3344
|
+
spawnForeman: manual || policy.mode !== "off" && swarmEligible,
|
|
3345
|
+
requireSwarmReadiness: policy.mode === "always",
|
|
3346
|
+
advanceWavesWithoutConfirmation: policy.autoAdvance,
|
|
3347
|
+
reason: manual ? "manual" : policy.mode === "off" ? "mode-off" : swarmEligible ? "eligible" : "not-ready"
|
|
3348
|
+
};
|
|
3349
|
+
}
|
|
3350
|
+
//#endregion
|
|
3351
|
+
//#region ../../lib/cloister/deacon-workspaces.ts
|
|
3352
|
+
init_projects();
|
|
3353
|
+
function listFeatureWorkspaces(options = {}) {
|
|
3354
|
+
const includeSlotWorkspaces = options.includeSlotWorkspaces ?? true;
|
|
3355
|
+
const projects = listProjectsSync();
|
|
3356
|
+
const workspaces = [];
|
|
3357
|
+
for (const { config: projectConfig } of projects) {
|
|
3358
|
+
const workspacesRoot = join$1(projectConfig.path, "workspaces");
|
|
3359
|
+
if (!existsSync(workspacesRoot)) continue;
|
|
3360
|
+
let entries;
|
|
3361
|
+
try {
|
|
3362
|
+
entries = readdirSync(workspacesRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("feature-")).filter((e) => includeSlotWorkspaces || !/-slot-\d+$/.test(e.name)).map((e) => e.name);
|
|
3363
|
+
} catch {
|
|
3364
|
+
continue;
|
|
3365
|
+
}
|
|
3366
|
+
for (const entry of entries) workspaces.push({
|
|
3367
|
+
issueId: entry.replace(/^feature-/, "").toUpperCase(),
|
|
3368
|
+
workspacePath: join$1(workspacesRoot, entry),
|
|
3369
|
+
projectPath: projectConfig.path
|
|
3370
|
+
});
|
|
3371
|
+
}
|
|
3372
|
+
return workspaces;
|
|
3373
|
+
}
|
|
3374
|
+
//#endregion
|
|
3375
|
+
//#region ../../lib/cloister/deacon-swarm-orphan-gc.ts
|
|
3376
|
+
/**
|
|
3377
|
+
* Reconcile GC for truly-orphaned slots (PAN-2214, completes PAN-2213(c)).
|
|
3378
|
+
*
|
|
3379
|
+
* An orphan is a slot index with an on-disk worktree or a local slot branch but
|
|
3380
|
+
* no slotAssignments entry and no live agent tmux session. Zero-commit-ahead
|
|
3381
|
+
* orphans are removed; orphans with unmerged commits are only reported so the
|
|
3382
|
+
* operator can push them via `pan swarm reset` — never deleted.
|
|
3383
|
+
*/
|
|
3384
|
+
async function gcOrphanedSlots(issueId, workspacePath, reconciled, deps) {
|
|
3385
|
+
const actions = [];
|
|
3386
|
+
const issueLower = issueId.toLowerCase();
|
|
3387
|
+
const worktreeSlotIndexes = await listSlotWorktreeIndexes(workspacePath, deps.runGitCommand);
|
|
3388
|
+
const branchesBySlot = new Map(reconciled.branches.map((branch) => [branch.slotIndex, branch.branch]));
|
|
3389
|
+
const candidateSlotIndexes = [...new Set([...worktreeSlotIndexes, ...branchesBySlot.keys()])].sort((a, b) => a - b);
|
|
3390
|
+
if (candidateSlotIndexes.length === 0) return actions;
|
|
3391
|
+
const ownedSlotIndexes = new Set([
|
|
3392
|
+
...(deps.listSlotAssignments ?? listSlotAssignments)(issueId, workspacePath).map((assignment) => assignment.slotIndex),
|
|
3393
|
+
...reconciled.merged.map((slot) => slot.slotIndex),
|
|
3394
|
+
...reconciled.inFlight.map((slot) => slot.slotIndex)
|
|
3395
|
+
]);
|
|
3396
|
+
const sessionNames = new Set(await deps.listSessionNames());
|
|
3397
|
+
for (const slotIndex of candidateSlotIndexes) {
|
|
3398
|
+
if (ownedSlotIndexes.has(slotIndex)) continue;
|
|
3399
|
+
if (sessionNames.has(`agent-${issueLower}-slot-${slotIndex}`)) continue;
|
|
3400
|
+
const branch = branchesBySlot.get(slotIndex);
|
|
3401
|
+
const aheadCount = branch ? await countCommitsAhead$1(workspacePath, branch, deps.runGitCommand) : 0;
|
|
3402
|
+
if (aheadCount === null || aheadCount > 0) {
|
|
3403
|
+
actions.push(`[swarm] orphan slot ${slotIndex} for ${issueId} preserved: ${branch} has ${aheadCount ?? "an unknown number of"} unmerged commit(s) — run \`pan swarm reset ${issueId}\` to push it to origin before cleanup`);
|
|
3404
|
+
continue;
|
|
3405
|
+
}
|
|
3406
|
+
if (worktreeSlotIndexes.has(slotIndex)) await deps.runGitCommand(`git worktree remove --force ${JSON.stringify(`${workspacePath}-slot-${slotIndex}`)}`, workspacePath);
|
|
3407
|
+
if (branch) await deps.runGitCommand(`git branch -D ${JSON.stringify(branch)}`, workspacePath);
|
|
3408
|
+
actions.push(`[swarm] gc-orphan slot ${slotIndex} for ${issueId}`);
|
|
3409
|
+
}
|
|
3410
|
+
return actions;
|
|
3411
|
+
}
|
|
3412
|
+
async function listSlotWorktreeIndexes(workspacePath, runGitCommand) {
|
|
3413
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
3414
|
+
try {
|
|
3415
|
+
const result = await runGitCommand("git worktree list --porcelain", workspacePath);
|
|
3416
|
+
const pattern = new RegExp(`^worktree ${escapeRegExp(workspacePath)}-slot-(\\d+)$`);
|
|
3417
|
+
for (const line of String(result?.stdout ?? "").split("\n")) {
|
|
3418
|
+
const match = pattern.exec(line.trim());
|
|
3419
|
+
if (match) indexes.add(Number(match[1]));
|
|
3420
|
+
}
|
|
3421
|
+
} catch {}
|
|
3422
|
+
return indexes;
|
|
3423
|
+
}
|
|
3424
|
+
async function countCommitsAhead$1(workspacePath, branch, runGitCommand) {
|
|
3425
|
+
try {
|
|
3426
|
+
const result = await runGitCommand(`git rev-list --count HEAD..${JSON.stringify(branch)}`, workspacePath);
|
|
3427
|
+
const count = Number(String(result?.stdout ?? "").trim());
|
|
3428
|
+
return Number.isFinite(count) ? count : null;
|
|
3429
|
+
} catch {
|
|
3430
|
+
return null;
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
3433
|
+
function escapeRegExp(value) {
|
|
3434
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3435
|
+
}
|
|
3436
|
+
//#endregion
|
|
3437
|
+
//#region ../../lib/cloister/deacon-swarm-record.ts
|
|
3438
|
+
init_record();
|
|
3439
|
+
init_record_update();
|
|
3440
|
+
async function writeSwarmFinalizedAt(workspacePath, issueId, finalizedAt) {
|
|
3441
|
+
await updateIssueRecordForWorkspace(workspacePath, issueId.toUpperCase(), (record) => ({
|
|
3442
|
+
...record,
|
|
3443
|
+
swarm: {
|
|
3444
|
+
...record.swarm ?? {},
|
|
3445
|
+
finalizedAt
|
|
3446
|
+
}
|
|
3447
|
+
}));
|
|
3448
|
+
}
|
|
3449
|
+
function readSwarmHold(workspacePath, issueId) {
|
|
3450
|
+
return readIssueRecordForWorkspaceSync(workspacePath, issueId.toUpperCase())?.swarm?.hold;
|
|
3451
|
+
}
|
|
3452
|
+
async function writeSwarmHold(workspacePath, issueId, hold) {
|
|
3453
|
+
await updateIssueRecordForWorkspace(workspacePath, issueId.toUpperCase(), (record) => ({
|
|
3454
|
+
...record,
|
|
3455
|
+
swarm: {
|
|
3456
|
+
...record.swarm ?? {},
|
|
3457
|
+
hold
|
|
3458
|
+
}
|
|
3459
|
+
}));
|
|
3460
|
+
}
|
|
3461
|
+
function readSwarmCompletionObservation(workspacePath, issueId, progressKey) {
|
|
3462
|
+
return readIssueRecordForWorkspaceSync(workspacePath, issueId.toUpperCase())?.swarm?.completionObservations?.[progressKey];
|
|
3463
|
+
}
|
|
3464
|
+
async function writeSwarmCompletionObservation(workspacePath, issueId, progressKey, observation) {
|
|
3465
|
+
await updateIssueRecordForWorkspace(workspacePath, issueId.toUpperCase(), (record) => ({
|
|
3466
|
+
...record,
|
|
3467
|
+
swarm: {
|
|
3468
|
+
...record.swarm ?? {},
|
|
3469
|
+
completionObservations: {
|
|
3470
|
+
...record.swarm?.completionObservations ?? {},
|
|
3471
|
+
[progressKey]: observation
|
|
3472
|
+
}
|
|
3473
|
+
}
|
|
3474
|
+
}));
|
|
3475
|
+
}
|
|
3476
|
+
async function clearSwarmCompletionObservationRecord(workspacePath, issueId, progressKey) {
|
|
3477
|
+
await updateIssueRecordForWorkspace(workspacePath, issueId.toUpperCase(), (record) => {
|
|
3478
|
+
const existing = record.swarm?.completionObservations;
|
|
3479
|
+
if (!existing?.[progressKey]) return record;
|
|
3480
|
+
const completionObservations = { ...existing };
|
|
3481
|
+
delete completionObservations[progressKey];
|
|
3482
|
+
return {
|
|
3483
|
+
...record,
|
|
3484
|
+
swarm: {
|
|
3485
|
+
...record.swarm ?? {},
|
|
3486
|
+
completionObservations
|
|
3487
|
+
}
|
|
3488
|
+
};
|
|
3489
|
+
});
|
|
3490
|
+
}
|
|
3491
|
+
async function writeSwarmForemanTakeover(workspacePath, issueId, itemId, slotIndex) {
|
|
3492
|
+
await updateIssueRecordForWorkspace(workspacePath, issueId.toUpperCase(), (record) => ({
|
|
3493
|
+
...record,
|
|
3494
|
+
swarm: {
|
|
3495
|
+
...record.swarm ?? {},
|
|
3496
|
+
reclaimedItems: {
|
|
3497
|
+
...record.swarm?.reclaimedItems ?? {},
|
|
3498
|
+
[itemId]: {
|
|
3499
|
+
slotIndex,
|
|
3500
|
+
reclaimedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3501
|
+
}
|
|
3502
|
+
}
|
|
3503
|
+
}
|
|
3504
|
+
}));
|
|
3505
|
+
}
|
|
3506
|
+
/**
|
|
3507
|
+
* PAN-2372 WI-3 / FR-6: clear a slot's completion marker once the coordinator
|
|
3508
|
+
* (WI-4) has consumed it (merge/requeue). No-op when no marker exists for the
|
|
3509
|
+
* slot, so callers can clear unconditionally on the terminal transition.
|
|
3510
|
+
*/
|
|
3511
|
+
async function clearSwarmSlotCompletion(workspacePath, issueId, slotIndex) {
|
|
3512
|
+
const normalizedIssueId = issueId.toUpperCase();
|
|
3513
|
+
const current = readIssueRecordForWorkspaceSync(workspacePath, normalizedIssueId);
|
|
3514
|
+
if (!current?.swarm?.slotCompletions || !(String(slotIndex) in current.swarm.slotCompletions)) return;
|
|
3515
|
+
await updateIssueRecordForWorkspace(workspacePath, normalizedIssueId, (record) => {
|
|
3516
|
+
const existing = record.swarm?.slotCompletions;
|
|
3517
|
+
if (!existing || !(String(slotIndex) in existing)) return record;
|
|
3518
|
+
const next = { ...existing };
|
|
3519
|
+
delete next[String(slotIndex)];
|
|
3520
|
+
return {
|
|
3521
|
+
...record,
|
|
3522
|
+
swarm: {
|
|
3523
|
+
...record.swarm,
|
|
3524
|
+
slotCompletions: next
|
|
3525
|
+
}
|
|
3526
|
+
};
|
|
3527
|
+
});
|
|
3528
|
+
}
|
|
3529
|
+
/**
|
|
3530
|
+
* Consume all durable ownership for a slot in one record update. Merged-slot
|
|
3531
|
+
* GC uses this after it removes the worktree and branch so a completion marker
|
|
3532
|
+
* cannot outlive the assignment and keep the freed index occupied.
|
|
3533
|
+
*/
|
|
3534
|
+
async function clearSwarmSlotOwnership(workspacePath, issueId, slotIndex, _itemId) {
|
|
3535
|
+
await updateIssueRecordForWorkspace(workspacePath, issueId.toUpperCase(), (record) => {
|
|
3536
|
+
const swarm = record.swarm ?? {};
|
|
3537
|
+
const slotAssignments = (swarm.slotAssignments ?? []).filter((assignment) => assignment.slotIndex !== slotIndex);
|
|
3538
|
+
const slotCompletions = { ...swarm.slotCompletions ?? {} };
|
|
3539
|
+
delete slotCompletions[String(slotIndex)];
|
|
3540
|
+
return {
|
|
3541
|
+
...record,
|
|
3542
|
+
swarm: {
|
|
3543
|
+
...swarm,
|
|
3544
|
+
slotAssignments,
|
|
3545
|
+
slotCompletions
|
|
3546
|
+
}
|
|
3547
|
+
};
|
|
3548
|
+
});
|
|
3549
|
+
}
|
|
3550
|
+
async function clearReleasedBlockedSwarmSlot(workspacePath, issueId, slotIndex) {
|
|
3551
|
+
await updateIssueRecordForWorkspace(workspacePath, issueId.toUpperCase(), (record) => {
|
|
3552
|
+
const releasedBlockedSlots = { ...record.swarm?.releasedBlockedSlots ?? {} };
|
|
3553
|
+
delete releasedBlockedSlots[String(slotIndex)];
|
|
3554
|
+
return {
|
|
3555
|
+
...record,
|
|
3556
|
+
swarm: {
|
|
3557
|
+
...record.swarm ?? {},
|
|
3558
|
+
releasedBlockedSlots
|
|
3559
|
+
}
|
|
3560
|
+
};
|
|
3561
|
+
});
|
|
3562
|
+
}
|
|
3563
|
+
//#endregion
|
|
3564
|
+
//#region ../../lib/cloister/deacon-swarm-completion.ts
|
|
3565
|
+
init_messaging();
|
|
3566
|
+
init_project_repos();
|
|
3567
|
+
init_config$1();
|
|
3568
|
+
const execAsync$4 = promisify(exec);
|
|
3569
|
+
async function classifyDoneWithoutSignal(slot, deps, options, observation) {
|
|
3570
|
+
if ((options.inferCompletion ?? "off") === "off" || !slot.agentId || !slot.branch || !options.workspacePath || !options.issueId) {
|
|
3571
|
+
if (options.workspacePath && options.issueId) await (deps.clearCompletionObservation ?? clearSwarmCompletionObservationRecord)(options.workspacePath, options.issueId, observation.progressKey);
|
|
3572
|
+
return null;
|
|
3573
|
+
}
|
|
3574
|
+
const aheadCount = await (deps.getSlotBranchAheadCount ?? defaultGetSlotBranchAheadCount)(options.workspacePath, options.issueId, slot.branch).catch(() => 0);
|
|
3575
|
+
const clean = await (deps.isSlotWorktreeClean ?? defaultIsSlotWorktreeClean)(`${options.workspacePath}-slot-${slot.slotIndex}`).catch(() => false);
|
|
3576
|
+
if (aheadCount < 1 || !clean) {
|
|
3577
|
+
await (deps.clearCompletionObservation ?? clearSwarmCompletionObservationRecord)(options.workspacePath, options.issueId, observation.progressKey);
|
|
3578
|
+
return null;
|
|
3579
|
+
}
|
|
3580
|
+
const signature = `${observation.commitTime ?? "none"}:${observation.outputDigest}:${aheadCount}:clean`;
|
|
3581
|
+
const previous = (deps.readCompletionObservation ?? readSwarmCompletionObservation)(options.workspacePath, options.issueId, observation.progressKey);
|
|
3582
|
+
const current = {
|
|
3583
|
+
signature,
|
|
3584
|
+
nudged: previous?.signature === signature ? previous.nudged : false,
|
|
3585
|
+
consecutiveDoneCount: previous?.signature === signature ? previous.consecutiveDoneCount + 1 : 1
|
|
3586
|
+
};
|
|
3587
|
+
const actions = [];
|
|
3588
|
+
const normalizedIssueId = options.issueId.toUpperCase();
|
|
3589
|
+
if (!current.nudged) {
|
|
3590
|
+
if (deps.sendCompletionNudge) await deps.sendCompletionNudge(slot.agentId, normalizedIssueId);
|
|
3591
|
+
current.nudged = true;
|
|
3592
|
+
actions.push(`[swarm] nudged slot ${slot.slotIndex} (item ${slot.itemId}) for ${normalizedIssueId}: run pan done ${normalizedIssueId}`);
|
|
3593
|
+
}
|
|
3594
|
+
await (deps.writeCompletionObservation ?? writeSwarmCompletionObservation)(options.workspacePath, options.issueId, observation.progressKey, current);
|
|
3595
|
+
return {
|
|
3596
|
+
...slot,
|
|
3597
|
+
lifecycle: "awaiting-completion-signal",
|
|
3598
|
+
reason: "no-progress-timeout",
|
|
3599
|
+
stalledForMs: observation.stalledForMs,
|
|
3600
|
+
signal: "completion-nudge",
|
|
3601
|
+
actions
|
|
3602
|
+
};
|
|
3603
|
+
}
|
|
3604
|
+
async function classifyDurableReadySlot(slot, deps, options) {
|
|
3605
|
+
if (!slot.branch || !options.workspacePath || !options.issueId) return null;
|
|
3606
|
+
if (await (deps.getSlotBranchAheadCount ?? defaultGetSlotBranchAheadCount)(options.workspacePath, options.issueId, slot.branch).catch(() => 0) < 1) return null;
|
|
3607
|
+
if (!await (deps.isSlotWorktreeClean ?? defaultIsSlotWorktreeClean)(`${options.workspacePath}-slot-${slot.slotIndex}`).catch(() => false)) return null;
|
|
3608
|
+
return {
|
|
3609
|
+
...slot,
|
|
3610
|
+
lifecycle: "ready-to-merge",
|
|
3611
|
+
exitStatus: 0,
|
|
3612
|
+
signal: "inferred"
|
|
3613
|
+
};
|
|
3614
|
+
}
|
|
3615
|
+
async function clearSwarmCompletionObservation(workspacePath, issueId, progressKey, deps) {
|
|
3616
|
+
await (deps.clearCompletionObservation ?? clearSwarmCompletionObservationRecord)(workspacePath, issueId, progressKey);
|
|
3617
|
+
}
|
|
3618
|
+
async function defaultGetSlotBranchAheadCount(workspacePath, issueId, branch) {
|
|
3619
|
+
const baseBranch = `feature/${issueId.toLowerCase()}`;
|
|
3620
|
+
const roots = resolveWorkspaceRepoRootsSync(issueId, branch.match(/-slot-(\d+)(?:-attempt-\d+)?$/) ? `${workspacePath}-slot-${branch.match(/-slot-(\d+)(?:-attempt-\d+)?$/)[1]}` : workspacePath);
|
|
3621
|
+
let total = 0;
|
|
3622
|
+
for (const root of roots) {
|
|
3623
|
+
if (root.degradedPolyrepo) return 0;
|
|
3624
|
+
const { stdout } = await execAsync$4(`git rev-list --count ${JSON.stringify(baseBranch)}..${JSON.stringify(branch)}`, { cwd: root.dir });
|
|
3625
|
+
if (!/^\d+$/.test(stdout.trim())) return 0;
|
|
3626
|
+
total += Number(stdout.trim());
|
|
3627
|
+
}
|
|
3628
|
+
return total;
|
|
3629
|
+
}
|
|
3630
|
+
async function defaultIsSlotWorktreeClean(slotWorkspacePath) {
|
|
3631
|
+
const match = /feature-([a-z]+-\d+)-slot-\d+$/.exec(slotWorkspacePath);
|
|
3632
|
+
const roots = match ? resolveWorkspaceRepoRootsSync(match[1].toUpperCase(), slotWorkspacePath) : [];
|
|
3633
|
+
if (roots.some((root) => root.degradedPolyrepo)) return false;
|
|
3634
|
+
return (roots.length > 0 ? await Promise.all(roots.map((root) => execAsync$4("git status --porcelain", { cwd: root.dir }).then((result) => result.stdout))) : [(await execAsync$4("git status --porcelain", { cwd: slotWorkspacePath })).stdout]).every(isStatePlaneOnlyStatus);
|
|
3635
|
+
}
|
|
3636
|
+
async function defaultSendCompletionNudge(agentId, issueId) {
|
|
3637
|
+
await messageAgent(agentId, `You appear to have committed clean slot work but have not signaled completion. If the slot is complete, run exactly:\n\npan done ${issueId}`, "deacon:swarm-completion-inference");
|
|
3638
|
+
}
|
|
3639
|
+
//#endregion
|
|
3640
|
+
//#region ../../lib/cloister/deacon-swarm-gc.ts
|
|
3641
|
+
init_agent_state();
|
|
3642
|
+
init_project_repos();
|
|
3643
|
+
const MERGED_LIVE_SLOT_IDLE_MS = 1800 * 1e3;
|
|
3644
|
+
async function gcMergedSlots(issueId, workspacePath, slots, deps) {
|
|
3645
|
+
return (await gcMergedSlotsWithStatus(issueId, workspacePath, slots, deps)).actions;
|
|
3646
|
+
}
|
|
3647
|
+
async function gcMergedSlotsWithStatus(issueId, workspacePath, slots, deps) {
|
|
3648
|
+
const actions = [];
|
|
3649
|
+
const uncleared = [];
|
|
3650
|
+
const sessionNames = new Set(await deps.listSessionNames());
|
|
3651
|
+
const worktreeExists = deps.slotWorktreeExists ?? existsSync$1;
|
|
3652
|
+
for (const slot of slots) {
|
|
3653
|
+
if (slot.status !== "merged") continue;
|
|
3654
|
+
const agentId = slot.agentId ?? `agent-${issueId.toLowerCase()}-slot-${slot.slotIndex}`;
|
|
3655
|
+
let reapAction = null;
|
|
3656
|
+
if (sessionNames.has(agentId)) {
|
|
3657
|
+
const completionProven = slot.mergedVia === "completed-status";
|
|
3658
|
+
const lastActivity = (deps.getAgentLastActivity ?? ((id) => getAgentStateSync(id)?.lastActivity))(agentId);
|
|
3659
|
+
const idleFor = lastActivity ? Date.now() - Date.parse(lastActivity) : 0;
|
|
3660
|
+
if (!completionProven && (!Number.isFinite(idleFor) || idleFor < MERGED_LIVE_SLOT_IDLE_MS)) {
|
|
3661
|
+
actions.push(`[swarm] gc skipped slot ${slot.slotIndex} (item ${slot.itemId}) for ${issueId}: agent session alive`);
|
|
3662
|
+
uncleared.push(slot);
|
|
3663
|
+
continue;
|
|
3664
|
+
}
|
|
3665
|
+
reapAction = completionProven ? `[swarm] gc reaped merged agent ${agentId}` : `[swarm] gc reaped idle merged agent ${agentId}`;
|
|
3666
|
+
}
|
|
3667
|
+
const slotWorkspace = `${workspacePath}-slot-${slot.slotIndex}`;
|
|
3668
|
+
const slotBranch = slot.branch ?? `feature/${issueId.toLowerCase()}-slot-${slot.slotIndex}`;
|
|
3669
|
+
if (worktreeExists(slotWorkspace)) {
|
|
3670
|
+
if (!await removeSlotWorkspace(issueId, workspacePath, slotWorkspace, slotBranch, slot, deps, actions)) {
|
|
3671
|
+
uncleared.push(slot);
|
|
3672
|
+
continue;
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3675
|
+
const branchExists = await slotBranchExists(deps.runGitCommand, workspacePath, slotBranch);
|
|
3676
|
+
if (branchExists === null) {
|
|
3677
|
+
actions.push(`[swarm] gc deferred slot ${slot.slotIndex} (item ${slot.itemId}) for ${issueId}: branch state of ${slotBranch} could not be determined`);
|
|
3678
|
+
uncleared.push(slot);
|
|
3679
|
+
continue;
|
|
3680
|
+
}
|
|
3681
|
+
if (branchExists) try {
|
|
3682
|
+
await deps.runGitCommand(`git branch -D ${JSON.stringify(slotBranch)}`, workspacePath);
|
|
3683
|
+
} catch (error) {
|
|
3684
|
+
actions.push(`[swarm] gc deferred slot ${slot.slotIndex} (item ${slot.itemId}) for ${issueId}: branch delete failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
3685
|
+
uncleared.push(slot);
|
|
3686
|
+
continue;
|
|
3687
|
+
}
|
|
3688
|
+
await deps.clearSlotAssignment(workspacePath, issueId, slot.slotIndex, slot.itemId);
|
|
3689
|
+
if (reapAction) try {
|
|
3690
|
+
await (deps.stopSlotAgent ?? ((id) => Effect.runPromise(stopAgent(id))))(agentId);
|
|
3691
|
+
actions.push(reapAction);
|
|
3692
|
+
} catch (error) {
|
|
3693
|
+
actions.push(`[swarm] could not reap merged agent ${agentId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
3694
|
+
}
|
|
3695
|
+
actions.push(`[swarm] gc slot ${slot.slotIndex} (item ${slot.itemId}) for ${issueId}`);
|
|
3696
|
+
}
|
|
3697
|
+
return {
|
|
3698
|
+
actions,
|
|
3699
|
+
uncleared
|
|
3700
|
+
};
|
|
3701
|
+
}
|
|
3702
|
+
/**
|
|
3703
|
+
* Whether the outer workspace has a local `slotBranch`, or null when that
|
|
3704
|
+
* cannot be determined. `git branch --list` exits 0 with empty output for an
|
|
3705
|
+
* absent branch, so a throw is always an indeterminate state, never "absent".
|
|
3706
|
+
*/
|
|
3707
|
+
async function slotBranchExists(runGitCommand, workspacePath, slotBranch) {
|
|
3708
|
+
try {
|
|
3709
|
+
const result = await runGitCommand(`git branch --list ${JSON.stringify(slotBranch)}`, workspacePath);
|
|
3710
|
+
return String(result?.stdout ?? "").trim().length > 0;
|
|
3711
|
+
} catch {
|
|
3712
|
+
return null;
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3715
|
+
/**
|
|
3716
|
+
* Remove a merged slot's workspace (PAN-3686). A polyrepo slot workspace is an
|
|
3717
|
+
* aggregate directory whose registered git worktrees are the nested sub-repo
|
|
3718
|
+
* checkouts (<slot>/fe, <slot>/api, …), each owned by a different parent
|
|
3719
|
+
* repository. Git cannot remove the aggregate root while nested worktrees
|
|
3720
|
+
* live inside it ("Directory not empty"), so every nested worktree is
|
|
3721
|
+
* detached through the workspace abstraction first, in its owning parent
|
|
3722
|
+
* repo.
|
|
3723
|
+
*
|
|
3724
|
+
* Cleanup is atomic in intent: a read-only preflight proves every nested repo
|
|
3725
|
+
* safe AND removable, and decides the aggregate root's removal route, before
|
|
3726
|
+
* the first mutation. Discovering an unsafe or unremovable nested checkout
|
|
3727
|
+
* after an earlier one was already detached is the Tier-1 partial-cleanup
|
|
3728
|
+
* defect the preflight exists to prevent (PAN-3686 post-deploy review). Any
|
|
3729
|
+
* preflight failure defers the whole slot with zero removals.
|
|
3730
|
+
*
|
|
3731
|
+
* Never discards unmerged or locally-modified nested work. PAN-3695: a slot
|
|
3732
|
+
* that reached merged status with unmerged nested branches is INTEGRATED here,
|
|
3733
|
+
* not just deferred — each unmerged nested slot branch is merged through the
|
|
3734
|
+
* canonical nested merge path (`git merge --no-ff` into the per-repo base
|
|
3735
|
+
* feature-workspace checkout, same as verifyAndMergeSlot) and ancestry is
|
|
3736
|
+
* re-verified repo by repo before any removal. A repo whose merge is pending
|
|
3737
|
+
* or failed defers the whole slot with an actionable repo-specific reason;
|
|
3738
|
+
* successful per-repo merges are preserved and the next pass retries the rest.
|
|
3739
|
+
* Uncommitted nested changes still defer the slot untouched.
|
|
3740
|
+
*/
|
|
3741
|
+
async function removeSlotWorkspace(issueId, workspacePath, slotWorkspace, slotBranch, slot, deps, actions) {
|
|
3742
|
+
const defer = (reason) => {
|
|
3743
|
+
actions.push(`[swarm] gc deferred slot ${slot.slotIndex} (item ${slot.itemId}) for ${issueId}: ${reason}`);
|
|
3744
|
+
return false;
|
|
3745
|
+
};
|
|
3746
|
+
const { isPolyrepo, nested } = (deps.listSlotWorkspaceWorktrees ?? resolveSlotWorkspaceWorktreesSync)(issueId, slotWorkspace);
|
|
3747
|
+
if (nested.length > 0) {
|
|
3748
|
+
const mergeFailures = await mergeNestedSlotBranches(issueId, workspacePath, slotBranch, nested, deps);
|
|
3749
|
+
if (mergeFailures.length > 0) return defer(`nested merge incomplete: ${mergeFailures.join("; ")}`);
|
|
3750
|
+
}
|
|
3751
|
+
for (const worktree of nested) {
|
|
3752
|
+
const blocked = await nestedWorktreeDirtyReason(deps.runGitCommand, worktree);
|
|
3753
|
+
if (blocked) return defer(`preserving nested work: ${blocked}`);
|
|
3754
|
+
}
|
|
3755
|
+
for (const worktree of nested) {
|
|
3756
|
+
const registered = await isRegisteredWorktree(deps.runGitCommand, worktree.parentRepo, worktree.dir);
|
|
3757
|
+
if (registered === null) return defer(`${worktree.repoKey}: worktree registration in parent repo could not be determined`);
|
|
3758
|
+
if (!registered) return defer(`${worktree.repoKey}: nested worktree is not registered in its parent repo (an earlier partial cleanup may have detached it); run pan swarm reset ${issueId}`);
|
|
3759
|
+
}
|
|
3760
|
+
const aggregateRegistered = await isRegisteredWorktree(deps.runGitCommand, workspacePath, slotWorkspace);
|
|
3761
|
+
if (aggregateRegistered === null) return defer("aggregate worktree registration could not be determined");
|
|
3762
|
+
const detached = [];
|
|
3763
|
+
for (const worktree of nested) {
|
|
3764
|
+
try {
|
|
3765
|
+
await deps.runGitCommand(`git worktree remove --force ${JSON.stringify(worktree.dir)}`, worktree.parentRepo);
|
|
3766
|
+
detached.push(worktree.repoKey);
|
|
3767
|
+
} catch (error) {
|
|
3768
|
+
const partial = detached.length > 0 ? ` (already detached: ${detached.join(", ")})` : "";
|
|
3769
|
+
return defer(`nested worktree remove failed in ${worktree.repoKey}${partial}: ${error instanceof Error ? error.message : String(error)}`);
|
|
3770
|
+
}
|
|
3771
|
+
try {
|
|
3772
|
+
await deps.runGitCommand(`git branch -D ${JSON.stringify(slotBranch)}`, worktree.parentRepo);
|
|
3773
|
+
} catch (error) {
|
|
3774
|
+
actions.push(`[swarm] gc note slot ${slot.slotIndex} (item ${slot.itemId}) for ${issueId}: nested branch ${slotBranch} delete failed in ${worktree.repoKey}: ${error instanceof Error ? error.message : String(error)}`);
|
|
3775
|
+
}
|
|
3776
|
+
}
|
|
3777
|
+
if (!aggregateRegistered && isPolyrepo) try {
|
|
3778
|
+
await (deps.removeDirectory ?? ((path) => rm(path, {
|
|
3779
|
+
recursive: true,
|
|
3780
|
+
force: true
|
|
3781
|
+
})))(slotWorkspace);
|
|
3782
|
+
return true;
|
|
3783
|
+
} catch (rmError) {
|
|
3784
|
+
return defer(`aggregate directory remove failed: ${rmError instanceof Error ? rmError.message : String(rmError)}`);
|
|
3785
|
+
}
|
|
3786
|
+
try {
|
|
3787
|
+
await deps.runGitCommand(`git worktree remove --force ${JSON.stringify(slotWorkspace)}`, workspacePath);
|
|
3788
|
+
return true;
|
|
3789
|
+
} catch (error) {
|
|
3790
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3791
|
+
if (isPolyrepo && /is not a working tree/.test(message)) try {
|
|
3792
|
+
await (deps.removeDirectory ?? ((path) => rm(path, {
|
|
3793
|
+
recursive: true,
|
|
3794
|
+
force: true
|
|
3795
|
+
})))(slotWorkspace);
|
|
3796
|
+
return true;
|
|
3797
|
+
} catch (rmError) {
|
|
3798
|
+
return defer(`aggregate directory remove failed: ${rmError instanceof Error ? rmError.message : String(rmError)}`);
|
|
3799
|
+
}
|
|
3800
|
+
return defer(`worktree remove failed: ${message}`);
|
|
3801
|
+
}
|
|
3802
|
+
}
|
|
3803
|
+
/**
|
|
3804
|
+
* Whether `worktreeDir` is a registered worktree of the repository at
|
|
3805
|
+
* `repoDir`, or null when the registration state cannot be determined. The
|
|
3806
|
+
* porcelain list always names at least the repository's own main worktree, so
|
|
3807
|
+
* a response with no `worktree` lines is an anomaly, not proof of "absent".
|
|
3808
|
+
* Shared with `pan swarm reset` (PAN-3713), which must distinguish an
|
|
3809
|
+
* already-unregistered nested worktree (the desired postcondition) from a
|
|
3810
|
+
* genuine removal failure.
|
|
3811
|
+
*/
|
|
3812
|
+
async function isRegisteredWorktree(runGitCommand, repoDir, worktreeDir) {
|
|
3813
|
+
try {
|
|
3814
|
+
const result = await runGitCommand("git worktree list --porcelain", repoDir);
|
|
3815
|
+
const paths = String(result?.stdout ?? "").split("\n").filter((line) => line.startsWith("worktree ")).map((line) => line.slice(9).trim());
|
|
3816
|
+
if (paths.length === 0) return null;
|
|
3817
|
+
return paths.includes(worktreeDir);
|
|
3818
|
+
} catch {
|
|
3819
|
+
return null;
|
|
3820
|
+
}
|
|
3821
|
+
}
|
|
3822
|
+
/**
|
|
3823
|
+
* Merge every nested slot branch that still has commits its per-repo feature
|
|
3824
|
+
* branch lacks, through the canonical nested merge path (PAN-3695): the merge
|
|
3825
|
+
* runs in the repo's base feature-workspace checkout, then ancestry is
|
|
3826
|
+
* re-verified by re-counting `featureBranch..slotBranch` — a merge command
|
|
3827
|
+
* exiting 0 is not proof the slot head landed — and the parent feature branch
|
|
3828
|
+
* is pushed to origin so the work actually leaves the machine. A dirty base
|
|
3829
|
+
* checkout refuses the merge, and a missing or degraded base checkout fails
|
|
3830
|
+
* the repo outright — without it, remote durability cannot be verified, even
|
|
3831
|
+
* when the local feature branch already contains the slot head. Every repo is
|
|
3832
|
+
* attempted even after a sibling fails, so successful merges are preserved and
|
|
3833
|
+
* only the failed repos are retried on the next pass. Returns the per-repo
|
|
3834
|
+
* failure reasons; an empty list means every nested repo's slot branch is an
|
|
3835
|
+
* ancestor of its pushed feature branch.
|
|
3836
|
+
*/
|
|
3837
|
+
async function mergeNestedSlotBranches(issueId, workspacePath, slotBranch, nested, deps) {
|
|
3838
|
+
const failures = [];
|
|
3839
|
+
const baseRoots = new Map((deps.listFeatureWorkspaceRepoRoots ?? resolveWorkspaceRepoRootsSync)(issueId, workspacePath).map((root) => [root.repoKey, root]));
|
|
3840
|
+
for (const worktree of nested) {
|
|
3841
|
+
const ahead = await countCommitsAhead(deps.runGitCommand, worktree.parentRepo, worktree.featureBranch, slotBranch);
|
|
3842
|
+
if (ahead === null) {
|
|
3843
|
+
failures.push(`${worktree.repoKey}: merge state of ${slotBranch} against ${worktree.featureBranch} could not be determined`);
|
|
3844
|
+
continue;
|
|
3845
|
+
}
|
|
3846
|
+
const baseRoot = baseRoots.get(worktree.repoKey);
|
|
3847
|
+
if (!baseRoot || baseRoot.degradedPolyrepo) {
|
|
3848
|
+
failures.push(ahead > 0 ? `${worktree.repoKey}: no base feature-workspace checkout for ${worktree.featureBranch} — cannot merge ${slotBranch} (${ahead} unmerged commit(s))` : `${worktree.repoKey}: no base feature-workspace checkout for ${worktree.featureBranch} — cannot verify and push remote durability`);
|
|
3849
|
+
continue;
|
|
3850
|
+
}
|
|
3851
|
+
if (ahead > 0) {
|
|
3852
|
+
const baseDirty = await baseCheckoutDirtyReason(deps.runGitCommand, worktree, baseRoot, slotBranch);
|
|
3853
|
+
if (baseDirty) {
|
|
3854
|
+
failures.push(baseDirty);
|
|
3855
|
+
continue;
|
|
3856
|
+
}
|
|
3857
|
+
try {
|
|
3858
|
+
await deps.runGitCommand(`git merge --no-ff ${JSON.stringify(slotBranch)}`, baseRoot.dir);
|
|
3859
|
+
} catch (error) {
|
|
3860
|
+
await deps.runGitCommand("git merge --abort", baseRoot.dir).catch(() => {});
|
|
3861
|
+
failures.push(`${worktree.repoKey}: ${slotBranch} did not merge cleanly into ${worktree.featureBranch}: ${error instanceof Error ? error.message : String(error)}`);
|
|
3862
|
+
continue;
|
|
3863
|
+
}
|
|
3864
|
+
const remaining = await countCommitsAhead(deps.runGitCommand, worktree.parentRepo, worktree.featureBranch, slotBranch);
|
|
3865
|
+
if (remaining !== 0) {
|
|
3866
|
+
failures.push(`${worktree.repoKey}: ancestry verification failed — ${slotBranch} still has ${remaining ?? "an unknown number of"} commit(s) not in ${worktree.featureBranch} after merge`);
|
|
3867
|
+
continue;
|
|
3868
|
+
}
|
|
3869
|
+
}
|
|
3870
|
+
const unpushed = await countCommitsAhead(deps.runGitCommand, baseRoot.dir, `origin/${worktree.featureBranch}`, worktree.featureBranch);
|
|
3871
|
+
if (ahead > 0 || unpushed === null || unpushed > 0) try {
|
|
3872
|
+
await deps.runGitCommand(`git push origin ${JSON.stringify(worktree.featureBranch)}`, baseRoot.dir);
|
|
3873
|
+
} catch (error) {
|
|
3874
|
+
failures.push(`${worktree.repoKey}: merged ${slotBranch} into ${worktree.featureBranch} but push to origin failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
3875
|
+
continue;
|
|
3876
|
+
}
|
|
3877
|
+
}
|
|
3878
|
+
return failures;
|
|
3879
|
+
}
|
|
3880
|
+
/**
|
|
3881
|
+
* Why a base feature-workspace checkout must not receive a merge right now, or
|
|
3882
|
+
* null when it is clean. Tracked local modifications block the merge (git would
|
|
3883
|
+
* refuse or, worse, entangle someone's uncommitted work); untracked files do
|
|
3884
|
+
* not, so they are ignored here.
|
|
3885
|
+
*/
|
|
3886
|
+
async function baseCheckoutDirtyReason(runGitCommand, worktree, baseRoot, slotBranch) {
|
|
3887
|
+
try {
|
|
3888
|
+
const status = await runGitCommand("git status --porcelain --untracked-files=no", baseRoot.dir);
|
|
3889
|
+
if (String(status?.stdout ?? "").trim().length > 0) return `${worktree.repoKey}: base feature checkout of ${worktree.featureBranch} has uncommitted changes — refusing to merge ${slotBranch}`;
|
|
3890
|
+
} catch {
|
|
3891
|
+
return `${worktree.repoKey}: base feature checkout state of ${worktree.featureBranch} could not be determined — refusing to merge ${slotBranch}`;
|
|
3892
|
+
}
|
|
3893
|
+
return null;
|
|
3894
|
+
}
|
|
3895
|
+
/**
|
|
3896
|
+
* Why a nested slot worktree must be preserved, or null when it is safe to
|
|
3897
|
+
* remove. Any tracked change or non-ignored untracked file blocks removal
|
|
3898
|
+
* (ignored build artifacts do not). Merge state is handled separately by
|
|
3899
|
+
* mergeNestedSlotBranches before this runs.
|
|
3900
|
+
*/
|
|
3901
|
+
async function nestedWorktreeDirtyReason(runGitCommand, worktree) {
|
|
3902
|
+
try {
|
|
3903
|
+
const status = await runGitCommand("git status --porcelain --untracked-files=all", worktree.dir);
|
|
3904
|
+
if (String(status?.stdout ?? "").trim().length > 0) return `${worktree.repoKey}: worktree has uncommitted changes`;
|
|
3905
|
+
} catch {
|
|
3906
|
+
return `${worktree.repoKey}: worktree status could not be determined`;
|
|
3907
|
+
}
|
|
3908
|
+
return null;
|
|
3909
|
+
}
|
|
3910
|
+
async function countCommitsAhead(runGitCommand, parentRepo, featureBranch, slotBranch) {
|
|
3911
|
+
try {
|
|
3912
|
+
const result = await runGitCommand(`git rev-list --count ${JSON.stringify(featureBranch)}..${JSON.stringify(slotBranch)}`, parentRepo);
|
|
3913
|
+
const stdout = String(result?.stdout ?? "").trim();
|
|
3914
|
+
if (!/^\d+$/.test(stdout)) return null;
|
|
3915
|
+
return Number(stdout);
|
|
3916
|
+
} catch {
|
|
3917
|
+
return null;
|
|
3918
|
+
}
|
|
3919
|
+
}
|
|
3920
|
+
//#endregion
|
|
3921
|
+
//#region ../../lib/cloister/deacon-swarm-finalization.ts
|
|
3922
|
+
init_review_status();
|
|
3923
|
+
init_io();
|
|
3924
|
+
async function defaultRequestIssueReview(issueId, workspacePath) {
|
|
3925
|
+
setReviewStatusSync(issueId, {
|
|
3926
|
+
reviewStatus: "pending",
|
|
3927
|
+
testStatus: "pending",
|
|
3928
|
+
mergeStatus: "pending",
|
|
3929
|
+
readyForMerge: false,
|
|
3930
|
+
verificationStatus: "pending",
|
|
3931
|
+
verificationCycleCount: 0,
|
|
3932
|
+
autoRequeueCount: 0,
|
|
3933
|
+
reviewRequestedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3934
|
+
});
|
|
3935
|
+
const { spawnReviewRoleForIssue } = await import("./review-agent-DgkUPRx0.js");
|
|
3936
|
+
return Effect.runPromise(spawnReviewRoleForIssue({
|
|
3937
|
+
issueId,
|
|
3938
|
+
workspace: workspacePath,
|
|
3939
|
+
branch: `feature/${issueId.toLowerCase()}`
|
|
3940
|
+
}));
|
|
3941
|
+
}
|
|
3942
|
+
//#endregion
|
|
3943
|
+
//#region ../../lib/cloister/swarm-tiered-hooks.ts
|
|
3944
|
+
/**
|
|
3945
|
+
* PAN-2385 — tiered-execution commit hooks for the swarm dispatch path.
|
|
3946
|
+
*
|
|
3947
|
+
* The tier-feed and tier-supervisor libraries shipped with PAN-1791/2222 but
|
|
3948
|
+
* had no production caller: commits landed and neither the standing crew nor
|
|
3949
|
+
* the subscribe-policy supervisor ever heard about them (found live during
|
|
3950
|
+
* the first Standing Crew run, PAN-2383). This module is the ignition.
|
|
3951
|
+
*
|
|
3952
|
+
* Fired from mergeReadySlots after a slot's bead commits merge into the
|
|
3953
|
+
* feature workspace:
|
|
3954
|
+
* 1. broadcastCommit — every live slot agent hears the rendered diff
|
|
3955
|
+
* (ingestion-only; everyone-hears-everything per docs/TIERED-EXECUTION.md).
|
|
3956
|
+
* 2. shouldSupervise(item, subscribe) → deliverCommitForReview — the
|
|
3957
|
+
* standing supervisor reviews per its subscribe policy. This is
|
|
3958
|
+
* independent of supervisor.owns_inspection, which only routes
|
|
3959
|
+
* `pan inspect`.
|
|
3960
|
+
*
|
|
3961
|
+
* Both hooks are best-effort: the commit has already landed, so a delivery
|
|
3962
|
+
* failure logs loudly and never blocks the merge loop.
|
|
3963
|
+
*/
|
|
3964
|
+
init_tmux();
|
|
3965
|
+
init_config_yaml();
|
|
3966
|
+
init_slot_reconcile();
|
|
3967
|
+
const execAsync$3 = promisify(exec);
|
|
3968
|
+
const defaultDeps$5 = {
|
|
3969
|
+
loadConfig: loadConfigSync,
|
|
3970
|
+
getHeadSha: async (workspacePath) => (await execAsync$3("git rev-parse HEAD", { cwd: workspacePath })).stdout.trim(),
|
|
3971
|
+
listAssignments: listSlotAssignments,
|
|
3972
|
+
isSessionAlive: (sessionName) => Effect.runPromise(sessionExists(sessionName)),
|
|
3973
|
+
broadcast: broadcastCommit,
|
|
3974
|
+
ensureSupervisor: spawnTierSupervisor,
|
|
3975
|
+
deliverReview: deliverCommitForReview,
|
|
3976
|
+
loadPrd: loadPrdDraft
|
|
3977
|
+
};
|
|
3978
|
+
/** Resolve the tier name a bead belongs to, for the feed header. Falls back to
|
|
3979
|
+
* the slot label when the tier table cannot resolve the item (fail-open here is
|
|
3980
|
+
* correct: the feed is informational and the commit has already landed). */
|
|
3981
|
+
function tierNameForItem(item, tiered, slotIndex) {
|
|
3982
|
+
if (item) try {
|
|
3983
|
+
return resolveTier(item, tiered).tierName;
|
|
3984
|
+
} catch {}
|
|
3985
|
+
return `slot-${slotIndex}`;
|
|
3986
|
+
}
|
|
3987
|
+
/**
|
|
3988
|
+
* Fire the commit feed + supervisor review for a bead whose commits just
|
|
3989
|
+
* merged into the feature workspace. Returns deacon action-log lines.
|
|
3990
|
+
*/
|
|
3991
|
+
async function fireTieredCommitHooks(options, deps = defaultDeps$5) {
|
|
3992
|
+
const actions = [];
|
|
3993
|
+
const { issueId, workspacePath, item, doc } = options;
|
|
3994
|
+
let tiered;
|
|
3995
|
+
try {
|
|
3996
|
+
tiered = deps.loadConfig().config.tieredExecution;
|
|
3997
|
+
} catch (err) {
|
|
3998
|
+
actions.push(`[tiered] skipped commit hooks for ${issueId}: config load failed (${err instanceof Error ? err.message : String(err)})`);
|
|
3999
|
+
return actions;
|
|
4000
|
+
}
|
|
4001
|
+
if (!tiered || !resolveTieredExecutionEnabled(tiered, doc.plan.metadata)) return actions;
|
|
4002
|
+
let sha;
|
|
4003
|
+
try {
|
|
4004
|
+
sha = await deps.getHeadSha(workspacePath);
|
|
4005
|
+
} catch (err) {
|
|
4006
|
+
actions.push(`[tiered] commit hooks for ${issueId} item ${item.id} could not resolve HEAD: ${err instanceof Error ? err.message : String(err)}`);
|
|
4007
|
+
return actions;
|
|
4008
|
+
}
|
|
4009
|
+
try {
|
|
4010
|
+
const itemsById = new Map(doc.plan.items.map((planItem) => [planItem.id, planItem]));
|
|
4011
|
+
const assignments = deps.listAssignments(issueId, workspacePath);
|
|
4012
|
+
const listeners = [];
|
|
4013
|
+
for (const assignment of assignments) {
|
|
4014
|
+
if (!assignment.agentId) continue;
|
|
4015
|
+
if (!await deps.isSessionAlive(assignment.agentId)) continue;
|
|
4016
|
+
listeners.push({
|
|
4017
|
+
tierName: tierNameForItem(itemsById.get(assignment.itemId), tiered, assignment.slotIndex),
|
|
4018
|
+
agentId: assignment.agentId
|
|
4019
|
+
});
|
|
4020
|
+
}
|
|
4021
|
+
if (listeners.length > 0) {
|
|
4022
|
+
const delivered = (await deps.broadcast({
|
|
4023
|
+
workspace: workspacePath,
|
|
4024
|
+
issueId,
|
|
4025
|
+
sha,
|
|
4026
|
+
itemTitle: item.title,
|
|
4027
|
+
itemId: item.id,
|
|
4028
|
+
tiers: listeners,
|
|
4029
|
+
feedConfig: tiered.feed
|
|
4030
|
+
})).filter((delivery) => delivery.result.ok).length;
|
|
4031
|
+
actions.push(`[tiered] broadcast commit ${sha.slice(0, 10)} (item ${item.id}) for ${issueId} to ${delivered}/${listeners.length} standing agents`);
|
|
4032
|
+
}
|
|
4033
|
+
} catch (err) {
|
|
4034
|
+
actions.push(`[tiered] feed broadcast FAILED for ${issueId} item ${item.id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
4035
|
+
}
|
|
4036
|
+
try {
|
|
4037
|
+
const supervisor = tiered.supervisor;
|
|
4038
|
+
if (supervisor && shouldSupervise(item, supervisor.subscribe)) {
|
|
4039
|
+
const agentId = supervisorAgentId(issueId);
|
|
4040
|
+
if (!await deps.isSessionAlive(agentId)) {
|
|
4041
|
+
await deps.ensureSupervisor(issueId, supervisor, { workspace: workspacePath });
|
|
4042
|
+
actions.push(`[tiered] spawned standing supervisor ${agentId} for ${issueId}`);
|
|
4043
|
+
}
|
|
4044
|
+
const prdMarkdown = await deps.loadPrd(workspacePath, issueId).catch(() => void 0);
|
|
4045
|
+
await deps.deliverReview({
|
|
4046
|
+
supervisorAgentId: agentId,
|
|
4047
|
+
workspacePath,
|
|
4048
|
+
issueId,
|
|
4049
|
+
item,
|
|
4050
|
+
sha,
|
|
4051
|
+
itemId: item.id,
|
|
4052
|
+
prdMarkdown
|
|
4053
|
+
});
|
|
4054
|
+
actions.push(`[tiered] supervisor review dispatched for ${issueId} item ${item.id} (subscribe=${supervisor.subscribe})`);
|
|
4055
|
+
}
|
|
4056
|
+
} catch (err) {
|
|
4057
|
+
actions.push(`[tiered] supervisor review FAILED for ${issueId} item ${item.id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
4058
|
+
}
|
|
4059
|
+
return actions;
|
|
4060
|
+
}
|
|
4061
|
+
//#endregion
|
|
4062
|
+
//#region ../../lib/cloister/redrive-gate.ts
|
|
4063
|
+
init_recovery_trip();
|
|
4064
|
+
/**
|
|
4065
|
+
* PAN-2543 shared autonomous re-drive gate.
|
|
4066
|
+
*
|
|
4067
|
+
* Recovery entry points classify agent gates, apply the intent policy, and
|
|
4068
|
+
* consult the cached memory verdict through this one function before acting.
|
|
4069
|
+
* Callers still reserve their role-specific concurrency slot after admission.
|
|
4070
|
+
*/
|
|
4071
|
+
init_agent_state();
|
|
4072
|
+
function decideAutonomousRedrive(state, context = {}) {
|
|
4073
|
+
const gateDecision = decideResumeGate(getAgentResumeGateBlockReason(state), "autonomous", context);
|
|
4074
|
+
if (gateDecision.decision !== "proceed") return {
|
|
4075
|
+
decision: "defer",
|
|
4076
|
+
reason: gateDecision.reason,
|
|
4077
|
+
...gateDecision.decision === "block" && gateDecision.needsYou ? { needsYou: true } : {}
|
|
4078
|
+
};
|
|
4079
|
+
const memoryVerdict = getCachedMemoryVerdict();
|
|
4080
|
+
if (memoryVerdict && memoryVerdict.band !== "ok") return {
|
|
4081
|
+
decision: "defer",
|
|
4082
|
+
reason: `memory pressure is ${memoryVerdict.band}`
|
|
4083
|
+
};
|
|
4084
|
+
return {
|
|
4085
|
+
decision: "proceed",
|
|
4086
|
+
gateDecision
|
|
4087
|
+
};
|
|
4088
|
+
}
|
|
4089
|
+
async function decideAgentAutonomousRedrive(state, agentDir, owesRework, deps = {}) {
|
|
4090
|
+
const admission = decideAutonomousRedrive(state, {
|
|
4091
|
+
hasCompletedHandoff: existsSync$1(join(agentDir, "completed")) || existsSync$1(join(agentDir, "completed.processed")),
|
|
4092
|
+
owesRework
|
|
4093
|
+
});
|
|
4094
|
+
if (admission.decision === "defer") return admission;
|
|
4095
|
+
const agentId = basename(agentDir);
|
|
4096
|
+
const pendingDecision = await (deps.detectPendingOperatorDecision ?? detectPendingOperatorDecision)(agentId);
|
|
4097
|
+
if (pendingDecision) return {
|
|
4098
|
+
decision: "defer",
|
|
4099
|
+
reason: `agent is waiting on an operator decision (${pendingDecision.reason})`,
|
|
4100
|
+
needsYou: true
|
|
4101
|
+
};
|
|
4102
|
+
return admission;
|
|
4103
|
+
}
|
|
4104
|
+
//#endregion
|
|
4105
|
+
//#region ../../lib/cloister/swarm-failed-slot.ts
|
|
4106
|
+
init_record_update();
|
|
4107
|
+
init_agent_state();
|
|
4108
|
+
//#endregion
|
|
4109
|
+
//#region ../../lib/cloister/swarm-blocked-slot.ts
|
|
4110
|
+
init_project_repos();
|
|
4111
|
+
const execFileAsync$3 = promisify(execFile);
|
|
4112
|
+
async function prepareReleasedSwarmSlot(issueId, workspacePath, slotIndex, itemId, branch) {
|
|
4113
|
+
await ensureRegisteredSlotWorktree(issueId, workspacePath, {
|
|
4114
|
+
agentId: `agent-${issueId.toLowerCase()}-slot-${slotIndex}`,
|
|
4115
|
+
branch,
|
|
4116
|
+
workspace: `${workspacePath}-slot-${slotIndex}`,
|
|
4117
|
+
slotIndex,
|
|
4118
|
+
slotItemId: itemId
|
|
4119
|
+
});
|
|
4120
|
+
}
|
|
4121
|
+
async function defaultIsSlotBranchPushed(workspacePath, issueId, branch) {
|
|
4122
|
+
const slotIndex = branch.match(/-slot-(\d+)(?:-attempt-\d+)?$/)?.[1];
|
|
4123
|
+
if (!slotIndex) return false;
|
|
4124
|
+
const roots = resolveWorkspaceRepoRootsSync(issueId, `${workspacePath}-slot-${slotIndex}`);
|
|
4125
|
+
if (roots.length === 0 || roots.some((root) => root.degradedPolyrepo)) return false;
|
|
4126
|
+
try {
|
|
4127
|
+
return (await Promise.all(roots.map(async (root) => {
|
|
4128
|
+
await refreshDurableSlotRef(root.dir, branch);
|
|
4129
|
+
return true;
|
|
4130
|
+
}))).every(Boolean);
|
|
4131
|
+
} catch {
|
|
4132
|
+
return false;
|
|
4133
|
+
}
|
|
4134
|
+
}
|
|
4135
|
+
/** Preserve a blocked attempt and provision a unique branch identity for slot reuse. */
|
|
4136
|
+
async function archiveBlockedSwarmSlot(issueId, workspacePath, slotIndex, branch, now = /* @__PURE__ */ new Date()) {
|
|
4137
|
+
const releasedAt = now.toISOString();
|
|
4138
|
+
const suffix = releasedAt.replace(/[-:.TZ]/g, "");
|
|
4139
|
+
const slotWorkspace = `${workspacePath}-slot-${slotIndex}`;
|
|
4140
|
+
const archivedWorktree = `${slotWorkspace}-blocked-${suffix}`;
|
|
4141
|
+
const archivedBranch = `${branch}-blocked-${suffix}`;
|
|
4142
|
+
const replacementBranch = `feature/${issueId.toLowerCase()}-slot-${slotIndex}-attempt-${suffix}`;
|
|
4143
|
+
const slotRoots = resolveWorkspaceRepoRootsSync(issueId, slotWorkspace);
|
|
4144
|
+
const baseRoots = new Map(resolveWorkspaceRepoRootsSync(issueId, workspacePath).map((root) => [root.repoKey, root]));
|
|
4145
|
+
if (slotRoots.length === 0 || slotRoots.some((root) => root.degradedPolyrepo)) throw new Error(`Cannot archive blocked slot ${slotIndex}: repository roots are unavailable.`);
|
|
4146
|
+
for (const root of slotRoots) await execFileAsync$3("git", [
|
|
4147
|
+
"push",
|
|
4148
|
+
"origin",
|
|
4149
|
+
`${await refreshDurableSlotRef(root.dir, branch)}:refs/heads/${archivedBranch}`
|
|
4150
|
+
], { cwd: root.dir });
|
|
4151
|
+
await rename(slotWorkspace, archivedWorktree);
|
|
4152
|
+
for (const root of slotRoots) {
|
|
4153
|
+
const baseRoot = baseRoots.get(root.repoKey);
|
|
4154
|
+
if (!baseRoot) throw new Error(`Cannot archive blocked slot ${slotIndex}: missing base repository ${root.repoKey}.`);
|
|
4155
|
+
const nestedPath = relative(slotWorkspace, root.dir);
|
|
4156
|
+
const archivedRoot = nestedPath ? resolve(archivedWorktree, nestedPath) : archivedWorktree;
|
|
4157
|
+
await execFileAsync$3("git", [
|
|
4158
|
+
"worktree",
|
|
4159
|
+
"repair",
|
|
4160
|
+
archivedRoot
|
|
4161
|
+
], { cwd: baseRoot.dir });
|
|
4162
|
+
await execFileAsync$3("git", [
|
|
4163
|
+
"branch",
|
|
4164
|
+
archivedBranch,
|
|
4165
|
+
branch
|
|
4166
|
+
], { cwd: baseRoot.dir });
|
|
4167
|
+
await execFileAsync$3("git", ["switch", archivedBranch], { cwd: archivedRoot });
|
|
4168
|
+
}
|
|
4169
|
+
return {
|
|
4170
|
+
archivedBranch,
|
|
4171
|
+
archivedWorktree,
|
|
4172
|
+
replacementBranch,
|
|
4173
|
+
releasedAt
|
|
4174
|
+
};
|
|
4175
|
+
}
|
|
4176
|
+
async function refreshDurableSlotRef(workspace, branch) {
|
|
4177
|
+
const remoteRef = `refs/remotes/origin/${branch}`;
|
|
4178
|
+
await execFileAsync$3("git", [
|
|
4179
|
+
"fetch",
|
|
4180
|
+
"--no-tags",
|
|
4181
|
+
"origin",
|
|
4182
|
+
`+refs/heads/${branch}:${remoteRef}`
|
|
4183
|
+
], { cwd: workspace });
|
|
4184
|
+
await execFileAsync$3("git", [
|
|
4185
|
+
"merge-base",
|
|
4186
|
+
"--is-ancestor",
|
|
4187
|
+
`refs/heads/${branch}`,
|
|
4188
|
+
remoteRef
|
|
4189
|
+
], { cwd: workspace });
|
|
4190
|
+
return remoteRef;
|
|
4191
|
+
}
|
|
4192
|
+
//#endregion
|
|
4193
|
+
//#region ../../lib/cloister/swarm-foreman.ts
|
|
4194
|
+
init_messaging();
|
|
4195
|
+
init_projects();
|
|
4196
|
+
init_tmux();
|
|
4197
|
+
init_work_agent_prompt();
|
|
4198
|
+
const defaultDeps$4 = {
|
|
4199
|
+
listSessionNamesSync: () => listSessionNamesSync(),
|
|
4200
|
+
messageAgent,
|
|
4201
|
+
buildWorkAgentPrompt,
|
|
4202
|
+
spawnRun,
|
|
4203
|
+
resolveProjectFromIssueSync
|
|
4204
|
+
};
|
|
4205
|
+
async function ensureSwarmForeman(issueId, workspacePath, options, deps = defaultDeps$4) {
|
|
4206
|
+
const issue = issueId.toUpperCase();
|
|
4207
|
+
const agentId = `agent-${issue.toLowerCase()}`;
|
|
4208
|
+
if (deps.listSessionNamesSync().includes(agentId)) {
|
|
4209
|
+
await deps.messageAgent(agentId, options.prompt ?? `Continue managing ${issue} as its swarm foreman. Run pan swarm status ${issue} --json before acting.`, "pan-swarm");
|
|
4210
|
+
return [`[swarm] attached to live foreman ${agentId} for ${issue}`];
|
|
4211
|
+
}
|
|
4212
|
+
const basePrompt = await deps.buildWorkAgentPrompt({
|
|
4213
|
+
issueId: issue,
|
|
4214
|
+
env: "LOCAL",
|
|
4215
|
+
workspacePath,
|
|
4216
|
+
projectRoot: deps.resolveProjectFromIssueSync(issue)?.projectPath
|
|
4217
|
+
});
|
|
4218
|
+
return [`[swarm] spawned foreman ${(await deps.spawnRun(issue, "work", {
|
|
4219
|
+
workspace: workspacePath,
|
|
4220
|
+
prompt: options.prompt ? `${basePrompt}\n\n${options.prompt}` : basePrompt,
|
|
4221
|
+
foreman: true,
|
|
4222
|
+
startedBy: options.startedBy
|
|
4223
|
+
})).id} for ${issue}`];
|
|
4224
|
+
}
|
|
4225
|
+
//#endregion
|
|
4226
|
+
//#region ../../lib/cloister/swarm-foreman-liveness.ts
|
|
4227
|
+
init_activity_logger();
|
|
4228
|
+
init_slot_reconcile();
|
|
4229
|
+
init_concurrency();
|
|
4230
|
+
const defaultDeps$3 = {
|
|
4231
|
+
listSlotAssignments,
|
|
4232
|
+
readSwarmHold,
|
|
4233
|
+
workResumeSlotsAvailable,
|
|
4234
|
+
ensureSwarmForeman,
|
|
4235
|
+
writeSwarmHold,
|
|
4236
|
+
emitActivityEntry: emitActivityEntrySync
|
|
4237
|
+
};
|
|
4238
|
+
const respawnFailures = /* @__PURE__ */ new Map();
|
|
4239
|
+
async function maintainSwarmForeman(issueId, workspacePath, reconciled, sessions, overrides = {}, allowSpawn = true, bootstrap = false) {
|
|
4240
|
+
const definedOverrides = Object.fromEntries(Object.entries(overrides).filter(([, value]) => value !== void 0));
|
|
4241
|
+
const deps = {
|
|
4242
|
+
...defaultDeps$3,
|
|
4243
|
+
...definedOverrides
|
|
4244
|
+
};
|
|
4245
|
+
const issueLower = issueId.toLowerCase();
|
|
4246
|
+
if (!allowSpawn) return [];
|
|
4247
|
+
const mergedSlotIndexes = new Set(reconciled.merged.map((slot) => slot.slotIndex));
|
|
4248
|
+
if (!(bootstrap || sessions.some((name) => name.startsWith(`agent-${issueLower}-slot-`)) || reconciled.branches.some((branch) => !mergedSlotIndexes.has(branch.slotIndex)) || deps.listSlotAssignments(issueId, workspacePath).length > 0)) {
|
|
4249
|
+
respawnFailures.delete(issueId);
|
|
4250
|
+
return [];
|
|
4251
|
+
}
|
|
4252
|
+
if (sessions.includes(`agent-${issueLower}`) || deps.readSwarmHold(workspacePath, issueId)) return [];
|
|
4253
|
+
if (deps.workResumeSlotsAvailable() <= 0) return [`[swarm-janitor] deferred foreman respawn for ${issueId}: resource governor`];
|
|
4254
|
+
const failures = respawnFailures.get(issueId) ?? 0;
|
|
4255
|
+
if (failures >= 3) return [];
|
|
4256
|
+
try {
|
|
4257
|
+
const prompt = `Resume ${issueId} swarm foreman duties: read .pan/continue.json, run pan swarm status ${issueId} --json, reconcile current state, then continue the wave loop.`;
|
|
4258
|
+
const actions = await deps.ensureSwarmForeman(issueId, workspacePath, {
|
|
4259
|
+
startedBy: "deacon:swarm-janitor",
|
|
4260
|
+
prompt
|
|
4261
|
+
});
|
|
4262
|
+
respawnFailures.delete(issueId);
|
|
4263
|
+
return actions;
|
|
4264
|
+
} catch (error) {
|
|
4265
|
+
const count = failures + 1;
|
|
4266
|
+
respawnFailures.set(issueId, count);
|
|
4267
|
+
const actions = [`[swarm-janitor] foreman respawn failed for ${issueId} (${count}/3): ${error instanceof Error ? error.message : String(error)}`];
|
|
4268
|
+
if (count < 3) return actions;
|
|
4269
|
+
const message = `${issueId} swarm halted after 3 consecutive foreman respawn failures; operator action is required.`;
|
|
4270
|
+
await deps.writeSwarmHold(workspacePath, issueId, {
|
|
4271
|
+
reason: message,
|
|
4272
|
+
setBy: "deacon:swarm-janitor",
|
|
4273
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
4274
|
+
});
|
|
4275
|
+
deps.emitActivityEntry({
|
|
4276
|
+
source: "cloister",
|
|
4277
|
+
level: "error",
|
|
4278
|
+
issueId,
|
|
4279
|
+
message
|
|
4280
|
+
});
|
|
4281
|
+
actions.push(`[swarm-janitor] ${message}`);
|
|
4282
|
+
return actions;
|
|
4283
|
+
}
|
|
4284
|
+
}
|
|
4285
|
+
//#endregion
|
|
4286
|
+
//#region ../../lib/cloister/deacon-swarm.ts
|
|
4287
|
+
init_messaging();
|
|
4288
|
+
init_spawn();
|
|
4289
|
+
init_slot_reconcile();
|
|
4290
|
+
init_record();
|
|
4291
|
+
init_record_update();
|
|
4292
|
+
init_tmux();
|
|
4293
|
+
init_io();
|
|
4294
|
+
init_review_status();
|
|
4295
|
+
init_concurrency();
|
|
4296
|
+
const execAsync$2 = promisify(exec);
|
|
4297
|
+
const DEFAULT_SWARM_STALL_THRESHOLD_MS = 1800 * 1e3;
|
|
4298
|
+
const slotProgressObservations = /* @__PURE__ */ new Map();
|
|
4299
|
+
const defaultDeps$2 = {
|
|
4300
|
+
findSpecByIssue,
|
|
4301
|
+
listFeatureWorkspaces: () => listFeatureWorkspaces({ includeSlotWorkspaces: false }),
|
|
4302
|
+
reconcileSlotState,
|
|
4303
|
+
listSessionNames: () => Effect.runPromise(listSessionNames()),
|
|
4304
|
+
isPaneDead: (sessionName) => Effect.runPromise(isPaneDead(sessionName)),
|
|
4305
|
+
getPaneExitStatus: async (sessionName) => {
|
|
4306
|
+
const raw = (await Effect.runPromise(listPaneValues(sessionName, "#{pane_dead_status}")))[0]?.trim();
|
|
4307
|
+
if (!raw) return null;
|
|
4308
|
+
const status = Number(raw);
|
|
4309
|
+
return Number.isFinite(status) ? status : null;
|
|
4310
|
+
},
|
|
4311
|
+
getAgentRuntimeState: (agentId) => Effect.runPromise(getAgentRuntimeSnapshot(agentId)),
|
|
4312
|
+
getPaneOutputDigest: async (sessionName) => Effect.runPromise(capturePane(sessionName, 200)),
|
|
4313
|
+
getBranchTipCommitTime: async (workspacePath, branch) => {
|
|
4314
|
+
try {
|
|
4315
|
+
const { stdout } = await execAsync$2(`git log -1 --format=%ct ${JSON.stringify(branch)}`, { cwd: workspacePath });
|
|
4316
|
+
const seconds = Number(stdout.trim());
|
|
4317
|
+
return Number.isFinite(seconds) ? seconds * 1e3 : null;
|
|
4318
|
+
} catch {
|
|
4319
|
+
return null;
|
|
4320
|
+
}
|
|
4321
|
+
},
|
|
4322
|
+
getSlotBranchAheadCount: defaultGetSlotBranchAheadCount,
|
|
4323
|
+
isSlotWorktreeClean: defaultIsSlotWorktreeClean,
|
|
4324
|
+
isSlotBranchPushed: defaultIsSlotBranchPushed,
|
|
4325
|
+
archiveBlockedSlot: archiveBlockedSwarmSlot,
|
|
4326
|
+
prepareReleasedSlot: prepareReleasedSwarmSlot,
|
|
4327
|
+
sendCompletionNudge: defaultSendCompletionNudge,
|
|
4328
|
+
slotWorktreeExists: existsSync$1,
|
|
4329
|
+
verifyAndMergeSlot,
|
|
4330
|
+
applyTaskOperationToPlanFile: (issueId, operation, workspacePath = "") => {
|
|
4331
|
+
return applyTaskStatusChange(resolveProjectForIssue(issueId) ?? getProjectConfigFromWorkspacePath(workspacePath), issueId, operation);
|
|
4332
|
+
},
|
|
4333
|
+
fireTieredCommitHooks,
|
|
4334
|
+
recordSlotAssignment,
|
|
4335
|
+
clearSlotAssignment,
|
|
4336
|
+
runGitCommand: (command, cwd) => execAsync$2(command, { cwd }),
|
|
4337
|
+
registeredSlotCapacityAvailable: (issueId, selectedCount) => registeredSlotCapacityAvailable(issueId, selectedCount),
|
|
4338
|
+
tryReserveSwarmSlot,
|
|
4339
|
+
releaseSwarmSlot,
|
|
4340
|
+
spawnRun,
|
|
4341
|
+
getIssueHold: defaultGetIssueHold,
|
|
4342
|
+
shouldDispatch: defaultShouldDispatch,
|
|
4343
|
+
readSwarmHold,
|
|
4344
|
+
getMaxSlotIndex: defaultGetMaxSlotIndex,
|
|
4345
|
+
listSlotAssignments,
|
|
4346
|
+
listReleasedSlotIndexes: (issueId, workspacePath) => Object.keys(readIssueRecordForWorkspaceSync(workspacePath, issueId)?.swarm?.releasedBlockedSlots ?? {}).map(Number),
|
|
4347
|
+
getReleasedSlotBranch: (issueId, workspacePath, slotIndex) => readIssueRecordForWorkspaceSync(workspacePath, issueId)?.swarm?.releasedBlockedSlots?.[String(slotIndex)]?.replacementBranch,
|
|
4348
|
+
clearReleasedSlot: clearReleasedBlockedSwarmSlot,
|
|
4349
|
+
readStatusOverrides: defaultReadStatusOverrides,
|
|
4350
|
+
readSlotCompletion: defaultReadSlotCompletion,
|
|
4351
|
+
clearSlotCompletion: clearSwarmSlotCompletion,
|
|
4352
|
+
getFinalizedAt: getSwarmFinalizedAt,
|
|
4353
|
+
setFinalizedAt: recordSwarmFinalizedAt,
|
|
4354
|
+
recordForemanTakeover: writeSwarmForemanTakeover,
|
|
4355
|
+
ensureSwarmForeman,
|
|
4356
|
+
sendStallEvent: (agentId, message) => messageAgent(agentId, message, "deacon:swarm-stall"),
|
|
4357
|
+
resolveAutomaticSwarmPolicy,
|
|
4358
|
+
requestIssueReview: defaultRequestIssueReview
|
|
4359
|
+
};
|
|
4360
|
+
function defaultGetMaxSlotIndex() {
|
|
4361
|
+
return Math.max(1, getConcurrencyLimits().reservedSwarmSlots);
|
|
4362
|
+
}
|
|
4363
|
+
function defaultGetIssueHold(issueId) {
|
|
4364
|
+
try {
|
|
4365
|
+
return getReviewStatusSync(issueId);
|
|
4366
|
+
} catch {
|
|
4367
|
+
return null;
|
|
4368
|
+
}
|
|
4369
|
+
}
|
|
4370
|
+
function defaultReadStatusOverrides(workspacePath, issueId) {
|
|
4371
|
+
const normalized = issueId.toUpperCase();
|
|
4372
|
+
const record = readIssueRecordForWorkspaceSync(workspacePath, normalized);
|
|
4373
|
+
if (record === null && existsSync$1(getIssueRecordPathForWorkspace(workspacePath, normalized))) {
|
|
4374
|
+
console.warn(`[swarm] record unreadable for ${normalized} — treating as no overrides; see .corrupt sidecar`);
|
|
4375
|
+
return;
|
|
4376
|
+
}
|
|
4377
|
+
return record?.statusOverrides;
|
|
4378
|
+
}
|
|
4379
|
+
/**
|
|
4380
|
+
* PAN-2372 WI-4 / FR-6: read a slot's durable completion marker from the record
|
|
4381
|
+
* door. Returns undefined when no record or no marker exists. Used by
|
|
4382
|
+
* classifyInFlightSlots to recognize a slot whose `pan done` durably recorded
|
|
4383
|
+
* completion even when the runtime plane (agent state, tmux session) is gone.
|
|
4384
|
+
*/
|
|
4385
|
+
function defaultReadSlotCompletion(workspacePath, issueId, slotIndex) {
|
|
4386
|
+
try {
|
|
4387
|
+
return readIssueRecordForWorkspaceSync(workspacePath, issueId.toUpperCase())?.swarm?.slotCompletions?.[String(slotIndex)];
|
|
4388
|
+
} catch {
|
|
4389
|
+
return;
|
|
4390
|
+
}
|
|
4391
|
+
}
|
|
4392
|
+
function defaultShouldDispatch(issueId) {
|
|
4393
|
+
if (isDeaconGloballyPausedSync()) return false;
|
|
4394
|
+
const hold = defaultGetIssueHold(issueId);
|
|
4395
|
+
return !(hold?.stuck || hold?.deaconIgnored);
|
|
4396
|
+
}
|
|
4397
|
+
async function swarmJanitorPass(deps = defaultDeps$2) {
|
|
4398
|
+
const actions = [];
|
|
4399
|
+
const sessions = await deps.listSessionNames();
|
|
4400
|
+
for (const workspace of deps.listFeatureWorkspaces()) {
|
|
4401
|
+
const issueId = workspace.issueId.toUpperCase();
|
|
4402
|
+
const spec = await Effect.runPromise((deps.findSpecByIssue ?? findSpecByIssue)(workspace.projectPath, issueId));
|
|
4403
|
+
if (!spec) continue;
|
|
4404
|
+
const reconciled = await deps.reconcileSlotState(issueId, workspace.workspacePath, spec.document);
|
|
4405
|
+
actions.push(`[swarm-janitor] enumerated ${issueId}`);
|
|
4406
|
+
actions.push(...await gcMergedSlots(issueId, workspace.workspacePath, reconciled.merged, deps));
|
|
4407
|
+
actions.push(...await gcOrphanedSlots(issueId, workspace.workspacePath, reconciled, deps));
|
|
4408
|
+
const automatic = (deps.resolveAutomaticSwarmPolicy ?? resolveAutomaticSwarmPolicy)(issueId, analyzeSwarmReadiness(spec.document).swarmEligible);
|
|
4409
|
+
actions.push(...await maintainSwarmForeman(issueId, workspace.workspacePath, reconciled, sessions, deps, automatic.policy.mode !== "off", automatic.spawnForeman));
|
|
4410
|
+
const classified = await classifyInFlightSlots(reconciled.inFlight, {
|
|
4411
|
+
...deps,
|
|
4412
|
+
listSessionNames: async () => sessions
|
|
4413
|
+
}, {
|
|
4414
|
+
issueId,
|
|
4415
|
+
workspacePath: workspace.workspacePath
|
|
4416
|
+
});
|
|
4417
|
+
for (const slot of classified.filter((candidate) => candidate.signal === "stall-event")) {
|
|
4418
|
+
await deps.sendStallEvent?.(`agent-${issueId.toLowerCase()}`, `[swarm-event] slot ${slot.slotIndex} stalled (no progress ${Math.floor((slot.stalledForMs ?? 0) / 6e4)}m)`);
|
|
4419
|
+
actions.push(`[swarm-janitor] notified ${issueId} foreman that slot ${slot.slotIndex} stalled`);
|
|
4420
|
+
}
|
|
4421
|
+
}
|
|
4422
|
+
return actions;
|
|
4423
|
+
}
|
|
4424
|
+
async function classifyInFlightSlots(slots, deps = defaultDeps$2, options = {}) {
|
|
4425
|
+
const sessionNames = new Set(await deps.listSessionNames());
|
|
4426
|
+
const classified = [];
|
|
4427
|
+
const now = options.now ?? Date.now();
|
|
4428
|
+
const stallThresholdMs = options.stallThresholdMs ?? swarmStallThresholdMs();
|
|
4429
|
+
for (const slot of slots) {
|
|
4430
|
+
if (options.workspacePath && options.issueId) {
|
|
4431
|
+
const completion = await (deps.readSlotCompletion ?? defaultReadSlotCompletion)(options.workspacePath, options.issueId, slot.slotIndex);
|
|
4432
|
+
if (completion) {
|
|
4433
|
+
if (completion.itemId === slot.itemId) {
|
|
4434
|
+
classified.push({
|
|
4435
|
+
...slot,
|
|
4436
|
+
lifecycle: "ready-to-merge",
|
|
4437
|
+
exitStatus: 0,
|
|
4438
|
+
signal: "durable-completion"
|
|
4439
|
+
});
|
|
4440
|
+
continue;
|
|
4441
|
+
}
|
|
4442
|
+
await (deps.clearSlotCompletion ?? clearSwarmSlotCompletion)(options.workspacePath, options.issueId, slot.slotIndex);
|
|
4443
|
+
}
|
|
4444
|
+
}
|
|
4445
|
+
const durableReady = await classifyDurableReadySlot(slot, deps, options);
|
|
4446
|
+
if (!slot.agentId) {
|
|
4447
|
+
if (durableReady) {
|
|
4448
|
+
classified.push(durableReady);
|
|
4449
|
+
continue;
|
|
4450
|
+
}
|
|
4451
|
+
classified.push({
|
|
4452
|
+
...slot,
|
|
4453
|
+
lifecycle: "failed",
|
|
4454
|
+
reason: "missing-agent"
|
|
4455
|
+
});
|
|
4456
|
+
continue;
|
|
4457
|
+
}
|
|
4458
|
+
if (!sessionNames.has(slot.agentId)) {
|
|
4459
|
+
if (durableReady) {
|
|
4460
|
+
classified.push(durableReady);
|
|
4461
|
+
continue;
|
|
4462
|
+
}
|
|
4463
|
+
classified.push({
|
|
4464
|
+
...slot,
|
|
4465
|
+
lifecycle: "failed",
|
|
4466
|
+
reason: "vanished-session"
|
|
4467
|
+
});
|
|
4468
|
+
continue;
|
|
4469
|
+
}
|
|
4470
|
+
if (!await deps.isPaneDead(slot.agentId)) {
|
|
4471
|
+
const outputDigest = deps.getPaneOutputDigest ? await deps.getPaneOutputDigest(slot.agentId).catch(() => "") : "";
|
|
4472
|
+
const commitTime = slot.branch && options.workspacePath && deps.getBranchTipCommitTime ? await deps.getBranchTipCommitTime(options.workspacePath, slot.branch).catch(() => null) : null;
|
|
4473
|
+
const progressKey = slotProgressKey(slot);
|
|
4474
|
+
const previous = slotProgressObservations.get(progressKey);
|
|
4475
|
+
if (!previous || previous.commitTime !== commitTime || previous.outputDigest !== outputDigest) {
|
|
4476
|
+
slotProgressObservations.set(progressKey, {
|
|
4477
|
+
commitTime,
|
|
4478
|
+
outputDigest,
|
|
4479
|
+
lastProgressAt: now
|
|
4480
|
+
});
|
|
4481
|
+
if (options.workspacePath && options.issueId) await clearSwarmCompletionObservation(options.workspacePath, options.issueId, progressKey, deps);
|
|
4482
|
+
classified.push({
|
|
4483
|
+
...slot,
|
|
4484
|
+
lifecycle: "running"
|
|
4485
|
+
});
|
|
4486
|
+
continue;
|
|
4487
|
+
}
|
|
4488
|
+
const stalledForMs = now - previous.lastProgressAt;
|
|
4489
|
+
if (stalledForMs > stallThresholdMs) {
|
|
4490
|
+
const awaitingSignal = await classifyDoneWithoutSignal(slot, deps, options, {
|
|
4491
|
+
commitTime,
|
|
4492
|
+
outputDigest,
|
|
4493
|
+
progressKey,
|
|
4494
|
+
stalledForMs
|
|
4495
|
+
});
|
|
4496
|
+
if (awaitingSignal) {
|
|
4497
|
+
classified.push(awaitingSignal);
|
|
4498
|
+
continue;
|
|
4499
|
+
}
|
|
4500
|
+
classified.push({
|
|
4501
|
+
...slot,
|
|
4502
|
+
lifecycle: "stalled",
|
|
4503
|
+
reason: "no-progress-timeout",
|
|
4504
|
+
stalledForMs,
|
|
4505
|
+
...!previous.stallNotified ? { signal: "stall-event" } : {}
|
|
4506
|
+
});
|
|
4507
|
+
previous.stallNotified = true;
|
|
4508
|
+
continue;
|
|
4509
|
+
}
|
|
4510
|
+
classified.push({
|
|
4511
|
+
...slot,
|
|
4512
|
+
lifecycle: "running"
|
|
4513
|
+
});
|
|
4514
|
+
continue;
|
|
4515
|
+
}
|
|
4516
|
+
const exitStatus = await deps.getPaneExitStatus(slot.agentId);
|
|
4517
|
+
if (exitStatus === 0) {
|
|
4518
|
+
classified.push({
|
|
4519
|
+
...slot,
|
|
4520
|
+
lifecycle: "ready-to-merge",
|
|
4521
|
+
exitStatus
|
|
4522
|
+
});
|
|
4523
|
+
continue;
|
|
4524
|
+
}
|
|
4525
|
+
classified.push({
|
|
4526
|
+
...slot,
|
|
4527
|
+
lifecycle: "failed",
|
|
4528
|
+
exitStatus,
|
|
4529
|
+
reason: exitStatus === null ? "pane-exit-unknown" : "pane-exit-nonzero"
|
|
4530
|
+
});
|
|
4531
|
+
}
|
|
4532
|
+
return classified;
|
|
4533
|
+
}
|
|
4534
|
+
function slotProgressKey(slot) {
|
|
4535
|
+
return slot.agentId ?? slot.branch ?? `${slot.itemId}:${slot.slotIndex}`;
|
|
4536
|
+
}
|
|
4537
|
+
function swarmStallThresholdMs() {
|
|
4538
|
+
const raw = process.env.PAN_SWARM_STALL_THRESHOLD_MS;
|
|
4539
|
+
if (!raw) return DEFAULT_SWARM_STALL_THRESHOLD_MS;
|
|
4540
|
+
const parsed = Number(raw);
|
|
4541
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_SWARM_STALL_THRESHOLD_MS;
|
|
4542
|
+
}
|
|
4543
|
+
function getSwarmFinalizedAt(issueId, workspacePath) {
|
|
4544
|
+
return workspacePath ? readIssueRecordForWorkspaceSync(workspacePath, issueId.toUpperCase())?.swarm?.finalizedAt : void 0;
|
|
4545
|
+
}
|
|
4546
|
+
async function recordSwarmFinalizedAt(issueId, workspacePath, finalizedAt) {
|
|
4547
|
+
await writeSwarmFinalizedAt(workspacePath, issueId.toUpperCase(), finalizedAt);
|
|
4548
|
+
}
|
|
4549
|
+
function recordSlotAssignment(workspacePath, issueId, assignment) {
|
|
4550
|
+
return updateIssueRecordForWorkspace(workspacePath, issueId.toUpperCase(), (record) => {
|
|
4551
|
+
const slotAssignments = [...(record.swarm?.slotAssignments ?? []).filter((slot) => slot.slotIndex !== assignment.slotIndex && slot.itemId !== assignment.itemId), {
|
|
4552
|
+
...assignment,
|
|
4553
|
+
assignedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4554
|
+
}].sort((a, b) => a.slotIndex - b.slotIndex);
|
|
4555
|
+
const slotCompletions = { ...record.swarm?.slotCompletions ?? {} };
|
|
4556
|
+
delete slotCompletions[String(assignment.slotIndex)];
|
|
4557
|
+
return {
|
|
4558
|
+
...record,
|
|
4559
|
+
swarm: {
|
|
4560
|
+
...record.swarm ?? {},
|
|
4561
|
+
slotAssignments,
|
|
4562
|
+
slotCompletions
|
|
4563
|
+
}
|
|
4564
|
+
};
|
|
4565
|
+
}).then(() => void 0);
|
|
4566
|
+
}
|
|
4567
|
+
function clearSlotAssignment(workspacePath, issueId, slotIndex, itemId) {
|
|
4568
|
+
return clearSwarmSlotOwnership(workspacePath, issueId, slotIndex, itemId);
|
|
4569
|
+
}
|
|
4570
|
+
/**
|
|
4571
|
+
* Whether the issue may register another slot. Counts only tmux-ALIVE slot
|
|
4572
|
+
* sessions (stale agents-table rows blocked all dispatch at zero live slots
|
|
4573
|
+
* after a reset), capped by the same swarm reserve tryReserveSwarmSlot
|
|
4574
|
+
* enforces — never maxWorkAgents (PAN-2214).
|
|
4575
|
+
*/
|
|
4576
|
+
function registeredSlotCapacityAvailable(issueId, selectedCount, liveSlotCount = countRunningSwarmSlotsForIssue(issueId), limits = getConcurrencyLimits()) {
|
|
4577
|
+
return liveSlotCount + selectedCount < limits.reservedSwarmSlots;
|
|
4578
|
+
}
|
|
4579
|
+
//#endregion
|
|
4580
|
+
//#region ../../lib/cloister/boot-reconciliation-predicates.ts
|
|
2963
4581
|
init_agent_state();
|
|
2964
4582
|
init_review_status();
|
|
2965
4583
|
function bootReconciliationSkipReason(agent) {
|
|
@@ -2973,6 +4591,7 @@ function bootReconciliationSkipReason(agent) {
|
|
|
2973
4591
|
}
|
|
2974
4592
|
//#endregion
|
|
2975
4593
|
//#region ../../lib/cloister/boot-reconciliation.ts
|
|
4594
|
+
init_control_settings();
|
|
2976
4595
|
init_agents();
|
|
2977
4596
|
init_persistent_logger();
|
|
2978
4597
|
init_config$1();
|
|
@@ -3183,7 +4802,6 @@ function startBootReconciliation(options = {}) {
|
|
|
3183
4802
|
}
|
|
3184
4803
|
//#endregion
|
|
3185
4804
|
//#region ../../lib/agents/pinned-launch.ts
|
|
3186
|
-
init_dag();
|
|
3187
4805
|
init_agent_state();
|
|
3188
4806
|
function parseFlagValue(content, flag) {
|
|
3189
4807
|
const escapedFlag = flag.replace("-", "\\-");
|
|
@@ -3284,6 +4902,8 @@ init_review_status();
|
|
|
3284
4902
|
init_control_settings();
|
|
3285
4903
|
init_agents$1();
|
|
3286
4904
|
init_tmux();
|
|
4905
|
+
init_io();
|
|
4906
|
+
init_dag();
|
|
3287
4907
|
const orphanFailureRecordedForAutoResume = /* @__PURE__ */ new Set();
|
|
3288
4908
|
function isVerifyPausedAgentState(state) {
|
|
3289
4909
|
if (state.paused !== true || !state.issueId) return false;
|
|
@@ -3539,7 +5159,7 @@ async function nudgeStalledResumeWorkAgents() {
|
|
|
3539
5159
|
const message = buildStalledResumePrompt(state);
|
|
3540
5160
|
if (!message) continue;
|
|
3541
5161
|
try {
|
|
3542
|
-
const { messageAgent } = await import("./agents-
|
|
5162
|
+
const { messageAgent } = await import("./agents-DpCuoF65.js");
|
|
3543
5163
|
await messageAgent(agentId, message);
|
|
3544
5164
|
recordCooldown(agentId, ".last-stalled-resume-nudge");
|
|
3545
5165
|
const action = `Re-sent stalled resume prompt to ${agentId} (${state.issueId})`;
|
|
@@ -3653,7 +5273,7 @@ async function nudgeIdleWorkAgentsWithOpenBeads() {
|
|
|
3653
5273
|
"Continue the per-item workflow without asking — claim it (`pan task claim <item-id>`), implement, commit, push, then complete it through `pan task done <item-id>`. Inspection is conditional on metadata.requiresInspection (default false; check the plan item before deciding to call `pan inspect`). Do NOT end your turn with a multi-paragraph summary; just advance to the next bead."
|
|
3654
5274
|
].join("\n");
|
|
3655
5275
|
try {
|
|
3656
|
-
const { messageAgent } = await import("./agents-
|
|
5276
|
+
const { messageAgent } = await import("./agents-DpCuoF65.js");
|
|
3657
5277
|
await messageAgent(agentId, message);
|
|
3658
5278
|
writeFileSync(join$1(getAgentDir(agentId), ".last-bead-nudge"), String(Date.now()), "utf-8");
|
|
3659
5279
|
const action = `Nudged idle ${agentId} (${state.issueId}) — ${openTasks.length} ready task(s)`;
|
|
@@ -3713,6 +5333,10 @@ async function handleAgentStoppedEvent$1(agentId, opts = {}, deps) {
|
|
|
3713
5333
|
logDeaconEventSync(`handleAgentStoppedEvent: ${agentId} skipped — role=${state.role} (not auto-resumable)`);
|
|
3714
5334
|
return null;
|
|
3715
5335
|
}
|
|
5336
|
+
if (state.foreman === true) {
|
|
5337
|
+
logDeaconEventSync(`handleAgentStoppedEvent: ${agentId} skipped — swarm janitor owns foreman liveness`);
|
|
5338
|
+
return null;
|
|
5339
|
+
}
|
|
3716
5340
|
if (state.phase === "retained-transcripts") {
|
|
3717
5341
|
logDeaconEventSync(`handleAgentStoppedEvent: ${agentId} skipped — retired (transcripts retained)`);
|
|
3718
5342
|
return null;
|
|
@@ -4191,6 +5815,7 @@ const cleanupClosedIssueAgentDirectories = (options) => Effect.tryPromise({
|
|
|
4191
5815
|
//#region ../../lib/cloister/transcript-retention.ts
|
|
4192
5816
|
init_state_dir_removal();
|
|
4193
5817
|
init_agents();
|
|
5818
|
+
init_record();
|
|
4194
5819
|
init_paths();
|
|
4195
5820
|
init_tmux();
|
|
4196
5821
|
function isTranscriptRetentionTerminalAgent(agent, readRecord = readIssueRecordForWorkspaceSync) {
|
|
@@ -4384,36 +6009,8 @@ async function sweepTranscriptRetention(options) {
|
|
|
4384
6009
|
return [action];
|
|
4385
6010
|
}
|
|
4386
6011
|
//#endregion
|
|
4387
|
-
//#region ../../lib/pan-dir/record-list.ts
|
|
4388
|
-
init_state_home();
|
|
4389
|
-
/** Canonical bounded enumeration facet of the issue-record read door. */
|
|
4390
|
-
init_record();
|
|
4391
|
-
async function readIssueRecordDirectory(recordsDir, records) {
|
|
4392
|
-
const entries = await promises$1.readdir(recordsDir, { withFileTypes: true }).catch(() => []);
|
|
4393
|
-
await Promise.all(entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map(async (entry) => {
|
|
4394
|
-
try {
|
|
4395
|
-
const record = JSON.parse(await promises$1.readFile(join(recordsDir, entry.name), "utf-8"));
|
|
4396
|
-
if (record.issueId) records.set(record.issueId.toUpperCase(), record);
|
|
4397
|
-
} catch (error) {
|
|
4398
|
-
console.warn(`[record] Preserving unreadable issue record ${join(recordsDir, entry.name)} during enumeration: ${error instanceof Error ? error.message : String(error)}`);
|
|
4399
|
-
}
|
|
4400
|
-
}));
|
|
4401
|
-
}
|
|
4402
|
-
async function listIssueRecords(project) {
|
|
4403
|
-
const stateHome = resolveStateReadHomeSync(project);
|
|
4404
|
-
const records = /* @__PURE__ */ new Map();
|
|
4405
|
-
if (stateHome.migrated) {
|
|
4406
|
-
await readIssueRecordDirectory(join(stateHome.root, RECORD_DIRNAME), records);
|
|
4407
|
-
return [...records.values()];
|
|
4408
|
-
}
|
|
4409
|
-
await readIssueRecordDirectory(join(project.path, ".pan", RECORD_DIRNAME), records);
|
|
4410
|
-
const workspacesDir = join(project.path, "workspaces");
|
|
4411
|
-
const workspaces = await promises$1.readdir(workspacesDir, { withFileTypes: true }).catch(() => []);
|
|
4412
|
-
await Promise.all(workspaces.filter((entry) => entry.isDirectory()).map((entry) => readIssueRecordDirectory(join(workspacesDir, entry.name, ".pan", RECORD_DIRNAME), records)));
|
|
4413
|
-
return [...records.values()];
|
|
4414
|
-
}
|
|
4415
|
-
//#endregion
|
|
4416
6012
|
//#region ../../lib/cloister/state-plane-patrol.ts
|
|
6013
|
+
init_record_update();
|
|
4417
6014
|
init_issue_closed();
|
|
4418
6015
|
function statePlaneReconcileEveryCycles(patrolIntervalMs) {
|
|
4419
6016
|
return Math.max(1, Math.ceil(3600 * 1e3 / patrolIntervalMs));
|
|
@@ -4497,6 +6094,7 @@ const recreatedStateWarnings = createRecreatedStateWarningReporter();
|
|
|
4497
6094
|
//#endregion
|
|
4498
6095
|
//#region ../../lib/cloister/deacon-main-divergence.ts
|
|
4499
6096
|
init_projects();
|
|
6097
|
+
init_state_plane();
|
|
4500
6098
|
async function recordMainDivergenceHealth(state, projects = listProjectsSync(), measure = getMainDivergence) {
|
|
4501
6099
|
const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4502
6100
|
const records = [];
|
|
@@ -4536,6 +6134,7 @@ function shouldRunRecoveryJanitor(janitor, patrolCycle) {
|
|
|
4536
6134
|
//#region ../../lib/cloister/pending-promotion-reconciler.ts
|
|
4537
6135
|
init_activity_logger();
|
|
4538
6136
|
init_internal_token();
|
|
6137
|
+
init_specs();
|
|
4539
6138
|
init_projects();
|
|
4540
6139
|
init_io();
|
|
4541
6140
|
init_issue_closed();
|
|
@@ -5777,8 +7376,8 @@ async function cleanupStaleAgentState() {
|
|
|
5777
7376
|
*/
|
|
5778
7377
|
async function cleanupAbandonedFeedback() {
|
|
5779
7378
|
const actions = [];
|
|
5780
|
-
const { getReviewStatusSync } = await import("./review-status-
|
|
5781
|
-
const { clearFeedbackFiles } = await import("./feedback-writer-
|
|
7379
|
+
const { getReviewStatusSync } = await import("./review-status-C_kQeeX0.js");
|
|
7380
|
+
const { clearFeedbackFiles } = await import("./feedback-writer-CH2846DP.js");
|
|
5782
7381
|
for (const { issueId, workspacePath } of listFeatureWorkspaces()) {
|
|
5783
7382
|
const panFeedbackDir = join$1(workspacePath, ".pan", "feedback");
|
|
5784
7383
|
if (!existsSync(panFeedbackDir)) continue;
|
|
@@ -5809,7 +7408,7 @@ async function cleanupAbandonedFeedback() {
|
|
|
5809
7408
|
const ORPHAN_REVIEWER_AGE_MS = 3600 * 1e3;
|
|
5810
7409
|
async function loadTmuxSessionsWithCreationTimes() {
|
|
5811
7410
|
try {
|
|
5812
|
-
const { stdout } = await execAsync(`tmux -L
|
|
7411
|
+
const { stdout } = await execAsync(`tmux -L ${getManagedTmuxSocketName()} -f ${join$1(OVERDECK_HOME, "tmux", "overdeck.tmux.conf")} list-sessions -F '#{session_name} #{session_created}'`, { encoding: "utf-8" });
|
|
5813
7412
|
const lines = stdout.split("\n").filter((l) => l.trim());
|
|
5814
7413
|
const sessions = [];
|
|
5815
7414
|
const creationTimes = /* @__PURE__ */ new Map();
|
|
@@ -5849,7 +7448,7 @@ async function handleOrphanReviewerSession(sessionName, sessions, creationTimes,
|
|
|
5849
7448
|
if (!issueId) return null;
|
|
5850
7449
|
const agentSession = `agent-${issueId.toLowerCase()}`;
|
|
5851
7450
|
if (sessions.includes(agentSession)) return null;
|
|
5852
|
-
const { getReviewStatusSync } = await import("./review-status-
|
|
7451
|
+
const { getReviewStatusSync } = await import("./review-status-C_kQeeX0.js");
|
|
5853
7452
|
try {
|
|
5854
7453
|
if (getReviewStatusSync(issueId)?.reviewStatus === "reviewing") return null;
|
|
5855
7454
|
} catch {}
|
|
@@ -5971,7 +7570,7 @@ async function checkOrphanedCompletions() {
|
|
|
5971
7570
|
async function checkPendingTestDispatch() {
|
|
5972
7571
|
const actions = [];
|
|
5973
7572
|
try {
|
|
5974
|
-
const { loadReviewStatuses, setReviewStatusSync } = await import("./review-status-
|
|
7573
|
+
const { loadReviewStatuses, setReviewStatusSync } = await import("./review-status-C_kQeeX0.js");
|
|
5975
7574
|
const statuses = loadReviewStatuses();
|
|
5976
7575
|
const now = Date.now();
|
|
5977
7576
|
for (const [issueId, status] of Object.entries(statuses)) {
|
|
@@ -6004,7 +7603,7 @@ async function checkPendingTestDispatch() {
|
|
|
6004
7603
|
console.log(`[deacon] ${issueId}: skipping test re-dispatch — issue is closed`);
|
|
6005
7604
|
continue;
|
|
6006
7605
|
}
|
|
6007
|
-
const { resolveProjectFromIssueSync } = await import("./projects-
|
|
7606
|
+
const { resolveProjectFromIssueSync } = await import("./projects-LftaVK99.js");
|
|
6008
7607
|
const resolved = resolveProjectFromIssueSync(issueId);
|
|
6009
7608
|
if (!resolved) continue;
|
|
6010
7609
|
const issueLower = issueId.toLowerCase();
|
|
@@ -6019,8 +7618,8 @@ async function checkPendingTestDispatch() {
|
|
|
6019
7618
|
logDeaconEventSync(`checkPendingTestDispatch: deferred test for ${issueId} — advancing ceiling reached (PAN-1665) — ${describeRunningAgents()}`);
|
|
6020
7619
|
continue;
|
|
6021
7620
|
}
|
|
6022
|
-
const { spawnRun } = await import("./agents-
|
|
6023
|
-
const { buildTestRolePrompt } = await import("./test-agent-queue-
|
|
7621
|
+
const { spawnRun } = await import("./agents-DpCuoF65.js");
|
|
7622
|
+
const { buildTestRolePrompt } = await import("./test-agent-queue-C5DI_gWX.js");
|
|
6024
7623
|
try {
|
|
6025
7624
|
const run = await spawnRun(issueId, "test", {
|
|
6026
7625
|
workspace,
|
|
@@ -6150,7 +7749,7 @@ async function checkCompletedButUnsignaledTests() {
|
|
|
6150
7749
|
const cmd = `pan admin specialists done test ${issueId} --status ${decision.status}${noteArg}${decision.uatStatus ? ` --uat-status ${decision.uatStatus}${decision.uatNotes ? ` --uat-notes "${decision.uatNotes.replace(/"/g, "'").slice(0, 120)}"` : ""}` : ""}`;
|
|
6151
7750
|
const nudge = `Your test verdict (${decision.status}) is already written to .pan/test/result.json. Your ONLY remaining task is to execute this Bash command immediately — do not analyze, do not summarize, do not ask questions, just run it:\n\n${cmd}\n\nRun this command NOW. Do not write any other response before executing it.`;
|
|
6152
7751
|
try {
|
|
6153
|
-
const { messageAgent } = await import("./agents-
|
|
7752
|
+
const { messageAgent } = await import("./agents-DpCuoF65.js");
|
|
6154
7753
|
await messageAgent(testSession, nudge);
|
|
6155
7754
|
unsignaledTestNudges.set(testSession, now);
|
|
6156
7755
|
const msg = `Nudged ${testSession} to signal ${decision.status} (verdict artifact present)`;
|
|
@@ -6164,7 +7763,7 @@ async function checkCompletedButUnsignaledTests() {
|
|
|
6164
7763
|
case "nudge-write": {
|
|
6165
7764
|
const nudge = `Your test run for ${issueId} looks finished but no verdict was recorded. Decide the automated-gate verdict and, when UAT was required, its separate verdict. Then do BOTH now: (1) write .pan/test/result.json as {"status":"passed"|"failed","notes":"<gate evidence>","uatStatus":"passed"|"failed","uatNotes":"<UAT evidence>"} (omit UAT fields only when UAT was not required), and (2) run: pan admin specialists done test ${issueId} --status passed|failed [--uat-status passed|failed]. Do this immediately — do not summarize or ask questions, just write the file and run the command.`;
|
|
6166
7765
|
try {
|
|
6167
|
-
const { messageAgent } = await import("./agents-
|
|
7766
|
+
const { messageAgent } = await import("./agents-DpCuoF65.js");
|
|
6168
7767
|
await messageAgent(testSession, nudge);
|
|
6169
7768
|
unsignaledTestNudges.set(testSession, now);
|
|
6170
7769
|
const msg = `Nudged ${testSession} to write+signal a test verdict (no artifact yet)`;
|
|
@@ -6196,8 +7795,8 @@ async function checkCompletedButUnsignaledTests() {
|
|
|
6196
7795
|
async function checkVerificationReviewContradiction() {
|
|
6197
7796
|
const actions = [];
|
|
6198
7797
|
try {
|
|
6199
|
-
const { loadReviewStatuses } = await import("./review-status-
|
|
6200
|
-
const { resolveProjectFromIssueSync } = await import("./projects-
|
|
7798
|
+
const { loadReviewStatuses } = await import("./review-status-C_kQeeX0.js");
|
|
7799
|
+
const { resolveProjectFromIssueSync } = await import("./projects-LftaVK99.js");
|
|
6201
7800
|
const statuses = loadReviewStatuses();
|
|
6202
7801
|
for (const [issueId, status] of Object.entries(statuses)) if (status.verificationStatus === "passed" && status.reviewStatus === "reviewing" && status.stuckReason === "review_infrastructure_failure") {
|
|
6203
7802
|
let reviewedAtCommit;
|
|
@@ -6206,12 +7805,12 @@ async function checkVerificationReviewContradiction() {
|
|
|
6206
7805
|
if (project) {
|
|
6207
7806
|
const workspacePath = join$1(project.projectPath, "workspaces", `feature-${issueId.toLowerCase()}`);
|
|
6208
7807
|
if (existsSync(workspacePath)) {
|
|
6209
|
-
const { snapshotWorkspaceHeadsPromise } = await import("./git-utils-
|
|
7808
|
+
const { snapshotWorkspaceHeadsPromise } = await import("./git-utils-CsKV0Dis.js");
|
|
6210
7809
|
reviewedAtCommit = await snapshotWorkspaceHeadsPromise(issueId, workspacePath);
|
|
6211
7810
|
}
|
|
6212
7811
|
}
|
|
6213
7812
|
} catch {}
|
|
6214
|
-
const { recordReviewVerdict } = await import("./review-verdict-writer-
|
|
7813
|
+
const { recordReviewVerdict } = await import("./review-verdict-writer-CD-4kDVx.js");
|
|
6215
7814
|
const bypass = await recordReviewVerdict(issueId, {
|
|
6216
7815
|
verdict: "passed",
|
|
6217
7816
|
notes: "Review bypassed: verification passed but review infrastructure repeatedly failed.",
|
|
@@ -6306,7 +7905,7 @@ async function checkReadyForMergeStuck() {
|
|
|
6306
7905
|
const testStatusGreenCiReconcileCooldowns = /* @__PURE__ */ new Map();
|
|
6307
7906
|
const TEST_STATUS_GREEN_CI_RECONCILE_COOLDOWN_MS = 300 * 1e3;
|
|
6308
7907
|
async function reconcileTestStatusFromGreenCi() {
|
|
6309
|
-
const { getCiCheckRunsState, getPullRequestHeadState, isGitHubAppConfigured } = await import("./github-app-
|
|
7908
|
+
const { getCiCheckRunsState, getPullRequestHeadState, isGitHubAppConfigured } = await import("./github-app-Oi5hdn1n.js");
|
|
6310
7909
|
return reconcileTestStatusFromGreenCiWithDeps({
|
|
6311
7910
|
isGitHubAppConfigured,
|
|
6312
7911
|
loadReviewStatuses,
|
|
@@ -6327,7 +7926,7 @@ async function reconcileTestStatusFromGreenCi() {
|
|
|
6327
7926
|
*/
|
|
6328
7927
|
async function clearStaleCiFeedback(issueId) {
|
|
6329
7928
|
const { readdir, rm } = await import("fs/promises");
|
|
6330
|
-
const { resolveProjectFromIssueSync } = await import("./projects-
|
|
7929
|
+
const { resolveProjectFromIssueSync } = await import("./projects-LftaVK99.js");
|
|
6331
7930
|
const projectConfig = resolveProjectFromIssueSync(issueId);
|
|
6332
7931
|
if (!projectConfig) return;
|
|
6333
7932
|
const repoDir = projectConfig.projectPath;
|
|
@@ -6621,7 +8220,7 @@ async function checkWorkspaceContainerHealth(sharedState) {
|
|
|
6621
8220
|
}
|
|
6622
8221
|
}
|
|
6623
8222
|
try {
|
|
6624
|
-
const { resolveProjectFromIssueSync } = await import("./projects-
|
|
8223
|
+
const { resolveProjectFromIssueSync } = await import("./projects-LftaVK99.js");
|
|
6625
8224
|
const resolved = resolveProjectFromIssueSync(issueLower.toUpperCase());
|
|
6626
8225
|
if (resolved) await killOrphanedWorkspaceProcesses(`${resolved.projectPath}/workspaces/feature-${issueLower}`);
|
|
6627
8226
|
} catch {}
|
|
@@ -6670,8 +8269,8 @@ async function checkWorkspaceContainerHealth(sharedState) {
|
|
|
6670
8269
|
async function checkMergedWorkSessions() {
|
|
6671
8270
|
const actions = [];
|
|
6672
8271
|
try {
|
|
6673
|
-
const { selectMergedWorkSessions } = await import("./reap-terminal-sessions-
|
|
6674
|
-
const { setAgentPaused } = await import("./agents-
|
|
8272
|
+
const { selectMergedWorkSessions } = await import("./reap-terminal-sessions-DgEIKMhH.js");
|
|
8273
|
+
const { setAgentPaused } = await import("./agents-DpCuoF65.js");
|
|
6675
8274
|
const toKill = selectMergedWorkSessions(loadReviewStatuses(), [...await Effect.runPromise(listSessionNames())]);
|
|
6676
8275
|
for (const session of toKill) try {
|
|
6677
8276
|
await Effect.runPromise(setAgentPaused(session, "merged — awaiting close-out (verify on main)", true));
|
|
@@ -6711,7 +8310,7 @@ const AWAITING_TEST_IDLE_REAP_MS = 600 * 1e3;
|
|
|
6711
8310
|
async function checkAwaitingTestWorkSessions() {
|
|
6712
8311
|
const actions = [];
|
|
6713
8312
|
try {
|
|
6714
|
-
const { selectAwaitingTestWorkSessions } = await import("./reap-terminal-sessions-
|
|
8313
|
+
const { selectAwaitingTestWorkSessions } = await import("./reap-terminal-sessions-DgEIKMhH.js");
|
|
6715
8314
|
const candidates = selectAwaitingTestWorkSessions(loadReviewStatuses(), [...await Effect.runPromise(listSessionNames())]);
|
|
6716
8315
|
const now = Date.now();
|
|
6717
8316
|
for (const session of candidates) {
|
|
@@ -6764,7 +8363,7 @@ async function runPatrol() {
|
|
|
6764
8363
|
hasLoggedGlobalPauseSkip = false;
|
|
6765
8364
|
addLog("info", `Patrol cycle ${state.patrolCycle} — checking per-project specialists`, state.patrolCycle);
|
|
6766
8365
|
console.log(`[deacon] Patrol cycle ${state.patrolCycle} - checking per-project specialists`);
|
|
6767
|
-
for (const a of await (await import("./stale-task-claims-
|
|
8366
|
+
for (const a of await (await import("./stale-task-claims-C2e1zdMT.js")).patrolStaleTaskClaims()) {
|
|
6768
8367
|
actions.push(a);
|
|
6769
8368
|
addLog("action", a, state.patrolCycle);
|
|
6770
8369
|
}
|
|
@@ -6772,30 +8371,30 @@ async function runPatrol() {
|
|
|
6772
8371
|
actions.push(...stuckRemediationActions);
|
|
6773
8372
|
for (const a of stuckRemediationActions) addLog("action", a, state.patrolCycle);
|
|
6774
8373
|
try {
|
|
6775
|
-
const sweepRecommendations = await (await import("./stall-sweeper-
|
|
8374
|
+
const sweepRecommendations = await (await import("./stall-sweeper-QNk4h9m6.js")).runStallSweeperPatrol();
|
|
6776
8375
|
actions.push(...sweepRecommendations);
|
|
6777
8376
|
for (const recommendation of sweepRecommendations) addLog("info", recommendation, state.patrolCycle);
|
|
6778
8377
|
} catch (err) {
|
|
6779
8378
|
console.warn(`[deacon] stall sweeper patrol failed: ${err?.message ?? err}`);
|
|
6780
8379
|
addLog("warn", `Stall sweeper patrol failed: ${err?.message ?? err}`, state.patrolCycle);
|
|
6781
8380
|
}
|
|
6782
|
-
const { reconcileInFlightJournals } = await import("./advancing-selfheal-
|
|
8381
|
+
const { reconcileInFlightJournals } = await import("./advancing-selfheal-65W4yUQi.js");
|
|
6783
8382
|
const reconciledJournalActions = await reconcileInFlightJournals();
|
|
6784
8383
|
actions.push(...reconciledJournalActions);
|
|
6785
8384
|
for (const a of reconciledJournalActions) addLog("action", a, state.patrolCycle);
|
|
6786
|
-
for (const a of (await import("./feedback-stuck-retirement-
|
|
8385
|
+
for (const a of (await import("./feedback-stuck-retirement-DpA5u1kt.js")).retireResolvedFeedbackDeliveryStuckFlags()) {
|
|
6787
8386
|
actions.push(a);
|
|
6788
8387
|
addLog("action", a, state.patrolCycle);
|
|
6789
8388
|
}
|
|
6790
8389
|
try {
|
|
6791
8390
|
const pendingFile = join$1(OVERDECK_HOME, "pending-post-merge.json");
|
|
6792
|
-
const { processPendingLifecycleForPatrol } = await import("./deacon-pending-lifecycle-
|
|
8391
|
+
const { processPendingLifecycleForPatrol } = await import("./deacon-pending-lifecycle-BObEdT-L.js");
|
|
6793
8392
|
const action = await processPendingLifecycleForPatrol(pendingFile);
|
|
6794
8393
|
if (action) actions.push(action);
|
|
6795
8394
|
} catch (err) {
|
|
6796
8395
|
console.warn(`[deacon] Failed to process pending lifecycle: ${err.message}`);
|
|
6797
8396
|
}
|
|
6798
|
-
if (state.patrolCycle % 5 === 0) await (await import("./deploy-patrol-
|
|
8397
|
+
if (state.patrolCycle % 5 === 0) await (await import("./deploy-patrol-CEwwEYDM.js")).runScheduledDeployPatrol();
|
|
6799
8398
|
const livenessActions = await reconcileAgentLiveness();
|
|
6800
8399
|
actions.push(...livenessActions);
|
|
6801
8400
|
for (const a of livenessActions) addLog("action", a, state.patrolCycle);
|
|
@@ -6816,7 +8415,7 @@ async function runPatrol() {
|
|
|
6816
8415
|
const idleStackActions = await reconcileIdleWorkspaceStacks();
|
|
6817
8416
|
actions.push(...idleStackActions);
|
|
6818
8417
|
for (const a of idleStackActions) addLog("action", a, state.patrolCycle);
|
|
6819
|
-
for (const a of await (await import("./bridge-pool-patrol-
|
|
8418
|
+
for (const a of await (await import("./bridge-pool-patrol-Ce8rNPym.js")).patrolDockerBridgePool()) {
|
|
6820
8419
|
actions.push(a);
|
|
6821
8420
|
addLog("action", a, state.patrolCycle);
|
|
6822
8421
|
}
|
|
@@ -6836,7 +8435,7 @@ async function runPatrol() {
|
|
|
6836
8435
|
actions.push(...suspendActions);
|
|
6837
8436
|
for (const a of suspendActions) addLog("action", a, state.patrolCycle);
|
|
6838
8437
|
try {
|
|
6839
|
-
const { resolveProjectFromIssueSync } = await import("./projects-
|
|
8438
|
+
const { resolveProjectFromIssueSync } = await import("./projects-LftaVK99.js");
|
|
6840
8439
|
const allStatuses = loadReviewStatuses();
|
|
6841
8440
|
for (const [issueId, status] of Object.entries(allStatuses)) {
|
|
6842
8441
|
if (!status.readyForMerge || status.mergeStatus === "merged") continue;
|
|
@@ -6856,7 +8455,7 @@ async function runPatrol() {
|
|
|
6856
8455
|
} catch (err) {
|
|
6857
8456
|
console.warn(`[deacon] Failed to check workspace existence: ${err.message}`);
|
|
6858
8457
|
}
|
|
6859
|
-
const { checkMergedAdvancingSessions } = await import("./advancing-selfheal-
|
|
8458
|
+
const { checkMergedAdvancingSessions } = await import("./advancing-selfheal-65W4yUQi.js");
|
|
6860
8459
|
const reapedWorkActions = await checkMergedWorkSessions();
|
|
6861
8460
|
actions.push(...reapedWorkActions);
|
|
6862
8461
|
for (const a of reapedWorkActions) addLog("action", a, state.patrolCycle);
|
|
@@ -6864,7 +8463,7 @@ async function runPatrol() {
|
|
|
6864
8463
|
actions.push(...reapedMergedAdvancingActions);
|
|
6865
8464
|
for (const a of reapedMergedAdvancingActions) addLog("action", a, state.patrolCycle);
|
|
6866
8465
|
try {
|
|
6867
|
-
const { refreshClaudeCredentialsForActiveRemoteAgents } = await import("./remote-completion-
|
|
8466
|
+
const { refreshClaudeCredentialsForActiveRemoteAgents } = await import("./remote-completion-CBV3b9TI.js");
|
|
6868
8467
|
const remoteCredentialActions = await refreshClaudeCredentialsForActiveRemoteAgents();
|
|
6869
8468
|
actions.push(...remoteCredentialActions);
|
|
6870
8469
|
for (const a of remoteCredentialActions) addLog(a.includes("failed") ? "warn" : "action", a, state.patrolCycle);
|
|
@@ -6872,7 +8471,7 @@ async function runPatrol() {
|
|
|
6872
8471
|
addLog("warn", `Remote credential refresh patrol failed: ${err.message}`, state.patrolCycle);
|
|
6873
8472
|
}
|
|
6874
8473
|
try {
|
|
6875
|
-
const { refreshHostHeartbeatForEphemeralVms } = await import("./remote-agents-
|
|
8474
|
+
const { refreshHostHeartbeatForEphemeralVms } = await import("./remote-agents-BVtASiOF.js");
|
|
6876
8475
|
const heartbeatActions = await refreshHostHeartbeatForEphemeralVms();
|
|
6877
8476
|
actions.push(...heartbeatActions);
|
|
6878
8477
|
for (const a of heartbeatActions) addLog(a.includes("failed") ? "warn" : "action", a, state.patrolCycle);
|
|
@@ -6880,7 +8479,7 @@ async function runPatrol() {
|
|
|
6880
8479
|
addLog("warn", `Remote heartbeat refresh patrol failed: ${err.message}`, state.patrolCycle);
|
|
6881
8480
|
}
|
|
6882
8481
|
try {
|
|
6883
|
-
const { reapCompletedRemoteAgents } = await import("./remote-completion-
|
|
8482
|
+
const { reapCompletedRemoteAgents } = await import("./remote-completion-CBV3b9TI.js");
|
|
6884
8483
|
const remoteReap = await reapCompletedRemoteAgents();
|
|
6885
8484
|
for (const r of remoteReap) if (r.status === "handed-off" || r.status === "error") {
|
|
6886
8485
|
const msg = `Remote reap ${r.issueId}: ${r.status} (${r.details.join("; ")})`;
|
|
@@ -6963,7 +8562,7 @@ async function runPatrol() {
|
|
|
6963
8562
|
actions.push(a);
|
|
6964
8563
|
addLog("action", a, state.patrolCycle);
|
|
6965
8564
|
}
|
|
6966
|
-
const swarmActions = await
|
|
8565
|
+
const swarmActions = await swarmJanitorPass();
|
|
6967
8566
|
actions.push(...swarmActions);
|
|
6968
8567
|
for (const a of swarmActions) addLog("action", a, state.patrolCycle);
|
|
6969
8568
|
const staleMergeActions = await reconcileStaleMergeStatus();
|
|
@@ -7027,6 +8626,10 @@ async function runPatrol() {
|
|
|
7027
8626
|
actions.push(result.message);
|
|
7028
8627
|
addLog(result.level, result.message, state.patrolCycle);
|
|
7029
8628
|
}
|
|
8629
|
+
if (state.patrolCycle % statePlaneReconcileEveryCycles(config.patrolIntervalMs) === 0) for (const result of await reconcileTerminalIssueResidue(projectConfigs)) {
|
|
8630
|
+
actions.push(result.message);
|
|
8631
|
+
addLog(result.level, result.message, state.patrolCycle);
|
|
8632
|
+
}
|
|
7030
8633
|
for (const warning of await recreatedStateWarnings(projectConfigs)) addLog(warning.level, warning.message, state.patrolCycle);
|
|
7031
8634
|
const divergenceWarnings = await recordMainDivergenceHealth(state, projectConfigs);
|
|
7032
8635
|
for (const warning of divergenceWarnings) addLog("warn", warning, state.patrolCycle);
|
|
@@ -7076,7 +8679,7 @@ async function runPatrol() {
|
|
|
7076
8679
|
const issueId = runtimeState.currentIssue;
|
|
7077
8680
|
try {
|
|
7078
8681
|
if (getReviewStatusSync(issueId)?.mergeStatus === "merging") {
|
|
7079
|
-
const { resolveProjectFromIssueSync } = await import("./projects-
|
|
8682
|
+
const { resolveProjectFromIssueSync } = await import("./projects-LftaVK99.js");
|
|
7080
8683
|
const resolved = resolveProjectFromIssueSync(issueId);
|
|
7081
8684
|
if (resolved) {
|
|
7082
8685
|
const branch = `feature/${issueId.toLowerCase()}`;
|
|
@@ -7090,7 +8693,7 @@ async function runPatrol() {
|
|
|
7090
8693
|
mergeStatus: "merged",
|
|
7091
8694
|
readyForMerge: false
|
|
7092
8695
|
});
|
|
7093
|
-
const { postMergeLifecycle } = await import("./merge-agent-
|
|
8696
|
+
const { postMergeLifecycle } = await import("./merge-agent-BuoC_PRZ.js");
|
|
7094
8697
|
postMergeLifecycle(issueId, resolved.projectPath).catch((err) => console.warn(`[deacon] postMergeLifecycle failed for ${issueId}: ${err}`));
|
|
7095
8698
|
actions.push(`Auto-completed stale merge for ${issueId}`);
|
|
7096
8699
|
} else {
|
|
@@ -7270,7 +8873,7 @@ function startDeacon() {
|
|
|
7270
8873
|
logDeaconEventSync(`startDeacon: health monitor starting (patrol every ${config.patrolIntervalMs / 1e3}s)`);
|
|
7271
8874
|
(async () => {
|
|
7272
8875
|
await reconcileAgentLiveness();
|
|
7273
|
-
const { runBootAdvancingSelfHeal } = await import("./advancing-selfheal-
|
|
8876
|
+
const { runBootAdvancingSelfHeal } = await import("./advancing-selfheal-65W4yUQi.js");
|
|
7274
8877
|
await runBootAdvancingSelfHeal();
|
|
7275
8878
|
runScheduledPatrol("startup");
|
|
7276
8879
|
})().catch((err) => {
|
|
@@ -7283,7 +8886,7 @@ function startDeacon() {
|
|
|
7283
8886
|
}, config.patrolIntervalMs);
|
|
7284
8887
|
memoryPatrolInterval = setInterval(() => {
|
|
7285
8888
|
(async () => {
|
|
7286
|
-
const { patrolMemoryPressure } = await import("./memory-pressure-patrol-
|
|
8889
|
+
const { patrolMemoryPressure } = await import("./memory-pressure-patrol-DPb8foam.js");
|
|
7287
8890
|
for (const action of await patrolMemoryPressure()) logDeaconEventSync(`memory-pressure-patrol: ${action}`);
|
|
7288
8891
|
})().catch((err) => {
|
|
7289
8892
|
logDeaconEventSync(`memory-pressure-patrol: error: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -7353,6 +8956,6 @@ function assessDeaconPatrolFreshness(params) {
|
|
|
7353
8956
|
};
|
|
7354
8957
|
}
|
|
7355
8958
|
//#endregion
|
|
7356
|
-
export {
|
|
8959
|
+
export { monitorReviewConvoySignals as $, reconcileAgentLiveness as A, cleanupClosedIssueAgentDirectories as B, getLastPatrolResult as C, isDeaconRunning as D, handleAgentStoppedForOrphanReviewerSessions as E, setAgentStatusChangedNotifier as F, redeliverUndeliveredKickoffs as G, isRapidPostResumeDeath as H, setAgentStoppedNotifier as I, isBootReconciliationCandidate as J, extendBootReconciliationGrace as K, setMergeReadyNotifier as L, resetPatrolHeartbeatForStartup as M, runPatrol as N, loadConfig as O, saveState as P, cleanupOrphanedReviewSessions as Q, startDeacon as R, getDeaconStatus as S, handleAgentStoppedEvent as T, nudgeIdleWorkAgentsWithOpenBeads as U, isPreKickoffLaunchDeath as V, nudgeStalledResumeWorkAgents as W, startBootReconciliation as X, listBootReconciliationCandidates as Y, resolveSwarmPolicy as Z, cleanupOrphanReviewerSessions as _, INSPECT_TIMEOUT_MS as _t, checkAwaitingTestWorkSessions as a, checkMissingReviewStatuses as at, containerRestartBackoffMs as b, checkMassDeath as c, handleWorkCompleted as ct, checkPendingTestDispatch as d, stalledReviewConvoyRecoveryState as dt, synthesizeReviewFromReports as et, checkReadyForMergeStuck as f, checkPostReviewCommits as ft, cleanupAbandonedFeedback as g, paneShowsClaudeBootBlockingScreen as gt, checkWorkspaceContainerHealth as h, stuckOverflowNativeRecoveryState as ht, checkAndSuspendIdleAgents as i, reconcileUnappliedReviewVerdicts as it, reconcileTestStatusFromGreenCi as j, loadState as k, checkMergedWorkSessions as l, recoverStalledReviewConvoys as lt, checkVerificationReviewContradiction as m, contextOverflowRecoveryState as mt, applyBootReconciliationDecision as n, checkStuckReviewing as nt, checkCompletedButUnsignaledTests as o, checkOrphanedReviewStatuses as ot, checkStuckWorkAgents as p, checkApiErrorAgents as pt, isAutoResumableRole as q, assessDeaconPatrolFreshness as r, isSynthesisForActiveReviewRun as rt, checkDeadEndAgents as s, handleReviewCoordinatorDied as st, MEMORY_PATROL_INTERVAL_MS as t, checkCompletedButUnsignaledReviews as tt, checkOrphanedCompletions as u, reviewConvoyLiveness as ut, cleanupOrphanedPlanningSessions as v, checkInspectAgentTimeouts as vt, handleAgentHeartbeatDeadEvent as w, getDeaconLogs as x, cleanupStaleAgentState as y, WORK_LAUNCHER_GRACE_MS as yt, stopDeacon as z };
|
|
7357
8960
|
|
|
7358
|
-
//# sourceMappingURL=deacon-
|
|
8961
|
+
//# sourceMappingURL=deacon-B24dNzr8.js.map
|