elanous 0.2.29 → 0.2.31

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 (709) 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/{cIpv5nU9AycwYWZI9H589 → LSXfY0u9QtfDbejY0Ukf_}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{058a98ba.7eb28d5e6dc03565.js → 0bb0ff36.b8d418980951e5ba.js} +7 -7
  5. package/apps/pwa/out/_next/static/chunks/1034-8b639cee57b8e4a8.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1085-6c2d35526f11f840.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1220.1ff7d5cdc34b1ae5.js +27 -0
  8. package/apps/pwa/out/_next/static/chunks/1247.5884ec81b39fe2e7.js +287 -0
  9. package/apps/pwa/out/_next/static/chunks/1302.87681521a7a4661e.js +62 -0
  10. package/apps/pwa/out/_next/static/chunks/1324-59dae3002413623b.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/{6013-2f769370d8b4c2c6.js → 1335-6731f392c35216eb.js} +3 -3
  12. package/apps/pwa/out/_next/static/chunks/1343-f40b5870ac1eda5a.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/146-b6cd56be11dda017.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/{8332.6f0f49df772e163b.js → 1654.32de4b324981a188.js} +1 -1
  15. package/apps/pwa/out/_next/static/chunks/1834-50625904996a8fa8.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/1849.1ce38efa6c527d9a.js +149 -0
  17. package/apps/pwa/out/_next/static/chunks/{8795.32573a185af18c7b.js → 1913.7cc6fb6055b1ed3f.js} +2 -2
  18. package/apps/pwa/out/_next/static/chunks/{419.736c0d6f426249a7.js → 2001.6c7e0fd02190886c.js} +1 -1
  19. package/apps/pwa/out/_next/static/chunks/2030.41fae98c7e907304.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/2099-08b2f2dfba742c9b.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/2112-e4efc21a6505d5bc.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/2134.ecee03a550ee7250.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/{6767.1fcfc9b8be83975f.js → 2213.2ccbada92c33ea0f.js} +16 -16
  24. package/apps/pwa/out/_next/static/chunks/2236.66061832181cb745.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/2422.b0bd272e7020d7e6.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/2534.e85ae21ad0dac829.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/{4759.1f76713aefb87db5.js → 2871.0bca6137343b3f76.js} +1 -1
  28. package/apps/pwa/out/_next/static/chunks/{6776.06ff1e3b464cc607.js → 2898.fd781bc7cf01a7b7.js} +1 -1
  29. package/apps/pwa/out/_next/static/chunks/{1782-59db1b5aa06df38a.js → 2940-619bfd8ef337c611.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/{8471.8d2a8c74f09c36ff.js → 2985.39ccdecd36848b12.js} +4 -4
  31. package/apps/pwa/out/_next/static/chunks/3040.92ff9ffc9f971cb9.js +115 -0
  32. package/apps/pwa/out/_next/static/chunks/3144.6861096c7b4edef7.js +64 -0
  33. package/apps/pwa/out/_next/static/chunks/{bc324b7c.02a09dd93f6a399b.js → 3267c0e6.d34854e7b3984a6e.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/3278.2e5d15b53a4091c8.js +2 -0
  35. package/apps/pwa/out/_next/static/chunks/{7725-4f83d5541fbdb81b.js → 3541-ce3a5424ed5ad7f1.js} +1 -1
  36. package/apps/pwa/out/_next/static/chunks/3583.a05e88cc8d594628.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/3771.e928edf2867d28c0.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/3902.e1e71aaac850a045.js +34 -0
  39. package/apps/pwa/out/_next/static/chunks/4063-03284b5de59965f3.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/{46bd8bea.2f4b4099172fcdb7.js → 409f1e57.d0649fc47691ebfe.js} +1 -1
  41. package/apps/pwa/out/_next/static/chunks/4111.2bfedbb233ad206b.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/{1142.65d002789ab3af7d.js → 4174.92ed04922709ac9e.js} +2 -2
  43. package/apps/pwa/out/_next/static/chunks/4300-c7ec4eeabbb5f3f7.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/4483.646d5065199e1b55.js +36 -0
  45. package/apps/pwa/out/_next/static/chunks/4519.28199ff26b2fd80d.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/4581.ef2443578054544f.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/4608-8d894e9cbe4f08bf.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/4639.ae48a01d1bb50a93.js +286 -0
  49. package/apps/pwa/out/_next/static/chunks/4739.08742ac668aada67.js +77 -0
  50. package/apps/pwa/out/_next/static/chunks/4745.67d6f99c245af3f7.js +226 -0
  51. package/apps/pwa/out/_next/static/chunks/4882-16cef71d6a1a32be.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/4999.c064a4000c2a9e94.js +79 -0
  53. package/apps/pwa/out/_next/static/chunks/5173-233188df2f42c626.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/5260-69f35419bca263e7.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/5272.b92d1b72a15c25fe.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/5304.04d24756f242a094.js +148 -0
  57. package/apps/pwa/out/_next/static/chunks/5307.60f2209e809bd627.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/5415-997980f828667736.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/{3396-97b97928c49190c9.js → 5458-16cd7a2b51b74c8c.js} +1 -1
  60. package/apps/pwa/out/_next/static/chunks/{3403.7199205e65124486.js → 5525.86debf231e181813.js} +1 -1
  61. package/apps/pwa/out/_next/static/chunks/{9143.ff14585db1d275ff.js → 5543.a438b8ab5a13ea47.js} +1 -1
  62. package/apps/pwa/out/_next/static/chunks/5572-42c9084f27ca761e.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/5661-80470aac963d9f36.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/5723.421338408b5c30c4.js +74 -0
  65. package/apps/pwa/out/_next/static/chunks/5809.64c3664267c50bce.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/6000.64bc21ac8627f2f5.js +94 -0
  67. package/apps/pwa/out/_next/static/chunks/6013.39459b9c68eabf5f.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/607-06466dff012b03ef.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/{8398.73a19b39715eda04.js → 6092.a7f99e68b188ca66.js} +1 -1
  70. package/apps/pwa/out/_next/static/chunks/60fb96c9.360b01d8d4927b7b.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/{5591.a35e7a217734a8b9.js → 6157.0c9af021de4fb31b.js} +2 -2
  72. package/apps/pwa/out/_next/static/chunks/6192.a190b989b81e9743.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/6323.8672b4817a306928.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/{7375.050ea0d72968e49f.js → 6433.fc573af22bbc9ab7.js} +17 -17
  75. package/apps/pwa/out/_next/static/chunks/6444.7d1fdcb9b09ae71c.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/6517.4147878ac06d584d.js +90 -0
  77. package/apps/pwa/out/_next/static/chunks/6532.e34302010b2f0b68.js +32 -0
  78. package/apps/pwa/out/_next/static/chunks/6544cb48.a87fe20ddf137be2.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/6546.74bc4ed82f19016d.js +104 -0
  80. package/apps/pwa/out/_next/static/chunks/6684-e7523ce7c2fad075.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/6726.ebdc82feb161431e.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/6739.00fb6f892e5e392b.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/6745.934c4921bf15513a.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/6767.ea607cf0c20a25af.js +24 -0
  85. package/apps/pwa/out/_next/static/chunks/6843.67bfd020da0926c5.js +93 -0
  86. package/apps/pwa/out/_next/static/chunks/6948.1062d4594652b5bf.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/7138-eaf841fc1dc03463.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/7174-cda29ee13d7790e7.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/7304-3c07583f4e405f4f.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/7371.80c05c653b08e907.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/7391.ebae168025c91b6a.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/{1852.7f7b53991fcb99ef.js → 7502.ccd5b8065545edd0.js} +2 -2
  93. package/apps/pwa/out/_next/static/chunks/7557.e325503a090a4310.js +8 -0
  94. package/apps/pwa/out/_next/static/chunks/7569-9ce9432c7f10989f.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/{6534.1ab00f370db7b105.js → 7862.30a8d6482bb238eb.js} +1 -1
  96. package/apps/pwa/out/_next/static/chunks/7886-55854585fadeded1.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/7947-d0759430f8042240.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/{285adb1a.5654e605dddf86d2.js → 7a1887c4.f36b88fba31da8fd.js} +1 -1
  99. package/apps/pwa/out/_next/static/chunks/8018.a32663f04a6fc5f9.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/8097-92f87bd108322f66.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/8215-1661812b6e9ad71f.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/8260-19c921a51ca8ec3e.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/8440.3a5cf5c85aa76db8.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8519-4599be2249161e68.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/8547.b028b90c5c01ace6.js +1 -0
  106. package/apps/pwa/out/_next/static/chunks/8587-cbe02a953a7f4990.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/877-2b977259c4b15655.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/9002.e5658224e3dc1b6f.js +24 -0
  109. package/apps/pwa/out/_next/static/chunks/9019-0a4b40c50c7314fb.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/{1516.dfaceff3fd2d0e0d.js → 9138.b6e80031afb1d973.js} +1 -1
  111. package/apps/pwa/out/_next/static/chunks/9188-d36e4fc869ddc166.js +1 -0
  112. package/apps/pwa/out/_next/static/chunks/{9907.49be0fcadaf214b5.js → 9221.7d22eaf353a2d4e7.js} +1 -1
  113. package/apps/pwa/out/_next/static/chunks/9230.44f5039e796e4ab4.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/{8767-2f6a634f5abeaa01.js → 9279-d28c5b7754d010ae.js} +1 -1
  115. package/apps/pwa/out/_next/static/chunks/939.336acce73e98482c.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/{3160-95c96f4d2a21dd26.js → 9398-6aed28f0a96d0d6f.js} +1 -1
  117. package/apps/pwa/out/_next/static/chunks/9407.e49124dedf0f5e72.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/9610.c07cce4bfcc14ed7.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/9667-ce78c2ce91050ff9.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/{328bb3ab-6628a1114fe3fb3b.js → 997ce8a5-2ad501927003b41b.js} +1 -1
  121. package/apps/pwa/out/_next/static/chunks/{6853.cc5174a9dee59593.js → 9989.3694300e5f000b14.js} +1 -1
  122. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-339fabb3499da0e1.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/app/approvals/page-bf3634a117322df7.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-3535a4a69f1fafaa.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/board/page-b9f51447f757fbf2.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-a5635eba0d73b84e.js → page-2ebb8fd59b9461f9.js} +1 -1
  127. package/apps/pwa/out/_next/static/chunks/app/bots/page-a2fc4e49a4fa4ba8.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/ceo/page-5a503541353e60a8.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/chat/page-6025101384e361a5.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/consult/page-f4b630cc8b5273cd.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/control/page-a645f00a06483e9a.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/decisions/page-61ac22cbab1ee4ec.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/design-check/page-e9bab7413b028b28.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/editor/page-c992eafe028b642e.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/exec/page-e547e6ce55f1fb33.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/field/page-d27e1b9850fd54bf.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/healthz/route-339fabb3499da0e1.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/inside/page-805ac2e137b7a121.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/intake/page-0fec29c7ba01894b.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/layout-339a07410cd6f36b.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/live/page-349f2752d8cd37ad.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/live-run/page-d5c6dcf31c25cecc.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/loops/page-2fd93ed8c9f16580.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/market/page-9b564dbff0fefc35.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-62f4475d3c38fb9b.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/morning/page-814ba51385225776.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/not-found-397d479626e5a459.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/observatory/page-af1bba4e3251b728.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-3ad3be5e7d3fdce9.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-f219ccdbc0bf27df.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4da93ad35d4a8d28.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/outputs/page-456666766e5ce6fc.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/page-7404e377e8adb8c5.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/reflection/page-14456240ed2a36ad.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-3a658d5fa6d16a25.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/sessions/page-0157de6e85f8bb6b.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-0a07363b130182a1.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/settings/page-841d73d4ac44ed49.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-6f43a3d1146e6377.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/setup/layout-339fabb3499da0e1.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/{page-29900ed0011b2c83.js → page-521ee23ac8c0abfa.js} +1 -1
  162. package/apps/pwa/out/_next/static/chunks/app/share/page-a9992ee285efaa83.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/showroom/page-3d48e85fa89ec04f.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/tasks/page-d2ced71132fac9b2.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/term/page-f08358bdf251adb9.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/today/page-233c71cd5f68f0d7.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/trace/page-70151d78de2f375e.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/vault/page-dcac33a3e2cb72c0.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/{page-9f92737b8766009a.js → page-149dc1caba411bb9.js} +1 -1
  170. package/apps/pwa/out/_next/static/chunks/app/workflows/page-0448145ccd5c45c1.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workspace/page-9cb0c444903da65d.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-17e504c396e0e389.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/{425cfc4d.fb91158e3971c39f.js → b313b242.cb1265eea908db61.js} +1 -1
  174. package/apps/pwa/out/_next/static/chunks/e5012ffe.f727e23c231f4342.js +122 -0
  175. package/apps/pwa/out/_next/static/chunks/ea695e3c-1f1d70d740cf3031.js +1 -0
  176. package/apps/pwa/out/_next/static/chunks/ebc7d829.264a0bcb6c750b50.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/framework-906af9bd029e166c.js +1 -0
  178. package/apps/pwa/out/_next/static/chunks/main-a24847f9f684d469.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-app-7bdcd8aa7c8397d3.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/pages/_app-6ba46a5066fc226d.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_error-a8dc6d07591f2a25.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/webpack-f13df28c15c55eee.js +1 -0
  183. package/apps/pwa/out/_next/static/css/{72becfd42807b50d.css → 3f5fc6f96e2cf7b2.css} +1 -1
  184. package/apps/pwa/out/approvals/index.html +1 -1
  185. package/apps/pwa/out/approvals/index.txt +23 -23
  186. package/apps/pwa/out/autopilot/index.html +1 -1
  187. package/apps/pwa/out/autopilot/index.txt +23 -23
  188. package/apps/pwa/out/board/index.html +1 -1
  189. package/apps/pwa/out/board/index.txt +23 -23
  190. package/apps/pwa/out/botlab/index.html +1 -1
  191. package/apps/pwa/out/botlab/index.txt +22 -22
  192. package/apps/pwa/out/bots/index.html +1 -1
  193. package/apps/pwa/out/bots/index.txt +22 -22
  194. package/apps/pwa/out/ceo/index.html +1 -1
  195. package/apps/pwa/out/ceo/index.txt +23 -23
  196. package/apps/pwa/out/chat/index.html +1 -1
  197. package/apps/pwa/out/chat/index.txt +22 -22
  198. package/apps/pwa/out/consult/index.html +1 -1
  199. package/apps/pwa/out/consult/index.txt +23 -23
  200. package/apps/pwa/out/control/index.html +1 -1
  201. package/apps/pwa/out/control/index.txt +23 -23
  202. package/apps/pwa/out/decisions/index.html +1 -1
  203. package/apps/pwa/out/decisions/index.txt +22 -22
  204. package/apps/pwa/out/design-check/index.html +1 -1
  205. package/apps/pwa/out/design-check/index.txt +23 -23
  206. package/apps/pwa/out/editor/index.html +1 -1
  207. package/apps/pwa/out/editor/index.txt +23 -23
  208. package/apps/pwa/out/exec/index.html +1 -1
  209. package/apps/pwa/out/exec/index.txt +23 -23
  210. package/apps/pwa/out/field/index.html +1 -1
  211. package/apps/pwa/out/field/index.txt +23 -23
  212. package/apps/pwa/out/healthz +1 -1
  213. package/apps/pwa/out/index.html +1 -1
  214. package/apps/pwa/out/index.txt +22 -22
  215. package/apps/pwa/out/inside/index.html +1 -1
  216. package/apps/pwa/out/inside/index.txt +22 -22
  217. package/apps/pwa/out/intake/index.html +1 -1
  218. package/apps/pwa/out/intake/index.txt +23 -23
  219. package/apps/pwa/out/live/index.html +1 -1
  220. package/apps/pwa/out/live/index.txt +23 -23
  221. package/apps/pwa/out/live-run/index.html +1 -1
  222. package/apps/pwa/out/live-run/index.txt +23 -23
  223. package/apps/pwa/out/loops/index.html +1 -1
  224. package/apps/pwa/out/loops/index.txt +22 -22
  225. package/apps/pwa/out/market/index.html +1 -1
  226. package/apps/pwa/out/market/index.txt +22 -22
  227. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  228. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  229. package/apps/pwa/out/morning/index.html +1 -1
  230. package/apps/pwa/out/morning/index.txt +23 -23
  231. package/apps/pwa/out/observatory/index.html +1 -1
  232. package/apps/pwa/out/observatory/index.txt +23 -23
  233. package/apps/pwa/out/ops/checklist/index.html +1 -1
  234. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  235. package/apps/pwa/out/ops/release/index.html +1 -1
  236. package/apps/pwa/out/ops/release/index.txt +22 -22
  237. package/apps/pwa/out/ops/seats/index.html +1 -1
  238. package/apps/pwa/out/ops/seats/index.txt +22 -22
  239. package/apps/pwa/out/outputs/index.html +1 -1
  240. package/apps/pwa/out/outputs/index.txt +22 -22
  241. package/apps/pwa/out/reflection/index.html +1 -1
  242. package/apps/pwa/out/reflection/index.txt +23 -23
  243. package/apps/pwa/out/scheduler/index.html +1 -1
  244. package/apps/pwa/out/scheduler/index.txt +23 -23
  245. package/apps/pwa/out/sessions/index.html +1 -1
  246. package/apps/pwa/out/sessions/index.txt +22 -22
  247. package/apps/pwa/out/settings/devices/index.html +1 -1
  248. package/apps/pwa/out/settings/devices/index.txt +23 -23
  249. package/apps/pwa/out/settings/index.html +1 -1
  250. package/apps/pwa/out/settings/index.txt +23 -23
  251. package/apps/pwa/out/setup/done/index.html +1 -1
  252. package/apps/pwa/out/setup/done/index.txt +23 -23
  253. package/apps/pwa/out/setup/index.html +1 -1
  254. package/apps/pwa/out/setup/index.txt +23 -23
  255. package/apps/pwa/out/share/index.html +1 -1
  256. package/apps/pwa/out/share/index.txt +23 -23
  257. package/apps/pwa/out/showroom/index.html +1 -1
  258. package/apps/pwa/out/showroom/index.txt +22 -22
  259. package/apps/pwa/out/tasks/index.html +1 -1
  260. package/apps/pwa/out/tasks/index.txt +23 -23
  261. package/apps/pwa/out/term/index.html +1 -1
  262. package/apps/pwa/out/term/index.txt +23 -23
  263. package/apps/pwa/out/today/index.html +1 -1
  264. package/apps/pwa/out/today/index.txt +23 -23
  265. package/apps/pwa/out/trace/index.html +1 -1
  266. package/apps/pwa/out/trace/index.txt +23 -23
  267. package/apps/pwa/out/vault/index.html +1 -1
  268. package/apps/pwa/out/vault/index.txt +23 -23
  269. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  270. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  271. package/apps/pwa/out/workflows/index.html +1 -1
  272. package/apps/pwa/out/workflows/index.txt +23 -23
  273. package/apps/pwa/out/workspace/index.html +1 -1
  274. package/apps/pwa/out/workspace/index.txt +23 -23
  275. package/apps/pwa/out/worktrees/index.html +1 -1
  276. package/apps/pwa/out/worktrees/index.txt +23 -23
  277. package/bin/elanous.mjs +7 -3
  278. package/graphs/context/nightly-condense.yaml +1 -0
  279. package/graphs/intake/intake-daily.yaml +8 -1
  280. package/graphs/intake/recipes.yaml +5 -0
  281. package/graphs/lecture/lecture-note.yaml +2 -2
  282. package/graphs/ops/harness-gc.yaml +1 -0
  283. package/graphs/release/recipes.yaml +3 -0
  284. package/graphs/release/release-loop.yaml +9 -5
  285. package/graphs/units/recipes.yaml +3 -0
  286. package/graphs/units/release-gate-recheck.yaml +15 -0
  287. package/package.json +2 -1
  288. package/scripts/check-native-tool-wiring.ts +3 -2
  289. package/scripts/ci-android-unit-tests.ts +16 -0
  290. package/scripts/ci-typecheck-changed.ts +37 -9
  291. package/scripts/coord-channel-watch.sh +22 -16
  292. package/scripts/coord-role-pack.ts +81 -2
  293. package/scripts/cron-run.ts +96 -3
  294. package/scripts/detect-test-interference.ts +38 -5
  295. package/scripts/gate-pr-cache.ts +65 -0
  296. package/scripts/gh-surface/capture.ts +126 -0
  297. package/scripts/gh-surface/gh +40 -0
  298. package/scripts/import-graph-scc.ts +38 -0
  299. package/scripts/install.ps1 +19 -0
  300. package/scripts/intake-social.ts +181 -0
  301. package/scripts/lecture-note/node.ts +71 -4
  302. package/scripts/lib/android-toolchain.ts +32 -0
  303. package/scripts/lib/deterministic-env.ts +62 -0
  304. package/scripts/lib/gate-test-env.ts +19 -0
  305. package/scripts/measure-goal-grounding-corpus.ts +2 -1
  306. package/scripts/ratchets/test-interference-patterns.allowlist.txt +257 -0
  307. package/scripts/ratchets/test-interference-patterns.ts +367 -0
  308. package/scripts/release-loop/cut-branch.ts +4 -3
  309. package/scripts/release-loop/export-check-node.ts +0 -1
  310. package/scripts/release-loop/fixtures/warm-failure-assertion/sample.junit.xml +8 -0
  311. package/scripts/release-loop/fixtures/warm-job-029/sample.json +1 -0
  312. package/scripts/release-loop/fixtures/warm-job-029/sample.junit.xml +10 -0
  313. package/scripts/release-loop/gate-closure.ts +12 -8
  314. package/scripts/release-loop/gate-diff.ts +1 -1
  315. package/scripts/release-loop/gate-host-mirror.ts +66 -0
  316. package/scripts/release-loop/gate-node.ts +523 -75
  317. package/scripts/release-loop/gate-partial.ts +106 -9
  318. package/scripts/release-loop/gate-pr-cache.ts +66 -0
  319. package/scripts/release-loop/gate-prewarm-plan.ts +66 -0
  320. package/scripts/release-loop/gate-r2-verdict.ts +189 -0
  321. package/scripts/release-loop/gate-recheck-plan-node.ts +52 -0
  322. package/scripts/release-loop/gate-warm-artifacts.ts +253 -0
  323. package/scripts/release-loop/import-graph-scc-run.ts +54 -0
  324. package/scripts/release-loop/import-graph-scc.ts +215 -0
  325. package/scripts/release-loop/node-verdict.ts +2 -0
  326. package/scripts/release-loop/probe-pod-host-dirs-live.ts +113 -0
  327. package/scripts/release-loop/resume-release.ts +33 -4
  328. package/scripts/release-loop/resume.ts +13 -5
  329. package/scripts/release-loop/spawn-lane.ts +2 -19
  330. package/scripts/release-loop/tui-sim-node.ts +3 -2
  331. package/scripts/release-loop/unattended-release.ts +16 -0
  332. package/scripts/test-deterministic.ts +5 -61
  333. package/scripts/train-reach-grouping.ts +48 -0
  334. package/scripts/tsc-incremental-cache.ts +86 -0
  335. package/scripts/tsc-incremental-diagnostics.ts +76 -0
  336. package/scripts/tsc-main-safety-net.ts +2 -2
  337. package/src/acp/client.ts +4 -1
  338. package/src/acp/core-turn-bridge.ts +50 -5
  339. package/src/ad-pipeline/run.ts +4 -2
  340. package/src/agent/runner.ts +169 -3
  341. package/src/agent/subagent-callable.ts +3 -2
  342. package/src/agent-mission/review-loop.ts +6 -3
  343. package/src/agent-substrate/acp-implementer.ts +14 -2
  344. package/src/agent-substrate/pr-reviewer.ts +12 -0
  345. package/src/agent-substrate/self-review-cli.ts +30 -4
  346. package/src/autopilot/pr-manager.ts +62 -7
  347. package/src/briefing/brief-items.ts +24 -22
  348. package/src/capture/tui-self-report.ts +15 -0
  349. package/src/chat/chat-cli.ts +10 -2
  350. package/src/chat/index.ts +77 -11
  351. package/src/chat/input-control-key.ts +5 -3
  352. package/src/chat/tool-interrupt-behavior.ts +38 -0
  353. package/src/chat/tool-render/dispatch.ts +5 -1
  354. package/src/chat/turn-typeahead.ts +75 -20
  355. package/src/cli/__fixtures__/agent-help-before/agent.txt +4 -2
  356. package/src/cli/agent-cli.ts +41 -13
  357. package/src/cli/config-audit.ts +123 -0
  358. package/src/cli/decisions-cli.ts +80 -6
  359. package/src/cli/log-event-names.ts +7 -0
  360. package/src/cli/mcp-login.ts +21 -5
  361. package/src/cli/msg-cli.ts +88 -0
  362. package/src/cli/pr-cli.ts +214 -57
  363. package/src/cli/pr-land-heavy-remote.ts +2 -2
  364. package/src/cli/pr-land-overlap-evidence.ts +63 -9
  365. package/src/cli/pr-land-pod-shadow-job.ts +83 -0
  366. package/src/cli/pr-land-pod-shadow.ts +16 -3
  367. package/src/cli/pr-land-virtual-rebase.ts +108 -0
  368. package/src/cli/release-cli.ts +80 -14
  369. package/src/cli/seat-cli.ts +138 -3
  370. package/src/cli/self-update.ts +5 -2
  371. package/src/cli/tasks-cli.ts +97 -19
  372. package/src/context-bus/context-first.ts +13 -0
  373. package/src/context-bus/context-now.ts +13 -2
  374. package/src/context-bus/ops-events.ts +75 -0
  375. package/src/control-plane/lease-holder.ts +12 -8
  376. package/src/control-plane/ledger.ts +163 -4
  377. package/src/control-plane/server.ts +91 -11
  378. package/src/core-turn/run-core-turn.ts +1 -0
  379. package/src/core-turn/run-goal-loop.ts +222 -3
  380. package/src/core-turn/seat-turn-cap.ts +79 -0
  381. package/src/core-turn/short-question.ts +38 -6
  382. package/src/core-turn/types.ts +3 -0
  383. package/src/dashboard/acp-boot.ts +3 -0
  384. package/src/dashboard/index.ts +224 -615
  385. package/src/dashboard/input/chat-main-entry-runtime.ts +1 -1
  386. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +4 -1
  387. package/src/dashboard/input/chat-main-session.ts +8 -1
  388. package/src/dashboard/input/chat-main-turn.ts +12 -3
  389. package/src/dashboard/input/compact-prepass-recovery.ts +33 -0
  390. package/src/dashboard/input/external-input-host.ts +26 -0
  391. package/src/dashboard/input/internal-turn.ts +261 -0
  392. package/src/dashboard/input/seat-autowake.ts +318 -0
  393. package/src/dashboard/input/turn-steer-bridge.ts +70 -0
  394. package/src/dashboard/loop-scheduler.ts +72 -0
  395. package/src/dashboard/seat-boot-preamble.ts +18 -0
  396. package/src/dashboard/skill-runtime.ts +2 -0
  397. package/src/dashboard/slash-runtime/dashboard-handlers.ts +126 -1181
  398. package/src/dashboard/slash-runtime/help-from-registry.ts +2 -1
  399. package/src/dashboard/slash-runtime/index.ts +13 -0
  400. package/src/dashboard/turn-busy-teardown.ts +50 -0
  401. package/src/decisions/decision-ledger.ts +45 -4
  402. package/src/discord-self-message.ts +4 -4
  403. package/src/domains/release-tool.ts +18 -1
  404. package/src/field-note/transcribe.ts +17 -0
  405. package/src/github/remote-call.ts +81 -0
  406. package/src/goals/registry.ts +46 -6
  407. package/src/goals/types.ts +7 -1
  408. package/src/graph-runner/runner.ts +1 -0
  409. package/src/grok/credential.ts +26 -0
  410. package/src/harness/harness-clean.ts +2 -1
  411. package/src/harness/harness-cli-command.ts +52 -5
  412. package/src/harness/harness-gc.ts +2 -1
  413. package/src/harness/harness-incidents.ts +57 -20
  414. package/src/harness/harness-pod-dispatch.ts +87 -10
  415. package/src/harness/harness-queue.ts +53 -4
  416. package/src/harness/harness-stop.ts +4 -3
  417. package/src/harness/harness-substrate-default.ts +78 -7
  418. package/src/harness/harness-worktree-auto.ts +2 -1
  419. package/src/harness/harness-worktrees.ts +3 -2
  420. package/src/hooks/dispatch.ts +2 -1
  421. package/src/index.ts +152 -25
  422. package/src/input/external-input-queue.ts +33 -1
  423. package/src/intake-plane/slash.ts +14 -3
  424. package/src/keybindings.ts +4 -1
  425. package/src/llm/tool-loop-steered-rejection.ts +33 -0
  426. package/src/llm.ts +138 -19
  427. package/src/loops/consensus.ts +348 -0
  428. package/src/loops/msg-wake.ts +206 -4
  429. package/src/loops/observe.ts +19 -3
  430. package/src/loops/orchestrator/tick.ts +64 -10
  431. package/src/loops/orchestrator/traffic.ts +64 -1
  432. package/src/loops/registry.ts +30 -0
  433. package/src/market/signed-index.ts +14 -0
  434. package/src/mcp/mcp-oauth.ts +54 -5
  435. package/src/models/prompts.ts +28 -0
  436. package/src/msg/msg-sse-client.ts +223 -0
  437. package/src/msg/msg-store.ts +86 -2
  438. package/src/mss/logging/log-store.ts +225 -1
  439. package/src/nexus/DAEMON-THIN-inventory-2026-10-15.md +122 -0
  440. package/src/nexus/api/http-server.ts +54 -15
  441. package/src/nexus/api/loop-resources.ts +19 -3
  442. package/src/nexus/api/ops-api.ts +11 -0
  443. package/src/nexus/daemon-thin-inventory.md +37 -0
  444. package/src/nexus/index.ts +70 -0
  445. package/src/nexus/state/state.ts +1 -0
  446. package/src/oauth/store.ts +32 -7
  447. package/src/onboarding/first-run.ts +28 -3
  448. package/src/ops/overview-cache.ts +16 -4
  449. package/src/ops/resources.ts +82 -0
  450. package/src/plugins/maker/connector-maker.ts +41 -0
  451. package/src/plugins/maker/skill-maker.ts +42 -0
  452. package/src/pr-tool/census.ts +288 -0
  453. package/src/prompt-library/chat-conciseness.ts +13 -0
  454. package/src/prompt-library/universal-preamble.ts +10 -5
  455. package/src/release-loop/gate-host-reservation.ts +97 -0
  456. package/src/release-loop/landing-freeze.ts +5 -0
  457. package/src/release-loop/light-rc.ts +5 -2
  458. package/src/release-loop/placement.ts +92 -1
  459. package/src/release-loop/rehearsal-shard-plan.ts +149 -0
  460. package/src/release-loop/rehearsal.ts +73 -17
  461. package/src/repl/index.ts +20 -2
  462. package/src/seat-loop/seat-knowhow.ts +282 -0
  463. package/src/seat-loop/seat-loop.ts +41 -2
  464. package/src/seat-loop/traffic-signal.ts +3 -0
  465. package/src/self-dev/ask-launch-flow.ts +24 -3
  466. package/src/self-dev/decompose-compose-graph.ts +28 -27
  467. package/src/self-dev/decompose-proposal.ts +53 -38
  468. package/src/self-dev/decompose.ts +17 -0
  469. package/src/self-dev/dev-pipeline.ts +31 -2
  470. package/src/self-dev/draft-residue.ts +24 -9
  471. package/src/self-dev/draft-sweep.ts +59 -4
  472. package/src/self-dev/external-call-zero.ts +77 -0
  473. package/src/self-dev/launch-preflight-text.ts +6 -0
  474. package/src/self-dev/launch-preflight.ts +25 -3
  475. package/src/self-dev/local-supervisor-judge.ts +185 -0
  476. package/src/self-dev/run-supervisor.ts +229 -19
  477. package/src/self-dev/run-types.ts +20 -0
  478. package/src/self-dev/shard-aggregate.ts +13 -2
  479. package/src/self-dev/shard-plan.ts +47 -0
  480. package/src/self-dev/shard-run.ts +112 -10
  481. package/src/self-dev/spawn-lane-files.ts +19 -0
  482. package/src/self-implement/app-api-budget.ts +51 -11
  483. package/src/self-implement/config-gate.ts +2 -1
  484. package/src/self-implement/expose-gate.ts +2 -1
  485. package/src/self-implement/gate-artifacts.ts +405 -0
  486. package/src/self-implement/gate-cli.ts +201 -16
  487. package/src/self-implement/gate-remote.ts +110 -25
  488. package/src/self-implement/gate-scenario.ts +85 -0
  489. package/src/self-implement/goal-author.ts +160 -8
  490. package/src/self-implement/graph-authority.ts +38 -6
  491. package/src/self-implement/graph-overlay-condition.ts +92 -1
  492. package/src/self-implement/heal-triage.ts +79 -0
  493. package/src/self-implement/host-regate.ts +61 -3
  494. package/src/self-implement/orchestrator.ts +14 -123
  495. package/src/self-implement/pr-title.ts +123 -0
  496. package/src/self-implement/run-checkpoint.ts +235 -0
  497. package/src/self-implement/run-ledger.ts +155 -10
  498. package/src/self-implement/run-stop.ts +4 -3
  499. package/src/self-implement/run-ttl.ts +28 -4
  500. package/src/self-implement/seams.ts +2 -0
  501. package/src/self-implement/shadow-stage.ts +7 -6
  502. package/src/self-implement/stop-autoheal.ts +2 -1
  503. package/src/self-implement/train-gate-remote.ts +37 -8
  504. package/src/self-implement/train-reach-grouping.ts +157 -0
  505. package/src/self-implement/train-scenario-layers.ts +133 -0
  506. package/src/self-implement/uncommitted-work.ts +2 -0
  507. package/src/session/pending-input.ts +19 -0
  508. package/src/session/session-start.ts +224 -0
  509. package/src/session-runtime/parallel-dispatch.ts +22 -5
  510. package/src/skills/tools/dev-harness.ts +6 -4
  511. package/src/skills/tools/shell.ts +1 -1
  512. package/src/steward/resync-tick.ts +114 -23
  513. package/src/steward/ta-repair-event-collect.ts +158 -0
  514. package/src/task-agent/card-evidence.ts +25 -1
  515. package/src/task-agent/green-sweep.ts +8 -3
  516. package/src/task-agent/judge.ts +76 -0
  517. package/src/task-agent/land-queue.ts +79 -0
  518. package/src/task-agent/live-moves.ts +30 -9
  519. package/src/task-agent/postland-card.ts +5 -0
  520. package/src/task-agent/prelaunch-already-done.ts +98 -0
  521. package/src/task-agent/real-launch-guard.ts +37 -0
  522. package/src/task-agent/rejudge-on-head.ts +89 -12
  523. package/src/task-agent/remote-rejudge-bind.ts +28 -8
  524. package/src/task-agent/repair-cycle.ts +45 -7
  525. package/src/task-agent/repair-worker.ts +7 -0
  526. package/src/task-agent/run-identity.ts +200 -0
  527. package/src/task-agent/shadow.ts +50 -3
  528. package/src/task-agent/task-hand.ts +158 -5
  529. package/src/task-agent/train-landing.ts +77 -0
  530. package/src/task-agent/work-order.ts +104 -0
  531. package/src/task-orchestrator/surfaces/pod-command-job.ts +88 -28
  532. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +88 -19
  533. package/src/task-orchestrator/surfaces/pod-host-acp-review.ts +2 -1
  534. package/src/task-orchestrator/surfaces/pod-image-source.ts +2 -1
  535. package/src/task-orchestrator/surfaces/pod-install-slots.ts +121 -9
  536. package/src/task-orchestrator/surfaces/pod-lease.ts +20 -6
  537. package/src/task-orchestrator/surfaces/pod-lifecycle-stages.ts +169 -0
  538. package/src/task-orchestrator/surfaces/pod-mirror-seed.ts +27 -0
  539. package/src/task-orchestrator/surfaces/pod-result-marker-wait.ts +33 -0
  540. package/src/task-orchestrator/surfaces/pod-reuse-cleanup.ts +63 -0
  541. package/src/task-orchestrator/surfaces/pod-source-receive.ts +19 -3
  542. package/src/task-orchestrator/surfaces/pod-warm-switch.ts +141 -0
  543. package/src/task-orchestrator/surfaces/pod-warm-ta-shadow.ts +85 -0
  544. package/src/task-orchestrator/surfaces/pod-warm-worker-protocol.ts +153 -0
  545. package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +208 -0
  546. package/src/task-orchestrator/surfaces/self-implement-pod.ts +132 -15
  547. package/src/telegram.ts +2 -4
  548. package/src/tui/input/pickers.ts +732 -0
  549. package/src/tui/slash/mission.ts +492 -0
  550. package/src/tui/slash/term.ts +877 -0
  551. package/src/tui-client/dashboard-session.ts +9 -0
  552. package/src/tui.ts +48 -0
  553. package/src/user-config/gemini-server-tools.ts +9 -0
  554. package/src/user-config.ts +53 -16
  555. package/src/util/spawn-sync-output.ts +7 -0
  556. package/src/ux-sim/fake-bin.ts +154 -0
  557. package/src/ux-sim/fake-clock.ts +36 -0
  558. package/src/ux-sim/headless-tui.ts +72 -0
  559. package/src/ux-sim/in-process-cli.ts +2 -1
  560. package/src/ux-sim/temp-git-remote.ts +82 -0
  561. package/src/version/packed-revision.json +1 -1
  562. package/src/video-pipeline/recipes/vlog.ts +25 -7
  563. package/apps/pwa/out/_next/static/chunks/1015-9cba22a5a4554797.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/1084-4e66af7376a18db7.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/1106.3a6410b7b0c70ad4.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/1127.32fc642f70423506.js +0 -8
  567. package/apps/pwa/out/_next/static/chunks/1189-d9b8e1995a831fae.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/1224.6da98c7ad0eb7dd8.js +0 -34
  569. package/apps/pwa/out/_next/static/chunks/1370.4e3e2558f111fd5d.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/1514-b7da377fdc4eb17a.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/1577.3c24c32584e10d70.js +0 -286
  572. package/apps/pwa/out/_next/static/chunks/161.130a569855c4d68c.js +0 -24
  573. package/apps/pwa/out/_next/static/chunks/1864.16da9556e78a9236.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/1b6f6b2a.aff090122be3726e.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/2341.e8a9135e5b910dd5.js +0 -36
  576. package/apps/pwa/out/_next/static/chunks/257.10afdbe3520f2fc2.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/2658.87550e692dc493f6.js +0 -64
  578. package/apps/pwa/out/_next/static/chunks/2673.85e2f02efae7a85f.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/2766-bee623b37472ed63.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/2794.6d836872b0bf43e2.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/2806.eb57ced1a528ed39.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/283.7a121c4910e6dad7.js +0 -1
  583. package/apps/pwa/out/_next/static/chunks/2870.1dc29aa5fc01f09f.js +0 -115
  584. package/apps/pwa/out/_next/static/chunks/293.fd38e77ce5d48103.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/2993-5952a1cd3fd5d441.js +0 -1
  586. package/apps/pwa/out/_next/static/chunks/32-a523427c5bd5ed00.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/3280.af38002a6424a905.js +0 -90
  588. package/apps/pwa/out/_next/static/chunks/3292-5ddffdb2f517b44f.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/3523.d3231daaf7aba993.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/3763.577de3181e1e6ea0.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/4138-cc56b0938ef72783.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/4255-8350c43d307a8875.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/4352.a4954693499c2714.js +0 -1
  594. package/apps/pwa/out/_next/static/chunks/4421-f2dcbc6fd80ec013.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/4513-51b94aeee37c9905.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/4928-c3b72f99aa588699.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/4986-f9fc0283fb325219.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/5014.671a7b9f69c2b7b1.js +0 -94
  599. package/apps/pwa/out/_next/static/chunks/5137.d398e24710b3feb6.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/5298.0bea74885cabe754.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/5315.74c53a39739b11c4.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/5317-8b1df8d02105cd07.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/5358-33051f41a2b239af.js +0 -1
  604. package/apps/pwa/out/_next/static/chunks/5388.a2ec02501f714ad5.js +0 -62
  605. package/apps/pwa/out/_next/static/chunks/5395.e6ac948a2d15058d.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/5409-f282bf533c151849.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/5466.d0f4aba9d52acfa8.js +0 -27
  608. package/apps/pwa/out/_next/static/chunks/5564.3d28b353470fe305.js +0 -104
  609. package/apps/pwa/out/_next/static/chunks/569.b0cbfb4dee4a167a.js +0 -93
  610. package/apps/pwa/out/_next/static/chunks/5835-72465b73a8bb8d74.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/5941.045d6093139e1f7a.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/6228.47cabb83564dfe54.js +0 -24
  613. package/apps/pwa/out/_next/static/chunks/6457-8d1993002a87d0fe.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/6513.ef54cfae52351982.js +0 -2
  615. package/apps/pwa/out/_next/static/chunks/6582.13d245bf87df69f1.js +0 -148
  616. package/apps/pwa/out/_next/static/chunks/6655.c33f6acc2c61bb89.js +0 -149
  617. package/apps/pwa/out/_next/static/chunks/6731-9d7195af289c8d4c.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/6733.0be05b1db9279fc5.js +0 -79
  619. package/apps/pwa/out/_next/static/chunks/6742-90937146253a8b9e.js +0 -1
  620. package/apps/pwa/out/_next/static/chunks/687-816858f77a396a4e.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/6888.72c7b8cfb02a7e61.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/6993.c17c828ac3ef3253.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/7076.9f27ef666b0cefdd.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/7113.1403c5d451804e8c.js +0 -287
  625. package/apps/pwa/out/_next/static/chunks/7169.9dd603c726c2cd7c.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/7247-e3ec21f761f86710.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/7261-a47b72f96f5199df.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/7335-5cda81ac90310077.js +0 -1
  629. package/apps/pwa/out/_next/static/chunks/7356-c2e4af090546e2d4.js +0 -1
  630. package/apps/pwa/out/_next/static/chunks/738-59518742078fda71.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/7385.a7375f10db73243a.js +0 -77
  632. package/apps/pwa/out/_next/static/chunks/7445.cf1cf2fe109bcd96.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/7505.a2057432ab6ef99a.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/7515.31f45fac1f9c70cb.js +0 -74
  635. package/apps/pwa/out/_next/static/chunks/7658-d4ce8c72a9548a91.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/8107-0b1ae46bc5bcb788.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/8132.bfea2f016ce8d992.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/8672.e299832bc7d18ee2.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/8735.f1ef798a5f260f6f.js +0 -226
  640. package/apps/pwa/out/_next/static/chunks/8774-88f67f7d8d53d299.js +0 -1
  641. package/apps/pwa/out/_next/static/chunks/8782.252e5a405d86e4ed.js +0 -32
  642. package/apps/pwa/out/_next/static/chunks/909-9ed7aa0dba804316.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/9100.22b0301256827007.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/9140-dde77575570bc71e.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/9292-a7ad9a01c7044b93.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/9517.00627dc51b02821c.js +0 -1
  647. package/apps/pwa/out/_next/static/chunks/9725.dcbb585652d8e9c5.js +0 -1
  648. package/apps/pwa/out/_next/static/chunks/9761.a3ebf5c389c24ddc.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/9972.834d2b2b7d48f1ed.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/9995-1a90486f5ae01546.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/a435ad95.577add051b4facb2.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-83372d8b5863ebf7.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d69256d7613c140a.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-352c5e9ba3f9b858.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/app/board/page-69f1d725524201a8.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/app/bots/page-ad9181d84285e4ea.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/app/ceo/page-86feb08080261a2f.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/app/chat/page-ffa15987e4756d03.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/app/consult/page-11a3ab44ac327cc1.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/app/control/page-6309b11c4264d24d.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/app/decisions/page-8800472079612e87.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/app/design-check/page-d387bf3f2301882a.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/app/editor/page-048f88d8c8658da6.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/app/exec/page-6c14f9739b7f6880.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/app/field/page-2834f44f64e98d74.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/app/healthz/route-83372d8b5863ebf7.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/app/inside/page-a13df6f88e824887.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/app/intake/page-8a4397248754cf2b.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/app/layout-daf0bd329360a336.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/app/live/page-f7ef1c28764cb476.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/app/live-run/page-61e74f28b9353dac.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/app/loops/page-8aaa6dedc4c10dfb.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/app/market/page-294602ba649be829.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-5fa5f41ed3fce323.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/app/morning/page-33bf667a5fb297bf.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/app/not-found-5ae3cbd6e5dba515.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/app/observatory/page-eb867d16ad6fdede.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1ab789fa68682f7e.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-6fd41b5b6ff13a71.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fb3d5f7642c99fba.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/app/outputs/page-70f705b2b5ed42ca.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/app/page-c9c93b358a7667b5.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/app/reflection/page-1563c556b81c96ed.js +0 -1
  684. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-ce89c20c45b3a673.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/app/sessions/page-423ae63d786f4ee9.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-591a5378a4c4447c.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/app/settings/page-a250496b19a5824b.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-c36a9277febc5911.js +0 -1
  689. package/apps/pwa/out/_next/static/chunks/app/setup/layout-83372d8b5863ebf7.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/app/share/page-a5de57edc6d7f942.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/app/showroom/page-16bdd4c9f40498df.js +0 -1
  692. package/apps/pwa/out/_next/static/chunks/app/tasks/page-75e946e6e71cc6d8.js +0 -1
  693. package/apps/pwa/out/_next/static/chunks/app/term/page-9eb56166774eb256.js +0 -1
  694. package/apps/pwa/out/_next/static/chunks/app/today/page-1c5f9ef35dbb22c5.js +0 -1
  695. package/apps/pwa/out/_next/static/chunks/app/trace/page-657ff9985e508498.js +0 -1
  696. package/apps/pwa/out/_next/static/chunks/app/vault/page-22dc72499da68fb2.js +0 -1
  697. package/apps/pwa/out/_next/static/chunks/app/workflows/page-094ad334e40f8169.js +0 -1
  698. package/apps/pwa/out/_next/static/chunks/app/workspace/page-8bcd379852ff6287.js +0 -1
  699. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-6b04413f280cd344.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/de70ab42-d28d47437208d458.js +0 -1
  701. package/apps/pwa/out/_next/static/chunks/f800c5e3.46dbd7f2af8c3fce.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/f8bc19ad.47579f6d684b0760.js +0 -122
  703. package/apps/pwa/out/_next/static/chunks/framework-ca9ac4609fb9fed8.js +0 -1
  704. package/apps/pwa/out/_next/static/chunks/main-app-0dd08d32d15a030f.js +0 -1
  705. package/apps/pwa/out/_next/static/chunks/main-f0399d33955c4e01.js +0 -1
  706. package/apps/pwa/out/_next/static/chunks/pages/_app-d42caca6f8ac2211.js +0 -1
  707. package/apps/pwa/out/_next/static/chunks/pages/_error-42e713c13aa41ca8.js +0 -1
  708. package/apps/pwa/out/_next/static/chunks/webpack-8fbf8f61a377aa16.js +0 -1
  709. /package/apps/pwa/out/_next/static/{cIpv5nU9AycwYWZI9H589 → LSXfY0u9QtfDbejY0Ukf_}/_ssgManifest.js +0 -0
@@ -1,7 +1,9 @@
1
- import { existsSync, readFileSync } from 'node:fs';
1
+ import { existsSync, lstatSync, mkdtempSync, readFileSync, realpathSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
2
3
  import { spawnSync } from 'node:child_process';
3
4
  import { join } from 'node:path/posix';
4
5
  import { prepareDeterministicChildEnvironment } from '../../scripts/lib/deterministic-env.js';
6
+ import { withGateTestEnv } from '../../scripts/lib/gate-test-env.js';
5
7
  import { runIsolationHardcodeGate } from '../../scripts/ci-isolation-hardcode-gate.js';
6
8
  import { runModelHardcodeGate } from '../../scripts/ci-model-hardcode-gate.js';
7
9
  import { runDaemonPortGate } from '../../scripts/ci-daemon-port-gate.js';
@@ -24,18 +26,29 @@ import {
24
26
  overlapWithCurrentChanges,
25
27
  } from '../cli/pr-granularity.js';
26
28
  import { listWorktrees } from '../git-fs/worktree.js';
27
- import { runShardedGateTests } from '../self-dev/shard-run.js';
29
+ import { runShardedGateTests, writeShardFailureEvidence } from '../self-dev/shard-run.js';
30
+ import { parseGateScenario, readGateScenarioFile } from './gate-scenario.js';
31
+ import { isGateSpawnLaneFile } from '../self-dev/spawn-lane-files.js';
32
+ import { defaultGateEnvKey, defaultRevResolver, resolveArtifactDir, writeGateArtifacts, type GateArtifactCounts, type GateArtifactsUnavailable, type RevResolver } from './gate-artifacts.js';
28
33
 
29
34
  export interface SelfGateCliOptions {
30
35
  base?: string;
31
36
  pr?: string;
32
37
  shards?: number;
38
+ /** OP FU (LAND-TRAIN-LIVE side) — opt-in with `shards`: sorted round-robin into exactly that N, no RSS pre-measurement.
39
+ * Every other output (shardsDetail · baseline · artifacts · exit) keeps its shape; absent = measured planner unchanged. */
40
+ fixedShardDistribution?: boolean;
33
41
  /** LIGHT-RC (0.2.19) — repository-relative test paths run on every call, whatever changed. Existing ones join the
34
42
  * run (deduplicated, same introduced/preexisting baseline as the rest); missing ones go to `alwaysIncludeMissing`. */
35
43
  alwaysInclude?: readonly string[];
36
44
  /** RELEASE-DAILY-LIGHT-RC ② — run every tracked test that imports a changed (non-test) source, past the caller cap
37
45
  * (importer index · reason=changed-source-importer). Off by default: the plain `self gate` scope is unchanged. */
38
46
  includeImporterTests?: boolean;
47
+ /** Optional v0 scenario JSON. Adds selected tests only after binding base/head/diff. */
48
+ scenario?: unknown;
49
+ scenarioFile?: string;
50
+ /** SIDE-VERIFY — 조각별 지금/기준 JUnit ⊕ summary.json 을 이 디렉토리에 «쓰기만» 한다(판정·exit·기존 줄 불변). */
51
+ artifactDir?: string;
39
52
  }
40
53
 
41
54
  interface ProcessResult {
@@ -81,6 +94,7 @@ export interface DirtyWorktreeClassification {
81
94
  interface SelfGateCliDeps {
82
95
  changedFiles?: (cwd: string, options: SelfGateCliOptions) => ChangedFileSelection;
83
96
  exists?: (path: string) => boolean;
97
+ isRegularFile?: (cwd: string, path: string) => boolean;
84
98
  runCommand?: (command: string, args: string[], cwd: string) => ProcessResult;
85
99
  readFile?: (path: string) => string | undefined;
86
100
  runTests?: (cwd: string, files: readonly string[]) => ProcessResult;
@@ -99,6 +113,45 @@ interface SelfGateCliDeps {
99
113
  runPwaGate?: (out: GateOutput) => number;
100
114
  listPrWorktrees?: (cwd: string) => PrWorktreeListEntry[] | { lookupFailed: string };
101
115
  inspectWorktreeDirtiness?: (path: string) => { dirty: boolean } | { lookupFailed: string };
116
+ /** `--artifact-dir` 전용 심 — 커밋 sha 해석(실패 = null) · 환경 키. */
117
+ resolveRev?: RevResolver;
118
+ envKey?: () => string;
119
+ }
120
+
121
+ /** `--artifact-dir` 가 쓸 재료 — 판정 경로가 «채우기만» 하고 읽지 않는다. */
122
+ interface GateArtifactCapture {
123
+ baseRef?: string;
124
+ testFiles?: readonly string[];
125
+ sharded?: ReturnType<typeof runShardedGateTests>;
126
+ verdict?: string;
127
+ verdictLine?: string;
128
+ unmeasuredReason?: string;
129
+ unshardedRc?: number | null;
130
+ artifactsUnavailable?: GateArtifactsUnavailable;
131
+ /** `unrun importer tests: N` 의 N — 색인 조회 실패면 null(그 이유가 importerLookupFailed). */
132
+ unrunImporters?: number | null;
133
+ importerLookupFailed?: string;
134
+ }
135
+
136
+ /** SIDE-VERIFY ③ — summary 정수 칸: 게이트가 이미 낸 `result.baseline` ⊕ `unrunImporterTotal` 을 «옮기기만» 한다.
137
+ * baseline 이 없으면(조각 unmeasured) 넷은 null — 「0」으로 접지 않는다. */
138
+ export function gateArtifactCounts(result: Pick<SelfGateCliResult, 'baseline'>, capture: Pick<GateArtifactCapture, 'unrunImporters' | 'importerLookupFailed' | 'unmeasuredReason'>):
139
+ { counts: GateArtifactCounts; reason?: string } {
140
+ const baseline = result.baseline;
141
+ const unrunImporters = capture.unrunImporters ?? null;
142
+ const reasons: string[] = [];
143
+ if (!baseline) reasons.push(capture.unmeasuredReason !== undefined ? `shards unmeasured (${capture.unmeasuredReason})` : 'test step produced no measurement');
144
+ if (unrunImporters === null) reasons.push(`importer lookup failed${capture.importerLookupFailed ? ` (${capture.importerLookupFailed})` : ''}`);
145
+ return {
146
+ counts: {
147
+ introduced: baseline?.introduced ?? null,
148
+ preexisting: baseline?.preexisting ?? null,
149
+ unknown: baseline?.unknown ?? null,
150
+ preconditionUnmet: baseline?.preconditionUnmet ?? null,
151
+ unrunImporters,
152
+ },
153
+ ...(reasons.length ? { reason: reasons.join('; ') } : {}),
154
+ };
102
155
  }
103
156
 
104
157
  interface GateOutput {
@@ -178,7 +231,7 @@ function defaultRunCommand(command: string, args: string[], cwd: string): Proces
178
231
  if (command === 'bun' && args[0] === 'test') {
179
232
  let isolated: ReturnType<typeof prepareDeterministicChildEnvironment>;
180
233
  try {
181
- isolated = prepareDeterministicChildEnvironment('elanous-gate-cli-test-env-');
234
+ isolated = prepareDeterministicChildEnvironment('elanous-gate-cli-test-env-', { env: withGateTestEnv(process.env) });
182
235
  } catch (error) {
183
236
  const message = `deterministic environment setup failed: ${String(error)}`;
184
237
  return { status: null, stdout: '', stderr: message, error: error instanceof Error ? error : new Error(message) };
@@ -592,9 +645,10 @@ function logGateCliBaseline(cwd: string, payload: GateCliBaselinePayload): void
592
645
  }
593
646
  }
594
647
 
595
- export function runSelfGateCli(cwd: string, options: SelfGateCliOptions = {}, deps: SelfGateCliDeps = {}): SelfGateCliResult {
596
- if (options.base && options.pr) throw new Error('use either --base <ref> or --pr <number>, not both');
597
- if (options.shards !== undefined && (!Number.isSafeInteger(options.shards) || options.shards < 1)) throw new Error('invalid --shards count');
648
+ type GateSelectionOptions = Pick<SelfGateCliOptions, 'base' | 'pr' | 'alwaysInclude' | 'includeImporterTests' | 'scenario' | 'scenarioFile'>;
649
+ type GateSelectionDeps = Pick<SelfGateCliDeps, 'changedFiles' | 'exists' | 'isRegularFile' | 'runCommand'>;
650
+
651
+ function prepareGateTestSelection(cwd: string, options: GateSelectionOptions, deps: GateSelectionDeps) {
598
652
  const selection = (deps.changedFiles ?? ((dir, input) => defaultChangedFiles(dir, input, deps.runCommand)))(cwd, options);
599
653
  const exists = deps.exists ?? ((path) => existsSync(`${cwd}/${path}`));
600
654
  // Caller: src/release-loop/light-rc.ts runLightRc → runSelfGateCli(dir, { ..., includeImporterTests: true }).
@@ -607,13 +661,35 @@ export function runSelfGateCli(cwd: string, options: SelfGateCliOptions = {}, de
607
661
  ? buildRouteConsumerTestIndex(cwd, availableIndex.testPaths, selection.files, selection.baseRef) : null;
608
662
  const scope = resolveGateScope(selection.files, exists, availableIndex,
609
663
  { routeConsumerTestIndex: routeIndex ?? undefined });
610
- const expose = publicExposureFiles(selection.files).length > 0
611
- ? runExposeGate(cwd, selection.files, getUserConfig().harness?.exposeGate === 'strict' ? 'strict' : 'warn', deps.runCommand)
612
- : { passed: true, log: '' };
664
+ return { selection, exists, lookupFailed, availableIndex, routeIndex, scope };
665
+ }
666
+
667
+ function finishGateTestSelection(prepared: ReturnType<typeof prepareGateTestSelection>, options: GateSelectionOptions, cwd: string, deps: GateSelectionDeps) {
668
+ const { selection, exists, lookupFailed, availableIndex, routeIndex, scope } = prepared;
613
669
  const alwaysInclude = [...new Set((options.alwaysInclude ?? []).map(normalizeGatePath).filter(Boolean))];
614
670
  const alwaysIncludeMissing = alwaysInclude.filter((path) => !exists(path));
615
671
  const scopedTests = scope.testArgs ?? [];
616
672
  const alwaysIncludeExtra = alwaysInclude.filter((path) => exists(path) && !scopedTests.includes(path));
673
+ let scenarioTests: string[] = [];
674
+ if (options.scenario !== undefined || options.scenarioFile !== undefined) {
675
+ if (options.scenario !== undefined && options.scenarioFile !== undefined) throw new Error('choose scenario or scenarioFile');
676
+ const run = deps.runCommand ?? defaultRunCommand;
677
+ const headSha = requireOutput(run, 'git', ['rev-parse', 'HEAD'], cwd).trim();
678
+ const baseSha = options.base
679
+ ? requireOutput(run, 'git', ['merge-base', options.base, 'HEAD'], cwd).trim()
680
+ : requireOutput(run, 'git', ['rev-parse', selection.baseRef], cwd).trim();
681
+ const scenario = parseGateScenario(options.scenarioFile === undefined ? options.scenario : readGateScenarioFile(options.scenarioFile), { baseSha, headSha, changedFiles: selection.files });
682
+ scenarioTests = scenario.selectedTests.map(({ path }) => path);
683
+ if (scenarioTests.some((path) => !exists(path))) throw new Error('gate scenario invalid: selected test missing');
684
+ const isRegularFile = deps.isRegularFile ?? ((dir: string, path: string) => {
685
+ try {
686
+ const root = realpathSync(dir);
687
+ const target = join(dir, path);
688
+ return lstatSync(target).isFile() && realpathSync(target).startsWith(`${root}/`);
689
+ } catch { return false; }
690
+ });
691
+ if (scenarioTests.some((path) => !isRegularFile(cwd, path))) throw new Error('gate scenario invalid: selected test is not a regular file');
692
+ }
617
693
  // RELEASE-DAILY-LIGHT-RC ② — the scope already runs importer tests as callers, but capped (GATE_CALLER_TEST_LIMIT);
618
694
  // a day of landings overflows that cap and source-only regressions went unrun (0.2.24: 20). Opt-in: every importer.
619
695
  const changedSources = selection.files.filter((path) => !isTestPath(path));
@@ -621,14 +697,90 @@ export function runSelfGateCli(cwd: string, options: SelfGateCliOptions = {}, de
621
697
  ? [...new Set(changedSources.flatMap((source) => availableIndex.testsBySource.get(source) ?? []))]
622
698
  .filter((path) => exists(path) && !scopedTests.includes(path) && !alwaysIncludeExtra.includes(path)).sort()
623
699
  : [];
624
- const testFiles = [...scopedTests, ...alwaysIncludeExtra, ...importerTestsAdded];
625
- const skipTestStep = scope.skipTestStep && alwaysIncludeExtra.length === 0 && importerTestsAdded.length === 0;
626
- const importerTestsNotRun = options.includeImporterTests && availableIndex
700
+ const defaultTests = [...scopedTests, ...alwaysIncludeExtra, ...importerTestsAdded];
701
+ const testFiles = scenarioTests.length ? [...new Set([...defaultTests, ...scenarioTests])] : defaultTests;
702
+ const skipTestStep = scope.skipTestStep && alwaysIncludeExtra.length === 0 && importerTestsAdded.length === 0 && scenarioTests.length === 0;
703
+ const importerTestsNotRun = (options.includeImporterTests || scenarioTests.length > 0) && availableIndex
627
704
  ? importerTestsNotInRunSet(availableIndex, changedSources, testFiles) : scope.importerTestsNotRun;
628
705
  const unrunImporterTotal = lookupFailed ? null : (importerTestsNotRun?.total ?? 0);
629
706
  // Overflowed callers that ② ran anyway are not «not run».
630
- const callerTestsOverflow = scope.callerTestsOverflow.filter(({ file }) => !importerTestsAdded.includes(file));
707
+ const callerTestsOverflow = scope.callerTestsOverflow.filter(({ file }) => !importerTestsAdded.includes(file) && !scenarioTests.includes(file));
631
708
  const importerLookupFailed = Boolean(lookupFailed);
709
+ return { ...prepared, alwaysInclude, alwaysIncludeMissing, alwaysIncludeExtra, importerTestsAdded, scenarioTests,
710
+ testFiles, skipTestStep, importerTestsNotRun, unrunImporterTotal, callerTestsOverflow, importerLookupFailed };
711
+ }
712
+
713
+ /** Read-only preflight for the same changed-file → importer → scope selection used by `self gate`. */
714
+ export function selectGateTestFiles(cwd: string, options: GateSelectionOptions = {}, deps: GateSelectionDeps = {}) {
715
+ if (options.base && options.pr) throw new Error('use either --base <ref> or --pr <number>, not both');
716
+ const picked = finishGateTestSelection(prepareGateTestSelection(cwd, options, deps), options, cwd, deps);
717
+ return {
718
+ changedFiles: picked.selection.files,
719
+ baseRef: picked.selection.baseRef,
720
+ reason: picked.scope.reason,
721
+ testFiles: picked.testFiles,
722
+ skipTestStep: picked.skipTestStep,
723
+ unverified: picked.scope.unverified,
724
+ alwaysIncludeMissing: picked.alwaysIncludeMissing,
725
+ importerTestsAdded: picked.importerTestsAdded,
726
+ importerTestsNotRun: picked.importerTestsNotRun,
727
+ unrunImporterTotal: picked.unrunImporterTotal,
728
+ lookupFailed: picked.lookupFailed,
729
+ routeLookupFailures: picked.routeIndex?.lookupFailures ?? [],
730
+ callerTestsOverflow: picked.callerTestsOverflow,
731
+ };
732
+ }
733
+
734
+ export function runSelfGateCli(cwd: string, options: SelfGateCliOptions = {}, deps: SelfGateCliDeps = {}): SelfGateCliResult {
735
+ const capture: GateArtifactCapture = {};
736
+ const result = runSelfGateCliCore(cwd, options, deps, capture);
737
+ if (options.artifactDir !== undefined) emitGateArtifacts(cwd, options.artifactDir, result, capture, deps);
738
+ return result;
739
+ }
740
+
741
+ /** 쓰기 실패는 «경고 한 줄»로만 접는다 — 판정·exit 코드는 그대로다. */
742
+ function emitGateArtifacts(cwd: string, artifactDir: string, result: SelfGateCliResult, capture: GateArtifactCapture, deps: SelfGateCliDeps): void {
743
+ if (!artifactDir.trim()) {
744
+ result.lines.push('⚠ artifacts: --artifact-dir 가 빈 경로라 안 썼다 (판정은 그대로다)');
745
+ return;
746
+ }
747
+ const dir = resolveArtifactDir(artifactDir, cwd);
748
+ try {
749
+ const resolveRev = deps.resolveRev ?? defaultRevResolver(cwd);
750
+ const counts = gateArtifactCounts(result, capture);
751
+ const written = writeGateArtifacts({
752
+ dir,
753
+ head40: resolveRev('HEAD'),
754
+ base40: capture.baseRef ? resolveRev(capture.baseRef) : null,
755
+ envKey: (deps.envKey ?? defaultGateEnvKey)(),
756
+ testFiles: capture.testFiles ?? result.testFiles,
757
+ verdict: capture.verdict ?? null,
758
+ ...(capture.verdictLine !== undefined ? { verdictLine: capture.verdictLine } : {}),
759
+ exitCode: result.exitCode,
760
+ counts: counts.counts,
761
+ ...(counts.reason !== undefined ? { countsUnavailableReason: counts.reason } : {}),
762
+ ...(capture.unmeasuredReason !== undefined ? { unmeasuredReason: capture.unmeasuredReason } : {}),
763
+ ...(capture.sharded ? { sharded: capture.sharded } : {}),
764
+ ...(capture.unshardedRc !== undefined ? { unshardedRc: capture.unshardedRc } : {}),
765
+ ...(capture.artifactsUnavailable ? { artifactsUnavailable: capture.artifactsUnavailable } : {}),
766
+ });
767
+ result.lines.push(`artifacts: ${written.summaryPath} (junit ${written.junitFiles.length}${capture.artifactsUnavailable ? ` · unavailable=${capture.artifactsUnavailable}` : ''})`);
768
+ } catch (error) {
769
+ result.lines.push(`⚠ artifacts: ${dir} 에 못 썼다 — ${error instanceof Error ? error.message : String(error)} (판정은 그대로다)`);
770
+ }
771
+ }
772
+
773
+ function runSelfGateCliCore(cwd: string, options: SelfGateCliOptions, deps: SelfGateCliDeps, capture: GateArtifactCapture): SelfGateCliResult {
774
+ if (options.base && options.pr) throw new Error('use either --base <ref> or --pr <number>, not both');
775
+ if (options.shards !== undefined && (!Number.isSafeInteger(options.shards) || options.shards < 1)) throw new Error('invalid --shards count');
776
+ if (options.fixedShardDistribution && options.shards === undefined) throw new Error('--fixed-shard-distribution requires --shards <n>');
777
+ const prepared = prepareGateTestSelection(cwd, options, deps);
778
+ const { selection, lookupFailed, routeIndex, scope } = prepared;
779
+ const expose = publicExposureFiles(selection.files).length > 0
780
+ ? runExposeGate(cwd, selection.files, getUserConfig().harness?.exposeGate === 'strict' ? 'strict' : 'warn', deps.runCommand)
781
+ : { passed: true, log: '' };
782
+ const { alwaysInclude, alwaysIncludeMissing, alwaysIncludeExtra, importerTestsAdded, scenarioTests, testFiles,
783
+ skipTestStep, importerTestsNotRun, unrunImporterTotal, callerTestsOverflow, importerLookupFailed } = finishGateTestSelection(prepared, options, cwd, deps);
632
784
  const lines = [
633
785
  // ⛔⭐ 「changed=0」을 «초록»으로 읽는 사고가 났다(2026-09-02 · 🅕 보고 · 🅣 재현).
634
786
  // 커밋 «전»에 돌리면 작업 트리 변경은 «안 세므로» changed=0 이 나오는데,
@@ -662,8 +814,13 @@ export function runSelfGateCli(cwd: string, options: SelfGateCliOptions = {}, de
662
814
  ...(options.includeImporterTests
663
815
  ? [`importer tests added: ${lookupFailed ? `lookup failed (${lookupFailed})` : `${importerTestsAdded.length} (reason=changed-source-importer${importerTestsAdded.length ? ` · ${importerTestsAdded.join(', ')}` : ''})`}`]
664
816
  : []),
817
+ ...(options.scenario !== undefined || options.scenarioFile !== undefined ? [`scenario tests: ${scenarioTests.length} (${scenarioTests.join(', ')})`] : []),
665
818
  ];
666
819
  if (expose.log) lines.push(expose.log);
820
+ capture.baseRef = selection.baseRef;
821
+ capture.testFiles = skipTestStep ? [] : testFiles;
822
+ capture.unrunImporters = unrunImporterTotal;
823
+ if (lookupFailed) capture.importerLookupFailed = lookupFailed;
667
824
  if (options.pr) {
668
825
  const worktreeObservation = observePrWorktreeDirtiness(cwd, selection.prHeadRefName, {
669
826
  runCommand: deps.runCommand,
@@ -687,12 +844,22 @@ export function runSelfGateCli(cwd: string, options: SelfGateCliOptions = {}, de
687
844
  if (skipTestStep) {
688
845
  const policyPassed = runPolicyGates(selection.files, cwd, lines, deps);
689
846
  logGateCliBaseline(cwd, { ...ZERO_GATE_BASELINE, unrunImporterTotal, lookupFailed: importerLookupFailed });
847
+ Object.assign(capture, { verdict: 'skipped', artifactsUnavailable: 'no-tests' } satisfies GateArtifactCapture);
690
848
  return { exitCode: androidPassed && iosPassed && pwaPassed && policyPassed && expose.passed ? 0 : 1, lines, changedFiles: selection.files, testFiles: [], unverified: scope.unverified, documentPaths: scope.documentPaths, documentsWithoutDerivedTests: scope.documentsWithoutDerivedTests, alwaysIncludeMissing, importerTestsAdded, baseline: { introduced: 0, preexisting: 0, unknown: 0, preconditionUnmet: 0 } };
691
849
  }
692
850
 
693
- if (options.shards !== undefined) {
694
- const sharded = (deps.runShards ?? runShardedGateTests)(cwd, testFiles, selection.baseRef, options.shards);
695
- lines.push(`shards: ${sharded.shards.length} (${sharded.shards.map((shard) => `${shard.id}=${shard.files.join(',')}`).join('; ')})${sharded.measurementFailures ? ` · 측정 실패 ${sharded.measurementFailures}` : ''}`);
851
+ // The default direct Bun path has one process and one 300s limit. Route real child/PTY tests through
852
+ // the separated shard runner even when the caller did not request --shards.
853
+ if (options.shards !== undefined || testFiles.some(isGateSpawnLaneFile)) {
854
+ const spawnFileCount = testFiles.filter(isGateSpawnLaneFile).length;
855
+ const shardCount = options.shards ?? (Math.min(3, spawnFileCount) + Number(testFiles.length > spawnFileCount));
856
+ const runShards = deps.runShards ?? runShardedGateTests;
857
+ const sharded = options.fixedShardDistribution
858
+ ? runShards(cwd, testFiles, selection.baseRef, shardCount, undefined, undefined, undefined, { fixedDistribution: true })
859
+ : runShards(cwd, testFiles, selection.baseRef, shardCount);
860
+ capture.sharded = sharded;
861
+ capture.verdict = sharded.aggregate.status;
862
+ lines.push(`shards: ${sharded.shards.length} (${sharded.shards.map((shard) => `${shard.id}=${shard.files.join(',')}`).join('; ')})${sharded.measurementFailures ? ` · 측정 실패 ${sharded.measurementFailures}` : ''}${options.fixedShardDistribution ? ' · 고정 분배(사전 측정 없음)' : ''}`);
696
863
  // 조각이 «못 쟀다»일 때 이유가 줄에 없으면 자리·시간 초과·죽음을 가를 수 없다(OP 10-09 light RC) — 종료 코드·신호를 같이 싣는다.
697
864
  const side = (code: number | null, signal: string | null | undefined, junit: string | undefined) => signal ? `signal ${signal}` : code === null ? 'exit ?' : `exit ${code}${junit === undefined ? ' · junit 없음' : ''}`;
698
865
  for (const attempt of sharded.attempts) {
@@ -705,12 +872,23 @@ export function runSelfGateCli(cwd: string, options: SelfGateCliOptions = {}, de
705
872
  if (attempt.currentJUnit === undefined && attempt.currentDiagnostic) lines.push(`shard diagnostic: ${attempt.shardId} #${attempt.attempt} 지금 · ${renderDiagnostic(attempt.currentDiagnostic)}`);
706
873
  if (attempt.baselineJUnit === undefined && attempt.baselineDiagnostic) lines.push(`shard diagnostic: ${attempt.shardId} #${attempt.attempt} 기준 · ${renderDiagnostic(attempt.baselineDiagnostic)}`);
707
874
  }
875
+ // OP FU (MK #8178 shard-9) — 실패 조각의 자식 출력 꼬리 ⊕ exit/signal 을 파일로 남긴다(관측만 · 판정·exit 불변).
876
+ // --artifact-dir 이 있으면 그 아래, 없으면 임시 디렉토리(JUnit 임시 디렉토리 옆 tmpdir)에.
877
+ if (sharded.failureEvidence?.length) {
878
+ try {
879
+ const evidenceDir = options.artifactDir?.trim() ? resolveArtifactDir(options.artifactDir, cwd) : mkdtempSync(join(tmpdir(), 'elanous-shard-failure-'));
880
+ for (const path of writeShardFailureEvidence(evidenceDir, sharded.failureEvidence)) lines.push(`shard failure evidence: ${path}`);
881
+ } catch (error) {
882
+ lines.push(`⚠ shard failure evidence: 못 썼다 — ${error instanceof Error ? error.message : String(error)} (판정은 그대로다)`);
883
+ }
884
+ }
708
885
  const { aggregate } = sharded;
709
886
  let shardBaseline: SelfGateCliResult['baseline'];
710
887
  let unmeasuredReason: string | undefined;
711
888
  if (aggregate.status === 'unmeasured') {
712
889
  unmeasuredReason = aggregate.retryShardIds.join(', ') || sharded.reason || 'no complete JUnit';
713
890
  lines.push(`shards: unmeasured (${unmeasuredReason})`);
891
+ capture.unmeasuredReason = unmeasuredReason;
714
892
  }
715
893
  else if (aggregate.report) {
716
894
  const report = aggregate.report;
@@ -727,6 +905,7 @@ export function runSelfGateCli(cwd: string, options: SelfGateCliOptions = {}, de
727
905
  logGateCliBaseline(cwd, { ...ZERO_GATE_BASELINE, unrunImporterTotal, lookupFailed: importerLookupFailed });
728
906
  shardBaseline = { introduced: 0, preexisting: 0, unknown: 0, preconditionUnmet: 0 };
729
907
  }
908
+ capture.verdictLine = lines[lines.length - 1];
730
909
  const policyPassed = runPolicyGates(selection.files, cwd, lines, deps);
731
910
  return {
732
911
  exitCode: aggregate.status !== 'passed' || !policyPassed || !androidPassed || !iosPassed || !pwaPassed || !expose.passed ? 1 : 0,
@@ -739,8 +918,10 @@ export function runSelfGateCli(cwd: string, options: SelfGateCliOptions = {}, de
739
918
 
740
919
  const test = (deps.runTests ?? ((dir, files) => (deps.runCommand ?? defaultRunCommand)('bun', ['test', ...files], dir)))(cwd, testFiles);
741
920
  const worktreeLog = formatProcessOutput(test);
921
+ Object.assign(capture, { unshardedRc: test.status, artifactsUnavailable: 'unsharded' } satisfies GateArtifactCapture);
742
922
  if (test.status === 0 && !test.error) {
743
923
  lines.push(formatPartialObservationNote(unrunImporterTotal ?? 0, `tests: pass (${testFiles.length} files)`));
924
+ Object.assign(capture, { verdict: 'passed', verdictLine: lines[lines.length - 1] } satisfies GateArtifactCapture);
744
925
  const policyPassed = runPolicyGates(selection.files, cwd, lines, deps);
745
926
  logGateCliBaseline(cwd, { ...ZERO_GATE_BASELINE, unrunImporterTotal, lookupFailed: importerLookupFailed });
746
927
  return { exitCode: policyPassed && androidPassed && iosPassed && pwaPassed && expose.passed ? 0 : 1, lines, changedFiles: selection.files, testFiles, unverified: scope.unverified, documentPaths: scope.documentPaths, documentsWithoutDerivedTests: scope.documentsWithoutDerivedTests, alwaysIncludeMissing, importerTestsAdded, baseline: { introduced: 0, preexisting: 0, unknown: 0, preconditionUnmet: 0 } };
@@ -763,6 +944,10 @@ export function runSelfGateCli(cwd: string, options: SelfGateCliOptions = {}, de
763
944
  lookupFailed: importerLookupFailed,
764
945
  });
765
946
  lines.push(formatPartialObservationNote(unrunImporterTotal ?? 0, formatGateBaselineNote(report, unrunImporterTotal ?? 0)));
947
+ Object.assign(capture, {
948
+ verdict: report.introduced > 0 || report.unknown > 0 || report.preconditionUnmet > 0 ? 'failed' : 'passed',
949
+ verdictLine: lines[lines.length - 1],
950
+ } satisfies GateArtifactCapture);
766
951
  const policyPassed = runPolicyGates(selection.files, cwd, lines, deps);
767
952
  const exitCode = !androidPassed || !iosPassed || !pwaPassed || !policyPassed || !expose.passed || report.introduced > 0 || report.unknown > 0 || report.preconditionUnmet > 0 ? 1 : 0;
768
953
  return { exitCode, lines, changedFiles: selection.files, testFiles, unverified: scope.unverified, documentPaths: scope.documentPaths, documentsWithoutDerivedTests: scope.documentsWithoutDerivedTests, alwaysIncludeMissing, importerTestsAdded, baseline: { introduced: report.introduced, preexisting: report.preexisting, unknown: report.unknown, preconditionUnmet: report.preconditionUnmet } };
@@ -7,6 +7,7 @@ import { existsSync, lstatSync, realpathSync } from 'node:fs';
7
7
  import { loadavg } from 'node:os';
8
8
  import { isAbsolute, join, relative, resolve, sep } from 'node:path';
9
9
  import { debug } from '../debug/log.js';
10
+ import { gateHostReservationRoot, inspectReleaseGateHost } from '../release-loop/gate-host-reservation.js';
10
11
  import type { GateRemoteConfig } from '../user-config.js';
11
12
 
12
13
  export type GateRemoteTool = 'self-gate' | 'ci-typecheck-changed' | 'public-export-test-run';
@@ -16,14 +17,15 @@ export interface GateRemoteSettings {
16
17
  host: string;
17
18
  loadThreshold: number;
18
19
  hostCap: number;
20
+ hostCaps?: Record<string, number>;
19
21
  mirror: string;
20
22
  slotWaitSeconds: number;
21
23
  }
22
24
 
23
- export const MAX_HOST_CAP = 2;
25
+ export const MAX_HOST_CAP = 8;
24
26
 
25
27
  export const GATE_REMOTE_DEFAULTS: GateRemoteSettings = {
26
- enabled: true, host: 'node-b', loadThreshold: 20, hostCap: 2, mirror: '~/mirror/elanous-agent.git', slotWaitSeconds: 600,
28
+ enabled: true, host: 'node-b', loadThreshold: 20, hostCap: 2, hostCaps: {}, mirror: '~/mirror/elanous-agent.git', slotWaitSeconds: 600,
27
29
  };
28
30
 
29
31
  const hostPattern = /^(?:[\w.-]+@)?[\w.-]+$/;
@@ -43,13 +45,23 @@ export function resolveGateRemoteSettings(config: GateRemoteConfig | undefined,
43
45
  enabled: config?.enabled ?? envEnabled ?? GATE_REMOTE_DEFAULTS.enabled,
44
46
  host: isValidGateHost(host) ? host : GATE_REMOTE_DEFAULTS.host,
45
47
  loadThreshold: config?.loadThreshold ?? envLoad ?? GATE_REMOTE_DEFAULTS.loadThreshold,
46
- // The cell's ceiling is 2 per host (GATE-REMOTE acceptance); config may only lower it.
47
- hostCap: Math.min(MAX_HOST_CAP, Math.max(1, Math.floor(config?.hostCap ?? GATE_REMOTE_DEFAULTS.hostCap))),
48
+ hostCap: Math.min(GATE_REMOTE_DEFAULTS.hostCap, Math.max(1, Math.floor(config?.hostCap ?? GATE_REMOTE_DEFAULTS.hostCap))),
49
+ hostCaps: Object.fromEntries(Object.entries(config?.hostCaps ?? {}).filter(([key, value]) =>
50
+ isValidGateHost(key) && Number.isSafeInteger(value) && value >= 1 && value <= MAX_HOST_CAP)),
48
51
  mirror: mirrorPattern.test(mirror) && !mirror.split('/').includes('..') ? mirror : GATE_REMOTE_DEFAULTS.mirror,
49
52
  slotWaitSeconds: config?.slotWaitSeconds ?? GATE_REMOTE_DEFAULTS.slotWaitSeconds,
50
53
  };
51
54
  }
52
55
 
56
+ /** Explicit and rerouted hosts use their own slot budget; absent entries retain the default cap. */
57
+ export function gateRemoteHostCap(settings: GateRemoteSettings, host: string): number {
58
+ const caps = settings.hostCaps;
59
+ const bareHost = host.split('@').at(-1)!;
60
+ const configured = caps && Object.hasOwn(caps, host) ? caps[host]
61
+ : caps && Object.hasOwn(caps, bareHost) ? caps[bareHost] : undefined;
62
+ return Math.min(MAX_HOST_CAP, Math.max(1, Math.floor(configured ?? settings.hostCap)));
63
+ }
64
+
53
65
  /** The token after a bare `--remote` is its host when it is a valid ssh destination that is not an existing path and does
54
66
  * not look like a test/list file — so `--remote node-b.lan a.test.ts` takes the host and `--remote a.test.ts` keeps the file. */
55
67
  export function looksLikeHost(token: string, exists: (path: string) => boolean = existsSync): boolean {
@@ -105,17 +117,21 @@ export type SlotLockTool = 'flock' | 'lockf';
105
117
  const SLOT_LOCK = { flock: 'flock -n', lockf: 'lockf -k -s -t 0' } as const;
106
118
 
107
119
  /** `tools` is the preference order (default: Linux flock, then BSD/macOS lockf); a test can pin one path. */
108
- export function hostSlotScript(cap: number, dir = SLOT_DIR, tools: readonly SlotLockTool[] = ['flock', 'lockf']): string[] {
120
+ function hostSlotScriptWithSlot(cap: number, dir: string, tools: readonly SlotLockTool[], passSlot: boolean): string[] {
109
121
  const n = Math.min(MAX_HOST_CAP, Math.max(1, Math.floor(cap)));
110
122
  const pick = tools.map((tool, i) => `${i ? 'elif' : 'if'} command -v ${tool} >/dev/null 2>&1; then GL="${SLOT_LOCK[tool]}"; `).join('');
111
123
  return [
112
124
  `GS=${quote(dir)}; mkdir -p "$GS" || infra slots`,
113
125
  `${pick}else infra lock-tool; fi`,
114
- `n=1; while [ $n -le ${n} ]; do $GL "$GS/slot$n" sh "$W/run.sh"; s=$?; [ -f "$W/started" ] && exit $s; n=$((n+1)); done`,
115
- `echo "${BUSY}" >&2; exit 98`,
126
+ `n=1; while [ $n -le ${n} ]; do $GL "$GS/slot$n" ${passSlot ? 'env GATE_REMOTE_SLOT="$n" ' : ''}sh "$W/run.sh"; s=$?; [ -f "$W/started" ] && exit $s; n=$((n+1)); done`,
127
+ `echo "${BUSY}=$((n-1))" >&2; exit 98`,
116
128
  ];
117
129
  }
118
130
 
131
+ export function hostSlotScript(cap: number, dir = SLOT_DIR, tools: readonly SlotLockTool[] = ['flock', 'lockf']): string[] {
132
+ return hostSlotScriptWithSlot(cap, dir, tools, false);
133
+ }
134
+
119
135
  // ── remote execution ───────────────────────────────────────────────
120
136
 
121
137
  export interface ExecResult { rc: number | null; stdout: string; stderr: string; error?: string }
@@ -149,7 +165,7 @@ export const PUSH_TIMEOUT_MS = 10 * 60_000;
149
165
 
150
166
  export type RemoteRunOutcome =
151
167
  | { kind: 'ran'; rc: number; stdout: Buffer; stderr: Buffer; commit: string; host: string; ms: number }
152
- | { kind: 'busy' }
168
+ | { kind: 'busy'; occupied?: number }
153
169
  | { kind: 'infra'; reason: string };
154
170
 
155
171
  export interface RemoteRunOptions {
@@ -167,6 +183,8 @@ export interface RemoteRunOptions {
167
183
  /** Extra local refs whose commits must exist remotely (e.g. a --base ref). Resolved to shas. */
168
184
  refs?: string[];
169
185
  installPwa?: boolean;
186
+ /** An exclusive slot checkout path allows the opt-in TypeScript graph cache to survive scratch runs. */
187
+ stableCheckout?: boolean;
170
188
  timeoutMs?: number;
171
189
  /** Files the remote run needs from this machine (e.g. the private export redaction table): sent over ssh stdin, written
172
190
  * mode 600 inside the scratch directory, exposed to the tool via `env`, removed with it. Never on argv. */
@@ -177,7 +195,7 @@ export interface PayloadFile { env: string; content: string }
177
195
  export type RemoteArgv = string[] | ((sha: (ref: string) => string) => string[]);
178
196
 
179
197
  /** Build the remote shell script (exported for tests). Every setup failure ends in an INFRA marker, the tool's rc in an RC marker. */
180
- export function remoteScript(input: { mirror: string; commit: string; fetchShas: string[]; mainSha?: string; argv: string[]; installPwa: boolean; payloadFiles?: PayloadFile[]; hostCap?: number; slotDir?: string }): string {
198
+ export function remoteScript(input: { mirror: string; commit: string; fetchShas: string[]; mainSha?: string; argv: string[]; installPwa: boolean; stableCheckout?: boolean; payloadFiles?: PayloadFile[]; hostCap?: number; slotDir?: string }): string {
181
199
  const mirror = input.mirror.startsWith('~/') ? `"$HOME"/${quote(input.mirror.slice(2))}` : quote(input.mirror);
182
200
  const fetches = input.fetchShas.map((sha) => `refs/elanous/gate/${sha}:refs/elanous/gate/${sha}`).join(' ');
183
201
  const payloads = (input.payloadFiles ?? []).filter((file) => /^[A-Z_][A-Z0-9_]*$/.test(file.env));
@@ -193,8 +211,25 @@ export function remoteScript(input: { mirror: string; commit: string; fetchShas:
193
211
  const work = [
194
212
  `infra() { echo "${INFRA}$1" >&2; exit 97; }`,
195
213
  ': > "$W/started"',
196
- `git clone -q --shared --no-checkout ${mirror} "$W/t" >"$W/setup.log" 2>&1 || infra clone`,
197
- 'cd "$W/t" || infra cd',
214
+ ...(input.stableCheckout ? [
215
+ 'T="$HOME/.cache/elanous/gate-remote/slot$GATE_REMOTE_SLOT/checkout"',
216
+ 'mkdir -p "$(dirname "$T")" || infra stable-dir',
217
+ 'if [ -d "$T/.git" ]; then',
218
+ ' git -C "$T" reset --hard -q >>"$W/setup.log" 2>&1 || infra stable-reset',
219
+ ' git -C "$T" clean -ffd -q >>"$W/setup.log" 2>&1 || infra stable-clean',
220
+ ` git -C "$T" remote set-url origin ${mirror} >>"$W/setup.log" 2>&1 || infra stable-origin`,
221
+ 'else',
222
+ ' rm -rf "$T" || infra stable-remove',
223
+ ` git clone -q --shared --no-checkout ${mirror} "$T" >"$W/setup.log" 2>&1 || infra clone`,
224
+ 'fi',
225
+ 'rm -rf "$T/apps/pwa/.next" || infra pwa-build-clean',
226
+ 'rm -f "$T/apps/pwa/"*.tsbuildinfo || infra pwa-buildinfo-clean',
227
+ 'ELANOUS_TSC_CACHE_DIR="$HOME/.cache/elanous/gate-remote/slot$GATE_REMOTE_SLOT/cache"; export ELANOUS_TSC_CACHE_DIR',
228
+ ] : [
229
+ 'T="$W/t"',
230
+ `git clone -q --shared --no-checkout ${mirror} "$T" >"$W/setup.log" 2>&1 || infra clone`,
231
+ ]),
232
+ 'cd "$T" || infra cd',
198
233
  `git fetch -q origin ${fetches} >>"$W/setup.log" 2>&1 || infra fetch`,
199
234
  `git checkout -q --detach ${input.commit} >>"$W/setup.log" 2>&1 || infra checkout`,
200
235
  ...(input.mainSha ? [`git update-ref refs/remotes/origin/main ${input.mainSha} || infra origin-main`] : []),
@@ -221,7 +256,7 @@ export function remoteScript(input: { mirror: string; commit: string; fetchShas:
221
256
  `cat > "$W/run.sh" <<'${RUN}' || infra run-script`,
222
257
  ...work,
223
258
  RUN,
224
- ...hostSlotScript(input.hostCap ?? GATE_REMOTE_DEFAULTS.hostCap, input.slotDir),
259
+ ...hostSlotScriptWithSlot(input.hostCap ?? GATE_REMOTE_DEFAULTS.hostCap, input.slotDir ?? SLOT_DIR, ['flock', 'lockf'], !!input.stableCheckout),
225
260
  '}',
226
261
  '',
227
262
  ].join('\n');
@@ -277,7 +312,7 @@ export function runOnRemote(opts: RemoteRunOptions, runner: GateRemoteRunner = r
277
312
  env: { ...process.env, GIT_SSH_COMMAND: 'ssh -o BatchMode=yes -o ConnectTimeout=10', GIT_TERMINAL_PROMPT: '0' },
278
313
  });
279
314
  if (push.rc !== 0 || push.error) return { kind: 'infra', reason: `push-failed rc=${push.rc}: ${(push.stderr || push.error || '').trim().split('\n').slice(-2).join(' | ')}` };
280
- const script = remoteScript({ mirror: opts.mirror, commit, fetchShas: shas, mainSha, argv, installPwa: opts.installPwa ?? false, payloadFiles: opts.payloadFiles, hostCap: opts.hostCap, slotDir: opts.slotDir });
315
+ const script = remoteScript({ mirror: opts.mirror, commit, fetchShas: shas, mainSha, argv, installPwa: opts.installPwa ?? false, stableCheckout: opts.stableCheckout, payloadFiles: opts.payloadFiles, hostCap: opts.hostCap, slotDir: opts.slotDir });
281
316
  const run = runner.ssh(opts.host, script, opts.timeoutMs ?? 120 * 60_000);
282
317
  // The script prints "\n<marker><rc>\n": everything before that leading newline is the tool's own stderr, byte for byte.
283
318
  const stdoutBytes = Buffer.from(run.stdout);
@@ -288,7 +323,10 @@ export function runOnRemote(opts: RemoteRunOptions, runner: GateRemoteRunner = r
288
323
  // run exits 0 after the rc marker, and its stderr belongs to the tool — never scanned for markers.
289
324
  if (!(run.rc === 0 && marker >= 0)) {
290
325
  const last = stderrText.trimEnd().split('\n').at(-1) ?? '';
291
- if (run.rc === 98 && last === BUSY) return { kind: 'busy' };
326
+ if (run.rc === 98) {
327
+ const busy = /^__GATE_REMOTE_BUSY(?:=(\d+))?$/.exec(last);
328
+ if (busy) return { kind: 'busy', ...(busy[1] !== undefined ? { occupied: Number(busy[1]) } : {}) };
329
+ }
292
330
  if (run.rc === 97 && last.startsWith(INFRA)) return { kind: 'infra', reason: `remote-${last.slice(INFRA.length).trim()}` };
293
331
  }
294
332
  // No marker, or ssh did not end cleanly after it (the script always exits 0 once the marker is written): output may be cut.
@@ -371,10 +409,20 @@ export interface DispatchOptions {
371
409
  installPwa?: boolean;
372
410
  payloadFiles?: PayloadFile[];
373
411
  json?: boolean;
374
- /** Set when this invocation cannot be reproduced remotely (e.g. self gate without --base): a remote decision becomes a
375
- * logged fallback with this reason instead of a silent local run. */
412
+ /** Set when this invocation cannot be reproduced remotely (e.g. self gate without --base): automatic placement
413
+ * falls back with this reason; an explicit self gate --remote reports an unmeasured check instead. */
376
414
  localOnlyReason?: string;
415
+ /** Self gate CLI only: explicit --remote must never be measured by a local fallback. */
416
+ explicitRemoteOnly?: boolean;
417
+ /** Self gate CLI only: name the host for both automatic and explicit remote execution. */
418
+ reportRemoteHost?: boolean;
419
+ /** Self gate's state universe; omitted by other callers and injected tests. */
420
+ reservationRoot?: string;
377
421
  runLocal: () => number | Promise<number>;
422
+ /** Called once with the final remote outcome (never on a local decision), before its output passes through or the
423
+ * local fallback runs — e.g. `self gate --artifact-dir` fetches (ran) or removes (infra) the host-side artifact dir.
424
+ * A throw is logged and swallowed: the hook never changes the check's rc. */
425
+ onRemoteOutcome?: (outcome: RemoteRunOutcome, host: string, runner: GateRemoteRunner) => void;
378
426
  }
379
427
 
380
428
  export interface DispatchDeps {
@@ -394,21 +442,52 @@ async function loadSettings(): Promise<GateRemoteSettings> {
394
442
  } catch { return resolveGateRemoteSettings(undefined); }
395
443
  }
396
444
 
397
- /** Decide local/remote, run, and return the tool's exit code. Infra failure ⇒ local fallback (never a test result). */
445
+ /** Decide local/remote, run, and return the tool's exit code. Automatic infra failure ⇒ visible local fallback. */
398
446
  export async function dispatchHeavyCheck(opts: DispatchOptions, deps: DispatchDeps = {}): Promise<number> {
399
447
  const settings = deps.settings ?? await loadSettings();
400
448
  const env = deps.env ?? process.env;
401
449
  const write = deps.write ?? { out: (d: string | Uint8Array) => process.stdout.write(d), err: (d: string | Uint8Array) => process.stderr.write(d) };
402
450
  const decision = decideGateDispatch({ flags: opts.flags, settings, load1: deps.load1 ?? loadavg()[0]!, env });
403
451
  debug.log('gate.remote', 'decision', { tool: opts.tool, mode: decision.mode, host: decision.host, reason: decision.reason, load1: decision.load1 });
404
- if (decision.mode === 'local') return opts.runLocal();
405
- const host = decision.host!;
452
+ if (decision.mode === 'local') {
453
+ if (opts.explicitRemoteOnly && opts.flags.remote !== undefined && decision.reason !== 'flag-local') {
454
+ write.err(`[gate.remote] remote unmeasured — ${decision.reason}\n`);
455
+ return 1;
456
+ }
457
+ return opts.runLocal();
458
+ }
459
+ let host = decision.host!;
460
+ const reservationRoot = opts.tool === 'self-gate' ? opts.reservationRoot ?? (deps.runner ? undefined : gateHostReservationRoot()) : undefined;
461
+ if (reservationRoot) {
462
+ const reservationHost = host.split('@').at(-1)!;
463
+ const first = inspectReleaseGateHost(reservationRoot, reservationHost);
464
+ if (first.status !== 'free') {
465
+ const alternate = reservationHost === 'node-b' ? 'node-c' : null;
466
+ const other = alternate ? inspectReleaseGateHost(reservationRoot, alternate) : null;
467
+ if (alternate && other?.status === 'free') {
468
+ debug.log('gate.remote', 'host-reserved-reroute', { from: host, to: alternate, reason: first.reason, status: first.status });
469
+ write.err(`[gate.remote] ${host} reserved (${first.reason}) — using ${alternate}\n`);
470
+ host = alternate;
471
+ } else {
472
+ const reason = `host-reserved: ${host} (${first.reason}); alternate ${alternate ?? '(none)'} ${other?.status ?? 'unavailable'}`;
473
+ debug.log('gate.remote', 'host-reserved-unmeasured', { host, reason });
474
+ write.err(`[gate.remote] remote unmeasured — ${reason}\n`);
475
+ return 1;
476
+ }
477
+ }
478
+ }
406
479
  const fallback = (reason: string) => {
480
+ if (opts.explicitRemoteOnly && opts.flags.remote !== undefined) {
481
+ debug.log('gate.remote', 'remote-unmeasured', { tool: opts.tool, host, reason });
482
+ write.err(`[gate.remote] remote unmeasured on ${host} — ${reason.replace(/\s+/g, ' ').trim()}\n`);
483
+ return 1;
484
+ }
407
485
  debug.log('gate.remote', 'fallback', { tool: opts.tool, host, reason });
408
486
  write.err(`[gate.remote] fallback to local — ${reason}\n`);
409
487
  return opts.runLocal();
410
488
  };
411
489
  if (opts.localOnlyReason) return fallback(opts.localOnlyReason);
490
+ const hostCap = gateRemoteHostCap(settings, host);
412
491
  const sleep = deps.sleep ?? ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)));
413
492
  const now = deps.now ?? Date.now;
414
493
  const deadline = now() + settings.slotWaitSeconds * 1000;
@@ -416,26 +495,32 @@ export async function dispatchHeavyCheck(opts: DispatchOptions, deps: DispatchDe
416
495
  let outcome: RemoteRunOutcome;
417
496
  for (;;) {
418
497
  try {
419
- outcome = runOnRemote({ repo: opts.repo, host, mirror: settings.mirror, argv: opts.remoteArgv, refs: opts.refs, ...(opts.commit !== undefined ? { commit: opts.commit } : {}), installPwa: opts.installPwa, payloadFiles: opts.payloadFiles, hostCap: settings.hostCap }, deps.runner);
498
+ outcome = runOnRemote({ repo: opts.repo, host, mirror: settings.mirror, argv: opts.remoteArgv, refs: opts.refs, ...(opts.commit !== undefined ? { commit: opts.commit } : {}), installPwa: opts.installPwa, payloadFiles: opts.payloadFiles, hostCap }, deps.runner);
420
499
  } catch (error) {
421
500
  outcome = { kind: 'infra', reason: `exception: ${error instanceof Error ? error.message : String(error)}` };
422
501
  }
423
502
  if (outcome.kind !== 'busy') break;
424
- debug.log('gate.remote', 'host-busy', { tool: opts.tool, host, cap: settings.hostCap });
425
- // Never start an attempt past the deadline: an expired wait means local, even if a slot would free a moment later.
503
+ debug.log('gate.remote', 'host-busy', { tool: opts.tool, host, cap: hostCap, occupied: outcome.occupied ?? null });
504
+ // Never start an attempt past the deadline: an expired wait falls back only for automatic placement.
426
505
  const left = deadline - now();
427
506
  if (left <= 0) break;
428
507
  await sleep(Math.min(15_000, left));
429
508
  if (now() >= deadline) break;
430
509
  }
431
- if (outcome.kind === 'busy') return fallback(`host-busy (cap ${settings.hostCap} held for ${settings.slotWaitSeconds}s)`);
510
+ if (opts.onRemoteOutcome) {
511
+ try { opts.onRemoteOutcome(outcome, host, deps.runner ?? realGateRemoteRunner); }
512
+ catch (error) { debug.log('gate.remote', 'outcome-hook-failed', { tool: opts.tool, host, error: error instanceof Error ? error.message : String(error) }); }
513
+ }
514
+ if (outcome.kind === 'busy') return fallback(`host-busy (occupied ${outcome.occupied ?? 'unknown'}/${hostCap}, held for ${settings.slotWaitSeconds}s)`);
432
515
  if (outcome.kind === 'infra') return fallback(outcome.reason);
433
- // Parity: the tool's own stdout/stderr/rc pass through unchanged — only a JSON stdout gains host · remote:true.
516
+ // Parity: the tool's own stdout/stderr/rc pass through unchanged; JSON gains host · remote:true,
517
+ // and the self gate CLI appends a host diagnostic to human-readable stderr.
434
518
  debug.log('gate.remote', 'result', { tool: opts.tool, host: outcome.host, commit: outcome.commit, rc: outcome.rc, ms: outcome.ms });
435
- // Bytes pass through untouched; only a JSON object stdout is re-serialised (to add host · remote:true).
519
+ // Tool bytes pass through untouched; only a JSON object stdout is re-serialised (to add host · remote:true).
436
520
  const text = opts.json ? outcome.stdout.toString('utf8') : undefined;
437
521
  const annotated = text !== undefined ? annotateJsonStdout(text, outcome.host) : undefined;
438
522
  write.out(annotated !== undefined && annotated !== text ? annotated : outcome.stdout);
439
523
  write.err(outcome.stderr);
524
+ if (opts.reportRemoteHost && !opts.json) write.err(`[gate.remote] executed on ${outcome.host} (rc=${outcome.rc})\n`);
440
525
  return outcome.rc;
441
526
  }