elanous 0.2.22 → 0.2.23
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/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-b23d2e92161d11fd.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-fbef064c213b2b18.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-d5341ac96203a983.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-0160ab83c8685a12.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-55c631c032dcc47e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-ad3bd8084048bee4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-e6784759a2531031.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-5c5d0e370f495ef6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-c325e21543cd48a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/{page-80ed511e02c81412.js → page-6168a7c43e6ef8f5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-31417d3604154a6d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-e9606dc0bdec9305.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-a648bc464c9dc754.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-25bb425afdb27541.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-32d72fdef1afe11a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-d27b794c8ba76bbb.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-2bd9ce9c020d02d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-a34096245e63b9f2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-22170f4f00a15496.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-4cc66d22ca4ec502.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-83b14c729fcea837.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-6e2781c523dad761.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-26dd415c8f13f341.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-ccdb83ed40c8d724.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-7ad3de8fa06fdea9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-015c02c4fec4023e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-6b0ae5b5c42f6d2f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-786111f30b6f0969.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-48fb31810693c357.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4e17cce5e2ed0fee.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-cb154acadb025414.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-16258e956ea93fc2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-c2dd2b19c93f90ab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f35dd1dd96ae87.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-d3c007c8a0ddd7ee.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-7837ce2aacb97b04.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-7350ee93f4fe1033.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-00723458575f2b19.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-745d9b6c015076f8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-736d33c7414a8a1a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-119be9a0444d2482.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-fa806c018833e332.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-c782041b5f5199ba.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-d431fbdea6ad93dd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-0c056abff82b9891.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-242d668e4fbf72c4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-847c3de6ca8a4416.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-81dd0d8ae923ed72.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-74e69ba2c8e970d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-70038f41ae7cf931.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/5619d59e87eddf66.css +1 -0
- package/apps/pwa/out/_next/static/css/d0a48dc099ed86cf.css +1 -0
- package/apps/pwa/out/_next/static/{aBi6EyMtLsrl7J8eH4kF7 → rfkMm4movqkqMebBrCXiR}/_buildManifest.js +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +23 -23
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +22 -22
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +23 -23
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +23 -23
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +22 -22
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +23 -23
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +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/graphs/log-horizon/log-horizon-watch.yaml +25 -0
- package/graphs/log-horizon/recipes.yaml +3 -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/bench-report.ts +82 -3
- package/scripts/ci-typecheck-changed.ts +10 -1
- package/scripts/hq/hq-fence-wrapper.sh +148 -0
- package/scripts/lib/typecheck-ratchet.ts +51 -0
- package/scripts/openai-relay-server.ts +5 -1
- package/scripts/release-loop/docs-node.ts +258 -43
- package/scripts/release-loop/gate-method.ts +59 -0
- package/scripts/release-loop/gate-node.ts +73 -7
- 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 +34 -4
- package/scripts/release-loop/resume.ts +53 -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 +8 -1
- package/scripts/rhythm/daily.ts +51 -2
- package/scripts/rhythm/weekly.ts +3 -2
- package/scripts/tsc-main-safety-net.ts +189 -0
- package/scripts/usage-rollup.ts +8 -4
- package/src/acp/codex-plugins.ts +48 -6
- package/src/agent/monad-agent-turn.ts +67 -5
- package/src/agent/run-context.ts +10 -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 +1 -1
- package/src/agent-mission/pr-review-watch.ts +6 -2
- package/src/agent-mission/review-loop.ts +33 -5
- package/src/agent-substrate/acp-reviewer.ts +17 -8
- package/src/agent-substrate/pr-reviewer.ts +164 -28
- package/src/agent-substrate/review-intent.ts +6 -4
- package/src/agent-substrate/self-review-cli.ts +111 -16
- 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/pr-manager.ts +12 -3
- 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/chat/index.ts +12 -3
- package/src/cli/agent-cli.ts +20 -0
- package/src/cli/brief-cli.ts +27 -21
- package/src/cli/cli-user-error.ts +9 -2
- package/src/cli/log-event-names.ts +3 -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 +30 -2
- package/src/cli/pr-land-overlap-evidence.ts +131 -0
- package/src/cli/release-cli.ts +53 -11
- package/src/cli/self-update.ts +173 -10
- package/src/cli/skills-cli.ts +12 -3
- package/src/cli/tasks-cli.ts +133 -27
- 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 +86 -2
- package/src/dashboard/chat-fold.ts +20 -0
- package/src/dashboard/first-screen-band.ts +151 -1
- package/src/dashboard/index.ts +27 -5
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +5 -3
- package/src/dashboard/stale-install-check.ts +13 -2
- package/src/dashboard/turn-stream-formatter.ts +25 -0
- package/src/debug/log-diet.ts +159 -0
- package/src/debug/log.ts +52 -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 +119 -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/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 +1 -0
- package/src/graph-runner/runner.ts +54 -5
- package/src/harness/dispatch-backpressure.ts +259 -0
- package/src/harness/harness-cli-command.ts +107 -28
- package/src/harness/harness-seams.ts +2 -20
- package/src/harness/harness-substrate-default.ts +72 -1
- package/src/harness/nested-elanous-policy.ts +4 -2
- package/src/hq/fence-wrapper.ts +7 -0
- package/src/hq/hq.ts +33 -2
- package/src/hq/lease.ts +13 -0
- package/src/hq/seats.ts +114 -18
- package/src/index.ts +163 -64
- 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/intelligence-map/model-alias.ts +6 -0
- package/src/intelligence-map/model-catalog.ts +41 -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-provider.ts +175 -0
- package/src/llm/bedrock.ts +345 -0
- package/src/llm/provider-credentials.ts +3 -1
- package/src/llm.ts +306 -35
- package/src/loops/budget.ts +150 -0
- 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/mcp/mcp-oauth.ts +136 -17
- package/src/model-tier/llm-tier-map.ts +48 -4
- package/src/models/prompts.ts +22 -1
- package/src/mss/logging/horizon-watch.ts +168 -0
- package/src/mss/logging/log-store.ts +204 -15
- 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/outbound-report.ts +8 -3
- package/src/nexus/api/telegram-trigger-bot.ts +6 -3
- package/src/nexus/index.ts +10 -0
- package/src/nexus/outbound/format.ts +2 -0
- package/src/nexus/outbound/router.ts +198 -13
- package/src/oauth/codex-account-rotation.ts +16 -18
- package/src/oauth/codex-account-store.ts +134 -5
- package/src/oauth/fallback-chain.ts +28 -6
- package/src/onboarding.ts +2 -1
- 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 +10 -6
- package/src/prompt-cache/openrouter.ts +94 -0
- package/src/prompt-enhance/enhance.ts +46 -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 +91 -22
- 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/retro/daily.ts +23 -9
- package/src/seat-loop/seat-loop.ts +34 -1
- 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 +67 -3
- package/src/self-dev/dev-pipeline.ts +4 -6
- package/src/self-dev/draft-residue.ts +463 -0
- package/src/self-dev/draft-sweep.ts +151 -10
- package/src/self-dev/draft-triage-rules.ts +27 -1
- 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 +57 -2
- package/src/self-dev/orchestrate-summary-line.ts +13 -0
- package/src/self-dev/orchestrate.ts +8 -135
- 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 +3 -22
- package/src/self-dev/run-types.ts +178 -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 +2 -4
- package/src/self-implement/blocked-draft-policy.ts +3 -19
- package/src/self-implement/draft-on-stop-mode.ts +18 -0
- package/src/self-implement/follow-up-goals.ts +114 -38
- 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.ts +24 -4
- package/src/self-implement/goal-digest.ts +19 -0
- package/src/self-implement/graph-overlay-condition.ts +4 -0
- package/src/self-implement/headless-elanous-driver.ts +11 -9
- 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 +133 -16
- 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/seams.ts +36 -3
- package/src/self-implement/self-implement-cli.ts +3 -1
- 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/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 +20 -9
- package/src/steward/triage.ts +4 -2
- package/src/storage/file-lock.ts +8 -1
- package/src/task-agent/actions.ts +4 -0
- package/src/task-agent/card-evidence.ts +59 -13
- package/src/task-agent/judge.ts +4 -3
- package/src/task-agent/land-worktree.ts +201 -0
- package/src/task-agent/live-move-history.ts +133 -0
- package/src/task-agent/live-moves.ts +106 -19
- package/src/task-agent/parent-host.ts +117 -8
- package/src/task-agent/rejudge-on-head.ts +297 -0
- package/src/task-agent/review-result-capture.ts +218 -0
- package/src/task-agent/shadow.ts +103 -7
- package/src/task-agent/task-hand.ts +136 -11
- package/src/task-orchestrator/surfaces/pod-child-providers.ts +29 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +6 -3
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +16 -2
- package/src/task-orchestrator/surfaces/pod-image-source.ts +212 -0
- package/src/task-orchestrator/surfaces/pod-lease.ts +93 -12
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +25 -2
- package/src/task-orchestrator/surfaces/pod-pool.ts +66 -14
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +259 -36
- package/src/task-orchestrator/surfaces/self-implement.ts +6 -2
- 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/user-config.ts +132 -24
- 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/scheduler.ts +1 -0
- 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 → rfkMm4movqkqMebBrCXiR}/_ssgManifest.js +0 -0
|
@@ -7,8 +7,9 @@ import { CliUserError } from '../cli/cli-user-error.js';
|
|
|
7
7
|
|
|
8
8
|
export type ChecklistStatus = 'green' | 'yellow' | 'red' | 'done';
|
|
9
9
|
export type ChecklistDisposition = 'move' | 'known-issue' | 'block';
|
|
10
|
-
/** `screen` = a five-surface feature cell — its evidence carries a `짝:` line (MANUAL-five-surface-parity §A). */
|
|
11
|
-
export type ChecklistKind = 'screen';
|
|
10
|
+
/** `screen` = a five-surface feature cell — its evidence carries a `짝:` line (MANUAL-five-surface-parity §A). `postpub` = measured after publication, outside the pre-publication gate. */
|
|
11
|
+
export type ChecklistKind = 'screen' | 'postpub';
|
|
12
|
+
const CHECKLIST_KINDS: readonly ChecklistKind[] = ['screen', 'postpub'];
|
|
12
13
|
export interface ChecklistItem {
|
|
13
14
|
id: string;
|
|
14
15
|
title: string;
|
|
@@ -227,7 +228,7 @@ export function validateCeoLoad(input: { ceoMinutes?: number; ceoDate?: string }
|
|
|
227
228
|
export function addItem(v: string, input: { id: string; title: string; refs?: string[]; owner?: string; kind?: ChecklistKind; priority?: ChecklistItem['priority']; predecessors?: string[]; deadlineVersion?: string; ceoMinutes?: number; ceoDate?: string; accelerator?: boolean }, options: { allowDuplicateId?: boolean } = {}): Checklist {
|
|
228
229
|
if (!input.id.trim()) throw new CliUserError('칸 id 가 비었다');
|
|
229
230
|
if (!input.title.trim()) throw new CliUserError('칸 제목이 비었다');
|
|
230
|
-
if (input.kind !== undefined && input.kind
|
|
231
|
+
if (input.kind !== undefined && !CHECKLIST_KINDS.includes(input.kind)) throw new CliUserError(`잘못된 종류: ${input.kind}`, 'screen|postpub');
|
|
231
232
|
if (input.owner !== undefined) parseOwner(input.owner);
|
|
232
233
|
if (input.priority !== undefined && !['P0', 'P1', 'P2'].includes(input.priority)) throw new CliUserError(`잘못된 우선순위: ${input.priority}`);
|
|
233
234
|
if (input.predecessors !== undefined && (!Array.isArray(input.predecessors) || input.predecessors.some((p) => typeof p !== 'string' || !p.trim() || p === input.id))) throw new CliUserError('잘못된 선행 칸');
|
|
@@ -258,7 +259,7 @@ export function setItem(v: string, id: string, patch: { status?: ChecklistStatus
|
|
|
258
259
|
if (!item) throw new CliUserError(`없는 칸: ${id}`, 'list 로 칸 목록을 본다');
|
|
259
260
|
if (patch.status !== undefined && !['green', 'yellow', 'red', 'done'].includes(patch.status)) throw new CliUserError(`잘못된 상태: ${patch.status}`);
|
|
260
261
|
if (patch.disposition !== undefined && !['move', 'known-issue', 'block'].includes(patch.disposition)) throw new CliUserError(`잘못된 처분: ${patch.disposition}`);
|
|
261
|
-
if (patch.kind !== undefined && patch.kind
|
|
262
|
+
if (patch.kind !== undefined && !CHECKLIST_KINDS.includes(patch.kind)) throw new CliUserError(`잘못된 종류: ${patch.kind}`, 'screen|postpub');
|
|
262
263
|
if (patch.priority !== undefined && !['P0', 'P1', 'P2'].includes(patch.priority)) throw new CliUserError(`잘못된 우선순위: ${patch.priority}`);
|
|
263
264
|
if (patch.predecessors !== undefined && (!Array.isArray(patch.predecessors) || patch.predecessors.some((p) => typeof p !== 'string' || !p.trim() || p === id))) throw new CliUserError('잘못된 선행 칸');
|
|
264
265
|
if (patch.deadlineVersion !== undefined) store.validateVersion(patch.deadlineVersion);
|
|
@@ -357,6 +358,8 @@ export interface ChecklistGate {
|
|
|
357
358
|
blocked: string[];
|
|
358
359
|
moved: string[];
|
|
359
360
|
knownIssues: Array<{ id: string; title: string; evidence: string }>;
|
|
361
|
+
/** Post-publication cells excluded from the pre-publication judgement. */
|
|
362
|
+
postpub?: string[];
|
|
360
363
|
/** Screen cells whose `짝:` line is missing or has an untracked ⏳ — a warning; it never changes `ok`. */
|
|
361
364
|
parity?: Array<{ id: string; why: string }>;
|
|
362
365
|
}
|
|
@@ -374,8 +377,9 @@ export function parityGap(evidence: string | undefined): string | null {
|
|
|
374
377
|
}
|
|
375
378
|
|
|
376
379
|
export function checklistGate(v: string): ChecklistGate {
|
|
377
|
-
const result: ChecklistGate = { ok: true, red: [], undecided: [], blocked: [], moved: [], knownIssues: [], parity: [] };
|
|
380
|
+
const result: ChecklistGate = { ok: true, red: [], undecided: [], blocked: [], moved: [], knownIssues: [], postpub: [], parity: [] };
|
|
378
381
|
for (const item of listChecklist(v).items) {
|
|
382
|
+
if (item.kind === 'postpub') { result.postpub!.push(item.id); continue; }
|
|
379
383
|
if (item.kind === 'screen') {
|
|
380
384
|
const why = parityGap(item.evidence);
|
|
381
385
|
if (why) result.parity!.push({ id: item.id, why });
|
|
@@ -388,16 +392,27 @@ export function checklistGate(v: string): ChecklistGate {
|
|
|
388
392
|
else result.undecided.push(item.id);
|
|
389
393
|
}
|
|
390
394
|
result.ok = result.red.length === 0 && result.undecided.length === 0 && result.blocked.length === 0;
|
|
395
|
+
if (result.postpub!.length) debug.log('release-loop.checklist', 'gate-postpub-excluded', { version: v, ids: result.postpub });
|
|
391
396
|
return result;
|
|
392
397
|
}
|
|
393
398
|
|
|
394
399
|
/** GATE-ENTRY-ALIGN: the cut-time judgement shared by `release run`'s entry check and the checklist-gate node —
|
|
395
|
-
* past the landing deadline a non-P0 yellow without a disposition is carried (moved)
|
|
400
|
+
* past the landing deadline a non-P0 yellow without a disposition is carried (moved); P0 yellow blocks unless OP recorded known-issue. */
|
|
396
401
|
export function cutChecklistGate(v: string, landBy: string | null | undefined, now: Date = new Date()): ChecklistGate & { autoMoved: string[] } {
|
|
397
|
-
const items = listChecklist(v)
|
|
402
|
+
const { items, history } = listChecklist(v);
|
|
398
403
|
const overdue = landBy !== null && landBy !== undefined && now.getTime() > Date.parse(landBy);
|
|
399
|
-
const autoMoved = overdue ? items.filter((item) => item.status === 'yellow' && item.priority !== 'P0' && item.disposition === undefined).map((item) => item.id) : [];
|
|
400
|
-
const p0 = items.filter((item) =>
|
|
404
|
+
const autoMoved = overdue ? items.filter((item) => item.kind !== 'postpub' && item.status === 'yellow' && item.priority !== 'P0' && item.disposition === undefined).map((item) => item.id) : [];
|
|
405
|
+
const p0 = items.filter((item) => {
|
|
406
|
+
if (item.kind === 'postpub' || item.status !== 'yellow' || item.priority !== 'P0') return false;
|
|
407
|
+
if (item.disposition === 'known-issue') {
|
|
408
|
+
const decision = [...history].reverse().find((entry) => entry.id === item.id && entry.field === 'disposition' && entry.to === 'known-issue');
|
|
409
|
+
if (decision?.by === 'OP') {
|
|
410
|
+
debug.log('release-loop.checklist', 'p0-known-issue-allowed', { version: v, id: item.id, by: decision.by, at: decision.at });
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return true;
|
|
415
|
+
}).map((item) => item.id);
|
|
401
416
|
const gate = checklistGate(v);
|
|
402
417
|
gate.undecided = gate.undecided.filter((id) => !autoMoved.includes(id) && !p0.includes(id));
|
|
403
418
|
gate.moved = gate.moved.filter((id) => !p0.includes(id)).concat(autoMoved);
|
|
@@ -3,6 +3,7 @@ import { createHash, randomUUID } from 'node:crypto';
|
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
4
|
import { Database } from 'bun:sqlite';
|
|
5
5
|
import { releaseLedgerRoot } from '../instance/resolve.js';
|
|
6
|
+
import { assertNotTestWritingOps } from '../instance/test-write-guard.js';
|
|
6
7
|
import { CliUserError } from '../cli/cli-user-error.js';
|
|
7
8
|
import { debug } from '../debug/log.js';
|
|
8
9
|
import { getUserConfig } from '../user-config.js';
|
|
@@ -46,6 +47,7 @@ function schemaCurrent(db: Database): boolean {
|
|
|
46
47
|
}
|
|
47
48
|
function open(root = releaseLedgerRoot()): Database {
|
|
48
49
|
const path = join(root, 'release', 'features.sqlite');
|
|
50
|
+
assertNotTestWritingOps(path, 'open the checklist ledger');
|
|
49
51
|
mkdirSync(dirname(path), { recursive: true });
|
|
50
52
|
const db = new Database(path, { create: true, strict: true });
|
|
51
53
|
try {
|
|
@@ -546,6 +548,7 @@ export function details(id: string): { id: string; title: string; owner: string
|
|
|
546
548
|
export function exportJson(version: string, released = releasedVersion(), dev = devVersion()): Checklist {
|
|
547
549
|
const data = list(version, released, dev);
|
|
548
550
|
const path = jsonPath(version);
|
|
551
|
+
assertNotTestWritingOps(path, 'export the checklist ledger');
|
|
549
552
|
mkdirSync(dirname(path), { recursive: true });
|
|
550
553
|
const temp = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
551
554
|
const contents = `${JSON.stringify(data, null, 2)}\n`;
|
|
@@ -23,11 +23,19 @@ export interface GateShard {
|
|
|
23
23
|
plannedMin?: number;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/** 게이트 노드의 판정 결과 — 끝난 조각의 rc≠0 을 «새로 생긴 실패»와 «기존 실패»로 가른 수. */
|
|
27
|
+
export interface GateShardsVerdict {
|
|
28
|
+
introduced: number;
|
|
29
|
+
preexisting: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
26
32
|
export interface GateShardsFile {
|
|
27
33
|
v: 1;
|
|
28
34
|
version: string;
|
|
29
35
|
updatedAt: string;
|
|
30
36
|
shards: GateShard[];
|
|
37
|
+
/** 판정이 나기 전에는 없다. 옛 파일(필드 없음)도 그대로 읽는다 — `v` 를 올리지 않는다. */
|
|
38
|
+
verdict?: GateShardsVerdict;
|
|
31
39
|
}
|
|
32
40
|
|
|
33
41
|
export function gateShardsPath(version: string, root: string = releaseLedgerRoot()): string {
|
|
@@ -58,7 +66,15 @@ function shardOf(value: unknown): GateShard | null {
|
|
|
58
66
|
};
|
|
59
67
|
}
|
|
60
68
|
|
|
61
|
-
|
|
69
|
+
function verdictOf(value: unknown): GateShardsVerdict | undefined {
|
|
70
|
+
if (!value || typeof value !== 'object') return undefined;
|
|
71
|
+
const raw = value as Record<string, unknown>;
|
|
72
|
+
const introduced = num(raw.introduced);
|
|
73
|
+
const preexisting = num(raw.preexisting);
|
|
74
|
+
return introduced !== undefined && preexisting !== undefined ? { introduced, preexisting } : undefined;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** 파일이 없거나 깨졌으면 null. 알 수 없는 상태값의 조각은 버리되 파일 전체는 살린다(깨진 verdict 도 필드만 버린다). */
|
|
62
78
|
export function readGateShards(path: string): GateShardsFile | null {
|
|
63
79
|
let parsed: unknown;
|
|
64
80
|
try { parsed = JSON.parse(readFileSync(path, 'utf8')); } catch { return null; }
|
|
@@ -68,7 +84,11 @@ export function readGateShards(path: string): GateShardsFile | null {
|
|
|
68
84
|
const version = str(raw.version, 40);
|
|
69
85
|
const updatedAt = str(raw.updatedAt, 40);
|
|
70
86
|
if (!version || !updatedAt) return null;
|
|
71
|
-
|
|
87
|
+
const verdict = verdictOf(raw.verdict);
|
|
88
|
+
return {
|
|
89
|
+
v: 1, version, updatedAt, shards: raw.shards.map(shardOf).filter((s): s is GateShard => s !== null),
|
|
90
|
+
...(verdict ? { verdict } : {}),
|
|
91
|
+
};
|
|
72
92
|
}
|
|
73
93
|
|
|
74
94
|
/** tmp → rename — 읽는 쪽이 반쯤 쓴 파일을 보지 않게. */
|
|
@@ -79,6 +99,17 @@ export function writeGateShards(path: string, file: GateShardsFile): void {
|
|
|
79
99
|
renameSync(tmp, path);
|
|
80
100
|
}
|
|
81
101
|
|
|
102
|
+
/**
|
|
103
|
+
* GATE-LIVE-OBS-RC-WORDING ⓒ — the gate node writes its verdict into the board it already keeps, after judging.
|
|
104
|
+
* No board (a local, non-Pod gate) ⇒ false and nothing is written: a verdict never creates a board out of thin air.
|
|
105
|
+
*/
|
|
106
|
+
export function recordGateShardsVerdict(path: string, verdict: GateShardsVerdict, now: Date = new Date()): boolean {
|
|
107
|
+
const file = readGateShards(path);
|
|
108
|
+
if (!file) return false;
|
|
109
|
+
writeGateShards(path, { ...file, updatedAt: now.toISOString(), verdict });
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
|
|
82
113
|
export interface GateShardsSummary {
|
|
83
114
|
total: number;
|
|
84
115
|
counts: Record<ShardState, number>;
|
|
@@ -89,6 +120,9 @@ export interface GateShardsSummary {
|
|
|
89
120
|
/** 끝났지만 rc≠0 인 조각 수 — 쓰는 쪽의 `done` 은 «조각 끝»일 뿐 «통과»가 아니다(판정은 게이트 노드가 기존/새로 가른다).
|
|
90
121
|
* 0.2.20 컷 실측: 끝난 넷이 모두 rc 1 인데 화면이 «통과»로 읽었다. */
|
|
91
122
|
doneWithFailures: number;
|
|
123
|
+
/** 게이트 노드의 판정(기존/새로). null = «판정 전» — 「새 실패 0」과 다른 값이다.
|
|
124
|
+
* `summarizeShards` 는 늘 채운다(선택 필드인 것은 이 요약을 손으로 만드는 픽스처·읽는 쪽을 깨지 않으려는 것). */
|
|
125
|
+
verdict?: GateShardsVerdict | null;
|
|
92
126
|
/** 도는 조각 중 계획 분을 가장 많이 넘긴 분(없으면 0). 넘겼으면 etaMin 은 «하한»일 뿐이라 화면이 그렇게 말한다
|
|
93
127
|
* — canary 10-07 실측: 계획 0.1분 · 실제 22분 → 넘김을 안 보이면 내내 «남은 약 0분». */
|
|
94
128
|
overrunMin: number;
|
|
@@ -134,6 +168,7 @@ export function summarizeShards(file: GateShardsFile, now: number = Date.now()):
|
|
|
134
168
|
counts,
|
|
135
169
|
waitReasons: [...reasons].map(([reason, count]) => ({ reason, count })).sort((a, b) => b.count - a.count),
|
|
136
170
|
doneWithFailures: file.shards.filter((s) => s.state === 'done' && s.rc !== undefined && s.rc !== 0).length,
|
|
171
|
+
verdict: file.verdict ?? null,
|
|
137
172
|
etaMin: estimate(file.shards, now),
|
|
138
173
|
overrunMin: Math.floor(overrun),
|
|
139
174
|
staleMin: stale === null ? null : Math.floor(stale),
|
|
@@ -144,7 +179,16 @@ export const SHARD_WORD: Record<ShardState, string> = {
|
|
|
144
179
|
pending: '대기', running: '돌기', done: '끝', retry: '재시도', timeout: '잘림', failed: '실패',
|
|
145
180
|
};
|
|
146
181
|
|
|
147
|
-
/**
|
|
182
|
+
/**
|
|
183
|
+
* `끝` 꼬리. 기존 실패가 있는 판에선 조각 전부가 rc 1 로 끝난다(0.2.21 컷: 24/24) — 그래서 판정 전엔 rc≠0 을 «실패»라 부르지 않고,
|
|
184
|
+
* 판정 뒤엔 새로 생긴 실패만 싣는다(기존 실패 수는 줄에 안 싣는다).
|
|
185
|
+
*/
|
|
186
|
+
function doneTail(summary: GateShardsSummary): string {
|
|
187
|
+
if (summary.verdict) return summary.verdict.introduced > 0 ? `(새 실패 ${summary.verdict.introduced})` : '';
|
|
188
|
+
return summary.doneWithFailures ? `(rc≠0 ${summary.doneWithFailures} · 기존 실패 포함 · 판정 전)` : '';
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** 외출 알림·/release 한 줄 — «조각 24 · 돌기 5 · 대기 8(CPU 부족 8) · 잘림 2 · 끝 9(rc≠0 3 · 기존 실패 포함 · 판정 전) · 남은 약 40분». */
|
|
148
192
|
export function shardsLine(summary: GateShardsSummary): string {
|
|
149
193
|
const order: ShardState[] = ['running', 'pending', 'retry', 'timeout', 'failed', 'done'];
|
|
150
194
|
const parts = [`조각 ${summary.total}`];
|
|
@@ -153,7 +197,7 @@ export function shardsLine(summary: GateShardsSummary): string {
|
|
|
153
197
|
if (!n) continue;
|
|
154
198
|
const why = state === 'pending' && summary.waitReasons.length
|
|
155
199
|
? `(${summary.waitReasons.slice(0, 2).map((w) => `${w.reason} ${w.count}`).join(' · ')})` : '';
|
|
156
|
-
const reported = state === 'done'
|
|
200
|
+
const reported = state === 'done' ? doneTail(summary) : '';
|
|
157
201
|
parts.push(`${SHARD_WORD[state]} ${n}${why}${reported}`);
|
|
158
202
|
}
|
|
159
203
|
if (summary.overrunMin >= 1) parts.push(`계획보다 ${summary.overrunMin}분 넘게 도는 중 — 남은 시간 추정 불가`);
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { appendFileSync, mkdirSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { debug } from '../debug/log.js';
|
|
5
|
+
import { releaseLedgerRoot } from '../instance/resolve.js';
|
|
6
|
+
|
|
7
|
+
export interface JudgeSignal { condition: string | null; observe: string | null; expect: string | null }
|
|
8
|
+
export type ClassifiedSignal = { kind: 'measurable'; argv: string[] } | { kind: 'unmeasurable'; reason: string };
|
|
9
|
+
export type GreenVerdict = 'proposed' | 'unmeasurable' | 'not-passed';
|
|
10
|
+
export interface LandedCell { version: string; id: string; title: string; pr: number }
|
|
11
|
+
export interface LandedGreenDeps {
|
|
12
|
+
cwd?: string;
|
|
13
|
+
ledgerRoot?: string;
|
|
14
|
+
run?: (argv: string[], cwd: string) => number;
|
|
15
|
+
log?: typeof debug.log;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Only a field label after a semicolon starts a new field; unknown tails stay attached to the observation. */
|
|
19
|
+
export function parseJudgeSignals(text: string): JudgeSignal[] {
|
|
20
|
+
return text.split(/\r?\n/).flatMap((line) => {
|
|
21
|
+
const match = /^\s*판정 신호:\s*(.*)$/.exec(line);
|
|
22
|
+
if (!match) return [];
|
|
23
|
+
const signal: JudgeSignal = { condition: null, observe: null, expect: null };
|
|
24
|
+
const body = match[1]!;
|
|
25
|
+
const fields = [...body.matchAll(/(?:^|;)\s*(조건|관측|기대)\s*=/g)];
|
|
26
|
+
for (let i = 0; i < fields.length; i++) {
|
|
27
|
+
const field = fields[i]!;
|
|
28
|
+
const value = body.slice(field.index! + field[0].length, fields[i + 1]?.index ?? body.length).trim();
|
|
29
|
+
const key = ({ 조건: 'condition', 관측: 'observe', 기대: 'expect' } as const)[field[1] as '조건' | '관측' | '기대'];
|
|
30
|
+
signal[key] = value || null;
|
|
31
|
+
}
|
|
32
|
+
return [signal];
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function classifySignal(signal: JudgeSignal): ClassifiedSignal {
|
|
37
|
+
const observe = signal.observe?.trim();
|
|
38
|
+
if (!observe || /[;|&$`<>]/.test(observe)) return { kind: 'unmeasurable', reason: 'missing observation or shell metacharacter' };
|
|
39
|
+
const argv = observe.split(/\s+/);
|
|
40
|
+
if (argv.length < 3 || argv[0] !== 'bun' || argv[1] !== 'test' || argv.slice(2).some((path) => !path.endsWith('.test.ts') || path.startsWith('-'))) {
|
|
41
|
+
return { kind: 'unmeasurable', reason: 'observation is not bun test with .test.ts paths' };
|
|
42
|
+
}
|
|
43
|
+
return { kind: 'measurable', argv };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function runTest(argv: string[], cwd: string): number {
|
|
47
|
+
const result = spawnSync(argv[0]!, argv.slice(1), { cwd, shell: false, timeout: 120_000, stdio: 'ignore' });
|
|
48
|
+
if (result.error) throw result.error;
|
|
49
|
+
return result.status ?? 1;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Proposals are evidence, never a checklist status transition. All I/O errors are observed and swallowed. */
|
|
53
|
+
export function judgeLandedCell({ version, id, title, pr }: LandedCell, deps: LandedGreenDeps = {}): GreenVerdict | undefined {
|
|
54
|
+
const log = deps.log ?? debug.log.bind(debug);
|
|
55
|
+
try {
|
|
56
|
+
const signals = parseJudgeSignals(title);
|
|
57
|
+
const classified = signals.map(classifySignal);
|
|
58
|
+
let verdict: GreenVerdict;
|
|
59
|
+
let reason: string | undefined;
|
|
60
|
+
if (!signals.length || classified.some((signal) => signal.kind === 'unmeasurable')) {
|
|
61
|
+
verdict = 'unmeasurable';
|
|
62
|
+
reason = !signals.length ? 'no judge signals' : classified.find((signal) => signal.kind === 'unmeasurable')!.reason;
|
|
63
|
+
} else {
|
|
64
|
+
verdict = 'proposed';
|
|
65
|
+
for (const signal of classified) {
|
|
66
|
+
if (signal.kind !== 'measurable' || (deps.run ?? runTest)(signal.argv, deps.cwd ?? process.cwd()) !== 0) {
|
|
67
|
+
verdict = 'not-passed';
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const dir = join(deps.ledgerRoot ?? releaseLedgerRoot(), 'release', version);
|
|
73
|
+
mkdirSync(dir, { recursive: true });
|
|
74
|
+
appendFileSync(join(dir, 'green-proposals.jsonl'), JSON.stringify({ at: new Date().toISOString(), version, id, pr, verdict, signals, ...(reason ? { reason } : {}) }) + '\n');
|
|
75
|
+
log('release.checklist', `landed-green-${verdict}`, { version, id, pr, verdict, signals, ...(reason ? { reason } : {}) });
|
|
76
|
+
return verdict;
|
|
77
|
+
} catch (error) {
|
|
78
|
+
try { log('release.checklist', 'landed-green-error', { version, id, pr, error: String(error) }); } catch { /* Logging must not break the merge path. */ }
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -42,6 +42,9 @@ export interface LightRcResult {
|
|
|
42
42
|
remote: string | null;
|
|
43
43
|
gateExitCode: number;
|
|
44
44
|
ok: boolean;
|
|
45
|
+
/** LIGHT-RC-MEASURE ③ — why the gate produced no measurement (e.g. `shard-2`); null = it measured. Carried in the
|
|
46
|
+
* JSON so the reason reaches the caller even when the remote child's human lines (stderr) do not. */
|
|
47
|
+
unmeasured: string | null;
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
export interface LightRcDeps {
|
|
@@ -57,6 +60,8 @@ export interface LightRcDeps {
|
|
|
57
60
|
|
|
58
61
|
/** Shown when the gate ran remotely but the host name did not come back. */
|
|
59
62
|
export const LIGHT_RC_UNKNOWN_HOST = '(remote · host unknown)';
|
|
63
|
+
/** No measurement came back and the gate named no reason (an older gate, or a result without the field). */
|
|
64
|
+
export const LIGHT_RC_REASON_NOT_REPORTED = 'reason not reported';
|
|
60
65
|
|
|
61
66
|
const defaultGit = (args: string[], cwd: string) => {
|
|
62
67
|
const run = spawnSync('git', args, { cwd, encoding: 'utf8' });
|
|
@@ -142,6 +147,7 @@ export async function runLightRc(version: string, opts: LightRcOptions = {}, dep
|
|
|
142
147
|
alwaysInclude: alwaysInclude.length,
|
|
143
148
|
alwaysIncludeMissing: [...local.alwaysIncludeMissing],
|
|
144
149
|
shards, remote: null, gateExitCode: local.exitCode, ok: false,
|
|
150
|
+
unmeasured: local.unmeasuredReason ?? (local.baseline ? null : LIGHT_RC_REASON_NOT_REPORTED),
|
|
145
151
|
};
|
|
146
152
|
} else {
|
|
147
153
|
const remote = parseRemoteResult(Buffer.concat(captured).toString('utf8'));
|
|
@@ -156,12 +162,15 @@ export async function runLightRc(version: string, opts: LightRcOptions = {}, dep
|
|
|
156
162
|
alwaysIncludeMissing: Array.isArray(remote.alwaysIncludeMissing) ? remote.alwaysIncludeMissing.map(String) : [],
|
|
157
163
|
shards, remote: typeof host === 'string' && host ? host : opts.remote ?? LIGHT_RC_UNKNOWN_HOST,
|
|
158
164
|
gateExitCode: typeof remote.gateExitCode === 'number' ? remote.gateExitCode : rc, ok: false,
|
|
165
|
+
unmeasured: typeof remote.unmeasured === 'string' && remote.unmeasured
|
|
166
|
+
? remote.unmeasured
|
|
167
|
+
: typeof remote.introduced === 'number' ? null : LIGHT_RC_REASON_NOT_REPORTED,
|
|
159
168
|
};
|
|
160
169
|
}
|
|
161
170
|
// Unmeasured (null) or unclassified failures are never green; policy-gate findings in the gate output are reported
|
|
162
171
|
// (gate rc), not a regression.
|
|
163
172
|
result.ok = result.introduced === 0 && result.unclassified === 0;
|
|
164
|
-
debug.log('release.light-rc', 'done', { version, base, introduced: result.introduced, preexisting: result.preexisting, unclassified: result.unclassified, shards, remote: result.remote });
|
|
173
|
+
debug.log('release.light-rc', 'done', { version, base, introduced: result.introduced, preexisting: result.preexisting, unclassified: result.unclassified, shards, remote: result.remote, unmeasured: result.unmeasured });
|
|
165
174
|
return result;
|
|
166
175
|
}
|
|
167
176
|
|
|
@@ -169,5 +178,6 @@ export function formatLightRc(result: LightRcResult): string[] {
|
|
|
169
178
|
return [
|
|
170
179
|
`${result.ok ? '✅' : '⛔'} light-rc ${result.version} · base ${result.base.slice(0, 12)} · ${result.remote ? `host ${result.remote}` : 'local'} · shards ${result.shards}`,
|
|
171
180
|
` introduced ${result.introduced ?? '못 쟀다'} · preexisting ${result.preexisting ?? '못 쟀다'} · unclassified ${result.unclassified ?? '못 쟀다'} · always-include ${result.alwaysInclude}${result.alwaysIncludeMissing.length ? ` (missing: ${result.alwaysIncludeMissing.join(', ')})` : ''} · gate rc ${result.gateExitCode}`,
|
|
181
|
+
...(result.unmeasured !== null ? [` shards: unmeasured (${result.unmeasured})`] : []),
|
|
172
182
|
];
|
|
173
183
|
}
|
|
@@ -4,6 +4,7 @@ import { join } from 'node:path';
|
|
|
4
4
|
import { releaseLedgerRoot } from '../instance/resolve.js';
|
|
5
5
|
import { debug } from '../debug/log.js';
|
|
6
6
|
import { listChecklist, setItem } from './checklist.js';
|
|
7
|
+
import { judgeLandedCell } from './landed-to-green.js';
|
|
7
8
|
import { evidencePlan, landedButYellow, type MergedChecklistPr } from './landed-but-yellow.js';
|
|
8
9
|
import { assignedVersions, evidenceAdd } from './feature-store.js';
|
|
9
10
|
|
|
@@ -13,6 +14,7 @@ export interface MergedPrChecklistDeps {
|
|
|
13
14
|
checklist?: typeof listChecklist;
|
|
14
15
|
addEvidence?: typeof evidenceAdd;
|
|
15
16
|
setStatus?: typeof setItem;
|
|
17
|
+
judgeLanded?: typeof judgeLandedCell;
|
|
16
18
|
log?: typeof debug.log;
|
|
17
19
|
}
|
|
18
20
|
|
|
@@ -58,6 +60,14 @@ export function syncMergedPrChecklist(number: number, cwd: string, goalFile?: st
|
|
|
58
60
|
(deps.addEvidence ?? evidenceAdd)(id, version, ref, 'harness', snapshot.released, snapshot.dev);
|
|
59
61
|
log('release.checklist', 'merged-pr-evidence-added', { version, id, pr: number, ref });
|
|
60
62
|
}
|
|
63
|
+
const cell = snapshot.items.find((item) => item.id === row.id)!;
|
|
64
|
+
if (cell.status !== 'green' && cell.status !== 'done') {
|
|
65
|
+
try {
|
|
66
|
+
(deps.judgeLanded ?? judgeLandedCell)({ version, id: row.id, title: cell.title, pr: number }, { cwd });
|
|
67
|
+
} catch (error) {
|
|
68
|
+
try { log('release.checklist', 'landed-green-error', { version, id: row.id, pr: number, error: String(error) }); } catch { /* Judgment remains fail-soft. */ }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
61
71
|
if (goal && declaresCompletion(goal)) {
|
|
62
72
|
if (!goalCells(goal).has(row.id)) {
|
|
63
73
|
log('release.checklist', 'merged-pr-status-skipped', { version, id: row.id, pr: number, reason: 'goal-does-not-name-cell' });
|
|
@@ -7,12 +7,13 @@ import { getElanousConfigDirOverride } from '../elanous-config-dir.js';
|
|
|
7
7
|
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
8
8
|
import { userConfigPath } from '../user-config.js';
|
|
9
9
|
import { addItem, listChecklist, ownerMatches, parseOwner, setItem, validateCeoLoad, type ChecklistItem } from './checklist.js';
|
|
10
|
-
import { move, releasedVersion } from './feature-store.js';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
10
|
+
import { assignedVersions, move, releasedVersion } from './feature-store.js';
|
|
11
|
+
import { debug } from '../debug/log.js';
|
|
12
|
+
import { kstDay, listSchedules, type ReleaseSchedule } from './release-schedule.js';
|
|
13
|
+
import { parseRubric, rubricScore, type rubricGrade } from './rubric.js';
|
|
13
14
|
|
|
14
15
|
export type PlacementPriority = 'P0' | 'P1' | 'P2';
|
|
15
|
-
export interface PlacementCell { id: string; title: string; owner: string; priority: PlacementPriority; predecessors: string[]; deadlineVersion?: string; ceoMinutes?: number; ceoDate?: string; accelerator?: boolean }
|
|
16
|
+
export interface PlacementCell { id: string; title: string; owner: string; priority: PlacementPriority; grade?: ReturnType<typeof rubricGrade>; predecessors: string[]; deadlineVersion?: string; ceoMinutes?: number; ceoDate?: string; accelerator?: boolean }
|
|
16
17
|
export interface PlacementDecision { id: string; from: string | null; version: string; reason: string; displaced: Array<{ id: string; from: string; to: string; reason: string }> }
|
|
17
18
|
export interface RebalanceResult {
|
|
18
19
|
decisions: PlacementDecision[];
|
|
@@ -24,6 +25,7 @@ export interface PlacementDeps {
|
|
|
24
25
|
merged24h?: number;
|
|
25
26
|
seatCap?: Record<string, number>;
|
|
26
27
|
ceoDailyCap?: number;
|
|
28
|
+
ceoCalendar?: CeoCalendar;
|
|
27
29
|
checklist?: typeof listChecklist;
|
|
28
30
|
released?: string;
|
|
29
31
|
dryRun?: boolean;
|
|
@@ -85,10 +87,18 @@ export function mergedPrsLast24h(now = new Date()): number {
|
|
|
85
87
|
return rows.filter((row) => Date.parse(row.mergedAt) >= Date.parse(since) && Date.parse(row.mergedAt) <= now.getTime()).length;
|
|
86
88
|
}
|
|
87
89
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
export interface CeoCalendar {
|
|
91
|
+
/** KST inclusive term ranges. A term cap can tighten the normal daily budget. */
|
|
92
|
+
terms?: Array<{ from: string; to: string; dailyCap: number }>;
|
|
93
|
+
/** KST event days explicitly override the term/normal cap, including exceptions with extra time. */
|
|
94
|
+
events?: Record<string, number>;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function placementConfig(root?: string): { seatCap?: Record<string, number>; ceoDailyCap?: number; ceoCalendar?: CeoCalendar } {
|
|
98
|
+
const path = root !== undefined ? join(root, 'config.json')
|
|
99
|
+
: getElanousConfigDirOverride() ? join(effectiveInstanceRoot(), 'config.json') : userConfigPath();
|
|
90
100
|
if (!existsSync(path)) return {};
|
|
91
|
-
const raw = JSON.parse(readFileSync(path, 'utf8')) as { release?: { placement?: { seatCap?: Record<string, number>; ceoDailyCap?: number } } };
|
|
101
|
+
const raw = JSON.parse(readFileSync(path, 'utf8')) as { release?: { placement?: { seatCap?: Record<string, number>; ceoDailyCap?: number; ceoCalendar?: CeoCalendar } } };
|
|
92
102
|
return raw.release?.placement ?? {};
|
|
93
103
|
}
|
|
94
104
|
|
|
@@ -100,23 +110,69 @@ function validCeoCap(cap: number): number {
|
|
|
100
110
|
|
|
101
111
|
export function placementCeoDailyCap(): number { return validCeoCap(placementConfig().ceoDailyCap ?? 30); }
|
|
102
112
|
|
|
113
|
+
function validDay(day: string): boolean {
|
|
114
|
+
return typeof day === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(day) && Number.isFinite(Date.parse(`${day}T00:00:00Z`))
|
|
115
|
+
&& new Date(`${day}T00:00:00Z`).toISOString().slice(0, 10) === day;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function calendarCap(day: string, cap: number, calendar: CeoCalendar): number {
|
|
119
|
+
if (!calendar || (calendar.terms !== undefined && !Array.isArray(calendar.terms))
|
|
120
|
+
|| (calendar.events !== undefined && (!calendar.events || typeof calendar.events !== 'object' || Array.isArray(calendar.events))))
|
|
121
|
+
throw new CliUserError('잘못된 대표 달력: release.placement.ceoCalendar');
|
|
122
|
+
let effective = cap;
|
|
123
|
+
for (const term of calendar.terms ?? []) {
|
|
124
|
+
if (!term || typeof term !== 'object') throw new CliUserError('잘못된 대표 학기 달력: release.placement.ceoCalendar.terms');
|
|
125
|
+
if (!validDay(term.from) || !validDay(term.to) || term.from > term.to) throw new CliUserError('잘못된 대표 학기 달력 날짜: release.placement.ceoCalendar.terms');
|
|
126
|
+
validCeoCap(term.dailyCap);
|
|
127
|
+
if (term.from <= day && day <= term.to) effective = Math.min(effective, term.dailyCap);
|
|
128
|
+
}
|
|
129
|
+
for (const [date, eventCap] of Object.entries(calendar.events ?? {})) {
|
|
130
|
+
if (!validDay(date)) throw new CliUserError('잘못된 대표 행사 달력 날짜: release.placement.ceoCalendar.events');
|
|
131
|
+
validCeoCap(eventCap);
|
|
132
|
+
}
|
|
133
|
+
return calendar.events?.[day] ?? effective;
|
|
134
|
+
}
|
|
135
|
+
|
|
103
136
|
/** Explicit day wins; otherwise a cell uses the target release's landing day in KST. */
|
|
104
137
|
function ceoDay(item: { ceoDate?: string }, row: ReleaseSchedule): string {
|
|
105
|
-
return item.ceoDate ??
|
|
138
|
+
return item.ceoDate ?? kstDay(row.landBy!);
|
|
106
139
|
}
|
|
107
140
|
|
|
108
141
|
type CeoWork = Pick<ChecklistItem, 'id' | 'ceoMinutes' | 'ceoDate'>;
|
|
109
|
-
function ceoOverload(item: CeoWork, row: ReleaseSchedule, snapshots: Map<string, { items: CeoWork[] }>, schedules: ReleaseSchedule[], cap: number): string | null {
|
|
142
|
+
function ceoOverload(item: CeoWork, row: ReleaseSchedule, snapshots: Map<string, { items: CeoWork[] }>, schedules: ReleaseSchedule[], cap: number, calendar: CeoCalendar = {}): string | null {
|
|
110
143
|
if (!item.ceoMinutes) return null;
|
|
111
144
|
const day = ceoDay(item, row);
|
|
145
|
+
const limit = calendarCap(day, cap, calendar);
|
|
112
146
|
const peers = [...snapshots].flatMap(([version, snapshot]) => {
|
|
113
147
|
const schedule = schedules.find((candidate) => candidate.version === version);
|
|
114
148
|
return snapshot.items.filter((other) => other.id !== item.id && other.ceoMinutes
|
|
115
149
|
&& (other.ceoDate === day || (!other.ceoDate && schedule?.landBy && ceoDay(other, schedule) === day)));
|
|
116
150
|
});
|
|
117
151
|
const total = item.ceoMinutes + peers.reduce((sum, other) => sum + other.ceoMinutes!, 0);
|
|
118
|
-
if (total <=
|
|
119
|
-
|
|
152
|
+
if (total <= limit) return null;
|
|
153
|
+
const reason = `대표 손 과부하 ${day} KST: ${[...peers.map((peer) => `${peer.id} ${peer.ceoMinutes}분`), `${item.id} ${item.ceoMinutes}분`].join(' + ')} = ${total}분 > 하루 상한 ${limit}분 — 늦추기(다음 날짜/판) · 자리 대행(대표 분량 축소) · 묶기(촬영·승인 합산 분량 축소)를 제안`;
|
|
154
|
+
debug.log('release.placement', 'ceo-overload', { day, id: item.id, total, cap: limit, reason });
|
|
155
|
+
return reason;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Called by setSchedule before a landing-day change can overload an already placed cell. */
|
|
159
|
+
export function checkScheduleCeoLoad(version: string, landBy: string, root?: string): void {
|
|
160
|
+
const schedules = listSchedules(root);
|
|
161
|
+
const proposed = schedules.some((row) => row.version === version)
|
|
162
|
+
? schedules.map((row) => row.version === version ? { ...row, landBy } : row)
|
|
163
|
+
: [...schedules, { version, cutAt: landBy, landBy, updatedAt: '', updatedBy: '' }];
|
|
164
|
+
const target = proposed.find((row) => row.version === version)!;
|
|
165
|
+
const targetItems = listChecklist(version, root).items;
|
|
166
|
+
const changed = targetItems.filter((item) => item.ceoMinutes && !item.ceoDate);
|
|
167
|
+
if (!changed.length) return;
|
|
168
|
+
const versions = [...new Set([...proposed.map((row) => row.version), ...assignedVersions(root)])];
|
|
169
|
+
const snapshots = new Map(versions.map((v) => [v, { items: v === version ? targetItems : listChecklist(v, root).items }]));
|
|
170
|
+
const config = placementConfig(root);
|
|
171
|
+
const cap = validCeoCap(config.ceoDailyCap ?? 30);
|
|
172
|
+
for (const item of changed) {
|
|
173
|
+
const issue = ceoOverload(item, target, snapshots, proposed, cap, config.ceoCalendar);
|
|
174
|
+
if (issue) throw new CliUserError(issue);
|
|
175
|
+
}
|
|
120
176
|
}
|
|
121
177
|
|
|
122
178
|
function dependencyViolation(
|
|
@@ -161,6 +217,7 @@ export function placeCell(input: PlacementCell, deps: PlacementDeps = {}): Place
|
|
|
161
217
|
validateCeoLoad(input);
|
|
162
218
|
const seat = parseOwner(input.owner).seat;
|
|
163
219
|
if (!['P0', 'P1', 'P2'].includes(input.priority)) throw new CliUserError(`잘못된 우선순위: ${input.priority}`);
|
|
220
|
+
if (input.grade !== undefined && !['P1', 'P2', 'P3', 'P4'].includes(input.grade)) throw new CliUserError(`잘못된 루브릭 등급: ${input.grade}`);
|
|
164
221
|
if (input.accelerator !== undefined && input.accelerator !== true && input.accelerator !== false) throw new CliUserError('가속 등급은 true 이거나 생략한다');
|
|
165
222
|
const now = (deps.now ?? new Date()).getTime();
|
|
166
223
|
const schedules = [...(deps.schedules ?? listSchedules())].sort((a, b) => versionOrder(a.version, b.version));
|
|
@@ -171,6 +228,9 @@ export function placeCell(input: PlacementCell, deps: PlacementDeps = {}): Place
|
|
|
171
228
|
const snapshots = new Map(sources.map((version) => [version, (deps.checklist ?? listChecklist)(version)]));
|
|
172
229
|
const { locations, conflict } = cellLocations([...snapshots].map(([version, snapshot]) => [version, snapshot.items] as [string, readonly { id: string }[]]), released, backlog);
|
|
173
230
|
if (conflict) throw new CliUserError(`여러 판의 같은 칸: ${conflict}`);
|
|
231
|
+
if (input.ceoMinutes || [...snapshots.values()].some((snapshot) => snapshot.items.some((item) => item.id === input.id && item.ceoMinutes))) {
|
|
232
|
+
for (const version of assignedVersions()) if (!snapshots.has(version)) snapshots.set(version, (deps.checklist ?? listChecklist)(version));
|
|
233
|
+
}
|
|
174
234
|
// A backlog copy never outranks a scheduled cell with the same id.
|
|
175
235
|
for (const version of backlog) for (const item of snapshots.get(version)?.items ?? []) if (!locations.has(item.id)) locations.set(item.id, version);
|
|
176
236
|
const from = locations.get(input.id) ?? null;
|
|
@@ -181,6 +241,7 @@ export function placeCell(input: PlacementCell, deps: PlacementDeps = {}): Place
|
|
|
181
241
|
const accelerator = input.accelerator === true || (input.accelerator === undefined && existing?.accelerator === true);
|
|
182
242
|
const load = { id: input.id, ceoMinutes: input.ceoMinutes ?? existing?.ceoMinutes, ceoDate: input.ceoDate ?? existing?.ceoDate };
|
|
183
243
|
const ceoCap = validCeoCap(deps.ceoDailyCap ?? placementCeoDailyCap());
|
|
244
|
+
const ceoCalendar = deps.ceoCalendar ?? placementConfig().ceoCalendar ?? {};
|
|
184
245
|
const predecessors = input.predecessors.map((id) => {
|
|
185
246
|
const version = locations.get(id);
|
|
186
247
|
if (!version) throw new CliUserError(`없는 선행 칸: ${id}`);
|
|
@@ -197,9 +258,12 @@ export function placeCell(input: PlacementCell, deps: PlacementDeps = {}): Place
|
|
|
197
258
|
if (input.priority === 'P1' && !deadlineVersion) throw new CliUserError(`P1 칸의 마감 판이 없다: ${input.id}`);
|
|
198
259
|
if (input.priority !== 'P0' && deadlineVersion && !schedules.some((row) => row.version === deadlineVersion)) throw new CliUserError(`없는 마감 판: ${deadlineVersion}`);
|
|
199
260
|
const eligible = open.filter((row) => predecessors.every((v) => versionOrder(row.version, v) > 0));
|
|
261
|
+
// Rubric grades rank discretionary placement, not incident/deadline priority. A P4 needs a separate merge/drop decision.
|
|
262
|
+
if (input.priority === 'P2' && input.grade === 'P4') throw new CliUserError(`${input.id} 배치할 판이 없다 — 루브릭 P4 칸은 합치거나 빼야 한다`);
|
|
263
|
+
const discretionary = eligible.filter((row) => !deadlineVersion || versionOrder(row.version, deadlineVersion) <= 0);
|
|
200
264
|
const candidates = input.priority === 'P0' ? eligible.filter((row) => row.version === first?.version)
|
|
201
265
|
: input.priority === 'P1' ? eligible.filter((row) => row.version === deadlineVersion)
|
|
202
|
-
:
|
|
266
|
+
: input.grade === 'P3' ? discretionary.reverse() : discretionary;
|
|
203
267
|
|
|
204
268
|
const occupied = (version: string) => snapshots.get(version)!.items.filter((item) => item.status !== 'done' && item.id !== input.id);
|
|
205
269
|
const capacity = (row: ReleaseSchedule) => Math.floor(rate * (Date.parse(row.landBy!) - now) / 86_400_000 * 0.7);
|
|
@@ -207,12 +271,13 @@ export function placeCell(input: PlacementCell, deps: PlacementDeps = {}): Place
|
|
|
207
271
|
let chosen: ReleaseSchedule | undefined;
|
|
208
272
|
let loadIssue: string | null = null;
|
|
209
273
|
const feasible = candidates.filter((row) => !dependencyViolation(snapshots, locations, new Map([[input.id, row.version]]), input, released));
|
|
210
|
-
//
|
|
274
|
+
// Without a deferral grade, keep the earliest eligible release; P3 starts at the latest eligible release.
|
|
275
|
+
// Accelerator displacement still only affects an otherwise full candidate.
|
|
211
276
|
for (const row of feasible) {
|
|
212
277
|
const placement = new Map([[input.id, row.version]]);
|
|
213
278
|
const items = occupied(row.version);
|
|
214
279
|
const seatItems = items.filter((item) => ownerMatches(item.owner, seat));
|
|
215
|
-
const directIssue = ceoOverload(load, row, snapshots, schedules, ceoCap);
|
|
280
|
+
const directIssue = ceoOverload(load, row, snapshots, schedules, ceoCap, ceoCalendar);
|
|
216
281
|
if (items.length < capacity(row) && seatItems.length < seatLimit) {
|
|
217
282
|
if (directIssue) { loadIssue ??= directIssue; continue; }
|
|
218
283
|
chosen = row;
|
|
@@ -238,8 +303,8 @@ export function placeCell(input: PlacementCell, deps: PlacementDeps = {}): Place
|
|
|
238
303
|
const proposed = new Map<string, { items: CeoWork[] }>([...snapshots].map(([version, snapshot]) => [version, { items: snapshot.items.filter((cell) => cell.id !== input.id && cell.id !== item.id) }]));
|
|
239
304
|
proposed.get(row.version)!.items.push(load);
|
|
240
305
|
proposed.get(next.version)!.items.push(item);
|
|
241
|
-
const issue = ceoOverload(load, row, proposed, schedules, ceoCap)
|
|
242
|
-
?? ceoOverload(item, next, proposed, schedules, ceoCap);
|
|
306
|
+
const issue = ceoOverload(load, row, proposed, schedules, ceoCap, ceoCalendar)
|
|
307
|
+
?? ceoOverload(item, next, proposed, schedules, ceoCap, ceoCalendar);
|
|
243
308
|
if (issue) { loadIssue ??= issue; return false; }
|
|
244
309
|
return true;
|
|
245
310
|
});
|
|
@@ -253,7 +318,7 @@ export function placeCell(input: PlacementCell, deps: PlacementDeps = {}): Place
|
|
|
253
318
|
loadIssue ??= directIssue;
|
|
254
319
|
}
|
|
255
320
|
if (!chosen) throw new CliUserError(loadIssue ?? `${input.id} 배치할 판이 없다 — 용량·선행·마감·동결을 확인하라`);
|
|
256
|
-
const reason = `${accelerator ? '가속 등급 · ' : ''}${input.priority} ${input.priority === 'P0' ? '사고·회귀·발행 막음: 다음 판' : input.priority === 'P1' ? `지시·행사 마감 판 ${deadlineVersion}` : '착지 마감 내 가장 이른 여유 판'}${accelerator ? ' · 같은 우선순위 안에서 가속 등급을 먼저' : ''} · PR/24h ${rate} · 용량 ${capacity(chosen)} · 자리 ${seatLimit}`;
|
|
321
|
+
const reason = `${accelerator ? '가속 등급 · ' : ''}${input.priority} ${input.priority === 'P0' ? '사고·회귀·발행 막음: 다음 판' : input.priority === 'P1' ? `지시·행사 마감 판 ${deadlineVersion}` : input.grade === 'P3' ? '루브릭 P3: 착지 마감 내 가장 늦은 여유 판부터' : '착지 마감 내 가장 이른 여유 판'}${accelerator ? ' · 같은 우선순위 안에서 가속 등급을 먼저' : ''} · PR/24h ${rate} · 용량 ${capacity(chosen)} · 자리 ${seatLimit}`;
|
|
257
322
|
const decision = { id: input.id, from, version: chosen.version, reason, displaced };
|
|
258
323
|
if (!deps.dryRun) {
|
|
259
324
|
const by = deps.by ?? 'OP';
|
|
@@ -268,7 +333,7 @@ export function placeCell(input: PlacementCell, deps: PlacementDeps = {}): Place
|
|
|
268
333
|
|
|
269
334
|
function moveConstraint(
|
|
270
335
|
item: ChecklistItem, to: ReleaseSchedule, backlog: readonly string[],
|
|
271
|
-
snapshots: Map<string, ChecklistItem[]>, now: number, rate: number, caps: Record<string, number>, rows: ReleaseSchedule[], ceoCap: number,
|
|
336
|
+
snapshots: Map<string, ChecklistItem[]>, now: number, rate: number, caps: Record<string, number>, rows: ReleaseSchedule[], ceoCap: number, calendar: CeoCalendar,
|
|
272
337
|
released: string | null | undefined,
|
|
273
338
|
): string | null {
|
|
274
339
|
if (!to.landBy || !available(to, now, backlog)) return '착지 마감이 지났거나 백로그 판이라 이동 불가';
|
|
@@ -278,8 +343,11 @@ function moveConstraint(
|
|
|
278
343
|
const destination = snapshots.get(to.version) ?? [];
|
|
279
344
|
if (destination.filter((cell) => cell.status !== 'done').length >= Math.floor(rate * (Date.parse(to.landBy) - now) / 86_400_000 * 0.7)) return '다음 판 PR 용량 초과';
|
|
280
345
|
if (destination.filter((cell) => cell.status !== 'done' && ownerMatches(cell.owner, seat)).length >= (caps[seat] ?? Infinity)) return '다음 판 자리 용량 초과';
|
|
281
|
-
const
|
|
282
|
-
|
|
346
|
+
const ceoSnapshots = new Map([...snapshots].map(([version, items]) => [version, { items }]));
|
|
347
|
+
if (item.ceoMinutes) for (const version of assignedVersions()) {
|
|
348
|
+
if (!ceoSnapshots.has(version)) ceoSnapshots.set(version, { items: listChecklist(version).items });
|
|
349
|
+
}
|
|
350
|
+
const loadIssue = ceoOverload(item, to, ceoSnapshots, rows, ceoCap, calendar);
|
|
283
351
|
if (loadIssue) return loadIssue;
|
|
284
352
|
const { locations, conflict } = cellLocations(snapshots, released);
|
|
285
353
|
if (conflict) return `여러 판의 같은 칸: ${conflict}`;
|
|
@@ -321,9 +389,10 @@ export function rebalance(version: string, deps: PlacementDeps = {}): RebalanceR
|
|
|
321
389
|
const rate = deps.merged24h ?? mergedPrsLast24h(new Date(now));
|
|
322
390
|
const caps = deps.seatCap ?? placementSeatCap();
|
|
323
391
|
const ceoCap = validCeoCap(deps.ceoDailyCap ?? placementCeoDailyCap());
|
|
392
|
+
const ceoCalendar = deps.ceoCalendar ?? placementConfig().ceoCalendar ?? {};
|
|
324
393
|
const result: RebalanceResult = { decisions: [], blocked: [] };
|
|
325
394
|
for (const { item, score } of unstarted) {
|
|
326
|
-
const violation = moveConstraint(item, next!, backlog, snapshots, now, rate, caps, rows, ceoCap, deps.released ?? releasedVersion());
|
|
395
|
+
const violation = moveConstraint(item, next!, backlog, snapshots, now, rate, caps, rows, ceoCap, ceoCalendar, deps.released ?? releasedVersion());
|
|
327
396
|
if (violation) {
|
|
328
397
|
result.blocked.push({ id: item.id, from: version, to: next!.version, reason: violation });
|
|
329
398
|
continue;
|
|
@@ -349,7 +418,7 @@ export function seatMove(id: string, from: string, to: string, by: string, reaso
|
|
|
349
418
|
if (!ownerMatches(item.owner, parseOwner(by).seat) || next?.version !== to) throw new CliUserError('남의 칸 당기기 거부 — COO 에 요청');
|
|
350
419
|
const now = (deps.now ?? new Date()).getTime();
|
|
351
420
|
const rate = deps.merged24h ?? mergedPrsLast24h(new Date(now));
|
|
352
|
-
const violation = moveConstraint(item, next, backlog, snapshots, now, rate, deps.seatCap ?? placementSeatCap(), rows, validCeoCap(deps.ceoDailyCap ?? placementCeoDailyCap()), deps.released ?? releasedVersion());
|
|
421
|
+
const violation = moveConstraint(item, next, backlog, snapshots, now, rate, deps.seatCap ?? placementSeatCap(), rows, validCeoCap(deps.ceoDailyCap ?? placementCeoDailyCap()), deps.ceoCalendar ?? placementConfig().ceoCalendar ?? {}, deps.released ?? releasedVersion());
|
|
353
422
|
if (violation) throw new CliUserError(`${id}: ${violation}`);
|
|
354
423
|
if (!deps.dryRun) move(id, from, to, by, undefined, undefined, reason);
|
|
355
424
|
}
|