elanous 0.2.23 → 0.2.25
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/chunks/1027.a0f04663d24c2779.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1038.676007ae505822e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1113.a37eadf7f740d955.js +1 -0
- package/apps/pwa/out/_next/static/chunks/112-fa7c526c20ce1405.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1144.5fa1fa1d48dbcd12.js +36 -0
- package/apps/pwa/out/_next/static/chunks/1146.80d15ea7c6e6ee95.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1157.f08bf2b70915e176.js +148 -0
- package/apps/pwa/out/_next/static/chunks/119b405e.8fdd0e44d1ff37aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1296.04560d98e73fbf51.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1314-f604ba013ec535b0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1454-37fd16f479f5554a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1464.f3209af0d8ae4ff2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8532.a86e99acf9b761cb.js → 1516.83dd2c1b86b4e507.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/{89849432.9eaa2f4c9c010965.js → 16e57f67.3ea19e893461e611.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/1729-d73dd25e564679d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1505.5826671109111e75.js → 1743.563d3168e95825ef.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/1843.5924c24295243261.js +27 -0
- package/apps/pwa/out/_next/static/chunks/1987.e28149724b92918b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2066-5ca18ffa5a4be68b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2240.d10a66695155fd93.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2391.e2cbc98d4abbb41a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2528.3868070ef1be69f9.js +24 -0
- package/apps/pwa/out/_next/static/chunks/2566-734cb83bbd405886.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2697.76ca2ac3efef6f3b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/275.0e6bad414729bfa0.js +32 -0
- package/apps/pwa/out/_next/static/chunks/2803-33bdae771855a8d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2846.d6a433b8663819a5.js +309 -0
- package/apps/pwa/out/_next/static/chunks/3143.257997a5f1ed79df.js +34 -0
- package/apps/pwa/out/_next/static/chunks/{5967-c0a80f8ffe5d4050.js → 3259-86f3be56e43eaf6a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3280-e177c33bfbdcfb0a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3294-2b6a6d45a46a6c1c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3309.364573ceedbdfd5f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3522.758840fc5e7fe630.js +287 -0
- package/apps/pwa/out/_next/static/chunks/3525-a7c989c1a661e880.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3694.b1a66387756458c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/370.124f9e678a76fc01.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3797-9643105335e660fe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5578.ac963634373b28c6.js → 4084.15ecaf4240bd9e77.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/4089-b3382b8843114eee.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8438.ee7f0df3b3f6fcb4.js → 4204.a409ab6250c0f6a8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4293.928a8d573c16aab7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4402.f9238a3b18a7bd43.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4490-9b090b0f9427f902.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4548.13502d0ca3b4f862.js +8 -0
- package/apps/pwa/out/_next/static/chunks/4565-67e4c620597965c8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7905.b230b9d06cb4ad5d.js → 4593.10f5b9bd702d4a6c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4599-548bc96cda44a5a7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4625.206574e33a9e7ae8.js +104 -0
- package/apps/pwa/out/_next/static/chunks/4862-392c9a71f0d8dfb4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5054.2906d1b02542edc5.js +149 -0
- package/apps/pwa/out/_next/static/chunks/{8569.b853a8daa8f31d12.js → 5081.a240b63fad28663a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5102-c822bbbee5a022a4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5194-8c93f2e7e4b6f717.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5305-bacd7eca0b9a5848.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5311.9071ee3f44fd7ff5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5369.9f34353442daa421.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2528.0af5c0e1fad88df6.js → 5408.377c8c543635b81f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{9502-32c5dc22e8f120f5.js → 5492-e05e26a033ba80d2.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/5785-ea8d2c633b6688e3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/589-49d730cb8138b397.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5904-264a6412ae3c9028.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5980.728531d5c566535c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/599-a914cc0468f56143.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6011.f525002e21bef0ac.js +23 -0
- package/apps/pwa/out/_next/static/chunks/6132.4381ab93683f1acf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6149.2e698635e454d8f6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5974.360f8441eb0ac1ec.js → 6196.34d6b229079e9e28.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6356-0afafc4230cf9767.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6406.64756073f7df35b8.js +77 -0
- package/apps/pwa/out/_next/static/chunks/6479-16e62aedafa0e310.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6599.b5246b46a2453329.js +64 -0
- package/apps/pwa/out/_next/static/chunks/{ce383d63.d2ede338788ed18e.js → 65ebff95.081daeff622d238e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6610.5c26eec011565acb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/666.37d18474edc1c552.js +2 -0
- package/apps/pwa/out/_next/static/chunks/6904.a0b5b1ed4f2d2c20.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{83cfc5c8.349535543c1548b6.js → 6def8a8e.c81f9ca41028eccf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7247-1f4aba94aba50d9f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7274-e71a7c5b7760cd02.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7290.1fcaeef3adb5d0d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7477-2c64e42760f82f33.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2721.bc9a8f36bd85c07a.js → 7483.ba01c6c209b81bba.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7505-47002b76ff1aef6c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7600-11884ca4da68e5d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7607.40d8bb3689697ccb.js +62 -0
- package/apps/pwa/out/_next/static/chunks/{6702.ebbd165d5e4a4a14.js → 7752.5cbdd0b433c54af9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7763.f51fcc19ca1414ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7785.846140c859b1adf6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7807-a0fdac6393e0bf36.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7837-20a63b4b7e9342b1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/800.7815414cf280545d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8925-2abfb48af477fb7f.js → 8085-1e5fce59500a2cc2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8100.415f2105faa63e97.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8290.701aa42ed13aa767.js +226 -0
- package/apps/pwa/out/_next/static/chunks/{b4e12462-5fab4a813a63dadd.js → 83a80514-b897b64df43ce108.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8444.d9c74214a3145a43.js +93 -0
- package/apps/pwa/out/_next/static/chunks/8569.98c915534928c7f9.js +24 -0
- package/apps/pwa/out/_next/static/chunks/{8264.00e434fbd02e9311.js → 8578.dff22d988cc2b106.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/8591.b1c7f876e5ec7cfb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8715-f63756021d3ab989.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8879.776c2ec7732aa114.js +115 -0
- package/apps/pwa/out/_next/static/chunks/8881.ddca15b3c82c490c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8946.5da5c87f32a2e1a8.js +41 -0
- package/apps/pwa/out/_next/static/chunks/9117.080b9b75be90e14d.js +74 -0
- package/apps/pwa/out/_next/static/chunks/9167.f3ecb28f367d6e69.js +94 -0
- package/apps/pwa/out/_next/static/chunks/9224.66095370ee28c93b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9238-0682b013c83d6449.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7518.49161e8f5814abdb.js → 9311.24c8f5ea793253dd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{9976.8f28864317639fe6.js → 94.4cfed21c0b781a06.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/9449.d3ed795da4befb1c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/949.1e7d45ccb71580aa.js +90 -0
- package/apps/pwa/out/_next/static/chunks/9638-d268329f2c6f4f8d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{0e3acd34.d42b2ef87fdf4234.js → 973cbf1b.3bbcf2b41548eca7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9842-d6644eb53d3dc6bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9863.94ca3263e760696e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6571-926ddf024a6ad5df.js → 9917-9e26abc5f7772735.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9938.b23e4d4dfa0114cd.js +79 -0
- package/apps/pwa/out/_next/static/chunks/{740.90b31555290236a9.js → 9974.e77b4ff4884557b3.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/{4c5e95ce.83e9b16add6fcd8c.js → ab4d57c2.e90bc2cb31949045.js} +7 -7
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d9babd8795f396a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-9360b1285b2a346e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f08f6529d09ffae1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-f000d54d258f9ef6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-ad3bd8084048bee4.js → page-b12149c51f7d04b1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-22f674e7e4c2c9d5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-8888733e40cea6d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-9717ec6964b92276.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-44eaad486bc98a45.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-97fa6cf3d34c7eb9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-305cd294e754906e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-a648bc464c9dc754.js → page-dea4b795741126fd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-35e05d7450461a67.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-45889e7070d11126.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-16a2e893f83ae4d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-d9babd8795f396a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-b2638013c0bf7dfd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-56888bc744e08dac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-c44f93a41167536f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-952c6ceec6d9de38.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-6d0202af568f4c63.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-1b810474507f3702.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-74366ae2693a7d95.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-4fb6fc34e20c07b7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-e9502921f4f01b1f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-b6169ea2f09cf287.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-6a14333d1b1b3967.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-8c91a9a66c9ec90a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-48fb31810693c357.js → page-4e634c5b98fd4e93.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ed3070d637850548.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/{page-cb154acadb025414.js → page-b9cc8a10bc6078b4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/page-3d3fc73dd187ed52.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-128458c8d86fbb5f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-9c04dac0831c2bbb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-cf0c2b0183991d09.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-04de9ca6d52a3893.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-62527677bbc2fdc4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-9a2e09c4a311764c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-d9babd8795f396a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-bfeb35d69e8b24bb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-89cb33bced824e4a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-b008aff96be56654.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-814394dcbae789b1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-6a87325c87f09dd2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-6445ac4cbb8dd869.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-852170303c6cd755.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-087ceeb975ef272b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-97c7361cc2a19319.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-20162174b77f96cf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-8c6901833f897496.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-05a4b2c9b91034aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/dcf24383-2f82e35f2d827393.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{53746282.1be36038d1abd949.js → e117f801.e92bf10242653497.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/e87d89b9.2e71c0ae475d28a1.js +122 -0
- package/apps/pwa/out/_next/static/chunks/{0673cc54.8e2ef1c23e49e190.js → f4dfbfc0.a69fb12f854882aa.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-bf135b997155e90e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-5571537543bea91c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-25c51c7df7f3a442.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-e3b2c1f5d82abd65.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-083f2e23e9bc3641.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-e94d22b7a028217b.js +1 -0
- package/apps/pwa/out/_next/static/css/72becfd42807b50d.css +1 -0
- package/apps/pwa/out/_next/static/{rfkMm4movqkqMebBrCXiR → dRS6lEhSRRBr1abbzp7fJ}/_buildManifest.js +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/catalog/models/openrouter/deepseek-v4-pro-0813.yaml +39 -0
- package/catalog/models/openrouter/deepseek-v4.1-flash.yaml +38 -0
- package/catalog/models/openrouter/glm-5.3-flash.yaml +35 -0
- package/catalog/providers/openrouter.yaml +2 -0
- package/graphs/implement-loop.yaml +31 -0
- package/graphs/ops/harness-gc.yaml +17 -0
- package/graphs/ops/recipes.yaml +3 -0
- package/graphs/overlays/README.md +1 -1
- package/graphs/overlays/stop-heal-ladder.yaml +105 -0
- package/graphs/release/release-loop.yaml +5 -5
- package/graphs/steward/recipes.yaml +4 -0
- package/graphs/steward/steward.yaml +10 -1
- package/package.json +1 -1
- package/scripts/add-api-key.sh +1 -1
- package/scripts/bench-report.ts +46 -1
- package/scripts/ci-typecheck-changed.ts +73 -14
- package/scripts/fleet/bedrock-pod-secret.ts +122 -0
- package/scripts/fleet/node-setup-linux.sh +97 -0
- package/scripts/fleet/node-setup.sh +11 -0
- package/scripts/fleet/zai-pod-secret.ts +86 -0
- package/scripts/land-global-checks.ts +3 -0
- package/scripts/measure-log-diet-2.ts +213 -0
- package/scripts/measure-tsc-affected-roots.ts +289 -0
- package/scripts/release-loop/auto-approve-node.ts +6 -0
- package/scripts/release-loop/gate-closure.ts +286 -0
- package/scripts/release-loop/gate-executor.ts +28 -0
- package/scripts/release-loop/gate-node.ts +333 -37
- package/scripts/release-loop/gate-partial.ts +34 -6
- package/scripts/release-loop/ops-upgrade-node.ts +63 -42
- package/scripts/release-loop/publish-node.ts +6 -0
- package/scripts/release-loop/resume-release.ts +46 -13
- package/scripts/release-loop/resume.ts +110 -4
- package/scripts/release-loop/unattended-release.ts +22 -7
- package/scripts/tsc-baseline-cache.ts +172 -0
- package/scripts/usage-rollup.ts +72 -6
- package/scripts/ux-sim.ts +30 -1
- package/skills/youtube-master/scripts/main.ts +4 -1
- package/skills/youtube-master/src/obsidian.ts +9 -0
- package/src/agent/run-context.ts +10 -0
- package/src/agent-mission/driver.ts +18 -3
- package/src/agent-mission/resource-ladder.ts +8 -3
- package/src/agent-mission/review-loop.ts +8 -2
- package/src/agent-substrate/acp-implementer.ts +146 -0
- package/src/agent-substrate/pr-reviewer.ts +60 -13
- package/src/agent-substrate/self-review-cli.ts +170 -15
- package/src/autopilot/mission-critique.ts +18 -5
- package/src/autopilot/mission-feasibility-negotiate.ts +11 -10
- package/src/autopilot/mission-trace.ts +12 -1
- package/src/autopilot/system-lookback-run.ts +16 -10
- package/src/bench/bench.ts +183 -0
- package/src/budget/cost-tier-advisor.ts +91 -0
- package/src/budget/fetchers/grok.ts +15 -5
- package/src/budget/llm-cost.ts +37 -5
- package/src/budget/shadow-arms.ts +77 -0
- package/src/budget/types.ts +10 -2
- package/src/budget/unified-usage.ts +256 -10
- package/src/cli/bench-cli.ts +34 -0
- package/src/cli/doctor-cli.ts +21 -0
- package/src/cli/friendly-errors.ts +113 -0
- package/src/cli/guardian-cli.ts +26 -1
- package/src/cli/image-cli.ts +101 -0
- package/src/cli/intake-cli.ts +3 -1
- package/src/cli/log-event-names.ts +4 -0
- package/src/cli/memory-cli.ts +182 -0
- package/src/cli/model-watch-cli.ts +4 -1
- package/src/cli/pr-cli.ts +62 -5
- package/src/cli/pr-land-overlap-evidence.ts +160 -12
- package/src/cli/pr-land-pod-shadow.ts +54 -0
- package/src/cli/release-cli.ts +27 -16
- package/src/cli/release-run-if-ready.ts +11 -0
- package/src/cli/resource-cli.ts +48 -0
- package/src/cli/retro-cli.ts +12 -3
- package/src/cli/rfc-status.ts +52 -2
- package/src/cli/seat-requests-cli.ts +25 -0
- package/src/cli/self-update.ts +104 -0
- package/src/cli/tasks-cli.ts +139 -10
- package/src/cli/usage-cli.ts +93 -8
- package/src/compact/verify-probe.ts +23 -3
- package/src/context-card/index.ts +50 -0
- package/src/control/control-status.ts +30 -1
- package/src/core-turn/run-goal-loop.ts +5 -4
- package/src/dashboard/ad-run-setup-bindings.ts +279 -0
- package/src/dashboard/index.ts +18 -276
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +4 -138
- package/src/dashboard/slash-runtime/shell-slash.ts +145 -0
- package/src/dashboard/stale-install-check.ts +11 -0
- package/src/debug/log-diet.ts +53 -10
- package/src/debug/log.ts +19 -1
- package/src/domains/morning-essential.ts +18 -3
- package/src/domains/release-tool.ts +43 -0
- package/src/error-report/report.ts +21 -4
- package/src/execution-loop/launch-gate.ts +158 -2
- package/src/fleet/host-doctor.ts +375 -0
- package/src/github/pr-labels.ts +1 -0
- package/src/goals/judge.ts +20 -6
- package/src/graph-runner/graph-cli.ts +42 -1
- package/src/graph-runner/runner.ts +176 -11
- package/src/graph-wizard/archetypes.ts +14 -8
- package/src/graph-wizard/generate.ts +77 -1
- package/src/grok/usage.ts +23 -2
- package/src/grok.ts +13 -7
- package/src/grounding/ref-index.ts +253 -0
- package/src/harness/dispatch-backpressure.ts +10 -2
- package/src/harness/harness-clean.ts +199 -78
- package/src/harness/harness-cli-command.ts +176 -52
- package/src/harness/harness-gc.ts +331 -0
- package/src/harness/harness-stop.ts +82 -10
- package/src/harness/harness-substrate-default.ts +142 -7
- package/src/harness/launch-registry.ts +6 -0
- package/src/harness/worktree-branch-prefix.ts +9 -0
- package/src/hq/fence.ts +187 -0
- package/src/hq/seats.ts +38 -22
- package/src/images/image-ledger.ts +179 -0
- package/src/index.ts +110 -186
- package/src/instance/child-scope.ts +32 -1
- package/src/instance/dev-project.ts +133 -0
- package/src/instance/resolve.ts +24 -0
- package/src/instance/spawn-elanous.ts +45 -0
- package/src/intake-plane/collect-github.ts +7 -2
- package/src/intake-plane/lens.ts +46 -4
- package/src/intake-plane/ref-prefetch.ts +110 -0
- package/src/intake-plane/requirement-funnel.ts +53 -1
- package/src/intelligence-map/catalog-feed.ts +119 -0
- package/src/intelligence-map/model-catalog.ts +72 -0
- package/src/intelligence-map/model-watch-intake.ts +40 -0
- package/src/intelligence-map/model-watch-mission.ts +14 -1
- package/src/llm/bedrock-eventstream.ts +215 -0
- package/src/llm/bedrock-provider.ts +114 -41
- package/src/llm/bedrock.ts +174 -28
- package/src/llm/open-weight-discipline.ts +21 -0
- package/src/llm/openrouter-byok-cost.ts +96 -0
- package/src/llm/openrouter-deepseek.ts +151 -0
- package/src/llm/openrouter-upstream-pins.config.ts +25 -0
- package/src/llm/zai-coding-plan.ts +700 -0
- package/src/llm.ts +91 -24
- package/src/loops/checker.ts +148 -9
- package/src/loops/orchestrator/resync-shadow.ts +34 -7
- package/src/maturity/feature-maturity.ts +3 -3
- package/src/model-tier/llm-tier-map.ts +9 -5
- package/src/models/prompts.ts +2 -1
- package/src/mss/logging/log-store.ts +58 -11
- package/src/nexus/api/http-server.ts +22 -0
- package/src/nexus/api/missions.ts +73 -2
- package/src/nexus/api/ops-api.ts +18 -0
- package/src/nexus/index.ts +7 -75
- package/src/nexus/tools-kind.ts +78 -0
- package/src/oauth/codex-account-rotation.ts +43 -3
- package/src/oauth/codex-account-store.ts +339 -27
- package/src/oauth/codex-quota-policy.ts +33 -4
- package/src/oauth/fallback-chain.ts +53 -15
- package/src/onboarding/first-run.ts +37 -18
- package/src/onboarding/web-first.ts +20 -2
- package/src/ops/machine-heartbeat.ts +185 -0
- package/src/ops/overview-cache.ts +175 -0
- package/src/ops/overview.ts +8 -0
- package/src/preview/terminal.ts +15 -5
- package/src/project/project-store.ts +20 -0
- package/src/prompt-cache/openai.ts +3 -0
- package/src/prompt-cache/types.ts +5 -0
- package/src/prompt-enhance/enhance.ts +23 -0
- package/src/release-loop/auto-start.ts +7 -5
- package/src/release-loop/checklist.ts +11 -1
- package/src/release-loop/gate-shards.ts +4 -0
- package/src/release-loop/light-rc.ts +84 -13
- package/src/release-loop/merged-pr-checklist.ts +35 -5
- package/src/release-loop/placement.ts +16 -1
- package/src/release-loop/rehearsal.ts +1 -1
- package/src/requirements/requirement-ledger.ts +536 -0
- package/src/retro/daily.ts +5 -5
- package/src/seat-loop/duty-ledger.ts +64 -0
- package/src/seat-loop/seat-loop.ts +32 -10
- package/src/self-dev/decompose.ts +37 -3
- package/src/self-dev/dev-cli.ts +44 -10
- package/src/self-dev/dev-pipeline.ts +39 -9
- package/src/self-dev/draft-residue.ts +27 -5
- package/src/self-dev/draft-sweep.ts +151 -0
- package/src/self-dev/fabric-decompose-adapter.ts +9 -2
- package/src/self-dev/graph-journey-route.ts +6 -0
- package/src/self-dev/orchestrate-cli.ts +25 -3
- package/src/self-dev/orchestrate.ts +41 -6
- package/src/self-dev/run-supervisor.ts +218 -15
- package/src/self-dev/run-types.ts +13 -0
- package/src/self-dev/supervisor-verdict-edges.ts +1 -0
- package/src/self-implement/budget-gate.ts +53 -13
- package/src/self-implement/child-context-composition.ts +139 -0
- package/src/self-implement/child-llm-preference.ts +13 -5
- package/src/self-implement/claude-acp-implement.ts +67 -0
- package/src/self-implement/flex-signals.ts +59 -0
- package/src/self-implement/frozen-merges.ts +8 -3
- package/src/self-implement/gate-cli.ts +29 -10
- package/src/self-implement/gate-remote.ts +4 -1
- package/src/self-implement/goal-author-decompose.ts +10 -2
- package/src/self-implement/goal-author.ts +36 -11
- package/src/self-implement/goal-new-paths.ts +42 -0
- package/src/self-implement/graph-overlay-condition.ts +28 -0
- package/src/self-implement/graph-overlay-yaml.ts +41 -7
- package/src/self-implement/graph-templates.ts +33 -1
- package/src/self-implement/graph-yaml.ts +56 -0
- package/src/self-implement/heal-intake.ts +5 -1
- package/src/self-implement/host-regate.ts +124 -30
- package/src/self-implement/importer-test-index.ts +3 -1
- package/src/self-implement/l8-merge-queue.ts +80 -2
- package/src/self-implement/merge-train.ts +19 -0
- package/src/self-implement/off-diff-evidence.ts +40 -1
- package/src/self-implement/orchestrator.ts +230 -32
- package/src/self-implement/premise-review.ts +159 -0
- package/src/self-implement/regate-detail.ts +79 -0
- package/src/self-implement/review-deferred-to-host.ts +21 -0
- package/src/self-implement/rework-budget-judgment.ts +23 -3
- package/src/self-implement/running-runs.ts +75 -5
- package/src/self-implement/scope-adjacency.ts +23 -0
- package/src/self-implement/seams.ts +89 -8
- package/src/self-implement/self-implement-cli.ts +9 -4
- package/src/self-implement/stop-autoheal.ts +69 -2
- package/src/session-runtime/retry-policy.ts +2 -1
- package/src/skills/tools/agent.ts +16 -1
- package/src/skills/tools/persistent-grounding.ts +11 -1
- package/src/skills/tools/shell.ts +33 -1
- package/src/steward/launch.ts +5 -4
- package/src/steward/rejudge-tick.ts +142 -0
- package/src/steward/triage-budget.ts +62 -0
- package/src/steward/triage.ts +83 -22
- package/src/task-agent/actions.ts +62 -5
- package/src/task-agent/board.ts +92 -3
- package/src/task-agent/card-evidence.ts +88 -1
- package/src/task-agent/completion-evidence.ts +5 -0
- package/src/task-agent/cover.ts +166 -3
- package/src/task-agent/judge.ts +16 -0
- package/src/task-agent/land-order-ledger.ts +95 -0
- package/src/task-agent/live-move-history.ts +87 -1
- package/src/task-agent/live-moves.ts +350 -20
- package/src/task-agent/parent-host.ts +27 -3
- package/src/task-agent/rejudge-on-head.ts +223 -24
- package/src/task-agent/review-prior-fix.ts +146 -0
- package/src/task-agent/review-result-capture.ts +3 -1
- package/src/task-agent/run-adopt.ts +136 -0
- package/src/task-agent/shadow.ts +65 -9
- package/src/task-agent/task-hand.ts +57 -7
- package/src/task-orchestrator/surfaces/pod-account-broker.ts +38 -5
- package/src/task-orchestrator/surfaces/pod-bedrock-child.ts +74 -0
- package/src/task-orchestrator/surfaces/pod-child-default.ts +50 -0
- package/src/task-orchestrator/surfaces/pod-child-providers.ts +14 -1
- package/src/task-orchestrator/surfaces/pod-command-job.ts +14 -0
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +79 -6
- package/src/task-orchestrator/surfaces/pod-host-acp-review.ts +122 -0
- package/src/task-orchestrator/surfaces/pod-lease.ts +17 -5
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +11 -5
- package/src/task-orchestrator/surfaces/pod-manifest-test-normalize.ts +23 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +73 -4
- package/src/task-orchestrator/surfaces/pod-zai-child.ts +101 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +432 -35
- package/src/task-orchestrator/surfaces/self-implement.ts +19 -1
- package/src/typecheck-ratchet.ts +1 -0
- package/src/user-config.ts +69 -15
- package/src/ux-sim/fault-cred-expiry.ts +100 -0
- package/src/ux-sim/fault-pod-death.ts +74 -0
- package/src/ux-sim/fault-pool-cap.ts +81 -0
- package/src/version/packed-revision.json +1 -1
- package/src/webclone/qa-table.ts +22 -0
- package/src/workflow-runtime/triggers/registry.ts +59 -0
- package/src/workflow-runtime/triggers/webhook-source.ts +35 -5
- package/apps/pwa/out/_next/static/chunks/0a09e062-34f747a17d8d8c2e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1168.a0fb3d9db76c00bd.js +0 -77
- package/apps/pwa/out/_next/static/chunks/1325-dc3f3c21eb181cb4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1357.a843c41d2cd7ec31.js +0 -34
- package/apps/pwa/out/_next/static/chunks/1430.53f49882b17607c2.js +0 -24
- package/apps/pwa/out/_next/static/chunks/1459-be6509b3419d8a9f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/153.0a4e3cd516930491.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1572.7bc291352e241a45.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1593-d07ad068c120dfb0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1608.b7c900baa56cab32.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1615-4d8156febaac4b35.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1646.e03f0fbc26bc72f6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1669-da843ed73aa301f5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/176.13ba0659e3e96fd4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1990.9c60f495ef33c070.js +0 -36
- package/apps/pwa/out/_next/static/chunks/2042e469.f86158dd5c1e51a1.js +0 -122
- package/apps/pwa/out/_next/static/chunks/2543-28ec6aeb80a5e1e1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2561.43c99d6ee914ee6a.js +0 -62
- package/apps/pwa/out/_next/static/chunks/2638.b39ab2f674caa1a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/272-b23d2e92161d11fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2778.67e0c1d484189ccf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2841.10ff309f6a3168af.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2886-9639f97d523d752a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2978.57fb87ce21cc5a85.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3664.15f968c39d15bd28.js +0 -149
- package/apps/pwa/out/_next/static/chunks/3786-935eb3fcde90e496.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3813.df4a01ae89c6f63f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3961-fbef064c213b2b18.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3992.f79c70480e8c4077.js +0 -309
- package/apps/pwa/out/_next/static/chunks/40-413e8641de9ed1e5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4054-521f11a503daed64.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4315.e7356f34c7d8ec79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4489-bf629de6f04f7681.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4540.557733cc88c6a82b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/462.0275b4338c5b6337.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4658-7ed9266098a787e4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4945-0f4046cfc3228ea3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5105-18b4165c5d3a86b0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5205-6992210fe31f45f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5287-72898b126d722457.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5313.1697309c532b9856.js +0 -2
- package/apps/pwa/out/_next/static/chunks/5474.991b90688c3cb321.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5544-871146192d095594.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5935.d5e8ca3e096772e8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6006.84e8b418a7a0db97.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6013-1267ba7454d6764b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6038-1e0f7ee1086da4cf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/617.709dd0019fe99796.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6261.fb9bf94947094636.js +0 -74
- package/apps/pwa/out/_next/static/chunks/6411.1966866fae31ffa9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6481-b829d99aa8d158f5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6541.2b7daa043100d0fa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6597.2d7a6d77ab485b27.js +0 -64
- package/apps/pwa/out/_next/static/chunks/67.5dd2dc543c2fa3de.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6777-312496b0d774719b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/681.0116f7036cfb4e8d.js +0 -32
- package/apps/pwa/out/_next/static/chunks/6871.30b2bf23b3126e36.js +0 -24
- package/apps/pwa/out/_next/static/chunks/6882.737d366d54e29d83.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7035.54c2c535164b1c9b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7086-8bfa7e8f796a03d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7126.5e5599f251726b67.js +0 -93
- package/apps/pwa/out/_next/static/chunks/7203-47ff0740a293e284.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7268.017f49dafb5f0ff3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7355.5d65a87e9567ae08.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7392.4018c2a62177f296.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7471.3a3d1be50559cfb8.js +0 -148
- package/apps/pwa/out/_next/static/chunks/7500.73489a345cef2201.js +0 -226
- package/apps/pwa/out/_next/static/chunks/7565.c2f72a15c1ed0396.js +0 -27
- package/apps/pwa/out/_next/static/chunks/7651.6d1c984d3d8587a4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7713.7ab863ed59b2d661.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7793.d7cd2026767ddd54.js +0 -115
- package/apps/pwa/out/_next/static/chunks/7837.c7c267e5dceadb11.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7891.09266382daf70d1c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7990-cbf6a01bcbdcb7e6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8119-819c549e0c197786.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8186-74ea19a3700d04a8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8267-e7b3262b735ae106.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8386.86518e55b75f7cf5.js +0 -8
- package/apps/pwa/out/_next/static/chunks/8437.ba6eeeb29590042f.js +0 -23
- package/apps/pwa/out/_next/static/chunks/8469-1500be57e582296f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8483-7164cecdaf178ca6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8722-4c940f02c496ad36.js +0 -1
- package/apps/pwa/out/_next/static/chunks/887-41d0dcf5af48c628.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9039.da86361496797522.js +0 -90
- package/apps/pwa/out/_next/static/chunks/905.d5745a25884754d4.js +0 -94
- package/apps/pwa/out/_next/static/chunks/9089-d909f3d4478b0628.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9096.dc5c7a267f83790c.js +0 -41
- package/apps/pwa/out/_next/static/chunks/93.a567257b6bdb7fe4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9320.419acb9665b46597.js +0 -287
- package/apps/pwa/out/_next/static/chunks/9356.b8ceda6634612ff8.js +0 -79
- package/apps/pwa/out/_next/static/chunks/94.e9e1043b35cd3eed.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9400-7498ea90b2e6ee8f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9930-58644af9c813d6e1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9932.367cf4725fedd6e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9935.1628b904cdc8ce0e.js +0 -104
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-a2aa8709d193ee0d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-d5341ac96203a983.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-0160ab83c8685a12.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-55c631c032dcc47e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-e6784759a2531031.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-5c5d0e370f495ef6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-c325e21543cd48a8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-6168a7c43e6ef8f5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-31417d3604154a6d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-e9606dc0bdec9305.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-25bb425afdb27541.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-32d72fdef1afe11a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-d27b794c8ba76bbb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-a2aa8709d193ee0d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-2bd9ce9c020d02d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-a34096245e63b9f2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-22170f4f00a15496.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-4cc66d22ca4ec502.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-83b14c729fcea837.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-6e2781c523dad761.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-26dd415c8f13f341.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-ccdb83ed40c8d724.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-7ad3de8fa06fdea9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-015c02c4fec4023e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-6b0ae5b5c42f6d2f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-786111f30b6f0969.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4e17cce5e2ed0fee.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-16258e956ea93fc2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-c2dd2b19c93f90ab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f35dd1dd96ae87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-d3c007c8a0ddd7ee.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-7837ce2aacb97b04.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-7350ee93f4fe1033.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-00723458575f2b19.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-a2aa8709d193ee0d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-745d9b6c015076f8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-736d33c7414a8a1a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-119be9a0444d2482.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-fa806c018833e332.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-c782041b5f5199ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-d431fbdea6ad93dd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-0c056abff82b9891.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-242d668e4fbf72c4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-847c3de6ca8a4416.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-81dd0d8ae923ed72.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-74e69ba2c8e970d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-70038f41ae7cf931.js +0 -1
- package/apps/pwa/out/_next/static/chunks/cd39fa05.fac4545f42a3ef70.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-b2763b10dc6b3836.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-27893e93b0ec193d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-f280047194e1daf8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-e90eff2d0b70564c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-70f2d654b3435d34.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-4794f053bd5e4230.js +0 -1
- package/apps/pwa/out/_next/static/css/5619d59e87eddf66.css +0 -1
- /package/apps/pwa/out/_next/static/{rfkMm4movqkqMebBrCXiR → dRS6lEhSRRBr1abbzp7fJ}/_ssgManifest.js +0 -0
|
@@ -14,11 +14,13 @@ import { loadEnvKnownFailures, splitKnownEnv } from './env-known-failures';
|
|
|
14
14
|
import { baselineFromCutLogs } from './baseline-from-logs';
|
|
15
15
|
import { mergePartialFailures, readPartialPlan, testFileOfId } from './gate-partial';
|
|
16
16
|
import { logPlanWalltime, planDurations, planShards, readFileMemory } from './shard-plan';
|
|
17
|
+
import { readJunitFileSeconds } from './gate-method';
|
|
17
18
|
import { deriveCdpTestPatterns } from '../test-deterministic';
|
|
18
19
|
import { emitNodeResult, readGraphContext } from './node-verdict.js';
|
|
20
|
+
import { isGateExecutor, type GateExecutor } from './gate-executor.js';
|
|
19
21
|
import { runPodCommand, parsePodCpu, resolvePodCpu, type RunPodCommandOptions, type PodCommandResult, type PodCpu } from '../../src/task-orchestrator/surfaces/pod-command-job.js';
|
|
20
22
|
import { POD_BUN_CACHE_HOST_PATH, parseInstallSeconds, podBunCacheVolume } from '../../src/task-orchestrator/surfaces/pod-bun-cache.js';
|
|
21
|
-
import { PodPoolScheduler, parsePodPool, checkPodPool } from '../../src/task-orchestrator/surfaces/pod-pool.js';
|
|
23
|
+
import { PodPoolScheduler, parsePodPool, checkPodPool, defaultRemoteRun, type PodPoolMember } from '../../src/task-orchestrator/surfaces/pod-pool.js';
|
|
22
24
|
import { POD_INSTALL_SLOTS_DEFAULT, POD_INSTALL_SLOTS_HOST_PATH, installSlotScript } from '../../src/task-orchestrator/surfaces/pod-install-slots.js';
|
|
23
25
|
import { gateDepsInstallScript, parseGateDeps } from '../../src/task-orchestrator/surfaces/pod-gate-deps.js';
|
|
24
26
|
import { gateShardsPath, recordGateShardsVerdict, writeGateShards, type GateShard } from '../../src/release-loop/gate-shards.js';
|
|
@@ -45,6 +47,8 @@ export interface GateOptions {
|
|
|
45
47
|
baselineCommit?: string;
|
|
46
48
|
remote?: string;
|
|
47
49
|
remoteMirror?: string;
|
|
50
|
+
/** GATE-HOST-EXECUTOR — `host` ignores every pod option and sweeps on `remote` (required). Absent = today's choice (pod when a pool is set). */
|
|
51
|
+
executor?: GateExecutor;
|
|
48
52
|
instanceRoot?: string;
|
|
49
53
|
ledgerRoot?: string;
|
|
50
54
|
repo?: string;
|
|
@@ -62,8 +66,24 @@ export interface GateOptions {
|
|
|
62
66
|
admissionWaitSeconds?: number;
|
|
63
67
|
/** GATE-MEM-ADMIT — an injected admission (tests · embedders); config never sets it. Absent = the gate's own pool's. */
|
|
64
68
|
admission?: GateAdmission;
|
|
69
|
+
/** GATE-POD-STALL-FAILFAST — seconds after the source checkout a shard may go without any test output (outside an
|
|
70
|
+
* install-slot wait) before it is stopped as unmeasured (`gatePodStallSeconds`); omitted = {@link GATE_POD_STALL_SECONDS_DEFAULT} · 0 = off. */
|
|
71
|
+
stallSeconds?: number;
|
|
72
|
+
/** GATE-POD-STALL-FAILFAST — injected live Pod log reader / Job stop (tests · embedders); config never sets them.
|
|
73
|
+
* Absent = kubectl on the gate's own pool; an injected pool without `liveLog` runs without the stall watch. */
|
|
74
|
+
liveLog?: (job: { name: string; context?: string }) => string | null;
|
|
75
|
+
/** false = the stop did not take; the watch asks again every poll until it does (void = taken). */
|
|
76
|
+
stopJob?: (job: { name: string; context?: string }) => boolean | void;
|
|
77
|
+
/** How often the stall watch reads the live log (ms); omitted = {@link GATE_POD_STALL_POLL_MS}. */
|
|
78
|
+
stallPollMs?: number;
|
|
65
79
|
/** GATE-LIVE-OBS — the shard state file (`gateShardsPath(version)`) this sweep keeps current; absent = not written. */
|
|
66
|
-
shardsFile?: { path: string; version: string }
|
|
80
|
+
shardsFile?: { path: string; version: string };
|
|
81
|
+
/** GATE-MULTI-HOST — is this pool member reachable (kubectl ns ⊕ ssh)? Injected by tests; absent = the real probe on
|
|
82
|
+
* the gate's own pool · an injected pool without it never marks a member down (unchanged). */
|
|
83
|
+
probeMember?: (member: PodPoolMember) => boolean;
|
|
84
|
+
/** GATE-MULTI-HOST — seconds a placed shard's Job status may stay unreadable before the shard counts as lost with its
|
|
85
|
+
* member; omitted = {@link GATE_MEMBER_UNREADABLE_SECONDS_DEFAULT}. Only a pool of two or more members uses it. */
|
|
86
|
+
memberUnreadableSeconds?: number };
|
|
67
87
|
}
|
|
68
88
|
export interface GateResult {
|
|
69
89
|
outcome: 'ok' | 'regression' | 'error';
|
|
@@ -83,7 +103,9 @@ export interface GateResult {
|
|
|
83
103
|
baselineDeferred?: { cutFiles: number; swept: number };
|
|
84
104
|
durationMs: number;
|
|
85
105
|
error?: string;
|
|
86
|
-
stalledShards?: Array<{ shard: number; files: string[]; reason: 'incomplete' | 'no-output' | 'job-failed' | 'unattributed' | 'timeout'; lastFile?: string; summaryFailures?: number; namedFailures?: number; detail?: string
|
|
106
|
+
stalledShards?: Array<{ shard: number; files: string[]; reason: 'incomplete' | 'no-output' | 'job-failed' | 'exit-code' | 'unattributed' | 'timeout'; lastFile?: string; summaryFailures?: number; namedFailures?: number; detail?: string;
|
|
107
|
+
/** GATE-POD-STALL-FAILFAST — the shard (and its one fresh retry) printed no test output within the stall window: unmeasured. */
|
|
108
|
+
stall?: { seconds: number; sinceCheckoutMs: number } }>;
|
|
87
109
|
partialSummary?: { pass: number; fail: number; errors: number; ran: number; files: number };
|
|
88
110
|
/** GATE-PARTIAL — this verdict re-ran only these files and carried the prior gate's results for the rest. */
|
|
89
111
|
partial?: { priorCommit: string; files: number };
|
|
@@ -255,13 +277,14 @@ export function shardParts(paths: readonly string[], seconds: (file: string) =>
|
|
|
255
277
|
* leaving `install.log`/`install.rc`, `part-<i>.log`/`.rc`/`.junit.xml`, and `shard.timeout` (the part index) when the
|
|
256
278
|
* deadline cut the shard. A part without `.rc` did not finish. The shell itself always exits 0 (as before).
|
|
257
279
|
*/
|
|
258
|
-
export function gateShardShell(o: { parts: readonly (readonly string[])[]; cdpPatterns: readonly string[]; softSeconds: number; cachePrefix: string; installSlots: number; depsDir?: string }): string {
|
|
280
|
+
export function gateShardShell(o: { parts: readonly (readonly string[])[]; cdpPatterns: readonly string[]; softSeconds: number; cachePrefix: string; installSlots: number; depsDir?: string; beatSeconds?: number }): string {
|
|
259
281
|
const run = o.parts.map((part, index) => {
|
|
260
282
|
const ignores = o.cdpPatterns.filter((pattern) => part.includes(pattern)).flatMap((pattern) => ['--path-ignore-patterns', pattern]);
|
|
261
283
|
return `run_part ${index} ${['bun', 'run', 'test:deterministic', ...ignores, ...part.map(asPath)].map(quote).join(' ')}`;
|
|
262
284
|
}).join(' && ');
|
|
263
285
|
return [
|
|
264
286
|
'mkdir -p "$HOME/outbox"; O="$HOME/outbox"',
|
|
287
|
+
...(o.beatSeconds ? gateBeatScript(o.beatSeconds, o.installSlots > 0) : []),
|
|
265
288
|
...(o.cachePrefix ? [o.cachePrefix] : []),
|
|
266
289
|
// Seconds since this container's PID 1 started (the clone ran before this shell) — 0 when /proc cannot tell.
|
|
267
290
|
`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)`,
|
|
@@ -286,9 +309,109 @@ export function gateShardShell(o: { parts: readonly (readonly string[])[]; cdpPa
|
|
|
286
309
|
'true',
|
|
287
310
|
].join('\n');
|
|
288
311
|
}
|
|
312
|
+
/**
|
|
313
|
+
* GATE-POD-STALL-FAILFAST (0.2.24) — the shard shell's live heartbeat on the Pod's stdout (everything else goes to the
|
|
314
|
+
* outbox, which the host sees only after the Job ends). 10-10 0.2.24 gate: the node's containerd was unresponsive, all 28
|
|
315
|
+
* shards ran to their 1697 s deadline with `finishedFiles: 0`, then retries and splits did the same for 2h+ and the node
|
|
316
|
+
* never finished. The beat lets the host see «no test output yet» while the Job runs:
|
|
317
|
+
* `ELANOUS_GATE_BEAT <seq> <slot|install|test> <0|1>` — the last field is 1 once any part log has a byte.
|
|
318
|
+
* It is observation only: the host hashes the shell without it into `runKey`.
|
|
319
|
+
*/
|
|
320
|
+
export function gateBeatScript(beatSeconds: number, installSlots: boolean): string[] {
|
|
321
|
+
return [
|
|
322
|
+
'gate_beat() {',
|
|
323
|
+
' local n=0 st t f',
|
|
324
|
+
' while :; do',
|
|
325
|
+
' n=$((n + 1)); t=0',
|
|
326
|
+
' for f in "$O"/part-*.log; do if [ -s "$f" ]; then t=1; break; fi; done',
|
|
327
|
+
' if [ -f "$O/install.rc" ]; then st=test',
|
|
328
|
+
// A deps miss with no slot line yet = waiting for an install slot (bounded by the slot script; not a stall).
|
|
329
|
+
...(installSlots ? [" elif [ -f \"$O/install.log\" ] && grep -q '^\\[gate\\] deps .* miss' \"$O/install.log\" 2>/dev/null && ! grep -q '^\\[gate\\] install-slot' \"$O/install.log\" 2>/dev/null; then st=slot"] : []),
|
|
330
|
+
' else st=install; fi',
|
|
331
|
+
' echo "ELANOUS_GATE_BEAT $n $st $t"',
|
|
332
|
+
` sleep ${Math.max(1, Math.floor(beatSeconds))} >/dev/null 2>&1 </dev/null`,
|
|
333
|
+
' done',
|
|
334
|
+
'}',
|
|
335
|
+
'gate_beat & gate_beat_pid=$!',
|
|
336
|
+
// Stopped before the shell returns, so no beat line lands between the Job's artifact lines.
|
|
337
|
+
'trap \'kill "$gate_beat_pid" 2>/dev/null; wait "$gate_beat_pid" 2>/dev/null\' EXIT',
|
|
338
|
+
];
|
|
339
|
+
}
|
|
340
|
+
export const GATE_POD_STALL_SECONDS_DEFAULT = 300;
|
|
341
|
+
export const GATE_POD_STALL_POLL_MS = 15_000;
|
|
342
|
+
/** At least ten beats per stall window, never slower than one per 30 s. */
|
|
343
|
+
export const gateStallBeatSeconds = (stallSeconds: number) => Math.min(30, Math.max(1, Math.floor(stallSeconds / 10)));
|
|
344
|
+
export interface ShardLiveLog { checkout: boolean; beat?: { seq: number; stage: 'slot' | 'install' | 'test'; testOutput: boolean } }
|
|
345
|
+
/** What the Pod's live stdout says so far: the source checkout line and the latest beat. */
|
|
346
|
+
export function readShardLiveLog(text: string): ShardLiveLog {
|
|
347
|
+
let checkout = false;
|
|
348
|
+
let beat: ShardLiveLog['beat'];
|
|
349
|
+
for (const line of text.split(/\r?\n/)) {
|
|
350
|
+
if (/^ELANOUS_POD_SOURCE (?:commit|default|pr) /.test(line)) checkout = true;
|
|
351
|
+
const match = /^ELANOUS_GATE_BEAT (\d+) (slot|install|test) ([01])\s*$/.exec(line);
|
|
352
|
+
if (match) beat = { seq: Number(match[1]), stage: match[2] as 'slot' | 'install' | 'test', testOutput: match[3] === '1' };
|
|
353
|
+
}
|
|
354
|
+
return { checkout, ...(beat ? { beat } : {}) };
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* GATE-POD-STALL-FAILFAST — watches one running shard Job. Armed once the source checkout line is seen; disarmed for good
|
|
358
|
+
* the moment any test output exists (that shard then behaves exactly as before). Stalled = `stallSeconds` since the
|
|
359
|
+
* checkout with no test output, not counting time the beat reported an install-slot wait. A silent shell (no beat at all)
|
|
360
|
+
* counts like any other stage. Only successful reads decide; a read failure never stalls a shard. `graceMs` covers the
|
|
361
|
+
* beat's report lag: output that appeared just inside the window is reported by the next beat before the stop decides.
|
|
362
|
+
*/
|
|
363
|
+
export function watchShardStall(o: { stallSeconds: number; pollMs: number; read: () => string | null; onStall: (sinceCheckoutMs: number) => void; now?: () => number; graceMs?: number;
|
|
364
|
+
/** Stops the Job; false = not taken, asked again every poll until it is or the watch ends (a stalled Job must not run to its deadline). */
|
|
365
|
+
stopJob?: () => boolean | void }): { stop(): void; stalled(): { sinceCheckoutMs: number } | undefined } {
|
|
366
|
+
const now = o.now ?? Date.now;
|
|
367
|
+
let checkoutAt: number | undefined;
|
|
368
|
+
let windowStart: number | undefined;
|
|
369
|
+
let lastSeq: number | undefined;
|
|
370
|
+
let hit: { sinceCheckoutMs: number } | undefined;
|
|
371
|
+
let done = false;
|
|
372
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
373
|
+
let ended = false;
|
|
374
|
+
const askStop = () => {
|
|
375
|
+
timer = undefined;
|
|
376
|
+
if (ended) return;
|
|
377
|
+
let taken: boolean | void = false;
|
|
378
|
+
try { taken = o.stopJob ? o.stopJob() : true; } catch { taken = false; }
|
|
379
|
+
if (taken === false && !ended) timer = setTimeout(askStop, o.pollMs);
|
|
380
|
+
};
|
|
381
|
+
const tick = () => {
|
|
382
|
+
timer = undefined;
|
|
383
|
+
if (done) return;
|
|
384
|
+
let text: string | null = null;
|
|
385
|
+
try { text = o.read(); } catch { text = null; }
|
|
386
|
+
if (done) return;
|
|
387
|
+
if (text !== null) {
|
|
388
|
+
const t = now();
|
|
389
|
+
const live = readShardLiveLog(text);
|
|
390
|
+
if (live.beat?.testOutput) { done = true; return; }
|
|
391
|
+
if (live.checkout && checkoutAt === undefined) { checkoutAt = t; windowStart = t; }
|
|
392
|
+
if (live.beat && live.beat.seq !== lastSeq) {
|
|
393
|
+
lastSeq = live.beat.seq;
|
|
394
|
+
if (live.beat.stage === 'slot' && windowStart !== undefined) windowStart = t;
|
|
395
|
+
}
|
|
396
|
+
if (checkoutAt !== undefined && windowStart !== undefined && t - windowStart >= o.stallSeconds * 1000 + (o.graceMs ?? 0)) {
|
|
397
|
+
done = true;
|
|
398
|
+
hit = { sinceCheckoutMs: t - checkoutAt };
|
|
399
|
+
o.onStall(hit.sinceCheckoutMs);
|
|
400
|
+
askStop();
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
timer = setTimeout(tick, o.pollMs);
|
|
405
|
+
};
|
|
406
|
+
timer = setTimeout(tick, o.pollMs);
|
|
407
|
+
return { stop: () => { done = true; ended = true; if (timer) clearTimeout(timer); }, stalled: () => hit };
|
|
408
|
+
}
|
|
289
409
|
/** Removes bun's own summary lines so one summary over several runs can follow (same rule as the sweep's merge). */
|
|
290
410
|
const stripSummary = (output: string) => output.replace(/(?:^|\n)\s*\d+ (?:pass|fail|errors?)\s*(?=\n|$)/g, '\n').replace(/Ran \d+ tests? across \d+ files?\.?/g, '');
|
|
291
411
|
export interface ShardArtifacts {
|
|
412
|
+
/** GATE-POD-STALL-FAILFAST (OP) — every unfinished part ended with a complete clean summary (0 fail · 0 error) but an exit
|
|
413
|
+
* other than 0/1: an exit-code anomaly (a test left process.exitCode set), with those parts' files. */
|
|
414
|
+
exitCodeOnly?: string[];
|
|
292
415
|
/** The verdict text: install log ⊕ every finished part (its summary removed) ⊕ one summary over the finished parts. */
|
|
293
416
|
output?: string;
|
|
294
417
|
junit?: string;
|
|
@@ -315,6 +438,8 @@ export function readShardArtifacts(dir: string, parts: readonly (readonly string
|
|
|
315
438
|
if (installRc !== 0) return { output: install, ...(installRc !== undefined ? { rc: installRc } : {}), timedOut, finished: [] };
|
|
316
439
|
const good: Array<{ log: string; junit?: string; rc: 0 | 1; files: readonly string[] }> = [];
|
|
317
440
|
const bad: string[] = [];
|
|
441
|
+
const exitAnomaly: string[] = [];
|
|
442
|
+
let otherBad = false;
|
|
318
443
|
let badRc: number | undefined;
|
|
319
444
|
parts.forEach((files, index) => {
|
|
320
445
|
const log = read(`part-${index}.log`);
|
|
@@ -326,6 +451,9 @@ export function readShardArtifacts(dir: string, parts: readonly (readonly string
|
|
|
326
451
|
return;
|
|
327
452
|
}
|
|
328
453
|
if (log !== undefined) bad.push(`# part ${index} (${rc === undefined ? 'unfinished' : `rc=${rc}`})\n${log}`);
|
|
454
|
+
if (clean !== undefined && rc !== undefined && rc !== 0 && rc !== 1 && Number(lastMatch(clean, /Ran (\d+) tests? across ([1-9]\d*) files?/)?.[1] ?? 0) > 0
|
|
455
|
+
&& summaryCount(clean, 'fail') === 0 && !(summaryCount(clean, 'error') > 0) && !/^# Unhandled error between tests/m.test(clean)) exitAnomaly.push(...files);
|
|
456
|
+
else otherBad = true;
|
|
329
457
|
// A finished part without a readable summary is not a verdict: its exit, or 2 when it claimed 0|1.
|
|
330
458
|
if (rc !== undefined && badRc === undefined) badRc = rc === 0 || rc === 1 ? 2 : rc;
|
|
331
459
|
});
|
|
@@ -346,6 +474,7 @@ export function readShardArtifacts(dir: string, parts: readonly (readonly string
|
|
|
346
474
|
timedOut: !complete && timedOut && badRc === undefined,
|
|
347
475
|
finished: good.flatMap((part) => [...part.files]),
|
|
348
476
|
...(bad.length ? { unfinishedLog: bad.join('\n') } : {}),
|
|
477
|
+
...(exitAnomaly.length && !otherBad ? { exitCodeOnly: exitAnomaly } : {}),
|
|
349
478
|
};
|
|
350
479
|
}
|
|
351
480
|
|
|
@@ -504,7 +633,11 @@ export function readIsolatedRun(run: CommandResult, label: string, file: string)
|
|
|
504
633
|
|
|
505
634
|
class StalledPodShards extends Error {
|
|
506
635
|
constructor(readonly stalledShards: NonNullable<GateResult['stalledShards']>, readonly partialSummary: NonNullable<GateResult['partialSummary']>) {
|
|
507
|
-
|
|
636
|
+
const unmeasured = stalledShards.filter((item) => item.stall);
|
|
637
|
+
const exitCode = stalledShards.filter((item) => item.reason === 'exit-code');
|
|
638
|
+
super(`pod sweep incomplete: stalled shards ${stalledShards.map(({ shard }) => shard).join(', ')}`
|
|
639
|
+
+ (unmeasured.length ? ` (unmeasured: no test output within ${unmeasured[0]!.stall!.seconds}s of checkout — shards ${[...new Set(unmeasured.map(({ shard }) => shard))].join(', ')})` : '')
|
|
640
|
+
+ (exitCode.length ? ` (exit-code anomaly: 0 fail but exit ≠ 0/1 — ${[...new Set(exitCode.flatMap(({ files }) => files))].join(', ')})` : ''));
|
|
508
641
|
}
|
|
509
642
|
}
|
|
510
643
|
|
|
@@ -570,6 +703,18 @@ export const GATE_ADMISSION_WAIT_SECONDS_DEFAULT = 300;
|
|
|
570
703
|
export type GateAdmissionOutcome = 'granted' | 'admission-timeout' | 'admission-error' | 'off';
|
|
571
704
|
export interface GateAdmissionResult { outcome: GateAdmissionOutcome; waitedMs: number; reason?: string; release: () => void }
|
|
572
705
|
export const gateAdmissionWaitText = (reading: string | null) => `메모리 여유 대기(admission) · ${reading ?? '측정 전'}`.slice(0, 160);
|
|
706
|
+
/**
|
|
707
|
+
* GATE-MULTI-HOST (0.2.25 · 대표) — the gate pool is a host roster (`release.loop.gatePodPool` =
|
|
708
|
+
* `pool-node-b@node-b:N,pool-node-c@node-c:M,…`); shards spread over it by free share (selfCapped). A member that fails a
|
|
709
|
+
* shard's launch or goes silent mid-Job is probed; if it is unreachable it leaves the roster for the rest of this gate and
|
|
710
|
+
* the shard runs again on the others — one dead host no longer stops the gate. One-member pools keep the old behaviour.
|
|
711
|
+
*/
|
|
712
|
+
export const GATE_MEMBER_UNREADABLE_SECONDS_DEFAULT = 600;
|
|
713
|
+
export function defaultGateMemberProbe(member: PodPoolMember): boolean {
|
|
714
|
+
if (!checkPodPool([member], defaultKubectl).ok) return false;
|
|
715
|
+
return !member.sshHost || defaultRemoteRun(member.sshHost, 'true').status === 0;
|
|
716
|
+
}
|
|
717
|
+
|
|
573
718
|
export function poolGateAdmission(pool: PodPoolScheduler): GateAdmission {
|
|
574
719
|
return { acquire: (signal) => pool.acquireAdmission(signal, undefined, { gate: true }), waitReason: () => pool.waitReason() };
|
|
575
720
|
}
|
|
@@ -694,9 +839,50 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
694
839
|
// An injected pool (tests) runs without admission unless `pod.admission` is passed; the gate's own pool asks it.
|
|
695
840
|
const gateAdmission = pod.admission ?? (poolOverride ? undefined : poolGateAdmission(poolScheduler));
|
|
696
841
|
debug.log('release-loop.gate', 'pod-admission', { waitSeconds: admissionWaitSeconds, source: pod.admissionWaitSeconds !== undefined ? 'config' : 'default', enabled: !!gateAdmission && admissionWaitSeconds > 0 });
|
|
842
|
+
// GATE-POD-STALL-FAILFAST: a shard with no test output within the stall window is stopped and recorded unmeasured.
|
|
843
|
+
const stallSeconds = pod.stallSeconds ?? GATE_POD_STALL_SECONDS_DEFAULT;
|
|
844
|
+
const stallPollMs = pod.stallPollMs ?? GATE_POD_STALL_POLL_MS;
|
|
845
|
+
if (!Number.isFinite(stallSeconds) || stallSeconds < 0 || !Number.isFinite(stallPollMs) || stallPollMs <= 0) throw new Error('invalid pod sweep options');
|
|
846
|
+
// An injected pool (tests) runs without the watch unless `pod.liveLog` is passed — never a real kubectl from a test.
|
|
847
|
+
const liveLog = pod.liveLog ?? (poolOverride ? undefined : (job: { name: string; context?: string }) => {
|
|
848
|
+
if (!job.context) return null;
|
|
849
|
+
const logs = defaultKubectl(['--context', job.context, '-n', 'elanous-test', 'logs', `job/${job.name}`, '-c', 'child', '--tail=400']);
|
|
850
|
+
return logs.status === 0 ? logs.stdout : null;
|
|
851
|
+
});
|
|
852
|
+
const stopJob = pod.stopJob ?? ((job: { name: string; context?: string }) => {
|
|
853
|
+
// Not `delete job`: runPodCommand would poll a missing Job forever. A 1 s deadline makes the Job fail now and
|
|
854
|
+
// runPodCommand returns through its normal path (logs · exit code · Secret cleanup).
|
|
855
|
+
if (!job.context) return false;
|
|
856
|
+
const patched = defaultKubectl(['--context', job.context, '-n', 'elanous-test', 'patch', 'job', job.name, '--type=merge', '-p', JSON.stringify({ spec: { activeDeadlineSeconds: 1 } })]);
|
|
857
|
+
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' });
|
|
858
|
+
return patched.status === 0;
|
|
859
|
+
});
|
|
860
|
+
const stallWatch = stallSeconds > 0 && !!liveLog;
|
|
861
|
+
debug.log('release-loop.gate', 'pod-stall-watch', { stallSeconds, source: pod.stallSeconds !== undefined ? 'config' : 'default', enabled: stallWatch });
|
|
862
|
+
// GATE-MULTI-HOST: members found unreachable during this gate — skipped for placement while any other member is left.
|
|
863
|
+
const downMembers = new Set<string>();
|
|
864
|
+
const multiHost = poolScheduler.members.length > 1;
|
|
865
|
+
const probeMember = pod.probeMember ?? (poolOverride ? undefined : defaultGateMemberProbe);
|
|
866
|
+
const memberUnreadableSeconds = pod.memberUnreadableSeconds ?? GATE_MEMBER_UNREADABLE_SECONDS_DEFAULT;
|
|
867
|
+
if (!Number.isFinite(memberUnreadableSeconds) || memberUnreadableSeconds < 0) throw new Error('invalid pod sweep options');
|
|
868
|
+
debug.log('release-loop.gate', 'pod-roster', { members: poolScheduler.members.map((m) => ({ context: m.context, host: m.sshHost ?? null, capacity: m.capacity })),
|
|
869
|
+
dropped: ready?.dropped ?? [], multiHost, probe: !!probeMember });
|
|
870
|
+
/** True when the shard's member is (now) known unreachable — a newly found one is logged and leaves the roster. */
|
|
871
|
+
const memberLost = (context: string, shard: number, error: string): boolean => {
|
|
872
|
+
if (downMembers.has(context)) return true;
|
|
873
|
+
const member = poolScheduler.members.find((m) => m.context === context);
|
|
874
|
+
if (!multiHost || !probeMember || !member) return false;
|
|
875
|
+
let reachable: boolean;
|
|
876
|
+
try { reachable = probeMember(member); } catch { reachable = false; }
|
|
877
|
+
if (reachable) return false;
|
|
878
|
+
downMembers.add(context);
|
|
879
|
+
debug.log('release-loop.gate', 'pod-member-down', { member: context, host: member.sshHost ?? null, shard, error: error.slice(0, 300),
|
|
880
|
+
remaining: poolScheduler.members.filter((m) => !downMembers.has(m.context)).map((m) => m.context) }, { level: 'warn' });
|
|
881
|
+
return true;
|
|
882
|
+
};
|
|
697
883
|
const jobLogTail = (job: PodCommandResult): string => {
|
|
698
884
|
if (podLogTail) return podLogTail(job);
|
|
699
|
-
for (const member of poolScheduler.members) {
|
|
885
|
+
for (const member of poolScheduler.members.filter((m) => !downMembers.has(m.context))) {
|
|
700
886
|
const logs = defaultKubectl(['--context', member.context, '-n', 'elanous-test', 'logs', `job/${job.job}`, '-c', 'child', '--tail=200']);
|
|
701
887
|
if (logs.status === 0 && logs.stdout) return logs.stdout;
|
|
702
888
|
}
|
|
@@ -734,25 +920,30 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
734
920
|
/** The gate's scheduler, seen through one shard: a slot wait and the Pod start become row states. */
|
|
735
921
|
// GATE-MEM-ADMIT: `placed` releases the shard's admission once a member is chosen — from then on the Job is
|
|
736
922
|
// Pending/Running in the cluster and the next admission reading counts it, so holding the lease would count it twice.
|
|
737
|
-
const trackedPool = (shard: number, retry: boolean, placed: () => void = () => {}): PodPoolScheduler => ({
|
|
923
|
+
const trackedPool = (shard: number, retry: boolean, placed: (member: NonNullable<Awaited<ReturnType<PodPoolScheduler['tryAcquire']>>>) => void = () => {}): PodPoolScheduler => ({
|
|
738
924
|
members: poolScheduler.members,
|
|
739
|
-
tryAcquire: async (...
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
925
|
+
tryAcquire: async (...[skip, onEmpty, mode]: Parameters<PodPoolScheduler['tryAcquire']>) => {
|
|
926
|
+
// GATE-MULTI-HOST: a lost member is skipped while another member is left (all lost = the old behaviour).
|
|
927
|
+
const anyLeft = poolScheduler.members.some((m) => !downMembers.has(m.context));
|
|
928
|
+
const member = await poolScheduler.tryAcquire(downMembers.size && anyLeft ? new Set([...(skip ?? []), ...downMembers]) : skip, onEmpty, mode);
|
|
929
|
+
if (member) placed(member);
|
|
930
|
+
if (member) markShard(shard, { state: retry ? 'retry' : 'running', startedAt: board[shard]?.startedAt ?? new Date().toISOString(), host: member.context }, ['waitReason']);
|
|
743
931
|
else markShard(shard, { waitReason: 'Pod 자리 대기' });
|
|
744
932
|
return member;
|
|
745
933
|
},
|
|
746
934
|
release: (member: Parameters<PodPoolScheduler['release']>[0]) => poolScheduler.release(member),
|
|
747
935
|
}) as unknown as PodPoolScheduler;
|
|
748
|
-
const runShard = async (paths: string[], shard: number, depth = 0, branch = '', retriedNoOutput = false, fresh = pod.freshShards === true, parentAttempt?: string): Promise<{ runs: ShardRun[]; stalled: NonNullable<GateResult['stalledShards']> }> => {
|
|
936
|
+
const runShard = async (paths: string[], shard: number, depth = 0, branch = '', retriedNoOutput = false, fresh = pod.freshShards === true, parentAttempt?: string, hostRetries = 0): Promise<{ runs: ShardRun[]; stalled: NonNullable<GateResult['stalledShards']> }> => {
|
|
749
937
|
// 파일별 소요는 junit 으로 남긴다 — 콘솔 요약(판정 원천)은 그대로이고, 느린 시험 목록(K10 D4)·계층 분리(D2)의 자가 된다.
|
|
750
938
|
// GATE-TIMEOUT-HEAL: parts (each with its own junit) under a soft deadline inside this shard's own Job deadline.
|
|
751
939
|
const shardDeadline = shardDeadlineSeconds(estimated(paths), deadlineSeconds);
|
|
752
940
|
const parts = shardParts(paths, (file) => estimated([file]));
|
|
753
941
|
const memoryLimit = paths.length === 1 && (depth >= 1 || heavy.has(paths[0]!)) ? POD_ISOLATION_MEMORY_LIMIT : undefined;
|
|
754
|
-
const
|
|
755
|
-
const
|
|
942
|
+
const shellOptions = { parts, cdpPatterns, softSeconds: softDeadlineSeconds(shardDeadline), cachePrefix, installSlots };
|
|
943
|
+
const keyShell = gateShardShell(shellOptions);
|
|
944
|
+
// The stall beat only reports; runKey keeps hashing the shell without it, so earlier results stay reusable.
|
|
945
|
+
const podShell = stallWatch ? gateShardShell({ ...shellOptions, beatSeconds: gateStallBeatSeconds(stallSeconds) }) : keyShell;
|
|
946
|
+
const runKey = createHash('sha256').update(JSON.stringify({ pool: pod.pool, podShell: keyShell, memoryLimit: memoryLimit ?? null, deadlineSeconds: shardDeadline, bunCache: bunCache ?? null })).digest('hex');
|
|
756
947
|
let start = Date.now();
|
|
757
948
|
let admitted: GateAdmissionResult | undefined;
|
|
758
949
|
let output: string | undefined;
|
|
@@ -761,9 +952,14 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
761
952
|
let jobExitCode: number | undefined;
|
|
762
953
|
let lastFile: string | undefined;
|
|
763
954
|
let jobFailed = false;
|
|
955
|
+
let jobError = '';
|
|
956
|
+
/** GATE-MULTI-HOST — the member this shard's Job was placed on (recorded in the shard json / log / board). */
|
|
957
|
+
let placedOn: string | undefined;
|
|
764
958
|
let timedOut = false;
|
|
765
959
|
let finished: string[] = [];
|
|
766
960
|
let unfinishedLog: string | undefined;
|
|
961
|
+
let exitCodeOnly: string[] | undefined;
|
|
962
|
+
let stall: { seconds: number; sinceCheckoutMs: number } | undefined;
|
|
767
963
|
// The way down a previous attempt took wins over its own log: that log was not a verdict, or it would have no children.
|
|
768
964
|
// The split this shard would take if it failed here — the same rule as the split code below (isolation at depth 2 ·
|
|
769
965
|
// cap chunks for a big root shard · halves otherwise · none for one file).
|
|
@@ -796,19 +992,35 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
796
992
|
admitted.outcome === 'granted' ? undefined : { level: 'warn' });
|
|
797
993
|
// The admission wait is not the shard's run time: deadline/timeout judgment and the measured duration start here.
|
|
798
994
|
start = Date.now();
|
|
995
|
+
const jobName = `gate-${randomUUID()}`;
|
|
996
|
+
// The member's context is known once the Job is placed; the live log is read (and the Job stopped) there.
|
|
997
|
+
let jobContext: string | undefined;
|
|
998
|
+
const liveJob = () => ({ name: jobName, ...(jobContext ? { context: jobContext } : {}) });
|
|
999
|
+
// Grace = two beats ⊕ one poll: test output that appeared inside the window is seen before the stop.
|
|
1000
|
+
const watch = stallWatch ? watchShardStall({ stallSeconds, pollMs: stallPollMs, graceMs: 2 * gateStallBeatSeconds(stallSeconds) * 1000 + stallPollMs, read: () => liveLog!(liveJob()),
|
|
1001
|
+
onStall: (sinceCheckoutMs) => debug.log('release-loop.gate', 'pod-shard-stall', { shard, branch, stallSeconds, sinceCheckoutMs }, { level: 'warn' }),
|
|
1002
|
+
stopJob: () => {
|
|
1003
|
+
try { return stopJob(liveJob()); }
|
|
1004
|
+
catch (error) { debug.log('release-loop.gate', 'pod-shard-stall-stop-failed', { job: jobName, error: String(error) }, { level: 'warn' }); return false; }
|
|
1005
|
+
} }) : undefined;
|
|
1006
|
+
const placed = admitted.release;
|
|
799
1007
|
const job = await podCommand({
|
|
800
|
-
pool: pod.pool, poolScheduler: trackedPool(shard, depth > 0 || branch !== '',
|
|
1008
|
+
pool: pod.pool, poolScheduler: trackedPool(shard, depth > 0 || branch !== '', (member) => { jobContext = member.context; placedOn = member.context; placed(); }), clone: true, source: { kind: 'commit', sha: commit }, deadlineSeconds: shardDeadline,
|
|
1009
|
+
...(multiHost ? { statusUnreadableMs: memberUnreadableSeconds * 1000 } : {}),
|
|
801
1010
|
...(bunCache ? { bunCache } : {}),
|
|
802
1011
|
...(installSlots > 0 ? { installSlots: POD_INSTALL_SLOTS_HOST_PATH } : {}),
|
|
803
1012
|
...(pod.cpu ? { cpu: pod.cpu } : {}),
|
|
804
1013
|
// 실패 뒤 다시 도는 파일 하나짜리 Job 은 메모리 한도를 올린다 — 16Gi 에선 무거운 한 파일이 혼자서도 OOM 이었다(09-30 `unwired-exports`).
|
|
805
1014
|
...(memoryLimit ? { memoryLimit } : {}),
|
|
806
|
-
name:
|
|
1015
|
+
name: jobName,
|
|
807
1016
|
command: ['bash', '-lc', podShell],
|
|
808
|
-
});
|
|
1017
|
+
}).finally(() => watch?.stop());
|
|
809
1018
|
jobExitCode = job.exitCode;
|
|
810
1019
|
const read = readShardArtifacts(job.artifactsDir, parts);
|
|
811
|
-
({ output, junit, rc, timedOut, finished, unfinishedLog } = read);
|
|
1020
|
+
({ output, junit, rc, timedOut, finished, unfinishedLog, exitCodeOnly } = read);
|
|
1021
|
+
// A stall counts only when the Job also left no verdict (a failed stop can let a late shard finish after all).
|
|
1022
|
+
const stalledAt = watch?.stalled();
|
|
1023
|
+
if (stalledAt && !finished.length && rc === undefined) stall = { seconds: stallSeconds, sinceCheckoutMs: stalledAt.sinceCheckoutMs };
|
|
812
1024
|
// A Job the cluster cut at its own deadline leaves no outbox at all: past the deadline with nothing is a timeout too.
|
|
813
1025
|
if (!timedOut && output === undefined && job.exitCode !== 0 && Date.now() - start >= shardDeadline * 1000) timedOut = true;
|
|
814
1026
|
lastFile = output === undefined && unfinishedLog === undefined ? undefined : lastStartedTestFile(`${output ?? ''}\n${unfinishedLog ?? ''}`, paths);
|
|
@@ -816,12 +1028,16 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
816
1028
|
try { lastFile = lastStartedTestFile(jobLogTail(job).split(/\r?\n/).slice(-200).join('\n'), paths); }
|
|
817
1029
|
catch { /* Log retrieval is diagnostic; do not hide the stalled shard. */ }
|
|
818
1030
|
}
|
|
819
|
-
} catch {
|
|
1031
|
+
} catch (error) {
|
|
820
1032
|
jobFailed = true;
|
|
1033
|
+
jobError = error instanceof Error ? error.message : String(error);
|
|
821
1034
|
}
|
|
822
1035
|
// A launch that failed before placement still gives its admission back (release is idempotent).
|
|
823
1036
|
admitted?.release();
|
|
824
1037
|
const durationMs = Date.now() - start;
|
|
1038
|
+
const fileSeconds = junit === undefined ? undefined : Object.fromEntries(readJunitFileSeconds(junit));
|
|
1039
|
+
const testSeconds = fileSeconds === undefined ? undefined : Object.values(fileSeconds).reduce((sum, seconds) => sum + seconds, 0);
|
|
1040
|
+
const podOverheadSeconds = testSeconds === undefined ? undefined : Math.max(0, durationMs / 1000 - testSeconds);
|
|
825
1041
|
// A reused shard keeps its files as they are — rewriting would replace the measured durationMs the next release plans by.
|
|
826
1042
|
if (logDir && !reused && !descent) {
|
|
827
1043
|
mkdirSync(logDir, { recursive: true });
|
|
@@ -837,13 +1053,16 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
837
1053
|
const unfinishedDestination = join(logDir, `pod-${shard}${branch}.unfinished.log`);
|
|
838
1054
|
if (unfinishedLog !== undefined) writeFileSync(unfinishedDestination, unfinishedLog, { mode: 0o600 });
|
|
839
1055
|
else rmSync(unfinishedDestination, { force: true });
|
|
840
|
-
writeFileSync(join(logDir, `pod-${shard}${branch}.json`), JSON.stringify({ durationMs, rc: rc ?? null,
|
|
1056
|
+
writeFileSync(join(logDir, `pod-${shard}${branch}.json`), JSON.stringify({ durationMs, rc: rc ?? null,
|
|
1057
|
+
...(stall ? { reason: 'stall', finishedFiles: 0, stallSeconds: stall.seconds, sinceCheckoutMs: stall.sinceCheckoutMs } : timedOut ? { reason: 'timeout', finishedFiles: finished.length } : {}), files: paths, plannedSeconds: estimated(paths), deadlineSeconds: shardDeadline, shardCount: shards.length, commit, runKey, attemptId, ...(parentAttempt ? { parentAttempt } : {}), jobExitCode: jobExitCode ?? null, jobFailed, ...(placedOn ? { host: placedOn } : {}),
|
|
1058
|
+
...(fileSeconds !== undefined ? { fileSeconds, testSeconds, podOverheadSeconds } : {}),
|
|
841
1059
|
...(admitted && admitted.outcome !== 'off' ? { admission: admitted.outcome, admissionWaitMs: admitted.waitedMs } : {}) }) + '\n', { mode: 0o600 });
|
|
842
1060
|
}
|
|
843
1061
|
const installSeconds = parseInstallSeconds(output ?? '');
|
|
844
1062
|
// GATE-TIMEOUT-HEAL ③: a cut shard is «reason: timeout» (with the files that still finished), not a bare rc=null.
|
|
845
|
-
if (!reused && !descent) debug.log('release-loop.gate', 'pod-shard', { shard, files: paths, durationMs, rc: rc ?? null, attempt: depth + 1, installSeconds, gateDeps: parseGateDeps(output ?? ''), deadlineSeconds: shardDeadline, parts: parts.length,
|
|
846
|
-
...(
|
|
1063
|
+
if (!reused && !descent) debug.log('release-loop.gate', 'pod-shard', { shard, files: paths, durationMs, rc: rc ?? null, attempt: depth + 1, installSeconds, gateDeps: parseGateDeps(output ?? ''), deadlineSeconds: shardDeadline, parts: parts.length, ...(placedOn ? { host: placedOn } : {}),
|
|
1064
|
+
...(testSeconds !== undefined ? { testSeconds } : {}),
|
|
1065
|
+
...(stall ? { reason: 'stall', finishedFiles: 0, stallSeconds: stall.seconds } : timedOut ? { reason: 'timeout', finishedFiles: finished.length } : {}) });
|
|
847
1066
|
if (!reused && !descent) markShard(shard, { ...(installSeconds !== null ? { installSec: installSeconds } : {}), ...(rc !== undefined ? { rc } : {}), ...(timedOut ? { state: 'timeout' as const } : {}) });
|
|
848
1067
|
let clean = output?.replace(/\x1b\[[0-9;]*[A-Za-z]/g, '');
|
|
849
1068
|
const ran = clean && lastMatch(clean, /Ran (\d+) tests? across (\d+) files?/);
|
|
@@ -854,7 +1073,13 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
854
1073
|
let namedFailures: number | undefined;
|
|
855
1074
|
let unattributedDetail: string | undefined;
|
|
856
1075
|
if (descent === 'split') reason = 'incomplete';
|
|
1076
|
+
// GATE-POD-STALL-FAILFAST: stopped with no test output — the existing «no output» reason (one fresh retry).
|
|
1077
|
+
else if (stall) reason = 'no-output';
|
|
857
1078
|
else if (timedOut) reason = 'timeout';
|
|
1079
|
+
// GATE-POD-STALL-FAILFAST (OP): a complete summary with 0 fail · 0 error but an exit other than 0/1 is an exit-code
|
|
1080
|
+
// anomaly (0.2.23: a test left process.exitCode set → «0 fail · rc 3» read as a stalled shard). Same split/isolation
|
|
1081
|
+
// path as job-failed — only the label differs, so the verdict names the file instead of saying «stalled».
|
|
1082
|
+
else if (!jobFailed && exitCodeOnly?.length && rc !== undefined && rc !== 0 && rc !== 1) reason = 'exit-code';
|
|
858
1083
|
else if (jobFailed || (jobExitCode !== undefined && jobExitCode !== 0) || (rc !== undefined && rc !== 0 && rc !== 1)) reason = 'job-failed';
|
|
859
1084
|
else if (!clean || rc === undefined) reason = 'no-output';
|
|
860
1085
|
else if (!ran || !Number.isFinite(passes) || !Number.isFinite(fails)
|
|
@@ -891,6 +1116,13 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
891
1116
|
debug.log('release-loop.gate', 'pod-shard-resume-miss', { shard, branch, reason });
|
|
892
1117
|
return runShard(paths, shard, depth, branch, retriedNoOutput, true, parentAttempt);
|
|
893
1118
|
}
|
|
1119
|
+
// GATE-MULTI-HOST: the Job failed because its member is gone (launch failed · status unreadable) — not the shard's
|
|
1120
|
+
// fault, so no split: the same files run again on the members left. Bounded by the roster size.
|
|
1121
|
+
if (jobFailed && placedOn && hostRetries < poolScheduler.members.length && memberLost(placedOn, shard, jobError)
|
|
1122
|
+
&& poolScheduler.members.some((m) => !downMembers.has(m.context))) {
|
|
1123
|
+
debug.log('release-loop.gate', 'pod-shard-host-retry', { shard, branch, files: paths.length, lostMember: placedOn, attempt: hostRetries + 1 }, { level: 'warn' });
|
|
1124
|
+
return runShard(paths, shard, depth, `${branch}-h${hostRetries}`, retriedNoOutput, undefined, attemptId, hostRetries + 1);
|
|
1125
|
+
}
|
|
894
1126
|
// GATE-TIMEOUT-HEAL ④: a Pod that died before any test ran (no outbox · jobExitCode≠0 — 0.2.19: isolated retries
|
|
895
1127
|
// ending with exit 5, the source checkout) gets the same one fresh retry as «no output» instead of stalling the sweep.
|
|
896
1128
|
// 137 (killed · OOM) keeps the isolation path: re-running an OOM file only re-measures the same kill.
|
|
@@ -901,6 +1133,9 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
901
1133
|
debug.log('release-loop.gate', 'pod-shard-retry', { shard, files: paths, reason, outcome: retry.stalled[0]?.reason ?? 'ok' });
|
|
902
1134
|
return retry;
|
|
903
1135
|
}
|
|
1136
|
+
// GATE-POD-STALL-FAILFAST: a stall after the fresh retry is unmeasured — no split, no isolation: smaller Pods on the
|
|
1137
|
+
// same stalled node only re-measure the stall (10-10: retries and splits repeated it for 2h+).
|
|
1138
|
+
if (stall && reason === 'no-output') return { runs: [], stalled: [{ shard, files: paths, reason, stall }] };
|
|
904
1139
|
if (reason) {
|
|
905
1140
|
// GATE-SKIP-KNOWN-RETRY — a shard whose summary is complete but whose failures could not be read back (unattributed)
|
|
906
1141
|
// and whose every named failure is one the previous release already had is not re-run: re-running only re-measures
|
|
@@ -992,7 +1227,11 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
992
1227
|
const total = measured.reduce((sum, run) => ({ pass: sum.pass + run.pass, fail: sum.fail + run.fail,
|
|
993
1228
|
errors: sum.errors + run.errors, ran: sum.ran + run.ran, files: sum.files + run.files }),
|
|
994
1229
|
{ pass: 0, fail: 0, errors: 0, ran: 0, files: 0 });
|
|
995
|
-
|
|
1230
|
+
const unmeasured = stalledShards.filter((item) => item.stall);
|
|
1231
|
+
if (unmeasured.length) debug.log('release-loop.gate', 'pod-sweep-unmeasured', { shards: [...new Set(unmeasured.map((item) => item.shard))], files: unmeasured.reduce((sum, item) => sum + item.files.length, 0),
|
|
1232
|
+
stallSeconds, measuredFiles: total.files }, { level: 'warn' });
|
|
1233
|
+
// An unmeasured (stalled) shard is never handed to the host re-run as an environment stall: the gate fails now.
|
|
1234
|
+
if (stalledShards.some((item) => item.reason !== 'no-output' || item.files.length !== 1 || item.stall)) throw new StalledPodShards(stalledShards, total);
|
|
996
1235
|
if (total.ran === 0 && total.errors === 0 && !stalledShards.length) throw new Error('sweep incomplete: no tests ran');
|
|
997
1236
|
const outputs = measured.map((run) => run.output.replace(/(?:^|\n)\s*\d+ (?:pass|fail|errors?)\s*(?=\n|$)/g, '\n').replace(/Ran \d+ tests? across \d+ files?\.?/g, ''));
|
|
998
1237
|
return { rc: total.fail + total.errors ? 1 : 0, output: outputs.join('\n') + `\n${total.pass} pass\n${total.fail} fail\n${total.errors} errors\nRan ${total.ran} tests across ${total.files} files.\n`,
|
|
@@ -1256,8 +1495,9 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
1256
1495
|
if (trustedKnown) knownFailures = [...trustedKnown.failures, ...(trustedKnown.errors ?? []), ...loadEnvKnownFailures(repo)];
|
|
1257
1496
|
} catch (error) { debug.log('release-loop.gate', 'known-failures-unavailable', { error: String(error) }); }
|
|
1258
1497
|
}
|
|
1498
|
+
const durationSource = join(ledger, 'release', opts.baselineVersion, 'gate-logs', 'cut');
|
|
1259
1499
|
const cutRun = await runner.sweep(cutTree, join(root, 'release', opts.version, 'gate-logs', partial ? 'cut-partial' : 'cut'),
|
|
1260
|
-
opts.pod ? { ...opts.pod, durationSource
|
|
1500
|
+
opts.pod ? { ...opts.pod, durationSource, ...(knownFailures.length ? { knownFailures } : {}),
|
|
1261
1501
|
// GATE-LIVE-OBS: the release ledger's shards.json (UX #24782) — never the run tree's universe; an explicit
|
|
1262
1502
|
// --ledger-root (tests) keeps it in that ledger.
|
|
1263
1503
|
shardsFile: { path: opts.ledgerRoot ? gateShardsPath(opts.version, opts.ledgerRoot) : gateShardsPath(opts.version), version: opts.version } } : undefined,
|
|
@@ -1346,7 +1586,7 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
1346
1586
|
// K9b — the baseline is swept on the same Pod pool as the cut, not on this host (0.2.6: an hour of local baseline
|
|
1347
1587
|
// after a 23-minute Pod cut).
|
|
1348
1588
|
const baseRun = await runner.sweep(baseTree!, join(root, 'release', opts.version, 'gate-logs', 'baseline'),
|
|
1349
|
-
opts.pod ? { ...opts.pod, durationSource
|
|
1589
|
+
opts.pod ? { ...opts.pod, durationSource } : undefined,
|
|
1350
1590
|
present);
|
|
1351
1591
|
baseline = await localStalled(baseRun, baseTree!, baseSha, 'baseline');
|
|
1352
1592
|
} else baseline = { failures: [], errors: [] };
|
|
@@ -1534,6 +1774,31 @@ export function graphGateResult(result: GateResult, graph: boolean) {
|
|
|
1534
1774
|
+ (result.stalledEnv.length ? ` · 환경 멈춤 ${result.stalledEnv.length} (Pod 밖 통과 ${result.stalledEnv.filter((item) => item.local === 'passed').length})` : '') };
|
|
1535
1775
|
}
|
|
1536
1776
|
|
|
1777
|
+
/** GATE-HOST-EXECUTOR — which executor produced (or will produce) this verdict, for the node output and observation. */
|
|
1778
|
+
export function gateExecutorOf(opts: Pick<GateOptions, 'pod' | 'remote'>): { executor: GateExecutor; host: string | null } {
|
|
1779
|
+
// A Pod sweep runs in shard Pods even when gateRemote is also set (that host only holds the mirror) — no SSH host judged it.
|
|
1780
|
+
return opts.pod ? { executor: 'pod', host: null } : { executor: 'host', host: opts.remote ?? 'local' };
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
/** What was asked for, read best-effort when the options themselves were refused — so a failed gate still says which executor. */
|
|
1784
|
+
export function requestedGateExecutor(args: string[], env: NodeJS.ProcessEnv): { executor: string | null; host: string | null } {
|
|
1785
|
+
let executor: unknown;
|
|
1786
|
+
let host: unknown;
|
|
1787
|
+
try {
|
|
1788
|
+
if (env.ELANOUS_GRAPH_CONTEXT && args.every((arg) => arg === '--json')) {
|
|
1789
|
+
const input = readGraphContext(env).input as Record<string, unknown>;
|
|
1790
|
+
executor = input.gateExecutor ?? (typeof input.gatePodPool === 'string' ? 'pod' : undefined);
|
|
1791
|
+
host = input.gateRemote;
|
|
1792
|
+
}
|
|
1793
|
+
} catch { /* the refusal itself is reported as the error */ }
|
|
1794
|
+
for (let i = 0; i < args.length; i++) {
|
|
1795
|
+
if (args[i] === '--executor') executor = args[i + 1];
|
|
1796
|
+
else if (args[i] === '--remote') host = args[i + 1];
|
|
1797
|
+
}
|
|
1798
|
+
// Same rule as gateExecutorOf: a pod gate names no SSH host.
|
|
1799
|
+
return { executor: typeof executor === 'string' ? executor : null, host: executor !== 'pod' && typeof host === 'string' ? host : null };
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1537
1802
|
export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOptions | 'help' {
|
|
1538
1803
|
const context = env.ELANOUS_GRAPH_CONTEXT && args.every((arg) => arg === '--json') ? readGraphContext(env) : undefined;
|
|
1539
1804
|
const fromGraph: Record<string, unknown> = context?.input ?? {};
|
|
@@ -1541,6 +1806,9 @@ export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOption
|
|
|
1541
1806
|
if (typeof graphCommit === 'string') fromGraph.commit = graphCommit;
|
|
1542
1807
|
const baselineCommit = context?.outputs.cutoff?.baseline as { sha?: unknown } | undefined;
|
|
1543
1808
|
if (typeof baselineCommit?.sha === 'string') fromGraph.previousCommit = baselineCommit.sha;
|
|
1809
|
+
if (fromGraph.gateExecutor !== undefined && !isGateExecutor(fromGraph.gateExecutor)) throw new Error(`gateExecutor must be pod or host: ${String(fromGraph.gateExecutor)}`);
|
|
1810
|
+
// GATE-HOST-EXECUTOR: a host gate never reads the pod settings (a bad pod value must not block the fallback executor).
|
|
1811
|
+
const hostExecutor = fromGraph.gateExecutor === 'host';
|
|
1544
1812
|
const opts: GateOptions = {
|
|
1545
1813
|
commit: typeof fromGraph.commit === 'string' ? fromGraph.commit : '',
|
|
1546
1814
|
version: typeof fromGraph.version === 'string' ? fromGraph.version : '',
|
|
@@ -1549,17 +1817,19 @@ export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOption
|
|
|
1549
1817
|
baselineCommit: typeof fromGraph.previousCommit === 'string' ? fromGraph.previousCommit : undefined,
|
|
1550
1818
|
remote: typeof fromGraph.gateRemote === 'string' ? fromGraph.gateRemote : undefined,
|
|
1551
1819
|
remoteMirror: typeof fromGraph.gateRemoteMirror === 'string' ? fromGraph.gateRemoteMirror : undefined,
|
|
1552
|
-
pod: typeof fromGraph.gatePodPool === 'string' ? {
|
|
1820
|
+
pod: !hostExecutor && typeof fromGraph.gatePodPool === 'string' ? {
|
|
1553
1821
|
pool: fromGraph.gatePodPool,
|
|
1554
1822
|
...(fromGraph.gatePodShards !== undefined ? { shards: Number(fromGraph.gatePodShards) } : {}),
|
|
1555
1823
|
...(fromGraph.gatePodShardTimeoutSeconds !== undefined ? { shardTimeoutSeconds: Number(fromGraph.gatePodShardTimeoutSeconds) } : {}),
|
|
1556
1824
|
...(typeof fromGraph.gatePodBunCache === 'string' && fromGraph.gatePodBunCache.trim() ? { bunCache: fromGraph.gatePodBunCache } : {}),
|
|
1557
1825
|
...(fromGraph.gatePodInstallSlots !== undefined ? { installSlots: Number(fromGraph.gatePodInstallSlots) } : {}),
|
|
1558
1826
|
...(fromGraph.gatePodAdmissionWaitSeconds !== undefined ? { admissionWaitSeconds: Number(fromGraph.gatePodAdmissionWaitSeconds) } : {}),
|
|
1827
|
+
...(fromGraph.gatePodStallSeconds !== undefined ? { stallSeconds: Number(fromGraph.gatePodStallSeconds) } : {}),
|
|
1559
1828
|
...(fromGraph.gatePodFreshShards === true ? { freshShards: true } : {}),
|
|
1560
1829
|
...((cpu) => cpu ? { cpu } : {})(parsePodCpu(fromGraph.gatePodCpu, 'release.loop.gatePodCpu')),
|
|
1561
1830
|
} : undefined,
|
|
1562
1831
|
};
|
|
1832
|
+
if (isGateExecutor(fromGraph.gateExecutor)) opts.executor = fromGraph.gateExecutor;
|
|
1563
1833
|
let freshShards = false;
|
|
1564
1834
|
for (let i = 0; i < args.length; i++) {
|
|
1565
1835
|
const arg = args[i];
|
|
@@ -1567,6 +1837,12 @@ export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOption
|
|
|
1567
1837
|
if (arg === '--json') continue;
|
|
1568
1838
|
// Applied after the loop and only to a Pod gate — on its own it must not turn a local gate into a Pod one (review r5).
|
|
1569
1839
|
if (arg === '--fresh-shards') { freshShards = true; continue; }
|
|
1840
|
+
if (arg === '--executor') {
|
|
1841
|
+
const value = args[++i];
|
|
1842
|
+
if (!isGateExecutor(value)) throw new Error(`--executor must be pod or host: ${value ?? ''}`);
|
|
1843
|
+
opts.executor = value;
|
|
1844
|
+
continue;
|
|
1845
|
+
}
|
|
1570
1846
|
const key = ({ '--commit': 'commit', '--version': 'version', '--baseline-version': 'baselineVersion', '--baseline-commit': 'baselineCommit', '--remote': 'remote', '--remote-mirror': 'remoteMirror', '--pod-pool': 'pod', '--pod-shards': 'pod', '--pod-shard-timeout-seconds': 'pod' } as Record<string, keyof GateOptions>)[arg!];
|
|
1571
1847
|
if (!key) throw new Error(`unknown option: ${arg}`);
|
|
1572
1848
|
const value = args[++i];
|
|
@@ -1582,29 +1858,49 @@ export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOption
|
|
|
1582
1858
|
else if (key === 'remoteMirror') opts.remoteMirror = value;
|
|
1583
1859
|
}
|
|
1584
1860
|
if (freshShards && opts.pod) opts.pod = { ...opts.pod, freshShards: true };
|
|
1861
|
+
// GATE-HOST-EXECUTOR: the host executor is the existing non-pod sweep on gateRemote — a configured pool must not pull it
|
|
1862
|
+
// back into Pods, and without a host it would silently sweep on the runner's own machine.
|
|
1863
|
+
if (opts.executor === 'host') {
|
|
1864
|
+
if (!opts.remote) throw new Error('gateExecutor host requires gateRemote (the SSH host whose clean clone runs the sweep)');
|
|
1865
|
+
opts.pod = undefined;
|
|
1866
|
+
}
|
|
1867
|
+
// An explicit pod choice must not quietly become a host sweep for want of a pool (absent executor = today's behaviour).
|
|
1868
|
+
if (opts.executor === 'pod' && !opts.pod?.pool) throw new Error('gateExecutor pod requires gatePodPool');
|
|
1585
1869
|
return opts;
|
|
1586
1870
|
}
|
|
1587
1871
|
|
|
1588
|
-
|
|
1872
|
+
/** The node's run: judge, emit the graph result line, and return the process exit code (ok 0 · regression 1 · else 2).
|
|
1873
|
+
* `runner` is a seam for tests; the process entry passes none. */
|
|
1874
|
+
export async function runGateNode(argv: string[], env: NodeJS.ProcessEnv, runner?: GateRunner): Promise<number> {
|
|
1589
1875
|
const start = Date.now();
|
|
1590
|
-
// The graph spawns this file as its own process, which inherits no daemon sink — without this every
|
|
1591
|
-
// `release-loop.gate`/`release.gate`/`pod.pool` event stayed in the file trail and `elanous logs` read 0 rows.
|
|
1592
|
-
await registerStandaloneLogSink('release-loop');
|
|
1593
1876
|
let result: GateResult;
|
|
1594
1877
|
let version = '';
|
|
1878
|
+
let placement: { executor: string | null; host: string | null } | undefined;
|
|
1595
1879
|
try {
|
|
1596
|
-
const opts = parseOptions(
|
|
1880
|
+
const opts = parseOptions(argv, env);
|
|
1881
|
+
if (opts !== 'help') {
|
|
1882
|
+
placement = gateExecutorOf(opts);
|
|
1883
|
+
debug.log('release-loop.gate', 'executor', { version: opts.version, ...placement, requested: opts.executor ?? null });
|
|
1884
|
+
}
|
|
1597
1885
|
if (opts === 'help') {
|
|
1598
|
-
console.log('Usage: bun scripts/release-loop/gate-node.ts --commit <sha> --version <v> [--baseline-version <prev>] [--baseline-commit <sha>] [--remote <ssh-host>] [--remote-mirror <path>] [--pod-pool <pool>] [--pod-shards <n>] [--pod-shard-timeout-seconds <n>] [--fresh-shards] [--json]\nWithout flags, input.commit, input.version and input.previousVersion come from the JSON file at ELANOUS_GRAPH_CONTEXT.');
|
|
1599
|
-
|
|
1886
|
+
console.log('Usage: bun scripts/release-loop/gate-node.ts --commit <sha> --version <v> [--baseline-version <prev>] [--baseline-commit <sha>] [--remote <ssh-host>] [--remote-mirror <path>] [--pod-pool <pool>] [--pod-shards <n>] [--pod-shard-timeout-seconds <n>] [--fresh-shards] [--executor <pod|host>] [--json]\nWithout flags, input.commit, input.version and input.previousVersion come from the JSON file at ELANOUS_GRAPH_CONTEXT.\n--executor host (input.gateExecutor) ignores every pod option and sweeps on --remote (input.gateRemote, required).');
|
|
1887
|
+
return 0;
|
|
1600
1888
|
}
|
|
1601
1889
|
version = opts.version;
|
|
1602
|
-
result = await judgeGate(opts);
|
|
1890
|
+
result = await (runner ? judgeGate(opts, runner) : judgeGate(opts));
|
|
1603
1891
|
} catch (error) {
|
|
1604
1892
|
result = { outcome: 'error', commit: '', introduced: [], preexisting: 0, fixed: 0, knownEnv: 0, knownEnvCleared: [], stalledEnv: [], rcNonzeroNoFailures: [], durationMs: Date.now() - start, error: String(error) };
|
|
1893
|
+
placement ??= requestedGateExecutor(argv, env);
|
|
1605
1894
|
}
|
|
1606
1895
|
if (result.error) console.error(result.error);
|
|
1607
|
-
debug.log('release-loop.gate', 'result', { version, outcome: result.outcome });
|
|
1608
|
-
emitNodeResult(graphGateResult(result, !!
|
|
1609
|
-
|
|
1896
|
+
debug.log('release-loop.gate', 'result', { version, outcome: result.outcome, ...(placement ?? {}) });
|
|
1897
|
+
emitNodeResult({ ...graphGateResult(result, !!env.ELANOUS_GRAPH_CONTEXT), ...(placement ?? {}) });
|
|
1898
|
+
return result.outcome === 'ok' ? 0 : result.outcome === 'regression' ? 1 : 2;
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
if (import.meta.main) {
|
|
1902
|
+
// The graph spawns this file as its own process, which inherits no daemon sink — without this every
|
|
1903
|
+
// `release-loop.gate`/`release.gate`/`pod.pool` event stayed in the file trail and `elanous logs` read 0 rows.
|
|
1904
|
+
await registerStandaloneLogSink('release-loop');
|
|
1905
|
+
process.exitCode = await runGateNode(process.argv.slice(2), process.env);
|
|
1610
1906
|
}
|