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
|
@@ -2,9 +2,13 @@
|
|
|
2
2
|
// changed — not the whole 24-shard sweep again (0.2.18: two timeout fixes on top of an 84-minute gate meant another full
|
|
3
3
|
// gate). The plan is opt-in (`release resume --from gate --partial`) and fail-closed: any non-test source change, a prior
|
|
4
4
|
// gate record for another commit, or a missing record means «full gate», never a guess.
|
|
5
|
+
// GATE-REUSE-BY-DIFF (0.2.25): a code change no longer forces the full sweep — with the tip's import closure
|
|
6
|
+
// (gate-closure.ts) the plan adds every test that can reach a changed file, and every test whose closure is opaque.
|
|
7
|
+
// `release resume --from gate` now plans this by default when the tip moved (`--no-partial` = full gate).
|
|
5
8
|
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
6
9
|
import { spawnSync } from 'node:child_process';
|
|
7
10
|
import { dirname, join } from 'node:path';
|
|
11
|
+
import { CODE_FILE, type ClosureVerdict } from './gate-closure';
|
|
8
12
|
|
|
9
13
|
export interface PartialPlan {
|
|
10
14
|
version: string;
|
|
@@ -12,7 +16,7 @@ export interface PartialPlan {
|
|
|
12
16
|
priorCommit: string;
|
|
13
17
|
/** The commit this plan is for — a gate on any other commit ignores it. */
|
|
14
18
|
forCommit: string;
|
|
15
|
-
/** Test files re-run: changed test files ⊕ files of the prior failures. */
|
|
19
|
+
/** Test files re-run: changed test files ⊕ files of the prior failures ⊕ tests reaching a changed code file. */
|
|
16
20
|
files: string[];
|
|
17
21
|
priorFailures: string[];
|
|
18
22
|
priorErrors: string[];
|
|
@@ -37,19 +41,43 @@ export function testFileOfId(id: string): string {
|
|
|
37
41
|
|
|
38
42
|
/** Decide whether a partial re-gate is safe for `changedFiles` (prior → for). Never guesses: any doubt is a full gate. */
|
|
39
43
|
export function planPartialRegate(input: { version: string; priorCommit: string; forCommit: string; changedFiles: readonly string[];
|
|
40
|
-
priorFailures: readonly string[]; priorErrors: readonly string[]
|
|
41
|
-
|
|
44
|
+
priorFailures: readonly string[]; priorErrors: readonly string[];
|
|
45
|
+
/** GATE-REUSE-BY-DIFF: the tests reaching these changed code files (seeds include changed tests). Without it any
|
|
46
|
+
* non-test change is a full gate. */
|
|
47
|
+
closure?: (changedCode: string[]) => ClosureVerdict;
|
|
48
|
+
/** Tracked at the tip — a deleted changed test is not re-run (nothing to run); a deleted prior-failure file is a full
|
|
49
|
+
* gate (its failure can be neither re-measured nor carried). Without it every changed test is planned. */
|
|
50
|
+
existsAtTip?: (file: string) => boolean }):
|
|
51
|
+
{ ok: true; plan: PartialPlan; closure?: { reached: number; opaque: number } } | { ok: false; reason: string } {
|
|
42
52
|
if (!/^[0-9a-f]{40}$/i.test(input.priorCommit) || !/^[0-9a-f]{40}$/i.test(input.forCommit)) return { ok: false, reason: 'commit is not a full sha' };
|
|
43
53
|
if (input.priorCommit === input.forCommit) return { ok: false, reason: 'no new commit since the prior gate' };
|
|
44
54
|
const source = input.changedFiles.filter((file) => !TEST_FILE.test(file) && !isInert(file));
|
|
45
|
-
|
|
55
|
+
const list = (files: string[]) => `${files.slice(0, 5).join(', ')}${files.length > 5 ? ` (+${files.length - 5})` : ''}`;
|
|
56
|
+
// Only code is followed through the closure: a changed fixture, JSON, lockfile, config or script is a full gate.
|
|
57
|
+
const notCode = source.filter((file) => !CODE_FILE.test(file));
|
|
58
|
+
if (source.length && (!input.closure || notCode.length)) return { ok: false, reason: `non-test change → full gate: ${list(notCode.length ? notCode : source)}` };
|
|
59
|
+
let reaching: string[] = [];
|
|
60
|
+
let closure: { reached: number; opaque: number } | undefined;
|
|
61
|
+
if (source.length) {
|
|
62
|
+
let verdict: ClosureVerdict;
|
|
63
|
+
try { verdict = input.closure!(input.changedFiles.filter((file) => CODE_FILE.test(file))); }
|
|
64
|
+
catch (error) { return { ok: false, reason: `import closure unreadable → full gate: ${String(error instanceof Error ? error.message : error).slice(0, 200)}` }; }
|
|
65
|
+
if (!verdict.ok) return { ok: false, reason: verdict.reason };
|
|
66
|
+
const unsafe = verdict.tests.filter((file) => !TEST_FILE.test(file) || file.split('/').includes('..'));
|
|
67
|
+
if (unsafe.length) return { ok: false, reason: `closure selected a test path the plan cannot carry → full gate: ${list(unsafe)}` };
|
|
68
|
+
reaching = verdict.tests;
|
|
69
|
+
closure = { reached: verdict.reached, opaque: verdict.opaque };
|
|
70
|
+
}
|
|
46
71
|
let failureFiles: string[];
|
|
47
72
|
try { failureFiles = [...input.priorFailures, ...input.priorErrors].map(testFileOfId); }
|
|
48
73
|
catch (error) { return { ok: false, reason: String(error instanceof Error ? error.message : error) }; }
|
|
49
|
-
const
|
|
74
|
+
const exists = input.existsAtTip ?? (() => true);
|
|
75
|
+
const gone = failureFiles.filter((file) => !exists(file));
|
|
76
|
+
if (gone.length) return { ok: false, reason: `prior failure file deleted at the tip → full gate: ${list(gone)}` };
|
|
77
|
+
const files = [...new Set([...input.changedFiles.filter((file) => TEST_FILE.test(file) && exists(file)), ...failureFiles, ...reaching])].sort();
|
|
50
78
|
if (!files.length) return { ok: false, reason: 'nothing to re-run' };
|
|
51
79
|
return { ok: true, plan: { version: input.version, priorCommit: input.priorCommit, forCommit: input.forCommit, files,
|
|
52
|
-
priorFailures: [...input.priorFailures], priorErrors: [...input.priorErrors] } };
|
|
80
|
+
priorFailures: [...input.priorFailures], priorErrors: [...input.priorErrors] }, ...(closure ? { closure } : {}) };
|
|
53
81
|
}
|
|
54
82
|
|
|
55
83
|
export function writePartialPlan(root: string, plan: PartialPlan): string {
|
|
@@ -9,7 +9,7 @@ import { prereleaseKind } from './release-version.js';
|
|
|
9
9
|
interface HostResult { host: string; ok: boolean; before: string; after: string; error?: string; skipped?: string; hooks?: string; restart?: 'no-service' }
|
|
10
10
|
/** Where the internal feed is packed from: a checkout of the release tag, never the run's working tree. */
|
|
11
11
|
export interface FeedSource { checkout: string; commit: string; deps: 'linked' | 'installed'; cleanup: () => void }
|
|
12
|
-
interface OpsUpgradeDeps { exists?: (path: string) => boolean; installSource?: () => string | null; feedSource?: (version: string) => FeedSource }
|
|
12
|
+
interface OpsUpgradeDeps { exists?: (path: string) => boolean; installSource?: () => string | null; installCommit?: () => string | null; feedSource?: (version: string) => FeedSource }
|
|
13
13
|
|
|
14
14
|
/** Detached worktree at refs/tags/v<version> with the PWA built — the feed ships what was released (10-04 0.2.11:
|
|
15
15
|
* packing process.cwd() shipped main HEAD ee689ad2 as «0.2.11» to node-b·cloud-vm instead of the cut 31129c91). */
|
|
@@ -52,16 +52,24 @@ export function tagFeedSource(run: CommandRunner, repo: string = process.cwd()):
|
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
function
|
|
55
|
+
function installMetadata(text: string): { source: string | null; commit: string | null } {
|
|
56
56
|
try {
|
|
57
|
-
const data: unknown = JSON.parse(
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
const data: unknown = JSON.parse(text);
|
|
58
|
+
if (!data || typeof data !== 'object') return { source: null, commit: null };
|
|
59
|
+
return {
|
|
60
|
+
source: 'source' in data && typeof data.source === 'string' ? data.source : null,
|
|
61
|
+
commit: 'commit' in data && typeof data.commit === 'string' ? data.commit : null,
|
|
62
|
+
};
|
|
63
|
+
} catch { return { source: null, commit: null }; }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function localInstallMetadata(): { source: string | null; commit: string | null } {
|
|
67
|
+
try { return installMetadata(readFileSync(join(homedir(), '.local/share/elanous/install.json'), 'utf8')); }
|
|
68
|
+
catch { return { source: null, commit: null }; }
|
|
60
69
|
}
|
|
61
70
|
|
|
62
|
-
function installedMatches(installed: unknown, version: string
|
|
63
|
-
return installed === version
|
|
64
|
-
&& installed.startsWith(`${version}-`) && /^[0-9a-f]{12}$/.test(installed.slice(version.length + 1));
|
|
71
|
+
function installedMatches(installed: unknown, version: string): boolean {
|
|
72
|
+
return installed === version;
|
|
65
73
|
}
|
|
66
74
|
|
|
67
75
|
function missingService(data: Record<string, unknown> | undefined): boolean {
|
|
@@ -116,17 +124,21 @@ export function runOpsUpgrade(run: CommandRunner = runCommand, deps: OpsUpgradeD
|
|
|
116
124
|
}
|
|
117
125
|
const results: HostResult[] = [];
|
|
118
126
|
for (const host of hosts as string[]) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const
|
|
127
|
+
// Injected install metadata replaces the machine's install.json entirely — a test that injects only the source
|
|
128
|
+
// must not inherit this machine's real commit (that turned update cases into kept-ahead skips).
|
|
129
|
+
const injected = deps.installSource !== undefined || deps.installCommit !== undefined;
|
|
130
|
+
let metadata: { source: string | null; commit: string | null } = host !== 'local'
|
|
131
|
+
? { source: null, commit: null }
|
|
132
|
+
: injected
|
|
133
|
+
? { source: deps.installSource?.() ?? null, commit: deps.installCommit?.() ?? null }
|
|
134
|
+
: localInstallMetadata();
|
|
135
|
+
let source = metadata.source;
|
|
136
|
+
let railSource = false;
|
|
137
|
+
let path: 'checkout' | 'release' = 'release';
|
|
122
138
|
const record = (result: HostResult) => {
|
|
123
139
|
results.push(result);
|
|
124
140
|
debug.log('release-loop.ops-upgrade', 'host', { host, path, restart: result.restart ?? (context.input.opsRestart === true ? result.ok ? 'ok' : 'failed' : 'skipped'), ok: result.ok });
|
|
125
141
|
};
|
|
126
|
-
if (host !== 'local' && feedError) {
|
|
127
|
-
record({ host, ok: false, before: '', after: '', skipped: '내부 피드 발행 실패', error: feedError });
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
142
|
const command = (args: string[]): CommandResult => host === 'local'
|
|
131
143
|
? run('elanous', args)
|
|
132
144
|
// The installer's own bin dir comes first — a --no-modify-path install (the bot VM) has elanous only there.
|
|
@@ -134,35 +146,41 @@ export function runOpsUpgrade(run: CommandRunner = runCommand, deps: OpsUpgradeD
|
|
|
134
146
|
let before = '';
|
|
135
147
|
let after = '';
|
|
136
148
|
try {
|
|
149
|
+
if (host !== 'local') {
|
|
150
|
+
const installed = run('ssh', [host, `cat "$HOME/.local/share/elanous/install.json" 2>/dev/null || true`]);
|
|
151
|
+
if (installed.status === 0) metadata = installMetadata(installed.stdout);
|
|
152
|
+
}
|
|
153
|
+
source = metadata.source;
|
|
154
|
+
if (source !== null && isAbsolute(source)) {
|
|
155
|
+
const repository = host === 'local'
|
|
156
|
+
? run('git', ['rev-parse', '--is-inside-work-tree'], source)
|
|
157
|
+
: run('ssh', [host, `git -C ${quote(source)} rev-parse --is-inside-work-tree`]);
|
|
158
|
+
railSource = repository.status === 0 && firstLine(repository.stdout) === 'true';
|
|
159
|
+
}
|
|
160
|
+
path = railSource ? 'checkout' : 'release';
|
|
137
161
|
const previous = command(['--version']);
|
|
138
162
|
if (previous.status === 0) before = firstLine(previous.stdout);
|
|
139
|
-
if (
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (
|
|
148
|
-
record({ host, ok:
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
const tag = git(['rev-parse', '--verify', `refs/tags/v${version}^{commit}`]);
|
|
152
|
-
if (tag.status !== 0 || !tag.stdout.trim()) {
|
|
153
|
-
record({ host, ok: false, before, after, error: `태그 없음: v${version}` });
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
const moved = git(['checkout', '--detach', `v${version}`]);
|
|
157
|
-
if (moved.status !== 0) {
|
|
158
|
-
record({ host, ok: false, before, after, error: `체크아웃 전환 실패: ${reason(moved)}` });
|
|
163
|
+
if (railSource) {
|
|
164
|
+
record({ host, ok: true, before, after: before, skipped: 'rail-source' });
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const installedCommit = metadata.commit;
|
|
168
|
+
if (installedCommit && /^[0-9a-f]{40,64}$/.test(installedCommit)) {
|
|
169
|
+
const ahead = run('git', ['merge-base', '--is-ancestor', `refs/tags/v${version}`, installedCommit]);
|
|
170
|
+
// Only exit 1 proves non-ancestry; missing local objects must not permit a potentially destructive downgrade.
|
|
171
|
+
if (ahead.status !== 1) {
|
|
172
|
+
record({ host, ok: true, before, after: before, skipped: 'kept-ahead' });
|
|
159
173
|
continue;
|
|
160
174
|
}
|
|
161
175
|
}
|
|
162
|
-
|
|
176
|
+
if (host !== 'local' && feedError) {
|
|
177
|
+
record({ host, ok: false, before, after: '', skipped: '내부 피드 발행 실패', error: feedError });
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
const updated = command(['update', ...(host !== 'local' && feedPath !== undefined ? [] : ['--version', version]), '--json', ...(context.input.opsRestart === true ? ['--restart'] : [])]);
|
|
163
181
|
const updateResult = lastResult(updated);
|
|
164
182
|
const noService = host !== 'local' && context.input.opsRestart === true && updateResult?.exitCode === 1 && missingService(updateResult);
|
|
165
|
-
if ((!noService && (updated.status !== 0 || updateResult?.exitCode !== 0)) || !installedMatches(updateResult?.installedVersion, version
|
|
183
|
+
if ((!noService && (updated.status !== 0 || updateResult?.exitCode !== 0)) || !installedMatches(updateResult?.installedVersion, version)) {
|
|
166
184
|
const error = (updated.status === 0 && updateResult?.exitCode === 0 || noService)
|
|
167
185
|
? `설치판 불일치 (기대 ${version}, 실제 ${String(updateResult?.installedVersion)})` : reason(updated);
|
|
168
186
|
record({ host, ok: false, before, after, error });
|
|
@@ -174,7 +192,7 @@ export function runOpsUpgrade(run: CommandRunner = runCommand, deps: OpsUpgradeD
|
|
|
174
192
|
continue;
|
|
175
193
|
}
|
|
176
194
|
after = firstLine(current.stdout);
|
|
177
|
-
if (!installedMatches(after.split(/\s+/)[0], version
|
|
195
|
+
if (!installedMatches(after.split(/\s+/)[0], version)) {
|
|
178
196
|
record({ host, ok: false, before, after, error: `올렸는데 판이 그대로 (기대 ${version}, 실제 ${after || '없음'})` });
|
|
179
197
|
continue;
|
|
180
198
|
}
|
|
@@ -198,14 +216,17 @@ export function runOpsUpgrade(run: CommandRunner = runCommand, deps: OpsUpgradeD
|
|
|
198
216
|
feed: feedError ? 'failed' as const : feedPath === undefined ? 'skipped-no-path' as const : 'published' as const, hosts: results };
|
|
199
217
|
}
|
|
200
218
|
|
|
201
|
-
|
|
219
|
+
function main(): void {
|
|
202
220
|
let version = '';
|
|
203
221
|
try {
|
|
204
222
|
version = readGraphContext().input.version;
|
|
205
223
|
// RELEASE-REHEARSAL-RC: a prerelease never lands docs on main or upgrades the operating hosts.
|
|
206
|
-
|
|
207
|
-
? { outcome: 'ok', verdict: 'pass', summary: `ops-upgrade skipped — ${version} is a prerelease`, skipped: 'prerelease' }
|
|
208
|
-
: runOpsUpgrade()
|
|
224
|
+
const result = prereleaseKind(version) !== null
|
|
225
|
+
? { outcome: 'ok' as const, verdict: 'pass' as const, summary: `ops-upgrade skipped — ${version} is a prerelease`, skipped: 'prerelease' }
|
|
226
|
+
: runOpsUpgrade(runCommand);
|
|
227
|
+
process.exitCode = finishNode('ops-upgrade', version, result);
|
|
209
228
|
}
|
|
210
229
|
catch (error) { process.exitCode = finishNode('ops-upgrade', version, errorResult(error)); }
|
|
211
230
|
}
|
|
231
|
+
|
|
232
|
+
if (import.meta.main) main();
|
|
@@ -5,6 +5,7 @@ import { join } from 'node:path';
|
|
|
5
5
|
import { WARNING_ONLY } from './auto-approve-node.js';
|
|
6
6
|
import { beginLandingMerge, landingFreezeMessage } from '../../src/release-loop/landing-freeze.js';
|
|
7
7
|
import { debug } from '../../src/debug/log.js';
|
|
8
|
+
import { registerStandaloneLogSink } from '../../src/domains/standalone-log-sink.js';
|
|
8
9
|
import { prereleaseKind } from './release-version.js';
|
|
9
10
|
import { publishAtError } from './publish-at.js';
|
|
10
11
|
import { errorResult, finishNode, lastResult, nodeOutput, readGraphContext, runCommand, type CommandRunner } from './node-verdict.js';
|
|
@@ -115,6 +116,11 @@ export function runPublish(run: CommandRunner = runCommand, prodFreezeRoot?: str
|
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
if (import.meta.main) {
|
|
119
|
+
// GATE-OBS2-2 — the release graph spawns this node as its own process, which inherits no daemon sink. Without this the
|
|
120
|
+
// `release-loop.publish / result` event (the one that says whether the irreversible publication actually happened) stayed
|
|
121
|
+
// in the file trail and `elanous logs` read 0 rows. Same pattern as gate-node.ts and auto-approve-node.ts (GATE-OBS2-1).
|
|
122
|
+
// Observability must never stop the publication node — a failed sink registration is skipped (fail-soft).
|
|
123
|
+
try { await registerStandaloneLogSink('release-loop'); } catch { /* fail-soft: the verdict still goes to stdout */ }
|
|
118
124
|
let version = '';
|
|
119
125
|
try { version = readGraphContext().input.version; process.exitCode = finishNode('publish', version, runPublish()); }
|
|
120
126
|
catch (error) { process.exitCode = finishNode('publish', version, errorResult(error)); }
|
|
@@ -10,6 +10,7 @@ import { lastJsonObject, runGraph, type GraphRunState } from '../../src/graph-ru
|
|
|
10
10
|
import { releaseRunUniverse } from './release-universe.js';
|
|
11
11
|
import { isReleaseVersion } from './release-version.js';
|
|
12
12
|
import { changedFilesBetween, clearPartialPlan, planPartialRegate, writePartialPlan } from './gate-partial.js';
|
|
13
|
+
import { gitClosureTree, testsReachingChanged, type ClosureTree } from './gate-closure.js';
|
|
13
14
|
import { releaseResumeAcceptedRegressions, releaseResumeGateExecutor, releaseResumeWaiver, type AcceptedRegression } from './resume.js';
|
|
14
15
|
|
|
15
16
|
const GRAPH = join(import.meta.dir, '../../graphs/release/release-loop.yaml');
|
|
@@ -29,8 +30,15 @@ export interface ResumeReleaseDeps {
|
|
|
29
30
|
now?: () => Date;
|
|
30
31
|
/** Machine release ledger (gate-failures.json of the prior gate) — default releaseLedgerRoot(). */
|
|
31
32
|
ledgerRoot?: string;
|
|
33
|
+
/** GATE-REUSE-BY-DIFF: the tip tree for the import closure — default read from git objects of the tip commit. */
|
|
34
|
+
closureTree?: (commit: string) => ClosureTree;
|
|
32
35
|
}
|
|
33
36
|
|
|
37
|
+
/** CLI entry files: a production file that only names one (help text, a goal's command line) does not run it. */
|
|
38
|
+
const CLI_ENTRIES = ['bin/elanous.mjs', 'src/index.ts'] as const;
|
|
39
|
+
/** The gate's own test-file set (gate-node sweep: `git ls-files '*.test.*'` filtered by this). */
|
|
40
|
+
const GATE_TEST = /\.test\.(?:tsx?|jsx?|mts|cts)$/;
|
|
41
|
+
|
|
34
42
|
export interface TipRefresh { version: string; branch: string; from: string; to: string; changed: boolean; statePath: string; backup?: string }
|
|
35
43
|
|
|
36
44
|
function gitRunner(repo: string) {
|
|
@@ -114,18 +122,25 @@ export function refreshReleaseBranchTip(runId: string, deps: ResumeReleaseDeps =
|
|
|
114
122
|
return { version, branch, from: recorded, to, changed: true, statePath, backup };
|
|
115
123
|
}
|
|
116
124
|
|
|
117
|
-
/** GATE-PARTIAL outcome of a resume — applied (plan written) or the reason it fell back to a full gate.
|
|
118
|
-
|
|
125
|
+
/** GATE-PARTIAL outcome of a resume — applied (plan written) or the reason it fell back to a full gate. `auto` marks the
|
|
126
|
+
* default plan of `--from gate` (GATE-REUSE-BY-DIFF); `closure` is set when code changes were followed. */
|
|
127
|
+
export type PartialOutcome = { requested: false }
|
|
128
|
+
| { requested: true; applied: true; files: number; priorCommit: string; auto?: true; closure?: { reached: number; opaque: number } }
|
|
129
|
+
| { requested: true; applied: false; reason: string; auto?: true };
|
|
119
130
|
|
|
120
|
-
/** Build (or clear) the partial re-gate plan for the new tip. Fail-closed: any doubt leaves a full gate.
|
|
131
|
+
/** Build (or clear) the partial re-gate plan for the new tip. Fail-closed: any doubt leaves a full gate.
|
|
132
|
+
* GATE-REUSE-BY-DIFF: `--from gate` on a moved tip plans by default (`partial: undefined`); `partial: false` opts out. */
|
|
121
133
|
function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean }, deps: ResumeReleaseDeps): PartialOutcome {
|
|
122
134
|
const root = deps.root ?? effectiveInstanceRoot();
|
|
123
|
-
// Any resume starts from no plan: one without
|
|
135
|
+
// Any resume starts from no plan: one without a partial plan (from whatever node) never inherits an older plan.
|
|
124
136
|
clearPartialPlan(root, tip.version);
|
|
125
|
-
|
|
137
|
+
const auto = opts.partial === undefined;
|
|
138
|
+
// The default applies only where it can help: a gate re-run on a tip that moved. An unchanged tip re-uses its own
|
|
139
|
+
// shard results by commit already.
|
|
140
|
+
if (opts.partial === false || (auto && (opts.from !== 'gate' || !tip.changed))) return { requested: false };
|
|
126
141
|
const fallback = (reason: string): PartialOutcome => {
|
|
127
|
-
debug.log('release-loop.resume', 'partial-fallback', { version: tip.version, reason });
|
|
128
|
-
return { requested: true, applied: false, reason };
|
|
142
|
+
debug.log('release-loop.resume', 'partial-fallback', { version: tip.version, reason, auto });
|
|
143
|
+
return { requested: true, applied: false, reason, ...(auto ? { auto: true as const } : {}) };
|
|
129
144
|
};
|
|
130
145
|
if (opts.from !== 'gate') return fallback('--partial only applies to --from gate');
|
|
131
146
|
if (!tip.changed) return fallback('release branch tip unchanged — nothing new to re-check');
|
|
@@ -151,12 +166,23 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
|
|
|
151
166
|
// The tip is already refreshed — a failed diff must not strand the resume: run the full gate and say why.
|
|
152
167
|
return fallback(`changed files unreadable: ${String(error instanceof Error ? error.message : error).slice(0, 200)}`);
|
|
153
168
|
}
|
|
169
|
+
// The tip tree (git objects of tip.to, never the working tree): existence of planned tests ⊕ the import closure.
|
|
170
|
+
let tree: ClosureTree;
|
|
171
|
+
try { tree = (deps.closureTree ?? ((commit: string) => gitClosureTree(deps.repo ?? process.cwd(), commit)))(tip.to); }
|
|
172
|
+
catch (error) { return fallback(`tip tree unreadable: ${String(error instanceof Error ? error.message : error).slice(0, 200)}`); }
|
|
173
|
+
const tracked = new Set(tree.files);
|
|
174
|
+
const tests = tree.files.filter((file) => GATE_TEST.test(file));
|
|
154
175
|
const planned = planPartialRegate({ version: tip.version, priorCommit: tip.from, forCommit: tip.to, changedFiles: changed,
|
|
155
|
-
priorFailures: prior.failures as string[], priorErrors: (prior.errors ?? []) as string[]
|
|
176
|
+
priorFailures: prior.failures as string[], priorErrors: (prior.errors ?? []) as string[],
|
|
177
|
+
existsAtTip: (file) => tracked.has(file),
|
|
178
|
+
closure: (changedCode) => testsReachingChanged(tree, changedCode, tests, { cliEntries: CLI_ENTRIES }) });
|
|
156
179
|
if (!planned.ok) return fallback(planned.reason);
|
|
180
|
+
if (planned.plan.files.length >= tests.length) return fallback(`every test is re-run (${tests.length}) — full gate`);
|
|
157
181
|
const path = writePartialPlan(root, planned.plan);
|
|
158
|
-
debug.log('release-loop.resume', 'partial-planned', { version: tip.version, priorCommit: tip.from, commit: tip.to, files: planned.plan.files.length,
|
|
159
|
-
|
|
182
|
+
debug.log('release-loop.resume', 'partial-planned', { version: tip.version, priorCommit: tip.from, commit: tip.to, files: planned.plan.files.length,
|
|
183
|
+
tests: tests.length, auto, path, ...(planned.closure ? { reached: planned.closure.reached, opaque: planned.closure.opaque } : {}) });
|
|
184
|
+
return { requested: true, applied: true, files: planned.plan.files.length, priorCommit: tip.from,
|
|
185
|
+
...(auto ? { auto: true as const } : {}), ...(planned.closure ? { closure: planned.closure } : {}) };
|
|
160
186
|
}
|
|
161
187
|
|
|
162
188
|
/** The graph file the run started from — a snapshot run refuses any other path (the installed CLI's own copy differs). */
|
|
@@ -183,6 +209,7 @@ export async function resumeReleaseRun(opts: { runId: string; from: string; part
|
|
|
183
209
|
if (gateExecutor) debug.log('release-loop.resume', 'gate-executor', { runId: opts.runId, from: opts.from, executor: gateExecutor.set.gateExecutor, host: gateExecutor.set.gateRemote ?? null, previous: gateExecutor.previous });
|
|
184
210
|
const waiver = original && (opts.waive !== undefined || opts.reason !== undefined)
|
|
185
211
|
? releaseResumeWaiver(original, opts) : undefined;
|
|
212
|
+
if (waiver) debug.log('release-loop.resume', 'waiver', { runId: opts.runId, node: waiver.nodeId, reason: waiver.reason, unmeasured: waiver.unmeasured ?? null });
|
|
186
213
|
const acceptedRegressions = original && opts.acceptedRegressions !== undefined && !waiver
|
|
187
214
|
? releaseResumeAcceptedRegressions(original, opts.acceptedRegressions, opts.from) : undefined;
|
|
188
215
|
// A waived verdict may reuse its original commit. A moved tip still requires a full re-gate.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { lastJsonObject, STOPPED_NODE_ERROR, type GraphRunState } from '../../src/graph-runner/runner.js';
|
|
2
2
|
import { publicWaiverReason } from './known-issues-node.js';
|
|
3
3
|
import { checkGateExecutorChoice, type GateExecutor } from './gate-executor.js';
|
|
4
4
|
|
|
@@ -28,11 +28,66 @@ export function releaseResumeAcceptedRegressions(state: GraphRunState, additions
|
|
|
28
28
|
return combined;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
const SWEEP_INCOMPLETE = /^(?:pod sweep incomplete|sweep incomplete|[\w-]+ shard incomplete)\b/;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* RELEASE-WAIVE-UNMEASURED — why the saved gate record at `index` carries no verdict, or undefined when it does (or may).
|
|
35
|
+
* Fail-closed: only three shapes count, each read from what the runner and gate-node actually write:
|
|
36
|
+
* ① stopped — `graph runs stop` wrote {ok:false, executed:false, exit:null, error:STOPPED_NODE_ERROR} as the run's last record;
|
|
37
|
+
* ② no verdict — the gate process ended with exit null (signal, idle timeout, or a spawn that failed) and printed no result line at
|
|
38
|
+
* all (no output line is a JSON object with an `outcome`), so the run went gate → failed. A verdict followed by more
|
|
39
|
+
* log lines and then a kill is still a verdict: refused;
|
|
40
|
+
* ③ sweep incomplete — gate-node printed outcome `error` (no verdict: the baseline comparison never ran, so `introduced`
|
|
41
|
+
* is still the empty initial list) and the error is an incomplete sweep (stalled shards / no tests ran). The finished
|
|
42
|
+
* shards' counts go into the detail. Any other error (cleanup, baseline persistence, …) may follow a verdict: refused.
|
|
43
|
+
* An `outcome: fail` (introduced > 0), an introduced list that is not empty, or an already-waived record is never unmeasured.
|
|
44
|
+
*/
|
|
45
|
+
/** Any output line that is a JSON object naming an `outcome` — a verdict was printed, whatever came after it. */
|
|
46
|
+
function printedVerdict(output: unknown): boolean {
|
|
47
|
+
if (typeof output !== 'string') return false;
|
|
48
|
+
return output.split('\n').some((line) => {
|
|
49
|
+
const text = line.trim();
|
|
50
|
+
if (!text.startsWith('{')) return false;
|
|
51
|
+
try { const value = JSON.parse(text) as unknown; return !!value && typeof value === 'object' && 'outcome' in value; } catch { return false; }
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function gateUnmeasuredDetail(state: GraphRunState, index: number): string | undefined {
|
|
56
|
+
const record = state.nodes[index];
|
|
57
|
+
if (!record || record.nodeId !== 'gate' || state.path[index] !== 'gate' || record.ok !== false || record.waiver !== undefined) return undefined;
|
|
58
|
+
if (state.nodes.some((saved) => saved.nodeId === 'gate' && saved !== record)) return undefined;
|
|
59
|
+
const last = index === state.path.length - 1 && index === state.nodes.length - 1;
|
|
60
|
+
if (last && record.executed === false && record.exit === null && record.error === STOPPED_NODE_ERROR && record.output === undefined) {
|
|
61
|
+
return `stopped: ${STOPPED_NODE_ERROR}${typeof record.seconds === 'number' ? ` after ${record.seconds}s` : ''}`;
|
|
62
|
+
}
|
|
63
|
+
const failedAfter = index === state.path.length - 2 && state.path.at(-1) === 'failed' && state.nodes.at(-1)?.nodeId === 'failed'
|
|
64
|
+
&& state.nodes.length === state.path.length;
|
|
65
|
+
if (!failedAfter || record.executed !== true) return undefined;
|
|
66
|
+
const result = lastJsonObject(record.output);
|
|
67
|
+
if (result === undefined) {
|
|
68
|
+
if (record.exit !== null || printedVerdict(record.output)) return undefined;
|
|
69
|
+
return `no-verdict: gate process ended with exit null and no result line${record.error ? ` — ${record.error.split('\n')[0]!.slice(0, 120)}` : ''}`;
|
|
70
|
+
}
|
|
71
|
+
if (result.outcome !== 'error' || !Array.isArray(result.introduced) || result.introduced.length !== 0) return undefined;
|
|
72
|
+
const error = typeof result.error === 'string' ? result.error : '';
|
|
73
|
+
if (!SWEEP_INCOMPLETE.test(error)) return undefined;
|
|
74
|
+
// The detail keeps what WAS measured: each stalled shard's reason and the finished shards' counts (failures there are
|
|
75
|
+
// not yet split into introduced vs pre-existing — the baseline comparison never ran).
|
|
76
|
+
const stalled = Array.isArray(result.stalledShards) ? result.stalledShards as Array<{ reason?: unknown }> : [];
|
|
77
|
+
const reasons = new Map<string, number>();
|
|
78
|
+
for (const shard of stalled) reasons.set(String(shard?.reason ?? 'unknown'), (reasons.get(String(shard?.reason ?? 'unknown')) ?? 0) + 1);
|
|
79
|
+
const summary = result.partialSummary as { pass?: unknown; fail?: unknown; errors?: unknown; ran?: unknown } | undefined;
|
|
80
|
+
const measured = summary && typeof summary === 'object'
|
|
81
|
+
? ` · finished shards measured pass ${String(summary.pass)} fail ${String(summary.fail)} errors ${String(summary.errors)} ran ${String(summary.ran)}` : '';
|
|
82
|
+
return `sweep incomplete: ${error.split('\n')[0]!.slice(0, 200)}`
|
|
83
|
+
+ (stalled.length ? ` · stalled shards ${stalled.length} (${[...reasons].map(([reason, count]) => `${reason}×${count}`).join(', ')})` : '') + measured;
|
|
84
|
+
}
|
|
85
|
+
|
|
31
86
|
/** Only the recorded failure at the restart boundary may be waived. `--from` is never a waiver. */
|
|
32
87
|
export function releaseResumeWaiver(
|
|
33
88
|
state: GraphRunState,
|
|
34
89
|
opts: { from: string; waive?: string; reason?: string; acceptedRegressions?: AcceptedRegression[] },
|
|
35
|
-
): { nodeId: string; reason: string; acceptedRegressions: AcceptedRegression[] } | undefined {
|
|
90
|
+
): { nodeId: string; reason: string; acceptedRegressions: AcceptedRegression[]; unmeasured?: string } | undefined {
|
|
36
91
|
if ((opts.waive === undefined) !== (opts.reason === undefined)) throw new Error('--waive <node> and --reason <text> must be supplied together');
|
|
37
92
|
if (opts.waive !== undefined && !opts.waive.trim()) throw new Error('--waive must not be empty');
|
|
38
93
|
if (opts.waive === undefined) return undefined;
|
|
@@ -41,13 +96,23 @@ export function releaseResumeWaiver(
|
|
|
41
96
|
publicWaiverReason(reason);
|
|
42
97
|
if (opts.from !== opts.waive) throw new Error('--from must name the waived node; --from alone does not waive a failure');
|
|
43
98
|
const index = state.path.indexOf(opts.waive);
|
|
44
|
-
|
|
99
|
+
const unique = index >= 0 && state.path.lastIndexOf(opts.waive) === index;
|
|
100
|
+
// RELEASE-WAIVE-UNMEASURED (OP 10-12): a gate cut before any verdict is the only gate that may be waived — a measured gate
|
|
101
|
+
// failure or an already-waived gate stays refused (fail-closed below), and the ledger records why it was unmeasured.
|
|
102
|
+
const unmeasured = opts.waive === 'gate' && unique ? gateUnmeasuredDetail(state, index) : undefined;
|
|
103
|
+
if (unmeasured !== undefined) {
|
|
104
|
+
return { nodeId: opts.waive, reason, unmeasured, acceptedRegressions: releaseResumeAcceptedRegressions(state,
|
|
105
|
+
[...(opts.acceptedRegressions ?? []), { id: `waive:${opts.waive}`, note: reason }]) };
|
|
106
|
+
}
|
|
107
|
+
if (!unique || state.path.lastIndexOf(opts.waive) !== index || state.nodes[index]?.nodeId !== opts.waive || state.nodes[index]?.ok !== false ||
|
|
45
108
|
state.nodes[index]?.executed !== true || state.path.at(-2) !== opts.waive || state.path.at(-1) !== 'failed' || state.nodes.at(-1)?.nodeId !== 'failed') {
|
|
46
109
|
throw new Error(`--waive ${opts.waive} requires the failed executable node on the saved path`);
|
|
47
110
|
}
|
|
48
111
|
// Only user-visible, non-publication checks can become accepted regressions; build, safety and approval gates stay blocking.
|
|
112
|
+
// A measured gate failure (outcome fail / introduced > 0, or any error that is not an incomplete sweep) lands here too.
|
|
49
113
|
if (!['mac-smoke', 'tui'].includes(opts.waive)) {
|
|
50
|
-
throw new Error(`--waive ${opts.waive} cannot waive a safety, side-effect or approval node`
|
|
114
|
+
throw new Error(`--waive ${opts.waive} cannot waive a safety, side-effect or approval node`
|
|
115
|
+
+ (opts.waive === 'gate' ? ' (only an unmeasured gate — stopped, killed or sweep incomplete — may be waived)' : ''));
|
|
51
116
|
}
|
|
52
117
|
return { nodeId: opts.waive, reason, acceptedRegressions: releaseResumeAcceptedRegressions(state,
|
|
53
118
|
[...(opts.acceptedRegressions ?? []), { id: `waive:${opts.waive}`, note: reason }]) };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 승격 착지의 base 쪽 tsc 진단을 «main 커밋별로 한 번만» 재고 재사용하는 캐시.
|
|
3
|
-
* 키 = base sha ⊕ tsc 명령 ⊕ 설정 파일(base 커밋에서 git show 로 읽은 내용) 해시 ⊕ tsc 판.
|
|
3
|
+
* 키 = base sha ⊕ tsc 명령 ⊕ 설정 파일(base 커밋에서 git show 로 읽은 내용) 해시 ⊕ 작업 트리 설치 키(bun.lock 실물) ⊕ tsc 판.
|
|
4
4
|
* 값 = rebase 전 정규화 진단(워크트리 절대 경로는 «<ROOT>» 자리표로 저장).
|
|
5
5
|
* ⛔ 캐시 실패는 호출자에게 던지지 않는다 — 읽기·쓰기 모두 miss 로 떨어진다.
|
|
6
6
|
*/
|
|
@@ -13,7 +13,7 @@ import type { TypecheckError } from '../src/typecheck-ratchet.js';
|
|
|
13
13
|
|
|
14
14
|
export const ROOT_PLACEHOLDER = '<ROOT>';
|
|
15
15
|
export const BASELINE_CACHE_MAX_ENTRIES = 50;
|
|
16
|
-
const CACHE_FORMAT =
|
|
16
|
+
const CACHE_FORMAT = 2;
|
|
17
17
|
|
|
18
18
|
/** 키에 들어가는 «기준 커밋의 설정 파일» — 이 중 하나라도 달라지면 같은 sha 라도 다른 진단이다. */
|
|
19
19
|
export const BASELINE_CACHE_CONFIG_FILES = ['tsconfig.gate.json', 'apps/pwa/tsconfig.json', 'tsconfig.pwa-env.json', 'tsconfig.json', 'bun.lock'] as const;
|
|
@@ -24,6 +24,8 @@ export interface BaselineCacheOptions {
|
|
|
24
24
|
env?: NodeJS.ProcessEnv;
|
|
25
25
|
/** git 실행 심 — 출력 바이트를 돌려주고 실패하면 던진다. */
|
|
26
26
|
git?: (args: string[], cwd: string) => Buffer;
|
|
27
|
+
/** 작업 트리 설치 파일 읽기 심 — 파일 바이트를 돌려주고 읽을 수 없으면 던진다. */
|
|
28
|
+
readInstallFile?: (path: string) => Buffer;
|
|
27
29
|
/** tsc 판 조회 심. */
|
|
28
30
|
tscVersion?: (cwd: string) => string;
|
|
29
31
|
maxEntries?: number;
|
|
@@ -59,6 +61,27 @@ function defaultTscVersion(cwd: string): string {
|
|
|
59
61
|
}
|
|
60
62
|
}
|
|
61
63
|
|
|
64
|
+
/** 키에 들어가는 «작업 트리에 실제로 깔린 판» — base 쪽 bun.lock 이 같아도 설치가 다르면 진단이 다르다. */
|
|
65
|
+
const BASELINE_CACHE_INSTALL_FILES = ['bun.lock', 'apps/pwa/bun.lock'] as const;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 지금 트리에 실제로 깔린 판의 키 — 작업 트리 루트 `bun.lock`(⊕ 있으면 `apps/pwa/bun.lock`) 파일 해시.
|
|
69
|
+
* 루트 `bun.lock` 을 못 읽으면 undefined(호출자는 캐시 없이 잰다 — base 키 규율과 같다) · `apps/pwa/bun.lock` 은 못 읽으면 absent.
|
|
70
|
+
*/
|
|
71
|
+
export function workingTreeInstallKey(cwd: string, options: Pick<BaselineCacheOptions, 'readInstallFile'> = {}): string | undefined {
|
|
72
|
+
const read = options.readInstallFile ?? ((path: string) => readFileSync(path));
|
|
73
|
+
const files: Record<string, string> = {};
|
|
74
|
+
for (const file of BASELINE_CACHE_INSTALL_FILES) {
|
|
75
|
+
try {
|
|
76
|
+
files[file] = createHash('sha256').update(read(join(cwd, file))).digest('hex');
|
|
77
|
+
} catch {
|
|
78
|
+
if (file === 'bun.lock') return undefined;
|
|
79
|
+
files[file] = 'absent';
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return createHash('sha256').update(JSON.stringify(files)).digest('hex');
|
|
83
|
+
}
|
|
84
|
+
|
|
62
85
|
/** 키를 못 만들면(base 를 sha 로 풀 수 없음 등) undefined — 호출자는 캐시 없이 잰다. */
|
|
63
86
|
export function computeBaselineCacheKey(base: string, command: string, cwd: string, options: BaselineCacheOptions = {}): string | undefined {
|
|
64
87
|
try {
|
|
@@ -73,8 +96,10 @@ export function computeBaselineCacheKey(base: string, command: string, cwd: stri
|
|
|
73
96
|
files[file] = 'absent';
|
|
74
97
|
}
|
|
75
98
|
}
|
|
99
|
+
const install = workingTreeInstallKey(cwd, options);
|
|
100
|
+
if (install === undefined) return undefined;
|
|
76
101
|
const tsc = (options.tscVersion ?? defaultTscVersion)(cwd);
|
|
77
|
-
return createHash('sha256').update(JSON.stringify({ format: CACHE_FORMAT, sha, command, files, tsc })).digest('hex');
|
|
102
|
+
return createHash('sha256').update(JSON.stringify({ format: CACHE_FORMAT, sha, command, files, install, tsc })).digest('hex');
|
|
78
103
|
} catch {
|
|
79
104
|
return undefined;
|
|
80
105
|
}
|
package/scripts/usage-rollup.ts
CHANGED
|
@@ -15,8 +15,11 @@ export interface UsageRow { site: string; provider: string; model: string; calls
|
|
|
15
15
|
|
|
16
16
|
type UsageData = { site?: string; provider?: string; billingProvider?: string; model?: string; inputTokens?: number; outputTokens?: number; cacheReadInputTokens?: number; cacheCreationInputTokens?: number; cost?: { kind?: string; usd?: number }; podRollupKey?: string; partial?: boolean; partialSeq?: number; partialAt?: string };
|
|
17
17
|
|
|
18
|
+
/** 재방출 행 dedup 에 필요한 칸만 — 호스트 소비처(bench-report · `elanous usage`)도 같은 규칙을 쓴다. */
|
|
19
|
+
export type PodRollupKeyed = { podRollupKey?: unknown; partial?: unknown; partialSeq?: unknown; partialAt?: unknown };
|
|
20
|
+
|
|
18
21
|
/** 같은 podRollupKey 두 행 중 어느 쪽이 더 완전한가 — 최종 > 부분 · 부분끼리는 seq 큰 쪽 · 같으면 at 늦은 쪽. 완전히 같으면 앞 행을 둔다. */
|
|
19
|
-
function rollupRowBeats(next:
|
|
22
|
+
function rollupRowBeats(next: PodRollupKeyed, kept: PodRollupKeyed): boolean {
|
|
20
23
|
const nextFinal = next.partial !== true;
|
|
21
24
|
const keptFinal = kept.partial !== true;
|
|
22
25
|
if (nextFinal !== keptFinal) return nextFinal;
|
|
@@ -27,31 +30,42 @@ function rollupRowBeats(next: UsageData, kept: UsageData): boolean {
|
|
|
27
30
|
return String(next.partialAt ?? '') > String(kept.partialAt ?? '');
|
|
28
31
|
}
|
|
29
32
|
|
|
33
|
+
/** ⭐ METER-POD-ROLLUP-GAP-2 — 같은 Pod 사용량의 재방출(부분 ⊕ 최종 · abort ⊕ resume)을 podRollupKey 로 한 번만 남긴다.
|
|
34
|
+
* 들어온 순서가 아니라 «더 완전한 행»을 남기고(rollupRowBeats), 키 없는 행은 그대로 다 둔다(순서 보존). */
|
|
35
|
+
export function dedupePodRollupRows<T>(rows: readonly T[], dataOf: (row: T) => PodRollupKeyed = (row) => row as PodRollupKeyed): T[] {
|
|
36
|
+
const kept: T[] = [];
|
|
37
|
+
const keptIndexByKey = new Map<string, number>();
|
|
38
|
+
for (const row of rows) {
|
|
39
|
+
const d = dataOf(row);
|
|
40
|
+
if (typeof d.podRollupKey === 'string') {
|
|
41
|
+
const at = keptIndexByKey.get(d.podRollupKey);
|
|
42
|
+
if (at !== undefined) {
|
|
43
|
+
if (rollupRowBeats(d, dataOf(kept[at]!))) kept[at] = row;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
keptIndexByKey.set(d.podRollupKey, kept.length);
|
|
47
|
+
}
|
|
48
|
+
kept.push(row);
|
|
49
|
+
}
|
|
50
|
+
return kept;
|
|
51
|
+
}
|
|
52
|
+
|
|
30
53
|
export function rollup(lines: readonly string[]): { rows: UsageRow[]; truncated: boolean } {
|
|
31
54
|
const by = new Map<string, UsageRow>();
|
|
32
55
|
// ⭐ MF-3c0d253e — 같은 Pod 사용량의 재방출(부분 ⊕ 최종 · abort ⊕ resume)은 podRollupKey 로 한 번만 센다(이중계상 방지).
|
|
33
56
|
// 들어온 순서가 아니라 «더 완전한 행»을 남긴다(최종 > 부분 · seq · at — rollupRowBeats).
|
|
34
57
|
// 키가 없는 행(기존 경로 전부)은 종전과 동작·바이트 그대로다.
|
|
35
58
|
let truncated = false;
|
|
36
|
-
const
|
|
37
|
-
const keptIndexByPodRollupKey = new Map<string, number>();
|
|
59
|
+
const parsed: UsageData[] = [];
|
|
38
60
|
for (const line of lines) {
|
|
39
61
|
if (/limitReached=true|truncated/.test(line) && !line.startsWith('{')) truncated = true;
|
|
40
62
|
if (!line.startsWith('{')) continue;
|
|
41
63
|
let r: { event?: string; data?: Record<string, unknown> };
|
|
42
64
|
try { r = JSON.parse(line); } catch { continue; }
|
|
43
65
|
if (r.event !== 'llm-usage' || !r.data) continue;
|
|
44
|
-
|
|
45
|
-
if (typeof d.podRollupKey === 'string') {
|
|
46
|
-
const keptIndex = keptIndexByPodRollupKey.get(d.podRollupKey);
|
|
47
|
-
if (keptIndex !== undefined) {
|
|
48
|
-
if (rollupRowBeats(d, entries[keptIndex]!)) entries[keptIndex] = d;
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
keptIndexByPodRollupKey.set(d.podRollupKey, entries.length);
|
|
52
|
-
}
|
|
53
|
-
entries.push(d);
|
|
66
|
+
parsed.push(r.data as UsageData);
|
|
54
67
|
}
|
|
68
|
+
const entries = dedupePodRollupRows(parsed);
|
|
55
69
|
for (const d of entries) {
|
|
56
70
|
const provider = d.billingProvider ?? d.provider ?? '?'; // ⭐ 과금 주체 우선(B10)
|
|
57
71
|
const key = `${d.site ?? '?'}|${provider}|${d.model ?? '?'}`;
|