elanous 0.2.27 → 0.2.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/{Ev2jivDMTSoEc-5rIXdcy → cIpv5nU9AycwYWZI9H589}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/{ab4d57c2.e90bc2cb31949045.js → 058a98ba.7eb28d5e6dc03565.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{4565-67e4c620597965c8.js → 1015-9cba22a5a4554797.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1084-4e66af7376a18db7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1106.3a6410b7b0c70ad4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1127.32fc642f70423506.js +8 -0
- package/apps/pwa/out/_next/static/chunks/{1516.83dd2c1b86b4e507.js → 1142.65d002789ab3af7d.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/1189-d9b8e1995a831fae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1224.6da98c7ad0eb7dd8.js +34 -0
- package/apps/pwa/out/_next/static/chunks/1370.4e3e2558f111fd5d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1514-b7da377fdc4eb17a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1516.dfaceff3fd2d0e0d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1577.3c24c32584e10d70.js +286 -0
- package/apps/pwa/out/_next/static/chunks/161.130a569855c4d68c.js +24 -0
- package/apps/pwa/out/_next/static/chunks/1782-59db1b5aa06df38a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1743.563d3168e95825ef.js → 1852.7f7b53991fcb99ef.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/1864.16da9556e78a9236.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1b6f6b2a.aff090122be3726e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2341.e8a9135e5b910dd5.js +36 -0
- package/apps/pwa/out/_next/static/chunks/257.10afdbe3520f2fc2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2658.87550e692dc493f6.js +64 -0
- package/apps/pwa/out/_next/static/chunks/2673.85e2f02efae7a85f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2766-bee623b37472ed63.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2794.6d836872b0bf43e2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2806.eb57ced1a528ed39.js +1 -0
- package/apps/pwa/out/_next/static/chunks/283.7a121c4910e6dad7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{65ebff95.081daeff622d238e.js → 285adb1a.5654e605dddf86d2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2870.1dc29aa5fc01f09f.js +115 -0
- package/apps/pwa/out/_next/static/chunks/293.fd38e77ce5d48103.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2993-5952a1cd3fd5d441.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3160-95c96f4d2a21dd26.js +1 -0
- package/apps/pwa/out/_next/static/chunks/32-a523427c5bd5ed00.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3280.af38002a6424a905.js +90 -0
- package/apps/pwa/out/_next/static/chunks/328bb3ab-6628a1114fe3fb3b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3292-5ddffdb2f517b44f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9917-9e26abc5f7772735.js → 3396-97b97928c49190c9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{6196.34d6b229079e9e28.js → 3403.7199205e65124486.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3523.d3231daaf7aba993.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3763.577de3181e1e6ea0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4138-cc56b0938ef72783.js +1 -0
- package/apps/pwa/out/_next/static/chunks/419.736c0d6f426249a7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4255-8350c43d307a8875.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6def8a8e.c81f9ca41028eccf.js → 425cfc4d.fb91158e3971c39f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4352.a4954693499c2714.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4421-f2dcbc6fd80ec013.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4513-51b94aeee37c9905.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{16e57f67.3ea19e893461e611.js → 46bd8bea.2f4b4099172fcdb7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{5081.a240b63fad28663a.js → 4759.1f76713aefb87db5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4928-c3b72f99aa588699.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4986-f9fc0283fb325219.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5014.671a7b9f69c2b7b1.js +94 -0
- package/apps/pwa/out/_next/static/chunks/5137.d398e24710b3feb6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5298.0bea74885cabe754.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5315.74c53a39739b11c4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5317-8b1df8d02105cd07.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5358-33051f41a2b239af.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5388.a2ec02501f714ad5.js +62 -0
- package/apps/pwa/out/_next/static/chunks/5395.e6ac948a2d15058d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5409-f282bf533c151849.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5466.d0f4aba9d52acfa8.js +27 -0
- package/apps/pwa/out/_next/static/chunks/5564.3d28b353470fe305.js +104 -0
- package/apps/pwa/out/_next/static/chunks/{9974.e77b4ff4884557b3.js → 5591.a35e7a217734a8b9.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/569.b0cbfb4dee4a167a.js +93 -0
- package/apps/pwa/out/_next/static/chunks/5835-72465b73a8bb8d74.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5941.045d6093139e1f7a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5492-e05e26a033ba80d2.js → 6013-2f769370d8b4c2c6.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/6228.47cabb83564dfe54.js +24 -0
- package/apps/pwa/out/_next/static/chunks/6457-8d1993002a87d0fe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6513.ef54cfae52351982.js +2 -0
- package/apps/pwa/out/_next/static/chunks/{5408.377c8c543635b81f.js → 6534.1ab00f370db7b105.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6582.13d245bf87df69f1.js +148 -0
- package/apps/pwa/out/_next/static/chunks/6655.c33f6acc2c61bb89.js +149 -0
- package/apps/pwa/out/_next/static/chunks/6731-9d7195af289c8d4c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6733.0be05b1db9279fc5.js +79 -0
- package/apps/pwa/out/_next/static/chunks/6742-90937146253a8b9e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8578.dff22d988cc2b106.js → 6767.1fcfc9b8be83975f.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/6776.06ff1e3b464cc607.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9311.24c8f5ea793253dd.js → 6853.cc5174a9dee59593.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/687-816858f77a396a4e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6888.72c7b8cfb02a7e61.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6993.c17c828ac3ef3253.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7076.9f27ef666b0cefdd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7113.1403c5d451804e8c.js +287 -0
- package/apps/pwa/out/_next/static/chunks/7169.9dd603c726c2cd7c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7247-e3ec21f761f86710.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7261-a47b72f96f5199df.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7335-5cda81ac90310077.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7356-c2e4af090546e2d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{94.4cfed21c0b781a06.js → 7375.050ea0d72968e49f.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/738-59518742078fda71.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7385.a7375f10db73243a.js +77 -0
- package/apps/pwa/out/_next/static/chunks/7445.cf1cf2fe109bcd96.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7505.a2057432ab6ef99a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7515.31f45fac1f9c70cb.js +74 -0
- package/apps/pwa/out/_next/static/chunks/7658-d4ce8c72a9548a91.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7725-4f83d5541fbdb81b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8107-0b1ae46bc5bcb788.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8132.bfea2f016ce8d992.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8332.6f0f49df772e163b.js +23 -0
- package/apps/pwa/out/_next/static/chunks/{7483.ba01c6c209b81bba.js → 8398.73a19b39715eda04.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{2846.d6a433b8663819a5.js → 8471.8d2a8c74f09c36ff.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/8672.e299832bc7d18ee2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8735.f1ef798a5f260f6f.js +226 -0
- package/apps/pwa/out/_next/static/chunks/{5785-ea8d2c633b6688e3.js → 8767-2f6a634f5abeaa01.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8774-88f67f7d8d53d299.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8782.252e5a405d86e4ed.js +32 -0
- package/apps/pwa/out/_next/static/chunks/8795.32573a185af18c7b.js +41 -0
- package/apps/pwa/out/_next/static/chunks/909-9ed7aa0dba804316.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9100.22b0301256827007.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9140-dde77575570bc71e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4593.10f5b9bd702d4a6c.js → 9143.ff14585db1d275ff.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9292-a7ad9a01c7044b93.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9517.00627dc51b02821c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9725.dcbb585652d8e9c5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9761.a3ebf5c389c24ddc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4204.a409ab6250c0f6a8.js → 9907.49be0fcadaf214b5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9972.834d2b2b7d48f1ed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9995-1a90486f5ae01546.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{973cbf1b.3bbcf2b41548eca7.js → a435ad95.577add051b4facb2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-83372d8b5863ebf7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-d69256d7613c140a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-352c5e9ba3f9b858.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-69f1d725524201a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-a5635eba0d73b84e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-ad9181d84285e4ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-86feb08080261a2f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-ffa15987e4756d03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-11a3ab44ac327cc1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-6309b11c4264d24d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-8800472079612e87.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-d387bf3f2301882a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-048f88d8c8658da6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-6c14f9739b7f6880.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-2834f44f64e98d74.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-83372d8b5863ebf7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-a13df6f88e824887.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-8a4397248754cf2b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-daf0bd329360a336.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-f7ef1c28764cb476.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-61e74f28b9353dac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/{page-ff27902d0b11ef4f.js → page-8aaa6dedc4c10dfb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-294602ba649be829.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-5fa5f41ed3fce323.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-25a302273b054fd7.js → page-33bf667a5fb297bf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-5ae3cbd6e5dba515.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-eb867d16ad6fdede.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1ab789fa68682f7e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-6fd41b5b6ff13a71.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fb3d5f7642c99fba.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-70f705b2b5ed42ca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-c9c93b358a7667b5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-1563c556b81c96ed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-ce89c20c45b3a673.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-423ae63d786f4ee9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-591a5378a4c4447c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-a250496b19a5824b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-c36a9277febc5911.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-83372d8b5863ebf7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-29900ed0011b2c83.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-a5de57edc6d7f942.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-16bdd4c9f40498df.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-75e946e6e71cc6d8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/{page-87b88f551f421d8b.js → page-9eb56166774eb256.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-1c5f9ef35dbb22c5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-657ff9985e508498.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-22dc72499da68fb2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-9f92737b8766009a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-094ad334e40f8169.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-8bcd379852ff6287.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-6b04413f280cd344.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{f4dfbfc0.a69fb12f854882aa.js → bc324b7c.02a09dd93f6a399b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/de70ab42-d28d47437208d458.js +1 -0
- package/apps/pwa/out/_next/static/chunks/f800c5e3.46dbd7f2af8c3fce.js +1 -0
- package/apps/pwa/out/_next/static/chunks/f8bc19ad.47579f6d684b0760.js +122 -0
- package/apps/pwa/out/_next/static/chunks/framework-ca9ac4609fb9fed8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-0dd08d32d15a030f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-f0399d33955c4e01.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-d42caca6f8ac2211.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-42e713c13aa41ca8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-8fbf8f61a377aa16.js +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +22 -22
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +22 -22
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +22 -22
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +21 -21
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +21 -21
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +22 -22
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +21 -21
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +22 -22
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +22 -22
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +21 -21
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +22 -22
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +22 -22
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +22 -22
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +22 -22
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +21 -21
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +21 -21
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +22 -22
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +22 -22
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +22 -22
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +21 -21
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +21 -21
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +21 -21
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +22 -22
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +22 -22
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +21 -21
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +21 -21
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +21 -21
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +21 -21
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +22 -22
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +22 -22
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +21 -21
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +22 -22
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +22 -22
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +22 -22
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +22 -22
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +22 -22
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +21 -21
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +22 -22
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +22 -22
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +22 -22
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +22 -22
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +22 -22
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +22 -22
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +22 -22
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +22 -22
- package/graphs/implement-loop.yaml +38 -1
- package/graphs/release/release-loop.yaml +6 -2
- package/graphs/steward/recipes.yaml +6 -0
- package/graphs/steward/release-day.yaml +23 -0
- package/graphs/steward/steward.yaml +9 -1
- package/graphs/work/code-pr.yaml +23 -0
- package/package.json +1 -1
- package/plugins/agent-team/agents/research.md +1 -0
- package/scripts/browser-observe-cycle.ts +76 -2
- package/scripts/ci-typecheck-changed.ts +53 -19
- package/scripts/coord-channel-watch.sh +26 -6
- package/scripts/coord-role-pack.ts +143 -0
- package/scripts/detect-test-interference.ts +31 -1
- package/scripts/lib/morning-report-options.ts +41 -0
- package/scripts/pr-land-extra-remote-gates.ts +49 -0
- package/scripts/release-loop/docs-node.ts +51 -22
- package/scripts/release-loop/gate-batch-safe.ts +23 -0
- package/scripts/release-loop/gate-history.ts +422 -0
- package/scripts/release-loop/gate-node.ts +336 -39
- package/scripts/release-loop/gate-triage-node.ts +190 -0
- package/scripts/release-loop/node-verdict.ts +3 -1
- package/scripts/release-loop/publish-node.ts +3 -0
- package/scripts/release-loop/spawn-lane.ts +68 -0
- package/scripts/release-loop/tui-node-checks.ts +28 -2
- package/scripts/release-loop/version-node.ts +62 -4
- package/scripts/skills/public-check.ts +5 -2
- package/scripts/test-deterministic.ts +549 -37
- package/scripts/usage-rollup.ts +11 -3
- package/scripts/ux-sim.ts +7 -1
- package/scripts/verify-kpack6-live.ts +230 -0
- package/src/acp/codex-plugins.ts +17 -0
- package/src/acp/core-turn-bridge.ts +98 -22
- package/src/acp/elanous-extensions.ts +2 -0
- package/src/acp/server.ts +2 -0
- package/src/agent/ref-grounding.ts +18 -0
- package/src/agent/task-notification.ts +2 -1
- package/src/agent-mission/in-agent-plugin.ts +8 -3
- package/src/agent-substrate/pr-reviewer.ts +29 -21
- package/src/agent-substrate/self-review-cli.ts +34 -10
- package/src/briefing/brief-items.ts +151 -35
- package/src/budget/llm-cost.ts +25 -2
- package/src/budget/pty-usage-reemit.ts +7 -0
- package/src/budget/run-usage-rollup.ts +9 -0
- package/src/budget/usage-daily-store.ts +98 -0
- package/src/budget/usage-outcomes.ts +32 -0
- package/src/capture/tui-self-report.ts +22 -4
- package/src/chat/index.ts +31 -10
- package/src/chat/pickers/modal-runtime.ts +1 -0
- package/src/chat/pickers/modals.ts +3 -1
- package/src/chat/turn-typeahead.ts +123 -16
- package/src/claims/claims-ledger.ts +23 -0
- package/src/claims/claims-render.ts +22 -2
- package/src/cli/brief-cli.ts +131 -10
- package/src/cli/claims-cli.ts +24 -4
- package/src/cli/dashboard-name-flag.ts +48 -0
- package/src/cli/doctor-cli.ts +80 -0
- package/src/cli/log-event-names.ts +12 -0
- package/src/cli/msg-cli.ts +210 -11
- package/src/cli/plugin-cli.ts +11 -3
- package/src/cli/pr-cli.ts +443 -104
- package/src/cli/pr-land-heavy-remote.ts +233 -0
- package/src/cli/pr-land-overlap-evidence.ts +104 -5
- package/src/cli/pr-land-refusal.ts +20 -0
- package/src/cli/pty-takeover-cli.ts +125 -16
- package/src/cli/release-cli.ts +46 -2
- package/src/cli/remote-resolve.ts +2 -1
- package/src/cli/rfc-cli.ts +24 -0
- package/src/cli/seat-cli.ts +26 -16
- package/src/cli/tailscale-serve.ts +5 -8
- package/src/cli/tasks-cli.ts +400 -27
- package/src/cli/usage-cli.ts +155 -16
- package/src/cli/usage-daily-cli.ts +84 -0
- package/src/context-bus/context-now.ts +82 -5
- package/src/core-turn/auth-rejected-notice.ts +41 -0
- package/src/core-turn/run-core-turn.ts +13 -2
- package/src/core-turn/run-goal-loop.ts +17 -1
- package/src/core-turn/short-question.ts +45 -12
- package/src/core-turn/types.ts +7 -0
- package/src/dashboard/acp-active-model.ts +40 -0
- package/src/dashboard/auto-compact-runtime.ts +2 -0
- package/src/dashboard/boot-banner.ts +80 -0
- package/src/dashboard/dashboard-status-lines.ts +11 -0
- package/src/dashboard/detail-viewer-runtime.ts +15 -2
- package/src/dashboard/event-loop-stall-monitor.ts +4 -0
- package/src/dashboard/first-screen-band.ts +63 -13
- package/src/dashboard/goal-loop-thinking.ts +16 -0
- package/src/dashboard/hitl-boot.ts +9 -4
- package/src/dashboard/index.ts +235 -389
- package/src/dashboard/input/chat-main-entry-runtime.ts +2 -0
- package/src/dashboard/input/chat-main-log-actions.ts +4 -0
- package/src/dashboard/input/chat-main-log-command.ts +12 -1
- package/src/dashboard/input/chat-main-plain-turn-runtime.ts +172 -3
- package/src/dashboard/input/chat-main-session.ts +6 -2
- package/src/dashboard/input/chat-main-slash-command.ts +7 -0
- package/src/dashboard/input/chat-main-turn.ts +2 -1
- package/src/dashboard/input/compact-prepass-recovery.ts +97 -0
- package/src/dashboard/input/dashboard-chord-actions.ts +10 -1
- package/src/dashboard/input/dashboard-chord-runtime.ts +2 -0
- package/src/dashboard/input/external-input-host.ts +45 -0
- package/src/dashboard/input/log-fold-binding.ts +18 -0
- package/src/dashboard/input/mouse-wiring.ts +17 -4
- package/src/dashboard/input/turn-steer-bridge.ts +94 -0
- package/src/dashboard/log-click-runtime.ts +5 -3
- package/src/dashboard/running-build-status.ts +74 -0
- package/src/dashboard/seat-boot-preamble.ts +113 -0
- package/src/dashboard/seat-local-instructions.ts +305 -0
- package/src/dashboard/skill-detection-runtime.ts +24 -4
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +100 -28
- package/src/dashboard/slash-runtime/model-choices.ts +35 -0
- package/src/dashboard/term-slash-runtime.ts +15 -0
- package/src/dashboard/turn-preamble.ts +30 -2
- package/src/dashboard/turn-session-layers.ts +176 -0
- package/src/dashboard/turn-stream-formatter.ts +14 -6
- package/src/dashboard/turn-stream-presentation-applier.ts +8 -0
- package/src/domains/morning-report.ts +3 -1
- package/src/domains/release-tool.ts +8 -9
- package/src/domains/surface-events.ts +4 -1
- package/src/domains/watch-default-scheduler.ts +8 -9
- package/src/fold-stack.ts +24 -1
- package/src/git-fs/dirty.ts +32 -4
- package/src/git-fs/index.ts +43 -1
- package/src/github/pr-labels.ts +10 -4
- package/src/graph-runner/runner.ts +102 -14
- package/src/harness/harness-clean.ts +21 -3
- package/src/harness/harness-cli-command.ts +22 -6
- package/src/harness/harness-gc.ts +32 -5
- package/src/harness/harness-pod-dispatch.ts +11 -4
- package/src/hq/seats.ts +17 -3
- package/src/index.ts +48 -6
- package/src/input/external-input-queue.ts +219 -0
- package/src/intelligence-map/model-bench-scores.ts +102 -0
- package/src/interactive-terminal-modal.ts +13 -0
- package/src/llm/route-decision.ts +25 -0
- package/src/llm-stream-retry-display.ts +59 -0
- package/src/llm.ts +253 -55
- package/src/log-entry.ts +3 -3
- package/src/log-pane/search.ts +29 -4
- package/src/loops/agent-reply.ts +256 -0
- package/src/loops/budget.ts +37 -7
- package/src/loops/loop-cli.ts +3 -2
- package/src/loops/msg-address.ts +57 -0
- package/src/loops/msg-wake.ts +282 -0
- package/src/loops/orchestrator/inbox.ts +130 -0
- package/src/loops/orchestrator/resync-shadow.ts +100 -12
- package/src/loops/orchestrator/tick.ts +94 -3
- package/src/loops/registry.ts +42 -4
- package/src/maturity/feature-maturity.ts +1 -1
- package/src/memory.ts +20 -5
- package/src/msg/msg-sse-server.ts +141 -0
- package/src/msg/msg-store.ts +84 -14
- package/src/native-tool-catalog.ts +125 -0
- package/src/nexus/api/http-server.ts +5 -0
- package/src/nexus/api/ops-api.ts +2 -0
- package/src/nexus/api/project-autopsy.ts +127 -0
- package/src/nexus/api/terminals.ts +3 -0
- package/src/nexus/api/workflow-approvals.ts +2 -0
- package/src/nexus/onboarding/first-boot-wizard.ts +56 -2
- package/src/oauth/codex-account-store.ts +15 -3
- package/src/oauth/codex-billing-route.ts +31 -0
- package/src/ops/overview-collect.ts +19 -2
- package/src/panes/log-pane.ts +67 -8
- package/src/patents/ledger.ts +128 -0
- package/src/persona/presets.ts +44 -0
- package/src/plugins/install/market-fetch.ts +32 -1
- package/src/plugins/maker/wizard-generate.ts +6 -4
- package/src/pod-lease/admission.ts +116 -41
- package/src/preview/terminal.ts +14 -1
- package/src/prompt-cache/types.ts +2 -0
- package/src/prompt-enhance/enhance.ts +24 -8
- package/src/prompt-library/universal-preamble.ts +273 -56
- package/src/pty-shell/pty-control-ipc.ts +4 -1
- package/src/pty-shell/pty-manifest.ts +77 -5
- package/src/pty-shell/registry.ts +19 -2
- package/src/pty-shell/tui-run-state.ts +253 -0
- package/src/release-loop/checklist.ts +11 -0
- package/src/release-loop/light-rc.ts +28 -3
- package/src/release-loop/merged-pr-checklist.ts +24 -5
- package/src/release-loop/rehearsal.ts +1 -1
- package/src/release-loop/rubric.ts +70 -0
- package/src/render.ts +5 -1
- package/src/rfc-registry/registry.ts +88 -0
- package/src/seat-dispatch/seat-request-ledger.ts +4 -0
- package/src/seat-loop/knowhow-lint.ts +233 -0
- package/src/seat-loop/seat-pack.ts +16 -4
- package/src/seat-loop/seat-report.ts +42 -2
- package/src/seat-loop/traffic-signal.ts +7 -0
- package/src/self-dev/ask-launch-flow.ts +48 -8
- package/src/self-dev/ask-path-candidates.ts +69 -14
- package/src/self-dev/decompose-proposal.ts +43 -1
- package/src/self-dev/graph-journey-nodes.ts +2 -0
- package/src/self-dev/launch-preflight-text.ts +5 -1
- package/src/self-dev/launch-preflight.ts +143 -10
- package/src/self-dev/run-supervisor.ts +71 -5
- package/src/self-dev/shard-aggregate.ts +2 -0
- package/src/self-dev/shard-run.ts +23 -2
- package/src/self-health/signatures.ts +132 -0
- package/src/self-implement/doctor-visit.ts +118 -0
- package/src/self-implement/gate-cli.ts +17 -4
- package/src/self-implement/goal-author-decompose.ts +80 -16
- package/src/self-implement/goal-author.ts +116 -73
- package/src/self-implement/goal-authoring-grounding.ts +1 -1
- package/src/self-implement/goal-file-name.ts +65 -0
- package/src/self-implement/goal-new-paths.ts +4 -5
- package/src/self-implement/graph-authority.ts +17 -3
- package/src/self-implement/graph-yaml.ts +15 -7
- package/src/self-implement/ground-goal.ts +2 -1
- package/src/self-implement/headless-elanous-driver.ts +16 -1
- package/src/self-implement/host-regate.ts +58 -9
- package/src/self-implement/orchestrator.ts +3 -10
- package/src/self-implement/review-mustfix-class.ts +115 -0
- package/src/self-implement/rework-policy.ts +80 -2
- package/src/self-implement/run-ledger.ts +111 -7
- package/src/self-implement/running-runs.ts +10 -6
- package/src/self-implement/self-implement-runtime.ts +32 -0
- package/src/self-implement/speed-claims.ts +145 -6
- package/src/session/pending-input.ts +62 -11
- package/src/session-runtime/index.ts +7 -1
- package/src/skills/tools/ops-ledger.ts +707 -0
- package/src/skills/tools/persistent-grounding.ts +103 -8
- package/src/skills/tools/shell.ts +196 -2
- package/src/status/bar.ts +32 -2
- package/src/status/status-provider-info.ts +58 -0
- package/src/steward/rejudge-tick.ts +12 -1
- package/src/steward/release-day.ts +158 -0
- package/src/steward/resync-tick.ts +123 -0
- package/src/task-agent/actions.ts +1 -0
- package/src/task-agent/adopt-pr.ts +140 -0
- package/src/task-agent/board.ts +11 -1
- package/src/task-agent/card-evidence.ts +37 -5
- package/src/task-agent/cover.ts +84 -17
- package/src/task-agent/failure-notify.ts +61 -0
- package/src/task-agent/green-sweep.ts +170 -0
- package/src/task-agent/land-worktree.ts +52 -5
- package/src/task-agent/live-move-history.ts +1 -0
- package/src/task-agent/live-moves.ts +43 -12
- package/src/task-agent/parent-host.ts +1 -1
- package/src/task-agent/postland-card.ts +139 -0
- package/src/task-agent/rejudge-on-head.ts +109 -7
- package/src/task-agent/remote-rejudge-bind.ts +91 -0
- package/src/task-agent/repair-branch-cas.ts +182 -0
- package/src/task-agent/repair-cycle.ts +288 -0
- package/src/task-agent/repair-next-move.ts +31 -0
- package/src/task-agent/repair-review.ts +24 -0
- package/src/task-agent/repair-worker.ts +87 -0
- package/src/task-agent/review-result-capture.ts +2 -1
- package/src/task-agent/shadow.ts +13 -7
- package/src/task-agent/task-hand.ts +115 -6
- package/src/task-orchestrator/surfaces/kubectl-transient-retry.ts +51 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +63 -7
- package/src/task-orchestrator/surfaces/pod-install-slots.ts +6 -11
- package/src/task-orchestrator/surfaces/pod-mirror-seed.ts +76 -0
- package/src/task-orchestrator/surfaces/pod-repair-commit.ts +96 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +175 -43
- package/src/telegram.ts +30 -1
- package/src/terminal/session-registry.ts +6 -0
- package/src/thinking-line.ts +12 -3
- package/src/tool-runtime/index.ts +4 -0
- package/src/tool-runtime/ops-ledger-runtimes.ts +84 -0
- package/src/tool-runtime/ref-consult-runtime.ts +17 -2
- package/src/tool-runtime/run-shell-runtime.ts +4 -1
- package/src/tool-runtime/self-cognition-runtimes.ts +5 -1
- package/src/tui.ts +5 -0
- package/src/user-config.ts +62 -7
- package/src/ux-sim/fake-llm.ts +29 -0
- package/src/ux-sim/fault-sqlite-busy.ts +50 -0
- package/src/ux-sim/in-process-cli.ts +175 -0
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/1027.a0f04663d24c2779.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1038.676007ae505822e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1113.a37eadf7f740d955.js +0 -1
- package/apps/pwa/out/_next/static/chunks/112-fa7c526c20ce1405.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1144.5fa1fa1d48dbcd12.js +0 -36
- package/apps/pwa/out/_next/static/chunks/1146.80d15ea7c6e6ee95.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1157.f08bf2b70915e176.js +0 -148
- package/apps/pwa/out/_next/static/chunks/119b405e.8fdd0e44d1ff37aa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1296.04560d98e73fbf51.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1314-f604ba013ec535b0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1454-a9c338f5bb556f42.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1464.f3209af0d8ae4ff2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1729-d73dd25e564679d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1843.5924c24295243261.js +0 -27
- package/apps/pwa/out/_next/static/chunks/1987.e28149724b92918b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2066-5ca18ffa5a4be68b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2240.d10a66695155fd93.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2391.e2cbc98d4abbb41a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2528.3868070ef1be69f9.js +0 -24
- package/apps/pwa/out/_next/static/chunks/2566-734cb83bbd405886.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2697.76ca2ac3efef6f3b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/275.0e6bad414729bfa0.js +0 -32
- package/apps/pwa/out/_next/static/chunks/2803-33bdae771855a8d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3143.257997a5f1ed79df.js +0 -34
- package/apps/pwa/out/_next/static/chunks/3259-86f3be56e43eaf6a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3280-e177c33bfbdcfb0a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3294-2b6a6d45a46a6c1c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3309.364573ceedbdfd5f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3522.758840fc5e7fe630.js +0 -287
- package/apps/pwa/out/_next/static/chunks/3525-a7c989c1a661e880.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3694.b1a66387756458c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/370.124f9e678a76fc01.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3797-9643105335e660fe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4084.15ecaf4240bd9e77.js +0 -286
- package/apps/pwa/out/_next/static/chunks/4089-b3382b8843114eee.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4293.928a8d573c16aab7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4402.f9238a3b18a7bd43.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4490-9b090b0f9427f902.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4548.13502d0ca3b4f862.js +0 -8
- package/apps/pwa/out/_next/static/chunks/4599-548bc96cda44a5a7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4625.206574e33a9e7ae8.js +0 -104
- package/apps/pwa/out/_next/static/chunks/4862-392c9a71f0d8dfb4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5054.2906d1b02542edc5.js +0 -149
- package/apps/pwa/out/_next/static/chunks/5102-c822bbbee5a022a4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5194-8c93f2e7e4b6f717.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5305-bacd7eca0b9a5848.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5311.9071ee3f44fd7ff5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5369.9f34353442daa421.js +0 -1
- package/apps/pwa/out/_next/static/chunks/589-49d730cb8138b397.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5904-264a6412ae3c9028.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5980.728531d5c566535c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/599-a914cc0468f56143.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6011.f525002e21bef0ac.js +0 -23
- package/apps/pwa/out/_next/static/chunks/6132.4381ab93683f1acf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6149.2e698635e454d8f6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6356-0afafc4230cf9767.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6406.64756073f7df35b8.js +0 -77
- package/apps/pwa/out/_next/static/chunks/6479-16e62aedafa0e310.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6599.b5246b46a2453329.js +0 -64
- package/apps/pwa/out/_next/static/chunks/6610.5c26eec011565acb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/666.37d18474edc1c552.js +0 -2
- package/apps/pwa/out/_next/static/chunks/6904.a0b5b1ed4f2d2c20.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7247-1f4aba94aba50d9f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7274-e71a7c5b7760cd02.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7290.1fcaeef3adb5d0d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7477-2c64e42760f82f33.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7505-47002b76ff1aef6c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7600-11884ca4da68e5d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7607.40d8bb3689697ccb.js +0 -62
- package/apps/pwa/out/_next/static/chunks/7752.5cbdd0b433c54af9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7763.f51fcc19ca1414ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7785.846140c859b1adf6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7807-a0fdac6393e0bf36.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7837-20a63b4b7e9342b1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/800.7815414cf280545d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8085-1e5fce59500a2cc2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8100.415f2105faa63e97.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8290.701aa42ed13aa767.js +0 -226
- package/apps/pwa/out/_next/static/chunks/83a80514-b897b64df43ce108.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8444.d9c74214a3145a43.js +0 -93
- package/apps/pwa/out/_next/static/chunks/8569.98c915534928c7f9.js +0 -24
- package/apps/pwa/out/_next/static/chunks/8591.b1c7f876e5ec7cfb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8715-f63756021d3ab989.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8879.776c2ec7732aa114.js +0 -115
- package/apps/pwa/out/_next/static/chunks/8881.ddca15b3c82c490c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8946.5da5c87f32a2e1a8.js +0 -41
- package/apps/pwa/out/_next/static/chunks/9117.080b9b75be90e14d.js +0 -74
- package/apps/pwa/out/_next/static/chunks/9167.f3ecb28f367d6e69.js +0 -94
- package/apps/pwa/out/_next/static/chunks/9224.66095370ee28c93b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9238-0682b013c83d6449.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9449.d3ed795da4befb1c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/949.1e7d45ccb71580aa.js +0 -90
- package/apps/pwa/out/_next/static/chunks/9638-d268329f2c6f4f8d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9842-d6644eb53d3dc6bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9863.94ca3263e760696e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9938.b23e4d4dfa0114cd.js +0 -79
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d9babd8795f396a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-a1ce306364f08da9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-07110da72c45e68f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-d2d76d4a7eae91bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-30007380f2e2a3c1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-e5a9302c105725a3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-b93c04b5311c97ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-718a49e86bf25f9c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-20662900d6b4b455.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-f707a509515ac50c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-6a16a48d1aa6e91f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-fcb06e305f515527.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-6acf5ec4e6c6d1af.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-39f82acf619cca23.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-7c2c8f81b185f9c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-d9babd8795f396a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-ae7aff07e32676bb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-482c60c1bc540d0a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-7cb996c1aa08b6f5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-623e08fe4477d606.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-db460cd8aecb8490.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-8397a5c6ca569bf4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1917ea87ea71ab6b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-670510eda8b9443f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-d73c3f1e00d93986.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-65caacf54389081e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4bb7556e2e97a750.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-77e04b4f8762c666.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-15c0a4095baddfba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-4eb96fc7241c2456.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-63e272a4cd3b6634.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-0d64ff212dc55d03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-7f096248993fc2d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e47fc72049149a38.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-f7d808e31e005895.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-5c2b24b5496dad73.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-d9babd8795f396a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-76cdb531668b5191.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-e2b63345bba1a11f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-d376e0c83edc041a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-d6db55fe2c04d128.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-dab163b338e3b966.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-08019efef111ed2e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-0260ffc7eda50db1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-980ce6fe36ad95eb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-99c4e6c7345a9156.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-0ab42e0f05823013.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-ad9220422d20ba33.js +0 -1
- package/apps/pwa/out/_next/static/chunks/dcf24383-2f82e35f2d827393.js +0 -1
- package/apps/pwa/out/_next/static/chunks/e117f801.e92bf10242653497.js +0 -1
- package/apps/pwa/out/_next/static/chunks/e87d89b9.2e71c0ae475d28a1.js +0 -122
- package/apps/pwa/out/_next/static/chunks/framework-bf135b997155e90e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-5571537543bea91c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-25c51c7df7f3a442.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-e3b2c1f5d82abd65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-083f2e23e9bc3641.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-e94d22b7a028217b.js +0 -1
- package/src/dashboard/bench-slash-runtime.ts +0 -40
- package/src/dashboard/input/search-modal-router.ts +0 -9
- package/src/dashboard/modals/pane-key-router.ts +0 -27
- package/src/dashboard/view-slash-runtime.ts +0 -48
- /package/apps/pwa/out/_next/static/{Ev2jivDMTSoEc-5rIXdcy → cIpv5nU9AycwYWZI9H589}/_ssgManifest.js +0 -0
|
@@ -11,20 +11,23 @@ import { effectiveInstanceRoot, releaseLedgerRoot } from '../../src/instance/res
|
|
|
11
11
|
import { getElanousConfigDirOverride } from '../../src/elanous-config-dir.js';
|
|
12
12
|
import { diffFailures, junitFailures, parseFailures, parseTimedOutFailures } from './gate-diff';
|
|
13
13
|
import { loadEnvKnownFailures, splitKnownEnv } from './env-known-failures';
|
|
14
|
+
import { loadGateBatchSafe } from './gate-batch-safe';
|
|
14
15
|
import { baselineFromCutLogs } from './baseline-from-logs';
|
|
15
16
|
import { mergePartialFailures, readPartialPlan, testFileOfId } from './gate-partial';
|
|
16
|
-
import { logPlanWalltime, planDurations,
|
|
17
|
+
import { logPlanWalltime, planDurations, readFileMemory } from './shard-plan';
|
|
18
|
+
import { isGateSpawnLaneFile, planGateSpawnSeparatedShards } from './spawn-lane';
|
|
17
19
|
import { readJunitFileSeconds } from './gate-method';
|
|
18
20
|
import { deriveCdpTestPatterns } from '../test-deterministic';
|
|
19
21
|
import { emitNodeResult, readGraphContext } from './node-verdict.js';
|
|
20
22
|
import { isGateExecutor, type GateExecutor } from './gate-executor.js';
|
|
21
|
-
import { runPodCommand, parsePodCpu, resolvePodCpu, type RunPodCommandOptions, type PodCommandResult, type PodCpu } from '../../src/task-orchestrator/surfaces/pod-command-job.js';
|
|
23
|
+
import { runPodCommand, parsePodCpu, podCpuMillis, resolvePodCpu, type RunPodCommandOptions, type PodCommandResult, type PodCpu } from '../../src/task-orchestrator/surfaces/pod-command-job.js';
|
|
22
24
|
import { POD_BUN_CACHE_HOST_PATH, parseInstallSeconds, podBunCacheVolume } from '../../src/task-orchestrator/surfaces/pod-bun-cache.js';
|
|
23
25
|
import { PodPoolScheduler, parsePodPool, checkPodPool, defaultRemoteRun, type PodPoolMember } from '../../src/task-orchestrator/surfaces/pod-pool.js';
|
|
24
26
|
import { POD_INSTALL_SLOTS_DEFAULT, POD_INSTALL_SLOTS_HOST_PATH, installSlotScript } from '../../src/task-orchestrator/surfaces/pod-install-slots.js';
|
|
25
27
|
import { gateDepsInstallScript, parseGateDeps } from '../../src/task-orchestrator/surfaces/pod-gate-deps.js';
|
|
26
28
|
import { gateShardsPath, recordGateShardsVerdict, writeGateShards, type GateShard } from '../../src/release-loop/gate-shards.js';
|
|
27
29
|
import { defaultKubectl } from '../../src/task-orchestrator/surfaces/self-implement-pod.js';
|
|
30
|
+
import { executeTuiNodeCheck, type ExecutedTuiCheck } from './tui-node-checks.js';
|
|
28
31
|
|
|
29
32
|
interface CommandResult { rc: number; output: string; passedIds?: string[]; stalledEnv?: Array<{ file: string; reason: 'no-output' }>; timedOut?: boolean }
|
|
30
33
|
export interface GateRunner {
|
|
@@ -52,6 +55,8 @@ export interface GateOptions {
|
|
|
52
55
|
instanceRoot?: string;
|
|
53
56
|
ledgerRoot?: string;
|
|
54
57
|
repo?: string;
|
|
58
|
+
/** Rehearsal's opt-in screen measurement; normal release gates keep their existing defaults. */
|
|
59
|
+
rehearsalTui?: boolean;
|
|
55
60
|
/** freshShards — GATE-SHARD-RESUME off: every shard starts a Pod even when this logDir already holds its result. */
|
|
56
61
|
pod?: { pool: string; shards?: number; shardTimeoutSeconds?: number; durationSource?: string; bunCache?: string; freshShards?: boolean;
|
|
57
62
|
/** GATE-SKIP-KNOWN-RETRY — failure ids the previous release already had (and env-known ones): a shard that fails only
|
|
@@ -96,6 +101,8 @@ export interface GateResult {
|
|
|
96
101
|
knownEnvCleared: string[];
|
|
97
102
|
stalledEnv: Array<{ file: string; reason: 'no-output' | 'isolated-timeout'; local: 'passed' | 'failed' | 'timeout' }>;
|
|
98
103
|
rcNonzeroNoFailures: Array<{ file: string; rc: number; pass: number; baseline: 'same' | 'clean' | 'missing' }>;
|
|
104
|
+
/** GATE-HOST-RECHECK-CAP — files left unmeasured when the final serial host recheck reaches its bound. */
|
|
105
|
+
unmeasuredRecheckFiles?: string[];
|
|
99
106
|
baselineSource?: 'ledger' | 'instance' | 'cut-logs' | 'swept';
|
|
100
107
|
/** GATE-BASELINE-CACHE — isolated baseline files answered from the ledger (hits) or run now (misses). */
|
|
101
108
|
baselineCache?: { hits: number; misses: number };
|
|
@@ -112,11 +119,14 @@ export interface GateResult {
|
|
|
112
119
|
/** GATE-INTRO-RECHECK — new failures that were only the «timed out» shape and passed one isolated re-run with a
|
|
113
120
|
* {@link GATE_TIMEOUT_RECHECK_MS} per-test limit: a warning (flaky under load), not an introduced regression. */
|
|
114
121
|
loadFlaky?: string[];
|
|
122
|
+
rehearsalTui?: ExecutedTuiCheck;
|
|
115
123
|
}
|
|
116
124
|
|
|
117
125
|
const sha = /^[0-9a-f]{7,40}$/i;
|
|
118
126
|
const versionPattern = /^\d+\.\d+\.\d+(?:-(?:rc|alpha|beta)\.\d+)?$/;
|
|
119
127
|
const failureFile = (root: string, version: string) => join(root, 'release', version, 'gate-failures.json');
|
|
128
|
+
/** Judged identities live beside the machine release ledger; gate-failures.json keeps its raw baseline schema. */
|
|
129
|
+
const verdictFile = (root: string, version: string) => join(root, 'release', version, 'gate-verdict.json');
|
|
120
130
|
const quote = (s: string) => `'${s.replaceAll("'", "'\\''")}'`;
|
|
121
131
|
const defaultRemoteMirror = '~/mirror/elanous-agent.git';
|
|
122
132
|
|
|
@@ -272,20 +282,70 @@ export function shardParts(paths: readonly string[], seconds: (file: string) =>
|
|
|
272
282
|
if (current.length) parts.push(current);
|
|
273
283
|
return parts;
|
|
274
284
|
}
|
|
285
|
+
/**
|
|
286
|
+
* GATE-ENV-BASELINE (W5 · 10-15) — the gate shard Pod's deterministic test env, exported before anything runs.
|
|
287
|
+
* 0.2.29 gate failures were the Pod's «environment», not the code: no LLM provider · `man` stubbed · CLI spawn timeouts.
|
|
288
|
+
* - `TZ=UTC` — the value `bun test` already uses when TZ is unset; pinned so a later image (or a shell profile) cannot move it.
|
|
289
|
+
* - `LANG`/`LC_ALL=C.UTF-8` — the image sets no locale (POSIX); C.UTF-8 ships with glibc, so no image rebuild.
|
|
290
|
+
* - `ELANOUS_TEST_FAKE_LLM=1` — the shared switch for the fake LLM provider. Its reader (UX device #27790) is not on main
|
|
291
|
+
* yet; until it lands the switch changes nothing. The reader yields to an explicit provider/endpoint, so a spawned CLI
|
|
292
|
+
* test that injects its own stub keeps it.
|
|
293
|
+
* Retries, splits and file isolation all re-enter `runShard`, which builds every Pod shell here — they carry it too.
|
|
294
|
+
* Provider keys are not set here on purpose: `test:deterministic` strips every credential-shaped key
|
|
295
|
+
* (`scripts/lib/deterministic-env.ts` `isCredentialKey`), so a test never reaches a real provider from the Pod.
|
|
296
|
+
* Binaries the image lacks (`man` pages — `git <sub> --help`) are not fixed here; see MANUAL-gate-regression-response-recipe §2a.
|
|
297
|
+
*/
|
|
298
|
+
export const GATE_POD_ENV_BASELINE: Readonly<Record<string, string>> = Object.freeze({
|
|
299
|
+
TZ: 'UTC',
|
|
300
|
+
LANG: 'C.UTF-8',
|
|
301
|
+
LC_ALL: 'C.UTF-8',
|
|
302
|
+
ELANOUS_TEST_FAKE_LLM: '1',
|
|
303
|
+
});
|
|
304
|
+
export function gateEnvBaselineLine(env: Readonly<Record<string, string>> = GATE_POD_ENV_BASELINE): string {
|
|
305
|
+
return `export ${Object.entries(env).map(([key, value]) => `${key}=${quote(value)}`).join(' ')}`;
|
|
306
|
+
}
|
|
275
307
|
/**
|
|
276
308
|
* The shard's Pod shell. Install once (behind the node's install slots), then run each part under the soft deadline,
|
|
277
309
|
* leaving `install.log`/`install.rc`, `part-<i>.log`/`.rc`/`.junit.xml`, and `shard.timeout` (the part index) when the
|
|
278
|
-
* deadline cut the shard.
|
|
310
|
+
* deadline cut the shard. Real child/PTY parts have an additional 120 s test limit; a cap hit also writes
|
|
311
|
+
* `shard.timeout` so the artifact reader does not mistake an unfinished child for a test verdict.
|
|
312
|
+
* A part without `.rc` did not finish. The shell itself always exits 0 (as before).
|
|
279
313
|
*/
|
|
280
|
-
export function gateShardShell(o: { parts: readonly (readonly string[])[]; cdpPatterns: readonly string[]; softSeconds: number; cachePrefix: string; installSlots: number; depsDir?: string; beatSeconds?: number }): string {
|
|
281
|
-
|
|
314
|
+
export function gateShardShell(o: { parts: readonly (readonly string[])[]; cdpPatterns: readonly string[]; softSeconds: number; cachePrefix: string; installSlots: number; depsDir?: string; beatSeconds?: number; parallelParts?: number }): string {
|
|
315
|
+
// The planner keeps spawn files in their own shards; reject a mixed part rather than silently lose its limit.
|
|
316
|
+
for (const part of o.parts) {
|
|
317
|
+
if (part.some(isGateSpawnLaneFile) && !part.every(isGateSpawnLaneFile)) throw new Error('gate spawn lane part mixed with regular tests');
|
|
318
|
+
}
|
|
319
|
+
const spawnParts = o.parts.flatMap((part, index) => part.length && part.every(isGateSpawnLaneFile) ? [index] : []);
|
|
320
|
+
const commands = o.parts.map((part, index) => {
|
|
282
321
|
const ignores = o.cdpPatterns.filter((pattern) => part.includes(pattern)).flatMap((pattern) => ['--path-ignore-patterns', pattern]);
|
|
283
|
-
return `run_part ${index} ${['bun', 'run', 'test:deterministic', ...ignores, ...part.map(asPath)].map(quote).join(' ')}`;
|
|
284
|
-
})
|
|
322
|
+
return `run_part ${index} ${['bun', 'run', 'test:deterministic', ...(spawnParts.includes(index) ? ['--timeout=120000'] : []), ...ignores, ...part.map(asPath)].map(quote).join(' ')}`;
|
|
323
|
+
});
|
|
324
|
+
// Only file-isolation batches use this path. A bounded group shares one clone/install, but every test process gets
|
|
325
|
+
// its own HOME, state and tmp directory; part logs/JUnit remain separate and the existing artifact reader judges them.
|
|
326
|
+
const width = Math.max(1, Math.floor(o.parallelParts ?? 1));
|
|
327
|
+
const isolateParts = o.parallelParts !== undefined;
|
|
328
|
+
const run = width === 1 ? commands.join(' && ') : Array.from({ length: Math.ceil(commands.length / width) }, (_, group) => [
|
|
329
|
+
...commands.slice(group * width, (group + 1) * width).map((line) => `(${line}) & pids+=("$!")`),
|
|
330
|
+
'for pid in "${pids[@]}"; do wait "$pid" || true; done; pids=()',
|
|
331
|
+
].join('; ')).join('; ');
|
|
285
332
|
return [
|
|
286
333
|
'mkdir -p "$HOME/outbox"; O="$HOME/outbox"',
|
|
334
|
+
'stage_at() { printf "%s\\t%s\\n" "$1" "$2" >> "$O/stages.tsv"; }',
|
|
335
|
+
'stage() { stage_at "$(date +%s)" "$1"; }',
|
|
336
|
+
// This shell's first moment, captured before anything else runs so clone-end/shell-start keep their meaning.
|
|
337
|
+
'shell_t=$(date +%s)',
|
|
338
|
+
// Boot stages only: the container's age at shell entry. gate_end below keeps reading pod_age at its own spot.
|
|
339
|
+
`boot_age=$(awk -v t="$(cut -d' ' -f22 /proc/1/stat 2>/dev/null)" -v hz="$(getconf CLK_TCK 2>/dev/null)" '{ if (t > 0 && hz > 0) print int($1 - t / hz); else print 0 }' /proc/uptime 2>/dev/null)`,
|
|
340
|
+
'case "$boot_age" in ""|*[!0-9]*) boot_age=0;; esac',
|
|
341
|
+
// GATE-WARM-BOOT-STAGES: boot on the Pod's own clock — prep start = the child container's PID 1 (none when the age is
|
|
342
|
+
// unknown, never a guess), clone end = this shell's first line (the source checkout runs right before it).
|
|
343
|
+
'if [ "$boot_age" -gt 0 ]; then stage_at "$(( shell_t - boot_age ))" prep-start; fi',
|
|
344
|
+
'stage_at "$shell_t" clone-end',
|
|
345
|
+
'stage_at "$shell_t" shell-start',
|
|
287
346
|
...(o.beatSeconds ? gateBeatScript(o.beatSeconds, o.installSlots > 0) : []),
|
|
288
347
|
...(o.cachePrefix ? [o.cachePrefix] : []),
|
|
348
|
+
gateEnvBaselineLine(),
|
|
289
349
|
// Seconds since this container's PID 1 started (the clone ran before this shell) — 0 when /proc cannot tell.
|
|
290
350
|
`pod_age=$(awk -v t="$(cut -d' ' -f22 /proc/1/stat 2>/dev/null)" -v hz="$(getconf CLK_TCK 2>/dev/null)" '{ if (t > 0 && hz > 0) print int($1 - t / hz); else print 0 }' /proc/uptime 2>/dev/null)`,
|
|
291
351
|
'case "$pod_age" in ""|*[!0-9]*) pod_age=0;; esac',
|
|
@@ -293,19 +353,45 @@ export function gateShardShell(o: { parts: readonly (readonly string[])[]; cdpPa
|
|
|
293
353
|
...(o.installSlots > 0 ? [installSlotScript({ slots: o.installSlots })] : []),
|
|
294
354
|
'run_part() {',
|
|
295
355
|
' local i=$1; shift',
|
|
356
|
+
...(spawnParts.length ? [' local spawn_part=0', ` case "$i" in ${spawnParts.join('|')}) spawn_part=1;; esac`] : []),
|
|
357
|
+
' stage "part-$i-start"',
|
|
358
|
+
...(isolateParts ? [
|
|
359
|
+
' local isolated; isolated=$(mktemp -d /tmp/gate-isolate.XXXXXX) || return 1',
|
|
360
|
+
' mkdir -p "$isolated/home" "$isolated/tmp" "$isolated/state" "$isolated/config" "$isolated/cache" "$isolated/data" "$isolated/xdg-state"',
|
|
361
|
+
' local source_repo="$PWD"',
|
|
362
|
+
' local -a run_env=(env HOME="$isolated/home" TMPDIR="$isolated/tmp" ELANOUS_STATE_DIR="$isolated/state" XDG_CONFIG_HOME="$isolated/config" XDG_CACHE_HOME="$isolated/cache" XDG_DATA_HOME="$isolated/data" XDG_STATE_HOME="$isolated/xdg-state")',
|
|
363
|
+
' stage "part-$i-setup-start"',
|
|
364
|
+
' local source_sha; source_sha=$(git rev-parse HEAD) || return 1',
|
|
365
|
+
' if ! "${run_env[@]}" git clone --shared --quiet --no-checkout "$source_repo" "$isolated/repo" > "$O/part-$i.setup.log" 2>&1 || ! "${run_env[@]}" git -C "$isolated/repo" checkout --detach --quiet "$source_sha" >> "$O/part-$i.setup.log" 2>&1; then cat "$O/part-$i.setup.log" > "$O/part-$i.log"; rm -rf "$isolated"; return 1; fi',
|
|
366
|
+
' ln -s "$source_repo/node_modules" "$isolated/repo/node_modules"',
|
|
367
|
+
' if [ -d "$source_repo/apps/pwa/node_modules" ]; then ln -s "$source_repo/apps/pwa/node_modules" "$isolated/repo/apps/pwa/node_modules"; fi',
|
|
368
|
+
' cd "$isolated/repo" || return 1',
|
|
369
|
+
' stage "part-$i-setup-end"',
|
|
370
|
+
] : [' local -a run_env=()']),
|
|
296
371
|
' local left=$(( gate_end - $(date +%s) ))',
|
|
297
372
|
' if [ "$left" -le 0 ]; then echo "$i" > "$O/shard.timeout"; return 1; fi',
|
|
298
|
-
'
|
|
373
|
+
...(spawnParts.length ? [' if [ "$spawn_part" -eq 1 ] && [ "$left" -gt 120 ]; then left=120; fi'] : []),
|
|
374
|
+
' stage "part-$i-test-start"',
|
|
375
|
+
...(spawnParts.length ? [' local test_start; test_start=$(date +%s)'] : []),
|
|
376
|
+
' "${run_env[@]}" timeout -k 10 "$left" "$@" --reporter=junit --reporter-outfile="$O/part-$i.junit.xml" > "$O/part-$i.log" 2>&1',
|
|
299
377
|
' local prc=$?',
|
|
378
|
+
...(spawnParts.length ? [' local test_end; test_end=$(date +%s)'] : []),
|
|
379
|
+
' stage "part-$i-test-end"',
|
|
380
|
+
...(isolateParts ? [' stage "part-$i-cleanup-start"', ' cd "$source_repo"', ' rm -rf "$isolated"', ' stage "part-$i-cleanup-end"'] : []),
|
|
381
|
+
' stage "part-$i-end"',
|
|
382
|
+
...(spawnParts.length ? [' if [ "$spawn_part" -eq 1 ] && { [ "$prc" -eq 124 ] || { [ "$test_end" -ge "$(( test_start + left ))" ] && { [ "$prc" -eq 137 ] || [ "$prc" -eq 143 ]; }; }; }; then echo "$i" > "$O/shard.timeout"; return 1; fi'] : []),
|
|
300
383
|
' if [ "$(date +%s)" -ge "$gate_end" ] && { [ "$prc" -eq 124 ] || [ "$prc" -eq 137 ] || [ "$prc" -eq 143 ]; }; then echo "$i" > "$O/shard.timeout"; return 1; fi',
|
|
301
384
|
' echo "$prc" > "$O/part-$i.rc"',
|
|
302
385
|
'}',
|
|
303
386
|
'if cd .. && cd repo; then',
|
|
387
|
+
' stage deps-start',
|
|
304
388
|
// GATE-IMAGE-DEPS: link the image's baked node_modules when the lockfiles match; only a miss installs (behind a slot).
|
|
305
389
|
...gateDepsInstallScript({ log: '"$O/install.log"', installSlots: o.installSlots, ...(o.depsDir ? { depsDir: o.depsDir } : {}) }).split('\n').map((line) => ` ${line}`),
|
|
306
390
|
' echo "$irc" > "$O/install.rc"',
|
|
307
|
-
|
|
391
|
+
' stage deps-end',
|
|
392
|
+
` if [ "$irc" -eq 0 ]; then stage test-start; ${width > 1 ? 'pids=(); ' : ''}${run || 'true'}; stage test-end; fi`,
|
|
308
393
|
'else echo 5 > "$O/install.rc"; fi',
|
|
394
|
+
'stage shell-end',
|
|
309
395
|
'true',
|
|
310
396
|
].join('\n');
|
|
311
397
|
}
|
|
@@ -353,6 +439,50 @@ export function readShardLiveLog(text: string): ShardLiveLog {
|
|
|
353
439
|
}
|
|
354
440
|
return { checkout, ...(beat ? { beat } : {}) };
|
|
355
441
|
}
|
|
442
|
+
|
|
443
|
+
/** Kubernetes clock boundaries for scheduling and init. Child start → shell-start includes source checkout and setup. */
|
|
444
|
+
export function readGatePodLifecycle(text: string): Record<string, string> {
|
|
445
|
+
try {
|
|
446
|
+
const pod = (JSON.parse(text) as { items?: Array<{ metadata?: { creationTimestamp?: string }; status?: {
|
|
447
|
+
conditions?: Array<{ type?: string; lastTransitionTime?: string }>;
|
|
448
|
+
initContainerStatuses?: Array<{ name?: string; state?: { terminated?: { startedAt?: string; finishedAt?: string } } }>;
|
|
449
|
+
containerStatuses?: Array<{ name?: string; state?: { terminated?: { startedAt?: string; finishedAt?: string } } }>;
|
|
450
|
+
} }> }).items?.[0];
|
|
451
|
+
if (!pod) return {};
|
|
452
|
+
const out: Record<string, string> = {};
|
|
453
|
+
const put = (key: string, value?: string) => { if (value && Number.isFinite(Date.parse(value))) out[key] = value; };
|
|
454
|
+
put('podCreated', pod.metadata?.creationTimestamp);
|
|
455
|
+
put('podScheduled', pod.status?.conditions?.find((item) => item.type === 'PodScheduled')?.lastTransitionTime);
|
|
456
|
+
for (const item of pod.status?.initContainerStatuses ?? []) {
|
|
457
|
+
if (!item.name || !/^[a-z0-9-]+$/.test(item.name)) continue;
|
|
458
|
+
put(`init-${item.name}-start`, item.state?.terminated?.startedAt);
|
|
459
|
+
put(`init-${item.name}-end`, item.state?.terminated?.finishedAt);
|
|
460
|
+
}
|
|
461
|
+
const child = pod.status?.containerStatuses?.find((item) => item.name === 'child');
|
|
462
|
+
put('childStart', child?.state?.terminated?.startedAt);
|
|
463
|
+
put('childEnd', child?.state?.terminated?.finishedAt);
|
|
464
|
+
return out;
|
|
465
|
+
} catch { return {}; }
|
|
466
|
+
}
|
|
467
|
+
/** Seconds with at least one isolated test process running; setup gaps between parallel groups remain overhead. */
|
|
468
|
+
export function gateBatchTestWallSeconds(stages: Record<string, number> | undefined, partCount: number): number | undefined {
|
|
469
|
+
if (!stages || partCount < 1) return undefined;
|
|
470
|
+
const spans: Array<[number, number]> = [];
|
|
471
|
+
for (let index = 0; index < partCount; index++) {
|
|
472
|
+
const start = stages[`part-${index}-test-start`];
|
|
473
|
+
const end = stages[`part-${index}-test-end`];
|
|
474
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || end! < start!) return undefined;
|
|
475
|
+
spans.push([start!, end!]);
|
|
476
|
+
}
|
|
477
|
+
spans.sort((a, b) => a[0] - b[0]);
|
|
478
|
+
let covered = 0;
|
|
479
|
+
let [from, to] = spans[0]!;
|
|
480
|
+
for (const [start, end] of spans.slice(1)) {
|
|
481
|
+
if (start <= to) to = Math.max(to, end);
|
|
482
|
+
else { covered += to - from; from = start; to = end; }
|
|
483
|
+
}
|
|
484
|
+
return covered + to - from;
|
|
485
|
+
}
|
|
356
486
|
/**
|
|
357
487
|
* GATE-POD-STALL-FAILFAST — watches one running shard Job. Armed once the source checkout line is seen; disarmed for good
|
|
358
488
|
* the moment any test output exists (that shard then behaves exactly as before). Stalled = `stallSeconds` since the
|
|
@@ -643,6 +773,8 @@ class StalledPodShards extends Error {
|
|
|
643
773
|
|
|
644
774
|
/** Isolated re-runs (host stall re-run · cut and baseline re-checks) are time-limited; sweeps and installs keep their full time. */
|
|
645
775
|
export const GATE_ISOLATED_TIMEOUT_MS = 300_000;
|
|
776
|
+
/** Bound the final serial host phase until isolated checks can run safely in parallel on separate worktrees. */
|
|
777
|
+
export const GATE_HOST_RECHECK_FILE_LIMIT = 32;
|
|
646
778
|
/** GATE-INTRO-RECHECK (0.2.19) — the per-test limit of the one re-run given to a «timed out after 5000ms» new failure.
|
|
647
779
|
* 0.2.18: both first-gate introductions were 5 s limits missed at 6.04/6.06 s, and they alone cost an 84-minute re-gate. */
|
|
648
780
|
export const GATE_TIMEOUT_RECHECK_MS = 60_000;
|
|
@@ -805,11 +937,12 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
805
937
|
// The TD1 measurements predate the fixture-only gate; do not isolate those five files at their old 2–13 GB peaks.
|
|
806
938
|
const fixtureOnly = new Set([...GATE_NIGHTLY_AUDITS, 'test/guardian/dispatch-surface-contract.test.ts', 'test/user-config-mcp.test.ts']);
|
|
807
939
|
const heavy = new Set(assignable.filter((file) => !fixtureOnly.has(file) && (memory.get(file) ?? 0) >= POD_HEAVY_FILE_MB));
|
|
808
|
-
const
|
|
940
|
+
const { shards: lightShards, spawnFiles: spawnLane, spawnShardCount } = planGateSpawnSeparatedShards(assignable.filter((file) => !heavy.has(file)), durations, shardCount);
|
|
809
941
|
const shards = [
|
|
810
|
-
...
|
|
942
|
+
...lightShards,
|
|
811
943
|
...[...heavy].map((file) => ({ files: [file], plannedSeconds: durations.get(file) ?? 0 })),
|
|
812
944
|
];
|
|
945
|
+
if (spawnLane.length) debug.log('release-loop.gate', 'pod-spawn-lane', { files: spawnLane, shards: spawnShardCount });
|
|
813
946
|
logPlanWalltime(planWall, shards);
|
|
814
947
|
if (heavy.size) debug.log('release-loop.gate', 'pod-heavy-alone', { files: [...heavy], thresholdMb: POD_HEAVY_FILE_MB, memoryLimit: POD_ISOLATION_MEMORY_LIMIT });
|
|
815
948
|
const known = assignable.filter((file) => durations.has(file)).length;
|
|
@@ -820,7 +953,10 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
820
953
|
});
|
|
821
954
|
// GATE-SPEED A3①: shard Pod CPU (`release.loop.gatePodCpu` · default request 1 / limit 4) — shards × request must fit the pool.
|
|
822
955
|
const cpu = resolvePodCpu(pod.cpu);
|
|
956
|
+
// Only evidence-backed opt-in files reach this batch; every other file retains its own Pod below.
|
|
957
|
+
const batchWidth = Math.max(1, Math.min(4, Math.floor(podCpuMillis(cpu.limit)! / 1000)));
|
|
823
958
|
debug.log('release-loop.gate', 'shard-resources', { shards: shards.length, cpuRequest: cpu.request, cpuLimit: cpu.limit, cpuSource: pod.cpu ? 'config' : 'default' });
|
|
959
|
+
debug.log('release-loop.gate', 'isolation-batch-width', { batchWidth, cpuLimit: cpu.limit });
|
|
824
960
|
const ready = poolOverride ? undefined : checkPodPool(parsePodPool(pod.pool), defaultKubectl);
|
|
825
961
|
if (ready && !ready.ok) throw new Error('pod command: 풀의 노드가 하나도 준비되지 않았다');
|
|
826
962
|
const poolScheduler = poolOverride ?? new PodPoolScheduler(ready!.ready);
|
|
@@ -893,6 +1029,8 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
893
1029
|
// 깊이 끝 OOM 조각은 «조각 전체»를 파일 하나씩 격리한다 — 상한 40 이면 나머지가 못 잰 채 남았다(09-30 ① 7번 조각 110 파일).
|
|
894
1030
|
const isolationRemaining = shards.map((item) => item.files.length);
|
|
895
1031
|
const knownFailures = new Set(pod.knownFailures ?? []);
|
|
1032
|
+
const batchSafe = loadGateBatchSafe(repo);
|
|
1033
|
+
debug.log('release-loop.gate', 'batch-safe-opt-in', { files: batchSafe.size });
|
|
896
1034
|
const estimated = (paths: string[]) => paths.reduce((sum, file) => {
|
|
897
1035
|
const value = durations.get(file);
|
|
898
1036
|
return sum + (value !== undefined && Number.isFinite(value) && value >= 0 ? value : unknownSeconds);
|
|
@@ -933,13 +1071,13 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
933
1071
|
},
|
|
934
1072
|
release: (member: Parameters<PodPoolScheduler['release']>[0]) => poolScheduler.release(member),
|
|
935
1073
|
}) as unknown as PodPoolScheduler;
|
|
936
|
-
const runShard = async (paths: string[], shard: number, depth = 0, branch = '', retriedNoOutput = false, fresh = pod.freshShards === true, parentAttempt?: string, hostRetries = 0): Promise<{ runs: ShardRun[]; stalled: NonNullable<GateResult['stalledShards']> }> => {
|
|
1074
|
+
const runShard = async (paths: string[], shard: number, depth = 0, branch = '', retriedNoOutput = false, fresh = pod.freshShards === true, parentAttempt?: string, hostRetries = 0, batchIsolation = false): Promise<{ runs: ShardRun[]; stalled: NonNullable<GateResult['stalledShards']> }> => {
|
|
937
1075
|
// 파일별 소요는 junit 으로 남긴다 — 콘솔 요약(판정 원천)은 그대로이고, 느린 시험 목록(K10 D4)·계층 분리(D2)의 자가 된다.
|
|
938
1076
|
// GATE-TIMEOUT-HEAL: parts (each with its own junit) under a soft deadline inside this shard's own Job deadline.
|
|
939
|
-
const shardDeadline = shardDeadlineSeconds(estimated(paths), deadlineSeconds);
|
|
940
|
-
const parts = shardParts(paths, (file) => estimated([file]));
|
|
941
|
-
const memoryLimit = paths.length === 1 && (depth >= 1 || heavy.has(paths[0]!)) ? POD_ISOLATION_MEMORY_LIMIT : undefined;
|
|
942
|
-
const shellOptions = { parts, cdpPatterns, softSeconds: softDeadlineSeconds(shardDeadline), cachePrefix, installSlots };
|
|
1077
|
+
const shardDeadline = shardDeadlineSeconds(batchIsolation ? Math.max(...paths.map((file) => estimated([file]))) * Math.ceil(paths.length / batchWidth) : estimated(paths), deadlineSeconds);
|
|
1078
|
+
const parts = batchIsolation ? paths.map((file) => [file]) : shardParts(paths, (file) => estimated([file]));
|
|
1079
|
+
const memoryLimit = batchIsolation || paths.length === 1 && (depth >= 1 || heavy.has(paths[0]!)) ? POD_ISOLATION_MEMORY_LIMIT : undefined;
|
|
1080
|
+
const shellOptions = { parts, cdpPatterns, softSeconds: softDeadlineSeconds(shardDeadline), cachePrefix, installSlots, ...(batchIsolation ? { parallelParts: batchWidth } : {}) };
|
|
943
1081
|
const keyShell = gateShardShell(shellOptions);
|
|
944
1082
|
// The stall beat only reports; runKey keeps hashing the shell without it, so earlier results stay reusable.
|
|
945
1083
|
const podShell = stallWatch ? gateShardShell({ ...shellOptions, beatSeconds: gateStallBeatSeconds(stallSeconds) }) : keyShell;
|
|
@@ -947,9 +1085,15 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
947
1085
|
let start = Date.now();
|
|
948
1086
|
let admitted: GateAdmissionResult | undefined;
|
|
949
1087
|
let output: string | undefined;
|
|
1088
|
+
const phaseTimes: Record<string, string> = {};
|
|
1089
|
+
const stamp = (phase: string) => { phaseTimes[phase] = new Date().toISOString(); };
|
|
1090
|
+
let podStages: Record<string, number> | undefined;
|
|
1091
|
+
let podLifecycle: Record<string, string> | undefined;
|
|
1092
|
+
let batchArtifactsComplete = true;
|
|
950
1093
|
let junit: string | undefined;
|
|
951
1094
|
let rc: number | undefined;
|
|
952
1095
|
let jobExitCode: number | undefined;
|
|
1096
|
+
let batchArtifactDir: string | undefined;
|
|
953
1097
|
let lastFile: string | undefined;
|
|
954
1098
|
let jobFailed = false;
|
|
955
1099
|
let jobError = '';
|
|
@@ -965,7 +1109,8 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
965
1109
|
// cap chunks for a big root shard · halves otherwise · none for one file).
|
|
966
1110
|
// Isolation follows only while this shard's isolation budget still covers every file (review r7) — the budget is the
|
|
967
1111
|
// shard's file count and isolations take disjoint files, so this holds unless the plan changed; if not, run the Pod.
|
|
968
|
-
const splitChild = depth === 2 && paths.length > 1 ? (isolationRemaining[shard]! >= paths.length
|
|
1112
|
+
const splitChild = depth === 2 && paths.length > 1 ? (isolationRemaining[shard]! >= paths.length
|
|
1113
|
+
? batchIsolation || !paths.every((file) => batchSafe.has(file)) ? '-file-0' : '-batch' : undefined)
|
|
969
1114
|
: depth < 2 && paths.length > 1 ? (depth === 0 && paths.length > POD_SHARD_FILE_CAP ? '-c0' : '-0') : undefined;
|
|
970
1115
|
const found = !fresh && logDir ? previousShardDescent(logDir, `pod-${shard}${branch}`, commit, paths, runKey, parentAttempt, splitChild) : undefined;
|
|
971
1116
|
const descent = found?.way;
|
|
@@ -975,7 +1120,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
975
1120
|
if (descent === 'retry') {
|
|
976
1121
|
// «descend» is only the way down; «resumed» comes from each leaf whose log reads cleanly (review r4).
|
|
977
1122
|
debug.log('release-loop.gate', 'pod-shard-descend', { shard, branch, files: paths.length, via: 'retry' });
|
|
978
|
-
return runShard(paths, shard, depth, `${branch}-retry`, true, undefined, attemptId);
|
|
1123
|
+
return runShard(paths, shard, depth, `${branch}-retry`, true, undefined, attemptId, hostRetries, batchIsolation);
|
|
979
1124
|
}
|
|
980
1125
|
if (descent === 'split') {
|
|
981
1126
|
output = undefined;
|
|
@@ -987,7 +1132,9 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
987
1132
|
jobExitCode = 0;
|
|
988
1133
|
lastFile = lastStartedTestFile(output, paths);
|
|
989
1134
|
} else try {
|
|
1135
|
+
stamp('admissionStart');
|
|
990
1136
|
admitted = await admitGateShard(gateAdmission, { waitMs: admissionWaitSeconds * 1000, onWait: (reason) => markShard(shard, { waitReason: reason }) });
|
|
1137
|
+
stamp('admissionEnd');
|
|
991
1138
|
if (admitted.outcome !== 'off') debug.log('release-loop.gate', 'shard-admission', { shard, branch, outcome: admitted.outcome, waitedMs: admitted.waitedMs, ...(admitted.reason ? { reason: admitted.reason } : {}) },
|
|
992
1139
|
admitted.outcome === 'granted' ? undefined : { level: 'warn' });
|
|
993
1140
|
// The admission wait is not the shard's run time: deadline/timeout judgment and the measured duration start here.
|
|
@@ -1005,7 +1152,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1005
1152
|
} }) : undefined;
|
|
1006
1153
|
const placed = admitted.release;
|
|
1007
1154
|
const job = await podCommand({
|
|
1008
|
-
pool: pod.pool, poolScheduler: trackedPool(shard, depth > 0 || branch !== '', (member) => { jobContext = member.context; placedOn = member.context; placed(); }), clone: true, source: { kind: 'commit', sha: commit }, deadlineSeconds: shardDeadline,
|
|
1155
|
+
pool: pod.pool, poolScheduler: trackedPool(shard, depth > 0 || branch !== '', (member) => { stamp('poolPlaced'); jobContext = member.context; placedOn = member.context; placed(); }), clone: true, source: { kind: 'commit', sha: commit }, deadlineSeconds: shardDeadline,
|
|
1009
1156
|
...(multiHost ? { statusUnreadableMs: memberUnreadableSeconds * 1000 } : {}),
|
|
1010
1157
|
...(bunCache ? { bunCache } : {}),
|
|
1011
1158
|
...(installSlots > 0 ? { installSlots: POD_INSTALL_SLOTS_HOST_PATH } : {}),
|
|
@@ -1015,8 +1162,30 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1015
1162
|
name: jobName,
|
|
1016
1163
|
command: ['bash', '-lc', podShell],
|
|
1017
1164
|
}).finally(() => watch?.stop());
|
|
1165
|
+
stamp('jobReturned');
|
|
1166
|
+
if (!poolOverride && placedOn) {
|
|
1167
|
+
try {
|
|
1168
|
+
const got = defaultKubectl(['--context', placedOn, '-n', 'elanous-test', 'get', 'pods', '-l', `job-name=${job.job}`, '-o', 'json']);
|
|
1169
|
+
if (got.status === 0) podLifecycle = readGatePodLifecycle(got.stdout);
|
|
1170
|
+
} catch { /* timings never change the verdict */ }
|
|
1171
|
+
}
|
|
1018
1172
|
jobExitCode = job.exitCode;
|
|
1173
|
+
batchArtifactDir = job.artifactsDir;
|
|
1019
1174
|
const read = readShardArtifacts(job.artifactsDir, parts);
|
|
1175
|
+
batchArtifactsComplete = !batchIsolation || parts.every((_, index) =>
|
|
1176
|
+
['rc', 'log', 'junit.xml'].every((suffix) => existsSync(join(job.artifactsDir, `part-${index}.${suffix}`))));
|
|
1177
|
+
const stagesPath = join(job.artifactsDir, 'stages.tsv');
|
|
1178
|
+
if (existsSync(stagesPath)) {
|
|
1179
|
+
try {
|
|
1180
|
+
podStages = Object.fromEntries(readFileSync(stagesPath, 'utf8').split(/\r?\n/).flatMap((line) => {
|
|
1181
|
+
const match = /^(\d+)\t([a-z0-9-]+)$/.exec(line);
|
|
1182
|
+
return match ? [[match[2]!, Number(match[1])]] : [];
|
|
1183
|
+
}));
|
|
1184
|
+
} catch (error) {
|
|
1185
|
+
debug.log('release-loop.gate', 'pod-stages-unreadable', { shard, branch, error: String(error) }, { level: 'warn' });
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
stamp('artifactsRead');
|
|
1020
1189
|
({ output, junit, rc, timedOut, finished, unfinishedLog, exitCodeOnly } = read);
|
|
1021
1190
|
// A stall counts only when the Job also left no verdict (a failed stop can let a late shard finish after all).
|
|
1022
1191
|
const stalledAt = watch?.stalled();
|
|
@@ -1037,7 +1206,12 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1037
1206
|
const durationMs = Date.now() - start;
|
|
1038
1207
|
const fileSeconds = junit === undefined ? undefined : Object.fromEntries(readJunitFileSeconds(junit));
|
|
1039
1208
|
const testSeconds = fileSeconds === undefined ? undefined : Object.values(fileSeconds).reduce((sum, seconds) => sum + seconds, 0);
|
|
1040
|
-
const
|
|
1209
|
+
const testWallSeconds = batchIsolation
|
|
1210
|
+
? gateBatchTestWallSeconds(podStages, parts.length)
|
|
1211
|
+
: podStages?.['test-start'] !== undefined && podStages?.['test-end'] !== undefined
|
|
1212
|
+
? Math.max(0, podStages['test-end'] - podStages['test-start']) : undefined;
|
|
1213
|
+
const podOverheadSeconds = testWallSeconds !== undefined || (!batchIsolation && testSeconds !== undefined)
|
|
1214
|
+
? Math.max(0, durationMs / 1000 - (testWallSeconds ?? testSeconds!)) : undefined;
|
|
1041
1215
|
// A reused shard keeps its files as they are — rewriting would replace the measured durationMs the next release plans by.
|
|
1042
1216
|
if (logDir && !reused && !descent) {
|
|
1043
1217
|
mkdirSync(logDir, { recursive: true });
|
|
@@ -1055,7 +1229,10 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1055
1229
|
else rmSync(unfinishedDestination, { force: true });
|
|
1056
1230
|
writeFileSync(join(logDir, `pod-${shard}${branch}.json`), JSON.stringify({ durationMs, rc: rc ?? null,
|
|
1057
1231
|
...(stall ? { reason: 'stall', finishedFiles: 0, stallSeconds: stall.seconds, sinceCheckoutMs: stall.sinceCheckoutMs } : timedOut ? { reason: 'timeout', finishedFiles: finished.length } : {}), files: paths, plannedSeconds: estimated(paths), deadlineSeconds: shardDeadline, shardCount: shards.length, commit, runKey, attemptId, ...(parentAttempt ? { parentAttempt } : {}), jobExitCode: jobExitCode ?? null, jobFailed, ...(placedOn ? { host: placedOn } : {}),
|
|
1058
|
-
...(
|
|
1232
|
+
...(Object.keys(phaseTimes).length ? { phaseTimes } : {}), ...(podStages ? { podStages } : {}),
|
|
1233
|
+
...(podLifecycle && Object.keys(podLifecycle).length ? { podLifecycle } : {}),
|
|
1234
|
+
...(fileSeconds !== undefined ? { fileSeconds, testSeconds } : {}), ...(testWallSeconds !== undefined ? { testWallSeconds } : {}),
|
|
1235
|
+
...(podOverheadSeconds !== undefined ? { podOverheadSeconds } : {}),
|
|
1059
1236
|
...(admitted && admitted.outcome !== 'off' ? { admission: admitted.outcome, admissionWaitMs: admitted.waitedMs } : {}) }) + '\n', { mode: 0o600 });
|
|
1060
1237
|
}
|
|
1061
1238
|
const installSeconds = parseInstallSeconds(output ?? '');
|
|
@@ -1076,6 +1253,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1076
1253
|
// GATE-POD-STALL-FAILFAST: stopped with no test output — the existing «no output» reason (one fresh retry).
|
|
1077
1254
|
else if (stall) reason = 'no-output';
|
|
1078
1255
|
else if (timedOut) reason = 'timeout';
|
|
1256
|
+
else if (batchIsolation && !batchArtifactsComplete) reason = 'incomplete';
|
|
1079
1257
|
// GATE-POD-STALL-FAILFAST (OP): a complete summary with 0 fail · 0 error but an exit other than 0/1 is an exit-code
|
|
1080
1258
|
// anomaly (0.2.23: a test left process.exitCode set → «0 fail · rc 3» read as a stalled shard). Same split/isolation
|
|
1081
1259
|
// path as job-failed — only the label differs, so the verdict names the file instead of saying «stalled».
|
|
@@ -1114,14 +1292,14 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1114
1292
|
// A reused result that the current tool cannot read cleanly is not a verdict: run that shard for real.
|
|
1115
1293
|
if (reused && reason) {
|
|
1116
1294
|
debug.log('release-loop.gate', 'pod-shard-resume-miss', { shard, branch, reason });
|
|
1117
|
-
return runShard(paths, shard, depth, branch, retriedNoOutput, true, parentAttempt);
|
|
1295
|
+
return runShard(paths, shard, depth, branch, retriedNoOutput, true, parentAttempt, hostRetries, batchIsolation);
|
|
1118
1296
|
}
|
|
1119
1297
|
// GATE-MULTI-HOST: the Job failed because its member is gone (launch failed · status unreadable) — not the shard's
|
|
1120
1298
|
// fault, so no split: the same files run again on the members left. Bounded by the roster size.
|
|
1121
1299
|
if (jobFailed && placedOn && hostRetries < poolScheduler.members.length && memberLost(placedOn, shard, jobError)
|
|
1122
1300
|
&& poolScheduler.members.some((m) => !downMembers.has(m.context))) {
|
|
1123
1301
|
debug.log('release-loop.gate', 'pod-shard-host-retry', { shard, branch, files: paths.length, lostMember: placedOn, attempt: hostRetries + 1 }, { level: 'warn' });
|
|
1124
|
-
return runShard(paths, shard, depth, `${branch}-h${hostRetries}`, retriedNoOutput, undefined, attemptId, hostRetries + 1);
|
|
1302
|
+
return runShard(paths, shard, depth, `${branch}-h${hostRetries}`, retriedNoOutput, undefined, attemptId, hostRetries + 1, batchIsolation);
|
|
1125
1303
|
}
|
|
1126
1304
|
// GATE-TIMEOUT-HEAL ④: a Pod that died before any test ran (no outbox · jobExitCode≠0 — 0.2.19: isolated retries
|
|
1127
1305
|
// ending with exit 5, the source checkout) gets the same one fresh retry as «no output» instead of stalling the sweep.
|
|
@@ -1129,13 +1307,45 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1129
1307
|
const diedBeforeTests = reason === 'job-failed' && output === undefined && !jobFailed && jobExitCode !== undefined && jobExitCode !== 0
|
|
1130
1308
|
&& jobExitCode !== 137 && (paths.length === 1 || jobExitCode === 5);
|
|
1131
1309
|
if ((reason === 'no-output' || diedBeforeTests) && !retriedNoOutput) {
|
|
1132
|
-
const retry = await runShard(paths, shard, depth, `${branch}-retry`, true, undefined, attemptId);
|
|
1310
|
+
const retry = await runShard(paths, shard, depth, `${branch}-retry`, true, undefined, attemptId, hostRetries, batchIsolation);
|
|
1133
1311
|
debug.log('release-loop.gate', 'pod-shard-retry', { shard, files: paths, reason, outcome: retry.stalled[0]?.reason ?? 'ok' });
|
|
1134
1312
|
return retry;
|
|
1135
1313
|
}
|
|
1136
1314
|
// GATE-POD-STALL-FAILFAST: a stall after the fresh retry is unmeasured — no split, no isolation: smaller Pods on the
|
|
1137
1315
|
// same stalled node only re-measure the stall (10-10: retries and splits repeated it for 2h+).
|
|
1138
1316
|
if (stall && reason === 'no-output') return { runs: [], stalled: [{ shard, files: paths, reason, stall }] };
|
|
1317
|
+
// A shared Job may finish some file parts before another part loses its JUnit/log/rc. Keep every complete part's
|
|
1318
|
+
// original verdict, including a failure, and rerun only parts without the full artifact trio.
|
|
1319
|
+
if (batchIsolation && !batchArtifactsComplete && batchArtifactDir && reason) {
|
|
1320
|
+
const measured: ShardRun[] = [];
|
|
1321
|
+
const missing: string[] = [];
|
|
1322
|
+
parts.forEach((part, index) => {
|
|
1323
|
+
const file = part[0]!;
|
|
1324
|
+
try {
|
|
1325
|
+
const prefix = join(batchArtifactDir!, `part-${index}`);
|
|
1326
|
+
const partLog = readFileSync(`${prefix}.log`, 'utf8');
|
|
1327
|
+
const partJunit = readFileSync(`${prefix}.junit.xml`, 'utf8');
|
|
1328
|
+
const partRc = Number(readFileSync(`${prefix}.rc`, 'utf8').trim());
|
|
1329
|
+
const ranPart = lastMatch(partLog, /Ran (\d+) tests? across (\d+) files?/);
|
|
1330
|
+
const passPart = summaryCount(partLog, 'pass'), failPart = summaryCount(partLog, 'fail');
|
|
1331
|
+
const errorsPart = summaryCount(partLog, 'error');
|
|
1332
|
+
if ((partRc !== 0 && partRc !== 1) || !ranPart || Number(ranPart[2]) !== 1
|
|
1333
|
+
|| !Number.isFinite(passPart) || !Number.isFinite(failPart)) throw new Error('incomplete part');
|
|
1334
|
+
const attributed = failuresOf({ rc: partRc, output: partLog }, `pod-${shard} part-${index}`);
|
|
1335
|
+
if (attributed.failures.length !== failPart) throw new Error('unattributed part');
|
|
1336
|
+
measured.push({ output: partLog, rc: partRc, pass: passPart, fail: failPart,
|
|
1337
|
+
errors: Number.isFinite(errorsPart) ? errorsPart : 0, ran: Number(ranPart[1]), files: 1,
|
|
1338
|
+
passedIds: junitPassedIds(partJunit).map(podRelative) });
|
|
1339
|
+
} catch { missing.push(file); }
|
|
1340
|
+
});
|
|
1341
|
+
if (measured.length && missing.length) {
|
|
1342
|
+
debug.log('release-loop.gate', 'isolation-batch-partial', { shard, branch, measured: measured.length, missing });
|
|
1343
|
+
const children = await Promise.all(missing.map((file) => runShard([file], shard, depth + 1,
|
|
1344
|
+
`${branch}-file-${paths.indexOf(file)}`, false, undefined, attemptId)));
|
|
1345
|
+
return { runs: [...measured, ...children.flatMap((child) => child.runs)],
|
|
1346
|
+
stalled: children.flatMap((child) => child.stalled) };
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1139
1349
|
if (reason) {
|
|
1140
1350
|
// GATE-SKIP-KNOWN-RETRY — a shard whose summary is complete but whose failures could not be read back (unattributed)
|
|
1141
1351
|
// and whose every named failure is one the previous release already had is not re-run: re-running only re-measures
|
|
@@ -1160,7 +1370,8 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1160
1370
|
debug.log('release-loop.gate', 'retry-narrowed', { shard, before: paths.length, after: narrowed.length });
|
|
1161
1371
|
const kept = new Set(paths.filter((file) => !narrowed.includes(file)));
|
|
1162
1372
|
const passedIds = junitPassedIds(junit!).map(podRelative).filter((id) => { try { return kept.has(testFileOfId(id)); } catch { return false; } });
|
|
1163
|
-
const
|
|
1373
|
+
const directBatch = narrowed.length > 1 && narrowed.length <= POD_SHARD_FILE_CAP && narrowed.every((file) => batchSafe.has(file));
|
|
1374
|
+
const child = await runShard(narrowed, shard, directBatch ? 2 : depth, `${branch}-n`, false, undefined, attemptId, 0, directBatch);
|
|
1164
1375
|
return { runs: [{ output: '', rc: 0, pass: passedIds.length, fail: 0, errors: 0, ran: passedIds.length, files: kept.size, passedIds },
|
|
1165
1376
|
...child.runs], stalled: child.stalled };
|
|
1166
1377
|
}
|
|
@@ -1169,19 +1380,27 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1169
1380
|
if (depth === 2 && paths.length > 1) {
|
|
1170
1381
|
const count = Math.min(paths.length, isolationRemaining[shard]!);
|
|
1171
1382
|
isolationRemaining[shard]! -= count;
|
|
1172
|
-
const
|
|
1173
|
-
|
|
1383
|
+
const batchPaths = paths.slice(0, count);
|
|
1384
|
+
// The common path already ran every file as an isolated part in this same Job. Only an incomplete batch
|
|
1385
|
+
// needs a fresh per-file Pod, preserving the earlier fail-closed behavior when the shared Job died.
|
|
1386
|
+
const canBatch = batchPaths.length > 1 && batchPaths.every((file) => batchSafe.has(file));
|
|
1387
|
+
const batch = batchIsolation || !canBatch ? undefined : await runShard(batchPaths, shard, depth + 1, `${branch}-batch`, false, undefined, attemptId, 0, true);
|
|
1388
|
+
const retryPaths = !batch ? batchPaths : batchPaths.filter((file) => batch.stalled.some((item) => item.files.includes(file)));
|
|
1389
|
+
const children = !batch || batch.stalled.length ? await Promise.all(retryPaths.map(async (file) => {
|
|
1390
|
+
const child = await runShard([file], shard, depth + 1, `${branch}-file-${batchPaths.indexOf(file)}`, false, undefined, attemptId);
|
|
1174
1391
|
debug.log('release-loop.gate', 'pod-shard-isolate', { shard, file, outcome: child.stalled[0]?.reason ?? 'ok' });
|
|
1175
1392
|
return child;
|
|
1176
|
-
}));
|
|
1177
|
-
|
|
1393
|
+
})) : [batch];
|
|
1394
|
+
debug.log('release-loop.gate', 'pod-shard-isolate-batch', { shard, files: batchPaths.length, fallback: !batch || batch.stalled.length > 0 });
|
|
1395
|
+
return { runs: [...(batch?.stalled.length ? batch.runs : []), ...children.flatMap((child) => child.runs)], stalled: [
|
|
1178
1396
|
...children.flatMap((child) => child.stalled),
|
|
1397
|
+
...(batch?.stalled.filter((item) => item.files.some((file) => !retryPaths.includes(file))) ?? []),
|
|
1179
1398
|
...(paths.length > count ? [{ shard, files: paths.slice(count), reason,
|
|
1180
1399
|
...(lastFile ? { lastFile } : {}),
|
|
1181
1400
|
}] : []),
|
|
1182
1401
|
] };
|
|
1183
1402
|
}
|
|
1184
|
-
if (depth
|
|
1403
|
+
if (depth >= 2 || paths.length === 1) return { runs: [], stalled: [{ shard, files: paths, reason,
|
|
1185
1404
|
...(lastFile ? { lastFile } : {}),
|
|
1186
1405
|
...(reason === 'unattributed' ? { summaryFailures: fails, namedFailures, ...(unattributedDetail ? { detail: unattributedDetail } : {}) } : {}),
|
|
1187
1406
|
}] };
|
|
@@ -1191,14 +1410,19 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1191
1410
|
if (depth === 0 && paths.length > POD_SHARD_FILE_CAP) {
|
|
1192
1411
|
const chunks = Array.from({ length: Math.ceil(paths.length / POD_SHARD_FILE_CAP) }, (_, i) => paths.slice(i * POD_SHARD_FILE_CAP, (i + 1) * POD_SHARD_FILE_CAP));
|
|
1193
1412
|
debug.log('release-loop.gate', 'pod-shard-cap-split', { shard, files: paths.length, chunks: chunks.length, cap: POD_SHARD_FILE_CAP, reason });
|
|
1194
|
-
|
|
1413
|
+
// Preserve the proven cap-chunk Job even with an empty opt-in list. Batch isolation is only for chunks whose
|
|
1414
|
+
// every file is explicitly safe; a regular cap chunk keeps the preexisting one-process Pod verdict.
|
|
1415
|
+
const children = await Promise.all(chunks.map((chunk, i) => runShard(chunk, shard, 2, `${branch}-c${i}`, false, undefined, attemptId, 0,
|
|
1416
|
+
chunk.length > 1 && chunk.every((file) => batchSafe.has(file)))));
|
|
1195
1417
|
return { runs: children.flatMap((child) => child.runs), stalled: children.flatMap((child) => child.stalled) };
|
|
1196
1418
|
}
|
|
1197
1419
|
debug.log('release-loop.gate', 'pod-shard-split', { shard, depth, files: paths, reason });
|
|
1198
1420
|
const middle = Math.ceil(paths.length / 2);
|
|
1199
1421
|
const children = await Promise.all([
|
|
1200
|
-
runShard(paths.slice(0, middle), shard, depth + 1, `${branch}-0`, false, undefined, attemptId
|
|
1201
|
-
|
|
1422
|
+
runShard(paths.slice(0, middle), shard, depth + 1, `${branch}-0`, false, undefined, attemptId, 0,
|
|
1423
|
+
depth + 1 === 2 && middle > 1 && paths.slice(0, middle).every((file) => batchSafe.has(file))),
|
|
1424
|
+
runShard(paths.slice(middle), shard, depth + 1, `${branch}-1`, false, undefined, attemptId, 0,
|
|
1425
|
+
depth + 1 === 2 && paths.length - middle > 1 && paths.slice(middle).every((file) => batchSafe.has(file))),
|
|
1202
1426
|
]);
|
|
1203
1427
|
return { runs: children.flatMap((child) => child.runs), stalled: children.flatMap((child) => child.stalled) };
|
|
1204
1428
|
}
|
|
@@ -1431,6 +1655,8 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
1431
1655
|
};
|
|
1432
1656
|
const repo = resolve(opts.repo ?? process.cwd());
|
|
1433
1657
|
const result: GateResult = { outcome: 'error', commit: opts.commit, introduced: [], preexisting: 0, fixed: 0, knownEnv: 0, knownEnvCleared: [], stalledEnv: [], rcNonzeroNoFailures: [], durationMs: 0 };
|
|
1658
|
+
const preexistingIds: string[] = [];
|
|
1659
|
+
let knownEnvIds: string[] = [];
|
|
1434
1660
|
let work: string | undefined;
|
|
1435
1661
|
let baseSnapshot = false;
|
|
1436
1662
|
let cutFailures: SweepFailures | undefined;
|
|
@@ -1598,11 +1824,13 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
1598
1824
|
const baselineIds = [...baseline.failures, ...baseline.errors];
|
|
1599
1825
|
const countedCut = splitKnownEnv(cutIds, known);
|
|
1600
1826
|
const countedBaseline = splitKnownEnv(baselineIds, known);
|
|
1601
|
-
|
|
1827
|
+
knownEnvIds = countedCut.knownEnv;
|
|
1828
|
+
result.knownEnv = knownEnvIds.length;
|
|
1602
1829
|
result.knownEnvCleared = [...new Set(cutRun.passedIds ?? [])].filter((id) => known.has(id) && !cutIds.includes(id) && !cutRun.stalledEnv?.some(({ file }) => fileOf(id) === file)).sort();
|
|
1603
1830
|
const diff = diffFailures(countedCut.counted, countedBaseline.counted);
|
|
1604
1831
|
result.fixed = result.baselineDeferred ? null : diff.fixed.length;
|
|
1605
|
-
|
|
1832
|
+
preexistingIds.push(...diff.common);
|
|
1833
|
+
result.preexisting = preexistingIds.length;
|
|
1606
1834
|
// GATE-BASELINE-CACHE — only the files whose new failures reproduce in isolation reach the baseline, and each
|
|
1607
1835
|
// (commit, file, host) is run at most once across gates: this gate's cut results also seed the next gate's baseline.
|
|
1608
1836
|
const cacheHost = opts.remote ?? 'local';
|
|
@@ -1619,7 +1847,15 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
1619
1847
|
try { saveBaselineFileCache(ledger, row.commit, [row]); }
|
|
1620
1848
|
catch (error) { debug.log('release.gate', 'baseline-cache-write-failed', { commit: row.commit, file: row.file, error: String(error) }); }
|
|
1621
1849
|
};
|
|
1622
|
-
|
|
1850
|
+
const recheckFiles = [...newFailureFiles].sort();
|
|
1851
|
+
const unmeasuredRecheckFiles = recheckFiles.slice(GATE_HOST_RECHECK_FILE_LIMIT);
|
|
1852
|
+
if (unmeasuredRecheckFiles.length) debug.log('release-loop.gate', 'isolated-recheck-capped',
|
|
1853
|
+
{ limit: GATE_HOST_RECHECK_FILE_LIMIT, total: recheckFiles.length, unmeasured: unmeasuredRecheckFiles });
|
|
1854
|
+
const recheckStartedAt = Date.now();
|
|
1855
|
+
const selectedRechecks = recheckFiles.slice(0, GATE_HOST_RECHECK_FILE_LIMIT);
|
|
1856
|
+
for (const [index, file] of selectedRechecks.entries()) {
|
|
1857
|
+
debug.log('release-loop.gate', 'isolated-recheck-progress',
|
|
1858
|
+
{ index: index + 1, total: selectedRechecks.length, file, elapsedSec: Math.round((Date.now() - recheckStartedAt) / 1000) });
|
|
1623
1859
|
// 0.2.15: the cut re-check of a new-failure file (InsidePage.test.tsx · headless Chrome) hung for good — limit it like the host re-run.
|
|
1624
1860
|
const cutCheck = cutHostFailed.has(file) ? undefined : await runner.command('bun', ['run', 'test:deterministic', asPath(file)], cutTree, GATE_ISOLATED_TIMEOUT_MS);
|
|
1625
1861
|
if (cutCheck?.timedOut) {
|
|
@@ -1703,13 +1939,25 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
1703
1939
|
debug.log('release-loop.gate', 'isolated-rc-nonzero-no-failures', { file, label: 'cut isolated', rc: rcOnly.rc, pass: rcOnly.pass, baseline });
|
|
1704
1940
|
}
|
|
1705
1941
|
for (const id of candidates) {
|
|
1706
|
-
if (oldFailures.has(id))
|
|
1942
|
+
if (oldFailures.has(id)) preexistingIds.push(id);
|
|
1707
1943
|
else result.introduced.push(id);
|
|
1708
1944
|
}
|
|
1709
1945
|
}
|
|
1946
|
+
result.preexisting = preexistingIds.length;
|
|
1710
1947
|
result.baselineCache = cacheStats;
|
|
1711
1948
|
debug.log('release.gate', 'baseline-cache', { hits: cacheStats.hits, misses: cacheStats.misses, commit: baseSha });
|
|
1712
|
-
|
|
1949
|
+
if (unmeasuredRecheckFiles.length) {
|
|
1950
|
+
result.unmeasuredRecheckFiles = unmeasuredRecheckFiles;
|
|
1951
|
+
result.outcome = 'error';
|
|
1952
|
+
result.error = `isolated recheck unmeasured: ${unmeasuredRecheckFiles.length} files exceed limit ${GATE_HOST_RECHECK_FILE_LIMIT}: ${unmeasuredRecheckFiles.join(', ')}`;
|
|
1953
|
+
} else result.outcome = result.introduced.length ? 'regression' : 'ok';
|
|
1954
|
+
if (opts.rehearsalTui) {
|
|
1955
|
+
result.rehearsalTui = await executeTuiNodeCheck(opts.commit, repo, runner.localCommand);
|
|
1956
|
+
if (result.rehearsalTui.outcome !== 'ok') {
|
|
1957
|
+
result.outcome = 'error';
|
|
1958
|
+
result.error = result.rehearsalTui.detail;
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1713
1961
|
} catch (error) {
|
|
1714
1962
|
result.error = error instanceof Error ? error.message : String(error);
|
|
1715
1963
|
if (error instanceof StalledPodShards) {
|
|
@@ -1751,7 +1999,53 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
1751
1999
|
result.error = `baseline persistence: ${String(error)}`;
|
|
1752
2000
|
}
|
|
1753
2001
|
}
|
|
1754
|
-
|
|
2002
|
+
{
|
|
2003
|
+
const ids = {
|
|
2004
|
+
introduced: [...result.introduced].sort(),
|
|
2005
|
+
knownEnv: [...knownEnvIds].sort(),
|
|
2006
|
+
preexisting: [...preexistingIds].sort(),
|
|
2007
|
+
loadFlaky: [...(result.loadFlaky ?? [])].sort(),
|
|
2008
|
+
...(result.unmeasuredRecheckFiles?.length ? { unmeasuredRecheckFiles: [...result.unmeasuredRecheckFiles].sort() } : {}),
|
|
2009
|
+
};
|
|
2010
|
+
try {
|
|
2011
|
+
for (const location of roots) {
|
|
2012
|
+
const path = verdictFile(location, opts.version);
|
|
2013
|
+
if (existsSync(path)) readFileSync(path, 'utf8');
|
|
2014
|
+
}
|
|
2015
|
+
for (const location of roots) {
|
|
2016
|
+
const path = verdictFile(location, opts.version);
|
|
2017
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
2018
|
+
const temp = join(dirname(path), `.gate-verdict-${process.pid}-${randomUUID()}.tmp`);
|
|
2019
|
+
try {
|
|
2020
|
+
writeFileSync(temp, JSON.stringify({ version: opts.version, commit: opts.commit, outcome: result.outcome, ...ids }, null, 2) + '\n', { mode: 0o600 });
|
|
2021
|
+
chmodSync(temp, 0o600);
|
|
2022
|
+
renameSync(temp, path);
|
|
2023
|
+
} finally { if (existsSync(temp)) rmSync(temp); }
|
|
2024
|
+
}
|
|
2025
|
+
} catch (error) {
|
|
2026
|
+
result.outcome = 'error';
|
|
2027
|
+
result.error = `verdict persistence: ${String(error)}`;
|
|
2028
|
+
const path = verdictFile(ledger, opts.version);
|
|
2029
|
+
try {
|
|
2030
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
2031
|
+
const temp = join(dirname(path), `.gate-verdict-${process.pid}-${randomUUID()}.tmp`);
|
|
2032
|
+
try {
|
|
2033
|
+
writeFileSync(temp, JSON.stringify({ version: opts.version, commit: opts.commit, outcome: result.outcome, ...ids }, null, 2) + '\n', { mode: 0o600 });
|
|
2034
|
+
chmodSync(temp, 0o600);
|
|
2035
|
+
renameSync(temp, path);
|
|
2036
|
+
} finally { if (existsSync(temp)) rmSync(temp); }
|
|
2037
|
+
} catch (persistenceError) {
|
|
2038
|
+
result.error += `; error verdict persistence: ${String(persistenceError)}`;
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
debug.log('release-loop.gate', 'judged', {
|
|
2042
|
+
version: opts.version, commit: opts.commit, outcome: result.outcome,
|
|
2043
|
+
introduced: result.introduced.length, preexisting: result.preexisting,
|
|
2044
|
+
knownEnv: result.knownEnv, loadFlaky: ids.loadFlaky.length,
|
|
2045
|
+
introducedIds: ids.introduced, knownEnvIds: ids.knownEnv, preexistingIds: ids.preexisting, loadFlakyIds: ids.loadFlaky,
|
|
2046
|
+
...(result.outcome === 'error' ? { error: result.error } : {}),
|
|
2047
|
+
});
|
|
2048
|
+
}
|
|
1755
2049
|
// GATE-LIVE-OBS-RC-WORDING ⓒ — the shards board says «판정 전» until this lands; then «새 실패 N» only (UX · gate-shards.ts).
|
|
1756
2050
|
// Same ledger as the board's own writes (sweep's shardsFile). A write failure is logged and never changes the verdict.
|
|
1757
2051
|
if (result.outcome !== 'error' && opts.pod) {
|
|
@@ -1810,6 +2104,7 @@ export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOption
|
|
|
1810
2104
|
// GATE-HOST-EXECUTOR: a host gate never reads the pod settings (a bad pod value must not block the fallback executor).
|
|
1811
2105
|
const hostExecutor = fromGraph.gateExecutor === 'host';
|
|
1812
2106
|
const opts: GateOptions = {
|
|
2107
|
+
rehearsalTui: fromGraph.rehearsalTui === true,
|
|
1813
2108
|
commit: typeof fromGraph.commit === 'string' ? fromGraph.commit : '',
|
|
1814
2109
|
version: typeof fromGraph.version === 'string' ? fromGraph.version : '',
|
|
1815
2110
|
baselineVersion: typeof fromGraph.previousVersion === 'string' ? fromGraph.previousVersion : undefined,
|
|
@@ -1837,6 +2132,7 @@ export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOption
|
|
|
1837
2132
|
if (arg === '--json') continue;
|
|
1838
2133
|
// Applied after the loop and only to a Pod gate — on its own it must not turn a local gate into a Pod one (review r5).
|
|
1839
2134
|
if (arg === '--fresh-shards') { freshShards = true; continue; }
|
|
2135
|
+
if (arg === '--rehearsal-tui') { opts.rehearsalTui = true; continue; }
|
|
1840
2136
|
if (arg === '--executor') {
|
|
1841
2137
|
const value = args[++i];
|
|
1842
2138
|
if (!isGateExecutor(value)) throw new Error(`--executor must be pod or host: ${value ?? ''}`);
|
|
@@ -1887,6 +2183,7 @@ export async function runGateNode(argv: string[], env: NodeJS.ProcessEnv, runner
|
|
|
1887
2183
|
return 0;
|
|
1888
2184
|
}
|
|
1889
2185
|
version = opts.version;
|
|
2186
|
+
// The graph's existing gate-node execution path delegates verdict classification and persistence here.
|
|
1890
2187
|
result = await (runner ? judgeGate(opts, runner) : judgeGate(opts));
|
|
1891
2188
|
} catch (error) {
|
|
1892
2189
|
result = { outcome: 'error', commit: '', introduced: [], preexisting: 0, fixed: 0, knownEnv: 0, knownEnvCleared: [], stalledEnv: [], rcNonzeroNoFailures: [], durationMs: Date.now() - start, error: String(error) };
|