elanous 0.2.28 → 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.
Files changed (609) hide show
  1. package/apps/pwa/out/404/index.html +1 -1
  2. package/apps/pwa/out/404.html +1 -1
  3. package/apps/pwa/out/_next/static/{y1ltt7oCZxVpC6zVPhZ2O → cIpv5nU9AycwYWZI9H589}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{ab4d57c2.e90bc2cb31949045.js → 058a98ba.7eb28d5e6dc03565.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/{4565-67e4c620597965c8.js → 1015-9cba22a5a4554797.js} +1 -1
  6. package/apps/pwa/out/_next/static/chunks/1084-4e66af7376a18db7.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1106.3a6410b7b0c70ad4.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1127.32fc642f70423506.js +8 -0
  9. package/apps/pwa/out/_next/static/chunks/{1516.83dd2c1b86b4e507.js → 1142.65d002789ab3af7d.js} +2 -2
  10. package/apps/pwa/out/_next/static/chunks/1189-d9b8e1995a831fae.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/1224.6da98c7ad0eb7dd8.js +34 -0
  12. package/apps/pwa/out/_next/static/chunks/1370.4e3e2558f111fd5d.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/1514-b7da377fdc4eb17a.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/1516.dfaceff3fd2d0e0d.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/1577.3c24c32584e10d70.js +286 -0
  16. package/apps/pwa/out/_next/static/chunks/161.130a569855c4d68c.js +24 -0
  17. package/apps/pwa/out/_next/static/chunks/1782-59db1b5aa06df38a.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/{1743.563d3168e95825ef.js → 1852.7f7b53991fcb99ef.js} +2 -2
  19. package/apps/pwa/out/_next/static/chunks/1864.16da9556e78a9236.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/1b6f6b2a.aff090122be3726e.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/2341.e8a9135e5b910dd5.js +36 -0
  22. package/apps/pwa/out/_next/static/chunks/257.10afdbe3520f2fc2.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/2658.87550e692dc493f6.js +64 -0
  24. package/apps/pwa/out/_next/static/chunks/2673.85e2f02efae7a85f.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/2766-bee623b37472ed63.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/2794.6d836872b0bf43e2.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/2806.eb57ced1a528ed39.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/283.7a121c4910e6dad7.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/{65ebff95.081daeff622d238e.js → 285adb1a.5654e605dddf86d2.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/2870.1dc29aa5fc01f09f.js +115 -0
  31. package/apps/pwa/out/_next/static/chunks/293.fd38e77ce5d48103.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/2993-5952a1cd3fd5d441.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/3160-95c96f4d2a21dd26.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/32-a523427c5bd5ed00.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/3280.af38002a6424a905.js +90 -0
  36. package/apps/pwa/out/_next/static/chunks/328bb3ab-6628a1114fe3fb3b.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/3292-5ddffdb2f517b44f.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/{9917-9e26abc5f7772735.js → 3396-97b97928c49190c9.js} +1 -1
  39. package/apps/pwa/out/_next/static/chunks/{6196.34d6b229079e9e28.js → 3403.7199205e65124486.js} +1 -1
  40. package/apps/pwa/out/_next/static/chunks/3523.d3231daaf7aba993.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/3763.577de3181e1e6ea0.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/4138-cc56b0938ef72783.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/419.736c0d6f426249a7.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/4255-8350c43d307a8875.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/{6def8a8e.c81f9ca41028eccf.js → 425cfc4d.fb91158e3971c39f.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/4352.a4954693499c2714.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/4421-f2dcbc6fd80ec013.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/4513-51b94aeee37c9905.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/{16e57f67.3ea19e893461e611.js → 46bd8bea.2f4b4099172fcdb7.js} +1 -1
  50. package/apps/pwa/out/_next/static/chunks/{5081.a240b63fad28663a.js → 4759.1f76713aefb87db5.js} +1 -1
  51. package/apps/pwa/out/_next/static/chunks/4928-c3b72f99aa588699.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/4986-f9fc0283fb325219.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/5014.671a7b9f69c2b7b1.js +94 -0
  54. package/apps/pwa/out/_next/static/chunks/5137.d398e24710b3feb6.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/5298.0bea74885cabe754.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/5315.74c53a39739b11c4.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/5317-8b1df8d02105cd07.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/5358-33051f41a2b239af.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/5388.a2ec02501f714ad5.js +62 -0
  60. package/apps/pwa/out/_next/static/chunks/5395.e6ac948a2d15058d.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/5409-f282bf533c151849.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/5466.d0f4aba9d52acfa8.js +27 -0
  63. package/apps/pwa/out/_next/static/chunks/5564.3d28b353470fe305.js +104 -0
  64. package/apps/pwa/out/_next/static/chunks/{9974.e77b4ff4884557b3.js → 5591.a35e7a217734a8b9.js} +2 -2
  65. package/apps/pwa/out/_next/static/chunks/569.b0cbfb4dee4a167a.js +93 -0
  66. package/apps/pwa/out/_next/static/chunks/5835-72465b73a8bb8d74.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/5941.045d6093139e1f7a.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/{5492-e05e26a033ba80d2.js → 6013-2f769370d8b4c2c6.js} +3 -3
  69. package/apps/pwa/out/_next/static/chunks/6228.47cabb83564dfe54.js +24 -0
  70. package/apps/pwa/out/_next/static/chunks/6457-8d1993002a87d0fe.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/6513.ef54cfae52351982.js +2 -0
  72. package/apps/pwa/out/_next/static/chunks/{5408.377c8c543635b81f.js → 6534.1ab00f370db7b105.js} +1 -1
  73. package/apps/pwa/out/_next/static/chunks/6582.13d245bf87df69f1.js +148 -0
  74. package/apps/pwa/out/_next/static/chunks/6655.c33f6acc2c61bb89.js +149 -0
  75. package/apps/pwa/out/_next/static/chunks/6731-9d7195af289c8d4c.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/6733.0be05b1db9279fc5.js +79 -0
  77. package/apps/pwa/out/_next/static/chunks/6742-90937146253a8b9e.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/{8578.dff22d988cc2b106.js → 6767.1fcfc9b8be83975f.js} +16 -16
  79. package/apps/pwa/out/_next/static/chunks/6776.06ff1e3b464cc607.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/{9311.24c8f5ea793253dd.js → 6853.cc5174a9dee59593.js} +1 -1
  81. package/apps/pwa/out/_next/static/chunks/687-816858f77a396a4e.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/6888.72c7b8cfb02a7e61.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/6993.c17c828ac3ef3253.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/7076.9f27ef666b0cefdd.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/7113.1403c5d451804e8c.js +287 -0
  86. package/apps/pwa/out/_next/static/chunks/7169.9dd603c726c2cd7c.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/7247-e3ec21f761f86710.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/7261-a47b72f96f5199df.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/7335-5cda81ac90310077.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/7356-c2e4af090546e2d4.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/{94.4cfed21c0b781a06.js → 7375.050ea0d72968e49f.js} +16 -16
  92. package/apps/pwa/out/_next/static/chunks/738-59518742078fda71.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/7385.a7375f10db73243a.js +77 -0
  94. package/apps/pwa/out/_next/static/chunks/7445.cf1cf2fe109bcd96.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/7505.a2057432ab6ef99a.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/7515.31f45fac1f9c70cb.js +74 -0
  97. package/apps/pwa/out/_next/static/chunks/7658-d4ce8c72a9548a91.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/7725-4f83d5541fbdb81b.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/8107-0b1ae46bc5bcb788.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/8132.bfea2f016ce8d992.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/8332.6f0f49df772e163b.js +23 -0
  102. package/apps/pwa/out/_next/static/chunks/{7483.ba01c6c209b81bba.js → 8398.73a19b39715eda04.js} +1 -1
  103. package/apps/pwa/out/_next/static/chunks/{2846.d6a433b8663819a5.js → 8471.8d2a8c74f09c36ff.js} +3 -3
  104. package/apps/pwa/out/_next/static/chunks/8672.e299832bc7d18ee2.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/8735.f1ef798a5f260f6f.js +226 -0
  106. package/apps/pwa/out/_next/static/chunks/{5785-ea8d2c633b6688e3.js → 8767-2f6a634f5abeaa01.js} +1 -1
  107. package/apps/pwa/out/_next/static/chunks/8774-88f67f7d8d53d299.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/8782.252e5a405d86e4ed.js +32 -0
  109. package/apps/pwa/out/_next/static/chunks/8795.32573a185af18c7b.js +41 -0
  110. package/apps/pwa/out/_next/static/chunks/909-9ed7aa0dba804316.js +1 -0
  111. package/apps/pwa/out/_next/static/chunks/9100.22b0301256827007.js +1 -0
  112. package/apps/pwa/out/_next/static/chunks/9140-dde77575570bc71e.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/{4593.10f5b9bd702d4a6c.js → 9143.ff14585db1d275ff.js} +1 -1
  114. package/apps/pwa/out/_next/static/chunks/9292-a7ad9a01c7044b93.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/9517.00627dc51b02821c.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/9725.dcbb585652d8e9c5.js +1 -0
  117. package/apps/pwa/out/_next/static/chunks/9761.a3ebf5c389c24ddc.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/{4204.a409ab6250c0f6a8.js → 9907.49be0fcadaf214b5.js} +1 -1
  119. package/apps/pwa/out/_next/static/chunks/9972.834d2b2b7d48f1ed.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/9995-1a90486f5ae01546.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/{973cbf1b.3bbcf2b41548eca7.js → a435ad95.577add051b4facb2.js} +1 -1
  122. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-83372d8b5863ebf7.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d69256d7613c140a.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-352c5e9ba3f9b858.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/board/page-69f1d725524201a8.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-fa46d4588e8442c2.js → page-a5635eba0d73b84e.js} +1 -1
  127. package/apps/pwa/out/_next/static/chunks/app/bots/page-ad9181d84285e4ea.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/ceo/{page-66ad46ed0279feb9.js → page-86feb08080261a2f.js} +1 -1
  129. package/apps/pwa/out/_next/static/chunks/app/chat/page-ffa15987e4756d03.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/consult/page-11a3ab44ac327cc1.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/control/page-6309b11c4264d24d.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/decisions/page-8800472079612e87.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/design-check/page-d387bf3f2301882a.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/editor/page-048f88d8c8658da6.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/exec/page-6c14f9739b7f6880.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/field/page-2834f44f64e98d74.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/healthz/route-83372d8b5863ebf7.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/inside/page-a13df6f88e824887.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/intake/page-8a4397248754cf2b.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/layout-daf0bd329360a336.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/live/page-f7ef1c28764cb476.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/live-run/page-61e74f28b9353dac.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/loops/{page-646b50f63b6fe732.js → page-8aaa6dedc4c10dfb.js} +1 -1
  144. package/apps/pwa/out/_next/static/chunks/app/market/page-294602ba649be829.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-5fa5f41ed3fce323.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/morning/{page-6f131b51193a36f9.js → page-33bf667a5fb297bf.js} +1 -1
  147. package/apps/pwa/out/_next/static/chunks/app/not-found-5ae3cbd6e5dba515.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/observatory/page-eb867d16ad6fdede.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1ab789fa68682f7e.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-6fd41b5b6ff13a71.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fb3d5f7642c99fba.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/outputs/page-70f705b2b5ed42ca.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/page-c9c93b358a7667b5.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/reflection/page-1563c556b81c96ed.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-ce89c20c45b3a673.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/sessions/page-423ae63d786f4ee9.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-591a5378a4c4447c.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/settings/page-a250496b19a5824b.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-c36a9277febc5911.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/setup/layout-83372d8b5863ebf7.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/page-29900ed0011b2c83.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/share/page-a5de57edc6d7f942.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/showroom/page-16bdd4c9f40498df.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/tasks/page-75e946e6e71cc6d8.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/term/page-9eb56166774eb256.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/today/page-1c5f9ef35dbb22c5.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/trace/page-657ff9985e508498.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/vault/page-22dc72499da68fb2.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-9f92737b8766009a.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/workflows/page-094ad334e40f8169.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workspace/page-8bcd379852ff6287.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-6b04413f280cd344.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/{f4dfbfc0.a69fb12f854882aa.js → bc324b7c.02a09dd93f6a399b.js} +1 -1
  174. package/apps/pwa/out/_next/static/chunks/de70ab42-d28d47437208d458.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/f800c5e3.46dbd7f2af8c3fce.js +1 -0
  176. package/apps/pwa/out/_next/static/chunks/f8bc19ad.47579f6d684b0760.js +122 -0
  177. package/apps/pwa/out/_next/static/chunks/framework-ca9ac4609fb9fed8.js +1 -0
  178. package/apps/pwa/out/_next/static/chunks/main-app-0dd08d32d15a030f.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-f0399d33955c4e01.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/pages/_app-d42caca6f8ac2211.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_error-42e713c13aa41ca8.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/webpack-8fbf8f61a377aa16.js +1 -0
  183. package/apps/pwa/out/approvals/index.html +1 -1
  184. package/apps/pwa/out/approvals/index.txt +22 -22
  185. package/apps/pwa/out/autopilot/index.html +1 -1
  186. package/apps/pwa/out/autopilot/index.txt +22 -22
  187. package/apps/pwa/out/board/index.html +1 -1
  188. package/apps/pwa/out/board/index.txt +22 -22
  189. package/apps/pwa/out/botlab/index.html +1 -1
  190. package/apps/pwa/out/botlab/index.txt +21 -21
  191. package/apps/pwa/out/bots/index.html +1 -1
  192. package/apps/pwa/out/bots/index.txt +21 -21
  193. package/apps/pwa/out/ceo/index.html +1 -1
  194. package/apps/pwa/out/ceo/index.txt +22 -22
  195. package/apps/pwa/out/chat/index.html +1 -1
  196. package/apps/pwa/out/chat/index.txt +21 -21
  197. package/apps/pwa/out/consult/index.html +1 -1
  198. package/apps/pwa/out/consult/index.txt +22 -22
  199. package/apps/pwa/out/control/index.html +1 -1
  200. package/apps/pwa/out/control/index.txt +22 -22
  201. package/apps/pwa/out/decisions/index.html +1 -1
  202. package/apps/pwa/out/decisions/index.txt +21 -21
  203. package/apps/pwa/out/design-check/index.html +1 -1
  204. package/apps/pwa/out/design-check/index.txt +22 -22
  205. package/apps/pwa/out/editor/index.html +1 -1
  206. package/apps/pwa/out/editor/index.txt +22 -22
  207. package/apps/pwa/out/exec/index.html +1 -1
  208. package/apps/pwa/out/exec/index.txt +22 -22
  209. package/apps/pwa/out/field/index.html +1 -1
  210. package/apps/pwa/out/field/index.txt +22 -22
  211. package/apps/pwa/out/healthz +1 -1
  212. package/apps/pwa/out/index.html +1 -1
  213. package/apps/pwa/out/index.txt +21 -21
  214. package/apps/pwa/out/inside/index.html +1 -1
  215. package/apps/pwa/out/inside/index.txt +21 -21
  216. package/apps/pwa/out/intake/index.html +1 -1
  217. package/apps/pwa/out/intake/index.txt +22 -22
  218. package/apps/pwa/out/live/index.html +1 -1
  219. package/apps/pwa/out/live/index.txt +22 -22
  220. package/apps/pwa/out/live-run/index.html +1 -1
  221. package/apps/pwa/out/live-run/index.txt +22 -22
  222. package/apps/pwa/out/loops/index.html +1 -1
  223. package/apps/pwa/out/loops/index.txt +21 -21
  224. package/apps/pwa/out/market/index.html +1 -1
  225. package/apps/pwa/out/market/index.txt +21 -21
  226. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  227. package/apps/pwa/out/missions/placeholder/index.txt +21 -21
  228. package/apps/pwa/out/morning/index.html +1 -1
  229. package/apps/pwa/out/morning/index.txt +22 -22
  230. package/apps/pwa/out/observatory/index.html +1 -1
  231. package/apps/pwa/out/observatory/index.txt +22 -22
  232. package/apps/pwa/out/ops/checklist/index.html +1 -1
  233. package/apps/pwa/out/ops/checklist/index.txt +21 -21
  234. package/apps/pwa/out/ops/release/index.html +1 -1
  235. package/apps/pwa/out/ops/release/index.txt +21 -21
  236. package/apps/pwa/out/ops/seats/index.html +1 -1
  237. package/apps/pwa/out/ops/seats/index.txt +21 -21
  238. package/apps/pwa/out/outputs/index.html +1 -1
  239. package/apps/pwa/out/outputs/index.txt +21 -21
  240. package/apps/pwa/out/reflection/index.html +1 -1
  241. package/apps/pwa/out/reflection/index.txt +22 -22
  242. package/apps/pwa/out/scheduler/index.html +1 -1
  243. package/apps/pwa/out/scheduler/index.txt +22 -22
  244. package/apps/pwa/out/sessions/index.html +1 -1
  245. package/apps/pwa/out/sessions/index.txt +21 -21
  246. package/apps/pwa/out/settings/devices/index.html +1 -1
  247. package/apps/pwa/out/settings/devices/index.txt +22 -22
  248. package/apps/pwa/out/settings/index.html +1 -1
  249. package/apps/pwa/out/settings/index.txt +22 -22
  250. package/apps/pwa/out/setup/done/index.html +1 -1
  251. package/apps/pwa/out/setup/done/index.txt +22 -22
  252. package/apps/pwa/out/setup/index.html +1 -1
  253. package/apps/pwa/out/setup/index.txt +22 -22
  254. package/apps/pwa/out/share/index.html +1 -1
  255. package/apps/pwa/out/share/index.txt +22 -22
  256. package/apps/pwa/out/showroom/index.html +1 -1
  257. package/apps/pwa/out/showroom/index.txt +21 -21
  258. package/apps/pwa/out/tasks/index.html +1 -1
  259. package/apps/pwa/out/tasks/index.txt +22 -22
  260. package/apps/pwa/out/term/index.html +1 -1
  261. package/apps/pwa/out/term/index.txt +22 -22
  262. package/apps/pwa/out/today/index.html +1 -1
  263. package/apps/pwa/out/today/index.txt +22 -22
  264. package/apps/pwa/out/trace/index.html +1 -1
  265. package/apps/pwa/out/trace/index.txt +22 -22
  266. package/apps/pwa/out/vault/index.html +1 -1
  267. package/apps/pwa/out/vault/index.txt +22 -22
  268. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  269. package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
  270. package/apps/pwa/out/workflows/index.html +1 -1
  271. package/apps/pwa/out/workflows/index.txt +22 -22
  272. package/apps/pwa/out/workspace/index.html +1 -1
  273. package/apps/pwa/out/workspace/index.txt +22 -22
  274. package/apps/pwa/out/worktrees/index.html +1 -1
  275. package/apps/pwa/out/worktrees/index.txt +22 -22
  276. package/graphs/release/release-loop.yaml +6 -2
  277. package/package.json +1 -1
  278. package/plugins/agent-team/agents/research.md +1 -0
  279. package/scripts/ci-typecheck-changed.ts +3 -2
  280. package/scripts/coord-channel-watch.sh +26 -6
  281. package/scripts/coord-role-pack.ts +143 -0
  282. package/scripts/lib/morning-report-options.ts +41 -0
  283. package/scripts/pr-land-extra-remote-gates.ts +49 -0
  284. package/scripts/release-loop/gate-batch-safe.ts +23 -0
  285. package/scripts/release-loop/gate-history.ts +422 -0
  286. package/scripts/release-loop/gate-node.ts +310 -46
  287. package/scripts/release-loop/gate-triage-node.ts +190 -0
  288. package/scripts/release-loop/spawn-lane.ts +68 -0
  289. package/scripts/release-loop/tui-node-checks.ts +23 -0
  290. package/scripts/release-loop/version-node.ts +62 -4
  291. package/scripts/test-deterministic.ts +41 -12
  292. package/scripts/usage-rollup.ts +11 -3
  293. package/src/acp/core-turn-bridge.ts +98 -22
  294. package/src/acp/elanous-extensions.ts +2 -0
  295. package/src/acp/server.ts +2 -0
  296. package/src/agent/ref-grounding.ts +18 -0
  297. package/src/agent-substrate/pr-reviewer.ts +29 -21
  298. package/src/budget/pty-usage-reemit.ts +7 -0
  299. package/src/budget/run-usage-rollup.ts +9 -0
  300. package/src/budget/usage-daily-store.ts +98 -0
  301. package/src/capture/tui-self-report.ts +22 -4
  302. package/src/chat/index.ts +31 -10
  303. package/src/chat/pickers/modal-runtime.ts +1 -0
  304. package/src/chat/pickers/modals.ts +3 -1
  305. package/src/chat/turn-typeahead.ts +123 -16
  306. package/src/cli/dashboard-name-flag.ts +48 -0
  307. package/src/cli/doctor-cli.ts +80 -0
  308. package/src/cli/log-event-names.ts +12 -0
  309. package/src/cli/msg-cli.ts +45 -2
  310. package/src/cli/pr-cli.ts +382 -105
  311. package/src/cli/pr-land-heavy-remote.ts +233 -0
  312. package/src/cli/pr-land-overlap-evidence.ts +104 -5
  313. package/src/cli/pty-takeover-cli.ts +125 -16
  314. package/src/cli/release-cli.ts +44 -2
  315. package/src/cli/remote-resolve.ts +2 -1
  316. package/src/cli/seat-cli.ts +26 -16
  317. package/src/cli/tasks-cli.ts +8 -2
  318. package/src/cli/usage-cli.ts +146 -12
  319. package/src/cli/usage-daily-cli.ts +84 -0
  320. package/src/context-bus/context-now.ts +82 -5
  321. package/src/core-turn/auth-rejected-notice.ts +41 -0
  322. package/src/core-turn/run-core-turn.ts +13 -2
  323. package/src/core-turn/run-goal-loop.ts +17 -1
  324. package/src/core-turn/short-question.ts +45 -12
  325. package/src/core-turn/types.ts +7 -0
  326. package/src/dashboard/acp-active-model.ts +40 -0
  327. package/src/dashboard/auto-compact-runtime.ts +2 -0
  328. package/src/dashboard/boot-banner.ts +80 -0
  329. package/src/dashboard/dashboard-status-lines.ts +11 -0
  330. package/src/dashboard/detail-viewer-runtime.ts +15 -2
  331. package/src/dashboard/event-loop-stall-monitor.ts +4 -0
  332. package/src/dashboard/goal-loop-thinking.ts +16 -0
  333. package/src/dashboard/hitl-boot.ts +9 -4
  334. package/src/dashboard/index.ts +228 -387
  335. package/src/dashboard/input/chat-main-entry-runtime.ts +2 -0
  336. package/src/dashboard/input/chat-main-log-actions.ts +4 -0
  337. package/src/dashboard/input/chat-main-log-command.ts +12 -1
  338. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +172 -3
  339. package/src/dashboard/input/chat-main-session.ts +6 -2
  340. package/src/dashboard/input/chat-main-slash-command.ts +7 -0
  341. package/src/dashboard/input/chat-main-turn.ts +2 -1
  342. package/src/dashboard/input/compact-prepass-recovery.ts +97 -0
  343. package/src/dashboard/input/dashboard-chord-actions.ts +10 -1
  344. package/src/dashboard/input/dashboard-chord-runtime.ts +2 -0
  345. package/src/dashboard/input/external-input-host.ts +45 -0
  346. package/src/dashboard/input/log-fold-binding.ts +18 -0
  347. package/src/dashboard/input/mouse-wiring.ts +17 -4
  348. package/src/dashboard/input/turn-steer-bridge.ts +94 -0
  349. package/src/dashboard/log-click-runtime.ts +5 -3
  350. package/src/dashboard/running-build-status.ts +74 -0
  351. package/src/dashboard/seat-boot-preamble.ts +113 -0
  352. package/src/dashboard/seat-local-instructions.ts +305 -0
  353. package/src/dashboard/slash-runtime/dashboard-handlers.ts +100 -28
  354. package/src/dashboard/slash-runtime/model-choices.ts +35 -0
  355. package/src/dashboard/term-slash-runtime.ts +15 -0
  356. package/src/dashboard/turn-preamble.ts +30 -2
  357. package/src/dashboard/turn-session-layers.ts +176 -0
  358. package/src/dashboard/turn-stream-formatter.ts +14 -6
  359. package/src/dashboard/turn-stream-presentation-applier.ts +8 -0
  360. package/src/domains/release-tool.ts +8 -9
  361. package/src/domains/surface-events.ts +4 -1
  362. package/src/domains/watch-default-scheduler.ts +8 -9
  363. package/src/fold-stack.ts +24 -1
  364. package/src/git-fs/dirty.ts +32 -4
  365. package/src/git-fs/index.ts +43 -1
  366. package/src/graph-runner/runner.ts +1 -0
  367. package/src/harness/harness-gc.ts +32 -5
  368. package/src/index.ts +14 -1
  369. package/src/input/external-input-queue.ts +219 -0
  370. package/src/interactive-terminal-modal.ts +13 -0
  371. package/src/llm/route-decision.ts +25 -0
  372. package/src/llm-stream-retry-display.ts +59 -0
  373. package/src/llm.ts +253 -55
  374. package/src/log-entry.ts +3 -3
  375. package/src/log-pane/search.ts +29 -4
  376. package/src/loops/agent-reply.ts +256 -0
  377. package/src/loops/budget.ts +37 -7
  378. package/src/loops/loop-cli.ts +3 -2
  379. package/src/loops/msg-address.ts +57 -0
  380. package/src/loops/msg-wake.ts +282 -0
  381. package/src/loops/orchestrator/inbox.ts +60 -3
  382. package/src/loops/orchestrator/tick.ts +24 -4
  383. package/src/loops/registry.ts +42 -4
  384. package/src/memory.ts +20 -5
  385. package/src/native-tool-catalog.ts +125 -0
  386. package/src/nexus/api/http-server.ts +5 -0
  387. package/src/nexus/api/ops-api.ts +2 -0
  388. package/src/nexus/api/project-autopsy.ts +127 -0
  389. package/src/nexus/api/terminals.ts +3 -0
  390. package/src/nexus/api/workflow-approvals.ts +2 -0
  391. package/src/nexus/onboarding/first-boot-wizard.ts +56 -2
  392. package/src/oauth/codex-account-store.ts +15 -3
  393. package/src/oauth/codex-billing-route.ts +31 -0
  394. package/src/ops/overview-collect.ts +19 -2
  395. package/src/panes/log-pane.ts +67 -8
  396. package/src/persona/presets.ts +44 -0
  397. package/src/plugins/maker/wizard-generate.ts +6 -4
  398. package/src/preview/terminal.ts +14 -1
  399. package/src/prompt-cache/types.ts +2 -0
  400. package/src/prompt-enhance/enhance.ts +24 -8
  401. package/src/prompt-library/universal-preamble.ts +273 -56
  402. package/src/pty-shell/pty-control-ipc.ts +4 -1
  403. package/src/pty-shell/pty-manifest.ts +77 -5
  404. package/src/pty-shell/registry.ts +19 -2
  405. package/src/pty-shell/tui-run-state.ts +253 -0
  406. package/src/release-loop/light-rc.ts +28 -3
  407. package/src/release-loop/rehearsal.ts +1 -1
  408. package/src/render.ts +5 -1
  409. package/src/seat-loop/seat-report.ts +42 -2
  410. package/src/self-dev/run-supervisor.ts +71 -5
  411. package/src/self-dev/shard-aggregate.ts +2 -0
  412. package/src/self-dev/shard-run.ts +23 -2
  413. package/src/self-health/signatures.ts +132 -0
  414. package/src/self-implement/doctor-visit.ts +118 -0
  415. package/src/self-implement/gate-cli.ts +11 -2
  416. package/src/self-implement/goal-author-decompose.ts +80 -16
  417. package/src/self-implement/goal-author.ts +114 -72
  418. package/src/self-implement/goal-authoring-grounding.ts +1 -1
  419. package/src/self-implement/goal-file-name.ts +65 -0
  420. package/src/self-implement/ground-goal.ts +2 -1
  421. package/src/self-implement/host-regate.ts +14 -0
  422. package/src/self-implement/review-mustfix-class.ts +115 -0
  423. package/src/self-implement/run-ledger.ts +111 -7
  424. package/src/self-implement/running-runs.ts +10 -6
  425. package/src/self-implement/self-implement-runtime.ts +32 -0
  426. package/src/session/pending-input.ts +62 -11
  427. package/src/session-runtime/index.ts +7 -1
  428. package/src/skills/tools/ops-ledger.ts +707 -0
  429. package/src/skills/tools/persistent-grounding.ts +47 -7
  430. package/src/skills/tools/shell.ts +117 -1
  431. package/src/status/bar.ts +32 -2
  432. package/src/status/status-provider-info.ts +58 -0
  433. package/src/task-agent/rejudge-on-head.ts +4 -1
  434. package/src/task-agent/remote-rejudge-bind.ts +34 -5
  435. package/src/task-agent/repair-cycle.ts +13 -0
  436. package/src/task-orchestrator/surfaces/kubectl-transient-retry.ts +51 -0
  437. package/src/task-orchestrator/surfaces/pod-command-job.ts +63 -7
  438. package/src/task-orchestrator/surfaces/pod-install-slots.ts +6 -11
  439. package/src/task-orchestrator/surfaces/pod-mirror-seed.ts +76 -0
  440. package/src/task-orchestrator/surfaces/self-implement-pod.ts +107 -33
  441. package/src/terminal/session-registry.ts +6 -0
  442. package/src/thinking-line.ts +12 -3
  443. package/src/tool-runtime/index.ts +4 -0
  444. package/src/tool-runtime/ops-ledger-runtimes.ts +84 -0
  445. package/src/tool-runtime/ref-consult-runtime.ts +17 -2
  446. package/src/tool-runtime/run-shell-runtime.ts +4 -1
  447. package/src/tool-runtime/self-cognition-runtimes.ts +5 -1
  448. package/src/tui.ts +5 -0
  449. package/src/user-config.ts +54 -6
  450. package/src/ux-sim/fake-llm.ts +29 -0
  451. package/src/ux-sim/in-process-cli.ts +175 -0
  452. package/src/version/packed-revision.json +1 -1
  453. package/apps/pwa/out/_next/static/chunks/1027.a0f04663d24c2779.js +0 -1
  454. package/apps/pwa/out/_next/static/chunks/1038.676007ae505822e7.js +0 -1
  455. package/apps/pwa/out/_next/static/chunks/1113.a37eadf7f740d955.js +0 -1
  456. package/apps/pwa/out/_next/static/chunks/112-fa7c526c20ce1405.js +0 -1
  457. package/apps/pwa/out/_next/static/chunks/1144.5fa1fa1d48dbcd12.js +0 -36
  458. package/apps/pwa/out/_next/static/chunks/1146.80d15ea7c6e6ee95.js +0 -1
  459. package/apps/pwa/out/_next/static/chunks/1157.f08bf2b70915e176.js +0 -148
  460. package/apps/pwa/out/_next/static/chunks/119b405e.8fdd0e44d1ff37aa.js +0 -1
  461. package/apps/pwa/out/_next/static/chunks/1296.04560d98e73fbf51.js +0 -1
  462. package/apps/pwa/out/_next/static/chunks/1314-f604ba013ec535b0.js +0 -1
  463. package/apps/pwa/out/_next/static/chunks/1454-29d1d773d5996a49.js +0 -1
  464. package/apps/pwa/out/_next/static/chunks/1464.f3209af0d8ae4ff2.js +0 -1
  465. package/apps/pwa/out/_next/static/chunks/1729-d73dd25e564679d4.js +0 -1
  466. package/apps/pwa/out/_next/static/chunks/1843.5924c24295243261.js +0 -27
  467. package/apps/pwa/out/_next/static/chunks/1987.e28149724b92918b.js +0 -1
  468. package/apps/pwa/out/_next/static/chunks/2066-5ca18ffa5a4be68b.js +0 -1
  469. package/apps/pwa/out/_next/static/chunks/2240.d10a66695155fd93.js +0 -1
  470. package/apps/pwa/out/_next/static/chunks/2391.e2cbc98d4abbb41a.js +0 -1
  471. package/apps/pwa/out/_next/static/chunks/2528.3868070ef1be69f9.js +0 -24
  472. package/apps/pwa/out/_next/static/chunks/2566-734cb83bbd405886.js +0 -1
  473. package/apps/pwa/out/_next/static/chunks/2697.76ca2ac3efef6f3b.js +0 -1
  474. package/apps/pwa/out/_next/static/chunks/275.0e6bad414729bfa0.js +0 -32
  475. package/apps/pwa/out/_next/static/chunks/2803-33bdae771855a8d1.js +0 -1
  476. package/apps/pwa/out/_next/static/chunks/3143.257997a5f1ed79df.js +0 -34
  477. package/apps/pwa/out/_next/static/chunks/3259-86f3be56e43eaf6a.js +0 -1
  478. package/apps/pwa/out/_next/static/chunks/3280-e177c33bfbdcfb0a.js +0 -1
  479. package/apps/pwa/out/_next/static/chunks/3294-2b6a6d45a46a6c1c.js +0 -1
  480. package/apps/pwa/out/_next/static/chunks/3309.364573ceedbdfd5f.js +0 -1
  481. package/apps/pwa/out/_next/static/chunks/3522.758840fc5e7fe630.js +0 -287
  482. package/apps/pwa/out/_next/static/chunks/3525-a7c989c1a661e880.js +0 -1
  483. package/apps/pwa/out/_next/static/chunks/3694.b1a66387756458c0.js +0 -1
  484. package/apps/pwa/out/_next/static/chunks/370.124f9e678a76fc01.js +0 -1
  485. package/apps/pwa/out/_next/static/chunks/3797-9643105335e660fe.js +0 -1
  486. package/apps/pwa/out/_next/static/chunks/4084.15ecaf4240bd9e77.js +0 -286
  487. package/apps/pwa/out/_next/static/chunks/4089-b3382b8843114eee.js +0 -1
  488. package/apps/pwa/out/_next/static/chunks/4293.928a8d573c16aab7.js +0 -1
  489. package/apps/pwa/out/_next/static/chunks/4402.f9238a3b18a7bd43.js +0 -1
  490. package/apps/pwa/out/_next/static/chunks/4490-9b090b0f9427f902.js +0 -1
  491. package/apps/pwa/out/_next/static/chunks/4548.13502d0ca3b4f862.js +0 -8
  492. package/apps/pwa/out/_next/static/chunks/4599-548bc96cda44a5a7.js +0 -1
  493. package/apps/pwa/out/_next/static/chunks/4625.206574e33a9e7ae8.js +0 -104
  494. package/apps/pwa/out/_next/static/chunks/4862-392c9a71f0d8dfb4.js +0 -1
  495. package/apps/pwa/out/_next/static/chunks/5054.2906d1b02542edc5.js +0 -149
  496. package/apps/pwa/out/_next/static/chunks/5102-c822bbbee5a022a4.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/5194-8c93f2e7e4b6f717.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/5305-bacd7eca0b9a5848.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/5311.9071ee3f44fd7ff5.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/5369.9f34353442daa421.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/589-49d730cb8138b397.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/5904-264a6412ae3c9028.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/5980.728531d5c566535c.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/599-56f2c0a5cc4a7295.js +0 -1
  505. package/apps/pwa/out/_next/static/chunks/6011.f525002e21bef0ac.js +0 -23
  506. package/apps/pwa/out/_next/static/chunks/6132.4381ab93683f1acf.js +0 -1
  507. package/apps/pwa/out/_next/static/chunks/6149.2e698635e454d8f6.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/6356-0afafc4230cf9767.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/6406.64756073f7df35b8.js +0 -77
  510. package/apps/pwa/out/_next/static/chunks/6479-16e62aedafa0e310.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/6599.b5246b46a2453329.js +0 -64
  512. package/apps/pwa/out/_next/static/chunks/6610.5c26eec011565acb.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/666.37d18474edc1c552.js +0 -2
  514. package/apps/pwa/out/_next/static/chunks/6904.a0b5b1ed4f2d2c20.js +0 -1
  515. package/apps/pwa/out/_next/static/chunks/7247-1f4aba94aba50d9f.js +0 -1
  516. package/apps/pwa/out/_next/static/chunks/7274-e71a7c5b7760cd02.js +0 -1
  517. package/apps/pwa/out/_next/static/chunks/7290.1fcaeef3adb5d0d4.js +0 -1
  518. package/apps/pwa/out/_next/static/chunks/7477-2c64e42760f82f33.js +0 -1
  519. package/apps/pwa/out/_next/static/chunks/7505-47002b76ff1aef6c.js +0 -1
  520. package/apps/pwa/out/_next/static/chunks/7600-11884ca4da68e5d9.js +0 -1
  521. package/apps/pwa/out/_next/static/chunks/7607.40d8bb3689697ccb.js +0 -62
  522. package/apps/pwa/out/_next/static/chunks/7752.5cbdd0b433c54af9.js +0 -1
  523. package/apps/pwa/out/_next/static/chunks/7763.f51fcc19ca1414ec.js +0 -1
  524. package/apps/pwa/out/_next/static/chunks/7785.846140c859b1adf6.js +0 -1
  525. package/apps/pwa/out/_next/static/chunks/7807-a0fdac6393e0bf36.js +0 -1
  526. package/apps/pwa/out/_next/static/chunks/7837-20a63b4b7e9342b1.js +0 -1
  527. package/apps/pwa/out/_next/static/chunks/800.7815414cf280545d.js +0 -1
  528. package/apps/pwa/out/_next/static/chunks/8085-1e5fce59500a2cc2.js +0 -1
  529. package/apps/pwa/out/_next/static/chunks/8100.415f2105faa63e97.js +0 -1
  530. package/apps/pwa/out/_next/static/chunks/8290.701aa42ed13aa767.js +0 -226
  531. package/apps/pwa/out/_next/static/chunks/83a80514-b897b64df43ce108.js +0 -1
  532. package/apps/pwa/out/_next/static/chunks/8444.d9c74214a3145a43.js +0 -93
  533. package/apps/pwa/out/_next/static/chunks/8569.98c915534928c7f9.js +0 -24
  534. package/apps/pwa/out/_next/static/chunks/8591.b1c7f876e5ec7cfb.js +0 -1
  535. package/apps/pwa/out/_next/static/chunks/8715-f63756021d3ab989.js +0 -1
  536. package/apps/pwa/out/_next/static/chunks/8879.776c2ec7732aa114.js +0 -115
  537. package/apps/pwa/out/_next/static/chunks/8881.ddca15b3c82c490c.js +0 -1
  538. package/apps/pwa/out/_next/static/chunks/8946.5da5c87f32a2e1a8.js +0 -41
  539. package/apps/pwa/out/_next/static/chunks/9117.080b9b75be90e14d.js +0 -74
  540. package/apps/pwa/out/_next/static/chunks/9167.f3ecb28f367d6e69.js +0 -94
  541. package/apps/pwa/out/_next/static/chunks/9224.66095370ee28c93b.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/9238-0682b013c83d6449.js +0 -1
  543. package/apps/pwa/out/_next/static/chunks/9449.d3ed795da4befb1c.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/949.1e7d45ccb71580aa.js +0 -90
  545. package/apps/pwa/out/_next/static/chunks/9638-d268329f2c6f4f8d.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/9842-d6644eb53d3dc6bf.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/9863.94ca3263e760696e.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/9938.b23e4d4dfa0114cd.js +0 -79
  549. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d9babd8795f396a5.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/app/approvals/page-5ab1e47978577c5e.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-24453f6a302816b4.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/app/board/page-6c131d30bb1cf871.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/app/bots/page-dc0772b11861af51.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/app/chat/page-5159dc8752c6cd29.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/app/consult/page-c5a2478861c08b87.js +0 -1
  556. package/apps/pwa/out/_next/static/chunks/app/control/page-f722a9dec084e08b.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/app/decisions/page-256ffd365d32e100.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/app/design-check/page-e0de78ae7b99f01c.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/app/editor/page-cfbdc44b708d9801.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/app/exec/page-4c741eb760f2f77c.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/app/field/page-e9ba98f5f1440a21.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/app/healthz/route-d9babd8795f396a5.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/app/inside/page-dafc901bb1b72b5f.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/app/intake/page-2f6f303fa114ec24.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/app/layout-1634081c5272006c.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/app/live/page-ced0bcfc6542b3fc.js +0 -1
  567. package/apps/pwa/out/_next/static/chunks/app/live-run/page-bb1f05cd51c38944.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/app/market/page-db069b48a59c8c00.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-f736324afa2117c1.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/app/not-found-435f5e2faf02bc01.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/app/observatory/page-497e0eedf20e7890.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-a8d28fdf995e16ea.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8966452228753c5e.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fd5d18656cb432b6.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/app/outputs/page-5663779cb1a37609.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/app/page-66a67eb19ebd976b.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/app/reflection/page-3b492aa78cf3c4a6.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-c71267e7681da982.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/app/sessions/page-a21d1e1797172692.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-cb249e81ed339318.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/app/settings/page-781cfc5114e097da.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-478157214db6a023.js +0 -1
  583. package/apps/pwa/out/_next/static/chunks/app/setup/layout-d9babd8795f396a5.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/app/setup/page-86141f12cf090b7f.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/app/share/page-7256c73bb1c36751.js +0 -1
  586. package/apps/pwa/out/_next/static/chunks/app/showroom/page-5c738ca7adef3de5.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/app/tasks/page-0138c8091ec17224.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/app/term/page-cc2f24f16d85e735.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/app/today/page-979d318a534e92da.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/app/trace/page-182d394f7e6e096b.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/app/vault/page-21050928a697ed7a.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-44b9d807005a9030.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/app/workflows/page-601fb77340e63c9a.js +0 -1
  594. package/apps/pwa/out/_next/static/chunks/app/workspace/page-75d3fb5d47af0edf.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-11bf9e5725a72c22.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/dcf24383-2f82e35f2d827393.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/e117f801.e92bf10242653497.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/e87d89b9.2e71c0ae475d28a1.js +0 -122
  599. package/apps/pwa/out/_next/static/chunks/framework-bf135b997155e90e.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/main-5571537543bea91c.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/main-app-25c51c7df7f3a442.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/pages/_app-e3b2c1f5d82abd65.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/pages/_error-083f2e23e9bc3641.js +0 -1
  604. package/apps/pwa/out/_next/static/chunks/webpack-e94d22b7a028217b.js +0 -1
  605. package/src/dashboard/bench-slash-runtime.ts +0 -40
  606. package/src/dashboard/input/search-modal-router.ts +0 -9
  607. package/src/dashboard/modals/pane-key-router.ts +0 -27
  608. package/src/dashboard/view-slash-runtime.ts +0 -48
  609. /package/apps/pwa/out/_next/static/{y1ltt7oCZxVpC6zVPhZ2O → 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, planShards, readFileMemory } from './shard-plan';
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,6 +119,7 @@ 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;
@@ -274,20 +282,70 @@ export function shardParts(paths: readonly string[], seconds: (file: string) =>
274
282
  if (current.length) parts.push(current);
275
283
  return parts;
276
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
+ }
277
307
  /**
278
308
  * The shard's Pod shell. Install once (behind the node's install slots), then run each part under the soft deadline,
279
309
  * leaving `install.log`/`install.rc`, `part-<i>.log`/`.rc`/`.junit.xml`, and `shard.timeout` (the part index) when the
280
- * deadline cut the shard. A part without `.rc` did not finish. The shell itself always exits 0 (as before).
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).
281
313
  */
282
- export function gateShardShell(o: { parts: readonly (readonly string[])[]; cdpPatterns: readonly string[]; softSeconds: number; cachePrefix: string; installSlots: number; depsDir?: string; beatSeconds?: number }): string {
283
- const run = o.parts.map((part, index) => {
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) => {
284
321
  const ignores = o.cdpPatterns.filter((pattern) => part.includes(pattern)).flatMap((pattern) => ['--path-ignore-patterns', pattern]);
285
- return `run_part ${index} ${['bun', 'run', 'test:deterministic', ...ignores, ...part.map(asPath)].map(quote).join(' ')}`;
286
- }).join(' && ');
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('; ');
287
332
  return [
288
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',
289
346
  ...(o.beatSeconds ? gateBeatScript(o.beatSeconds, o.installSlots > 0) : []),
290
347
  ...(o.cachePrefix ? [o.cachePrefix] : []),
348
+ gateEnvBaselineLine(),
291
349
  // Seconds since this container's PID 1 started (the clone ran before this shell) — 0 when /proc cannot tell.
292
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)`,
293
351
  'case "$pod_age" in ""|*[!0-9]*) pod_age=0;; esac',
@@ -295,19 +353,45 @@ export function gateShardShell(o: { parts: readonly (readonly string[])[]; cdpPa
295
353
  ...(o.installSlots > 0 ? [installSlotScript({ slots: o.installSlots })] : []),
296
354
  'run_part() {',
297
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=()']),
298
371
  ' local left=$(( gate_end - $(date +%s) ))',
299
372
  ' if [ "$left" -le 0 ]; then echo "$i" > "$O/shard.timeout"; return 1; fi',
300
- ' timeout -k 10 "$left" "$@" --reporter=junit --reporter-outfile="$O/part-$i.junit.xml" > "$O/part-$i.log" 2>&1',
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',
301
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'] : []),
302
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',
303
384
  ' echo "$prc" > "$O/part-$i.rc"',
304
385
  '}',
305
386
  'if cd .. && cd repo; then',
387
+ ' stage deps-start',
306
388
  // GATE-IMAGE-DEPS: link the image's baked node_modules when the lockfiles match; only a miss installs (behind a slot).
307
389
  ...gateDepsInstallScript({ log: '"$O/install.log"', installSlots: o.installSlots, ...(o.depsDir ? { depsDir: o.depsDir } : {}) }).split('\n').map((line) => ` ${line}`),
308
390
  ' echo "$irc" > "$O/install.rc"',
309
- ` if [ "$irc" -eq 0 ]; then ${run || 'true'}; fi`,
391
+ ' stage deps-end',
392
+ ` if [ "$irc" -eq 0 ]; then stage test-start; ${width > 1 ? 'pids=(); ' : ''}${run || 'true'}; stage test-end; fi`,
310
393
  'else echo 5 > "$O/install.rc"; fi',
394
+ 'stage shell-end',
311
395
  'true',
312
396
  ].join('\n');
313
397
  }
@@ -355,6 +439,50 @@ export function readShardLiveLog(text: string): ShardLiveLog {
355
439
  }
356
440
  return { checkout, ...(beat ? { beat } : {}) };
357
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
+ }
358
486
  /**
359
487
  * GATE-POD-STALL-FAILFAST — watches one running shard Job. Armed once the source checkout line is seen; disarmed for good
360
488
  * the moment any test output exists (that shard then behaves exactly as before). Stalled = `stallSeconds` since the
@@ -645,6 +773,8 @@ class StalledPodShards extends Error {
645
773
 
646
774
  /** Isolated re-runs (host stall re-run · cut and baseline re-checks) are time-limited; sweeps and installs keep their full time. */
647
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;
648
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.
649
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. */
650
780
  export const GATE_TIMEOUT_RECHECK_MS = 60_000;
@@ -807,11 +937,12 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
807
937
  // The TD1 measurements predate the fixture-only gate; do not isolate those five files at their old 2–13 GB peaks.
808
938
  const fixtureOnly = new Set([...GATE_NIGHTLY_AUDITS, 'test/guardian/dispatch-surface-contract.test.ts', 'test/user-config-mcp.test.ts']);
809
939
  const heavy = new Set(assignable.filter((file) => !fixtureOnly.has(file) && (memory.get(file) ?? 0) >= POD_HEAVY_FILE_MB));
810
- const light = assignable.filter((file) => !heavy.has(file));
940
+ const { shards: lightShards, spawnFiles: spawnLane, spawnShardCount } = planGateSpawnSeparatedShards(assignable.filter((file) => !heavy.has(file)), durations, shardCount);
811
941
  const shards = [
812
- ...(light.length ? planShards(light, durations, shardCount) : []),
942
+ ...lightShards,
813
943
  ...[...heavy].map((file) => ({ files: [file], plannedSeconds: durations.get(file) ?? 0 })),
814
944
  ];
945
+ if (spawnLane.length) debug.log('release-loop.gate', 'pod-spawn-lane', { files: spawnLane, shards: spawnShardCount });
815
946
  logPlanWalltime(planWall, shards);
816
947
  if (heavy.size) debug.log('release-loop.gate', 'pod-heavy-alone', { files: [...heavy], thresholdMb: POD_HEAVY_FILE_MB, memoryLimit: POD_ISOLATION_MEMORY_LIMIT });
817
948
  const known = assignable.filter((file) => durations.has(file)).length;
@@ -822,7 +953,10 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
822
953
  });
823
954
  // GATE-SPEED A3①: shard Pod CPU (`release.loop.gatePodCpu` · default request 1 / limit 4) — shards × request must fit the pool.
824
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)));
825
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 });
826
960
  const ready = poolOverride ? undefined : checkPodPool(parsePodPool(pod.pool), defaultKubectl);
827
961
  if (ready && !ready.ok) throw new Error('pod command: 풀의 노드가 하나도 준비되지 않았다');
828
962
  const poolScheduler = poolOverride ?? new PodPoolScheduler(ready!.ready);
@@ -895,6 +1029,8 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
895
1029
  // 깊이 끝 OOM 조각은 «조각 전체»를 파일 하나씩 격리한다 — 상한 40 이면 나머지가 못 잰 채 남았다(09-30 ① 7번 조각 110 파일).
896
1030
  const isolationRemaining = shards.map((item) => item.files.length);
897
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 });
898
1034
  const estimated = (paths: string[]) => paths.reduce((sum, file) => {
899
1035
  const value = durations.get(file);
900
1036
  return sum + (value !== undefined && Number.isFinite(value) && value >= 0 ? value : unknownSeconds);
@@ -935,13 +1071,13 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
935
1071
  },
936
1072
  release: (member: Parameters<PodPoolScheduler['release']>[0]) => poolScheduler.release(member),
937
1073
  }) as unknown as PodPoolScheduler;
938
- 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']> }> => {
939
1075
  // 파일별 소요는 junit 으로 남긴다 — 콘솔 요약(판정 원천)은 그대로이고, 느린 시험 목록(K10 D4)·계층 분리(D2)의 자가 된다.
940
1076
  // GATE-TIMEOUT-HEAL: parts (each with its own junit) under a soft deadline inside this shard's own Job deadline.
941
- const shardDeadline = shardDeadlineSeconds(estimated(paths), deadlineSeconds);
942
- const parts = shardParts(paths, (file) => estimated([file]));
943
- const memoryLimit = paths.length === 1 && (depth >= 1 || heavy.has(paths[0]!)) ? POD_ISOLATION_MEMORY_LIMIT : undefined;
944
- 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 } : {}) };
945
1081
  const keyShell = gateShardShell(shellOptions);
946
1082
  // The stall beat only reports; runKey keeps hashing the shell without it, so earlier results stay reusable.
947
1083
  const podShell = stallWatch ? gateShardShell({ ...shellOptions, beatSeconds: gateStallBeatSeconds(stallSeconds) }) : keyShell;
@@ -949,9 +1085,15 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
949
1085
  let start = Date.now();
950
1086
  let admitted: GateAdmissionResult | undefined;
951
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;
952
1093
  let junit: string | undefined;
953
1094
  let rc: number | undefined;
954
1095
  let jobExitCode: number | undefined;
1096
+ let batchArtifactDir: string | undefined;
955
1097
  let lastFile: string | undefined;
956
1098
  let jobFailed = false;
957
1099
  let jobError = '';
@@ -967,7 +1109,8 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
967
1109
  // cap chunks for a big root shard · halves otherwise · none for one file).
968
1110
  // Isolation follows only while this shard's isolation budget still covers every file (review r7) — the budget is the
969
1111
  // shard's file count and isolations take disjoint files, so this holds unless the plan changed; if not, run the Pod.
970
- const splitChild = depth === 2 && paths.length > 1 ? (isolationRemaining[shard]! >= paths.length ? '-file-0' : undefined)
1112
+ const splitChild = depth === 2 && paths.length > 1 ? (isolationRemaining[shard]! >= paths.length
1113
+ ? batchIsolation || !paths.every((file) => batchSafe.has(file)) ? '-file-0' : '-batch' : undefined)
971
1114
  : depth < 2 && paths.length > 1 ? (depth === 0 && paths.length > POD_SHARD_FILE_CAP ? '-c0' : '-0') : undefined;
972
1115
  const found = !fresh && logDir ? previousShardDescent(logDir, `pod-${shard}${branch}`, commit, paths, runKey, parentAttempt, splitChild) : undefined;
973
1116
  const descent = found?.way;
@@ -977,7 +1120,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
977
1120
  if (descent === 'retry') {
978
1121
  // «descend» is only the way down; «resumed» comes from each leaf whose log reads cleanly (review r4).
979
1122
  debug.log('release-loop.gate', 'pod-shard-descend', { shard, branch, files: paths.length, via: 'retry' });
980
- return runShard(paths, shard, depth, `${branch}-retry`, true, undefined, attemptId);
1123
+ return runShard(paths, shard, depth, `${branch}-retry`, true, undefined, attemptId, hostRetries, batchIsolation);
981
1124
  }
982
1125
  if (descent === 'split') {
983
1126
  output = undefined;
@@ -989,7 +1132,9 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
989
1132
  jobExitCode = 0;
990
1133
  lastFile = lastStartedTestFile(output, paths);
991
1134
  } else try {
1135
+ stamp('admissionStart');
992
1136
  admitted = await admitGateShard(gateAdmission, { waitMs: admissionWaitSeconds * 1000, onWait: (reason) => markShard(shard, { waitReason: reason }) });
1137
+ stamp('admissionEnd');
993
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 } : {}) },
994
1139
  admitted.outcome === 'granted' ? undefined : { level: 'warn' });
995
1140
  // The admission wait is not the shard's run time: deadline/timeout judgment and the measured duration start here.
@@ -1007,7 +1152,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
1007
1152
  } }) : undefined;
1008
1153
  const placed = admitted.release;
1009
1154
  const job = await podCommand({
1010
- 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,
1011
1156
  ...(multiHost ? { statusUnreadableMs: memberUnreadableSeconds * 1000 } : {}),
1012
1157
  ...(bunCache ? { bunCache } : {}),
1013
1158
  ...(installSlots > 0 ? { installSlots: POD_INSTALL_SLOTS_HOST_PATH } : {}),
@@ -1017,8 +1162,30 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
1017
1162
  name: jobName,
1018
1163
  command: ['bash', '-lc', podShell],
1019
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
+ }
1020
1172
  jobExitCode = job.exitCode;
1173
+ batchArtifactDir = job.artifactsDir;
1021
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');
1022
1189
  ({ output, junit, rc, timedOut, finished, unfinishedLog, exitCodeOnly } = read);
1023
1190
  // A stall counts only when the Job also left no verdict (a failed stop can let a late shard finish after all).
1024
1191
  const stalledAt = watch?.stalled();
@@ -1039,7 +1206,12 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
1039
1206
  const durationMs = Date.now() - start;
1040
1207
  const fileSeconds = junit === undefined ? undefined : Object.fromEntries(readJunitFileSeconds(junit));
1041
1208
  const testSeconds = fileSeconds === undefined ? undefined : Object.values(fileSeconds).reduce((sum, seconds) => sum + seconds, 0);
1042
- const podOverheadSeconds = testSeconds === undefined ? undefined : Math.max(0, durationMs / 1000 - testSeconds);
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;
1043
1215
  // A reused shard keeps its files as they are — rewriting would replace the measured durationMs the next release plans by.
1044
1216
  if (logDir && !reused && !descent) {
1045
1217
  mkdirSync(logDir, { recursive: true });
@@ -1057,7 +1229,10 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
1057
1229
  else rmSync(unfinishedDestination, { force: true });
1058
1230
  writeFileSync(join(logDir, `pod-${shard}${branch}.json`), JSON.stringify({ durationMs, rc: rc ?? null,
1059
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 } : {}),
1060
- ...(fileSeconds !== undefined ? { fileSeconds, testSeconds, podOverheadSeconds } : {}),
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 } : {}),
1061
1236
  ...(admitted && admitted.outcome !== 'off' ? { admission: admitted.outcome, admissionWaitMs: admitted.waitedMs } : {}) }) + '\n', { mode: 0o600 });
1062
1237
  }
1063
1238
  const installSeconds = parseInstallSeconds(output ?? '');
@@ -1078,6 +1253,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
1078
1253
  // GATE-POD-STALL-FAILFAST: stopped with no test output — the existing «no output» reason (one fresh retry).
1079
1254
  else if (stall) reason = 'no-output';
1080
1255
  else if (timedOut) reason = 'timeout';
1256
+ else if (batchIsolation && !batchArtifactsComplete) reason = 'incomplete';
1081
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
1082
1258
  // anomaly (0.2.23: a test left process.exitCode set → «0 fail · rc 3» read as a stalled shard). Same split/isolation
1083
1259
  // path as job-failed — only the label differs, so the verdict names the file instead of saying «stalled».
@@ -1116,14 +1292,14 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
1116
1292
  // A reused result that the current tool cannot read cleanly is not a verdict: run that shard for real.
1117
1293
  if (reused && reason) {
1118
1294
  debug.log('release-loop.gate', 'pod-shard-resume-miss', { shard, branch, reason });
1119
- return runShard(paths, shard, depth, branch, retriedNoOutput, true, parentAttempt);
1295
+ return runShard(paths, shard, depth, branch, retriedNoOutput, true, parentAttempt, hostRetries, batchIsolation);
1120
1296
  }
1121
1297
  // GATE-MULTI-HOST: the Job failed because its member is gone (launch failed · status unreadable) — not the shard's
1122
1298
  // fault, so no split: the same files run again on the members left. Bounded by the roster size.
1123
1299
  if (jobFailed && placedOn && hostRetries < poolScheduler.members.length && memberLost(placedOn, shard, jobError)
1124
1300
  && poolScheduler.members.some((m) => !downMembers.has(m.context))) {
1125
1301
  debug.log('release-loop.gate', 'pod-shard-host-retry', { shard, branch, files: paths.length, lostMember: placedOn, attempt: hostRetries + 1 }, { level: 'warn' });
1126
- 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);
1127
1303
  }
1128
1304
  // GATE-TIMEOUT-HEAL ④: a Pod that died before any test ran (no outbox · jobExitCode≠0 — 0.2.19: isolated retries
1129
1305
  // ending with exit 5, the source checkout) gets the same one fresh retry as «no output» instead of stalling the sweep.
@@ -1131,13 +1307,45 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
1131
1307
  const diedBeforeTests = reason === 'job-failed' && output === undefined && !jobFailed && jobExitCode !== undefined && jobExitCode !== 0
1132
1308
  && jobExitCode !== 137 && (paths.length === 1 || jobExitCode === 5);
1133
1309
  if ((reason === 'no-output' || diedBeforeTests) && !retriedNoOutput) {
1134
- 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);
1135
1311
  debug.log('release-loop.gate', 'pod-shard-retry', { shard, files: paths, reason, outcome: retry.stalled[0]?.reason ?? 'ok' });
1136
1312
  return retry;
1137
1313
  }
1138
1314
  // GATE-POD-STALL-FAILFAST: a stall after the fresh retry is unmeasured — no split, no isolation: smaller Pods on the
1139
1315
  // same stalled node only re-measure the stall (10-10: retries and splits repeated it for 2h+).
1140
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
+ }
1141
1349
  if (reason) {
1142
1350
  // GATE-SKIP-KNOWN-RETRY — a shard whose summary is complete but whose failures could not be read back (unattributed)
1143
1351
  // and whose every named failure is one the previous release already had is not re-run: re-running only re-measures
@@ -1162,7 +1370,8 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
1162
1370
  debug.log('release-loop.gate', 'retry-narrowed', { shard, before: paths.length, after: narrowed.length });
1163
1371
  const kept = new Set(paths.filter((file) => !narrowed.includes(file)));
1164
1372
  const passedIds = junitPassedIds(junit!).map(podRelative).filter((id) => { try { return kept.has(testFileOfId(id)); } catch { return false; } });
1165
- const child = await runShard(narrowed, shard, depth, `${branch}-n`, false, undefined, attemptId);
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);
1166
1375
  return { runs: [{ output: '', rc: 0, pass: passedIds.length, fail: 0, errors: 0, ran: passedIds.length, files: kept.size, passedIds },
1167
1376
  ...child.runs], stalled: child.stalled };
1168
1377
  }
@@ -1171,19 +1380,27 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
1171
1380
  if (depth === 2 && paths.length > 1) {
1172
1381
  const count = Math.min(paths.length, isolationRemaining[shard]!);
1173
1382
  isolationRemaining[shard]! -= count;
1174
- const children = await Promise.all(paths.slice(0, count).map(async (file, index) => {
1175
- const child = await runShard([file], shard, depth + 1, `${branch}-file-${index}`, false, undefined, attemptId);
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);
1176
1391
  debug.log('release-loop.gate', 'pod-shard-isolate', { shard, file, outcome: child.stalled[0]?.reason ?? 'ok' });
1177
1392
  return child;
1178
- }));
1179
- return { runs: children.flatMap((child) => child.runs), stalled: [
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: [
1180
1396
  ...children.flatMap((child) => child.stalled),
1397
+ ...(batch?.stalled.filter((item) => item.files.some((file) => !retryPaths.includes(file))) ?? []),
1181
1398
  ...(paths.length > count ? [{ shard, files: paths.slice(count), reason,
1182
1399
  ...(lastFile ? { lastFile } : {}),
1183
1400
  }] : []),
1184
1401
  ] };
1185
1402
  }
1186
- if (depth === 2 || paths.length === 1) return { runs: [], stalled: [{ shard, files: paths, reason,
1403
+ if (depth >= 2 || paths.length === 1) return { runs: [], stalled: [{ shard, files: paths, reason,
1187
1404
  ...(lastFile ? { lastFile } : {}),
1188
1405
  ...(reason === 'unattributed' ? { summaryFailures: fails, namedFailures, ...(unattributedDetail ? { detail: unattributedDetail } : {}) } : {}),
1189
1406
  }] };
@@ -1193,14 +1410,19 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
1193
1410
  if (depth === 0 && paths.length > POD_SHARD_FILE_CAP) {
1194
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));
1195
1412
  debug.log('release-loop.gate', 'pod-shard-cap-split', { shard, files: paths.length, chunks: chunks.length, cap: POD_SHARD_FILE_CAP, reason });
1196
- const children = await Promise.all(chunks.map((chunk, i) => runShard(chunk, shard, 2, `${branch}-c${i}`, false, undefined, attemptId)));
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)))));
1197
1417
  return { runs: children.flatMap((child) => child.runs), stalled: children.flatMap((child) => child.stalled) };
1198
1418
  }
1199
1419
  debug.log('release-loop.gate', 'pod-shard-split', { shard, depth, files: paths, reason });
1200
1420
  const middle = Math.ceil(paths.length / 2);
1201
1421
  const children = await Promise.all([
1202
- runShard(paths.slice(0, middle), shard, depth + 1, `${branch}-0`, false, undefined, attemptId),
1203
- runShard(paths.slice(middle), shard, depth + 1, `${branch}-1`, false, undefined, attemptId),
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))),
1204
1426
  ]);
1205
1427
  return { runs: children.flatMap((child) => child.runs), stalled: children.flatMap((child) => child.stalled) };
1206
1428
  }
@@ -1625,7 +1847,15 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
1625
1847
  try { saveBaselineFileCache(ledger, row.commit, [row]); }
1626
1848
  catch (error) { debug.log('release.gate', 'baseline-cache-write-failed', { commit: row.commit, file: row.file, error: String(error) }); }
1627
1849
  };
1628
- for (const file of newFailureFiles) {
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) });
1629
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.
1630
1860
  const cutCheck = cutHostFailed.has(file) ? undefined : await runner.command('bun', ['run', 'test:deterministic', asPath(file)], cutTree, GATE_ISOLATED_TIMEOUT_MS);
1631
1861
  if (cutCheck?.timedOut) {
@@ -1716,7 +1946,18 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
1716
1946
  result.preexisting = preexistingIds.length;
1717
1947
  result.baselineCache = cacheStats;
1718
1948
  debug.log('release.gate', 'baseline-cache', { hits: cacheStats.hits, misses: cacheStats.misses, commit: baseSha });
1719
- result.outcome = result.introduced.length ? 'regression' : 'ok';
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
+ }
1720
1961
  } catch (error) {
1721
1962
  result.error = error instanceof Error ? error.message : String(error);
1722
1963
  if (error instanceof StalledPodShards) {
@@ -1758,31 +1999,51 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
1758
1999
  result.error = `baseline persistence: ${String(error)}`;
1759
2000
  }
1760
2001
  }
1761
- if (result.outcome !== 'error') {
2002
+ {
1762
2003
  const ids = {
1763
2004
  introduced: [...result.introduced].sort(),
1764
2005
  knownEnv: [...knownEnvIds].sort(),
1765
2006
  preexisting: [...preexistingIds].sort(),
1766
2007
  loadFlaky: [...(result.loadFlaky ?? [])].sort(),
2008
+ ...(result.unmeasuredRecheckFiles?.length ? { unmeasuredRecheckFiles: [...result.unmeasuredRecheckFiles].sort() } : {}),
1767
2009
  };
1768
2010
  try {
1769
- const path = verdictFile(ledger, opts.version);
1770
- mkdirSync(dirname(path), { recursive: true });
1771
- const temp = join(dirname(path), `.gate-verdict-${process.pid}-${randomUUID()}.tmp`);
1772
- try {
1773
- writeFileSync(temp, JSON.stringify({ version: opts.version, commit: opts.commit, outcome: result.outcome, ...ids }, null, 2) + '\n', { mode: 0o600 });
1774
- chmodSync(temp, 0o600);
1775
- renameSync(temp, path);
1776
- } finally { if (existsSync(temp)) rmSync(temp); }
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
+ }
1777
2025
  } catch (error) {
1778
2026
  result.outcome = 'error';
1779
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
+ }
1780
2040
  }
1781
- if (result.outcome !== 'error') debug.log('release-loop.gate', 'judged', {
1782
- version: opts.version, commit: opts.commit,
2041
+ debug.log('release-loop.gate', 'judged', {
2042
+ version: opts.version, commit: opts.commit, outcome: result.outcome,
1783
2043
  introduced: result.introduced.length, preexisting: result.preexisting,
1784
2044
  knownEnv: result.knownEnv, loadFlaky: ids.loadFlaky.length,
1785
2045
  introducedIds: ids.introduced, knownEnvIds: ids.knownEnv, preexistingIds: ids.preexisting, loadFlakyIds: ids.loadFlaky,
2046
+ ...(result.outcome === 'error' ? { error: result.error } : {}),
1786
2047
  });
1787
2048
  }
1788
2049
  // GATE-LIVE-OBS-RC-WORDING ⓒ — the shards board says «판정 전» until this lands; then «새 실패 N» only (UX · gate-shards.ts).
@@ -1843,6 +2104,7 @@ export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOption
1843
2104
  // GATE-HOST-EXECUTOR: a host gate never reads the pod settings (a bad pod value must not block the fallback executor).
1844
2105
  const hostExecutor = fromGraph.gateExecutor === 'host';
1845
2106
  const opts: GateOptions = {
2107
+ rehearsalTui: fromGraph.rehearsalTui === true,
1846
2108
  commit: typeof fromGraph.commit === 'string' ? fromGraph.commit : '',
1847
2109
  version: typeof fromGraph.version === 'string' ? fromGraph.version : '',
1848
2110
  baselineVersion: typeof fromGraph.previousVersion === 'string' ? fromGraph.previousVersion : undefined,
@@ -1870,6 +2132,7 @@ export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOption
1870
2132
  if (arg === '--json') continue;
1871
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).
1872
2134
  if (arg === '--fresh-shards') { freshShards = true; continue; }
2135
+ if (arg === '--rehearsal-tui') { opts.rehearsalTui = true; continue; }
1873
2136
  if (arg === '--executor') {
1874
2137
  const value = args[++i];
1875
2138
  if (!isGateExecutor(value)) throw new Error(`--executor must be pod or host: ${value ?? ''}`);
@@ -1920,6 +2183,7 @@ export async function runGateNode(argv: string[], env: NodeJS.ProcessEnv, runner
1920
2183
  return 0;
1921
2184
  }
1922
2185
  version = opts.version;
2186
+ // The graph's existing gate-node execution path delegates verdict classification and persistence here.
1923
2187
  result = await (runner ? judgeGate(opts, runner) : judgeGate(opts));
1924
2188
  } catch (error) {
1925
2189
  result = { outcome: 'error', commit: '', introduced: [], preexisting: 0, fixed: 0, knownEnv: 0, knownEnvCleared: [], stalledEnv: [], rcNonzeroNoFailures: [], durationMs: Date.now() - start, error: String(error) };