elanous 0.2.24 → 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-24282bf9f90031a5.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-dea4b795741126fd.js +1 -0
- 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-1d809a87139dd024.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-b92caddb5c715f30.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/{Q-7iofoxOEv1WX18S35of → dRS6lEhSRRBr1abbzp7fJ}/_buildManifest.js +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +22 -22
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +22 -22
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +22 -22
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +21 -21
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +21 -21
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +22 -22
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +21 -21
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +22 -22
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +22 -22
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +21 -21
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +22 -22
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +22 -22
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +22 -22
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +22 -22
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +21 -21
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +21 -21
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +22 -22
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +22 -22
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +22 -22
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +21 -21
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +21 -21
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +21 -21
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +22 -22
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +22 -22
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +21 -21
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +21 -21
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +21 -21
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +21 -21
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +22 -22
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +22 -22
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +21 -21
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +22 -22
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +22 -22
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +22 -22
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +22 -22
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +22 -22
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +21 -21
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +22 -22
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +22 -22
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +22 -22
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +22 -22
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +22 -22
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +22 -22
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +22 -22
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +22 -22
- 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/bench-report.ts +46 -1
- package/scripts/ci-typecheck-changed.ts +7 -5
- package/scripts/release-loop/auto-approve-node.ts +6 -0
- package/scripts/release-loop/gate-closure.ts +286 -0
- package/scripts/release-loop/gate-node.ts +73 -13
- 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 +37 -10
- package/scripts/release-loop/resume.ts +69 -4
- package/scripts/tsc-baseline-cache.ts +28 -3
- package/scripts/usage-rollup.ts +27 -13
- 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-substrate/acp-implementer.ts +14 -7
- package/src/agent-substrate/pr-reviewer.ts +30 -10
- package/src/agent-substrate/self-review-cli.ts +126 -10
- package/src/budget/cost-tier-advisor.ts +91 -0
- package/src/budget/shadow-arms.ts +77 -0
- package/src/budget/types.ts +5 -3
- package/src/budget/unified-usage.ts +172 -8
- package/src/cli/doctor-cli.ts +21 -0
- package/src/cli/friendly-errors.ts +113 -0
- package/src/cli/intake-cli.ts +3 -1
- package/src/cli/memory-cli.ts +182 -0
- package/src/cli/model-watch-cli.ts +4 -1
- package/src/cli/pr-land-pod-shadow.ts +54 -0
- package/src/cli/release-cli.ts +9 -7
- 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/seat-requests-cli.ts +25 -0
- package/src/cli/tasks-cli.ts +31 -7
- package/src/cli/usage-cli.ts +3 -1
- 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/dashboard/slash-runtime/dashboard-handlers.ts +4 -138
- package/src/dashboard/slash-runtime/shell-slash.ts +145 -0
- package/src/debug/log-diet.ts +16 -4
- package/src/domains/morning-essential.ts +18 -3
- package/src/domains/release-tool.ts +4 -0
- package/src/execution-loop/launch-gate.ts +158 -2
- package/src/fleet/host-doctor.ts +375 -0
- package/src/goals/judge.ts +20 -6
- package/src/graph-runner/runner.ts +9 -5
- package/src/graph-wizard/archetypes.ts +14 -8
- package/src/graph-wizard/generate.ts +77 -1
- package/src/grok.ts +13 -7
- package/src/harness/harness-cli-command.ts +56 -47
- package/src/harness/harness-stop.ts +82 -12
- package/src/index.ts +17 -175
- package/src/intake-plane/collect-github.ts +7 -2
- package/src/intake-plane/lens.ts +46 -4
- package/src/intake-plane/requirement-funnel.ts +53 -1
- package/src/intelligence-map/catalog-feed.ts +119 -0
- package/src/intelligence-map/model-watch-intake.ts +40 -0
- package/src/intelligence-map/model-watch-mission.ts +14 -1
- package/src/llm/open-weight-discipline.ts +21 -0
- package/src/llm.ts +4 -8
- package/src/maturity/feature-maturity.ts +1 -1
- package/src/nexus/api/http-server.ts +22 -0
- package/src/oauth/codex-account-rotation.ts +4 -1
- package/src/oauth/codex-account-store.ts +20 -7
- package/src/oauth/codex-quota-policy.ts +33 -4
- package/src/preview/terminal.ts +15 -5
- package/src/project/project-store.ts +20 -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/rehearsal.ts +1 -1
- package/src/seat-loop/duty-ledger.ts +64 -0
- package/src/self-dev/decompose.ts +37 -3
- package/src/self-dev/dev-pipeline.ts +32 -7
- package/src/self-dev/draft-residue.ts +16 -6
- package/src/self-dev/draft-sweep.ts +52 -4
- package/src/self-dev/fabric-decompose-adapter.ts +9 -2
- package/src/self-dev/run-supervisor.ts +37 -7
- package/src/self-implement/gate-cli.ts +29 -10
- package/src/self-implement/gate-remote.ts +4 -1
- package/src/self-implement/goal-author.ts +12 -2
- package/src/self-implement/goal-new-paths.ts +42 -0
- 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 +88 -19
- 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 +2 -2
- package/src/self-implement/scope-adjacency.ts +23 -0
- package/src/self-implement/seams.ts +42 -7
- 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/rejudge-tick.ts +142 -0
- package/src/task-agent/board.ts +92 -3
- package/src/task-agent/card-evidence.ts +88 -1
- package/src/task-agent/cover.ts +65 -0
- package/src/task-agent/parent-host.ts +27 -3
- package/src/task-agent/rejudge-on-head.ts +151 -7
- package/src/task-agent/review-result-capture.ts +3 -1
- package/src/task-agent/shadow.ts +12 -0
- package/src/task-agent/task-hand.ts +4 -1
- package/src/task-orchestrator/surfaces/pod-command-job.ts +14 -0
- package/src/task-orchestrator/surfaces/pod-host-acp-review.ts +122 -0
- package/src/task-orchestrator/surfaces/pod-manifest-test-normalize.ts +23 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +53 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +235 -28
- package/src/user-config.ts +18 -4
- 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/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-0eed0fb0aab716b6.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-aaae04ed1d32622f.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-3b9fc39362473ae7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f2b62c19ca68fef8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-d7602d489edf5f37.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-8b7c5ab6b5bc8d64.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-4eb84d454f8c3415.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-b2e494bdad57fcb5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-c0ab229673a8b0eb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-3e2b45f3c36dbac4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-c22aa511b28743ad.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-26345242e4049f55.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-bf88c0f558d8ef54.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-52fec523e5b5d3ad.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-67723dfdcde92b31.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-22725e2570f1126b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-1891335950d68b70.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-3267af1ebdc73cf5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-c314b75c11234998.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-9f9c85415f61782b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-c1d4276059085587.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-833aab587f26ad4c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8526c17ae38e6a50.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-f998637a8402115c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-b7bb53828b17b335.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-1b1850de0257e46d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-6f23159fedb41f67.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-da9e570234d481fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-8ce3a38e210b810a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-57ffeeb6019440fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-abbb417bf514630b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-2c2b3f29cec1efa4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-9369279f72fc7b51.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-64861f14039499c3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ddc4c93efd8ab94b.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-901a1391b81053bc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-68d9bbaac9841a58.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-b5c0c996f329371e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-89881ccbcf728978.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-0e87a803b3d7165e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-58d4e5cb30ef4871.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-02ce4c3b5f39e4d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-e620d1bb8eadcec2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-acf868c64d77f950.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-54fa605fa6dba457.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-bc79f0833533fa84.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-d5c4398eea03d8f3.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/{Q-7iofoxOEv1WX18S35of → dRS6lEhSRRBr1abbzp7fJ}/_ssgManifest.js +0 -0
package/scripts/bench-report.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { spawnSync } from 'node:child_process';
|
|
14
14
|
import { readFileSync } from 'node:fs';
|
|
15
15
|
import { dirname, join } from 'node:path';
|
|
16
|
+
import { dedupePodRollupRows } from './usage-rollup.js';
|
|
16
17
|
|
|
17
18
|
export interface LogRow { category?: string; event?: string; data?: Record<string, unknown> | null }
|
|
18
19
|
export interface ArmDecl { id: string; provider: string; model: string | null }
|
|
@@ -40,6 +41,40 @@ export interface ArmReport {
|
|
|
40
41
|
reviewMustFixCount: number | null;
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
export interface ProbeJudgment {
|
|
45
|
+
model: string;
|
|
46
|
+
runs: number;
|
|
47
|
+
passed: number;
|
|
48
|
+
failed: number;
|
|
49
|
+
unmeasured: number;
|
|
50
|
+
verdict: 'pass' | 'fail' | 'unmeasured';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function judgeProbe(
|
|
54
|
+
arms: readonly ArmReport[],
|
|
55
|
+
opts?: { passNumerator?: number; passDenominator?: number },
|
|
56
|
+
): ProbeJudgment[] {
|
|
57
|
+
const passNumerator = opts?.passNumerator ?? 8;
|
|
58
|
+
const passDenominator = opts?.passDenominator ?? 9;
|
|
59
|
+
const groups = new Map<string, { runs: number; passed: number; failed: number; unmeasured: number }>();
|
|
60
|
+
for (const arm of arms) {
|
|
61
|
+
const model = arm.declared?.model ?? '못 이음';
|
|
62
|
+
const group = groups.get(model) ?? { runs: 0, passed: 0, failed: 0, unmeasured: 0 };
|
|
63
|
+
group.runs++;
|
|
64
|
+
if (arm.gatePassed === null || arm.leakCalls === null) group.unmeasured++;
|
|
65
|
+
else if (arm.gatePassed === true && arm.leakCalls === 0) group.passed++;
|
|
66
|
+
else group.failed++;
|
|
67
|
+
groups.set(model, group);
|
|
68
|
+
}
|
|
69
|
+
return [...groups].map(([model, counts]) => ({
|
|
70
|
+
model,
|
|
71
|
+
...counts,
|
|
72
|
+
verdict: counts.unmeasured > 0
|
|
73
|
+
? 'unmeasured'
|
|
74
|
+
: counts.passed * passDenominator >= counts.runs * passNumerator ? 'pass' : 'fail',
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
|
|
43
78
|
const num = (v: unknown): number => (typeof v === 'number' && Number.isFinite(v) ? v : 0);
|
|
44
79
|
const str = (v: unknown): string | null => (typeof v === 'string' && v.length > 0 ? v : null);
|
|
45
80
|
const armKey = (id: string): string => (id.startsWith('pod/') ? id : `pod/${id}`);
|
|
@@ -99,7 +134,8 @@ export function buildBenchReport(rows: readonly LogRow[], runId: string): { runI
|
|
|
99
134
|
a.reviewMustFixCount = typeof r.data!.reviewMustFixCount === 'number' ? r.data!.reviewMustFixCount : null;
|
|
100
135
|
}
|
|
101
136
|
const leak = new Map<string, number>();
|
|
102
|
-
|
|
137
|
+
// ⭐ METER-POD-ROLLUP-GAP-2 — 같은 Pod 의 재방출(부분 ⊕ 최종 · abort ⊕ resume)은 podRollupKey 로 한 번만 센다(팔 사용량 이중계상 방지).
|
|
138
|
+
for (const r of dedupePodRollupRows(mine, (row) => (row.event === 'llm-usage' ? row.data ?? {} : {}))) {
|
|
103
139
|
if (r.event !== 'llm-usage') continue;
|
|
104
140
|
const site = str(r.data!.site);
|
|
105
141
|
const id = str(r.data!.armId);
|
|
@@ -203,6 +239,15 @@ export function renderBenchReport(rep: ReturnType<typeof buildBenchReport>): str
|
|
|
203
239
|
lines.push(leaked.length
|
|
204
240
|
? `⛔ 팔 밖 모델로 간 호출이 있다(${leaked.join(', ')}) — 이 판으로 팔을 비교하지 않는다.`
|
|
205
241
|
: '⛔ 한 판(팔당 N=1)이다 — 결론이 아니라 관측이다. 비교는 팔마다 표본이 쌓인 뒤에.');
|
|
242
|
+
const probe = judgeProbe(rep.arms);
|
|
243
|
+
lines.push(
|
|
244
|
+
'',
|
|
245
|
+
'## 프로브 판정',
|
|
246
|
+
'',
|
|
247
|
+
'| 모델 | 통과/런 | 판정 |',
|
|
248
|
+
'|---|---:|---|',
|
|
249
|
+
...probe.map((row) => `| ${row.model} | ${row.passed}/${row.runs} | ${row.verdict} |`),
|
|
250
|
+
);
|
|
206
251
|
return lines.join('\n');
|
|
207
252
|
}
|
|
208
253
|
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
ROOT_PLACEHOLDER,
|
|
18
18
|
baselineCacheEnabled,
|
|
19
19
|
computeBaselineCacheKey,
|
|
20
|
+
workingTreeInstallKey,
|
|
20
21
|
lookupBaselineCache,
|
|
21
22
|
storeBaselineCache,
|
|
22
23
|
toPlaceholderPaths,
|
|
@@ -819,22 +820,23 @@ export function rebaseWorktreePaths(diagnostics: readonly TypecheckError[], work
|
|
|
819
820
|
export type BaselineCacheIo = BaselineCacheOptions & { log?: (message: string) => void };
|
|
820
821
|
|
|
821
822
|
/**
|
|
822
|
-
* 같은 base 커밋 ⊕ 같은 명령 ⊕ 같은 설정
|
|
823
|
+
* 같은 base 커밋 ⊕ 같은 명령 ⊕ 같은 설정 해시 ⊕ 같은 작업 트리 설치 키면 저장된 진단을 재사용한다(워크트리·tsc 0회).
|
|
823
824
|
* 캐시의 어떤 실패도 던지지 않는다 — miss 로 떨어져 아래 측정이 그대로 돈다.
|
|
824
825
|
*/
|
|
825
|
-
function lookupCachedBaseline(key: string, command: string, cwd: string, cacheIo: BaselineCacheIo): BaselineDiagnostics | undefined {
|
|
826
|
+
function lookupCachedBaseline(key: string, command: string, cwd: string, cacheIo: BaselineCacheIo, installKey?: string): BaselineDiagnostics | undefined {
|
|
826
827
|
const lookup = lookupBaselineCache(key, cacheIo);
|
|
827
828
|
const result = lookup.result === 'hit' ? 'hit' : lookup.result === 'corrupt' ? 'corrupt' : 'miss';
|
|
828
|
-
cacheIo.log?.(`[tsc-gate] typecheck baseline-cache ${result === 'hit' ? 'hit' : 'miss'} ${key.slice(0, 12)}`);
|
|
829
|
-
debug.log('typecheck.gate', 'baseline-cache', { result, key, command });
|
|
829
|
+
cacheIo.log?.(`[tsc-gate] typecheck baseline-cache ${result === 'hit' ? 'hit' : 'miss'} ${key.slice(0, 12)} installKey ${installKey?.slice(0, 12) ?? 'n/a'}`);
|
|
830
|
+
debug.log('typecheck.gate', 'baseline-cache', { result, key, command, installKey: installKey ?? null });
|
|
830
831
|
if (lookup.result !== 'hit') return undefined;
|
|
831
832
|
return { diagnostics: rebaseWorktreePaths(lookup.diagnostics, ROOT_PLACEHOLDER, cwd) };
|
|
832
833
|
}
|
|
833
834
|
|
|
834
835
|
export function readMergeBaseDiagnostics(base: string, command: string, cwd = process.cwd(), run = runTsc, cacheIo: BaselineCacheIo = {}): BaselineDiagnostics {
|
|
835
836
|
const cacheKey = baselineCacheEnabled(cacheIo.env ?? process.env) ? computeBaselineCacheKey(base, command, cwd, cacheIo) : undefined;
|
|
837
|
+
const installKey = cacheKey ? workingTreeInstallKey(cwd, cacheIo) : undefined;
|
|
836
838
|
if (cacheKey) {
|
|
837
|
-
const cached = lookupCachedBaseline(cacheKey, command, cwd, cacheIo);
|
|
839
|
+
const cached = lookupCachedBaseline(cacheKey, command, cwd, cacheIo, installKey);
|
|
838
840
|
if (cached) return cached;
|
|
839
841
|
}
|
|
840
842
|
const workspace = mkdtempSync(join(tmpdir(), 'ci-typecheck-baseline-'));
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
4
4
|
import { debug } from '../../src/debug/log.js';
|
|
5
|
+
import { registerStandaloneLogSink } from '../../src/domains/standalone-log-sink.js';
|
|
5
6
|
import { effectiveInstanceRoot } from '../../src/instance/resolve.js';
|
|
6
7
|
import { emitNodeResult, readGraphContext, type GraphContext } from './node-verdict.js';
|
|
7
8
|
import { regressWarning, validatedTuiRegress } from './tui-node-checks.js';
|
|
@@ -78,6 +79,11 @@ export function runAutoApprove(context: GraphContext = readGraphContext(), deps:
|
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
if (import.meta.main) {
|
|
82
|
+
// GATE-OBS2-1 — the release graph spawns this node as its own process, which inherits no daemon sink. Without this the
|
|
83
|
+
// `release-loop.approve / auto-decision` event (the one that says why a release was or was not published unattended)
|
|
84
|
+
// stayed in the file trail and `elanous logs` read 0 rows. Same pattern as gate-node.ts.
|
|
85
|
+
// Observability must never stop the release node from emitting its verdict — a failed sink registration is skipped.
|
|
86
|
+
try { await registerStandaloneLogSink('release-loop'); } catch { /* fail-soft: the decision still goes to stdout */ }
|
|
81
87
|
try {
|
|
82
88
|
const result = runAutoApprove();
|
|
83
89
|
emitNodeResult(result);
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
// GATE-REUSE-BY-DIFF (0.2.25 · 10-12): which test files can a code change reach? A test whose whole module closure
|
|
2
|
+
// (static ⊕ literal dynamic imports ⊕ requires ⊕ literal repo paths it reads or spawns) avoids every changed file runs
|
|
3
|
+
// the same code at both commits, so its prior gate result is reusable. Everything else — and every test whose closure we
|
|
4
|
+
// cannot read for sure («opaque») — is re-run. Doubt never shrinks the set: a closure we cannot build is a full gate.
|
|
5
|
+
//
|
|
6
|
+
// Read from the tip commit's git objects, never from the working tree (the resume can run from any checkout).
|
|
7
|
+
import { spawnSync } from 'node:child_process';
|
|
8
|
+
import { dirname, join, normalize } from 'node:path/posix';
|
|
9
|
+
import ts from 'typescript';
|
|
10
|
+
import { sourceCandidates } from '../../src/self-implement/importer-test-index.js';
|
|
11
|
+
|
|
12
|
+
/** Files the closure follows (code). Changing anything else that is not a test or inert Markdown is a full gate. */
|
|
13
|
+
export const CODE_FILE = /\.(?:[cm]?[jt]sx?)$/;
|
|
14
|
+
const TEST_FILE = /^(?:[\w.-]+\/)*[\w.-]+\.test\.(?:tsx?|jsx?|mts|cts)$/;
|
|
15
|
+
/** Test-side helpers: their filesystem scans and unresolved code paths make a test opaque (production code is trusted to
|
|
16
|
+
* reach code only through imports and the literal paths below). */
|
|
17
|
+
const TEST_SIDE_DIR = /(?:^|\/)(?:test|tests|__tests__|__mocks__|fixtures|__fixtures__|test-helpers|testing)\//;
|
|
18
|
+
const TEST_SIDE_NAME = /(?:^|[-_.])(?:test-?utils?|test-?helpers?|test-?support|testing|fixtures?|mocks?)$/i;
|
|
19
|
+
const isTestSide = (file: string): boolean => TEST_FILE.test(file) || TEST_SIDE_DIR.test(file)
|
|
20
|
+
|| TEST_SIDE_NAME.test((file.split('/').pop() ?? file).replace(CODE_FILE, ''));
|
|
21
|
+
/** A test-side file that lists directories or tracked files reads paths we cannot see. */
|
|
22
|
+
const FS_SCAN = /\b(?:readdirSync|readdir|opendirSync|opendir|globSync|Glob|walkSync)\b|\bls-files\b/;
|
|
23
|
+
/** `import(x)` / `require(x)` with a non-literal argument loads a module we cannot name. */
|
|
24
|
+
const NON_LITERAL_LOAD = /(?<![.\w$])(?:import|require)\s*\(\s*(?!['"`)\s])([^)]*)\)?/g;
|
|
25
|
+
/** `const NAME = "literal"` bindings — `import(NAME)` with such a NAME loads that literal. */
|
|
26
|
+
const CONST_STRING = /\bconst\s+([A-Za-z_$][\w$]*)\s*=\s*(?:process\.env\.[\w$]+\s*(?:\?\?|\|\|)\s*)?(['"`])((?:(?!\2)[^\n\\$])*)\2\s*[;,\n]/g;
|
|
27
|
+
/** `import(["..", "x", "y.js"].join("/"))` — a specifier spelled as joined literals. */
|
|
28
|
+
const JOINED_IMPORT = /(?<![.\w$])import\s*\(\s*\[((?:\s*"[^"\n]*"\s*,?)+)\]\.join\(\s*"([^"\n]*)"\s*\)\s*\)/g;
|
|
29
|
+
/** Process launches — a file that has one may run a repo path it names. */
|
|
30
|
+
const SPAWNS = /(?<![\w$])(?:spawn|spawnSync|exec|execSync|execFile|execFileSync|fork|Bun\.spawn|Bun\.spawnSync|Bun\.\$)\s*[(`]/;
|
|
31
|
+
const LITERAL = /(['"`])((?:(?!\1)[^\n\\])*)\1/g;
|
|
32
|
+
/** An npm package specifier (`x`, `x/sub`, `@scope/x/sub`). */
|
|
33
|
+
const PACKAGE_NAME = /^(?:@[a-z0-9][\w.~-]*\/)?[a-z0-9][\w.~-]*(?:\/[^\s]*)?$/i;
|
|
34
|
+
const SUFFIXES = ['.ts', '.tsx', '.js', '.jsx', '.mts', '.cts', '.mjs', '.cjs'];
|
|
35
|
+
|
|
36
|
+
export interface ClosureTree {
|
|
37
|
+
/** Every tracked path at the tip. */
|
|
38
|
+
files: readonly string[];
|
|
39
|
+
/** Content at the tip (code files only are asked for); undefined = unreadable. */
|
|
40
|
+
read: (path: string) => string | undefined;
|
|
41
|
+
/** bun test preload files (bunfig.toml `[test].preload`), repo-relative. */
|
|
42
|
+
preload: readonly string[];
|
|
43
|
+
/** tsconfig `paths` aliases: specifier prefix → repo dir, scoped to importers under `scope`. */
|
|
44
|
+
aliases?: readonly { scope: string; prefix: string; target: string }[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type ClosureVerdict =
|
|
48
|
+
| { ok: true; tests: string[]; reached: number; opaque: number; opaqueFiles: string[] }
|
|
49
|
+
| { ok: false; reason: string };
|
|
50
|
+
|
|
51
|
+
function loaderOf(file: string): 'ts' | 'tsx' | 'js' {
|
|
52
|
+
return /\.[jt]sx$/.test(file) ? 'tsx' : /\.[cm]?ts$/.test(file) ? 'ts' : 'js';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface ClosureOptions {
|
|
56
|
+
/** CLI entry files a production literal does not make a dependency of (help text, child command builders). */
|
|
57
|
+
cliEntries?: readonly string[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface ClosureGraph {
|
|
61
|
+
/** Every dependency a file can load: imports of any kind ⊕ literal repo paths. */
|
|
62
|
+
forward: Map<string, string[]>;
|
|
63
|
+
/** Only top-level `import`/`export … from` — what loading the file evaluates before any of its code runs. */
|
|
64
|
+
staticImports: Map<string, string[]>;
|
|
65
|
+
opaque: Map<string, string>;
|
|
66
|
+
tracked: Set<string>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Forward dependency edges of every code file at the tip ⊕ the files whose dependencies we cannot fully name. */
|
|
70
|
+
function closureGraph(tree: ClosureTree, options: ClosureOptions = {}): ClosureGraph {
|
|
71
|
+
const tracked = new Set(tree.files);
|
|
72
|
+
const dirs = new Set<string>();
|
|
73
|
+
for (const file of tree.files) for (let at = file.indexOf('/'); at > 0; at = file.indexOf('/', at + 1)) dirs.add(file.slice(0, at));
|
|
74
|
+
const resolveFrom = (from: string, spec: string): string | undefined => {
|
|
75
|
+
for (const candidate of sourceCandidates(from, spec)) if (tracked.has(candidate)) return candidate;
|
|
76
|
+
const base = normalize(join(dirname(from), spec.replace(/[?#].*$/, '')));
|
|
77
|
+
if (tracked.has(base)) return base;
|
|
78
|
+
for (const suffix of SUFFIXES) {
|
|
79
|
+
if (tracked.has(`${base}${suffix}`)) return `${base}${suffix}`;
|
|
80
|
+
if (tracked.has(`${base}/index${suffix}`)) return `${base}/index${suffix}`;
|
|
81
|
+
}
|
|
82
|
+
return undefined;
|
|
83
|
+
};
|
|
84
|
+
/** A literal is a file name, not a module specifier: exact (or emitted `.js` → `.ts`), extensionless relative ones get
|
|
85
|
+
* a source suffix (`mock.module('../y')`) — never a directory index (`'src/'` is not `src/index.ts`). */
|
|
86
|
+
const resolveLiteral = (from: string, value: string): string | undefined => {
|
|
87
|
+
const relative = value.startsWith('./') || value.startsWith('../');
|
|
88
|
+
const bases = relative ? [from] : [from, '_'];
|
|
89
|
+
if (/\.[A-Za-z0-9]+$/.test(value.split('/').pop() ?? '')) {
|
|
90
|
+
for (const base of bases) for (const candidate of sourceCandidates(base, value)) if (tracked.has(candidate)) return candidate;
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
if (!relative) return undefined;
|
|
94
|
+
const base = normalize(join(dirname(from), value));
|
|
95
|
+
for (const suffix of SUFFIXES) if (tracked.has(`${base}${suffix}`)) return `${base}${suffix}`;
|
|
96
|
+
return undefined;
|
|
97
|
+
};
|
|
98
|
+
const transpilers = { ts: new Bun.Transpiler({ loader: 'ts' }), tsx: new Bun.Transpiler({ loader: 'tsx' }), js: new Bun.Transpiler({ loader: 'js' }) };
|
|
99
|
+
const forward = new Map<string, string[]>();
|
|
100
|
+
const staticImports = new Map<string, string[]>();
|
|
101
|
+
const opaque = new Map<string, string>();
|
|
102
|
+
const blindAs = (file: string, why: string): void => { if (!opaque.has(file)) opaque.set(file, why); };
|
|
103
|
+
for (const file of tree.files) {
|
|
104
|
+
if (!CODE_FILE.test(file)) continue;
|
|
105
|
+
const text = tree.read(file);
|
|
106
|
+
if (text === undefined) { blindAs(file, 'unreadable'); continue; }
|
|
107
|
+
const edges = new Set<string>();
|
|
108
|
+
const testSide = isTestSide(file);
|
|
109
|
+
let specs: { path: string; kind: string }[];
|
|
110
|
+
// The transpiled text has no comments or types: literals and loads are read from code only (a path in a comment is
|
|
111
|
+
// not a dependency).
|
|
112
|
+
let code: string;
|
|
113
|
+
try {
|
|
114
|
+
const source = text.replace(/^#![^\n]*/, '');
|
|
115
|
+
specs = [...transpilers[loaderOf(file)].scanImports(source)];
|
|
116
|
+
code = transpilers[loaderOf(file)].transformSync(source).replace(JOINED_IMPORT, (_all, items: string, separator: string) => {
|
|
117
|
+
const joined = [...items.matchAll(/"([^"\n]*)"/g)].map((item) => item[1]).join(separator);
|
|
118
|
+
specs.push({ path: joined, kind: 'dynamic-import' });
|
|
119
|
+
return `import(${JSON.stringify(joined)})`;
|
|
120
|
+
});
|
|
121
|
+
} catch { blindAs(file, 'unparsable'); continue; }
|
|
122
|
+
// A load whose argument is not a literal: resolvable only when it is one identifier bound to a string constant in
|
|
123
|
+
// this file (`const M = './pitch-api.js'; import(M)`); anything else loads a module we cannot name.
|
|
124
|
+
const bindings = new Map<string, string>();
|
|
125
|
+
for (const match of code.matchAll(CONST_STRING)) bindings.set(match[1]!, match[3]!);
|
|
126
|
+
const codeOnly = code.replace(LITERAL, '""');
|
|
127
|
+
for (const match of codeOnly.matchAll(NON_LITERAL_LOAD)) {
|
|
128
|
+
const bound = bindings.get((match[1] ?? '').trim());
|
|
129
|
+
if (bound === undefined) { blindAs(file, 'non-literal import/require'); continue; }
|
|
130
|
+
if (!bound.startsWith('.')) continue; // a package name (optional dependency)
|
|
131
|
+
const hit = resolveFrom(file, bound);
|
|
132
|
+
if (hit) specs.push({ path: bound, kind: 'dynamic-import' }); else blindAs(file, `unresolved import ${bound}`);
|
|
133
|
+
}
|
|
134
|
+
if (testSide && FS_SCAN.test(code)) blindAs(file, `scans (${FS_SCAN.exec(code)?.[0]})`);
|
|
135
|
+
const eager = new Set<string>();
|
|
136
|
+
for (const { path: spec, kind } of specs) {
|
|
137
|
+
let hit: string | undefined;
|
|
138
|
+
if (spec.startsWith('.') || spec.startsWith('/')) {
|
|
139
|
+
hit = spec.startsWith('/') ? undefined : resolveFrom(file, spec);
|
|
140
|
+
if (!hit) { blindAs(file, `unresolved import ${spec}`); continue; }
|
|
141
|
+
} else {
|
|
142
|
+
const alias = tree.aliases?.find((entry) => (entry.scope === '' || file.startsWith(`${entry.scope}/`)) && spec.startsWith(entry.prefix));
|
|
143
|
+
if (!alias) {
|
|
144
|
+
// Not a package name (`@/x`, `~/x`, `#x`): an alias we do not know — never assume it is outside the repo.
|
|
145
|
+
if (!/^(?:node:|bun:)/.test(spec) && !PACKAGE_NAME.test(spec)) blindAs(file, `unresolved alias ${spec}`);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
hit = resolveFrom(`${alias.target}/_`, `./${spec.slice(alias.prefix.length)}`);
|
|
149
|
+
if (!hit) { blindAs(file, `unresolved alias ${spec}`); continue; }
|
|
150
|
+
}
|
|
151
|
+
edges.add(hit);
|
|
152
|
+
if (kind === 'import-statement') eager.add(hit);
|
|
153
|
+
// Anything else is a package (node_modules / builtin) — lockfile and package.json changes are full gates.
|
|
154
|
+
}
|
|
155
|
+
// Literal repo paths: a test that reads `src/x.ts`, spawns `bin/elanous.mjs` or mocks `../y` depends on that file.
|
|
156
|
+
for (const match of code.matchAll(LITERAL)) {
|
|
157
|
+
const value = match[2]!;
|
|
158
|
+
if (!value || value.length > 300 || value.includes('${') || /\s/.test(value) || /^[a-z]+:/i.test(value)) continue;
|
|
159
|
+
const pathLike = value.startsWith('./') || value.startsWith('../') || (value.includes('/') && /^[\w@.-]+(?:\/[\w@.-]+)+\/?$/.test(value)) || CODE_FILE.test(value);
|
|
160
|
+
if (!pathLike) continue;
|
|
161
|
+
const plain = value.replace(/\/$/, '');
|
|
162
|
+
const hit = resolveLiteral(file, plain);
|
|
163
|
+
// A path naming another test file is runner input (a list, a fixture string), not code this file loads.
|
|
164
|
+
if (hit) {
|
|
165
|
+
// A production file that names the CLI entry but spawns nothing only prints it (help text, a goal's command line).
|
|
166
|
+
const printsOnly = (options.cliEntries ?? []).includes(hit) && !SPAWNS.test(code);
|
|
167
|
+
if (!TEST_FILE.test(hit) && (testSide || !printsOnly)) edges.add(hit);
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (!testSide) continue;
|
|
171
|
+
const rootDir = normalize(plain.startsWith('.') ? join(dirname(file), plain) : plain);
|
|
172
|
+
// A test-side literal naming a tracked directory scans it; one naming a code file we cannot find is a path we miss.
|
|
173
|
+
if (dirs.has(rootDir) || (plain.startsWith('.') && dirs.has(normalize(join(dirname(file), plain))))) blindAs(file, `names directory ${plain}`);
|
|
174
|
+
// With or without a slash: `join(dir, '..', 'oauth', 'store.ts')` builds the path from pieces, so the bare file name
|
|
175
|
+
// is all we see — and a code file we cannot find is a path we miss.
|
|
176
|
+
else if (CODE_FILE.test(plain) && !TEST_FILE.test(plain)) blindAs(file, `unresolved path ${plain}`);
|
|
177
|
+
}
|
|
178
|
+
edges.delete(file);
|
|
179
|
+
eager.delete(file);
|
|
180
|
+
forward.set(file, [...edges]);
|
|
181
|
+
staticImports.set(file, [...eager]);
|
|
182
|
+
}
|
|
183
|
+
return { forward, staticImports, opaque, tracked };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** The tests among `tests` that can reach any of `changed` (code files) — or whose closure is opaque. */
|
|
187
|
+
export function testsReachingChanged(tree: ClosureTree, changed: readonly string[], tests: readonly string[], options: ClosureOptions = {}): ClosureVerdict {
|
|
188
|
+
const { forward, staticImports, opaque, tracked } = closureGraph(tree, options);
|
|
189
|
+
const reverse = new Map<string, string[]>();
|
|
190
|
+
for (const [from, edges] of forward) for (const to of edges) {
|
|
191
|
+
const list = reverse.get(to);
|
|
192
|
+
if (list) list.push(from); else reverse.set(to, [from]);
|
|
193
|
+
}
|
|
194
|
+
const reachedFrom = (seeds: Iterable<string>): Set<string> => {
|
|
195
|
+
const seen = new Set<string>(seeds);
|
|
196
|
+
const queue = [...seen];
|
|
197
|
+
while (queue.length) for (const importer of reverse.get(queue.pop()!) ?? []) if (!seen.has(importer)) { seen.add(importer); queue.push(importer); }
|
|
198
|
+
return seen;
|
|
199
|
+
};
|
|
200
|
+
// Preload runs before every test file: what it evaluates on load (its static import closure) is a dependency of all
|
|
201
|
+
// of them — changed, missing or unreadable there is a full gate.
|
|
202
|
+
const preloadClosure = new Set<string>();
|
|
203
|
+
const queue = [...tree.preload];
|
|
204
|
+
while (queue.length) {
|
|
205
|
+
const file = queue.pop()!;
|
|
206
|
+
if (preloadClosure.has(file)) continue;
|
|
207
|
+
preloadClosure.add(file);
|
|
208
|
+
queue.push(...(staticImports.get(file) ?? []));
|
|
209
|
+
}
|
|
210
|
+
const changedSet = new Set(changed);
|
|
211
|
+
for (const file of preloadClosure) {
|
|
212
|
+
if (!tracked.has(file)) return { ok: false, reason: `bun test preload closure file ${file} is missing — full gate` };
|
|
213
|
+
if (changedSet.has(file)) return { ok: false, reason: `bun test preload closure file ${file} changed — full gate` };
|
|
214
|
+
// Any load we cannot name here — even one that looks lazy — may run before every test: a full gate (#26516 review).
|
|
215
|
+
const why = opaque.get(file);
|
|
216
|
+
if (why) return { ok: false, reason: `bun test preload closure file ${file} is opaque (${why}) — full gate` };
|
|
217
|
+
}
|
|
218
|
+
const reached = reachedFrom(changed);
|
|
219
|
+
const blind = reachedFrom([...opaque.keys()].filter((file) => !preloadClosure.has(file)));
|
|
220
|
+
const selected = tests.filter((test) => reached.has(test) || blind.has(test)).sort();
|
|
221
|
+
return { ok: true, tests: selected, reached: tests.filter((test) => reached.has(test)).length, opaque: tests.filter((test) => blind.has(test) && !reached.has(test)).length,
|
|
222
|
+
opaqueFiles: [...opaque].filter(([file]) => !preloadClosure.has(file)).map(([file, why]) => `${file}: ${why}`).sort() };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** `[test].preload` of a bunfig.toml text (repo-relative). Null when the file has a preload we cannot parse. */
|
|
226
|
+
export function bunfigPreload(text: string | undefined): string[] | null {
|
|
227
|
+
if (text === undefined) return [];
|
|
228
|
+
const section = /^\s*\[test\]\s*$([\s\S]*?)(?=^\s*\[|(?![\s\S]))/m.exec(text)?.[1] ?? '';
|
|
229
|
+
const line = /^\s*preload\s*=\s*(.+)$/m.exec(section.replace(/#[^\n]*/g, ''));
|
|
230
|
+
if (!line) return /preload/.test(section.replace(/#[^\n]*/g, '')) ? null : [];
|
|
231
|
+
const list = /^\[([^\]]*)\]\s*$/.exec(line[1]!.trim()) ?? /^(["'][^"']*["'])\s*$/.exec(line[1]!.trim());
|
|
232
|
+
if (!list) return null;
|
|
233
|
+
return [...list[1]!.matchAll(/["']([^"']+)["']/g)].map((entry) => normalize(entry[1]!).replace(/^\.\//, ''));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** The tip tree from git objects (`ls-tree` ⊕ one `cat-file --batch`). Throws on any git failure. */
|
|
237
|
+
export function gitClosureTree(repo: string, commit: string): ClosureTree {
|
|
238
|
+
if (!/^[0-9a-f]{40}$/i.test(commit)) throw new Error(`not a full sha: ${commit}`);
|
|
239
|
+
// git-spawn-allow: read-only object reads of one release commit.
|
|
240
|
+
const listed = spawnSync('git', ['ls-tree', '-r', '-z', '--name-only', commit], { cwd: repo, encoding: 'utf8', maxBuffer: 256 * 1024 * 1024 });
|
|
241
|
+
if (listed.status !== 0 || listed.error) throw new Error(`git ls-tree failed: ${(listed.stderr || listed.error || '').toString().trim()}`);
|
|
242
|
+
const files = listed.stdout.split('\0').filter(Boolean);
|
|
243
|
+
const wanted = files.filter((file) => CODE_FILE.test(file) || file === 'bunfig.toml' || /(?:^|\/)tsconfig[\w.-]*\.json$/.test(file));
|
|
244
|
+
// git-spawn-allow: read-only object reads of one release commit.
|
|
245
|
+
const batch = spawnSync('git', ['cat-file', '--batch'], { cwd: repo, input: wanted.map((file) => `${commit}:${file}`).join('\n') + '\n', maxBuffer: 2 * 1024 * 1024 * 1024 });
|
|
246
|
+
if (batch.status !== 0 || batch.error) throw new Error(`git cat-file failed: ${(batch.stderr || batch.error || '').toString().trim()}`);
|
|
247
|
+
const out = batch.stdout as Buffer;
|
|
248
|
+
const contents = new Map<string, string>();
|
|
249
|
+
let at = 0;
|
|
250
|
+
for (const file of wanted) {
|
|
251
|
+
const end = out.indexOf(10, at);
|
|
252
|
+
if (end < 0) throw new Error('git cat-file output truncated');
|
|
253
|
+
const header = out.subarray(at, end).toString('utf8');
|
|
254
|
+
const size = /^[0-9a-f]+ blob (\d+)$/.exec(header);
|
|
255
|
+
if (!size) { at = end + 1; continue; } // «missing» (a submodule or symlink target) — read() answers undefined → opaque
|
|
256
|
+
const length = Number(size[1]);
|
|
257
|
+
contents.set(file, out.subarray(end + 1, end + 1 + length).toString('utf8'));
|
|
258
|
+
at = end + 1 + length + 1;
|
|
259
|
+
}
|
|
260
|
+
const preload = bunfigPreload(contents.get('bunfig.toml'));
|
|
261
|
+
if (preload === null) throw new Error('bunfig.toml [test].preload is unreadable');
|
|
262
|
+
return { files, read: (path) => contents.get(path), preload, aliases: tsconfigAliases(contents) };
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/** `compilerOptions.paths` entries of the form `"<prefix>*": ["<dir>/*"]` — scoped to the tsconfig's directory. */
|
|
266
|
+
function tsconfigAliases(contents: ReadonlyMap<string, string>): { scope: string; prefix: string; target: string }[] {
|
|
267
|
+
const aliases: { scope: string; prefix: string; target: string }[] = [];
|
|
268
|
+
for (const [file, text] of contents) {
|
|
269
|
+
if (!/(?:^|\/)tsconfig[\w.-]*\.json$/.test(file)) continue;
|
|
270
|
+
// TypeScript's own JSONC reader (comments, trailing commas, `src/**/*.ts` globs). An alias we cannot read would
|
|
271
|
+
// silently look like a package — so an unreadable tsconfig is an error (full gate), never skipped.
|
|
272
|
+
const parsed = ts.parseConfigFileTextToJson(file, text);
|
|
273
|
+
if (parsed.error) throw new Error(`tsconfig unreadable: ${file}`);
|
|
274
|
+
const options = (parsed.config as { compilerOptions?: { paths?: unknown; baseUrl?: unknown } } | undefined)?.compilerOptions;
|
|
275
|
+
const paths = options?.paths;
|
|
276
|
+
const baseUrl = options?.baseUrl;
|
|
277
|
+
if (!paths || typeof paths !== 'object') continue;
|
|
278
|
+
const scope = dirname(file) === '.' ? '' : dirname(file);
|
|
279
|
+
const base = normalize(join(scope || '.', typeof baseUrl === 'string' ? baseUrl : '.'));
|
|
280
|
+
for (const [key, targets] of Object.entries(paths as Record<string, unknown>)) {
|
|
281
|
+
if (key === '*' || !key.endsWith('*') || !Array.isArray(targets) || typeof targets[0] !== 'string' || !targets[0].endsWith('*')) continue;
|
|
282
|
+
aliases.push({ scope, prefix: key.slice(0, -1), target: normalize(join(base, targets[0].slice(0, -1))).replace(/\/$/, '') });
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return aliases;
|
|
286
|
+
}
|
|
@@ -14,12 +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';
|
|
19
20
|
import { isGateExecutor, type GateExecutor } from './gate-executor.js';
|
|
20
21
|
import { runPodCommand, parsePodCpu, resolvePodCpu, type RunPodCommandOptions, type PodCommandResult, type PodCpu } from '../../src/task-orchestrator/surfaces/pod-command-job.js';
|
|
21
22
|
import { POD_BUN_CACHE_HOST_PATH, parseInstallSeconds, podBunCacheVolume } from '../../src/task-orchestrator/surfaces/pod-bun-cache.js';
|
|
22
|
-
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';
|
|
23
24
|
import { POD_INSTALL_SLOTS_DEFAULT, POD_INSTALL_SLOTS_HOST_PATH, installSlotScript } from '../../src/task-orchestrator/surfaces/pod-install-slots.js';
|
|
24
25
|
import { gateDepsInstallScript, parseGateDeps } from '../../src/task-orchestrator/surfaces/pod-gate-deps.js';
|
|
25
26
|
import { gateShardsPath, recordGateShardsVerdict, writeGateShards, type GateShard } from '../../src/release-loop/gate-shards.js';
|
|
@@ -76,7 +77,13 @@ export interface GateOptions {
|
|
|
76
77
|
/** How often the stall watch reads the live log (ms); omitted = {@link GATE_POD_STALL_POLL_MS}. */
|
|
77
78
|
stallPollMs?: number;
|
|
78
79
|
/** GATE-LIVE-OBS — the shard state file (`gateShardsPath(version)`) this sweep keeps current; absent = not written. */
|
|
79
|
-
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 };
|
|
80
87
|
}
|
|
81
88
|
export interface GateResult {
|
|
82
89
|
outcome: 'ok' | 'regression' | 'error';
|
|
@@ -696,6 +703,18 @@ export const GATE_ADMISSION_WAIT_SECONDS_DEFAULT = 300;
|
|
|
696
703
|
export type GateAdmissionOutcome = 'granted' | 'admission-timeout' | 'admission-error' | 'off';
|
|
697
704
|
export interface GateAdmissionResult { outcome: GateAdmissionOutcome; waitedMs: number; reason?: string; release: () => void }
|
|
698
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
|
+
|
|
699
718
|
export function poolGateAdmission(pool: PodPoolScheduler): GateAdmission {
|
|
700
719
|
return { acquire: (signal) => pool.acquireAdmission(signal, undefined, { gate: true }), waitReason: () => pool.waitReason() };
|
|
701
720
|
}
|
|
@@ -840,9 +859,30 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
840
859
|
});
|
|
841
860
|
const stallWatch = stallSeconds > 0 && !!liveLog;
|
|
842
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
|
+
};
|
|
843
883
|
const jobLogTail = (job: PodCommandResult): string => {
|
|
844
884
|
if (podLogTail) return podLogTail(job);
|
|
845
|
-
for (const member of poolScheduler.members) {
|
|
885
|
+
for (const member of poolScheduler.members.filter((m) => !downMembers.has(m.context))) {
|
|
846
886
|
const logs = defaultKubectl(['--context', member.context, '-n', 'elanous-test', 'logs', `job/${job.job}`, '-c', 'child', '--tail=200']);
|
|
847
887
|
if (logs.status === 0 && logs.stdout) return logs.stdout;
|
|
848
888
|
}
|
|
@@ -882,16 +922,18 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
882
922
|
// Pending/Running in the cluster and the next admission reading counts it, so holding the lease would count it twice.
|
|
883
923
|
const trackedPool = (shard: number, retry: boolean, placed: (member: NonNullable<Awaited<ReturnType<PodPoolScheduler['tryAcquire']>>>) => void = () => {}): PodPoolScheduler => ({
|
|
884
924
|
members: poolScheduler.members,
|
|
885
|
-
tryAcquire: async (...
|
|
886
|
-
|
|
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);
|
|
887
929
|
if (member) placed(member);
|
|
888
|
-
if (member) markShard(shard, { state: retry ? 'retry' : 'running', startedAt: board[shard]?.startedAt ?? new Date().toISOString() }, ['waitReason']);
|
|
930
|
+
if (member) markShard(shard, { state: retry ? 'retry' : 'running', startedAt: board[shard]?.startedAt ?? new Date().toISOString(), host: member.context }, ['waitReason']);
|
|
889
931
|
else markShard(shard, { waitReason: 'Pod 자리 대기' });
|
|
890
932
|
return member;
|
|
891
933
|
},
|
|
892
934
|
release: (member: Parameters<PodPoolScheduler['release']>[0]) => poolScheduler.release(member),
|
|
893
935
|
}) as unknown as PodPoolScheduler;
|
|
894
|
-
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']> }> => {
|
|
895
937
|
// 파일별 소요는 junit 으로 남긴다 — 콘솔 요약(판정 원천)은 그대로이고, 느린 시험 목록(K10 D4)·계층 분리(D2)의 자가 된다.
|
|
896
938
|
// GATE-TIMEOUT-HEAL: parts (each with its own junit) under a soft deadline inside this shard's own Job deadline.
|
|
897
939
|
const shardDeadline = shardDeadlineSeconds(estimated(paths), deadlineSeconds);
|
|
@@ -910,6 +952,9 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
910
952
|
let jobExitCode: number | undefined;
|
|
911
953
|
let lastFile: string | undefined;
|
|
912
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;
|
|
913
958
|
let timedOut = false;
|
|
914
959
|
let finished: string[] = [];
|
|
915
960
|
let unfinishedLog: string | undefined;
|
|
@@ -960,7 +1005,8 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
960
1005
|
} }) : undefined;
|
|
961
1006
|
const placed = admitted.release;
|
|
962
1007
|
const job = await podCommand({
|
|
963
|
-
pool: pod.pool, poolScheduler: trackedPool(shard, depth > 0 || branch !== '', (member) => { jobContext = member.context; placed(); }), clone: true, source: { kind: 'commit', sha: commit }, deadlineSeconds: shardDeadline,
|
|
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 } : {}),
|
|
964
1010
|
...(bunCache ? { bunCache } : {}),
|
|
965
1011
|
...(installSlots > 0 ? { installSlots: POD_INSTALL_SLOTS_HOST_PATH } : {}),
|
|
966
1012
|
...(pod.cpu ? { cpu: pod.cpu } : {}),
|
|
@@ -982,12 +1028,16 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
982
1028
|
try { lastFile = lastStartedTestFile(jobLogTail(job).split(/\r?\n/).slice(-200).join('\n'), paths); }
|
|
983
1029
|
catch { /* Log retrieval is diagnostic; do not hide the stalled shard. */ }
|
|
984
1030
|
}
|
|
985
|
-
} catch {
|
|
1031
|
+
} catch (error) {
|
|
986
1032
|
jobFailed = true;
|
|
1033
|
+
jobError = error instanceof Error ? error.message : String(error);
|
|
987
1034
|
}
|
|
988
1035
|
// A launch that failed before placement still gives its admission back (release is idempotent).
|
|
989
1036
|
admitted?.release();
|
|
990
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);
|
|
991
1041
|
// A reused shard keeps its files as they are — rewriting would replace the measured durationMs the next release plans by.
|
|
992
1042
|
if (logDir && !reused && !descent) {
|
|
993
1043
|
mkdirSync(logDir, { recursive: true });
|
|
@@ -1004,12 +1054,14 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1004
1054
|
if (unfinishedLog !== undefined) writeFileSync(unfinishedDestination, unfinishedLog, { mode: 0o600 });
|
|
1005
1055
|
else rmSync(unfinishedDestination, { force: true });
|
|
1006
1056
|
writeFileSync(join(logDir, `pod-${shard}${branch}.json`), JSON.stringify({ durationMs, rc: rc ?? null,
|
|
1007
|
-
...(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,
|
|
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 } : {}),
|
|
1008
1059
|
...(admitted && admitted.outcome !== 'off' ? { admission: admitted.outcome, admissionWaitMs: admitted.waitedMs } : {}) }) + '\n', { mode: 0o600 });
|
|
1009
1060
|
}
|
|
1010
1061
|
const installSeconds = parseInstallSeconds(output ?? '');
|
|
1011
1062
|
// GATE-TIMEOUT-HEAL ③: a cut shard is «reason: timeout» (with the files that still finished), not a bare rc=null.
|
|
1012
|
-
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,
|
|
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 } : {}),
|
|
1013
1065
|
...(stall ? { reason: 'stall', finishedFiles: 0, stallSeconds: stall.seconds } : timedOut ? { reason: 'timeout', finishedFiles: finished.length } : {}) });
|
|
1014
1066
|
if (!reused && !descent) markShard(shard, { ...(installSeconds !== null ? { installSec: installSeconds } : {}), ...(rc !== undefined ? { rc } : {}), ...(timedOut ? { state: 'timeout' as const } : {}) });
|
|
1015
1067
|
let clean = output?.replace(/\x1b\[[0-9;]*[A-Za-z]/g, '');
|
|
@@ -1064,6 +1116,13 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1064
1116
|
debug.log('release-loop.gate', 'pod-shard-resume-miss', { shard, branch, reason });
|
|
1065
1117
|
return runShard(paths, shard, depth, branch, retriedNoOutput, true, parentAttempt);
|
|
1066
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
|
+
}
|
|
1067
1126
|
// GATE-TIMEOUT-HEAL ④: a Pod that died before any test ran (no outbox · jobExitCode≠0 — 0.2.19: isolated retries
|
|
1068
1127
|
// ending with exit 5, the source checkout) gets the same one fresh retry as «no output» instead of stalling the sweep.
|
|
1069
1128
|
// 137 (killed · OOM) keeps the isolation path: re-running an OOM file only re-measures the same kill.
|
|
@@ -1436,8 +1495,9 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
1436
1495
|
if (trustedKnown) knownFailures = [...trustedKnown.failures, ...(trustedKnown.errors ?? []), ...loadEnvKnownFailures(repo)];
|
|
1437
1496
|
} catch (error) { debug.log('release-loop.gate', 'known-failures-unavailable', { error: String(error) }); }
|
|
1438
1497
|
}
|
|
1498
|
+
const durationSource = join(ledger, 'release', opts.baselineVersion, 'gate-logs', 'cut');
|
|
1439
1499
|
const cutRun = await runner.sweep(cutTree, join(root, 'release', opts.version, 'gate-logs', partial ? 'cut-partial' : 'cut'),
|
|
1440
|
-
opts.pod ? { ...opts.pod, durationSource
|
|
1500
|
+
opts.pod ? { ...opts.pod, durationSource, ...(knownFailures.length ? { knownFailures } : {}),
|
|
1441
1501
|
// GATE-LIVE-OBS: the release ledger's shards.json (UX #24782) — never the run tree's universe; an explicit
|
|
1442
1502
|
// --ledger-root (tests) keeps it in that ledger.
|
|
1443
1503
|
shardsFile: { path: opts.ledgerRoot ? gateShardsPath(opts.version, opts.ledgerRoot) : gateShardsPath(opts.version), version: opts.version } } : undefined,
|
|
@@ -1526,7 +1586,7 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
1526
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
|
|
1527
1587
|
// after a 23-minute Pod cut).
|
|
1528
1588
|
const baseRun = await runner.sweep(baseTree!, join(root, 'release', opts.version, 'gate-logs', 'baseline'),
|
|
1529
|
-
opts.pod ? { ...opts.pod, durationSource
|
|
1589
|
+
opts.pod ? { ...opts.pod, durationSource } : undefined,
|
|
1530
1590
|
present);
|
|
1531
1591
|
baseline = await localStalled(baseRun, baseTree!, baseSha, 'baseline');
|
|
1532
1592
|
} else baseline = { failures: [], errors: [] };
|