elanous 0.2.36 → 0.2.37
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/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/{uC-N0emedeNOFuaBQFv-I → 8kITumG9GAZ1LwT9POrAR}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/1041-5841aae848fb6403.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1087.3177e54f672dc526.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1105.efe1e13503ac7a33.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1224.2ceca6c180d5130b.js +62 -0
- package/apps/pwa/out/_next/static/chunks/{4286.b70c4520306817e8.js → 1262.a89bb27a5b2f844e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1417-1768233ac92f8a5d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1612.81bbc19af06af16c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3410.7f9af5ab77cbe65f.js → 1746.3563617ea64023a3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1766.728d42053070bb2d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1773.d38e945481e04125.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1974-c12f9600da3d3c26.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1ed5e8af.cf424815c7c0efad.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9250-072df22eaadaa6b5.js → 2002-a43a3f380df873ec.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2061.7232dc9dc058417f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/210.90b2cbd64926cf34.js +32 -0
- package/apps/pwa/out/_next/static/chunks/{3727.7949ccaacaa71242.js → 2283.edd9627b7bdfb546.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/2318-450e75d07605b8fe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4615.8525eb0add35eba6.js → 2375.bf364c85dbb6f085.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/244-a810d282a20f1783.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2479-fe7e188bd9402d14.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2514.03b60f266531a015.js +148 -0
- package/apps/pwa/out/_next/static/chunks/2526-a657538869888604.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2533-6f0341ff075809e9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2676.e7994c695ef0f30d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2734.3ffdf54254fb6f99.js +27 -0
- package/apps/pwa/out/_next/static/chunks/2770-23252bf3a37880b9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2969.66b8b545139d1544.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3128.a9210d08800ea6a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5711.957b095705c12560.js → 3199.6a1a9b0e6cd01dc6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3212.12179fd3eee8cef1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2986.45d7652e4f13557e.js → 3578.4e9f9948a9a6f2f4.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/3760-2909ddb9279281cd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3825.1921b5901bf33dce.js +36 -0
- package/apps/pwa/out/_next/static/chunks/3994-63eb884ed0547011.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4036.d54e55e1ebf5acdc.js +90 -0
- package/apps/pwa/out/_next/static/chunks/4115.5b438b99f0ae729f.js +226 -0
- package/apps/pwa/out/_next/static/chunks/4269.d8f9f34bb0fc6c43.js +287 -0
- package/apps/pwa/out/_next/static/chunks/431-4eed5bd2c5fe1719.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7481.39f7a4654907bf6e.js → 4313.76f232c304a15bde.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4324.c99ba3e83eb1b691.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4337e36d.f42c3b391b3d8f20.js +122 -0
- package/apps/pwa/out/_next/static/chunks/{6943.c6b24124853a47b2.js → 4383.18e5c54db3a22c88.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4599-96eff3f2a9c242c1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3757-180308bc61021b05.js → 4725-34bed25720cc326b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4825-a9d9d4e9f5892309.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4833-7934c592c5569f00.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4879-68518f547a207aff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4896.f9b70285363b27b2.js +24 -0
- package/apps/pwa/out/_next/static/chunks/{771.c3cdb6c1f7f47c02.js → 4931.95cc6dbd230774ca.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/4937-28d437999b77c9dd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/49aa54ab.80f39398e0ed0f30.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{552.b7d061ad91eeac51.js → 5046.7fd5cbd859299448.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/5056.482687319b82638e.js +104 -0
- package/apps/pwa/out/_next/static/chunks/506.16b83d56a437a9ee.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5260-20593b0a25d0afbc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5404.cc4e9fec26fd5925.js +1 -0
- package/apps/pwa/out/_next/static/chunks/576.485e83e3f3dec19a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5832-a6be14e663f65726.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5854-56648a9fe646b9a0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9f31e478.fefcbcd3c388ebc5.js → 58edce7d.f1b0c72acf13b123.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5925.3b1f8b135710249b.js +93 -0
- package/apps/pwa/out/_next/static/chunks/{3136.30aaaa5fa03662a3.js → 5952.294fc574a8625184.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{da5ecc5c-b005ad8cac737d57.js → 5f6af561-f90b7a67f4f75d13.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6168.17749aa3905fa073.js +74 -0
- package/apps/pwa/out/_next/static/chunks/{3324.a09703033abcbd2f.js → 6252.240578f10952e091.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6416.5d50b8ec9986ca98.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8904.7d51af2dc033965c.js → 6680.6a693c28aae4cd19.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/{275-02ea4c2a33980422.js → 6745-5a63492933959dd9.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/6758.e7fbf532953c0783.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6795.0cb8a59730d99703.js +149 -0
- package/apps/pwa/out/_next/static/chunks/{528.38ce20ef8aa251ff.js → 6819.be5c9c4d67f22922.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/6877-84d47b7e85d0c41e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6971-fb5f9dc0a2cb5d2d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3a3d9966.cc4d4585a2708e7c.js → 709a918c.4fd3b4e2a32cb4e3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/717.522b08685979b814.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7191.e41eb8f455c7f089.js +41 -0
- package/apps/pwa/out/_next/static/chunks/7233.8e744b7513119409.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7238-97c2226f80504bd8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7285.307963afcf7e7660.js +24 -0
- package/apps/pwa/out/_next/static/chunks/7299-fbe4b8c47a87614a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7314.df0d95930b0c3f50.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7406.c0589fdf2ee66629.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4304.e9b2512834a9e23e.js → 7424.c0fe2a595d4232b5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/752-90e3e7daad3d8e39.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7524-bcd3d9928c6842d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7549.9a4de3bf231cfc52.js +1 -0
- package/apps/pwa/out/_next/static/chunks/757-4fbaf50942b8c424.js +1 -0
- package/apps/pwa/out/_next/static/chunks/765.76ae970ceb597b5b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2683.c9773a0c589b031b.js → 7867.e8725745ddd1d940.js} +4 -4
- package/apps/pwa/out/_next/static/chunks/{6466-92e65a48490b68a9.js → 7893-e444a1b0809cd1e4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7956-33748cabb3a05562.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{ccae1c4b-0cf414f5eff1b10d.js → 7c5ed21f-f286708e57cf88f7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8030-aa66b478b53dbfca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8051.c88c07dacbbc5f21.js +8 -0
- package/apps/pwa/out/_next/static/chunks/{3840-5825d56d46976a92.js → 8080-40d781e7704d3671.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8149.80e402d4345e8dae.js +77 -0
- package/apps/pwa/out/_next/static/chunks/8205.50a5268f4e12ba0b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8231.a128feaa6f8b6079.js +1 -0
- package/apps/pwa/out/_next/static/chunks/85.4ddb1dda2ea3ffa2.js +286 -0
- package/apps/pwa/out/_next/static/chunks/8537.26517a09e8247beb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8595-e9b7081ab773a61f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8821-30cc65a61a60951f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8905.b3385aadb7b133e6.js +79 -0
- package/apps/pwa/out/_next/static/chunks/8983.246bb3a1ac38f50f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{873d6c12.09bd499f57171543.js → 8c58d09b.e8724a299658d86f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9072.18342f5941a1f3ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9123.9df04866b2d6e00f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{575.d929793a53f27c4b.js → 9151.a2c51e93f9156dae.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9175-7d383df15a82e2be.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9181-bf89c07ce5b8f599.js +1 -0
- package/apps/pwa/out/_next/static/chunks/920-429bbc4504b45f71.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9207.625edf45d656151c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9378.d2575c6ffd6dd756.js +94 -0
- package/apps/pwa/out/_next/static/chunks/957.db5b5c4d32a35240.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{190.285c832bd9626f8c.js → 958.53a8dcf4729e6997.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9647.8f0cd0898c592a29.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9665-26abb02665704c2c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9703.e7d81b6faef1050a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9882-7a9164b3c2dacbe5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9998.b74e033efadecd10.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c604fb980a634d88.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-ec2d8a4979ca9075.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-cb6add08ad3c7660.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-e2eaa321ec223fe6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-5b8a09c475b5f844.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-91a77daedfbac191.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-f37fc5e974bda39b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-91429a0b78415a94.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-b95798b58263b910.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-763d53e175e17aa1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-d55023204055b12f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-89d2140d78abc810.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-82dc069abfe27f55.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-25d9eee7beb552c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-770b4594c713ee97.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-c604fb980a634d88.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-ba8e0dcae4f83985.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-5c112c6d7c33656e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-09788e1aa9a87db6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/{page-e2de2e59111c9aad.js → page-3337c6a1328c2674.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/{page-f2b2e4bf6e981450.js → page-89acd5d68c0bc560.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-5c13e2c76371d797.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-653a9f18642825c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-2af16e62c12f22df.js → page-fe986fe79b107546.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-1e5da297557d2354.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-91ea9f5869a40943.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-7824e21b999962e2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-53d00a60cc75a922.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-88a1edbff62eaf1d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4e931248b3f9574b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-443364c9e4a23b31.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-b9af2ac1c4737ce8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-25ab9c614c14129b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-dbcdcfac0a395a8a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-bf1de6645089ec58.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c4201761a5e839c9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-c304f469ea5d70e4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8e5a238cf5e50c9c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-c604fb980a634d88.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-32b526069ee50b3c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-fb9e639c107f5f72.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-84220c776608cedc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-e475618cbfd5ca2b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-ac2c1e6169cfce57.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-fccd3020af2f8806.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-9c0d7198ff7b5a91.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-d54b1ec9b9c6430d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-8c4bef1791d732ca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-46fcc0b0bbc6fbfe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-c1c80e345fa9d6f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-7dfda00afd172f5b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{053a429e.9a63436527cc751c.js → d9815f84.4918f682a45a8502.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{ceba0382.650712b56b85b582.js → de8446d3.31b73eb239fecd7b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/f21fd540.63ac75328926ed30.js +1 -0
- package/apps/pwa/out/_next/static/chunks/framework-77fdcc873519a6cd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-d0d620d2ce235713.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-e0ef79c79337ba90.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-84b1bc71e3f9ce6a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-2acb61f1363128cb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-462ff5727ceb7824.js +1 -0
- package/apps/pwa/out/_next/static/css/{b5d88eba78a3d7bf.css → be9019f696217521.css} +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +23 -23
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +22 -22
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +23 -23
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +23 -23
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +22 -22
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +23 -23
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +23 -23
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +23 -23
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +23 -23
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +22 -22
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +22 -22
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +23 -23
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +23 -23
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +23 -23
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +22 -22
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +22 -22
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +22 -22
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +23 -23
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +23 -23
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +22 -22
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +22 -22
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +22 -22
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +22 -22
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +23 -23
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +23 -23
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +22 -22
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +23 -23
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +23 -23
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +23 -23
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +23 -23
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +23 -23
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +22 -22
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +23 -23
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +23 -23
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +23 -23
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +23 -23
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +23 -23
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +23 -23
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +23 -23
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/graphs/drafts/recipes.yaml +27 -7
- package/graphs/work/draft-close.yaml +54 -0
- package/graphs/work/green-close.yaml +37 -0
- package/graphs/work/recipes.yaml +46 -1
- package/graphs/work/run-research.yaml +35 -0
- package/package.json +1 -1
- package/scripts/__fixtures__/authoring-paths-10x3.json +32 -0
- package/scripts/boundary-proof-mapping-audit.ts +88 -0
- package/scripts/codex-fallback-live-probe.ts +17 -15
- package/scripts/fleet/node-setup.sh +32 -2
- package/scripts/fleet/run-light-worker.sh +9 -0
- package/scripts/gate-0226-failure-census.ts +507 -0
- package/scripts/goals/authoring-metrics.ts +258 -0
- package/scripts/graph-units/draft-close-node.ts +222 -0
- package/scripts/hook-order-sweep.ts +7 -4
- package/scripts/land-global-checks.ts +69 -1
- package/scripts/measure-nl-routing-corpus.ts +207 -2
- package/scripts/release-loop/docs-node.ts +140 -0
- package/scripts/release-loop/gate-closure.ts +48 -0
- package/scripts/release-loop/gate-diff.ts +2 -1
- package/scripts/release-loop/known-issues-node.ts +6 -4
- package/scripts/release-loop/notes-repair.ts +216 -0
- package/scripts/release-loop/prepare-node.ts +61 -5
- package/scripts/release-loop/resume-release.ts +114 -4
- package/scripts/release-loop/resume-test-traffic.ts +10 -0
- package/scripts/release-loop/version-node.ts +81 -12
- package/scripts/train/green-leg.ts +176 -0
- package/scripts/tui-continue-real-evidence.ts +47 -0
- package/scripts/ux-sim.ts +1 -1
- package/src/acp/client.ts +31 -5
- package/src/acp/core-turn-bridge.ts +6 -0
- package/src/acp/stderr-summary.ts +49 -0
- package/src/agent/monad-agent-turn.ts +1 -0
- package/src/agent/terminal-surface.ts +217 -0
- package/src/agent-substrate/pr-reviewer.ts +11 -3
- package/src/agent-substrate/self-review-cli.ts +343 -31
- package/src/autopilot/lineage/sources.ts +1 -1
- package/src/autopilot/pr-manager.ts +64 -13
- package/src/boot/daemon-runtime.ts +35 -3
- package/src/boot/daemon-tools/pty-shell.ts +2 -1
- package/src/budget/run-usage-rollup.ts +12 -7
- package/src/budget/unified-usage.ts +20 -0
- package/src/capture/tui-self-report.ts +32 -3
- package/src/cli/autopilot-cli.ts +24 -9
- package/src/cli/decisions-cli.ts +7 -4
- package/src/cli/doctor-cli.ts +116 -7
- package/src/cli/logs-cli.ts +357 -2
- package/src/cli/memory-cli.ts +30 -1
- package/src/cli/msg-cli.ts +15 -11
- package/src/cli/pr-granularity.ts +5 -3
- package/src/cli/release-checklist-read-cli.ts +109 -4
- package/src/cli/release-cli.ts +43 -10
- package/src/cli/self-update.ts +23 -5
- package/src/cli/self-utterances-cli.ts +13 -16
- package/src/cli/tasks-cli.ts +309 -11
- package/src/cli/usage-cli.ts +12 -10
- package/src/context-bus/claude-memory-import.ts +99 -0
- package/src/context-bus/long-term-memory.ts +125 -0
- package/src/core-turn/run-goal-loop.ts +5 -2
- package/src/core-turn/types.ts +2 -0
- package/src/dashboard/chat/acp-chat.ts +9 -0
- package/src/dashboard/index.ts +97 -13
- package/src/dashboard/input/agent-wake.ts +43 -0
- package/src/dashboard/input/chat-main-acp-dispatch.ts +21 -1
- package/src/dashboard/replay-preview.ts +25 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +17 -8
- package/src/dashboard/synchronized-frame-output.ts +74 -0
- package/src/display/coordinator.ts +45 -12
- package/src/display/resize-listener.ts +41 -20
- package/src/display/synchronized-output-region.ts +60 -0
- package/src/domains/surface-events.ts +80 -1
- package/src/exec-requests/planner.ts +37 -2
- package/src/execution-loop/launch-gate.ts +4 -3
- package/src/git-fs/cli-stderr.ts +45 -0
- package/src/git-fs/gh-cli.ts +6 -3
- package/src/git-fs/git-cli.ts +11 -5
- package/src/graph-runner/graph-cli.ts +26 -2
- package/src/graph-runner/graph-run-api.ts +3 -2
- package/src/graph-runner/runner.ts +17 -5
- package/src/graph-wizard/generate.ts +29 -6
- package/src/harness/harness-cli-command.ts +179 -33
- package/src/harness/review-adapter.ts +26 -6
- package/src/index.ts +116 -13
- package/src/instance/test-write-guard-real-transport.fixture.ts +52 -0
- package/src/llm/route-decision.ts +13 -0
- package/src/llm-tool-name.ts +37 -0
- package/src/llm-tool-schema.ts +35 -0
- package/src/llm.ts +123 -11
- package/src/loops/checker.ts +100 -0
- package/src/loops/consensus.ts +26 -2
- package/src/loops/loop-cli.ts +9 -4
- package/src/loops/orchestrator/anomaly-cluster.ts +243 -0
- package/src/loops/orchestrator/tick.ts +49 -4
- package/src/model-tier/llm-tier-map.ts +12 -17
- package/src/msg/msg-store.ts +17 -0
- package/src/native-tool-catalog.ts +1 -1
- package/src/nexus/api/graph-wizard-api.ts +27 -2
- package/src/nexus/api/grid.ts +24 -4
- package/src/nexus/api/http-server.ts +1 -1
- package/src/nexus/api/ops-api.ts +14 -6
- package/src/nexus/state/state.ts +7 -1
- package/src/oauth/codex-account-store.ts +34 -1
- package/src/ops/loops-map.ts +46 -0
- package/src/ops/overview-cache.ts +22 -4
- package/src/ops/overview.ts +41 -5
- package/src/policy/llm-policy.ts +3 -1
- package/src/prompt-enhance/enhance.ts +4 -4
- package/src/pty-shell/availability.ts +7 -0
- package/src/pty-shell/herdr-agent-report.ts +40 -31
- package/src/pty-shell/herdr-prune-stale.ts +17 -0
- package/src/pty-shell/herdr-sequence-lease.ts +60 -0
- package/src/pty-shell/pty-control-ipc.ts +187 -18
- package/src/pty-shell/registry.ts +25 -15
- package/src/pty-shell/tui-run-state.ts +235 -1
- package/src/release-loop/checklist.ts +50 -5
- package/src/release-loop/feature-store.ts +9 -9
- package/src/release-loop/landed-but-yellow.ts +93 -12
- package/src/repl/index.ts +8 -0
- package/src/seat-loop/seat-report.ts +1 -0
- package/src/self-dev/ask-launch-flow.ts +86 -10
- package/src/self-dev/decompose-proposal.ts +29 -18
- package/src/self-dev/dev-pipeline.ts +29 -7
- package/src/self-dev/draft-sweep.ts +241 -16
- package/src/self-dev/draft-triage-rules.ts +19 -5
- package/src/self-dev/review-provider-fallback.ts +45 -7
- package/src/self-dev/shard-plan.ts +48 -0
- package/src/self-dev/shard-run.ts +50 -3
- package/src/self-implement/doctor-visit.ts +83 -0
- package/src/self-implement/gate-artifacts.ts +6 -2
- package/src/self-implement/gate-baseline.ts +64 -1
- package/src/self-implement/gate-cli.ts +93 -9
- package/src/self-implement/gate-remote.ts +28 -4
- package/src/self-implement/gate-scope.ts +13 -1
- package/src/self-implement/goal-author.ts +333 -18
- package/src/self-implement/goal-supervisor-fill.ts +75 -26
- package/src/self-implement/importer-test-index.ts +215 -2
- package/src/self-implement/knowhow-pack.ts +5 -5
- package/src/self-implement/orchestrator.ts +318 -25
- package/src/self-implement/review-round-brief.ts +300 -0
- package/src/self-implement/rework-policy.ts +14 -0
- package/src/self-implement/run-ledger.ts +170 -6
- package/src/self-implement/run-stop.ts +1 -1
- package/src/self-implement/running-runs.ts +17 -3
- package/src/self-implement/seams.ts +12 -6
- package/src/self-implement/stop-autoheal.ts +1 -0
- package/src/self-implement/test-seams.ts +13 -1
- package/src/self-implement/train-reach-grouping.ts +8 -2
- package/src/session/chat.ts +7 -1
- package/src/session/id-prefix.ts +60 -0
- package/src/session/index.ts +4 -0
- package/src/session-runtime/index.ts +9 -1
- package/src/skills/tools/pty.ts +5 -0
- package/src/status/metrics.ts +12 -1
- package/src/steward/rejudge-tick.ts +12 -5
- package/src/steward/resync-tick.ts +31 -4
- package/src/task-agent/actions.ts +199 -4
- package/src/task-agent/boundary-proof-mapping.ts +12 -0
- package/src/task-agent/boundary-proof-producer.ts +147 -0
- package/src/task-agent/completion-evidence.ts +2 -1
- package/src/task-agent/green-close-recipe.ts +239 -0
- package/src/task-agent/green-sweep.ts +64 -10
- package/src/task-agent/live-moves.ts +48 -3
- package/src/task-agent/map-host-adapter.ts +134 -0
- package/src/task-agent/map-reduce.ts +158 -0
- package/src/task-agent/map-run-ledger.ts +226 -0
- package/src/task-agent/parent-host.ts +21 -11
- package/src/task-agent/rejudge-on-head.ts +72 -5
- package/src/task-agent/repair-cycle.ts +41 -0
- package/src/task-agent/repair-next-move.ts +12 -2
- package/src/task-agent/review-prior-fix.ts +12 -2
- package/src/task-agent/shadow.ts +188 -5
- package/src/task-agent/task-hand.ts +158 -13
- package/src/task-agent/task-parent-role-llm.ts +9 -0
- package/src/task-agent/tier-split.ts +4 -3
- package/src/task-agent/work-order.ts +53 -2
- package/src/task-orchestrator/dispatcher.ts +46 -1
- package/src/task-orchestrator/generator-prompt.ts +6 -0
- package/src/task-orchestrator/surfaces/pod-artifact-return.ts +16 -6
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +8 -6
- package/src/task-orchestrator/surfaces/pod-ledger-events.ts +97 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +50 -4
- package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +15 -9
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +217 -36
- package/src/task-orchestrator/surfaces/self-implement.ts +4 -0
- package/src/testing/linux-env.ts +177 -0
- package/src/tui/chat/goal-interim.ts +5 -1
- package/src/tui/entry.ts +258 -4
- package/src/tui/input/at-picker.ts +55 -0
- package/src/tui/slash/resume.ts +35 -12
- package/src/tui/slash/signals.ts +114 -0
- package/src/tui/slash/term-next.ts +71 -0
- package/src/tui-client/local-chat-turn.ts +4 -3
- package/src/user-config.ts +19 -1
- package/src/ux-sim/boot-daemon-tools-pty-shell-device.ts +136 -0
- package/src/ux-sim/classic-tui-seat-scenarios.ts +158 -4
- package/src/ux-sim/fake-gh.ts +174 -0
- package/src/ux-sim/fake-kubectl.ts +142 -0
- package/src/ux-sim/fake-llm.ts +6 -1
- package/src/ux-sim/next-tui-seat-scenarios.ts +124 -6
- package/src/ux-sim/resume-picker-device.ts +173 -0
- package/src/ux-sim/session-id-seed.ts +60 -0
- package/src/ux-sim/skills-tools-shell-device.ts +156 -0
- package/src/ux-sim/task-agent-parent-host-device.ts +264 -0
- package/src/ux-sim/task-agent-rejudge-on-head-device.ts +114 -0
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/109.f196abf3484bb97f.js +0 -287
- package/apps/pwa/out/_next/static/chunks/1201.afbfc5b1c5e5cbe4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1517.9275b89572098ff3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1574.ad11f8db8396ab45.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1614.7a8c5c40aee0261f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1871-f1eb29d8dbb4065b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2193.0ea24c4a7aa1d004.js +0 -36
- package/apps/pwa/out/_next/static/chunks/223-ad865c1e80491d78.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2265-f0d90b6dbec05f81.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2267.eb4a62a209f09ec4.js +0 -149
- package/apps/pwa/out/_next/static/chunks/2341-86cd06c0cf01aa33.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2352.9abc42422bade876.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2397-c1653a816061842d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2500.cbc1257b774aae52.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2709.8e1889034255fc5f.js +0 -24
- package/apps/pwa/out/_next/static/chunks/2812ca6a.3b9f4a7a08885744.js +0 -1
- package/apps/pwa/out/_next/static/chunks/284.ac77c0db55bfd3a8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2941.3c3546f96bc6efa7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2976.b000698a3c6512dc.js +0 -24
- package/apps/pwa/out/_next/static/chunks/3031-85328a92fcce4da6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3148-98efd4869d660a87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3155.2587a55196ecb394.js +0 -226
- package/apps/pwa/out/_next/static/chunks/3277.785db1cf09f98601.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3320-f9c77381f85a2c0a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3386-91049feabff9a94e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/339.80afb7e8839372af.js +0 -8
- package/apps/pwa/out/_next/static/chunks/3538.a445e50bb359d068.js +0 -32
- package/apps/pwa/out/_next/static/chunks/3540c645.2595a6af01a97672.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3621.581b2054cc14bee4.js +0 -286
- package/apps/pwa/out/_next/static/chunks/3676.8c28d16fd84ca843.js +0 -1
- package/apps/pwa/out/_next/static/chunks/375-b84540853523fe52.js +0 -1
- package/apps/pwa/out/_next/static/chunks/379.ee262e37586fe1ae.js +0 -74
- package/apps/pwa/out/_next/static/chunks/3969-81054381eaec4c4c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4067-bf9aa60547972937.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4116.a84a92a2492c080e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4176.e5729c93c29151eb.js +0 -104
- package/apps/pwa/out/_next/static/chunks/4227-e6c84686bc8946ca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4327.b9e08a71c0009348.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4946.bd106eafd668f248.js +0 -148
- package/apps/pwa/out/_next/static/chunks/5085-bce4904931767ca6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5125.2eb9968b83767193.js +0 -93
- package/apps/pwa/out/_next/static/chunks/5128-75b733fd74a9a1eb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5489f3fe.4bb1458b09f6dead.js +0 -122
- package/apps/pwa/out/_next/static/chunks/5560-2a4648f6a30f16e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5772-e29e14f8ff127122.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5887.5b2f5b44ce0f7e9a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5899-4c112a2d908cf97f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/599.c39c571b6733f2a2.js +0 -41
- package/apps/pwa/out/_next/static/chunks/6041.1b65efb796ebc2a5.js +0 -79
- package/apps/pwa/out/_next/static/chunks/6057-765c0a26028c86b2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6142.f289e0d039f453a6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6199-6bcd61d2a3910e04.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6236.cdf7293e710a9b2d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6387-237e079dac9a3ae6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6448-b6d272d074276e14.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6566.926a1d1b7c911442.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6580-cdc0e4a634ce4708.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6659-b242dc31b20b3f29.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6690.c5d9b321b1f79384.js +0 -1
- package/apps/pwa/out/_next/static/chunks/671-e9b6ad0c771c5514.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6862.bcbf89dd0f4496b3.js +0 -27
- package/apps/pwa/out/_next/static/chunks/6912-7b375ee48732c767.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6925.562d532110879886.js +0 -1
- package/apps/pwa/out/_next/static/chunks/727.7deb87749b0a343a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7545.b1aa42cd15503319.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7551-416d298c2dc4a9ab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7784.1f0ac4a16d190cf4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7870-309de463786ac1fc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7888-e29b151bb6a3bb8b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7891.03f910124a737985.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7905-6ba78d9234608f79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8037.e254fd797aa224d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8207.f527886b7c464529.js +0 -1
- package/apps/pwa/out/_next/static/chunks/856.50bda61439d465e6.js +0 -62
- package/apps/pwa/out/_next/static/chunks/9031-92e9b487edb681ee.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9060.5410d9f6a95f6287.js +0 -90
- package/apps/pwa/out/_next/static/chunks/914.8abefd48d579547b.js +0 -94
- package/apps/pwa/out/_next/static/chunks/9187-c87a2207d4ac5d46.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9289.3e92f278821e18e0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9319.7816d3cc741cd34f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9327-e016bb9f1674d24f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9365.3de48bf75193ff9a.js +0 -77
- package/apps/pwa/out/_next/static/chunks/9383.5b5e9aa56b5fea97.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9387-cb929573ef436f39.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9489.c27becacc3283dec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9514.131c52cc75fe6439.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9536.28962663c7734376.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9686-12286f5bbf15a5a0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9776.289d40cc5a4c342a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9789.60f723d18097ec30.js +0 -1
- package/apps/pwa/out/_next/static/chunks/989.f0c17523c5e61f65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9972-1df393aa8d4f7d52.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-96750aba7430959f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-4105e5bbbb043024.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-5d4fa75c2acb113e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-a79a9bdb3b2142bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-f1c2047484992a35.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-15ffc49b2d8c70e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-67d53a3fc8734135.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-e30c8dc696491336.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-2136e04fa77696e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-fecd2814790c2e12.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-280aa4a98fc5bb44.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-c14e96b65fb4b883.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-187dac74825ed6bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-6bc7246ea8c92448.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-083747f1d4518f61.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-96750aba7430959f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-15ca95a16ec96e1f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-a8ac031b3810eb64.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-8835f93110494cda.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-5b92b7f75df20948.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-468eddf2b1a84ecb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-18b40867a77a5793.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-fe934f2979f5a4a2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-3eec435537357170.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-ff88cbfd2aa99fc8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-a5b39fb780c70ab5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ae465dde1d1b3933.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-e7abc34a48a6f71e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-cab9674fde9ead26.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-1da49bc8712284d0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-5e8aa657551ac912.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-fcc36f36c6dc1fe2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-d22db22f2a7296ca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-d12dc7b1773878f0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-99debec9ea7fbec0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-96750aba7430959f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-2a088a78bad7e924.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-2e8ed57db7e69993.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-4b3a16213c2ae8fe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f098747ddf38c98b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-c15958835534404b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-68279e3c843c1fd7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-a34a74453367a39d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-8767a7db2155a5bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-bd5976683460d7f2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-b7364835e7f2ef54.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-311af16b3ee0ffca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-fb670757274ae990.js +0 -1
- package/apps/pwa/out/_next/static/chunks/f9a61e4d.1fc2c1dbdd49fe46.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-6d497cbf1eef04c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-012f5edec932f337.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-da5aeb809edc94a2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-5ee319af6f749dfe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-4bc84653a718d6c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-8a96cf6775c73d6e.js +0 -1
- /package/apps/pwa/out/_next/static/{uC-N0emedeNOFuaBQFv-I → 8kITumG9GAZ1LwT9POrAR}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MAPREDUCE-FANOUT backend. Inventory and card execution are ports so the CLI can
|
|
3
|
+
* bind the existing checklist/draft readers and tasks hand Pod launcher separately.
|
|
4
|
+
* A run snapshots its inventory before any card starts; one terminal row belongs
|
|
5
|
+
* to each item. This module never changes a checklist or closes a PR.
|
|
6
|
+
*/
|
|
7
|
+
import { randomUUID } from 'node:crypto';
|
|
8
|
+
import { debug } from '../debug/log.js';
|
|
9
|
+
import {
|
|
10
|
+
appendMapRunRow, createMapRun, readMapRun, reduceMapRun,
|
|
11
|
+
type MapRecipe, type MapRowKind, type MapRunRow, type MapRunStart,
|
|
12
|
+
} from './map-run-ledger.js';
|
|
13
|
+
|
|
14
|
+
export type MapSource = 'landed-but-yellow' | 'drafts';
|
|
15
|
+
export interface MapItem { id: string }
|
|
16
|
+
export interface MapInventory<T extends MapItem> {
|
|
17
|
+
items: readonly T[];
|
|
18
|
+
complete: boolean;
|
|
19
|
+
snapshotTime: string;
|
|
20
|
+
snapshotRef: string;
|
|
21
|
+
recipeSha: string;
|
|
22
|
+
mainSha: string;
|
|
23
|
+
checklistSnapshotPath: string | null;
|
|
24
|
+
requestedPool: string;
|
|
25
|
+
selectedPool: string | null;
|
|
26
|
+
imageCommit: string | null;
|
|
27
|
+
/** A failed inventory is recorded as a suppressed HOLD row. */
|
|
28
|
+
reason?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface MapCard {
|
|
31
|
+
id: string;
|
|
32
|
+
itemId: string;
|
|
33
|
+
mapRunId: string;
|
|
34
|
+
recipe: MapRecipe;
|
|
35
|
+
text: string;
|
|
36
|
+
}
|
|
37
|
+
export interface MapContext {
|
|
38
|
+
mapRunId: string;
|
|
39
|
+
recipe: MapRecipe;
|
|
40
|
+
source: MapSource;
|
|
41
|
+
dryRun: boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface MapPorts<T extends MapItem> {
|
|
44
|
+
inventory: (recipe: MapRecipe, source: MapSource, limit: number | undefined) => Promise<MapInventory<T>>;
|
|
45
|
+
/** Pure card authoring. Do not persist or launch here. */
|
|
46
|
+
buildCard: (item: T, context: MapContext) => MapCard;
|
|
47
|
+
/** Production adapter must launch through tasks hand and wait for its terminal recipe result. */
|
|
48
|
+
launchCard: (card: MapCard, item: T, context: MapContext) => Promise<MapRunRow>;
|
|
49
|
+
/** Optional pure preview; default dry-run yields a HOLD without invoking launchCard. */
|
|
50
|
+
previewCard?: (card: MapCard, item: T, context: MapContext) => MapRunRow | Promise<MapRunRow>;
|
|
51
|
+
log?: (event: string, data: Record<string, unknown>) => void;
|
|
52
|
+
}
|
|
53
|
+
export interface MapRequest {
|
|
54
|
+
recipe: MapRecipe;
|
|
55
|
+
source: MapSource;
|
|
56
|
+
limit?: number;
|
|
57
|
+
concurrency?: number;
|
|
58
|
+
dryRun?: boolean;
|
|
59
|
+
root?: string;
|
|
60
|
+
mapRunId?: string;
|
|
61
|
+
}
|
|
62
|
+
export interface MapResult { mapRunId: string; expected: number; observed: number; dryRun: boolean }
|
|
63
|
+
|
|
64
|
+
const validPairs: Readonly<Record<MapRecipe, MapSource>> = {
|
|
65
|
+
'green-close': 'landed-but-yellow', 'draft-close': 'drafts',
|
|
66
|
+
};
|
|
67
|
+
const idPattern = /^[A-Za-z0-9][A-Za-z0-9_-]{0,95}$/;
|
|
68
|
+
|
|
69
|
+
function checkPositive(value: number | undefined, label: string): void {
|
|
70
|
+
if (value !== undefined && (!Number.isSafeInteger(value) || value < 1)) throw new Error(`map ${label} must be a positive integer`);
|
|
71
|
+
}
|
|
72
|
+
function hold(context: MapContext, itemId: string | null, reason: string, suppressed = false): MapRunRow {
|
|
73
|
+
return { event: 'row', mapRunId: context.mapRunId, itemId, recipe: context.recipe,
|
|
74
|
+
kind: 'hold', verdict: 'hold', reason, evidence: [], ...(suppressed ? { rowsSuppressed: true, pr: null } : {}) };
|
|
75
|
+
}
|
|
76
|
+
function record(log: MapPorts<MapItem>['log'], event: string, data: Record<string, unknown>): void {
|
|
77
|
+
try { (log ?? ((name, fields) => debug.log('task-agent.map-reduce', name, fields)))(event, data); }
|
|
78
|
+
catch { /* diagnostics must not turn a completed item into a failed one */ }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Bounded workers, with no unbounded Promise.all fanout. Default is a read-only preview. */
|
|
82
|
+
export async function mapInventory<T extends MapItem>(request: MapRequest, ports: MapPorts<T>): Promise<MapResult> {
|
|
83
|
+
if (validPairs[request.recipe] !== request.source) throw new Error('map recipe/source mismatch');
|
|
84
|
+
checkPositive(request.limit, 'limit');
|
|
85
|
+
checkPositive(request.concurrency, 'concurrency');
|
|
86
|
+
const concurrency = request.concurrency ?? 2;
|
|
87
|
+
const dryRun = request.dryRun !== false;
|
|
88
|
+
const mapRunId = request.mapRunId ?? `map-${randomUUID()}`;
|
|
89
|
+
if (!idPattern.test(mapRunId)) throw new Error('map invalid run id');
|
|
90
|
+
const context: MapContext = { mapRunId, recipe: request.recipe, source: request.source, dryRun };
|
|
91
|
+
const inventory = await ports.inventory(request.recipe, request.source, request.limit);
|
|
92
|
+
// A limit is a selection boundary, not permission to silently truncate a partial inventory.
|
|
93
|
+
const items = inventory.complete ? inventory.items.slice(0, request.limit) : [];
|
|
94
|
+
const start: MapRunStart = { event: 'start', mapRunId, recipe: request.recipe,
|
|
95
|
+
recipeSha: inventory.recipeSha, snapshotTime: inventory.snapshotTime, snapshotRef: inventory.snapshotRef,
|
|
96
|
+
mainSha: inventory.mainSha, checklistSnapshotPath: inventory.checklistSnapshotPath,
|
|
97
|
+
inventoryComplete: inventory.complete, itemIds: items.map((item) => item.id),
|
|
98
|
+
requestedPool: inventory.requestedPool, selectedPool: inventory.selectedPool,
|
|
99
|
+
imageCommit: inventory.imageCommit, dryRun };
|
|
100
|
+
createMapRun(start, request.root);
|
|
101
|
+
record(ports.log, 'start', { mapRunId, recipe: request.recipe, source: request.source, count: items.length, concurrency, dryRun });
|
|
102
|
+
if (!inventory.complete) {
|
|
103
|
+
appendMapRunRow(hold(context, null, inventory.reason || 'inventory-incomplete', true), request.root);
|
|
104
|
+
record(ports.log, 'inventory-hold', { mapRunId, reason: inventory.reason ?? 'inventory-incomplete' });
|
|
105
|
+
return { mapRunId, expected: 0, observed: 1, dryRun };
|
|
106
|
+
}
|
|
107
|
+
let next = 0;
|
|
108
|
+
let observed = 0;
|
|
109
|
+
async function worker(): Promise<void> {
|
|
110
|
+
while (next < items.length) {
|
|
111
|
+
const item = items[next++]!;
|
|
112
|
+
let row: MapRunRow;
|
|
113
|
+
try {
|
|
114
|
+
const card = ports.buildCard(item, context);
|
|
115
|
+
if (!card || !idPattern.test(card.id) || card.itemId !== item.id || card.mapRunId !== mapRunId
|
|
116
|
+
|| card.recipe !== request.recipe || !card.text.trim()) throw new Error('card identity mismatch');
|
|
117
|
+
row = dryRun
|
|
118
|
+
? ports.previewCard ? await ports.previewCard(card, item, context) : hold(context, item.id, 'dry-run-preview')
|
|
119
|
+
: await ports.launchCard(card, item, context);
|
|
120
|
+
if (row.mapRunId !== mapRunId || row.itemId !== item.id || row.recipe !== request.recipe)
|
|
121
|
+
throw new Error('result identity mismatch');
|
|
122
|
+
row = { ...row, result: { ...row.result, cardId: card.id } };
|
|
123
|
+
} catch (error) {
|
|
124
|
+
row = hold(context, item.id, `map-item-failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
125
|
+
}
|
|
126
|
+
// The ledger validates recipe-specific evidence and rejects duplicate/unknown rows.
|
|
127
|
+
// An invalid result is a hard error; never launder it into a successful map run.
|
|
128
|
+
appendMapRunRow(row, request.root);
|
|
129
|
+
observed++;
|
|
130
|
+
record(ports.log, 'item', { mapRunId, itemId: item.id, kind: row.kind, reason: row.reason });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, () => worker()));
|
|
134
|
+
record(ports.log, 'finished', { mapRunId, expected: items.length, observed });
|
|
135
|
+
return { mapRunId, expected: items.length, observed, dryRun };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface MapReduceTable { mapRunId: string; markdown: string; writes: 0 }
|
|
139
|
+
const countKinds: readonly MapRowKind[] = ['green-candidate', 'mock-pr', 'implementation-remains', 'harvest', 'close-candidate', 'hold'];
|
|
140
|
+
function cell(value: unknown): string { return String(value ?? '').replaceAll('|', '\\|').replaceAll(/\r?\n/g, ' '); }
|
|
141
|
+
|
|
142
|
+
/** Read-only aggregation. Applying green/close candidates belongs to a separate guarded reducer. */
|
|
143
|
+
export function renderMapReduceTable(mapRunId: string, root?: string): MapReduceTable {
|
|
144
|
+
const { start, rows } = readMapRun(mapRunId, root);
|
|
145
|
+
const summary = reduceMapRun(mapRunId, root);
|
|
146
|
+
const lines = [
|
|
147
|
+
`# Map run ${cell(mapRunId)}`,
|
|
148
|
+
'',
|
|
149
|
+
`Recipe: ${start.recipe} · snapshot: ${cell(start.snapshotRef)} · dry-run: ${start.dryRun} · verdict: ${summary.verdict} (${summary.reason})`,
|
|
150
|
+
`Expected: ${summary.expected} · observed: ${summary.observed} · writes: 0`,
|
|
151
|
+
`Counts: ${countKinds.map((kind) => `${kind} ${summary.counts[kind]}`).join(' · ')}`,
|
|
152
|
+
'',
|
|
153
|
+
'| Item | Card | Result | PR | Reason |',
|
|
154
|
+
'| --- | --- | --- | --- | --- |',
|
|
155
|
+
...rows.map((row) => `| ${cell(row.itemId ?? 'inventory')} | ${cell(row.result?.cardId)} | ${row.kind} | ${cell(row.pr)} | ${cell(row.reason)} |`),
|
|
156
|
+
];
|
|
157
|
+
return { mapRunId, markdown: `${lines.join('\n')}\n`, writes: 0 };
|
|
158
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MAPREDUCE-FANOUT: one immutable inventory snapshot, followed by one result per item.
|
|
3
|
+
* The reducer is read-only. A partial inventory or missing/duplicate result is a hold,
|
|
4
|
+
* never evidence for a checklist change or a draft close.
|
|
5
|
+
*/
|
|
6
|
+
import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, writeSync } from 'node:fs';
|
|
7
|
+
import { dirname, isAbsolute, join } from 'node:path';
|
|
8
|
+
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
9
|
+
import { withFileLockSync } from '../storage/file-lock.js';
|
|
10
|
+
|
|
11
|
+
export type MapRecipe = 'green-close' | 'draft-close';
|
|
12
|
+
export type MapRowKind = 'green-candidate' | 'mock-pr' | 'implementation-remains'
|
|
13
|
+
| 'harvest' | 'close-candidate' | 'hold';
|
|
14
|
+
export type MapEvidenceVerdict = 'pass' | 'fail' | 'hold' | 'unmeasured';
|
|
15
|
+
|
|
16
|
+
export interface MapRunStart {
|
|
17
|
+
event: 'start';
|
|
18
|
+
mapRunId: string;
|
|
19
|
+
recipe: MapRecipe;
|
|
20
|
+
recipeSha: string;
|
|
21
|
+
snapshotTime: string;
|
|
22
|
+
snapshotRef: string;
|
|
23
|
+
/** Exact origin/main captured with the inventory, independent of the image or recipe commit. */
|
|
24
|
+
mainSha: string;
|
|
25
|
+
/** The checklist snapshot artifact used by green-close; draft-close records null. */
|
|
26
|
+
checklistSnapshotPath: string | null;
|
|
27
|
+
inventoryComplete: boolean;
|
|
28
|
+
/** Stable identities from the complete inventory; item display titles are never keys. */
|
|
29
|
+
itemIds: string[];
|
|
30
|
+
requestedPool: string;
|
|
31
|
+
selectedPool: string | null;
|
|
32
|
+
imageCommit: string | null;
|
|
33
|
+
dryRun: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface MapRunRow {
|
|
37
|
+
event: 'row';
|
|
38
|
+
mapRunId: string;
|
|
39
|
+
itemId: string | null;
|
|
40
|
+
recipe: MapRecipe;
|
|
41
|
+
kind: MapRowKind;
|
|
42
|
+
verdict: MapRowKind;
|
|
43
|
+
reason: string;
|
|
44
|
+
evidence: string[];
|
|
45
|
+
e1?: { verdict: MapEvidenceVerdict; mergeSha?: string; mainAncestor?: boolean };
|
|
46
|
+
e2?: { verdict: MapEvidenceVerdict; command?: string; exitCode?: number };
|
|
47
|
+
e2Source?: 'completion-boundary' | 'prior-evidence' | 'missing';
|
|
48
|
+
e3?: { verdict: MapEvidenceVerdict; reason: string };
|
|
49
|
+
/** Only an inventory-wide failure uses this form; it suppresses all item rows and requires pr:null. */
|
|
50
|
+
rowsSuppressed?: boolean;
|
|
51
|
+
pr?: number | null;
|
|
52
|
+
prUrl?: string | null;
|
|
53
|
+
headOid?: string | null;
|
|
54
|
+
podJob?: string | null;
|
|
55
|
+
podUid?: string | null;
|
|
56
|
+
startedAt?: string | null;
|
|
57
|
+
endedAt?: string | null;
|
|
58
|
+
rc?: number | null;
|
|
59
|
+
result?: Record<string, unknown>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface MapRunRead {
|
|
63
|
+
start: MapRunStart;
|
|
64
|
+
rows: MapRunRow[];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface MapRunReduce {
|
|
68
|
+
mapRunId: string;
|
|
69
|
+
verdict: 'complete' | 'hold';
|
|
70
|
+
reason: string;
|
|
71
|
+
expected: number;
|
|
72
|
+
observed: number;
|
|
73
|
+
counts: Record<MapRowKind, number>;
|
|
74
|
+
/** A dry-run has no write authority, even when every item is a candidate. */
|
|
75
|
+
writes: 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,95}$/;
|
|
79
|
+
const SHA = /^[0-9a-f]{40}$/;
|
|
80
|
+
const KINDS = new Set<MapRowKind>(['green-candidate', 'mock-pr', 'implementation-remains', 'harvest', 'close-candidate', 'hold']);
|
|
81
|
+
const RECIPE_KINDS: Record<MapRecipe, ReadonlySet<MapRowKind>> = {
|
|
82
|
+
'green-close': new Set(['green-candidate', 'mock-pr', 'implementation-remains', 'hold']),
|
|
83
|
+
'draft-close': new Set(['harvest', 'close-candidate', 'hold']),
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
function checkId(value: string, label: string): void {
|
|
87
|
+
if (!ID.test(value)) throw new Error(`map-run invalid ${label}: ${value}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function mapRunLedgerPath(mapRunId: string, root = effectiveInstanceRoot()): string {
|
|
91
|
+
checkId(mapRunId, 'id');
|
|
92
|
+
return join(root, 'map-runs', `${mapRunId}.jsonl`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function validateStart(start: MapRunStart): void {
|
|
96
|
+
checkId(start.mapRunId, 'id');
|
|
97
|
+
if (start.recipe !== 'green-close' && start.recipe !== 'draft-close') throw new Error('map-run unknown recipe');
|
|
98
|
+
if (!SHA.test(start.recipeSha)) throw new Error('map-run recipe SHA missing');
|
|
99
|
+
if (!start.snapshotTime || !start.snapshotRef) throw new Error('map-run snapshot identity missing');
|
|
100
|
+
if (!SHA.test(start.mainSha) || !('checklistSnapshotPath' in start)
|
|
101
|
+
|| (start.recipe === 'green-close' && (!start.checklistSnapshotPath || !isAbsolute(start.checklistSnapshotPath)))
|
|
102
|
+
|| (start.recipe === 'draft-close' && start.checklistSnapshotPath !== null)) {
|
|
103
|
+
throw new Error('map-run main or checklist snapshot provenance missing');
|
|
104
|
+
}
|
|
105
|
+
if (!Array.isArray(start.itemIds) || start.itemIds.some((id) => typeof id !== 'string' || !ID.test(id))
|
|
106
|
+
|| new Set(start.itemIds).size !== start.itemIds.length) throw new Error('map-run item identities missing or duplicate');
|
|
107
|
+
if (typeof start.inventoryComplete !== 'boolean' || typeof start.dryRun !== 'boolean') throw new Error('map-run completeness or dry-run missing');
|
|
108
|
+
if (!start.inventoryComplete && start.itemIds.length) throw new Error('map-run partial inventory cannot name items');
|
|
109
|
+
if (!start.requestedPool || !('selectedPool' in start) || !('imageCommit' in start)) throw new Error('map-run pool or image provenance missing');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function validateRow(row: MapRunRow, start: MapRunStart): void {
|
|
113
|
+
if (row.mapRunId !== start.mapRunId || row.recipe !== start.recipe || !KINDS.has(row.kind) || row.verdict !== row.kind
|
|
114
|
+
|| !Array.isArray(row.evidence)
|
|
115
|
+
|| row.evidence.some((value) => typeof value !== 'string') || typeof row.reason !== 'string') {
|
|
116
|
+
throw new Error('map-run row contract mismatch');
|
|
117
|
+
}
|
|
118
|
+
if (!RECIPE_KINDS[start.recipe].has(row.kind)) throw new Error('map-run kind outside recipe');
|
|
119
|
+
if (row.rowsSuppressed) {
|
|
120
|
+
if (start.inventoryComplete || row.itemId !== null || row.pr !== null || row.kind !== 'hold') {
|
|
121
|
+
throw new Error('map-run suppressed row must hold an incomplete inventory without a PR');
|
|
122
|
+
}
|
|
123
|
+
} else if (!start.inventoryComplete) {
|
|
124
|
+
throw new Error('map-run incomplete inventory suppresses item rows');
|
|
125
|
+
} else if (!row.itemId || !start.itemIds.includes(row.itemId)) {
|
|
126
|
+
throw new Error(`map-run unknown item: ${row.itemId}`);
|
|
127
|
+
}
|
|
128
|
+
if (row.kind === 'hold' && !row.reason.trim()) throw new Error('map-run hold needs a reason');
|
|
129
|
+
if (row.kind !== 'hold' && row.evidence.length === 0) throw new Error('map-run candidate needs evidence');
|
|
130
|
+
if (row.kind === 'green-candidate'
|
|
131
|
+
&& (row.e1?.verdict !== 'pass' || row.e2?.verdict !== 'pass' || row.e3?.verdict !== 'pass'
|
|
132
|
+
|| !SHA.test(row.e1.mergeSha ?? '') || row.e1.mainAncestor !== true
|
|
133
|
+
|| !row.e3.reason.trim()
|
|
134
|
+
|| typeof row.pr !== 'number' || !Number.isSafeInteger(row.pr) || row.pr <= 0
|
|
135
|
+
|| !row.prUrl || !row.e2Source || row.e2Source === 'missing'
|
|
136
|
+
|| !row.e2.command?.trim() || row.e2.exitCode !== 0)) {
|
|
137
|
+
throw new Error('map-run green candidate needs E1/E2/E3 and PR proof');
|
|
138
|
+
}
|
|
139
|
+
if ((row.kind === 'harvest' || row.kind === 'close-candidate')
|
|
140
|
+
&& (typeof row.pr !== 'number' || !Number.isSafeInteger(row.pr) || row.pr <= 0 || !SHA.test(row.headOid ?? ''))) {
|
|
141
|
+
throw new Error('map-run draft decision needs exact PR head');
|
|
142
|
+
}
|
|
143
|
+
if (row.rc != null && row.rc !== 0 && row.kind !== 'hold') throw new Error('map-run failed Pod cannot yield a candidate');
|
|
144
|
+
for (const field of ['podJob', 'podUid', 'startedAt', 'endedAt'] as const) {
|
|
145
|
+
const value = row[field];
|
|
146
|
+
if (value != null && (typeof value !== 'string' || !value.trim())) throw new Error(`map-run invalid ${field}`);
|
|
147
|
+
}
|
|
148
|
+
if (row.result != null && (typeof row.result !== 'object' || Array.isArray(row.result))) {
|
|
149
|
+
throw new Error('map-run invalid result');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Exclusive create prevents a retry from silently replacing the original inventory. */
|
|
154
|
+
export function createMapRun(start: MapRunStart, root?: string): string {
|
|
155
|
+
validateStart(start);
|
|
156
|
+
const path = mapRunLedgerPath(start.mapRunId, root);
|
|
157
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
158
|
+
const fd = openSync(path, 'wx', 0o600);
|
|
159
|
+
try { writeSync(fd, `${JSON.stringify(start)}\n`); }
|
|
160
|
+
finally { closeSync(fd); }
|
|
161
|
+
return path;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function readMapRun(mapRunId: string, root?: string): MapRunRead {
|
|
165
|
+
const path = mapRunLedgerPath(mapRunId, root);
|
|
166
|
+
if (!existsSync(path)) throw new Error(`map-run missing: ${mapRunId}`);
|
|
167
|
+
const raw = readFileSync(path, 'utf8');
|
|
168
|
+
if (!raw.endsWith('\n')) throw new Error('map-run truncated JSONL tail');
|
|
169
|
+
const lines = raw.trimEnd().split('\n');
|
|
170
|
+
let start: MapRunStart;
|
|
171
|
+
try { start = JSON.parse(lines[0]!) as MapRunStart; }
|
|
172
|
+
catch { throw new Error('map-run invalid start JSON'); }
|
|
173
|
+
if (start.event !== 'start' || start.mapRunId !== mapRunId) throw new Error('map-run invalid start');
|
|
174
|
+
validateStart(start);
|
|
175
|
+
const rows: MapRunRow[] = [];
|
|
176
|
+
const seen = new Set<string>();
|
|
177
|
+
for (const line of lines.slice(1)) {
|
|
178
|
+
let row: MapRunRow;
|
|
179
|
+
try { row = JSON.parse(line) as MapRunRow; }
|
|
180
|
+
catch { throw new Error('map-run invalid row JSON'); }
|
|
181
|
+
if (row.event !== 'row') throw new Error('map-run unexpected event');
|
|
182
|
+
validateRow(row, start);
|
|
183
|
+
const key = row.rowsSuppressed ? '__inventory_hold__' : row.itemId!;
|
|
184
|
+
if (seen.has(key)) throw new Error(`map-run duplicate row: ${key}`);
|
|
185
|
+
seen.add(key);
|
|
186
|
+
rows.push(row);
|
|
187
|
+
}
|
|
188
|
+
if (rows.some((row) => row.rowsSuppressed) && rows.length !== 1) throw new Error('map-run suppressed inventory has item rows');
|
|
189
|
+
return { start, rows };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** The lock protects the read-before-append duplicate check across concurrent Pod completions. */
|
|
193
|
+
export function appendMapRunRow(row: MapRunRow, root?: string): void {
|
|
194
|
+
const path = mapRunLedgerPath(row.mapRunId, root);
|
|
195
|
+
withFileLockSync(`${path}.lock`, () => {
|
|
196
|
+
const current = readMapRun(row.mapRunId, root);
|
|
197
|
+
validateRow(row, current.start);
|
|
198
|
+
const key = row.rowsSuppressed ? '__inventory_hold__' : row.itemId!;
|
|
199
|
+
if (current.rows.some((existing) => (existing.rowsSuppressed ? '__inventory_hold__' : existing.itemId) === key)) {
|
|
200
|
+
throw new Error(`map-run duplicate row: ${key}`);
|
|
201
|
+
}
|
|
202
|
+
if (row.rowsSuppressed && current.rows.length) throw new Error('map-run inventory hold cannot follow item rows');
|
|
203
|
+
if (current.rows.some((existing) => existing.rowsSuppressed)) throw new Error('map-run inventory hold suppresses item rows');
|
|
204
|
+
appendFileSync(path, `${JSON.stringify(row)}\n`);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** No consumer of this result can write: incomplete evidence remains an explicit hold. */
|
|
209
|
+
export function reduceMapRun(mapRunId: string, root?: string): MapRunReduce {
|
|
210
|
+
const { start, rows } = readMapRun(mapRunId, root);
|
|
211
|
+
const counts: Record<MapRowKind, number> = {
|
|
212
|
+
'green-candidate': 0, 'mock-pr': 0, 'implementation-remains': 0,
|
|
213
|
+
harvest: 0, 'close-candidate': 0, hold: 0,
|
|
214
|
+
};
|
|
215
|
+
for (const row of rows) counts[row.kind]++;
|
|
216
|
+
const accounted = start.inventoryComplete && rows.length === start.itemIds.length
|
|
217
|
+
&& rows.every((row) => !row.rowsSuppressed);
|
|
218
|
+
const complete = accounted && start.itemIds.length > 0 && counts.hold === 0;
|
|
219
|
+
return {
|
|
220
|
+
mapRunId, verdict: complete ? 'complete' : 'hold',
|
|
221
|
+
reason: !start.inventoryComplete ? 'inventory-incomplete' : start.itemIds.length === 0 ? 'empty-inventory'
|
|
222
|
+
: !accounted ? 'results-missing'
|
|
223
|
+
: counts.hold ? 'item-hold' : 'all-items-accounted',
|
|
224
|
+
expected: start.itemIds.length, observed: rows.length, counts, writes: 0,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
@@ -81,7 +81,7 @@ export function resolveParentHosts(config: ParentHostSetting | undefined, env: N
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
/** Capacity sample is read on the host before launch. Unknown or saturated capacity is not a positive weight. */
|
|
84
|
-
export interface ParentHostCapacity { approvalHeadroom: number; load: number }
|
|
84
|
+
export interface ParentHostCapacity { active: number; waiting: number; approvalHeadroom: number; load: number }
|
|
85
85
|
|
|
86
86
|
export interface ParentHostPodFit {
|
|
87
87
|
state: 'fits' | 'never-fits' | 'unmeasured';
|
|
@@ -127,26 +127,35 @@ export function readParentHostPodFit(target: ParentHostConfig, memoryLimit: stri
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
const CAPACITY_MARKER = '__ELANOUS_PARENT_CAPACITY__';
|
|
130
|
+
const CAPACITY_ERROR_MARKER = '__ELANOUS_PARENT_CAPACITY_ERROR__';
|
|
130
131
|
const APPROVAL_MARKER = '__ELANOUS_PARENT_APPROVALS__';
|
|
131
132
|
const LOAD_MARKER = '__ELANOUS_PARENT_LOAD__';
|
|
133
|
+
/** Best-effort macOS `ps` fallback; Linux/WSL uses argv boundaries below. */
|
|
134
|
+
export const PARENT_HOST_ACTIVE_FILTER = String.raw`/^(([^ ]+\/)?(bun|node|nodejs) )?([^ ]+\/)?elanous(\.mjs)?( --config-dir [^ ]+)? harness (say|ask)( |$)/ && / --no-auto-merge( |$)/ && !/ --substrate local( |$)/ && !/^([^ ]+\/)?[^ ]*sh -c/`;
|
|
135
|
+
/** Count exact Linux argv entries so Pod-local commands and goal text cannot masquerade as host parents. */
|
|
136
|
+
export const PARENT_HOST_PROC_COUNT_SCRIPT = "count=0; proc_root=${1:-/proc}; for file in \"$proc_root\"/[0-9]*/cmdline; do [ -r \"$file\" ] || continue; argv=(); while IFS= read -r -d '' arg; do argv+=(\"$arg\"); done < \"$file\" 2>/dev/null || true; [ ${#argv[@]} -ge 3 ] || continue; program=\"${argv[0]##*/}\"; case \"$program\" in elanous|elanous.mjs) ;; bun|node|nodejs) script_match=0; for ((i=1; i<${#argv[@]}; i++)); do if [[ \"${argv[i]}\" == */elanous.mjs ]]; then script_match=1; break; fi; [[ \"${argv[i]}\" == harness ]] && break; done; [ \"$script_match\" -eq 1 ] || continue ;; *) continue ;; esac; harness=0; for ((i=0; i+1<${#argv[@]}; i++)); do if [[ \"${argv[i]}\" == harness && ( \"${argv[i+1]}\" == say || \"${argv[i+1]}\" == ask ) ]]; then harness=1; break; fi; done; [ \"$harness\" -eq 1 ] || continue; has_no_auto_merge=0; for arg in \"${argv[@]}\"; do [[ \"$arg\" == --no-auto-merge ]] && has_no_auto_merge=1; done; [ \"$has_no_auto_merge\" -eq 1 ] || continue; local_substrate=0; for ((i=0; i+1<${#argv[@]}; i++)); do if [[ \"${argv[i]}\" == --substrate && \"${argv[i+1]}\" == local ]]; then local_substrate=1; break; fi; done; [ \"$local_substrate\" -eq 1 ] || count=$((count+1)); done; printf '__ELANOUS_PARENT_CAPACITY__%s\\n' \"$count\""
|
|
132
137
|
/** Recent admission decisions are the host's measured approval backlog, not an inferred process count. */
|
|
133
138
|
export function readParentHostCapacity(target: ParentHostConfig, ssh: SshRunner = defaultSshRunner): ParentHostCapacity | null {
|
|
134
139
|
const executable = remotePath(target.elanous ?? DEFAULT_REMOTE_ELANOUS);
|
|
135
140
|
const configDir = remotePath(target.configDir ?? DEFAULT_REMOTE_CONFIG_DIR);
|
|
136
|
-
const
|
|
141
|
+
const unavailable = (reason: string): null => { try { debug.log('task-agent.parent-host', 'capacity-unavailable', { host: target.host, reason }); } catch { /* fail-soft */ } return null; };
|
|
142
|
+
const processCount = `if [ -r /proc/self/cmdline ]; then if command -v bash >/dev/null 2>&1; then bash -c ${shellQuote(PARENT_HOST_PROC_COUNT_SCRIPT)} -- /proc; else printf "${CAPACITY_ERROR_MARKER}bash-unavailable\\n"; exit 1; fi; else parents=$(ps -Ao command) || exit 1; printf '%s\\n' "$parents" | awk '${PARENT_HOST_ACTIVE_FILTER} { n++ } END { print "${CAPACITY_MARKER}" n+0 }'; fi`;
|
|
143
|
+
const script = `${processCount}; `
|
|
137
144
|
+ `${executable} --config-dir ${configDir} logs --event admit-by-usage --since 5m --json --limit 1000 || exit 1; `
|
|
138
145
|
+ `printf '\\n${APPROVAL_MARKER}\\n'; `
|
|
139
146
|
+ `cpu=$(ps -Ao %cpu=) || exit 1; printf '%s\\n' "$cpu" | awk '{ total += $1 } END { printf "${LOAD_MARKER}%.3f\\n", total+0 }'; `
|
|
140
147
|
+ `cores=$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null) || exit 1; printf '${LOAD_MARKER}cores=%s\\n' "$cores"`;
|
|
141
148
|
try {
|
|
142
149
|
const result = ssh(target.host, script, 15_000);
|
|
143
|
-
if (result.status !== 0) return
|
|
150
|
+
if (result.status !== 0) return unavailable(result.stdout.includes(`${CAPACITY_ERROR_MARKER}bash-unavailable`) ? 'bash-unavailable' : 'remote-command-failed');
|
|
144
151
|
const activeMatch = new RegExp(`^${CAPACITY_MARKER}(\\d+)$`, 'm').exec(result.stdout);
|
|
145
152
|
const boundary = result.stdout.indexOf(`\n${APPROVAL_MARKER}\n`);
|
|
146
153
|
const logsStart = result.stdout.indexOf('\n');
|
|
147
154
|
const cpuMatch = new RegExp(`^${LOAD_MARKER}([0-9]+(?:\\.[0-9]+)?)$`, 'm').exec(result.stdout);
|
|
148
155
|
const coresMatch = new RegExp(`^${LOAD_MARKER}cores=(\\d+)$`, 'm').exec(result.stdout);
|
|
149
|
-
if (!activeMatch || activeMatch.index !== 0
|
|
156
|
+
if (!activeMatch || activeMatch.index !== 0) return unavailable('active-count-marker-missing');
|
|
157
|
+
if (logsStart < 0 || boundary < logsStart) return unavailable('approval-boundary-missing');
|
|
158
|
+
if (!cpuMatch || !coresMatch) return unavailable('load-marker-missing');
|
|
150
159
|
const active = Number(activeMatch[1]);
|
|
151
160
|
const cores = Number(coresMatch[1]);
|
|
152
161
|
const cpu = Number(cpuMatch[1]);
|
|
@@ -172,23 +181,24 @@ export function readParentHostCapacity(target: ParentHostConfig, ssh: SshRunner
|
|
|
172
181
|
decisions.set(data.runId, data.recommended as number);
|
|
173
182
|
}
|
|
174
183
|
const waiting = [...decisions.values()].filter((n) => n === 0).length;
|
|
175
|
-
return { approvalHeadroom: Math.max(0, capacity - active - waiting), load: Math.max(0, cpu / (cores * 100)) };
|
|
176
|
-
} catch { return
|
|
184
|
+
return { active, waiting, approvalHeadroom: Math.max(0, capacity - active - waiting), load: Math.max(0, cpu / (cores * 100)) };
|
|
185
|
+
} catch { return unavailable('capacity-parse-failed'); }
|
|
177
186
|
}
|
|
178
187
|
|
|
179
|
-
/** Weighted rendezvous
|
|
180
|
-
export function
|
|
188
|
+
/** Weighted rendezvous rank with evidence fields for launch-time observation. */
|
|
189
|
+
export function rankParentHosts(targets: readonly ParentHostConfig[], runId: string, capacities: Readonly<Record<string, ParentHostCapacity | null>>): Array<{ target: ParentHostConfig; weight: number; score: number }> {
|
|
181
190
|
return targets.map((target, index) => {
|
|
182
191
|
const sample = capacities[target.host];
|
|
183
192
|
const weight = sample && Number.isSafeInteger(sample.approvalHeadroom) && sample.approvalHeadroom > 0 && Number.isFinite(sample.load) && sample.load >= 0
|
|
184
193
|
? sample.approvalHeadroom / (1 + sample.load) : 0;
|
|
185
194
|
const hash = createHash('sha256').update(`${runId}\0${target.host}`).digest();
|
|
186
195
|
const fraction = (hash.readUIntBE(0, 6) + 1) / (2 ** 48 + 1);
|
|
187
|
-
return { target, index, score: weight > 0 ? -Math.log(fraction) / weight : Infinity };
|
|
188
|
-
}).sort((a, b) => a.score - b.score || a.index - b.index).map(({ target }) => target);
|
|
196
|
+
return { target, index, weight, score: weight > 0 ? -Math.log(fraction) / weight : Infinity };
|
|
197
|
+
}).sort((a, b) => a.score - b.score || a.index - b.index).map(({ target, weight, score }) => ({ target, weight, score }));
|
|
189
198
|
}
|
|
190
199
|
|
|
191
|
-
|
|
200
|
+
|
|
201
|
+
/** Keep `$HOME/...` outside quotes so the remote shell expands it; quote every other path. */
|
|
192
202
|
function remotePath(path: string): string {
|
|
193
203
|
return path.startsWith('$HOME/') ? `"$HOME"/${shellQuote(path.slice('$HOME/'.length))}` : shellQuote(path);
|
|
194
204
|
}
|
|
@@ -102,6 +102,8 @@ export interface RejudgeDeps {
|
|
|
102
102
|
notifyRepair?: (seat: string, body: string) => void;
|
|
103
103
|
/** Shared new-launch budget for this `tasks rejudge --all` call. */
|
|
104
104
|
repairBudget?: import('./repair-cycle.js').RepairTickBudget;
|
|
105
|
+
/** Verify a later PR head contains the harvested owner commit (read-only). */
|
|
106
|
+
ownerHarvestAncestor?: (cwd: string, owner: string, head: string) => boolean;
|
|
105
107
|
/** One review request across all candidates in one --all tick; standalone card rejudge is uncapped. */
|
|
106
108
|
reviewBudget?: { max: number; requested: number };
|
|
107
109
|
/** One land or queue attempt across all candidates in one --all tick. */
|
|
@@ -346,10 +348,11 @@ const catchUpFail = (reason: string, extra: Partial<CatchUpResult> = {}): CatchU
|
|
|
346
348
|
/**
|
|
347
349
|
* origin/main 을 PR 머리에 3-way merge(rerere 켬) 하고 일반 push 한다.
|
|
348
350
|
* ⛔ rebase · force-push · reset · 남의 가지 파일째 checkout — 충돌이면 merge --abort.
|
|
351
|
+
* 이전 시도의 worktree 경로가 남았으면 새 경로로 한 번 더 시도한다. 남은 트리를 강제 제거하지 않는다.
|
|
349
352
|
*/
|
|
350
353
|
export async function defaultCatchUpMain(request: CatchUpRequest, git: CatchUpGit = defaultCatchUpGit): Promise<CatchUpResult> {
|
|
351
354
|
const repo = request.cwd;
|
|
352
|
-
|
|
355
|
+
let work = request.worktreePath ?? join(tmpdir(), `ta-catch-up-${request.pr}-${safe(request.head.slice(0, 12))}-${process.pid}`);
|
|
353
356
|
const gitRepo = (args: string[]) => git(repo, args);
|
|
354
357
|
const gitWork = (args: string[]) => git(work, args);
|
|
355
358
|
const fromBase = request.fromBase;
|
|
@@ -367,7 +370,12 @@ export async function defaultCatchUpMain(request: CatchUpRequest, git: CatchUpGi
|
|
|
367
370
|
return catchUpFail(`fetched PR head ${(fetchedHead ?? '?').slice(0, 12)} ≠ ${request.head.slice(0, 12)}`, { fromBase, toBase });
|
|
368
371
|
}
|
|
369
372
|
|
|
370
|
-
|
|
373
|
+
let added = gitRepo(['worktree', 'add', '--detach', work, request.head]);
|
|
374
|
+
if (added.status !== 0 && /already exists|already registered|already used by worktree/i.test(added.stderr || added.stdout)) {
|
|
375
|
+
// 남은 경로가 살아 있는 다른 시도의 것일 수 있으므로 건드리지 않는다.
|
|
376
|
+
work = `${work}-retry-${randomUUID()}`;
|
|
377
|
+
added = gitRepo(['worktree', 'add', '--detach', work, request.head]);
|
|
378
|
+
}
|
|
371
379
|
if (added.status !== 0) return catchUpFail(`worktree add failed: ${(added.stderr || added.stdout).trim().slice(-200)}`, { fromBase, toBase });
|
|
372
380
|
const cleanup = () => { gitRepo(['worktree', 'remove', '--force', work]); };
|
|
373
381
|
|
|
@@ -495,7 +503,8 @@ export async function rejudgeCardOnHead(cardId: string, deps: RejudgeDeps): Prom
|
|
|
495
503
|
if ('reason' in found) return target('pr-unreadable', found.reason, pr, fromHead);
|
|
496
504
|
const { cwd, view } = found;
|
|
497
505
|
const toHead = view.head;
|
|
498
|
-
const
|
|
506
|
+
const harvest = pendingOwnerHarvest(card, pr);
|
|
507
|
+
const eligible = !!reviews.length || adopted || !!harvestReviewPending(card, pr) || (!!card.parentHost && (deps.remoteRunTerminal ?? remoteBoundRunTerminal)(card));
|
|
499
508
|
const trustedTrain = view.state === 'CLOSED' && card.status === 'launched'
|
|
500
509
|
? (() => { try { return (deps.trainLanding ?? verifiedTrainLanding)(pr, cwd); } catch { return null; } })() : null;
|
|
501
510
|
// An unreviewed or unproven remote card retains the old guard. Only verified train
|
|
@@ -543,6 +552,25 @@ export async function rejudgeCardOnHead(cardId: string, deps: RejudgeDeps): Prom
|
|
|
543
552
|
: target('pr-not-open', `PR #${pr} is ${view.state}`, pr, fromHead, toHead);
|
|
544
553
|
}
|
|
545
554
|
if (view.state !== 'OPEN') return target('pr-not-open', `PR #${pr} is ${view.state}`, pr, fromHead, toHead);
|
|
555
|
+
if (harvest) {
|
|
556
|
+
const ancestor = deps.ownerHarvestAncestor ?? ((repo: string, owner: string, head: string) => {
|
|
557
|
+
const result = spawnSync('git', ['merge-base', '--is-ancestor', owner, head], { cwd: repo, encoding: 'utf8', timeout: 10_000 });
|
|
558
|
+
return result.status === 0;
|
|
559
|
+
});
|
|
560
|
+
let reached = sameHead(toHead, harvest.head);
|
|
561
|
+
if (!reached) { try { reached = ancestor(cwd, harvest.head, toHead); } catch { reached = false; } }
|
|
562
|
+
if (!reached) {
|
|
563
|
+
emit({ card: card.id, pr, fromHead, toHead, step: 'review', decision: 'awaiting-harvest',
|
|
564
|
+
reason: `PR #${pr} head ${toHead.slice(0, 12)} awaits owner-worktree-commit ${harvest.head.slice(0, 12)} · next: wait for harvest` }, false);
|
|
565
|
+
return outcomes;
|
|
566
|
+
}
|
|
567
|
+
updateTaskCard(statePath, card.id, current => current && cardPrNumber(current) === pr
|
|
568
|
+
&& typeof current.pr === 'object' && current.pr.url === url && pendingOwnerHarvest(current, pr)?.head === harvest.head
|
|
569
|
+
&& !current.history.some(entry => entry.event === 'review-harvest-reached' && entry.pr === pr
|
|
570
|
+
&& entry.runId === current.runId && entry.head && sameHead(entry.head, toHead))
|
|
571
|
+
? { ...current, history: [...current.history, { at: now().toISOString(), event: 'review-harvest-reached',
|
|
572
|
+
pr, head: toHead, ...(current.runId ? { runId: current.runId } : {}), detail: `PR #${pr} head reached owner-worktree-commit · next: review` }] } : undefined);
|
|
573
|
+
}
|
|
546
574
|
// An explicitly rejudged PR can be reopened. Clear the old marker before normal head judgement.
|
|
547
575
|
if (boundPrTerminal(card)) {
|
|
548
576
|
updateTaskCard(statePath, card.id, (current) => current && cardPrNumber(current) === pr && typeof current.pr === 'object' && current.pr.url === url
|
|
@@ -552,6 +580,26 @@ export async function rejudgeCardOnHead(cardId: string, deps: RejudgeDeps): Prom
|
|
|
552
580
|
const step = (s: RejudgeStep, decision: string, reason: string, record: boolean, extra: Partial<RejudgeOutcome> = {}) =>
|
|
553
581
|
emit({ card: card!.id, pr, fromHead, toHead, step: s, decision, reason, ...extra }, record);
|
|
554
582
|
const requestReview = async (candidate: TaskCard, reason: string, forceNewReview = false): Promise<void> => {
|
|
583
|
+
// The head used for this decision may have moved while the card was being updated.
|
|
584
|
+
// Never request a review of a new head until it is verified against the owner commit.
|
|
585
|
+
if (harvest) {
|
|
586
|
+
let fresh: PrHeadView | null;
|
|
587
|
+
try { fresh = await (live.prHead ?? defaultPrHead)(pr, cwd); } catch { fresh = null; }
|
|
588
|
+
if (!fresh || fresh.state !== 'OPEN' || !fresh.url || !sameUrl(fresh.url, url)) {
|
|
589
|
+
step('review', 'awaiting-harvest', `PR #${pr} fresh head unavailable · next: wait for harvest`, false);
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
let reached = sameHead(fresh.head, harvest.head);
|
|
593
|
+
if (!reached) {
|
|
594
|
+
try { reached = (deps.ownerHarvestAncestor ?? ((repo: string, owner: string, head: string) =>
|
|
595
|
+
spawnSync('git', ['merge-base', '--is-ancestor', owner, head], { cwd: repo, encoding: 'utf8', timeout: 10_000 }).status === 0))(cwd, harvest.head, fresh.head); }
|
|
596
|
+
catch { reached = false; }
|
|
597
|
+
}
|
|
598
|
+
if (!reached) {
|
|
599
|
+
step('review', 'awaiting-harvest', `PR #${pr} head ${fresh.head.slice(0, 12)} awaits owner-worktree-commit ${harvest.head.slice(0, 12)} · next: wait for harvest`, false);
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
555
603
|
const budget = deps.reviewBudget;
|
|
556
604
|
if (budget && budget.requested >= budget.max) {
|
|
557
605
|
step('review', 'tick-cap', `review request cap ${budget.max} reached — next tick`, false);
|
|
@@ -560,9 +608,12 @@ export async function rejudgeCardOnHead(cardId: string, deps: RejudgeDeps): Prom
|
|
|
560
608
|
// Reserve before awaiting the worker so another candidate cannot launch a second request.
|
|
561
609
|
if (budget) budget.requested++;
|
|
562
610
|
const before = { reviewRequests: candidate.reviewRequests, landAttempts: candidate.landAttempts };
|
|
563
|
-
const result = await executeLiveReview(candidate, pr, { runId: candidate.runId ?? null, stopReason: reason, cwd, forceNewReview
|
|
611
|
+
const result = await executeLiveReview(candidate, pr, { runId: candidate.runId ?? null, stopReason: reason, cwd, forceNewReview,
|
|
612
|
+
...(harvest ? { ownerHarvestHead: harvest.head, ...(deps.ownerHarvestAncestor ? { ownerHarvestAncestor: deps.ownerHarvestAncestor } : {}) } : {}) },
|
|
613
|
+
{ ...live, ...(deps.log ? { log: deps.log } : {}) });
|
|
564
614
|
recordMove(statePath, candidate.id, candidate.runId, 'review', result, pr, before, now, deps.log);
|
|
565
|
-
step('review', result.executed
|
|
615
|
+
step('review', harvest && !result.executed && result.detail.includes('awaits owner-worktree-commit') ? 'awaiting-harvest'
|
|
616
|
+
: result.executed ? 'review-requested' : 'review-request-failed', result.detail, true);
|
|
566
617
|
};
|
|
567
618
|
|
|
568
619
|
// Adoption is a first judgement for the current head, even when the implementation run
|
|
@@ -938,6 +989,21 @@ function recordMove(statePath: string, cardId: string, runId: string | undefined
|
|
|
938
989
|
} catch { /* recordLiveMoveHistory 는 스스로 관측한다 */ }
|
|
939
990
|
}
|
|
940
991
|
|
|
992
|
+
function harvestReviewPending(card: TaskCard, pr: number): boolean {
|
|
993
|
+
if (card.status !== 'launched' || typeof card.pr !== 'object' || card.pr.number !== pr || !card.pr.url || !card.runId) return false;
|
|
994
|
+
const latest = card.history.filter(entry => (entry.event === 'review-skipped-harvest' || entry.event === 'review-harvest-reached')
|
|
995
|
+
&& entry.pr === pr && typeof entry.head === 'string' && /^[a-f0-9]{40}$/i.test(entry.head)
|
|
996
|
+
&& entry.runId === card.runId).at(-1);
|
|
997
|
+
return !!latest && !(card.reviewRequests ?? []).some(entry => entry.pr === pr && sameHead(entry.head, latest.head!));
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
function pendingOwnerHarvest(card: TaskCard, pr: number): { head: string } | undefined {
|
|
1001
|
+
if (!harvestReviewPending(card, pr)) return undefined;
|
|
1002
|
+
const latest = card.history.filter(entry => entry.event === 'review-skipped-harvest' && entry.pr === pr
|
|
1003
|
+
&& entry.runId === card.runId && typeof entry.head === 'string' && /^[a-f0-9]{40}$/i.test(entry.head)).at(-1);
|
|
1004
|
+
return latest?.effect === 'wait' ? { head: latest.head! } : undefined;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
941
1007
|
/** 재판정 대상 카드 — 기존 리뷰 또는 HQ 가 종결 원장을 검증할 원격 부모 카드. */
|
|
942
1008
|
export function rejudgeCandidates(tasks: Readonly<Record<string, TaskCard>>, opts: {
|
|
943
1009
|
now?: Date; maxAgeHours?: number; onAgeSkip?: (card: TaskCard, ageHours: number | null) => void;
|
|
@@ -949,6 +1015,7 @@ export function rejudgeCandidates(tasks: Readonly<Record<string, TaskCard>>, opt
|
|
|
949
1015
|
return Object.values(tasks).filter((card) => {
|
|
950
1016
|
const pr = cardPrNumber(card);
|
|
951
1017
|
const eligible = pr !== undefined && !boundPrTerminal(card) && ((card.reviewRequests ?? []).some((entry) => entry.pr === pr)
|
|
1018
|
+
|| (pr !== undefined && harvestReviewPending(card, pr))
|
|
952
1019
|
|| (card.status === 'launched' && card.taLand === true && ((!!card.parentHost && !!card.runId && !!card.runChildId)
|
|
953
1020
|
|| (card.adoptedPr?.pr === pr && typeof card.pr === 'object' && !!card.pr.url && sameUrl(card.adoptedPr.url, card.pr.url)))));
|
|
954
1021
|
if (!eligible) return false;
|