elanous 0.2.35 → 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/{T1mBQqmkwKDLGtyisDCOd → 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/{9952.5daf5f94063a2b3e.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/1746.3563617ea64023a3.js +115 -0
- 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/{1dc5a567.9b092bfae3d7099b.js → 1ed5e8af.cf424815c7c0efad.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{4705-194e64e47ca0fbdb.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/{13.02a07436dec15f89.js → 210.90b2cbd64926cf34.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{9512.5a76207a730e0491.js → 2283.edd9627b7bdfb546.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/{6866-79aa5b4e47cac6aa.js → 2318-450e75d07605b8fe.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{938.66ad1ef0e5abae1c.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/3199.6a1a9b0e6cd01dc6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3212.12179fd3eee8cef1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9214.63a510d39178e997.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/{2018.a741297bce759490.js → 4313.76f232c304a15bde.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{1165.bad13fc9151a7288.js → 4324.c99ba3e83eb1b691.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{3f7c4ee5.3dde54f01a816613.js → 4337e36d.f42c3b391b3d8f20.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{7385.50a308a477bdfc21.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/{8557-e7898769499cd56f.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/{5483-dd5d7697f6bdb106.js → 4833-7934c592c5569f00.js} +1 -1
- 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/{8444.4c26b379ca2b9596.js → 4931.95cc6dbd230774ca.js} +17 -17
- 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/{3408.21d1570602f4b6c0.js → 5046.7fd5cbd859299448.js} +1 -1
- 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/58edce7d.f1b0c72acf13b123.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5925.3b1f8b135710249b.js +93 -0
- package/apps/pwa/out/_next/static/chunks/5952.294fc574a8625184.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{b6705a4f-b9a9047de9eecfd0.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/6252.240578f10952e091.js +34 -0
- package/apps/pwa/out/_next/static/chunks/6416.5d50b8ec9986ca98.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5101.e32282718f99ed6f.js → 6680.6a693c28aae4cd19.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{4718-17d2af636796a330.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/{6236.e18cced2d5ba6afb.js → 6819.be5c9c4d67f22922.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/6877-84d47b7e85d0c41e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2833-0fe47af99ed13c29.js → 6971-fb5f9dc0a2cb5d2d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{da5176c6.6773b6c5994e1d99.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/7424.c0fe2a595d4232b5.js +23 -0
- 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/{7644.17e37ba6f1d4226e.js → 7867.e8725745ddd1d940.js} +4 -4
- package/apps/pwa/out/_next/static/chunks/7893-e444a1b0809cd1e4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7956-33748cabb3a05562.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{0368f25d-ad84af92fefee68f.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/{359-2250789a2d3499e7.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/{b2f6ba4d.fb56fdbbf798df33.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/{1530.0e612369b6fd7ea0.js → 9151.a2c51e93f9156dae.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{4441-875b97f5821ed58a.js → 9175-7d383df15a82e2be.js} +1 -1
- 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/958.53a8dcf4729e6997.js +64 -0
- 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-3337c6a1328c2674.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-89acd5d68c0bc560.js +1 -0
- 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-fe986fe79b107546.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-47c5d4db587266bd.js → page-1e5da297557d2354.js} +1 -1
- 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-1f5a60f70cb4dd0c.js → page-88a1edbff62eaf1d.js} +1 -1
- 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-bac7dee94e5eae4a.js → page-443364c9e4a23b31.js} +1 -1
- 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-dc0a532f5b9196ec.js → page-ac2c1e6169cfce57.js} +1 -1
- 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/{d06f9160.a3d9062f95de8ad7.js → d9815f84.4918f682a45a8502.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{cbe38a15.5a032598e39970c6.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/docs/goal-context/05-authoring-lessons.jsonl +47 -0
- package/docs/goal-context/README.md +3 -0
- package/graphs/default-loop.yaml +6 -1
- package/graphs/drafts/recipes.yaml +27 -7
- package/graphs/implement-loop.yaml +24 -0
- package/graphs/release/recipes.yaml +6 -0
- package/graphs/release/release-loop.yaml +17 -4
- package/graphs/wizard/recipes.yaml +44 -0
- package/graphs/wizard/wizard-loop.yaml +44 -0
- 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/research-report.yaml +2 -0
- 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/ci-typecheck-changed.ts +139 -5
- package/scripts/codex-fallback-live-probe.ts +17 -15
- package/scripts/coord-role-pack.ts +1 -1
- package/scripts/first10-gate.ts +416 -0
- package/scripts/fleet/node-setup.sh +32 -2
- package/scripts/fleet/run-light-worker.sh +9 -0
- package/scripts/fleet/wsl-node-setup.sh +432 -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 +344 -8
- package/scripts/release-loop/gate-closure.ts +48 -0
- package/scripts/release-loop/gate-diff.ts +2 -1
- package/scripts/release-loop/gate-node.ts +249 -53
- package/scripts/release-loop/known-issues-node.ts +6 -4
- package/scripts/release-loop/node-verdict.ts +2 -0
- package/scripts/release-loop/notes-repair.ts +216 -0
- package/scripts/release-loop/prepare-node.ts +61 -5
- package/scripts/release-loop/resume-cap.ts +14 -34
- package/scripts/release-loop/resume-release.ts +151 -15
- package/scripts/release-loop/resume-test-traffic.ts +10 -0
- package/scripts/release-loop/traffic-node.ts +59 -0
- package/scripts/release-loop/traffic-transition.ts +159 -0
- package/scripts/release-loop/version-node.ts +81 -12
- package/scripts/rhythm/daily.ts +137 -3
- package/scripts/train/green-leg.ts +176 -0
- package/scripts/tui-continue-real-evidence.ts +47 -0
- package/scripts/ux-sim.ts +53 -0
- package/skills/README.md +1 -0
- package/src/acp/client.ts +31 -5
- package/src/acp/core-turn-bridge.ts +54 -6
- package/src/acp/stderr-summary.ts +49 -0
- package/src/agent/delegate-hint.ts +16 -0
- package/src/agent/monad-agent-turn.ts +1 -0
- package/src/agent/runner.ts +82 -2
- package/src/agent/task-notification.ts +56 -10
- package/src/agent/terminal-surface.ts +217 -0
- package/src/agent/types.ts +4 -0
- package/src/agent-substrate/pr-reviewer.ts +64 -4
- package/src/agent-substrate/review-intent.ts +29 -2
- package/src/agent-substrate/review-observation.ts +3 -0
- package/src/agent-substrate/self-review-cli.ts +490 -26
- package/src/autopilot/lineage/sources.ts +1 -1
- package/src/autopilot/mission-codebase-gate.ts +141 -12
- package/src/autopilot/pr-manager.ts +89 -18
- package/src/boot/daemon-runtime.ts +35 -3
- package/src/boot/daemon-tools/pty-shell.ts +2 -1
- package/src/boot/daemon-tools/self-implement.ts +1 -0
- package/src/budget/run-usage-rollup.ts +12 -7
- package/src/budget/unified-usage.ts +20 -0
- package/src/budget/usage-outcomes.ts +8 -4
- package/src/capture/tui-self-report.ts +132 -1
- package/src/chat/goal-continue-verdict.ts +41 -0
- package/src/chat/index.ts +30 -4
- package/src/chat/internal-turn-origin.ts +1 -0
- package/src/chat/paste-fold.ts +86 -0
- package/src/chat/tool-render/block.ts +32 -0
- package/src/chat/tool-render/dispatch.ts +2 -20
- package/src/chat/tool-render/result-text.ts +98 -0
- package/src/chat/turn-typeahead.ts +70 -7
- package/src/cli/autopilot-cli.ts +24 -9
- package/src/cli/config-audit.ts +119 -0
- package/src/cli/decisions-cli.ts +7 -4
- package/src/cli/doctor-cli.ts +137 -6
- package/src/cli/guardian-cli.ts +10 -0
- package/src/cli/herdr-cli.ts +34 -0
- package/src/cli/light-entry-route.ts +6 -3
- package/src/cli/log-event-names.ts +2 -0
- package/src/cli/logs-cli.ts +357 -2
- package/src/cli/make-cli.ts +247 -11
- package/src/cli/memory-cli.ts +30 -1
- package/src/cli/msg-cli.ts +132 -14
- package/src/cli/pr-cli.ts +29 -16
- package/src/cli/pr-granularity.ts +5 -3
- package/src/cli/pr-land-base-read.ts +11 -2
- package/src/cli/pr-land-heavy-remote.ts +6 -3
- package/src/cli/pr-land-overlap-evidence.ts +23 -4
- package/src/cli/pr-land-recovery.ts +117 -1
- package/src/cli/pty-takeover-cli.ts +8 -7
- package/src/cli/release-checklist-read-cli.ts +109 -4
- package/src/cli/release-cli.ts +64 -15
- package/src/cli/release-single-flight.ts +1 -1
- package/src/cli/self-update.ts +23 -5
- package/src/cli/self-utterances-cli.ts +13 -16
- package/src/cli/tasks-card-show.ts +162 -0
- package/src/cli/tasks-cli.ts +868 -352
- package/src/cli/tasks-read-cli.ts +197 -0
- 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-core-turn.ts +4 -2
- package/src/core-turn/run-goal-loop.ts +76 -16
- package/src/core-turn/types.ts +8 -1
- package/src/dashboard/chat/acp-chat.ts +9 -0
- package/src/dashboard/context/keys.ts +8 -31
- package/src/dashboard/index.ts +299 -62
- package/src/dashboard/input/agent-wake.ts +50 -1
- package/src/dashboard/input/chat-main-acp-dispatch.ts +21 -1
- package/src/dashboard/input/chat-main-plain-turn-runtime.ts +146 -3
- package/src/dashboard/input/chat-main-slash-command.ts +4 -2
- package/src/dashboard/input/goal-continue.ts +210 -0
- package/src/dashboard/input/goal-interim.ts +2 -0
- package/src/dashboard/input/internal-turn.ts +2 -8
- package/src/dashboard/input/seat-autowake.ts +9 -7
- package/src/dashboard/input/turn-autoheal.ts +52 -0
- package/src/dashboard/replay-preview.ts +25 -0
- package/src/dashboard/seat-boot-preamble.ts +132 -5
- package/src/dashboard/slash-runtime/chat-persona.ts +65 -8
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +20 -10
- package/src/dashboard/slash-runtime/model-choices.ts +15 -0
- package/src/dashboard/stale-install-check.ts +3 -99
- package/src/dashboard/synchronized-frame-output.ts +74 -0
- package/src/debug/batch-queue.ts +119 -0
- package/src/debug/tui-log-tag.ts +67 -0
- package/src/display/agent-surface.ts +7 -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/standalone-log-sink.ts +15 -2
- package/src/domains/surface-events.ts +80 -1
- package/src/esc-abort-gate.ts +69 -0
- 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/git-fs/worktree.ts +95 -1
- package/src/graph-runner/graph-cli.ts +44 -2
- package/src/graph-runner/graph-run-api.ts +3 -2
- package/src/graph-runner/runner.ts +37 -16
- package/src/graph-wizard/generate.ts +131 -4
- package/src/graph-wizard/steps.ts +15 -0
- package/src/grounding/proactive-ground.ts +86 -0
- package/src/harness/control-inbox.ts +49 -1
- package/src/harness/harness-cli-command.ts +230 -40
- package/src/harness/harness-incidents.ts +52 -1
- package/src/harness/pod-control-send.ts +33 -5
- package/src/harness/review-adapter.ts +26 -6
- package/src/harness/self-send-remote.ts +69 -0
- package/src/harness/self-send-routing-guard.ts +6 -0
- package/src/harness/self-send-target.ts +4 -3
- package/src/index.ts +443 -25
- package/src/input-core/context-key-runtime.ts +21 -0
- package/src/instance/test-write-guard-real-transport.fixture.ts +52 -0
- package/src/light-cli.ts +6 -1
- 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-wellformed.ts +70 -0
- package/src/llm.ts +158 -15
- 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/msg-wake.ts +35 -39
- package/src/loops/orchestrator/anomaly-cluster.ts +243 -0
- package/src/loops/orchestrator/inbox.ts +59 -11
- package/src/loops/orchestrator/knowhow-pack.ts +364 -0
- package/src/loops/orchestrator/tick.ts +54 -5
- package/src/maturity/feature-maturity.ts +1 -1
- package/src/model-tier/llm-tier-map.ts +12 -17
- package/src/models/prompts.ts +17 -2
- package/src/msg/msg-post-office.ts +116 -0
- package/src/msg/msg-store.ts +17 -0
- package/src/mss/logging/log-store.ts +79 -22
- package/src/native-tool-catalog.ts +1 -1
- package/src/nexus/api/decisions-route.ts +38 -18
- package/src/nexus/api/graph-wizard-api.ts +46 -3
- 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/boot/register-mcp-clients.ts +263 -38
- package/src/nexus/onboarding/first-boot-wizard.ts +56 -8
- package/src/nexus/state/state.ts +7 -1
- package/src/oauth/codex-account-store.ts +34 -1
- package/src/onboarding/first-run.ts +37 -6
- package/src/onboarding/web-first.ts +43 -11
- package/src/ops/loops-map.ts +46 -0
- package/src/ops/overview-cache.ts +22 -4
- package/src/ops/overview-collect.ts +12 -6
- package/src/ops/overview.ts +44 -8
- package/src/persona/loader.ts +11 -0
- package/src/persona/types.ts +8 -0
- package/src/policy/llm-policy.ts +3 -1
- package/src/prompt-enhance/enhance.ts +4 -4
- package/src/prompt-library/session-guidance.ts +14 -0
- package/src/provider-summary.ts +8 -0
- package/src/pty-shell/availability.ts +7 -0
- package/src/pty-shell/herdr-agent-report.ts +155 -37
- package/src/pty-shell/herdr-prompt.ts +266 -0
- 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 +327 -15
- package/src/pty-shell/pty-special-keys.ts +42 -2
- package/src/pty-shell/registry.ts +25 -15
- package/src/pty-shell/stale-install-check.ts +99 -0
- package/src/pty-shell/tui-run-state.ts +249 -3
- package/src/release-loop/auto-start.ts +20 -2
- package/src/release-loop/checklist.ts +126 -9
- package/src/release-loop/feature-store.ts +9 -9
- package/src/release-loop/landed-but-yellow.ts +93 -12
- package/src/release-loop/rehearsal.ts +36 -20
- package/src/repl/index.ts +27 -1
- package/src/seat-loop/knowhow-lint.ts +1 -1
- 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 +183 -20
- package/src/self-dev/decompose.ts +61 -0
- package/src/self-dev/dev-cli.ts +37 -3
- package/src/self-dev/dev-pipeline.ts +37 -9
- package/src/self-dev/draft-sweep.ts +241 -16
- package/src/self-dev/draft-triage-rules.ts +50 -5
- package/src/self-dev/glm-credential-guard.ts +77 -0
- package/src/self-dev/graph-journey-nodes.ts +1 -0
- package/src/self-dev/graph-journey-route.ts +12 -2
- package/src/self-dev/orchestrate-cli.ts +68 -3
- package/src/self-dev/orchestrate.ts +116 -17
- package/src/self-dev/review-provider-fallback.ts +45 -7
- package/src/self-dev/run-supervisor.ts +55 -2
- package/src/self-dev/run-types.ts +1 -1
- package/src/self-dev/shard-plan.ts +48 -0
- package/src/self-dev/shard-run.ts +50 -3
- package/src/self-health/autoheal.ts +153 -0
- package/src/self-health/signatures.ts +6 -0
- package/src/self-implement/device-first.ts +116 -0
- package/src/self-implement/doctor-visit.ts +322 -1
- package/src/self-implement/evidence-command-budget.ts +80 -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-cli.ts +1 -0
- package/src/self-implement/goal-author-decompose.ts +265 -4
- package/src/self-implement/goal-author.ts +366 -26
- package/src/self-implement/goal-elaborate.ts +176 -22
- package/src/self-implement/goal-refine.ts +3 -0
- package/src/self-implement/goal-supervisor-fill.ts +75 -26
- package/src/self-implement/graph-variant.ts +40 -6
- package/src/self-implement/headless-elanous-driver.ts +19 -3
- package/src/self-implement/importer-test-index.ts +215 -2
- package/src/self-implement/knowhow-pack.ts +5 -5
- package/src/self-implement/off-diff-evidence.ts +58 -6
- package/src/self-implement/orchestrator.ts +536 -42
- package/src/self-implement/pod-warm-pool-profile.ts +1 -0
- package/src/self-implement/pwa-import-graph.ts +1 -1
- package/src/self-implement/review-round-brief.ts +300 -0
- package/src/self-implement/rework-policy.ts +44 -0
- package/src/self-implement/run-ledger.ts +278 -6
- package/src/self-implement/run-stop.ts +1 -1
- package/src/self-implement/run-ttl.ts +38 -24
- package/src/self-implement/running-runs.ts +17 -3
- package/src/self-implement/scope-adjacency.ts +3 -1
- package/src/self-implement/seams.ts +72 -10
- 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/self-implement/wiring-check.ts +358 -0
- 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/shell-env-bootstrap.ts +11 -4
- package/src/skills/runner.ts +4 -2
- package/src/skills/tools/agent.ts +290 -48
- package/src/skills/tools/pty.ts +5 -0
- package/src/skills/tools/schedule-wakeup.ts +23 -4
- package/src/skills/tools/shell.ts +61 -6
- package/src/skills/tools/terminal-modal-inject.ts +1 -2
- package/src/skills/tools/terminal-modal.ts +10 -8
- package/src/skills/url-router.ts +41 -1
- package/src/smart-wizard/run.ts +66 -0
- package/src/status/bar.ts +34 -6
- package/src/status/metrics.ts +12 -1
- package/src/status/status-provider-info.ts +37 -6
- package/src/steward/rejudge-tick.ts +22 -7
- package/src/steward/resync-tick.ts +31 -4
- package/src/steward/ta-repair-event-collect.ts +17 -6
- package/src/task-agent/actions.ts +202 -5
- 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/land-queue.ts +21 -8
- package/src/task-agent/live-moves.ts +70 -9
- 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/mission.ts +30 -0
- package/src/task-agent/parent-host.ts +21 -11
- package/src/task-agent/rejudge-on-head.ts +87 -5
- package/src/task-agent/repair-cycle.ts +179 -32
- package/src/task-agent/repair-next-move.ts +42 -4
- package/src/task-agent/repair-outcomes.ts +29 -0
- package/src/task-agent/repair-worker.ts +14 -4
- package/src/task-agent/review-prior-fix.ts +12 -2
- package/src/task-agent/shadow.ts +229 -8
- package/src/task-agent/task-card-prefix.ts +2 -0
- package/src/task-agent/task-hand.ts +630 -30
- package/src/task-agent/task-parent-role-llm.ts +45 -0
- package/src/task-agent/tier-split.ts +80 -10
- 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-account-broker.ts +9 -0
- package/src/task-orchestrator/surfaces/pod-artifact-return.ts +16 -6
- package/src/task-orchestrator/surfaces/pod-child-reuse.ts +146 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +86 -5
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +215 -3
- package/src/task-orchestrator/surfaces/pod-launch-history.ts +133 -0
- 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-manifest-test-normalize.ts +11 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +126 -8
- package/src/task-orchestrator/surfaces/pod-provenance.ts +8 -2
- package/src/task-orchestrator/surfaces/pod-repair-commit.ts +18 -3
- package/src/task-orchestrator/surfaces/pod-si-warm-entry.ts +16 -0
- package/src/task-orchestrator/surfaces/pod-si-warm-host.ts +224 -0
- package/src/task-orchestrator/surfaces/pod-si-warm-protocol.ts +201 -0
- package/src/task-orchestrator/surfaces/pod-si-warm-provider.ts +444 -0
- package/src/task-orchestrator/surfaces/pod-si-warm-runner.ts +387 -0
- package/src/task-orchestrator/surfaces/pod-si-warm-transport.ts +157 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-protocol.ts +4 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +491 -36
- package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +3 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +612 -54
- package/src/task-orchestrator/surfaces/self-implement.ts +46 -2
- package/src/testing/linux-env.ts +177 -0
- package/src/tool-runtime/index.ts +15 -2
- package/src/tool-runtime/mcp-proxy-bootstrap.ts +29 -0
- package/src/tty-foreground.ts +155 -0
- package/src/tui/chat/goal-interim.ts +177 -0
- package/src/tui/chat/log-view.ts +205 -18
- package/src/tui/chat/self-health-status.ts +232 -0
- package/src/tui/entry.ts +623 -80
- package/src/tui/input/at-picker.ts +55 -0
- package/src/tui/input/composer.ts +8 -1
- package/src/tui/slash/compact-command.ts +7 -0
- package/src/tui/slash/compact.ts +1 -7
- package/src/tui/slash/resume.ts +98 -0
- 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 +49 -5
- package/src/tui-client/next-dashboard-runtime.ts +8 -0
- package/src/tui-client/next-loop-command.ts +70 -0
- package/src/tui-client/next-wakeup.ts +191 -0
- package/src/tui.ts +37 -0
- package/src/ui/modal-adapter.ts +2 -2
- 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 +670 -0
- package/src/ux-sim/cli-config-audit-device.ts +121 -0
- package/src/ux-sim/dashboard-event-loop-stall-monitor-device.ts +238 -0
- package/src/ux-sim/event-monkey.ts +3 -2
- package/src/ux-sim/fake-bin.ts +45 -0
- package/src/ux-sim/fake-gh.ts +174 -0
- package/src/ux-sim/fake-herdr.ts +238 -0
- package/src/ux-sim/fake-kubectl.ts +142 -0
- package/src/ux-sim/fake-llm.ts +116 -6
- package/src/ux-sim/fault-glm-no-credential.ts +116 -0
- package/src/ux-sim/fault-nextmove-dispatch.ts +102 -1
- package/src/ux-sim/fault-scope-escape-lift.ts +33 -6
- package/src/ux-sim/fault-tui-400-autoheal.ts +297 -0
- package/src/ux-sim/goal-device.ts +619 -0
- package/src/ux-sim/in-process-cli.ts +22 -5
- package/src/ux-sim/mock-authoring-first-run-child.ts +35 -0
- package/src/ux-sim/mock-authoring-first-run.ts +97 -0
- package/src/ux-sim/mock-seat-pty-frame-child.ts +29 -0
- package/src/ux-sim/mock-seat-pty-frame.ts +297 -0
- package/src/ux-sim/mock-seat-pty-sequence-child.ts +21 -0
- package/src/ux-sim/mock-seat-pty-sequence.ts +206 -0
- package/src/ux-sim/next-tui-seat-scenarios.ts +749 -0
- package/src/ux-sim/onboarding-first-run-device.ts +269 -0
- package/src/ux-sim/pty-key-modifiers-device.ts +85 -0
- 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/subagent-tools-device.ts +81 -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/ux-sim/task-orchestrator-surfaces-self-implement-pod-device.ts +250 -0
- package/src/ux-sim/tty-foreground-steal-child.ts +21 -0
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/1540.af38731fa7c4a3e0.js +0 -41
- package/apps/pwa/out/_next/static/chunks/1674.2a65fd5668e1cc5f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1850.f2b470ba5a471da0.js +0 -24
- package/apps/pwa/out/_next/static/chunks/193.62da94cd0b88323b.js +0 -74
- package/apps/pwa/out/_next/static/chunks/1975.260f6d249fac5f7c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1982-67cd8dfe8682325f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1993-c0007b7ad0c0ef41.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1999-bb415351c8a2846c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1c735de4.f993a30bbd3c57c4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2085.8f950b4bda1cbbc7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2159-6179a961376956b9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/216.b5c2a421aa1d28a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2251.49fbe369f74c372c.js +0 -148
- package/apps/pwa/out/_next/static/chunks/2286-2939577360d3dcc0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2625.5d870aa999f33db1.js +0 -34
- package/apps/pwa/out/_next/static/chunks/2668.34d21539e62bf111.js +0 -77
- package/apps/pwa/out/_next/static/chunks/2930.d0edb4f108787ef7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2973.77318e7d85f59477.js +0 -62
- package/apps/pwa/out/_next/static/chunks/3103.0df8da390948bb84.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3123.2bee9ba15d320bd2.js +0 -90
- package/apps/pwa/out/_next/static/chunks/3162-6cf652bf7124d8cf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3577-7340009f7007a795.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3589.5444a06bcf6e775c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3618.f514792d6c59d7fd.js +0 -36
- package/apps/pwa/out/_next/static/chunks/3669.343f9119aa276001.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3677-4215f826f6aa68e5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3736.c2efad489604cc3b.js +0 -79
- package/apps/pwa/out/_next/static/chunks/3817.f9aeab2bb6675d73.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4182-0b9b1251c29cc0cf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4346.8601952e73bf5803.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4582.bd68683c076649e3.js +0 -286
- package/apps/pwa/out/_next/static/chunks/4605-6e623f55aec77422.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4607.5cc27468628eb4bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4627.f07508b3936fd6c0.js +0 -24
- package/apps/pwa/out/_next/static/chunks/4747-c22262c093e46807.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4775-178215aed0daba61.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4815-6ffb8c3c991cc45d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4834-beca8b47ad29a898.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5023-fb4802b6d2492b02.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5154.a118f3957ed14e36.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5210-a2e0015e3d4d5d9c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5356.84ac712cc15928bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5401.22352141b6fd336e.js +0 -23
- package/apps/pwa/out/_next/static/chunks/5637.281e5dec4ea6ecf1.js +0 -94
- package/apps/pwa/out/_next/static/chunks/5662.02eaa029af05ad46.js +0 -1
- package/apps/pwa/out/_next/static/chunks/56d372f2.681ab4ec4c998fae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5760.22456f433c6f42c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5852.db1f7449f4f74612.js +0 -287
- package/apps/pwa/out/_next/static/chunks/5891-0f11a7d7362a9072.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5943.264d14a44ca32513.js +0 -1
- package/apps/pwa/out/_next/static/chunks/604-1b3c39df083c02aa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6121-5f97072330738caf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6193.07d8ca5af4b09086.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6203-b3fea3a60d69ff14.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6286.d638fb5953e2e757.js +0 -8
- package/apps/pwa/out/_next/static/chunks/630.fe5a46428d63b013.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6301.ae0245f507e4395e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6673.fbbe5699f6ea5991.js +0 -27
- package/apps/pwa/out/_next/static/chunks/6777-f6d301efb5de828d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6786.423f24c88de034c7.js +0 -93
- package/apps/pwa/out/_next/static/chunks/6854-cef64a948c5e122d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6b89c8bd.24df687a820500b3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7037-fe27f001db30d323.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7080.2d826860534fd88e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7118.8731be28c3ebb14b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7196.44241bc1df3e26fe.js +0 -149
- package/apps/pwa/out/_next/static/chunks/7235.c7c4e6cb5c693c2d.js +0 -104
- package/apps/pwa/out/_next/static/chunks/7706-58bfb1546637f217.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7839.5b91455338c9fd3f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7857-31f13ee96f4011aa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/81.f74cb60312fe4001.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8219.baa715d1deee80e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8256.e3ac8421a71f82f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8310-444a6a895c402f12.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8423.f7dda8aca4fcaae5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8437-8d5492094b166cfa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8496.75649af8652c74f4.js +0 -226
- package/apps/pwa/out/_next/static/chunks/8576.65bb654b524f3614.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8609.558ce2f8f9aea2be.js +0 -64
- package/apps/pwa/out/_next/static/chunks/8614.caec96cfb91f4ae7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8637.47f5b35afcae5e6b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8714.ff0b7ce3ec3e3ae2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8732.367f44a916202353.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9141-7fd1f73a5400bdc2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/921.c4267d0d69a65404.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9261.3962c3321b047691.js +0 -115
- package/apps/pwa/out/_next/static/chunks/936-26efd1a0efdd44d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9627-4b9efe616b27e2e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/967-2aa39b2d04b4c690.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9734.40eb1e558c29ccea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9908.3aa54f7a586a73dc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9954-ca8d29752bdbb45a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9974-507fa67cb94066dd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-ce9e00f7fb45506a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-fa59d6d17daa3389.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-53f68bbe7bd4b774.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-1c500fbb9f4a7a04.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-99d72673aa5c307c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-d2d5adc1e9484c88.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-c607c5aae637f1cc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-fa8c23596d555c0f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-bdab8c5e855c8416.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-406b47745b53f269.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-a4276386b7051f50.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-8a26bab9ad375f25.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-775f48aa1dc7a2d5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-1c4dd5f959d0c558.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-c48938a8377c9f70.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-ce9e00f7fb45506a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-c6e7905859425aa9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-c280f4252452e830.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-cf8c57498f20970a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-ca3a17caf409fc83.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-26e6c18a1a750cf6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-3abc872cc6aeee39.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-f7481b7b2174fe3d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1ae7ef8077680337.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-415f1e243c7ccb13.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-73f380f17b76f95c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-2991073ad66ee2d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fbad60d1e358fb2c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-398047d6949e8ca5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-2d33a2231540ca89.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-5bf1e6bf70bcb96c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-ba59c19376a0917d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-111f187139add30f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-acaae197d5be5779.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0df20b0f546a0d7d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-ce9e00f7fb45506a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-27e1202d24b8fe35.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-bda78c50a78363e5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-da592691293d9c9f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f832994317a2e9f0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-cfccbbeae0473404.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-527e2073af84c81b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-bac86e9112671357.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6d59dff410e9d76a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-53a61f8be09f02d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-db8f559e763c17f9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c02aa4cc66455093.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-50e6acebc630849b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-7a1ae342d5ed75e6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-7ad7d505ba1f92b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-02edba75232cb235.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-97d6ebae3e8eee4a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-e986adaa429163e8.js +0 -1
- /package/apps/pwa/out/_next/static/{T1mBQqmkwKDLGtyisDCOd → 8kITumG9GAZ1LwT9POrAR}/_ssgManifest.js +0 -0
|
@@ -12,6 +12,7 @@ import { getElanousConfigDirOverride } from '../../src/elanous-config-dir.js';
|
|
|
12
12
|
import { diffFailures, junitFailures, parseFailures, parseTimedOutFailures } from './gate-diff';
|
|
13
13
|
import { loadEnvKnownFailures, splitKnownEnv } from './env-known-failures';
|
|
14
14
|
import { loadGateBatchSafe } from './gate-batch-safe';
|
|
15
|
+
import { gateHistory } from './gate-history.js';
|
|
15
16
|
import { baselineFromCutLogs } from './baseline-from-logs';
|
|
16
17
|
import { mergePartialFailures, readPartialPlanResult, testFileOfId, verifyDocsOnlyCarry } from './gate-partial';
|
|
17
18
|
import { planGateRadiusShadow, shadowActual, writeGateRadiusShadow, type GateRadiusShadowPlan } from './gate-radius-shadow';
|
|
@@ -80,7 +81,9 @@ interface CommandResult { rc: number; output: string; passedIds?: string[]; stal
|
|
|
80
81
|
/** GATE-POD-STALL-FAILFAST ① — shard Pods whose tests the mid-run watch aborted in this sweep. */
|
|
81
82
|
abortedShards?: number;
|
|
82
83
|
/** Each contributing Pod leaf already passed failuresOf; repeated printed ids cannot add new failures. */
|
|
83
|
-
validatedShards?: boolean
|
|
84
|
+
validatedShards?: boolean
|
|
85
|
+
/** GATE-RISK-LANE — the watch-list shard this sweep ran first, with its plan and measurement (observation only). */
|
|
86
|
+
riskLane?: GateRiskLaneRecord }
|
|
84
87
|
function redactedGateLogTail(input: string): string {
|
|
85
88
|
// Redact before trimming: a truncated token might no longer match the redaction rule.
|
|
86
89
|
const bytes = Buffer.from(redactSecretText(input), 'utf8');
|
|
@@ -88,6 +91,25 @@ function redactedGateLogTail(input: string): string {
|
|
|
88
91
|
while (start < bytes.length && (bytes[start]! & 0xc0) === 0x80) start++;
|
|
89
92
|
return bytes.subarray(start).toString('utf8');
|
|
90
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* GATE-FLOW-SIM — the gate's outside world as one seam: a scenario hands in the repo's fake clock
|
|
96
|
+
* (`src/ux-sim/fake-clock.ts`) plus fake Pod/kubectl/ssh tools, and the production entry hands in nothing.
|
|
97
|
+
* Every field falls back to the real tool, so an absent seam is today's behaviour byte for byte.
|
|
98
|
+
*/
|
|
99
|
+
export interface GateTools {
|
|
100
|
+
/** Wall clock the gate measures with; absent = `Date.now`. */
|
|
101
|
+
now?: () => number;
|
|
102
|
+
/** The Pod Job runner (the fake Pod stage of a scenario); absent = the positional `podCommand`, else `runPodCommand`. */
|
|
103
|
+
podCommand?: (options: RunPodCommandOptions) => Promise<PodCommandResult>;
|
|
104
|
+
/** Kubernetes client (pool readiness · Pod lifecycle · Job log tail · failure evidence); absent = `defaultKubectl`. */
|
|
105
|
+
kubectl?: typeof defaultKubectl;
|
|
106
|
+
/** SSH transport behind {@link GateRunner.command} on a remote host; absent = a real `ssh` child. */
|
|
107
|
+
ssh?: (host: string, script: string, limitMs?: number) => { rc: number; output: string; timedOut?: boolean };
|
|
108
|
+
/** SSH reachability probe of a multi-host roster; absent = `defaultRemoteRun`. */
|
|
109
|
+
remoteRun?: typeof defaultRemoteRun;
|
|
110
|
+
/** Pool admission a shard waits on (leases ⊕ a cluster reading); absent = the gate's own pool admission. */
|
|
111
|
+
admission?: GateAdmission;
|
|
112
|
+
}
|
|
91
113
|
export interface GateRunner {
|
|
92
114
|
command(cmd: string, args: string[], cwd: string, limitMs?: number): Promise<CommandResult>;
|
|
93
115
|
localCommand(cmd: string, args: string[], cwd: string, limitMs?: number): Promise<CommandResult>;
|
|
@@ -103,6 +125,8 @@ export interface GateRunner {
|
|
|
103
125
|
removeSnapshot(tree: string): Promise<void>;
|
|
104
126
|
/** Absolute remote bare mirror path, set before preparing the remote trees. */
|
|
105
127
|
remoteMirror?: string;
|
|
128
|
+
/** GATE-FLOW-SIM — the clock this runner measures with (the injected fake clock in a scenario); absent = `Date.now`. */
|
|
129
|
+
now?(): number;
|
|
106
130
|
}
|
|
107
131
|
export interface GateOptions {
|
|
108
132
|
commit: string;
|
|
@@ -130,6 +154,8 @@ export interface GateOptions {
|
|
|
130
154
|
/** Opt-in gate shadow: provider fills the fenced lease identity. The Job remains authoritative. */
|
|
131
155
|
warmReplay?: (input: Omit<WarmWorkerCommand, 'leaseId' | 'generation' | 'workerId' | 'key'>) => Promise<{ command: WarmWorkerCommand | null; transport: WarmPodTransportResult }>;
|
|
132
156
|
warmQuarantine?: (command: WarmWorkerCommand, reason: string) => Promise<void>;
|
|
157
|
+
/** Explicit gate-profile producer handoff; shadow-only callers keep their Job path. */
|
|
158
|
+
warmProducerEnabled?: boolean;
|
|
133
159
|
warmFinalize?: (command: WarmWorkerCommand) => Promise<void>;
|
|
134
160
|
/** Set only on the cut recheck; baseline Jobs never consume the cut worker. */
|
|
135
161
|
warmCutSha?: string;
|
|
@@ -185,6 +211,8 @@ export interface GateOptions {
|
|
|
185
211
|
abortTests?: (job: { name: string; context?: string }, reason: string) => boolean | void;
|
|
186
212
|
/** GATE-LIVE-OBS — the shard state file (`gateShardsPath(version)`) this sweep keeps current; absent = not written. */
|
|
187
213
|
shardsFile?: { path: string; version: string };
|
|
214
|
+
/** GATE-RISK-LANE — the ledgers `gateHistory` reads its watch list from; absent = no risk lane (the sweep plans as before). */
|
|
215
|
+
riskWatch?: { instanceRoots: readonly string[]; ledgerRoots: readonly string[] };
|
|
188
216
|
/** GATE-MULTI-HOST — is this pool member reachable (kubectl ns ⊕ ssh)? Injected by tests; absent = the real probe on
|
|
189
217
|
* the gate's own pool · an injected pool without it never marks a member down (unchanged). */
|
|
190
218
|
probeMember?: (member: PodPoolMember) => boolean;
|
|
@@ -202,7 +230,7 @@ export function withWarmGateHooks(opts: GateOptions, config: unknown, injectedRu
|
|
|
202
230
|
&& config.pod && typeof config.pod === 'object' && 'warmReuse' in config.pod
|
|
203
231
|
? config.pod.warmReuse as WarmReuseSwitchConfig : undefined;
|
|
204
232
|
return { ...opts, pod: { ...opts.pod, warmReplay: requestWarmPodReplay,
|
|
205
|
-
warmFinalize: finalizeWarmPodReplay, warmQuarantine: quarantineWarmPodReplay, warmReuseConfig } };
|
|
233
|
+
warmFinalize: finalizeWarmPodReplay, warmQuarantine: quarantineWarmPodReplay, warmProducerEnabled: true, warmReuseConfig } };
|
|
206
234
|
}
|
|
207
235
|
export interface GateResult {
|
|
208
236
|
outcome: 'ok' | 'regression' | 'error';
|
|
@@ -531,6 +559,71 @@ export function shardParts(paths: readonly string[], seconds: (file: string) =>
|
|
|
531
559
|
if (current.length) parts.push(current);
|
|
532
560
|
return parts;
|
|
533
561
|
}
|
|
562
|
+
/** GATE-RISK-LANE — the watched files this sweep ran first, and how that shard was planned and measured. */
|
|
563
|
+
export interface GateRiskLaneRecord { files: string[]; plannedSeconds: number; durationMs: number; rc: 0 | 1 | null }
|
|
564
|
+
|
|
565
|
+
/** GATE-RISK-LANE — the shard plan plus the risk shard it may have placed first. */
|
|
566
|
+
export interface GateRiskLanePlan {
|
|
567
|
+
/** Execution order: the risk shard first (absent when nothing is watched), then the current plan unchanged. */
|
|
568
|
+
shards: Array<{ files: string[]; plannedSeconds: number }>;
|
|
569
|
+
/** Assignable files this plan treated as risk — exactly `shards[0].files`; empty means «the current plan». */
|
|
570
|
+
riskFiles: string[];
|
|
571
|
+
riskPlannedSeconds: number;
|
|
572
|
+
spawnFiles: string[];
|
|
573
|
+
spawnShardCount: number;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* GATE-RISK-LANE — the watch list the gate-history table already keeps (`gateHistory().watchList`: introduced or flaky
|
|
578
|
+
* in at least two of the last three releases). Reading it never blocks a gate: an unreadable ledger set is «no list».
|
|
579
|
+
*/
|
|
580
|
+
export function readGateRiskWatchList(o: { instanceRoots: readonly string[]; ledgerRoots: readonly string[] }): string[] {
|
|
581
|
+
try { return gateHistory({ instanceRoots: o.instanceRoots, ledgerRoots: o.ledgerRoots }).watchList; }
|
|
582
|
+
catch (error) {
|
|
583
|
+
debug.log('release-loop.gate', 'risk-lane-watch-unavailable', { reason: String(error) });
|
|
584
|
+
return [];
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* GATE-RISK-LANE (0.2.36) — the next full sweep runs the files the watch list names in its first shard, so a repeat
|
|
590
|
+
* regression breaks before the general shards spend their deadline. Only the order changes:
|
|
591
|
+
* - a watched heavy file already runs alone at the isolation limit, and a watched spawn file keeps its own lane (a part
|
|
592
|
+
* mixing real child/PTY files with regular ones is refused by {@link gateShardShell}) — neither joins the risk shard;
|
|
593
|
+
* - every assignable file is assigned exactly once: the risk files leave the lane they would have been balanced into;
|
|
594
|
+
* - with no watch list, or one that names no file of this sweep, the plan is today's plan byte for byte;
|
|
595
|
+
* - when the watch list hits, the sweep runs exactly one more shard than configured (the risk shard in front), which is
|
|
596
|
+
* the same trade the configuration already makes for spawning a Pool Job.
|
|
597
|
+
*/
|
|
598
|
+
export function planGateRiskLaneShards(o: { assignable: readonly string[]; durations: Map<string, number>; shardCount: number;
|
|
599
|
+
heavy: ReadonlySet<string>; watchList: readonly string[] }): GateRiskLanePlan {
|
|
600
|
+
const light = o.assignable.filter((file) => !o.heavy.has(file));
|
|
601
|
+
const watched = new Set(o.watchList);
|
|
602
|
+
const riskFiles = [...new Set(light)].filter((file) => watched.has(file) && !isGateSpawnLaneFile(file)).sort();
|
|
603
|
+
const heavyShards = [...o.heavy].map((file) => ({ files: [file], plannedSeconds: o.durations.get(file) ?? 0 }));
|
|
604
|
+
const planWith = (files: readonly string[]) => planGateSpawnSeparatedShards(files, o.durations, o.shardCount);
|
|
605
|
+
if (!riskFiles.length) {
|
|
606
|
+
const current = planWith(light);
|
|
607
|
+
return { shards: [...current.shards, ...heavyShards], riskFiles: [], riskPlannedSeconds: 0,
|
|
608
|
+
spawnFiles: current.spawnFiles, spawnShardCount: current.spawnShardCount };
|
|
609
|
+
}
|
|
610
|
+
const risk = new Set(riskFiles);
|
|
611
|
+
const rest = planWith(light.filter((file) => !risk.has(file)));
|
|
612
|
+
// An unmeasured file takes the same median weight planShards gives it, so the shard's plan stays comparable.
|
|
613
|
+
const known = o.assignable.map((file) => o.durations.get(file))
|
|
614
|
+
.filter((value): value is number => value !== undefined && Number.isFinite(value) && value >= 0).sort((a, b) => a - b);
|
|
615
|
+
const median = known.length ? (known[Math.floor((known.length - 1) / 2)]! + known[Math.floor(known.length / 2)]!) / 2 : 1;
|
|
616
|
+
const plannedSeconds = riskFiles.reduce((sum, file) => {
|
|
617
|
+
const value = o.durations.get(file);
|
|
618
|
+
return sum + (value !== undefined && Number.isFinite(value) && value >= 0 ? value : median);
|
|
619
|
+
}, 0);
|
|
620
|
+
return {
|
|
621
|
+
shards: [{ files: [...riskFiles], plannedSeconds }, ...rest.shards, ...heavyShards],
|
|
622
|
+
riskFiles: [...riskFiles], riskPlannedSeconds: plannedSeconds,
|
|
623
|
+
spawnFiles: rest.spawnFiles, spawnShardCount: rest.spawnShardCount,
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
|
|
534
627
|
/**
|
|
535
628
|
* GATE-ENV-BASELINE (W5 · 10-15) — the gate shard Pod's deterministic test env, exported before anything runs.
|
|
536
629
|
* 0.2.29 gate failures were the Pod's «environment», not the code: no LLM provider · `man` stubbed · CLI spawn timeouts.
|
|
@@ -554,7 +647,8 @@ export function gateEnvBaselineLine(env: Readonly<Record<string, string>> = gate
|
|
|
554
647
|
* `shard.timeout` so the artifact reader does not mistake an unfinished child for a test verdict.
|
|
555
648
|
* A part without `.rc` did not finish. The shell itself always exits 0 (as before).
|
|
556
649
|
*/
|
|
557
|
-
export function gateShardShell(o: { parts: readonly (readonly string[])[]; cdpPatterns: readonly string[]; softSeconds: number; cachePrefix: string; installSlots: number; depsDir?: string; beatSeconds?: number; parallelParts?: number; testTimeoutMs?: number; partLimitSeconds?: number; sourceBinding?: { cutSha: string; attemptId: string } }): string {
|
|
650
|
+
export function gateShardShell(o: { parts: readonly (readonly string[])[]; cdpPatterns: readonly string[]; softSeconds: number; cachePrefix: string; installSlots: number; depsDir?: string; beatSeconds?: number; parallelParts?: number; testTimeoutMs?: number; partLimitSeconds?: number; rulesBaseSha?: string; sourceBinding?: { cutSha: string; attemptId: string } }): string {
|
|
651
|
+
if (o.rulesBaseSha !== undefined && !/^[0-9a-f]{40}$/.test(o.rulesBaseSha)) throw new Error('invalid rules gate base SHA');
|
|
558
652
|
// The planner keeps spawn files in their own shards; reject a mixed part rather than silently lose its limit.
|
|
559
653
|
for (const part of o.parts) {
|
|
560
654
|
if (part.some(isGateSpawnLaneFile) && !part.every(isGateSpawnLaneFile)) throw new Error('gate spawn lane part mixed with regular tests');
|
|
@@ -597,6 +691,7 @@ export function gateShardShell(o: { parts: readonly (readonly string[])[]; cdpPa
|
|
|
597
691
|
...(o.beatSeconds ? gateBeatScript(o.beatSeconds, o.installSlots > 0) : []),
|
|
598
692
|
...(o.cachePrefix ? [o.cachePrefix] : []),
|
|
599
693
|
gateEnvBaselineLine(),
|
|
694
|
+
...(o.rulesBaseSha ? [`export ELANOUS_GATE_BASE_SHA=${quote(o.rulesBaseSha)}`] : []),
|
|
600
695
|
// Seconds since this container's PID 1 started (the clone ran before this shell) — 0 when /proc cannot tell.
|
|
601
696
|
`pod_age=$(awk -v t="$(cut -d' ' -f22 /proc/1/stat 2>/dev/null)" -v hz="$(getconf CLK_TCK 2>/dev/null)" '{ if (t > 0 && hz > 0) print int($1 - t / hz); else print 0 }' /proc/uptime 2>/dev/null)`,
|
|
602
697
|
'case "$pod_age" in ""|*[!0-9]*) pod_age=0;; esac',
|
|
@@ -1267,9 +1362,9 @@ export const gateAdmissionWaitText = (reading: string | null) => `메모리 여
|
|
|
1267
1362
|
* the shard runs again on the others — one dead host no longer stops the gate. One-member pools keep the old behaviour.
|
|
1268
1363
|
*/
|
|
1269
1364
|
export const GATE_MEMBER_UNREADABLE_SECONDS_DEFAULT = 600;
|
|
1270
|
-
export function defaultGateMemberProbe(member: PodPoolMember): boolean {
|
|
1271
|
-
if (!checkPodPool([member], defaultKubectl).ok) return false;
|
|
1272
|
-
return !member.sshHost || defaultRemoteRun(member.sshHost, 'true').status === 0;
|
|
1365
|
+
export function defaultGateMemberProbe(member: PodPoolMember, tools: GateTools = {}): boolean {
|
|
1366
|
+
if (!checkPodPool([member], tools.kubectl ?? defaultKubectl).ok) return false;
|
|
1367
|
+
return !member.sshHost || (tools.remoteRun ?? defaultRemoteRun)(member.sshHost, 'true').status === 0;
|
|
1273
1368
|
}
|
|
1274
1369
|
|
|
1275
1370
|
export function poolGateAdmission(pool: PodPoolScheduler): GateAdmission {
|
|
@@ -1303,8 +1398,27 @@ export async function admitGateShard(admission: GateAdmission | undefined, o: {
|
|
|
1303
1398
|
}
|
|
1304
1399
|
}
|
|
1305
1400
|
|
|
1306
|
-
|
|
1401
|
+
/**
|
|
1402
|
+
* The real `ssh` transport behind {@link GateRunner.command} on a remote host. A scenario replaces it through
|
|
1403
|
+
* {@link GateTools.ssh} so the gate flow can run without any remote call.
|
|
1404
|
+
*/
|
|
1405
|
+
export function defaultSshCommand(host: string, script: string, limitMs?: number): { rc: number; output: string; timedOut?: boolean } {
|
|
1406
|
+
// Start in /tmp, not the caller checkout. macOS resolves that symlink to /private/tmp before the child sees it;
|
|
1407
|
+
// the remote command still `cd`s to the quoted path, so either spelling is the same directory.
|
|
1408
|
+
const run = spawnSync('ssh', [host, script],
|
|
1409
|
+
{ cwd: '/tmp', encoding: 'utf8', maxBuffer: 128 * 1024 * 1024, ...(limitMs ? { timeout: limitMs + 60_000, killSignal: 'SIGKILL' as const } : {}) });
|
|
1410
|
+
const timedOut = !!limitMs && (run.status === 124 || run.status === 137 || (run.error as NodeJS.ErrnoException | undefined)?.code === 'ETIMEDOUT');
|
|
1411
|
+
return { rc: run.status ?? 2, output: `${run.stdout ?? ''}\n${run.stderr ?? ''}${run.error ? `\n${run.error}` : ''}`, ...(timedOut ? { timedOut } : {}) };
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
export function createGateRunner(repo: string, remote?: string, commandOverride?: GateRunner['command'], podCommand: (options: RunPodCommandOptions) => Promise<PodCommandResult> = runPodCommand, poolOverride?: PodPoolScheduler, podLogTail?: (job: PodCommandResult) => string, tools: GateTools = {}): GateRunner {
|
|
1307
1415
|
let remoteMirror: string | undefined;
|
|
1416
|
+
// GATE-FLOW-SIM: the one place the real outside world is named — an injected seam replaces it, absent = today's tools.
|
|
1417
|
+
const clock = tools.now ?? Date.now;
|
|
1418
|
+
const kubectl = tools.kubectl ?? defaultKubectl;
|
|
1419
|
+
const ssh = tools.ssh ?? defaultSshCommand;
|
|
1420
|
+
const remoteRun = tools.remoteRun ?? defaultRemoteRun;
|
|
1421
|
+
const runPod = tools.podCommand ?? podCommand;
|
|
1308
1422
|
// GATE-HOSTMIRROR-EXPLICIT: resolved once per runner and handed to every podCommand (shard · retry · recheck).
|
|
1309
1423
|
let gateMirror: GateHostMirror | undefined;
|
|
1310
1424
|
const gateHostMirror = (pod: NonNullable<GateOptions['pod']>): { hostMirror?: string } => {
|
|
@@ -1325,14 +1439,19 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1325
1439
|
// GNU timeout signals its own process group: -s KILL would kill timeout too (ssh 255), so TERM first then KILL after 10s (124 / 137).
|
|
1326
1440
|
// No coreutils timeout on the remote → run unlimited there and rely on the local ssh deadline.
|
|
1327
1441
|
const limitPrefix = limitMs ? `$(command -v timeout >/dev/null 2>&1 && echo 'timeout -k 10 ${Math.ceil(limitMs / 1000)}') ` : '';
|
|
1328
|
-
|
|
1329
|
-
// the remote command still `cd`s to the quoted path, so either spelling is the same directory.
|
|
1330
|
-
const run = spawnSync('ssh', [remote, `PATH=$HOME/.bun/bin:/opt/homebrew/bin:$PATH; export PATH; cd ${quote(cwd)} && ${limitPrefix}${[cmd, ...args].map(quote).join(' ')}`],
|
|
1331
|
-
{ cwd: '/tmp', encoding: 'utf8', maxBuffer: 128 * 1024 * 1024, ...(limitMs ? { timeout: limitMs + 60_000, killSignal: 'SIGKILL' as const } : {}) });
|
|
1332
|
-
const timedOut = !!limitMs && (run.status === 124 || run.status === 137 || (run.error as NodeJS.ErrnoException | undefined)?.code === 'ETIMEDOUT');
|
|
1333
|
-
return { rc: run.status ?? 2, output: `${run.stdout ?? ''}\n${run.stderr ?? ''}${run.error ? `\n${run.error}` : ''}`, ...(timedOut ? { timedOut } : {}) };
|
|
1442
|
+
return ssh(remote, `PATH=$HOME/.bun/bin:/opt/homebrew/bin:$PATH; export PATH; cd ${quote(cwd)} && ${limitPrefix}${[cmd, ...args].map(quote).join(' ')}`, limitMs);
|
|
1334
1443
|
}
|
|
1335
1444
|
: localCommand);
|
|
1445
|
+
const rulesGateBaseFor = async (commit: string, files: readonly string[]): Promise<string | undefined> => {
|
|
1446
|
+
if (!files.includes('test/rules-contract.test.ts')) return undefined;
|
|
1447
|
+
// The gate mirror only receives pinned commit refs, so its Pod checkout has no origin/main.
|
|
1448
|
+
// Resolve the same merge-base in the source repository and pass the exact commit to the Pod.
|
|
1449
|
+
const resolved = await localCommand('git', ['merge-base', commit, 'origin/main'], repo);
|
|
1450
|
+
if (resolved.rc !== 0) throw new Error(`rules gate base unavailable for ${commit.slice(0, 12)}: ${tail40(resolved.output)}`);
|
|
1451
|
+
const base = resolved.output.trim();
|
|
1452
|
+
if (!/^[0-9a-f]{40}$/.test(base)) throw new Error(`rules gate base invalid for ${commit.slice(0, 12)}: ${base}`);
|
|
1453
|
+
return base;
|
|
1454
|
+
};
|
|
1336
1455
|
const podSweep = async (tree: string, logDir: string | undefined, pod: NonNullable<GateOptions['pod']>, only?: readonly string[]): Promise<CommandResult> => {
|
|
1337
1456
|
const sweepId = randomUUID();
|
|
1338
1457
|
const shardCount = pod.shards ?? 24;
|
|
@@ -1346,7 +1465,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1346
1465
|
check(head, 'cut tree HEAD');
|
|
1347
1466
|
const commit = head.output.trim();
|
|
1348
1467
|
if (!/^[0-9a-f]{40}$/.test(commit)) throw new Error('invalid cut tree HEAD');
|
|
1349
|
-
const selectionStart =
|
|
1468
|
+
const selectionStart = clock();
|
|
1350
1469
|
const listed = await command('git', ['ls-files', '*.test.*'], tree);
|
|
1351
1470
|
check(listed, 'git ls-files tests');
|
|
1352
1471
|
const allFiles = listed.output.split(/\r?\n/).filter((file) => /\.test\.(?:tsx?|jsx?|mts|cts)$/.test(file)).sort();
|
|
@@ -1356,6 +1475,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1356
1475
|
const missing = only?.filter((file) => !allFiles.includes(file)) ?? [];
|
|
1357
1476
|
if (missing.length) throw new Error(`partial plan file missing at this commit: ${missing.slice(0, 5).join(', ')}`);
|
|
1358
1477
|
const files = onlySet ? allFiles.filter((file) => onlySet.has(file)) : allFiles;
|
|
1478
|
+
const rulesBaseSha = await rulesGateBaseFor(commit, files);
|
|
1359
1479
|
if (files.some((file) => file.startsWith('-') || file.startsWith('/') || file.startsWith('./')
|
|
1360
1480
|
|| file.split('/').some((part) => !part || part === '.' || part === '..') || /[\r\n]/.test(file))) throw new Error('unsafe pod test path');
|
|
1361
1481
|
if (new Set(files).size !== files.length) throw new Error('duplicate pod test path');
|
|
@@ -1375,12 +1495,14 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1375
1495
|
// The TD1 measurements predate the fixture-only gate; do not isolate those five files at their old 2–13 GB peaks.
|
|
1376
1496
|
const fixtureOnly = new Set([...GATE_NIGHTLY_AUDITS, 'test/guardian/dispatch-surface-contract.test.ts', 'test/user-config-mcp.test.ts']);
|
|
1377
1497
|
const heavy = new Set(assignable.filter((file) => !fixtureOnly.has(file) && (memory.get(file) ?? 0) >= POD_HEAVY_FILE_MB));
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
if (
|
|
1498
|
+
// GATE-RISK-LANE — the failure history this sweep already keeps (`gateHistory().watchList`) puts its watched files in
|
|
1499
|
+
// the first shard; heavy files stay solo and spawn files keep their own lane, so the rest of the plan is unchanged.
|
|
1500
|
+
const watchList = pod.riskWatch ? readGateRiskWatchList(pod.riskWatch) : [];
|
|
1501
|
+
const riskPlan = planGateRiskLaneShards({ assignable, durations, shardCount, heavy, watchList });
|
|
1502
|
+
const shards = riskPlan.shards;
|
|
1503
|
+
if (riskPlan.spawnFiles.length) debug.log('release-loop.gate', 'pod-spawn-lane', { files: riskPlan.spawnFiles, shards: riskPlan.spawnShardCount });
|
|
1504
|
+
if (riskPlan.riskFiles.length) debug.log('release-loop.gate', 'pod-risk-lane-plan', { files: riskPlan.riskFiles, shard: 0,
|
|
1505
|
+
plannedSeconds: riskPlan.riskPlannedSeconds, shards: shards.length, watch: watchList.length });
|
|
1384
1506
|
logPlanWalltime(planWall, shards);
|
|
1385
1507
|
if (heavy.size) debug.log('release-loop.gate', 'pod-heavy-alone', { files: [...heavy], thresholdMb: POD_HEAVY_FILE_MB, memoryLimit: POD_ISOLATION_MEMORY_LIMIT });
|
|
1386
1508
|
const known = assignable.filter((file) => durations.has(file)).length;
|
|
@@ -1389,14 +1511,14 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1389
1511
|
maxPlannedSeconds: Math.max(...shards.map((shard) => shard.plannedSeconds)),
|
|
1390
1512
|
source: durations.size ? pod.durationSource : 'none',
|
|
1391
1513
|
});
|
|
1392
|
-
reportSelectionTimed(
|
|
1514
|
+
reportSelectionTimed(clock() - selectionStart, assignable.length, pod.onSelectionTimed);
|
|
1393
1515
|
// GATE-SPEED A3①: shard Pod CPU (`release.loop.gatePodCpu` · default request 1 / limit 4) — shards × request must fit the pool.
|
|
1394
1516
|
const cpu = resolvePodCpu(pod.cpu);
|
|
1395
1517
|
// Only evidence-backed opt-in files reach this batch; every other file retains its own Pod below.
|
|
1396
1518
|
const batchWidth = Math.max(1, Math.min(4, Math.floor(podCpuMillis(cpu.limit)! / 1000)));
|
|
1397
1519
|
debug.log('release-loop.gate', 'shard-resources', { shards: shards.length, cpuRequest: cpu.request, cpuLimit: cpu.limit, cpuSource: pod.cpu ? 'config' : 'default' });
|
|
1398
1520
|
debug.log('release-loop.gate', 'isolation-batch-width', { batchWidth, cpuLimit: cpu.limit });
|
|
1399
|
-
const ready = poolOverride ? undefined : checkPodPool(parsePodPool(pod.pool),
|
|
1521
|
+
const ready = poolOverride ? undefined : checkPodPool(parsePodPool(pod.pool), kubectl);
|
|
1400
1522
|
if (ready && !ready.ok) throw new Error('pod command: 풀의 노드가 하나도 준비되지 않았다');
|
|
1401
1523
|
const poolScheduler = poolOverride ?? new PodPoolScheduler(ready!.ready);
|
|
1402
1524
|
const configCache = typeof pod.bunCache === 'string' ? pod.bunCache.trim() : '';
|
|
@@ -1412,7 +1534,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1412
1534
|
const admissionWaitSeconds = pod.admissionWaitSeconds ?? GATE_ADMISSION_WAIT_SECONDS_DEFAULT;
|
|
1413
1535
|
if (!Number.isSafeInteger(admissionWaitSeconds) || admissionWaitSeconds < 0) throw new Error('invalid pod sweep options');
|
|
1414
1536
|
// An injected pool (tests) runs without admission unless `pod.admission` is passed; the gate's own pool asks it.
|
|
1415
|
-
const gateAdmission = pod.admission ?? (poolOverride ? undefined : poolGateAdmission(poolScheduler));
|
|
1537
|
+
const gateAdmission = pod.admission ?? tools.admission ?? (poolOverride ? undefined : poolGateAdmission(poolScheduler));
|
|
1416
1538
|
debug.log('release-loop.gate', 'pod-admission', { waitSeconds: admissionWaitSeconds, source: pod.admissionWaitSeconds !== undefined ? 'config' : 'default', enabled: !!gateAdmission && admissionWaitSeconds > 0 });
|
|
1417
1539
|
// GATE-POD-STALL-FAILFAST: a shard with no test output within the stall window is stopped and recorded unmeasured.
|
|
1418
1540
|
const stallSeconds = pod.stallSeconds ?? GATE_POD_STALL_SECONDS_DEFAULT;
|
|
@@ -1421,14 +1543,14 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1421
1543
|
// An injected pool (tests) runs without the watch unless `pod.liveLog` is passed — never a real kubectl from a test.
|
|
1422
1544
|
const liveLog = pod.liveLog ?? (poolOverride ? undefined : (job: { name: string; context?: string }) => {
|
|
1423
1545
|
if (!job.context) return null;
|
|
1424
|
-
const logs =
|
|
1546
|
+
const logs = kubectl(['--context', job.context, '-n', 'elanous-test', 'logs', `job/${job.name}`, '-c', 'child', '--tail=400']);
|
|
1425
1547
|
return logs.status === 0 ? logs.stdout : null;
|
|
1426
1548
|
});
|
|
1427
1549
|
const stopJob = pod.stopJob ?? ((job: { name: string; context?: string }) => {
|
|
1428
1550
|
// Not `delete job`: runPodCommand would poll a missing Job forever. A 1 s deadline makes the Job fail now and
|
|
1429
1551
|
// runPodCommand returns through its normal path (logs · exit code · Secret cleanup).
|
|
1430
1552
|
if (!job.context) return false;
|
|
1431
|
-
const patched =
|
|
1553
|
+
const patched = kubectl(['--context', job.context, '-n', 'elanous-test', 'patch', 'job', job.name, '--type=merge', '-p', JSON.stringify({ spec: { activeDeadlineSeconds: 1 } })]);
|
|
1432
1554
|
if (patched.status !== 0) debug.log('release-loop.gate', 'pod-shard-stall-stop-failed', { job: job.name, error: patched.stderr.trim().slice(0, 300) }, { level: 'warn' });
|
|
1433
1555
|
return patched.status === 0;
|
|
1434
1556
|
});
|
|
@@ -1439,7 +1561,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1439
1561
|
if (!Number.isFinite(midRunStallSeconds) || midRunStallSeconds < 0) throw new Error('invalid pod sweep options');
|
|
1440
1562
|
const abortTests = pod.abortTests ?? (poolOverride ? undefined : (job: { name: string; context?: string }, reason: string) => {
|
|
1441
1563
|
if (!job.context) return false;
|
|
1442
|
-
const ran =
|
|
1564
|
+
const ran = kubectl(['--context', job.context, '-n', 'elanous-test', 'exec', `job/${job.name}`, '-c', 'child', '--', 'bash', '-c', gateShardAbortScript(reason)]);
|
|
1443
1565
|
if (ran.status !== 0) debug.log('release-loop.gate', 'shard-abort-failed', { job: job.name, rc: ran.status, out: `${ran.stdout}${ran.stderr}`.trim().slice(0, 300) }, { level: 'warn' });
|
|
1444
1566
|
return ran.status === 0;
|
|
1445
1567
|
});
|
|
@@ -1451,7 +1573,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1451
1573
|
const downMembers = new Set<string>();
|
|
1452
1574
|
let tailPlacedHost: string | undefined;
|
|
1453
1575
|
const multiHost = poolScheduler.members.length > 1;
|
|
1454
|
-
const probeMember = pod.probeMember ?? (poolOverride ? undefined : defaultGateMemberProbe);
|
|
1576
|
+
const probeMember = pod.probeMember ?? (poolOverride ? undefined : (member: PodPoolMember) => defaultGateMemberProbe(member, { kubectl, remoteRun }));
|
|
1455
1577
|
const memberUnreadableSeconds = pod.memberUnreadableSeconds ?? GATE_MEMBER_UNREADABLE_SECONDS_DEFAULT;
|
|
1456
1578
|
if (!Number.isFinite(memberUnreadableSeconds) || memberUnreadableSeconds < 0) throw new Error('invalid pod sweep options');
|
|
1457
1579
|
debug.log('release-loop.gate', 'pod-roster', { members: poolScheduler.members.map((m) => ({ context: m.context, host: m.sshHost ?? null, capacity: m.capacity })),
|
|
@@ -1472,7 +1594,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1472
1594
|
const jobLogTail = (job: PodCommandResult): string => {
|
|
1473
1595
|
if (podLogTail) return podLogTail(job);
|
|
1474
1596
|
for (const member of poolScheduler.members.filter((m) => !downMembers.has(m.context))) {
|
|
1475
|
-
const logs =
|
|
1597
|
+
const logs = kubectl(['--context', member.context, '-n', 'elanous-test', 'logs', `job/${job.job}`, '-c', 'child', '--tail=200']);
|
|
1476
1598
|
if (logs.status === 0 && logs.stdout) return logs.stdout;
|
|
1477
1599
|
}
|
|
1478
1600
|
return '';
|
|
@@ -1484,7 +1606,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1484
1606
|
let termination: { reason?: string; message?: string; exitCode?: number; finishedAt?: string } | undefined;
|
|
1485
1607
|
let podName: string | undefined;
|
|
1486
1608
|
let latestEndedAt = -Infinity;
|
|
1487
|
-
const status = (pod.failureKubectl ??
|
|
1609
|
+
const status = (pod.failureKubectl ?? kubectl)([...prefix, 'get', 'pods', '-l', `job-name=${job.name}`, '-o', 'json']);
|
|
1488
1610
|
if (status.status !== 0) errors.push(`Pod 상태: ${status.stderr.trim() || `kubectl rc=${status.status}`}`);
|
|
1489
1611
|
else try {
|
|
1490
1612
|
const pods = JSON.parse(status.stdout) as { items?: Array<{ metadata?: { name?: string; creationTimestamp?: string }; status?: { containerStatuses?: Array<{ name?: string; state?: { terminated?: typeof termination } }> } }> };
|
|
@@ -1498,7 +1620,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1498
1620
|
}
|
|
1499
1621
|
if (!termination) errors.push('Pod 상태: child 종료 사유 없음');
|
|
1500
1622
|
} catch (error) { errors.push(`Pod 상태 JSON: ${String(error)}`); }
|
|
1501
|
-
const logs = podName ? (pod.failureKubectl ??
|
|
1623
|
+
const logs = podName ? (pod.failureKubectl ?? kubectl)([...prefix, 'logs', `pod/${podName}`, '-c', 'child', '--tail=100']) : undefined;
|
|
1502
1624
|
if (!logs) errors.push('로그: 종료된 Pod 이름 없음');
|
|
1503
1625
|
else if (logs.status !== 0) errors.push(`로그: ${logs.stderr.trim() || `kubectl rc=${logs.status}`}`);
|
|
1504
1626
|
return { ...(podName ? { podName } : {}), ...(termination ? { termination } : {}), ...(logs?.status === 0 ? { logTail: logs.stdout } : {}),
|
|
@@ -1572,11 +1694,11 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1572
1694
|
const shardDeadline = shardDeadlineSeconds(batchIsolation ? Math.max(...paths.map((file) => estimated([file]))) * Math.ceil(paths.length / batchWidth) : estimated(paths), deadlineSeconds);
|
|
1573
1695
|
const parts = batchIsolation ? paths.map((file) => [file]) : shardParts(paths, (file) => estimated([file]));
|
|
1574
1696
|
const memoryLimit = batchIsolation || paths.length === 1 && (depth >= 1 || heavy.has(paths[0]!)) ? POD_ISOLATION_MEMORY_LIMIT : undefined;
|
|
1575
|
-
const shellOptions = { parts, cdpPatterns, softSeconds: softDeadlineSeconds(shardDeadline), cachePrefix, installSlots, ...(batchIsolation ? { parallelParts: batchWidth } : {}) };
|
|
1697
|
+
const shellOptions = { parts, cdpPatterns, softSeconds: softDeadlineSeconds(shardDeadline), cachePrefix, installSlots, ...(rulesBaseSha ? { rulesBaseSha } : {}), ...(batchIsolation ? { parallelParts: batchWidth } : {}) };
|
|
1576
1698
|
const keyShell = gateShardShell(shellOptions);
|
|
1577
1699
|
// The stall beat only reports; runKey keeps hashing the shell without it, so earlier results stay reusable.
|
|
1578
1700
|
const runKey = createHash('sha256').update(JSON.stringify({ pool: pod.pool, podShell: keyShell, memoryLimit: memoryLimit ?? null, deadlineSeconds: shardDeadline, bunCache: bunCache ?? null })).digest('hex');
|
|
1579
|
-
let start =
|
|
1701
|
+
let start = clock();
|
|
1580
1702
|
let admitted: GateAdmissionResult | undefined;
|
|
1581
1703
|
let output: string | undefined;
|
|
1582
1704
|
const phaseTimes: Record<string, string> = {};
|
|
@@ -1648,7 +1770,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1648
1770
|
if (admitted.outcome !== 'off') debug.log('release-loop.gate', 'shard-admission', { shard, branch, outcome: admitted.outcome, waitedMs: admitted.waitedMs, ...(admitted.reason ? { reason: admitted.reason } : {}) },
|
|
1649
1771
|
admitted.outcome === 'granted' ? undefined : { level: 'warn' });
|
|
1650
1772
|
// The admission wait is not the shard's run time: deadline/timeout judgment and the measured duration start here.
|
|
1651
|
-
start =
|
|
1773
|
+
start = clock();
|
|
1652
1774
|
const jobName = `gate-${randomUUID()}`;
|
|
1653
1775
|
jobNameForCapture = jobName;
|
|
1654
1776
|
// The member's context is known once the Job is placed; the live log is read (and the Job stopped) there.
|
|
@@ -1671,7 +1793,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1671
1793
|
},
|
|
1672
1794
|
} : {}) }) : undefined;
|
|
1673
1795
|
const placed = admitted.release;
|
|
1674
|
-
const job = await
|
|
1796
|
+
const job = await runPod({
|
|
1675
1797
|
provenanceGate: true,
|
|
1676
1798
|
provenance: { actorKind: process.env.ELANOUS_SEAT ? 'seat' : 'loop', actor: process.env.ELANOUS_SEAT || 'release-loop',
|
|
1677
1799
|
character: 'gate-shard-runner', launchHost: hostname(), entrance: process.env.ELANOUS_RELEASE_ENTRANCE?.trim() || 'release-run',
|
|
@@ -1700,7 +1822,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1700
1822
|
// Production pool sweeps always use this Kubernetes lookup; lookup failure is receipt-invalid only.
|
|
1701
1823
|
if (!poolOverride && placedOn) {
|
|
1702
1824
|
try {
|
|
1703
|
-
const got =
|
|
1825
|
+
const got = kubectl(['--context', placedOn, '-n', 'elanous-test', 'get', 'pods', '-l', `job-name=${job.job}`, '-o', 'json']);
|
|
1704
1826
|
if (got.status === 0) {
|
|
1705
1827
|
podLifecycle = readGatePodLifecycle(got.stdout);
|
|
1706
1828
|
imageDigest = actualGatePodImageDigest(got.stdout);
|
|
@@ -1733,7 +1855,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1733
1855
|
if (abort?.taken) abortedShards++;
|
|
1734
1856
|
if (stalledAt && !finished.length && rc === undefined) stall = { seconds: stallSeconds, sinceCheckoutMs: stalledAt.sinceCheckoutMs };
|
|
1735
1857
|
// A Job the cluster cut at its own deadline leaves no outbox at all: past the deadline with nothing is a timeout too.
|
|
1736
|
-
if (!timedOut && output === undefined && job.exitCode !== 0 &&
|
|
1858
|
+
if (!timedOut && output === undefined && job.exitCode !== 0 && clock() - start >= shardDeadline * 1000) timedOut = true;
|
|
1737
1859
|
lastFile = output === undefined && unfinishedLog === undefined ? undefined : lastStartedTestFile(`${output ?? ''}\n${unfinishedLog ?? ''}`, paths);
|
|
1738
1860
|
if (!lastFile && (job.exitCode !== 0 || rc === undefined || (rc !== 0 && rc !== 1))) {
|
|
1739
1861
|
try { lastFile = lastStartedTestFile(jobLogTail(job).split(/\r?\n/).slice(-200).join('\n'), paths); }
|
|
@@ -1746,7 +1868,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1746
1868
|
}
|
|
1747
1869
|
// A launch that failed before placement still gives its admission back (release is idempotent).
|
|
1748
1870
|
admitted?.release();
|
|
1749
|
-
const durationMs =
|
|
1871
|
+
const durationMs = clock() - start;
|
|
1750
1872
|
const fileSeconds = junit === undefined ? undefined : Object.fromEntries(readJunitFileSeconds(junit));
|
|
1751
1873
|
const testSeconds = fileSeconds === undefined ? undefined : Object.values(fileSeconds).reduce((sum, seconds) => sum + seconds, 0);
|
|
1752
1874
|
const testWallSeconds = batchIsolation
|
|
@@ -1794,7 +1916,8 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1794
1916
|
...(fileSeconds !== undefined ? { fileSeconds, testSeconds } : {}), ...(testWallSeconds !== undefined ? { testWallSeconds } : {}),
|
|
1795
1917
|
...(podOverheadSeconds !== undefined ? { podOverheadSeconds } : {}),
|
|
1796
1918
|
...(unfinishedLogBytes !== undefined ? { unfinishedLogBytes } : {}),
|
|
1797
|
-
...(admitted && admitted.outcome !== 'off' ? { admission: admitted.outcome, admissionWaitMs: admitted.waitedMs
|
|
1919
|
+
...(admitted && admitted.outcome !== 'off' ? { admission: admitted.outcome, admissionWaitMs: admitted.waitedMs,
|
|
1920
|
+
...(admitted.reason ? { admissionReason: admitted.reason } : {}) } : {}),
|
|
1798
1921
|
...(abort ? { abort: { reason: `abort: ${abort.reason}`, at: abort.at, by: abort.by, sinceLastOutputMs: abort.sinceLastOutputMs, taken: abort.taken } } : {}),
|
|
1799
1922
|
stallWatch: shardStallWatchRecord(stallSeconds, !!liveLog, stallWatchState) }) + '\n', { mode: 0o600 });
|
|
1800
1923
|
if (paths.length === 1 && (rc === 0 || rc === 1) && !jobFailed && jobExitCode === 0
|
|
@@ -2009,9 +2132,13 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
2009
2132
|
ran: Number(ran![1]), files: Number(ran![2]), passedIds: junit ? junitPassedIds(junit) : [] }], stalled: [] };
|
|
2010
2133
|
};
|
|
2011
2134
|
// 모든 조각이 끝난 뒤 판정한다 — 한 조각의 예외로 먼저 돌아가면 다른 Pod 가 도는 채로 정리가 시작된다(#22002 리뷰 R3).
|
|
2135
|
+
// GATE-RISK-LANE — the risk shard's own wall time (planned vs measured in its log line); it never changes a verdict.
|
|
2136
|
+
const shardDurations: number[] = [];
|
|
2012
2137
|
const settled = await Promise.allSettled(shards.map(async (item, shard) => {
|
|
2138
|
+
const shardStartedAt = clock();
|
|
2013
2139
|
try {
|
|
2014
2140
|
const done = await runShard(item.files, shard);
|
|
2141
|
+
shardDurations[shard] = clock() - shardStartedAt;
|
|
2015
2142
|
const state = !done.stalled.length ? 'done' as const : done.stalled.some((stall) => stall.reason === 'timeout') ? 'timeout' as const : 'failed' as const;
|
|
2016
2143
|
markShard(shard, { state, endedAt: new Date().toISOString() }, ['waitReason']);
|
|
2017
2144
|
return done;
|
|
@@ -2023,6 +2150,16 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
2023
2150
|
const rejected = settled.find((result): result is PromiseRejectedResult => result.status === 'rejected');
|
|
2024
2151
|
if (rejected) throw rejected.reason;
|
|
2025
2152
|
const results = settled.flatMap((result) => result.status === 'fulfilled' ? [result.value] : []);
|
|
2153
|
+
// GATE-RISK-LANE — planned vs measured for the shard that ran first, with its own result. Its Pods are ordinary
|
|
2154
|
+
// shard Pods: their runs join `measured` below, so the sweep's summary and every later judgment are unchanged.
|
|
2155
|
+
let riskLane: GateRiskLaneRecord | undefined;
|
|
2156
|
+
if (riskPlan.riskFiles.length) {
|
|
2157
|
+
const riskRuns = results[0]?.runs ?? [];
|
|
2158
|
+
riskLane = { files: [...riskPlan.riskFiles], plannedSeconds: riskPlan.riskPlannedSeconds,
|
|
2159
|
+
durationMs: shardDurations[0] ?? 0, rc: riskRuns.length ? (riskRuns.some((run) => run.fail + run.errors > 0) ? 1 : 0) : null };
|
|
2160
|
+
debug.log('release-loop.gate', 'pod-risk-lane', { files: riskLane.files, shard: 0, plannedSeconds: riskLane.plannedSeconds,
|
|
2161
|
+
durationMs: riskLane.durationMs, rc: riskLane.rc, ...(results[0]?.stalled[0] ? { reason: results[0].stalled[0]!.reason } : {}) });
|
|
2162
|
+
}
|
|
2026
2163
|
let stalledShards = results.flatMap((result) => result.stalled);
|
|
2027
2164
|
const measured = results.flatMap((result) => result.runs);
|
|
2028
2165
|
// GATE-STALLED-TAIL-REMEASURE: after the sweep, give only a one-file no-output leaf one fresh attempt on a different
|
|
@@ -2079,7 +2216,8 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
2079
2216
|
podSweep: { id: sweepId, shards: shards.length, files: assignable },
|
|
2080
2217
|
passedIds: [...new Set(measured.flatMap((run) => run.passedIds))].sort(),
|
|
2081
2218
|
stalledEnv: stalledShards.flatMap((item) => item.files.map((file) => ({ file, reason: 'no-output' as const }))),
|
|
2082
|
-
...(abortedShards ? { abortedShards } : {})
|
|
2219
|
+
...(abortedShards ? { abortedShards } : {}),
|
|
2220
|
+
...(riskLane ? { riskLane } : {}) };
|
|
2083
2221
|
};
|
|
2084
2222
|
const runPodRecheckBatch = async (files: readonly string[], commit: string, pod: NonNullable<GateOptions['pod']>, timeoutMs: number, relaxed = false): Promise<Map<string, CommandResult>> => {
|
|
2085
2223
|
const warmShadowScope = pod.warmShadowScope ?? { requested: false };
|
|
@@ -2102,6 +2240,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
2102
2240
|
|| file.split('/').some((part) => part === '.' || part === '..')))
|
|
2103
2241
|
throw new Error('unsafe pod recheck files');
|
|
2104
2242
|
if (!sha.test(commit)) throw new Error('invalid pod recheck commit');
|
|
2243
|
+
const rulesBaseSha = await rulesGateBaseFor(commit, files);
|
|
2105
2244
|
const installSlots = pod.installSlots ?? POD_INSTALL_SLOTS_DEFAULT;
|
|
2106
2245
|
if (!Number.isSafeInteger(installSlots) || installSlots < 0) throw new Error('invalid pod recheck install slots');
|
|
2107
2246
|
const bunCache = pod.bunCache?.trim() || process.env[POD_BUN_CACHE_HOST_PATH]?.trim();
|
|
@@ -2110,24 +2249,51 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
2110
2249
|
const deadlineSeconds = partLimitSeconds * Math.ceil(files.length / 2) + POD_SHARD_EMIT_RESERVE_SECONDS + 60;
|
|
2111
2250
|
const shell = gateShardShell({ parts: files.map((file) => [file]), cdpPatterns: [], softSeconds: softDeadlineSeconds(deadlineSeconds),
|
|
2112
2251
|
cachePrefix: bunCache ? podBunCacheVolume(bunCache).shellPrefix : '', installSlots, parallelParts: files.length > 1 ? 2 : 1,
|
|
2113
|
-
partLimitSeconds, sourceBinding: { cutSha: commit, attemptId }, ...(relaxed ? { testTimeoutMs: GATE_TIMEOUT_RECHECK_MS } : {}) });
|
|
2114
|
-
const
|
|
2252
|
+
partLimitSeconds, sourceBinding: { cutSha: commit, attemptId }, ...(rulesBaseSha ? { rulesBaseSha } : {}), ...(relaxed ? { testTimeoutMs: GATE_TIMEOUT_RECHECK_MS } : {}) });
|
|
2253
|
+
const warmEligible = files.length === 1 && pod.warmEligible === true && !!pod.warmReplay && !!pod.warmQuarantine && !!pod.warmFinalize
|
|
2254
|
+
&& pod.warmCutSha === commit;
|
|
2255
|
+
const warmRequest = warmEligible && pod.warmProducerEnabled && pod.warmReplayOwner !== false ? { protocol: 1 as const, runId: randomUUID(), attemptId, cutSha: commit,
|
|
2256
|
+
...(pod.warmBaselineSha ? { baselineSha: pod.warmBaselineSha } : {}), deadlineAt: Date.now() + timeoutMs + 60_000,
|
|
2257
|
+
files, envProfile: 'gate-v1' } : undefined;
|
|
2258
|
+
const replay = warmRequest ? await pod.warmReplay!(warmRequest) : undefined;
|
|
2259
|
+
const leased = replay?.command;
|
|
2260
|
+
let closed = false;
|
|
2261
|
+
const quarantine = async (reason: string) => {
|
|
2262
|
+
if (!leased || closed) return;
|
|
2263
|
+
await pod.warmQuarantine!(leased, reason);
|
|
2264
|
+
closed = true;
|
|
2265
|
+
};
|
|
2266
|
+
let producer: RunPodCommandOptions['warmProducer'];
|
|
2267
|
+
if (leased && replay?.transport.status === 'observed' && replay.transport.reason === 'complete') {
|
|
2268
|
+
if (warmCommandMatchesRequest(leased, warmRequest!) && leased.podName && leased.podUid
|
|
2269
|
+
&& leased.podContext && leased.image?.endsWith(`@${leased.key.imageDigest}`)) {
|
|
2270
|
+
producer = { pod: leased.podName, podUid: leased.podUid, context: leased.podContext, image: leased.image,
|
|
2271
|
+
commit, files: ['package.json', 'bun.lock'] };
|
|
2272
|
+
} else await quarantine('gate-producer-identity');
|
|
2273
|
+
}
|
|
2274
|
+
let job: PodCommandResult;
|
|
2275
|
+
try {
|
|
2276
|
+
job = await runPod({ pool: pod.pool, ...(poolOverride ? { poolScheduler: poolOverride } : {}), clone: true,
|
|
2115
2277
|
provenanceGate: true,
|
|
2116
2278
|
provenance: { actorKind: process.env.ELANOUS_SEAT ? 'seat' : 'loop', actor: process.env.ELANOUS_SEAT || 'release-loop',
|
|
2117
2279
|
character: 'gate-shard-runner', launchHost: hostname(), entrance: 'release-recheck', kind: 'gate-recheck',
|
|
2118
2280
|
run: process.env.ELANOUS_RUN_ID || attemptId, phase: 'release-gate', requestedAt: new Date().toISOString(),
|
|
2119
2281
|
graphNode: 'gate-triage', commit, reason: `release gate recheck ${files.length} files`, pool: pod.pool,
|
|
2120
|
-
memReq: POD_ISOLATION_MEMORY_LIMIT, warmReused:
|
|
2121
|
-
source: { kind: 'commit', sha: commit }, deadlineSeconds, ...(
|
|
2282
|
+
memReq: POD_ISOLATION_MEMORY_LIMIT, warmReused: !!producer },
|
|
2283
|
+
source: { kind: 'commit', sha: commit }, deadlineSeconds, ...(producer ? { warmProducer: producer, image: producer.image, imageCommit: commit } : {}),
|
|
2284
|
+
...(bunCache ? { bunCache } : {}), ...gateHostMirror(pod),
|
|
2122
2285
|
...(installSlots > 0 ? { installSlots: POD_INSTALL_SLOTS_HOST_PATH } : {}), ...(pod.cpu ? { cpu: pod.cpu } : {}),
|
|
2123
2286
|
memoryLimit: POD_ISOLATION_MEMORY_LIMIT, name: `gate-recheck-${randomUUID()}`, command: ['bash', '-lc', shell] });
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
}
|
|
2287
|
+
} catch (error) {
|
|
2288
|
+
await quarantine('gate-job-failed');
|
|
2289
|
+
throw error;
|
|
2290
|
+
}
|
|
2291
|
+
try {
|
|
2292
|
+
const artifact = (name: string) => {
|
|
2293
|
+
const path = join(job.artifactsDir, name);
|
|
2294
|
+
return existsSync(path) ? readFileSync(path, 'utf8') : undefined;
|
|
2295
|
+
};
|
|
2128
2296
|
const installed = artifact('install.rc')?.trim() === '0';
|
|
2129
|
-
const warmEligible = files.length === 1 && pod.warmEligible === true && !!pod.warmReplay && !!pod.warmQuarantine && !!pod.warmFinalize
|
|
2130
|
-
&& pod.warmCutSha === commit;
|
|
2131
2297
|
const incomplete = (message: string, file: string): Error => warmEligible
|
|
2132
2298
|
? new WarmRecheckIncomplete(message, file, { files: [file], attemptId, status: 'unmeasured', reason: 'reference-unmeasured', quarantined: 'no' })
|
|
2133
2299
|
: new Error(message);
|
|
@@ -2164,7 +2330,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
2164
2330
|
}
|
|
2165
2331
|
debug.log('release-loop.gate', 'pod-isolated-recheck', { file, commit, job: job.job, rc: rc ?? 124, relaxed });
|
|
2166
2332
|
}
|
|
2167
|
-
if (warmEligible && pod.warmReplay && pod.warmQuarantine && pod.warmFinalize) {
|
|
2333
|
+
if (warmEligible && !pod.warmProducerEnabled && pod.warmReplay && pod.warmQuarantine && pod.warmFinalize) {
|
|
2168
2334
|
shadowDeadline = Date.now() + shadowTimeoutMs;
|
|
2169
2335
|
const first = results.get(files[0]!)!;
|
|
2170
2336
|
const reference = first.warmReference;
|
|
@@ -2240,11 +2406,26 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
2240
2406
|
debug.log('release-loop.gate', 'warm-shadow-recheck', first.warmShadow);
|
|
2241
2407
|
}
|
|
2242
2408
|
}
|
|
2409
|
+
if (producer && leased) {
|
|
2410
|
+
const reference = results.get(files[0]!)?.warmReference;
|
|
2411
|
+
const decision = job.exitCode === 0 && reference && !timedOut
|
|
2412
|
+
? assessWarmWorkerShadow(leased, [reference], replay!.transport.reply) : null;
|
|
2413
|
+
if (decision?.reason !== 'match') await quarantine(decision?.reason ?? 'reference-unmeasured');
|
|
2414
|
+
else {
|
|
2415
|
+
await pod.warmFinalize!(leased);
|
|
2416
|
+
closed = true;
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2243
2419
|
return results;
|
|
2420
|
+
} catch (error) {
|
|
2421
|
+
await quarantine('gate-job-incomplete');
|
|
2422
|
+
throw error;
|
|
2423
|
+
}
|
|
2244
2424
|
};
|
|
2245
2425
|
return {
|
|
2246
2426
|
command,
|
|
2247
2427
|
localCommand: commandOverride && !remote ? commandOverride : localCommand,
|
|
2428
|
+
now: clock,
|
|
2248
2429
|
async recheckPod(file, commit, pod, timeoutMs, relaxed = false) {
|
|
2249
2430
|
return (await runPodRecheckBatch([file], commit, pod, timeoutMs, relaxed)).get(file)!;
|
|
2250
2431
|
},
|
|
@@ -2413,7 +2594,6 @@ export function saveBaselineFileCache(ledger: string, commit: string, rows: Base
|
|
|
2413
2594
|
|
|
2414
2595
|
export async function judgeGate(opts: GateOptions, runner: GateRunner = createGateRunner(opts.repo ?? process.cwd(), opts.remote),
|
|
2415
2596
|
imageProbe: typeof preflightGateImageDigest = preflightGateImageDigest): Promise<GateResult> {
|
|
2416
|
-
const start = Date.now();
|
|
2417
2597
|
if (opts.pod?.warmReplay) opts = { ...opts, pod: { ...opts.pod,
|
|
2418
2598
|
warmShadowScope: { requested: false, attempts: 0 } } };
|
|
2419
2599
|
const explicitConfig = getElanousConfigDirOverride();
|
|
@@ -2424,6 +2604,10 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
2424
2604
|
debug.log('release.run', opts.forceFreeze ? 'freeze-forced' : 'frozen', { version: opts.version, reason: frozen.reason, until: frozen.until, node: 'gate' });
|
|
2425
2605
|
if (!opts.forceFreeze) throw new Error(landingFreezeMessage(frozen));
|
|
2426
2606
|
}
|
|
2607
|
+
// GATE-FLOW-SIM — the injected clock (absent = `Date.now`), read only after the freeze check: a frozen gate must not
|
|
2608
|
+
// touch its runner at all (a refusal is decided before any side effect).
|
|
2609
|
+
const clock = runner.now ?? Date.now;
|
|
2610
|
+
const start = clock();
|
|
2427
2611
|
const ledger = explicitConfig ? root : (opts.ledgerRoot ?? releaseLedgerRoot());
|
|
2428
2612
|
const roots = [...new Set([ledger, root])];
|
|
2429
2613
|
const cachedBaseline = (version: string, commit: string) => {
|
|
@@ -2561,6 +2745,9 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
2561
2745
|
const runCut = (selected: typeof partial) => selected?.kind === 'carry' ? undefined : runner.sweep(cutTree,
|
|
2562
2746
|
join(root, 'release', opts.version, 'gate-logs', selected ? 'cut-partial' : 'cut'),
|
|
2563
2747
|
opts.pod ? { ...opts.pod, durationSource, ...(knownFailures.length ? { knownFailures } : {}), onSelectionTimed: ({ ms }) => { result.selectionMs = ms; },
|
|
2748
|
+
// GATE-RISK-LANE — the watch list is read from this gate's own ledgers (the instance root ⊕ the machine ledger);
|
|
2749
|
+
// the cut sweep puts those files first, the baseline sweep keeps its plan.
|
|
2750
|
+
riskWatch: { instanceRoots: roots, ledgerRoots: roots },
|
|
2564
2751
|
// GATE-LIVE-OBS: the release ledger's shards.json (UX #24782) — never the run tree's universe; an explicit
|
|
2565
2752
|
// --ledger-root (tests) keeps it in that ledger.
|
|
2566
2753
|
shardsFile: { path: opts.ledgerRoot ? gateShardsPath(opts.version, opts.ledgerRoot) : gateShardsPath(opts.version), version: opts.version } } : undefined,
|
|
@@ -2575,6 +2762,10 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
2575
2762
|
partial = null;
|
|
2576
2763
|
cutRun = await runCut(null);
|
|
2577
2764
|
}
|
|
2765
|
+
if (radius?.plan && partial === radius.plan) debug.log('release-loop.gate', 'baseline-consumed', {
|
|
2766
|
+
version: opts.version, baselineCommit: radius.plan.priorCommit, commit: opts.commit,
|
|
2767
|
+
selectedFiles: radius.plan.files.length, rehearsalRunId: radius.rehearsalRunId,
|
|
2768
|
+
});
|
|
2578
2769
|
cutSweepRc = cutRun?.rc ?? null;
|
|
2579
2770
|
cutSweepUsable = !!cutRun && (cutRun.rc === 0 || cutRun.rc === 1) && !cutRun.timedOut
|
|
2580
2771
|
&& !cutRun.abortedShards && !cutRun.stalledEnv?.length;
|
|
@@ -2770,7 +2961,7 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
2770
2961
|
debug.log('release-loop.gate', 'isolated-recheck-plan', { files: recheckFiles.length, window: recheckLimit,
|
|
2771
2962
|
concurrency: opts.pod && runner.recheckPod ? recheckConcurrency : 1, executor: opts.pod && runner.recheckPod ? 'pod' : 'host',
|
|
2772
2963
|
remote: opts.remote ?? null, podBatchFiles: opts.pod && runner.recheckPod && runner.recheckPodBatch ? GATE_RECHECK_POD_BATCH_FILES : 1 });
|
|
2773
|
-
const recheckStartedAt =
|
|
2964
|
+
const recheckStartedAt = clock();
|
|
2774
2965
|
const recordWarmShadow = (shadow?: WarmShadowObservation) => {
|
|
2775
2966
|
if (shadow && !result.warmShadow?.some((entry) => entry.attemptId === shadow.attemptId))
|
|
2776
2967
|
(result.warmShadow ??= []).push(shadow);
|
|
@@ -2933,6 +3124,11 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
2933
3124
|
result.introduced.sort();
|
|
2934
3125
|
preexistingIds.sort();
|
|
2935
3126
|
result.loadFlaky?.sort();
|
|
3127
|
+
// GATE-RISK-LANE — did the shard that ran first carry this gate's new failures? Observation only: the outcome above
|
|
3128
|
+
// is the existing aggregation, and the counts (introduced · preexisting) are the same ones the verdict writes.
|
|
3129
|
+
if (cutRun?.riskLane) debug.log('release-loop.gate', 'risk-lane-introduced', { files: cutRun.riskLane.files,
|
|
3130
|
+
plannedSeconds: cutRun.riskLane.plannedSeconds, durationMs: cutRun.riskLane.durationMs, rc: cutRun.riskLane.rc,
|
|
3131
|
+
introduced: result.introduced.filter((id) => cutRun!.riskLane!.files.includes(fileOf(id))).length });
|
|
2936
3132
|
result.preexisting = preexistingIds.length;
|
|
2937
3133
|
result.baselineCache = cacheStats;
|
|
2938
3134
|
debug.log('release.gate', 'baseline-cache', { hits: cacheStats.hits, misses: cacheStats.misses, commit: baseSha });
|
|
@@ -2985,7 +3181,7 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
2985
3181
|
} catch (error) { result.outcome = 'error'; result.error = `cleanup: ${String(error)}`; }
|
|
2986
3182
|
}
|
|
2987
3183
|
if (opts.remote) runner.remoteMirror = undefined;
|
|
2988
|
-
result.durationMs =
|
|
3184
|
+
result.durationMs = clock() - start;
|
|
2989
3185
|
}
|
|
2990
3186
|
if (cutFailures && !result.stalledShards?.length && !result.stalledEnv.length && !result.unmeasuredRecheckFiles?.length) {
|
|
2991
3187
|
try {
|