elanous 0.2.22 → 0.2.24
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/NOTICE +7 -0
- package/README.md +32 -22
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/{aBi6EyMtLsrl7J8eH4kF7 → Q-7iofoxOEv1WX18S35of}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/{119fc678.6f591cdb84ca5791.js → 0673cc54.8e2ef1c23e49e190.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{a9f2b54d-40c16bd0efdb04b8.js → 0a09e062-34f747a17d8d8c2e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{f8626309.a4e5d362b1d55e59.js → 0e3acd34.d42b2ef87fdf4234.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1168.a0fb3d9db76c00bd.js +77 -0
- package/apps/pwa/out/_next/static/chunks/{7175-350f4ce2873bc038.js → 1325-dc3f3c21eb181cb4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1357.a843c41d2cd7ec31.js +34 -0
- package/apps/pwa/out/_next/static/chunks/1430.53f49882b17607c2.js +24 -0
- package/apps/pwa/out/_next/static/chunks/1459-be6509b3419d8a9f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6767.8e6c5f6e744fcd9f.js → 1505.5826671109111e75.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/153.0a4e3cd516930491.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1572.7bc291352e241a45.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1593-d07ad068c120dfb0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1608.b7c900baa56cab32.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1615-4d8156febaac4b35.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1646.e03f0fbc26bc72f6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7327-2d69fb321ba73389.js → 1669-da843ed73aa301f5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/176.13ba0659e3e96fd4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1990.9c60f495ef33c070.js +36 -0
- package/apps/pwa/out/_next/static/chunks/2042e469.f86158dd5c1e51a1.js +122 -0
- package/apps/pwa/out/_next/static/chunks/2528.0af5c0e1fad88df6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2543-28ec6aeb80a5e1e1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2561.43c99d6ee914ee6a.js +62 -0
- package/apps/pwa/out/_next/static/chunks/2638.b39ab2f674caa1a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/272-0eed0fb0aab716b6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7899.597af4a9996c907f.js → 2721.bc9a8f36bd85c07a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2778.67e0c1d484189ccf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2841.10ff309f6a3168af.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2886-9639f97d523d752a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2978.57fb87ce21cc5a85.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3664.15f968c39d15bd28.js +149 -0
- package/apps/pwa/out/_next/static/chunks/3786-935eb3fcde90e496.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3813.df4a01ae89c6f63f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3961-aaae04ed1d32622f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3992.f79c70480e8c4077.js +309 -0
- package/apps/pwa/out/_next/static/chunks/40-413e8641de9ed1e5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4054-521f11a503daed64.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4315.e7356f34c7d8ec79.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4489-bf629de6f04f7681.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4540.557733cc88c6a82b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/462.0275b4338c5b6337.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4658-7ed9266098a787e4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4945-0f4046cfc3228ea3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{d70879db.c66c56a95a91efc6.js → 4c5e95ce.83e9b16add6fcd8c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5105-18b4165c5d3a86b0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5205-6992210fe31f45f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5287-72898b126d722457.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6158.566c890adc06a459.js → 5313.1697309c532b9856.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/{79b78388.663188133449d707.js → 53746282.1be36038d1abd949.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5474.991b90688c3cb321.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5544-871146192d095594.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5578.ac963634373b28c6.js +286 -0
- package/apps/pwa/out/_next/static/chunks/5935.d5e8ca3e096772e8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5967-c0a80f8ffe5d4050.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1268.0840ff91e083fcbd.js → 5974.360f8441eb0ac1ec.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6006.84e8b418a7a0db97.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6013-1267ba7454d6764b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6038-1e0f7ee1086da4cf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/617.709dd0019fe99796.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6261.fb9bf94947094636.js +74 -0
- package/apps/pwa/out/_next/static/chunks/6411.1966866fae31ffa9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6481-b829d99aa8d158f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6541.2b7daa043100d0fa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6571-926ddf024a6ad5df.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6597.2d7a6d77ab485b27.js +64 -0
- package/apps/pwa/out/_next/static/chunks/67.5dd2dc543c2fa3de.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9832.2ab2e818013574c7.js → 6702.ebbd165d5e4a4a14.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6777-312496b0d774719b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/681.0116f7036cfb4e8d.js +32 -0
- package/apps/pwa/out/_next/static/chunks/6871.30b2bf23b3126e36.js +24 -0
- package/apps/pwa/out/_next/static/chunks/6882.737d366d54e29d83.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7035.54c2c535164b1c9b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7086-8bfa7e8f796a03d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7126.5e5599f251726b67.js +93 -0
- package/apps/pwa/out/_next/static/chunks/7203-47ff0740a293e284.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7268.017f49dafb5f0ff3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7355.5d65a87e9567ae08.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7392.4018c2a62177f296.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6110.1297d4093fdfbb0d.js → 740.90b31555290236a9.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/7471.3a3d1be50559cfb8.js +148 -0
- package/apps/pwa/out/_next/static/chunks/7500.73489a345cef2201.js +226 -0
- package/apps/pwa/out/_next/static/chunks/{9070.067c106528ec1ae6.js → 7518.49161e8f5814abdb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7565.c2f72a15c1ed0396.js +27 -0
- package/apps/pwa/out/_next/static/chunks/7651.6d1c984d3d8587a4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7713.7ab863ed59b2d661.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7793.d7cd2026767ddd54.js +115 -0
- package/apps/pwa/out/_next/static/chunks/7837.c7c267e5dceadb11.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7891.09266382daf70d1c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7905.b230b9d06cb4ad5d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7990-cbf6a01bcbdcb7e6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8119-819c549e0c197786.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8186-74ea19a3700d04a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8194.484f51e273d9abd2.js → 8264.00e434fbd02e9311.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/8267-e7b3262b735ae106.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8386.86518e55b75f7cf5.js +8 -0
- package/apps/pwa/out/_next/static/chunks/{088d8e8a.8069543d082bec12.js → 83cfc5c8.349535543c1548b6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8437.ba6eeeb29590042f.js +23 -0
- package/apps/pwa/out/_next/static/chunks/{1948.5f35b39eef222a94.js → 8438.ee7f0df3b3f6fcb4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8469-1500be57e582296f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8483-7164cecdaf178ca6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4588.ad3364cb7fb97ada.js → 8532.a86e99acf9b761cb.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/8569.b853a8daa8f31d12.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8722-4c940f02c496ad36.js +1 -0
- package/apps/pwa/out/_next/static/chunks/887-41d0dcf5af48c628.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8925-2abfb48af477fb7f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{88729267.9c6c000157c83f2f.js → 89849432.9eaa2f4c9c010965.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/9039.da86361496797522.js +90 -0
- package/apps/pwa/out/_next/static/chunks/905.d5745a25884754d4.js +94 -0
- package/apps/pwa/out/_next/static/chunks/9089-d909f3d4478b0628.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9096.dc5c7a267f83790c.js +41 -0
- package/apps/pwa/out/_next/static/chunks/93.a567257b6bdb7fe4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9320.419acb9665b46597.js +287 -0
- package/apps/pwa/out/_next/static/chunks/9356.b8ceda6634612ff8.js +79 -0
- package/apps/pwa/out/_next/static/chunks/94.e9e1043b35cd3eed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9400-7498ea90b2e6ee8f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3522-1de1319d028ac89c.js → 9502-32c5dc22e8f120f5.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/9930-58644af9c813d6e1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9932.367cf4725fedd6e2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9935.1628b904cdc8ce0e.js +104 -0
- package/apps/pwa/out/_next/static/chunks/{7630.2b9a68659053ff4e.js → 9976.8f28864317639fe6.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-a2aa8709d193ee0d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-3b9fc39362473ae7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f2b62c19ca68fef8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-d7602d489edf5f37.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-24282bf9f90031a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-8b7c5ab6b5bc8d64.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-4eb84d454f8c3415.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-b2e494bdad57fcb5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/{page-80ed511e02c81412.js → page-c0ab229673a8b0eb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-3e2b45f3c36dbac4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-c22aa511b28743ad.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-26345242e4049f55.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-bf88c0f558d8ef54.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-52fec523e5b5d3ad.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-67723dfdcde92b31.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-a2aa8709d193ee0d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-22725e2570f1126b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-1891335950d68b70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-3267af1ebdc73cf5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-c314b75c11234998.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-9f9c85415f61782b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-c1d4276059085587.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-833aab587f26ad4c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8526c17ae38e6a50.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-f998637a8402115c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-b7bb53828b17b335.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-1b1850de0257e46d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-6f23159fedb41f67.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-1d809a87139dd024.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-da9e570234d481fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-b92caddb5c715f30.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-8ce3a38e210b810a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-57ffeeb6019440fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-abbb417bf514630b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-2c2b3f29cec1efa4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-9369279f72fc7b51.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-64861f14039499c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ddc4c93efd8ab94b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-a2aa8709d193ee0d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-901a1391b81053bc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-68d9bbaac9841a58.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-b5c0c996f329371e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-89881ccbcf728978.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-0e87a803b3d7165e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-58d4e5cb30ef4871.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-02ce4c3b5f39e4d2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-e620d1bb8eadcec2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-acf868c64d77f950.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-54fa605fa6dba457.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-bc79f0833533fa84.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-d5c4398eea03d8f3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/b4e12462-5fab4a813a63dadd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/cd39fa05.fac4545f42a3ef70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{f6b7c9ba.64a895b8b2fedbdc.js → ce383d63.d2ede338788ed18e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{framework-5c169757551134ed.js → framework-b2763b10dc6b3836.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/main-app-27893e93b0ec193d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-f280047194e1daf8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-e90eff2d0b70564c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-70f2d654b3435d34.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-4794f053bd5e4230.js +1 -0
- package/apps/pwa/out/_next/static/css/72becfd42807b50d.css +1 -0
- package/apps/pwa/out/_next/static/css/d0a48dc099ed86cf.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +23 -23
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +22 -22
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +23 -23
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +23 -23
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +22 -22
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +23 -23
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +24 -23
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +23 -23
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +23 -23
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +22 -22
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +22 -22
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +23 -23
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +23 -23
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +24 -23
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +22 -22
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +22 -22
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +22 -22
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +23 -23
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +23 -23
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +22 -22
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +22 -22
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +22 -22
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +22 -22
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +23 -23
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +23 -23
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +22 -22
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +23 -23
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +23 -23
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +23 -23
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +23 -23
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +23 -23
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +22 -22
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +23 -23
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +23 -23
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +23 -23
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +23 -23
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +23 -23
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +23 -23
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +23 -23
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/catalog/models/anthropic/claude-haiku-5-5.yaml +32 -0
- package/catalog/models/anthropic/claude-opus-5-5.yaml +5 -0
- package/catalog/models/anthropic/claude-sonnet-5-5.yaml +31 -0
- package/catalog/models/anthropic/claude-sonnet-5.yaml +8 -2
- package/catalog/models/openai/gpt-6-astra.yaml +6 -0
- package/catalog/models/openai/gpt-6-luna.yaml +6 -0
- package/catalog/models/openai/gpt-6-sol.yaml +6 -0
- package/catalog/models/openrouter/deepseek-v4-pro-0813.yaml +39 -0
- package/catalog/models/openrouter/deepseek-v4.1-flash.yaml +38 -0
- package/catalog/models/openrouter/glm-5.3-flash.yaml +35 -0
- package/catalog/providers/openrouter.yaml +2 -0
- package/graphs/implement-loop.yaml +31 -0
- package/graphs/log-horizon/log-horizon-watch.yaml +25 -0
- package/graphs/log-horizon/recipes.yaml +3 -0
- package/graphs/ops/harness-gc.yaml +17 -0
- package/graphs/ops/recipes.yaml +3 -0
- package/graphs/overlays/README.md +1 -1
- package/graphs/overlays/stop-heal-ladder.yaml +105 -0
- package/graphs/release/recipes.yaml +1 -1
- package/graphs/release/release-loop.yaml +6 -2
- package/graphs/release-rehearsal/recipes.yaml +12 -0
- package/graphs/release-rehearsal/release-rehearsal.yaml +17 -0
- package/package.json +4 -1
- package/scripts/add-api-key.sh +1 -1
- package/scripts/bench-report.ts +82 -3
- package/scripts/ci-typecheck-changed.ts +80 -14
- package/scripts/fleet/bedrock-pod-secret.ts +122 -0
- package/scripts/fleet/node-setup-linux.sh +97 -0
- package/scripts/fleet/node-setup.sh +11 -0
- package/scripts/fleet/zai-pod-secret.ts +86 -0
- package/scripts/hq/hq-fence-wrapper.sh +148 -0
- package/scripts/land-global-checks.ts +3 -0
- package/scripts/lib/typecheck-ratchet.ts +51 -0
- package/scripts/measure-log-diet-2.ts +213 -0
- package/scripts/measure-tsc-affected-roots.ts +289 -0
- package/scripts/openai-relay-server.ts +5 -1
- package/scripts/release-loop/docs-node.ts +258 -43
- package/scripts/release-loop/gate-executor.ts +28 -0
- package/scripts/release-loop/gate-method.ts +59 -0
- package/scripts/release-loop/gate-node.ts +335 -33
- package/scripts/release-loop/gate-wall.ts +208 -0
- package/scripts/release-loop/known-issues-node.ts +53 -8
- package/scripts/release-loop/publish-at.ts +14 -0
- package/scripts/release-loop/publish-node.ts +17 -2
- package/scripts/release-loop/resume-release.ts +40 -4
- package/scripts/release-loop/resume.ts +94 -0
- package/scripts/release-loop/shard-plan.ts +3 -11
- package/scripts/release-loop/tui-node-checks.ts +22 -0
- package/scripts/release-loop/unattended-release.ts +29 -7
- package/scripts/rhythm/daily.ts +51 -2
- package/scripts/rhythm/weekly.ts +3 -2
- package/scripts/tsc-baseline-cache.ts +147 -0
- package/scripts/tsc-main-safety-net.ts +189 -0
- package/scripts/usage-rollup.ts +64 -8
- package/src/acp/codex-plugins.ts +48 -6
- package/src/agent/monad-agent-turn.ts +67 -5
- package/src/agent/run-context.ts +20 -0
- package/src/agent/tool-profile.ts +28 -2
- package/src/agent-mission/acp-judge.ts +8 -2
- package/src/agent-mission/capabilities-cli.ts +16 -1
- package/src/agent-mission/driver.ts +19 -4
- package/src/agent-mission/pr-review-watch.ts +6 -2
- package/src/agent-mission/resource-ladder.ts +8 -3
- package/src/agent-mission/review-loop.ts +41 -7
- package/src/agent-substrate/acp-implementer.ts +139 -0
- package/src/agent-substrate/acp-reviewer.ts +17 -8
- package/src/agent-substrate/pr-reviewer.ts +191 -28
- package/src/agent-substrate/review-intent.ts +6 -4
- package/src/agent-substrate/self-review-cli.ts +155 -21
- package/src/agent-substrate/shard-boundary-intent.ts +92 -26
- package/src/ask-user-question/pending-questions.ts +8 -2
- package/src/autopilot/build/llm-conflict-merge.ts +216 -12
- package/src/autopilot/codebase-grounding.ts +44 -0
- package/src/autopilot/llm-control-brain.ts +13 -1
- package/src/autopilot/mission-codebase-gate.ts +8 -77
- package/src/autopilot/mission-critique.ts +18 -5
- package/src/autopilot/mission-feasibility-negotiate.ts +11 -10
- package/src/autopilot/mission-trace.ts +12 -1
- package/src/autopilot/pr-manager.ts +12 -3
- package/src/autopilot/system-lookback-run.ts +16 -10
- package/src/bench/bench.ts +183 -0
- package/src/boot/acp-server.ts +4 -7
- package/src/boot/acp-socket-path.ts +7 -0
- package/src/briefing/brief-items.ts +21 -13
- package/src/budget/fetchers/grok.ts +15 -5
- package/src/budget/llm-cost.ts +37 -5
- package/src/budget/types.ts +8 -2
- package/src/budget/unified-usage.ts +88 -6
- package/src/chat/index.ts +12 -3
- package/src/cli/agent-cli.ts +20 -0
- package/src/cli/bench-cli.ts +34 -0
- package/src/cli/brief-cli.ts +27 -21
- package/src/cli/cli-user-error.ts +9 -2
- package/src/cli/guardian-cli.ts +26 -1
- package/src/cli/image-cli.ts +101 -0
- package/src/cli/log-event-names.ts +7 -0
- package/src/cli/logs-cli.ts +1 -0
- package/src/cli/mcp-login.ts +111 -37
- package/src/cli/plugin-cli.ts +21 -1
- package/src/cli/pod-cli.ts +26 -0
- package/src/cli/pr-cli.ts +90 -5
- package/src/cli/pr-land-overlap-evidence.ts +279 -0
- package/src/cli/release-cli.ts +63 -12
- package/src/cli/rfc-status.ts +52 -2
- package/src/cli/self-update.ts +277 -10
- package/src/cli/skills-cli.ts +12 -3
- package/src/cli/tasks-cli.ts +241 -30
- package/src/cli/usage-cli.ts +91 -8
- package/src/context-bus/context-first.ts +51 -2
- package/src/context-bus/context-now-surfaces.ts +13 -2
- package/src/context-bus/long-term-memory.ts +3 -1
- package/src/context-bus/nightly-condense.ts +3 -2
- package/src/core-turn/run-goal-loop.ts +87 -2
- package/src/dashboard/ad-run-setup-bindings.ts +279 -0
- package/src/dashboard/chat-fold.ts +20 -0
- package/src/dashboard/first-screen-band.ts +151 -1
- package/src/dashboard/index.ts +45 -281
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +5 -3
- package/src/dashboard/stale-install-check.ts +24 -2
- package/src/dashboard/turn-stream-formatter.ts +25 -0
- package/src/debug/log-diet.ts +190 -0
- package/src/debug/log.ts +70 -17
- package/src/display/coordinator.ts +25 -2
- package/src/domains/coo-admin-tool.ts +27 -0
- package/src/domains/cron-match.ts +1 -0
- package/src/domains/outbound-alert.ts +17 -6
- package/src/domains/release-tool.ts +158 -10
- package/src/domains/schedule-registry.ts +10 -7
- package/src/domains/schedule-runner.ts +1 -0
- package/src/elanous-config-dir.ts +2 -2
- package/src/elanous-daemon.ts +3 -3
- package/src/error-report/report.ts +21 -4
- package/src/execution-loop/launch-gate.ts +1 -1
- package/src/expression/i18n/messages.en.ts +5 -0
- package/src/expression/i18n/messages.ja.ts +5 -0
- package/src/expression/i18n/messages.ko.ts +5 -0
- package/src/expression/i18n/messages.zh.ts +5 -0
- package/src/expression/i18n/types.ts +8 -0
- package/src/github/pr-labels.ts +2 -0
- package/src/graph-runner/graph-cli.ts +42 -1
- package/src/graph-runner/runner.ts +218 -8
- package/src/grok/usage.ts +23 -2
- package/src/grounding/ref-index.ts +253 -0
- package/src/harness/dispatch-backpressure.ts +267 -0
- package/src/harness/harness-clean.ts +199 -78
- package/src/harness/harness-cli-command.ts +229 -35
- package/src/harness/harness-gc.ts +331 -0
- package/src/harness/harness-seams.ts +2 -20
- package/src/harness/harness-stop.ts +4 -2
- package/src/harness/harness-substrate-default.ts +214 -8
- package/src/harness/launch-registry.ts +6 -0
- package/src/harness/nested-elanous-policy.ts +4 -2
- package/src/harness/worktree-branch-prefix.ts +9 -0
- package/src/hq/fence-wrapper.ts +7 -0
- package/src/hq/fence.ts +187 -0
- package/src/hq/hq.ts +33 -2
- package/src/hq/lease.ts +13 -0
- package/src/hq/seats.ts +133 -21
- package/src/images/image-ledger.ts +179 -0
- package/src/index.ts +250 -69
- package/src/instance/child-scope.ts +32 -1
- package/src/instance/dev-project.ts +133 -0
- package/src/instance/resolve.ts +24 -0
- package/src/instance/spawn-elanous.ts +45 -0
- package/src/intake-plane/collect-github.ts +19 -1
- package/src/intake-plane/digest.ts +45 -20
- package/src/intake-plane/lens.ts +36 -8
- package/src/intake-plane/ref-prefetch.ts +110 -0
- package/src/intelligence-map/model-alias.ts +6 -0
- package/src/intelligence-map/model-catalog.ts +113 -0
- package/src/knowledge/kgs/pack.ts +5 -2
- package/src/knowledge/kgs/sqlite-store.ts +15 -0
- package/src/knowledge/tools/knowledge-query.ts +20 -2
- package/src/knowledge/write.ts +234 -2
- package/src/llm/bedrock-eventstream.ts +215 -0
- package/src/llm/bedrock-provider.ts +248 -0
- package/src/llm/bedrock.ts +491 -0
- package/src/llm/openrouter-byok-cost.ts +96 -0
- package/src/llm/openrouter-deepseek.ts +151 -0
- package/src/llm/openrouter-upstream-pins.config.ts +25 -0
- package/src/llm/provider-credentials.ts +3 -1
- package/src/llm/zai-coding-plan.ts +700 -0
- package/src/llm.ts +383 -41
- package/src/loops/budget.ts +150 -0
- package/src/loops/checker.ts +148 -9
- package/src/loops/orchestrator/resync-shadow.ts +34 -7
- package/src/loops/orchestrator/seat-share.ts +6 -49
- package/src/loops/orchestrator/traffic.ts +5 -81
- package/src/market/signed-index.ts +42 -0
- package/src/maturity/feature-maturity.ts +2 -2
- package/src/mcp/mcp-oauth.ts +136 -17
- package/src/model-tier/llm-tier-map.ts +52 -4
- package/src/models/prompts.ts +23 -1
- package/src/mss/logging/horizon-watch.ts +168 -0
- package/src/mss/logging/log-store.ts +262 -26
- package/src/native-tool-catalog.ts +1 -1
- package/src/nexus/api/consult-requests.ts +7 -10
- package/src/nexus/api/discord-trigger-bot.ts +6 -3
- package/src/nexus/api/error-reports.ts +30 -8
- package/src/nexus/api/http-server.ts +8 -0
- package/src/nexus/api/loop-resources.ts +42 -0
- package/src/nexus/api/missions.ts +73 -2
- package/src/nexus/api/ops-api.ts +18 -0
- package/src/nexus/api/outbound-report.ts +8 -3
- package/src/nexus/api/telegram-trigger-bot.ts +6 -3
- package/src/nexus/index.ts +17 -75
- package/src/nexus/outbound/format.ts +2 -0
- package/src/nexus/outbound/router.ts +198 -13
- package/src/nexus/tools-kind.ts +78 -0
- package/src/oauth/codex-account-rotation.ts +55 -20
- package/src/oauth/codex-account-store.ts +451 -23
- package/src/oauth/fallback-chain.ts +75 -15
- package/src/onboarding/first-run.ts +37 -18
- package/src/onboarding/web-first.ts +20 -2
- package/src/onboarding.ts +2 -1
- package/src/ops/machine-heartbeat.ts +185 -0
- package/src/ops/overview-cache.ts +175 -0
- package/src/ops/overview.ts +8 -0
- package/src/plugins/adapters/claude-package.ts +4 -12
- package/src/plugins/core/capability-policy.ts +141 -0
- package/src/plugins/core/host.ts +21 -8
- package/src/plugins/core/manifest.ts +19 -2
- package/src/plugins/core/trust-store.ts +15 -0
- package/src/plugins/core/types.ts +12 -0
- package/src/plugins/install/market-fetch.ts +68 -16
- package/src/plugins/install/plugin-install.ts +187 -16
- package/src/plugins/maker/plugin-maker.ts +25 -20
- package/src/plugins/maker/wizard-generate.ts +16 -2
- package/src/policy/llm-policy.ts +14 -1
- package/src/process/pid-liveness.ts +1 -1
- package/src/prompt-cache/openai.ts +13 -6
- package/src/prompt-cache/openrouter.ts +94 -0
- package/src/prompt-cache/types.ts +5 -0
- package/src/prompt-enhance/enhance.ts +69 -5
- package/src/prompt-library/family-prompt-coverage.ts +1 -0
- package/src/prompt-library/universal-preamble.ts +2 -2
- package/src/registry/discovery/openrouter-live-resolve.ts +122 -0
- package/src/registry/loader.ts +6 -1
- package/src/release-loop/checklist.ts +24 -9
- package/src/release-loop/feature-store.ts +3 -0
- package/src/release-loop/gate-shards.ts +48 -4
- package/src/release-loop/landed-to-green.ts +81 -0
- package/src/release-loop/light-rc.ts +11 -1
- package/src/release-loop/merged-pr-checklist.ts +10 -0
- package/src/release-loop/placement.ts +107 -23
- package/src/release-loop/rehearsal.ts +317 -0
- package/src/release-loop/release-schedule.ts +13 -1
- package/src/release-loop/rubric-types.ts +1 -0
- package/src/release-loop/rubric.ts +2 -2
- package/src/requirements/requirement-ledger.ts +536 -0
- package/src/retro/daily.ts +26 -12
- package/src/seat-loop/seat-loop.ts +66 -11
- package/src/seat-loop/traffic-signal.ts +44 -0
- package/src/self-dev/ask-launch-flow.ts +2 -2
- package/src/self-dev/dev-cli.ts +109 -11
- package/src/self-dev/dev-pipeline.ts +11 -8
- package/src/self-dev/draft-residue.ts +475 -0
- package/src/self-dev/draft-sweep.ts +254 -10
- package/src/self-dev/draft-triage-rules.ts +27 -1
- package/src/self-dev/graph-journey-route.ts +6 -0
- package/src/self-dev/launch-preflight-text.ts +549 -0
- package/src/self-dev/launch-preflight.ts +10 -545
- package/src/self-dev/orchestrate-cli.ts +82 -5
- package/src/self-dev/orchestrate-summary-line.ts +13 -0
- package/src/self-dev/orchestrate.ts +49 -141
- package/src/self-dev/review-provider-fallback.ts +115 -19
- package/src/self-dev/run-store.ts +1 -3
- package/src/self-dev/run-supervisor.ts +190 -36
- package/src/self-dev/run-types.ts +191 -0
- package/src/self-dev/shard-aggregate.ts +61 -13
- package/src/self-dev/shard-plan.ts +5 -1
- package/src/self-dev/shard-run.ts +23 -5
- package/src/self-dev/supervisor-verdict-edges.ts +3 -4
- package/src/self-implement/blocked-draft-policy.ts +3 -19
- package/src/self-implement/budget-gate.ts +53 -13
- package/src/self-implement/child-context-composition.ts +139 -0
- package/src/self-implement/child-llm-preference.ts +13 -5
- package/src/self-implement/claude-acp-implement.ts +67 -0
- package/src/self-implement/draft-on-stop-mode.ts +18 -0
- package/src/self-implement/flex-signals.ts +59 -0
- package/src/self-implement/follow-up-goals.ts +114 -38
- package/src/self-implement/frozen-merges.ts +8 -3
- package/src/self-implement/gate-baseline-reap.ts +157 -0
- package/src/self-implement/gate-baseline.ts +78 -16
- package/src/self-implement/gate-cli.ts +12 -2
- package/src/self-implement/gate-remote.ts +17 -6
- package/src/self-implement/goal-author-decompose.ts +10 -2
- package/src/self-implement/goal-author.ts +48 -13
- package/src/self-implement/goal-digest.ts +19 -0
- package/src/self-implement/graph-overlay-condition.ts +32 -0
- package/src/self-implement/graph-overlay-yaml.ts +41 -7
- package/src/self-implement/graph-templates.ts +33 -1
- package/src/self-implement/graph-yaml.ts +56 -0
- package/src/self-implement/headless-elanous-driver.ts +11 -9
- package/src/self-implement/heal-intake.ts +5 -1
- package/src/self-implement/host-regate.ts +120 -29
- package/src/self-implement/l8-merge-queue.ts +67 -5
- package/src/self-implement/merge-train.ts +131 -0
- package/src/self-implement/off-diff-evidence.ts +9 -0
- package/src/self-implement/orchestrator.ts +271 -25
- package/src/self-implement/review-finding-recurrence.ts +28 -0
- package/src/self-implement/rework-policy.ts +2 -1
- package/src/self-implement/round-summary-card.ts +63 -0
- package/src/self-implement/run-ledger.ts +4 -3
- package/src/self-implement/run-step-projection.ts +147 -0
- package/src/self-implement/run-stop.ts +1 -1
- package/src/self-implement/running-runs.ts +75 -5
- package/src/self-implement/seams.ts +83 -4
- package/src/self-implement/self-implement-cli.ts +12 -5
- package/src/self-implement/stop-autoheal.ts +69 -2
- package/src/self-implement/train-gate-remote.ts +158 -0
- package/src/session-runtime/index.ts +10 -1
- package/src/session-runtime/retry-api.ts +11 -1
- package/src/session-runtime/retry-policy.ts +2 -1
- package/src/skills/gift-install.ts +119 -0
- package/src/skills/runner.ts +59 -2
- package/src/skills/skill-feedback.ts +31 -1
- package/src/skills/skill-tier.ts +1 -0
- package/src/smart-wizard/capability-index.ts +231 -0
- package/src/smart-wizard/rung.ts +74 -0
- package/src/steward/launch.ts +22 -10
- package/src/steward/triage-budget.ts +62 -0
- package/src/steward/triage.ts +87 -24
- package/src/storage/file-lock.ts +8 -1
- package/src/task-agent/actions.ts +66 -5
- package/src/task-agent/card-evidence.ts +59 -13
- package/src/task-agent/completion-evidence.ts +5 -0
- package/src/task-agent/cover.ts +101 -3
- package/src/task-agent/judge.ts +20 -3
- package/src/task-agent/land-order-ledger.ts +95 -0
- package/src/task-agent/land-worktree.ts +201 -0
- package/src/task-agent/live-move-history.ts +219 -0
- package/src/task-agent/live-moves.ts +451 -34
- package/src/task-agent/parent-host.ts +117 -8
- package/src/task-agent/rejudge-on-head.ts +352 -0
- package/src/task-agent/review-prior-fix.ts +146 -0
- package/src/task-agent/review-result-capture.ts +218 -0
- package/src/task-agent/run-adopt.ts +136 -0
- package/src/task-agent/shadow.ts +154 -14
- package/src/task-agent/task-hand.ts +186 -14
- package/src/task-orchestrator/surfaces/pod-account-broker.ts +38 -5
- package/src/task-orchestrator/surfaces/pod-bedrock-child.ts +74 -0
- package/src/task-orchestrator/surfaces/pod-child-default.ts +50 -0
- package/src/task-orchestrator/surfaces/pod-child-providers.ts +42 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +6 -3
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +94 -7
- package/src/task-orchestrator/surfaces/pod-image-source.ts +212 -0
- package/src/task-orchestrator/surfaces/pod-lease.ts +110 -17
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +32 -3
- package/src/task-orchestrator/surfaces/pod-pool.ts +86 -18
- package/src/task-orchestrator/surfaces/pod-zai-child.ts +101 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +463 -50
- package/src/task-orchestrator/surfaces/self-implement.ts +25 -3
- package/src/telegram-service.ts +5 -1
- package/src/telegram.ts +17 -4
- package/src/time/format.ts +13 -4
- package/src/tool-runtime/goal-author-runtime.ts +1 -0
- package/src/tool-runtime/self-cognition-catalog.ts +23 -0
- package/src/tool-runtime/self-cognition-runtimes.ts +2 -24
- package/src/typecheck-ratchet.ts +1 -0
- package/src/user-config.ts +180 -32
- package/src/ux-sim/slash-menu-frames.ts +168 -0
- package/src/version/packed-revision.json +1 -1
- package/src/web-terminal/preview-tap-registry.ts +5 -2
- package/src/workflow-runtime/triggers/registry.ts +59 -0
- package/src/workflow-runtime/triggers/scheduler.ts +1 -0
- package/src/workflow-runtime/triggers/webhook-source.ts +35 -5
- package/apps/pwa/out/_next/static/chunks/1109.78a7a3288c0259d9.js +0 -148
- package/apps/pwa/out/_next/static/chunks/115.1c6690b8604f81c4.js +0 -32
- package/apps/pwa/out/_next/static/chunks/1262.76a3f08e67c5f816.js +0 -309
- package/apps/pwa/out/_next/static/chunks/1307-08b716476f1d50af.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1358d292.5c87f12cad6eaa91.js +0 -122
- package/apps/pwa/out/_next/static/chunks/1438.27a1a7b8f2308004.js +0 -149
- package/apps/pwa/out/_next/static/chunks/1442.02d85cd88f4c1eac.js +0 -79
- package/apps/pwa/out/_next/static/chunks/1501-1aad662d82ba43f7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1584.9e2dd53da6e6d4b1.js +0 -24
- package/apps/pwa/out/_next/static/chunks/1586.b989b0614afb77e6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1609.2ad355062870151f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1612-f6df0fc0eed7157b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1923.37f1d5fe7ebe3860.js +0 -27
- package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1993.3fd074a2a246234e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/211-9c9090c59e2c5907.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2140.d2daf9ee96e295d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2212.0359851457473efc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/224.583e562365a52f74.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2328.0f37465646bafa7d.js +0 -36
- package/apps/pwa/out/_next/static/chunks/2372.bf71372184991937.js +0 -286
- package/apps/pwa/out/_next/static/chunks/2489-d238d3e2eb5b2bf5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2549-a8515c86f9e8cb96.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2655.72cc6a97276c5acc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2743-ef6e8c3ced74cce5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2840-5458ad50c49cac6a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2856.f75d5baef7f49b74.js +0 -1
- package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3344.0de4b0ec5a454b7a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/335.a13f339e97893437.js +0 -94
- package/apps/pwa/out/_next/static/chunks/3394.646348f34b2500be.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3443.6276909b53d72d53.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3495.abd4254d808bf2dc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3721.ba5b82db0d9a5572.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3857-5985236d9307f676.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3919.9ff7dc25afefee33.js +0 -74
- package/apps/pwa/out/_next/static/chunks/3998.bd9a69d9b50e9bd2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4003.ab6db1dc266576c9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4144-fad095b48a1862ee.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4216.e8ac94b9fe6b96ab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4327.c44dc2b55cb43ee5.js +0 -34
- package/apps/pwa/out/_next/static/chunks/4401.94257d497b6d26bc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4559.17ca2cdbaa8d414f.js +0 -115
- package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4706.97e734bd4c9816ca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4795-f372490b178583fc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4982.6e44352f751b9a1b.js +0 -77
- package/apps/pwa/out/_next/static/chunks/5342.49c5c5a530e62b50.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5360-08a80abdf3a4da99.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5431.66c58ab1327ee0b7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5633-d6852385000738ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5717.bcfcb96e701b82e0.js +0 -90
- package/apps/pwa/out/_next/static/chunks/5755-30d102586789bf7f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5957.3f1a4012388dd962.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-5b9e8a45a5b0c823.js +0 -1
- package/apps/pwa/out/_next/static/chunks/602.9bf02ac8e9502598.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6020.3a1b347e2d2dfacc.js +0 -8
- package/apps/pwa/out/_next/static/chunks/6375.fce1d174ba6d0324.js +0 -64
- package/apps/pwa/out/_next/static/chunks/6546.92a828aef441271f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/658.957fdb0a8c6c6c8e.js +0 -287
- package/apps/pwa/out/_next/static/chunks/6706.323590aabb8b1293.js +0 -226
- package/apps/pwa/out/_next/static/chunks/6747.b67947f293a4c02d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6868.000b24a3045c5574.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6946.bba74e9c96050f30.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7033.a558f1b2f6912a53.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7065.46029c26c53eac50.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7087.6fae577d09249bc1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/722.de8d9801e0f6d414.js +0 -41
- package/apps/pwa/out/_next/static/chunks/726.83532d69452ee24b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7497.57981e9d7a75a52a.js +0 -24
- package/apps/pwa/out/_next/static/chunks/7672.4af122ad892117c7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/771.6ab889a233e7dae7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8096.f7da0776cfde6300.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8401-35e94e9091f5eb8d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8769.815ca69879f608b4.js +0 -104
- package/apps/pwa/out/_next/static/chunks/8857-aa1cc4ac90af4d40.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8f835b4f.048eeb02299e3e12.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9068.5f2764fccf86c1b2.js +0 -93
- package/apps/pwa/out/_next/static/chunks/9103-60fbef33e66f0a46.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9247-55bcfbb0e8057f59.js +0 -1
- package/apps/pwa/out/_next/static/chunks/935.4ddaa99fc1d1126c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9451.ebfb0dcc9c02250f.js +0 -23
- package/apps/pwa/out/_next/static/chunks/9559.e648369b1d57cf9a.js +0 -62
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-88459e0903da2803.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-d85c43f46f58716b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-4e645600579ae952.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-87edbfd19117823b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-7f0c2b7801ac6a54.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-bf1f030a9d1adffd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-cbaf66b1e1804adf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-9f5610caaa06178c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-ec8f3575e339de74.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-86d753e460f1a989.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-4166a7d51cf0a57d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-2a19c5cd3b562580.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-b6a09cde2e07521a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-f7eae1518ca0c353.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-456a8573ad811381.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-8462d6a543cbb2d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-2974cdfb87c0f9c9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-f927ffa25f3c86d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-abd86cd832aaa011.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-79daf68f6256adaa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8c3f168fc3f91939.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-786fb4504d0f2fa9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-75209f14597dcb8f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-bf30affe3a85af7e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1187a44b7515434b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-3d0910185a66ed54.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f401ee1dd5a26e25.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-5dfe7604fd842784.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-76cf838050a8d681.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-7ec8e289b4c14df6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-8c8ac17d52be2a79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-d359ab048d63a9d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-b8b345c880bf78d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-f151cc10ad026940.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-9875f103057da433.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-9789328af9a9aa62.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-bf495a1e284f4e00.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-a263d94bca4be22a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-3962eab8ec58d16e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-9abcfac07e02a542.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-bee49f927c333abb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-bac2d56dcb02d955.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-937cbcc13176295c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d09495594f7f480d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-69cb93d1a2e57e0e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-72ec543928d67c3e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-82906eeda0186a7b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/f6231284-45726550aba08fca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-f2b4016b9b981bc8.js +0 -1
- package/apps/pwa/out/_next/static/css/f2bd5257e6af88d8.css +0 -1
- /package/apps/pwa/out/_next/static/{aBi6EyMtLsrl7J8eH4kF7 → Q-7iofoxOEv1WX18S35of}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// GATE-SPEED-P2 — measure one commit's gate wall clock without folding earlier attempts into retries.
|
|
2
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { readJunitFileSeconds } from './gate-method.js';
|
|
5
|
+
import { resolveGateLogDir } from './gate-timing.js';
|
|
6
|
+
|
|
7
|
+
type TimedPod = { name: string; start: number; end: number; durationMs: number; root: boolean };
|
|
8
|
+
export interface GateWallAttempt {
|
|
9
|
+
commit: string;
|
|
10
|
+
records: number;
|
|
11
|
+
roots: number;
|
|
12
|
+
retries: number;
|
|
13
|
+
wallMin: number;
|
|
14
|
+
rootWallMin: number | null;
|
|
15
|
+
retryTailMin: number | null;
|
|
16
|
+
overhead: { medianMin: number | null; p90Min: number | null; unmeasuredRoots: number };
|
|
17
|
+
}
|
|
18
|
+
export interface GateWallReport {
|
|
19
|
+
dir: string;
|
|
20
|
+
targetMin: number;
|
|
21
|
+
measured: boolean;
|
|
22
|
+
commit: string | null;
|
|
23
|
+
records: number;
|
|
24
|
+
roots: number;
|
|
25
|
+
retries: number;
|
|
26
|
+
wallMin: number | null;
|
|
27
|
+
wallSource: 'pod-records' | null;
|
|
28
|
+
rootWallMin: number | null;
|
|
29
|
+
retryTailMin: number | null;
|
|
30
|
+
overhead: GateWallAttempt['overhead'];
|
|
31
|
+
retryOnlyAttempts: number;
|
|
32
|
+
attempts: GateWallAttempt[];
|
|
33
|
+
shardsWallMin: number | null;
|
|
34
|
+
disagree: boolean;
|
|
35
|
+
unreadable: string[];
|
|
36
|
+
/** ✓ only when every pod record was read; a slower verdict is already certain. */
|
|
37
|
+
verdict: 'pass' | 'fail' | 'undetermined' | null;
|
|
38
|
+
/** Why shards.json was not compared although present (it carries no attempt identity). */
|
|
39
|
+
shardsSkipped: 'not-latest-attempt' | 'outside-attempt-window' | null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const POD_JSON = /^pod-.+\.json$/;
|
|
43
|
+
const ROOT = /^pod-\d+\.json$/;
|
|
44
|
+
function median(values: number[]): number {
|
|
45
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
46
|
+
const mid = Math.floor(sorted.length / 2);
|
|
47
|
+
return sorted.length % 2 ? sorted[mid]! : (sorted[mid - 1]! + sorted[mid]!) / 2;
|
|
48
|
+
}
|
|
49
|
+
function spanMin(pods: TimedPod[]): number {
|
|
50
|
+
return (Math.max(...pods.map((p) => p.end)) - Math.min(...pods.map((p) => p.start))) / 60_000;
|
|
51
|
+
}
|
|
52
|
+
function attempt(dir: string, commit: string, pods: TimedPod[]): GateWallAttempt {
|
|
53
|
+
const roots = pods.filter((p) => p.root);
|
|
54
|
+
const overhead: number[] = [];
|
|
55
|
+
let unmeasuredRoots = 0;
|
|
56
|
+
for (const pod of roots) {
|
|
57
|
+
try {
|
|
58
|
+
const xml = readFileSync(join(dir, pod.name.replace(/\.json$/, '.junit.xml')), 'utf8');
|
|
59
|
+
const seconds = [...readJunitFileSeconds(xml).values()];
|
|
60
|
+
if (!seconds.length) { unmeasuredRoots++; continue; }
|
|
61
|
+
overhead.push((pod.durationMs - seconds.reduce((sum, s) => sum + s, 0) * 1000) / 60_000);
|
|
62
|
+
} catch { unmeasuredRoots++; }
|
|
63
|
+
}
|
|
64
|
+
const wall = spanMin(pods);
|
|
65
|
+
const rootWall = roots.length ? spanMin(roots) : null;
|
|
66
|
+
const sorted = overhead.sort((a, b) => a - b);
|
|
67
|
+
return {
|
|
68
|
+
commit, records: pods.length, roots: roots.length, retries: pods.length - roots.length,
|
|
69
|
+
wallMin: wall, rootWallMin: rootWall,
|
|
70
|
+
retryTailMin: rootWall === null ? null : wall - rootWall,
|
|
71
|
+
overhead: {
|
|
72
|
+
medianMin: sorted.length ? median(sorted) : null,
|
|
73
|
+
p90Min: sorted.length ? sorted[Math.ceil(sorted.length * 0.9) - 1]! : null,
|
|
74
|
+
unmeasuredRoots,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const SHARDS_WINDOW_SLACK_MS = 5 * 60_000;
|
|
80
|
+
/**
|
|
81
|
+
* shards.json is a snapshot of the latest attempt, not an archive of every commit, and has no attempt identity.
|
|
82
|
+
* A row is attributed to the attempt only when its root id is one of the attempt's roots AND
|
|
83
|
+
* a row starts inside the attempt's pod-record window (from 5 min before its first start to its last end) — a
|
|
84
|
+
* previous attempt's snapshot reusing `pod-0` started earlier and is rejected. The end is free: a longer
|
|
85
|
+
* snapshot is exactly the disagreement this report keeps.
|
|
86
|
+
*/
|
|
87
|
+
function shardsSpan(dir: string, rootNames: Set<string>, window: { start: number; end: number }): number | 'outside-attempt-window' | null {
|
|
88
|
+
try {
|
|
89
|
+
const file: unknown = JSON.parse(readFileSync(join(dir, 'shards.json'), 'utf8'));
|
|
90
|
+
if (!file || typeof file !== 'object' || !('shards' in file) || !Array.isArray(file.shards)) return null;
|
|
91
|
+
const rows = file.shards as unknown[];
|
|
92
|
+
if (!rows.length) return null;
|
|
93
|
+
const starts: number[] = [];
|
|
94
|
+
const ends: number[] = [];
|
|
95
|
+
for (const row of rows) {
|
|
96
|
+
if (!row || typeof row !== 'object' || !('id' in row) || typeof row.id !== 'string'
|
|
97
|
+
|| !rootNames.has(`${row.id}.json`)) return null;
|
|
98
|
+
const start = 'startedAt' in row && typeof row.startedAt === 'string' ? Date.parse(row.startedAt) : NaN;
|
|
99
|
+
const end = 'endedAt' in row && typeof row.endedAt === 'string' ? Date.parse(row.endedAt) : NaN;
|
|
100
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || end < start) return null;
|
|
101
|
+
if (start < window.start - SHARDS_WINDOW_SLACK_MS || start > window.end) return 'outside-attempt-window';
|
|
102
|
+
starts.push(start);
|
|
103
|
+
ends.push(end);
|
|
104
|
+
}
|
|
105
|
+
if (Math.max(...ends) < Math.min(...starts)) return null;
|
|
106
|
+
return (Math.max(...ends) - Math.min(...starts)) / 60_000;
|
|
107
|
+
} catch { return null; }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function gateWall(dir: string, opts: { commit?: string; targetMin?: number } = {}): GateWallReport {
|
|
111
|
+
const targetMin = opts.targetMin ?? 30;
|
|
112
|
+
if (!Number.isFinite(targetMin) || targetMin <= 0) throw new Error('target-min 은 양수여야 합니다');
|
|
113
|
+
const groups = new Map<string, TimedPod[]>();
|
|
114
|
+
const unreadable: string[] = [];
|
|
115
|
+
const names = existsSync(dir) && statSync(dir).isDirectory() ? readdirSync(dir).filter((n) => POD_JSON.test(n)).sort() : [];
|
|
116
|
+
for (const name of names) {
|
|
117
|
+
try {
|
|
118
|
+
const pod: unknown = JSON.parse(readFileSync(join(dir, name), 'utf8'));
|
|
119
|
+
if (!pod || typeof pod !== 'object' || !('commit' in pod) || !('durationMs' in pod)
|
|
120
|
+
|| typeof pod.commit !== 'string' || !pod.commit.trim()
|
|
121
|
+
|| typeof pod.durationMs !== 'number' || !Number.isFinite(pod.durationMs) || pod.durationMs < 0) {
|
|
122
|
+
unreadable.push(name); continue;
|
|
123
|
+
}
|
|
124
|
+
const end = statSync(join(dir, name)).mtimeMs;
|
|
125
|
+
if (!Number.isFinite(end)) { unreadable.push(name); continue; }
|
|
126
|
+
const group = groups.get(pod.commit) ?? [];
|
|
127
|
+
group.push({ name, start: end - pod.durationMs, end, durationMs: pod.durationMs, root: ROOT.test(name) });
|
|
128
|
+
groups.set(pod.commit, group);
|
|
129
|
+
} catch { unreadable.push(name); }
|
|
130
|
+
}
|
|
131
|
+
const ordered = [...groups].sort((a, b) => Math.max(...b[1].map((p) => p.end)) - Math.max(...a[1].map((p) => p.end)));
|
|
132
|
+
const matching = opts.commit === undefined ? ordered : ordered.filter(([commit]) => commit.startsWith(opts.commit!));
|
|
133
|
+
if (opts.commit !== undefined && matching.length > 1) throw new Error(`모호한 커밋 앞자리: ${opts.commit}`);
|
|
134
|
+
const chosen = matching.find(([, pods]) => pods.some((p) => p.root));
|
|
135
|
+
const attempts = ordered.map(([commit, pods]) => attempt(dir, commit, pods));
|
|
136
|
+
const selected = chosen && attempts.find((a) => a.commit === chosen[0]);
|
|
137
|
+
// The snapshot has no commit field. Only the latest recorded attempt can own it; require its root row IDs too.
|
|
138
|
+
const latest = chosen !== undefined && ordered[0]?.[0] === chosen[0];
|
|
139
|
+
const span = chosen && latest
|
|
140
|
+
? shardsSpan(dir, new Set(chosen[1].filter((p) => p.root).map((p) => p.name)),
|
|
141
|
+
{ start: Math.min(...chosen[1].map((p) => p.start)), end: Math.max(...chosen[1].map((p) => p.end)) }) : null;
|
|
142
|
+
const snapshot = typeof span === 'number' ? span : null;
|
|
143
|
+
const shardsWallMin = snapshot;
|
|
144
|
+
const hasShards = existsSync(join(dir, 'shards.json'));
|
|
145
|
+
const shardsSkipped = !hasShards || !chosen ? null : !latest ? 'not-latest-attempt' as const : span === 'outside-attempt-window' ? span : null;
|
|
146
|
+
const wallMin = selected?.wallMin ?? null;
|
|
147
|
+
const verdict = wallMin === null ? null : wallMin > targetMin ? 'fail' as const : unreadable.length ? 'undetermined' as const : 'pass' as const;
|
|
148
|
+
return {
|
|
149
|
+
dir, targetMin, measured: selected !== undefined, commit: selected?.commit ?? null,
|
|
150
|
+
records: selected?.records ?? 0, roots: selected?.roots ?? 0, retries: selected?.retries ?? 0,
|
|
151
|
+
wallMin: selected?.wallMin ?? null, wallSource: selected ? 'pod-records' : null,
|
|
152
|
+
rootWallMin: selected?.rootWallMin ?? null, retryTailMin: selected?.retryTailMin ?? null,
|
|
153
|
+
overhead: selected?.overhead ?? { medianMin: null, p90Min: null, unmeasuredRoots: 0 },
|
|
154
|
+
retryOnlyAttempts: attempts.filter((a) => a.roots === 0).length,
|
|
155
|
+
attempts, shardsWallMin,
|
|
156
|
+
disagree: snapshot !== null && chosen !== undefined && Math.abs(snapshot - spanMin(chosen[1])) > 5,
|
|
157
|
+
unreadable, verdict, shardsSkipped,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function formatGateWall(report: GateWallReport): string {
|
|
162
|
+
const number = (value: number | null) => value === null ? '못 잼' : `${value.toFixed(1)}분`;
|
|
163
|
+
if (!report.measured) return `게이트 벽시계 못 잼 · 목표 ≤${report.targetMin}분 판정 못 잼 · 루트 못 잼 · 재시도 꼬리 못 잼 · 조각 준비 중앙 못 잼`;
|
|
164
|
+
return [
|
|
165
|
+
`게이트 벽시계 ${number(report.wallMin)}(pod-records · 커밋 ${report.commit!.slice(0, 8)}) · 목표 ≤${report.targetMin}분 ${report.verdict === 'pass' ? '✓' : report.verdict === 'fail' ? '✗' : `판정 보류(못 읽은 기록 ${report.unreadable.length})`} · 루트 ${number(report.rootWallMin)} · 재시도 꼬리 ${number(report.retryTailMin)} · 조각 준비 중앙 ${number(report.overhead.medianMin)}`,
|
|
166
|
+
`커밋 ${report.commit} · 기록 ${report.records} · 루트 ${report.roots} · 재시도 ${report.retries} · 루트 없는 시도 ${report.retryOnlyAttempts} · 준비 p90 ${number(report.overhead.p90Min)} · 준비 못 잰 루트 ${report.overhead.unmeasuredRoots}`,
|
|
167
|
+
...(report.shardsWallMin === null ? [] : [`shards.json 벽시계 ${number(report.shardsWallMin)} · 기록 추정 ${number(report.wallMin)}${report.disagree ? ' · ⚠ 5분 초과 불일치' : ''}`]),
|
|
168
|
+
...(report.shardsSkipped ? [`shards.json 대조 생략 — 이 시도의 것인지 확인 못 함(${report.shardsSkipped})`] : []),
|
|
169
|
+
...(report.unreadable.length ? [`⚠ 못 읽은 기록 ${report.unreadable.length}: ${report.unreadable.slice(0, 3).join(', ')}`] : []),
|
|
170
|
+
].join('\n');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export const GATE_WALL_USAGE = '사용법: bun scripts/release-loop/gate-wall.ts <version|dir> [--commit <sha>] [--target-min <n>] [--json]';
|
|
174
|
+
|
|
175
|
+
if (import.meta.main) {
|
|
176
|
+
const args = process.argv.slice(2);
|
|
177
|
+
let target: string | undefined;
|
|
178
|
+
let commit: string | undefined;
|
|
179
|
+
let targetMin: number | undefined;
|
|
180
|
+
let json = false;
|
|
181
|
+
let error: string | undefined;
|
|
182
|
+
for (let i = 0; i < args.length; i++) {
|
|
183
|
+
const arg = args[i]!;
|
|
184
|
+
if (arg === '--json') { json = true; continue; }
|
|
185
|
+
if (arg === '--commit' || arg === '--target-min') {
|
|
186
|
+
const value = args[++i];
|
|
187
|
+
if (!value || value.startsWith('-')) { error = `값이 없는 옵션: ${arg}`; break; }
|
|
188
|
+
if (arg === '--commit') commit = value;
|
|
189
|
+
else {
|
|
190
|
+
targetMin = Number(value);
|
|
191
|
+
if (!Number.isFinite(targetMin) || targetMin <= 0) { error = `잘못된 목표 분: ${value}`; break; }
|
|
192
|
+
}
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (arg.startsWith('-')) { error = `알 수 없는 옵션: ${arg}`; break; }
|
|
196
|
+
if (target) { error = `예상치 못한 인자: ${arg}`; break; }
|
|
197
|
+
target = arg;
|
|
198
|
+
}
|
|
199
|
+
if (error || !target) { console.error(`${error ?? '대상 경로 또는 버전이 필요합니다'}\n${GATE_WALL_USAGE}`); process.exit(2); }
|
|
200
|
+
try {
|
|
201
|
+
const report = gateWall(resolveGateLogDir(target), { commit, targetMin });
|
|
202
|
+
console.log(json ? JSON.stringify(report, null, 2) : formatGateWall(report));
|
|
203
|
+
if (!report.measured) process.exitCode = 1;
|
|
204
|
+
} catch (e) {
|
|
205
|
+
console.error(`${e instanceof Error ? e.message : String(e)}\n${GATE_WALL_USAGE}`);
|
|
206
|
+
process.exitCode = 2;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { effectiveInstanceRoot } from '../../src/instance/resolve.js';
|
|
4
|
+
import type { GraphRunState } from '../../src/graph-runner/runner.js';
|
|
3
5
|
import { join } from 'node:path';
|
|
4
6
|
import { emitNodeResult, readGraphContext, runCommand, type CommandRunner, type GraphContext } from './node-verdict.js';
|
|
5
7
|
|
|
@@ -64,6 +66,37 @@ function cleanBullet(text: string): string | null {
|
|
|
64
66
|
return line;
|
|
65
67
|
}
|
|
66
68
|
|
|
69
|
+
/** A waiver is copied verbatim into public notes, so reject internal or resolved claims before resuming. */
|
|
70
|
+
export function publicWaiverReason(text: string): string {
|
|
71
|
+
const bullet = cleanBullet(text);
|
|
72
|
+
if (!bullet || resolved.test(bullet) || !/[a-z]{3}/i.test(bullet)) throw new Error('unsafe public waiver reason');
|
|
73
|
+
return bullet;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function recordedWaivers(context: GraphContext, accepted: Issue[]): Issue[] {
|
|
77
|
+
const contextPath = process.env.ELANOUS_GRAPH_CONTEXT;
|
|
78
|
+
if (!contextPath || !/\.json\.contexts\/[1-9]\d*\.json$/.test(contextPath)) return [];
|
|
79
|
+
const raw = JSON.parse(readFileSync(contextPath, 'utf8')) as { graphId?: string; runId?: string; nodeId?: string };
|
|
80
|
+
if (raw.graphId !== 'release-loop' || raw.nodeId !== 'known-issues' || !raw.runId || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(raw.runId)) return [];
|
|
81
|
+
const statePath = join(effectiveInstanceRoot(), 'graph-runs', 'release-loop', `${raw.runId}.json`);
|
|
82
|
+
const visit = Number(contextPath.slice(contextPath.lastIndexOf('/') + 1, -'.json'.length));
|
|
83
|
+
if (contextPath !== join(`${statePath}.contexts`, `${visit}.json`)) return [];
|
|
84
|
+
const state = JSON.parse(readFileSync(statePath, 'utf8')) as GraphRunState;
|
|
85
|
+
if (state.graphId !== 'release-loop' || state.runId !== raw.runId || state.path[visit - 1] !== 'known-issues' ||
|
|
86
|
+
JSON.stringify((state.input as { acceptedRegressions?: unknown })?.acceptedRegressions ?? []) !== JSON.stringify(context.input.acceptedRegressions ?? [])) return [];
|
|
87
|
+
return accepted.filter((issue) => {
|
|
88
|
+
if (!issue.id.startsWith('waive:')) return false;
|
|
89
|
+
const node = issue.id.slice('waive:'.length);
|
|
90
|
+
const index = state.path.indexOf(node);
|
|
91
|
+
const record = state.nodes[index];
|
|
92
|
+
const waiver = record?.waiver;
|
|
93
|
+
return index >= 0 && index < visit - 1 && record?.nodeId === node && waiver !== undefined && waiver.reason === issue.note &&
|
|
94
|
+
// The runner writes a waiver only over a failed executed node (releaseResumeWaiver); its exit may be 0 (outcome fail) or null (timeout).
|
|
95
|
+
record.ok === true && record.executed === false &&
|
|
96
|
+
context.outputs[node]?.verdict === 'waived' && context.outputs[node]?.reason === issue.note;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
67
100
|
function command(run: CommandRunner, program: string, args: string[], cwd?: string): string {
|
|
68
101
|
const result = run(program, args, cwd);
|
|
69
102
|
if (result.status !== 0) throw new Error(`${program} ${args[0]} failed: ${result.stderr || result.stdout}`);
|
|
@@ -85,13 +118,18 @@ export function runKnownIssues(context: GraphContext = readGraphContext(), run:
|
|
|
85
118
|
let bullets: string[];
|
|
86
119
|
let fallbackUsed = false;
|
|
87
120
|
const allIssues = [...checklist, ...accepted];
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
121
|
+
// The persisted failed-node waiver, not a claimed output or an ID prefix, is the public-note provenance.
|
|
122
|
+
const waiverIssues = recordedWaivers(context, accepted);
|
|
123
|
+
const modelIssues = allIssues.filter((issue) => !waiverIssues.includes(issue));
|
|
124
|
+
const sourceIds = modelIssues.map((issue) => issue.id);
|
|
125
|
+
const prompt = `Write public English release-note bullets for these known issues. Describe only user-visible impact; include a one-line workaround when available. No internal names, test paths, track names, PR numbers, or people. Each bullet must have at most two sentences. Return ONLY a JSON array with exactly one object {"id":"input ID","bullet":"English public sentence"} for each input, using each input ID exactly once. Do not include IDs in bullet text. Input: ${JSON.stringify({ checklist, acceptedRegressions: accepted.filter((issue) => !waiverIssues.includes(issue)) })}`;
|
|
126
|
+
let response: ReturnType<CommandRunner> = { status: 0, stdout: JSON.stringify({ reply: '[]' }), stderr: '' };
|
|
127
|
+
if (modelIssues.length) {
|
|
128
|
+
try {
|
|
129
|
+
response = run('elanous', ['--test', 'ask', '--json', prompt]);
|
|
130
|
+
} catch {
|
|
131
|
+
response = { status: 1, stdout: '', stderr: '' };
|
|
132
|
+
}
|
|
95
133
|
}
|
|
96
134
|
if (response.status !== 0) {
|
|
97
135
|
bullets = [FALLBACK];
|
|
@@ -105,7 +143,7 @@ export function runKnownIssues(context: GraphContext = readGraphContext(), run:
|
|
|
105
143
|
return FALLBACK;
|
|
106
144
|
}
|
|
107
145
|
if (resolved.test(safe)) throw new Error(`bullet for ${sourceIds[index]} claims the issue is resolved`);
|
|
108
|
-
if (!coversIssue(safe,
|
|
146
|
+
if (!coversIssue(safe, modelIssues[index]!, modelIssues.filter((_, otherIndex) => otherIndex !== index))) {
|
|
109
147
|
throw new Error(`bullet does not identify input ${sourceIds[index]}`);
|
|
110
148
|
}
|
|
111
149
|
return safe;
|
|
@@ -118,6 +156,13 @@ export function runKnownIssues(context: GraphContext = readGraphContext(), run:
|
|
|
118
156
|
throw new Error(`known issues coverage not verified: ${error instanceof Error ? error.message : String(error)}`);
|
|
119
157
|
}
|
|
120
158
|
}
|
|
159
|
+
// A waiver's supplied public impact must survive even when the model omits it or is unavailable.
|
|
160
|
+
for (const issue of waiverIssues) {
|
|
161
|
+
const bullet = cleanBullet(issue.note ?? '');
|
|
162
|
+
if (!bullet || resolved.test(bullet)) throw new Error(`unsafe public waiver reason for ${issue.id}`);
|
|
163
|
+
const publicBullet = /[.!?]$/.test(bullet) ? bullet : `${bullet}.`;
|
|
164
|
+
if (!bullets.includes(publicBullet)) bullets.push(publicBullet);
|
|
165
|
+
}
|
|
121
166
|
const original = readFileSync(notes, 'utf8');
|
|
122
167
|
const heading = /^## Known issues\s*$/m;
|
|
123
168
|
const match = heading.exec(original);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// RELEASE-PUBLISH-AT — the one validator for a promised publish time (CLI entry and the publish node share it).
|
|
2
|
+
|
|
3
|
+
/** `--publish-at` must be an offset-bearing ISO datetime; returns the refusal message, or null when valid. */
|
|
4
|
+
export function publishAtError(value: string): string | null {
|
|
5
|
+
const refused = '--publish-at requires an offset-bearing ISO datetime on a real calendar date';
|
|
6
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?(?:Z|[+-](\d{2}):(\d{2}))$/.exec(value);
|
|
7
|
+
if (!m || !Number.isFinite(Date.parse(value))) return refused;
|
|
8
|
+
const [y, mo, d, h, mi, sec = '0', oh = '0', om = '0'] = m.slice(1) as string[];
|
|
9
|
+
// Date.parse rolls 02-30 over into March; a promised publish day must be the day that was written.
|
|
10
|
+
const day = new Date(Date.UTC(Number(y), Number(mo) - 1, Number(d)));
|
|
11
|
+
if (day.getUTCFullYear() !== Number(y) || day.getUTCMonth() !== Number(mo) - 1 || day.getUTCDate() !== Number(d)) return refused;
|
|
12
|
+
if (Number(h) > 23 || Number(mi) > 59 || Number(sec) > 59 || Number(oh) > 23 || Number(om) > 59) return refused;
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
@@ -6,6 +6,7 @@ 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
8
|
import { prereleaseKind } from './release-version.js';
|
|
9
|
+
import { publishAtError } from './publish-at.js';
|
|
9
10
|
import { errorResult, finishNode, lastResult, nodeOutput, readGraphContext, runCommand, type CommandRunner } from './node-verdict.js';
|
|
10
11
|
|
|
11
12
|
export function publicNotes(markdown: string, pages: { pages: Array<{ id: string; slug?: string; source?: string }> }): string {
|
|
@@ -43,7 +44,8 @@ export function waitForAssets(run: CommandRunner, repo: string, tag: string, nam
|
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
export function runPublish(run: CommandRunner = runCommand, prodFreezeRoot?: string
|
|
47
|
+
export function runPublish(run: CommandRunner = runCommand, prodFreezeRoot?: string,
|
|
48
|
+
clock: { now: () => number; sleep: (ms: number) => void } = { now: Date.now, sleep: Bun.sleepSync }) {
|
|
47
49
|
const context = readGraphContext();
|
|
48
50
|
const version = context.input.version;
|
|
49
51
|
// A measured automatic decision or the existing human approval must authorize publication.
|
|
@@ -69,6 +71,17 @@ export function runPublish(run: CommandRunner = runCommand, prodFreezeRoot?: str
|
|
|
69
71
|
if (doc.status !== 0 || registry.status !== 0) throw new Error(`docs branch missing notes or pages: ${branch}`);
|
|
70
72
|
if (!doc.stdout.startsWith(`# ${version}\n`)) throw new Error('release notes version mismatch');
|
|
71
73
|
const body = labelPrerelease(version, publicNotes(doc.stdout, JSON.parse(registry.stdout)));
|
|
74
|
+
if (context.input.publishAt !== undefined) {
|
|
75
|
+
const at = context.input.publishAt;
|
|
76
|
+
if (typeof at !== 'string' || publishAtError(at) !== null) {
|
|
77
|
+
throw new Error('invalid publishAt in release run input');
|
|
78
|
+
}
|
|
79
|
+
const deadline = Date.parse(at);
|
|
80
|
+
while (clock.now() < deadline) {
|
|
81
|
+
const remaining = deadline - clock.now();
|
|
82
|
+
clock.sleep(Math.min(remaining, 60_000));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
72
85
|
const dir = mkdtempSync(join(tmpdir(), 'release-publish-notes-'));
|
|
73
86
|
try {
|
|
74
87
|
const file = join(dir, 'notes.md');
|
|
@@ -83,7 +96,9 @@ export function runPublish(run: CommandRunner = runCommand, prodFreezeRoot?: str
|
|
|
83
96
|
if (context.input.forceFreeze !== true) return { outcome: 'fail' as const, verdict: 'fail' as const, summary: `publish blocked: ${landingFreezeMessage(frozen)}` };
|
|
84
97
|
}
|
|
85
98
|
let published: ReturnType<typeof run>;
|
|
86
|
-
|
|
99
|
+
// The graph recipe no longer bounds this node (it may wait for publishAt), so the irreversible publication itself
|
|
100
|
+
// keeps the former 30-minute bound on every run — with or without publishAt.
|
|
101
|
+
try { published = run('bun', ['bin/elanous.mjs', 'release', 'publish', '--dir', out, '--notes-file', file, '--yes', '--json'], undefined, 1_800_000); }
|
|
87
102
|
finally { landing.end(); }
|
|
88
103
|
if (published.stderr) process.stderr.write(published.stderr);
|
|
89
104
|
const result = lastResult(published);
|
|
@@ -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 { releaseResumeAcceptedRegressions, releaseResumeGateExecutor, releaseResumeWaiver, type AcceptedRegression } from './resume.js';
|
|
13
14
|
|
|
14
15
|
const GRAPH = join(import.meta.dir, '../../graphs/release/release-loop.yaml');
|
|
15
16
|
|
|
@@ -55,7 +56,7 @@ function safeRunId(runId: string): string {
|
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
/** Point the saved version-release output at the current release/<v> tip (fast-forward of the recorded commit only). */
|
|
58
|
-
export function refreshReleaseBranchTip(runId: string, deps: ResumeReleaseDeps = {}, from?: string): TipRefresh {
|
|
59
|
+
export function refreshReleaseBranchTip(runId: string, deps: ResumeReleaseDeps = {}, from?: string, waiverCommit?: string): TipRefresh {
|
|
59
60
|
const root = deps.root ?? effectiveInstanceRoot();
|
|
60
61
|
const statePath = join(root, 'graph-runs', 'release-loop', `${safeRunId(runId)}.json`);
|
|
61
62
|
const state = JSON.parse(readFileSync(statePath, 'utf8')) as GraphRunState;
|
|
@@ -81,6 +82,9 @@ export function refreshReleaseBranchTip(runId: string, deps: ResumeReleaseDeps =
|
|
|
81
82
|
if (!/^[0-9a-f]{40}$/i.test(advertised)) throw new Error(`${branch} is not on origin — this run was not cut as a release branch`);
|
|
82
83
|
git(['fetch', 'origin', `+refs/heads/${branch}:refs/remotes/origin/${branch}`]);
|
|
83
84
|
const to = git(['rev-parse', '--verify', `refs/remotes/origin/${branch}^{commit}`]);
|
|
85
|
+
if (waiverCommit && (recorded.toLowerCase() !== waiverCommit.toLowerCase() || to.toLowerCase() !== waiverCommit.toLowerCase() || advertised.toLowerCase() !== waiverCommit.toLowerCase())) {
|
|
86
|
+
throw new Error('release branch moved — re-gate the new tip before waiving a node');
|
|
87
|
+
}
|
|
84
88
|
if (to !== advertised) throw new Error(`${branch} moved while it was read (${advertised} → ${to})`);
|
|
85
89
|
if (to === recorded) {
|
|
86
90
|
debug.log('release-loop.resume', 'tip-unchanged', { runId, version, commit: to });
|
|
@@ -164,14 +168,46 @@ function savedGraphPath(statePath: string): string | undefined {
|
|
|
164
168
|
}
|
|
165
169
|
|
|
166
170
|
/** Refresh the branch tip, then restart the failed run at `from` with the saved graph snapshot. */
|
|
167
|
-
export async function resumeReleaseRun(opts: { runId: string; from: string; partial?: boolean }, deps: ResumeReleaseDeps = {}): Promise<{ tip: TipRefresh; partial: PartialOutcome; state: GraphRunState }> {
|
|
171
|
+
export async function resumeReleaseRun(opts: { runId: string; from: string; partial?: boolean; waive?: string; reason?: string; acceptedRegressions?: AcceptedRegression[]; gateExecutor?: string; gateHost?: string }, deps: ResumeReleaseDeps = {}): Promise<{ tip: TipRefresh; partial: PartialOutcome; state: GraphRunState }> {
|
|
168
172
|
if (opts.from === 'version-release') throw new Error('--from version-release would cut again — resume at a later node');
|
|
169
173
|
// RELEASE-LEDGER-UNIVERSE: decide the universe once; the run lookup and every resumed node use it.
|
|
170
174
|
deps = { ...deps, root: releaseRunUniverse({ root: deps.root }) };
|
|
171
|
-
const
|
|
175
|
+
const statePath = join(deps.root!, 'graph-runs', 'release-loop', `${safeRunId(opts.runId)}.json`);
|
|
176
|
+
let waiverCommit: string | undefined;
|
|
177
|
+
if (opts.gateHost !== undefined && opts.gateExecutor === undefined) throw new Error('--gate-host requires --gate-executor host');
|
|
178
|
+
const original = opts.waive !== undefined || opts.reason !== undefined || opts.acceptedRegressions !== undefined || opts.gateExecutor !== undefined
|
|
179
|
+
? JSON.parse(readFileSync(statePath, 'utf8')) as GraphRunState : undefined;
|
|
180
|
+
// GATE-HOST-EXECUTOR: checked before the tip refresh writes anything; the runner checks it again under the resume lock.
|
|
181
|
+
const gateExecutor = original && opts.gateExecutor !== undefined
|
|
182
|
+
? releaseResumeGateExecutor(original, { from: opts.from, executor: opts.gateExecutor, host: opts.gateHost }) : undefined;
|
|
183
|
+
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
|
+
const waiver = original && (opts.waive !== undefined || opts.reason !== undefined)
|
|
185
|
+
? releaseResumeWaiver(original, opts) : undefined;
|
|
186
|
+
const acceptedRegressions = original && opts.acceptedRegressions !== undefined && !waiver
|
|
187
|
+
? releaseResumeAcceptedRegressions(original, opts.acceptedRegressions, opts.from) : undefined;
|
|
188
|
+
// A waived verdict may reuse its original commit. A moved tip still requires a full re-gate.
|
|
189
|
+
if (waiver) {
|
|
190
|
+
const saved = JSON.parse(readFileSync(statePath, 'utf8')) as GraphRunState;
|
|
191
|
+
const versionRecord = saved.nodes.find((node) => node.nodeId === 'version-release');
|
|
192
|
+
const output = lastJsonObject(versionRecord?.output);
|
|
193
|
+
const version = output?.version;
|
|
194
|
+
const recorded = output?.commit;
|
|
195
|
+
if (typeof version !== 'string' || typeof recorded !== 'string' || !isReleaseVersion(version) || !/^[0-9a-f]{40}$/i.test(recorded)) throw new Error('release waiver requires a recorded release commit');
|
|
196
|
+
const git = deps.git ?? gitRunner(deps.repo ?? process.cwd());
|
|
197
|
+
const branch = `release/${version}`;
|
|
198
|
+
const advertised = git(['ls-remote', '--heads', 'origin', `refs/heads/${branch}`]).split(/\s+/)[0];
|
|
199
|
+
if (advertised?.toLowerCase() !== recorded.toLowerCase()) throw new Error('release branch moved — re-gate the new tip before waiving a node');
|
|
200
|
+
waiverCommit = recorded;
|
|
201
|
+
}
|
|
202
|
+
// Check the refreshed tip before refresh writes a new version-release record or backup.
|
|
203
|
+
const tip = refreshReleaseBranchTip(opts.runId, deps, opts.from, waiverCommit);
|
|
172
204
|
const partial = preparePartial(tip, opts, deps);
|
|
173
205
|
// Resume the graph at the path it was started from (its snapshot is used); only tests inject another path.
|
|
174
206
|
const graphPath = deps.graphPath ?? savedGraphPath(tip.statePath) ?? GRAPH;
|
|
175
|
-
const state = await (deps.graph ?? runGraph)(graphPath, { resumeRunId: opts.runId, fromNodeId: opts.from, pinChildUniverse: true,
|
|
207
|
+
const state = await (deps.graph ?? runGraph)(graphPath, { resumeRunId: opts.runId, fromNodeId: opts.from, pinChildUniverse: true,
|
|
208
|
+
...(waiver ? { releaseWaiver: { ...waiver, requestedRegressions: opts.acceptedRegressions ?? [] } } : {}),
|
|
209
|
+
...(acceptedRegressions ? { releaseAcceptedRegressions: { requested: opts.acceptedRegressions!, acceptedRegressions } } : {}),
|
|
210
|
+
...(gateExecutor ? { releaseGateExecutor: { executor: gateExecutor.set.gateExecutor, ...(opts.gateHost !== undefined ? { host: opts.gateHost } : {}) } } : {}),
|
|
211
|
+
deps: { root: deps.root, ...(deps.runBash ? { runBash: deps.runBash } : {}) } });
|
|
176
212
|
return { tip, partial, state };
|
|
177
213
|
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { GraphRunState } from '../../src/graph-runner/runner.js';
|
|
2
|
+
import { publicWaiverReason } from './known-issues-node.js';
|
|
3
|
+
import { checkGateExecutorChoice, type GateExecutor } from './gate-executor.js';
|
|
4
|
+
|
|
5
|
+
export interface AcceptedRegression { id: string; note: string }
|
|
6
|
+
|
|
7
|
+
/** Merge explicit public issues with the saved release input without changing the ledger shape. */
|
|
8
|
+
export function releaseResumeAcceptedRegressions(state: GraphRunState, additions: AcceptedRegression[], from?: string): AcceptedRegression[] {
|
|
9
|
+
if (!Array.isArray(additions) || additions.some((issue) => !issue || typeof issue.id !== 'string' || !issue.id.trim() || typeof issue.note !== 'string' || !issue.note.trim())) {
|
|
10
|
+
throw new Error('acceptedRegressions must be an array of {id, note}');
|
|
11
|
+
}
|
|
12
|
+
const input = state.input;
|
|
13
|
+
if (!input || typeof input !== 'object' || Array.isArray(input) || typeof (input as { version?: unknown }).version !== 'string') throw new Error('release run has no input');
|
|
14
|
+
const existing = (input as { acceptedRegressions?: unknown }).acceptedRegressions;
|
|
15
|
+
if (existing !== undefined && (!Array.isArray(existing) || existing.some((issue) => !issue || typeof issue.id !== 'string' || !issue.id.trim() || typeof issue.note !== 'string' || !issue.note.trim()))) {
|
|
16
|
+
throw new Error('saved acceptedRegressions is invalid');
|
|
17
|
+
}
|
|
18
|
+
// A rerun of a formerly waived node supersedes only that node's recorded waiver;
|
|
19
|
+
// independently supplied accepted issues (including similarly named IDs) are not inferred to be waivers.
|
|
20
|
+
const prior = (existing ?? []) as AcceptedRegression[];
|
|
21
|
+
// Every waived record at or after the restart boundary is cut from the ledger, so each one's issue retires too.
|
|
22
|
+
const start = from === undefined ? -1 : state.path.indexOf(from);
|
|
23
|
+
const retired = start < 0 ? [] : state.nodes.slice(start)
|
|
24
|
+
.filter((record) => record.waiver && record.executed === false && record.ok === true)
|
|
25
|
+
.map((record) => ({ id: `waive:${record.nodeId}`, note: record.waiver!.reason }));
|
|
26
|
+
const combined = [...prior.filter((issue) => !retired.some((gone) => gone.id === issue.id && gone.note === issue.note)), ...additions];
|
|
27
|
+
if (new Set(combined.map(({ id }) => id)).size !== combined.length) throw new Error('acceptedRegressions IDs must be unique');
|
|
28
|
+
return combined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Only the recorded failure at the restart boundary may be waived. `--from` is never a waiver. */
|
|
32
|
+
export function releaseResumeWaiver(
|
|
33
|
+
state: GraphRunState,
|
|
34
|
+
opts: { from: string; waive?: string; reason?: string; acceptedRegressions?: AcceptedRegression[] },
|
|
35
|
+
): { nodeId: string; reason: string; acceptedRegressions: AcceptedRegression[] } | undefined {
|
|
36
|
+
if ((opts.waive === undefined) !== (opts.reason === undefined)) throw new Error('--waive <node> and --reason <text> must be supplied together');
|
|
37
|
+
if (opts.waive !== undefined && !opts.waive.trim()) throw new Error('--waive must not be empty');
|
|
38
|
+
if (opts.waive === undefined) return undefined;
|
|
39
|
+
const reason = opts.reason?.trim();
|
|
40
|
+
if (!reason) throw new Error('--reason must not be empty');
|
|
41
|
+
publicWaiverReason(reason);
|
|
42
|
+
if (opts.from !== opts.waive) throw new Error('--from must name the waived node; --from alone does not waive a failure');
|
|
43
|
+
const index = state.path.indexOf(opts.waive);
|
|
44
|
+
if (index < 0 || state.path.lastIndexOf(opts.waive) !== index || state.nodes[index]?.nodeId !== opts.waive || state.nodes[index]?.ok !== false ||
|
|
45
|
+
state.nodes[index]?.executed !== true || state.path.at(-2) !== opts.waive || state.path.at(-1) !== 'failed' || state.nodes.at(-1)?.nodeId !== 'failed') {
|
|
46
|
+
throw new Error(`--waive ${opts.waive} requires the failed executable node on the saved path`);
|
|
47
|
+
}
|
|
48
|
+
// Only user-visible, non-publication checks can become accepted regressions; build, safety and approval gates stay blocking.
|
|
49
|
+
if (!['mac-smoke', 'tui'].includes(opts.waive)) {
|
|
50
|
+
throw new Error(`--waive ${opts.waive} cannot waive a safety, side-effect or approval node`);
|
|
51
|
+
}
|
|
52
|
+
return { nodeId: opts.waive, reason, acceptedRegressions: releaseResumeAcceptedRegressions(state,
|
|
53
|
+
[...(opts.acceptedRegressions ?? []), { id: `waive:${opts.waive}`, note: reason }]) };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** GATE-HOST-EXECUTOR — one explicit executor switch, recorded on the run (`GraphRunState.inputOverrides`). */
|
|
57
|
+
export interface GateExecutorOverride {
|
|
58
|
+
kind: 'gate-executor';
|
|
59
|
+
at: string;
|
|
60
|
+
from: string;
|
|
61
|
+
set: { gateExecutor: GateExecutor; gateRemote?: string };
|
|
62
|
+
/** Input keys removed — switching back to pod drops the host an earlier override added (the original had none). */
|
|
63
|
+
unset?: Array<'gateRemote'>;
|
|
64
|
+
previous: { gateExecutor: unknown; gateRemote: unknown };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Switch the gate executor of a resumed release run. Not a waiver: the gate node runs again (the restart must be at or
|
|
69
|
+
* before gate) and produces a normal verdict; only where its sweep runs changes.
|
|
70
|
+
*/
|
|
71
|
+
export function releaseResumeGateExecutor(state: GraphRunState, opts: { from: string; executor: unknown; host?: unknown; at?: string }): GateExecutorOverride {
|
|
72
|
+
const input = state.input;
|
|
73
|
+
if (!input || typeof input !== 'object' || Array.isArray(input) || typeof (input as { version?: unknown }).version !== 'string') throw new Error('release run has no input');
|
|
74
|
+
const saved = input as { gateExecutor?: unknown; gateRemote?: unknown; gatePodPool?: unknown };
|
|
75
|
+
// Host executor without --gate-host: the run's own gateRemote (release.loop.gateRemote when it started), like `release run`.
|
|
76
|
+
const choice = checkGateExecutorChoice(opts.executor, opts.executor === 'host' && opts.host === undefined ? saved.gateRemote : opts.host);
|
|
77
|
+
if (choice.executor === 'pod' && (typeof saved.gatePodPool !== 'string' || !saved.gatePodPool.trim())) {
|
|
78
|
+
throw new Error('--gate-executor pod requires the saved run input gatePodPool');
|
|
79
|
+
}
|
|
80
|
+
const start = state.path.indexOf(opts.from);
|
|
81
|
+
if (start < 0) throw new Error(`--from ${opts.from} is not a node on the saved path`);
|
|
82
|
+
const gate = state.path.indexOf('gate');
|
|
83
|
+
// A restart after gate keeps the recorded verdict — switching its executor there would record a choice nothing used.
|
|
84
|
+
if (gate >= 0 && start > gate) throw new Error('--gate-executor needs a resume that re-runs gate (--from gate or earlier)');
|
|
85
|
+
// Back to pod: restore the gateRemote the run started with (an earlier host override replaced it).
|
|
86
|
+
const firstHost = choice.executor === 'pod' ? state.inputOverrides?.find((item) => item.kind === 'gate-executor' && item.set.gateRemote !== undefined) : undefined;
|
|
87
|
+
const restored = firstHost ? firstHost.previous.gateRemote : undefined;
|
|
88
|
+
return {
|
|
89
|
+
kind: 'gate-executor', at: opts.at ?? new Date().toISOString(), from: opts.from,
|
|
90
|
+
set: { gateExecutor: choice.executor, ...(choice.host ? { gateRemote: choice.host } : typeof restored === 'string' ? { gateRemote: restored } : {}) },
|
|
91
|
+
...(firstHost && typeof restored !== 'string' ? { unset: ['gateRemote' as const] } : {}),
|
|
92
|
+
previous: { gateExecutor: saved.gateExecutor ?? null, gateRemote: saved.gateRemote ?? null },
|
|
93
|
+
};
|
|
94
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
|
2
2
|
import { basename, dirname, join, resolve } from 'node:path';
|
|
3
3
|
import { debug } from '../../src/debug/log.js';
|
|
4
|
+
import { readJunitFileSeconds } from './gate-method';
|
|
4
5
|
|
|
5
6
|
/** Plan weights for a Pod sweep: wall-scaled when the previous cut recorded shard wall times, junit seconds otherwise. */
|
|
6
7
|
export function planDurations(durationSource: string | undefined): { durations: Map<string, number>; wall?: { shards: number; scaledFiles: number }; fallback?: string } {
|
|
@@ -66,21 +67,12 @@ export function logPlanWalltime(wall: { shards: number; scaledFiles: number } |
|
|
|
66
67
|
export function readFileDurations(dir: string): Map<string, number> {
|
|
67
68
|
const durations = new Map<string, number>();
|
|
68
69
|
let names: string[];
|
|
69
|
-
const entities: Record<string, string> = { '&': '&', '"': '"', ''': "'", '<': '<', '>': '>' };
|
|
70
70
|
try { names = readdirSync(dir).filter((name) => name.endsWith('.junit.xml')).sort(); }
|
|
71
71
|
catch { return durations; }
|
|
72
72
|
for (const name of names) {
|
|
73
73
|
try {
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
const attrs = match[1]!;
|
|
77
|
-
const file = /\bfile="([^"]+)"/.exec(attrs)?.[1];
|
|
78
|
-
const rawTime = /\btime="([^"]+)"/.exec(attrs)?.[1];
|
|
79
|
-
if (!file || rawTime === undefined) continue;
|
|
80
|
-
const seconds = Number(rawTime);
|
|
81
|
-
if (!Number.isFinite(seconds) || seconds < 0) continue;
|
|
82
|
-
const path = file.replace(/&(?:amp|quot|apos|lt|gt);/g, (entity) => entities[entity]!);
|
|
83
|
-
durations.set(path, Math.max(durations.get(path) ?? 0, seconds));
|
|
74
|
+
for (const [file, seconds] of readJunitFileSeconds(readFileSync(join(dir, name), 'utf8'))) {
|
|
75
|
+
durations.set(file, Math.max(durations.get(file) ?? 0, seconds));
|
|
84
76
|
}
|
|
85
77
|
} catch { /* One unreadable report must not discard the others. */ }
|
|
86
78
|
}
|
|
@@ -28,6 +28,28 @@ export function tuiVerdict(checks: readonly TuiCheck[]): 'pass' | 'fail' {
|
|
|
28
28
|
return checks.every((c) => c.pass) ? 'pass' : 'fail';
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/** `tui-sim-node.ts --json` prints the raw result (no `outcome`); `node-verdict.ts tui` adds it. Same rule here so a
|
|
32
|
+
* prefetched raw result is judged exactly like the wrapped node result: exit 0 with pass|flaky → ok. */
|
|
33
|
+
export function withTuiOutcome(status: number | null, data: Record<string, unknown> | null): Record<string, unknown> | null {
|
|
34
|
+
if (!data) return null;
|
|
35
|
+
const outcome = status === 0 && (data.verdict === 'pass' || data.verdict === 'flaky') ? 'ok' : status === 1 ? 'fail' : 'error';
|
|
36
|
+
return { ...data, outcome };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function reusableTui(data: unknown, commit: string): Record<string, unknown> | null {
|
|
40
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) return null;
|
|
41
|
+
const value = data as Record<string, unknown>;
|
|
42
|
+
if (value.outcome !== 'ok' || value.commit !== commit || (value.verdict !== 'pass' && value.verdict !== 'flaky')
|
|
43
|
+
|| (value.verdict === 'pass' ? value.attempts !== 1 : value.attempts !== 2)
|
|
44
|
+
|| !Array.isArray(value.checks) || value.checks.length !== 6) return null;
|
|
45
|
+
const ids = ['boot', 'prompt', 'status-bar', 'help-opens', 'help-closes', 'no-error-text'];
|
|
46
|
+
if (value.checks.some((check: unknown, i: number) => !check || typeof check !== 'object'
|
|
47
|
+
|| (check as TuiCheck).id !== ids[i] || (check as TuiCheck).pass !== true
|
|
48
|
+
|| typeof (check as TuiCheck).detail !== 'string')) return null;
|
|
49
|
+
if (value.regress === null || typeof value.regress !== 'object' || Array.isArray(value.regress)) return null;
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
|
|
31
53
|
export type TuiRegress = {
|
|
32
54
|
pass: number;
|
|
33
55
|
fail: number;
|