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
@@ -11,13 +11,23 @@ import { hostLeaseBaseDir, type PendingJobIdentity } from '../../pod-lease/host-
11
11
  // POD-CPU-REQUEST-RIGHTSIZE(10-09 · OP 실측): 하니스 Pod 실사용은 대부분 100m 미만(LLM 응답 대기)인데 요청 1 CPU 를
12
12
  // 잡아 node-b 32코어가 «요청 31/32»로 막혔다(실사용 4.6코어 · 14%). 요청은 실사용 근처로, 한도(4)는 그대로 — 몰릴 때는 한도까지 쓴다.
13
13
  // 게이트·pod-command 기본은 pod-command-job.ts 의 POD_COMMAND_CPU_REQUEST_DEFAULT(1)로 따로 둔다(테스트 조각은 CPU 를 실제로 쓴다).
14
- export const POD_CHILD_REQUESTS = { cpu: '250m', memory: '6Gi' } as const;
14
+ // SI-TASK-ADMIT-RIGHTSIZE: one host env restores the previous 6Gi request and uncapped p95 admission.
15
+ // An invalid override fails conservative at 6Gi rather than silently under-reserving a Pod.
16
+ export function resolveSiTaskMemoryPolicy(override: string | undefined): { requestMemory: string; admissionCapBytes: number | null; source: string } {
17
+ if (override === undefined) return { requestMemory: '3Gi', admissionCapBytes: 3 * 1024 ** 3, source: 'default' };
18
+ if (override === '6Gi') return { requestMemory: '6Gi', admissionCapBytes: null, source: 'override' };
19
+ return { requestMemory: '6Gi', admissionCapBytes: null, source: 'invalid-override-fallback' };
20
+ }
21
+ const siMemoryPolicy = resolveSiTaskMemoryPolicy(process.env.ELANOUS_POD_SI_REQUEST_MEMORY);
22
+ export const POD_CHILD_REQUESTS = { cpu: '250m', memory: siMemoryPolicy.requestMemory } as const;
15
23
  /** Finished harness and command Jobs release their Pod slots after 30 minutes. */
16
24
  export const POD_JOB_TTL_AFTER_FINISHED_SECONDS = 1800;
17
25
  /** 게이트가 돌 때 하니스 «한 자리»로 셈하는 CPU — 요청(250m)보다 보수적으로 1 코어를 잡아 게이트 조각의 CPU 를 지킨다(GATE-RESERVE-AUTO 셈은 그대로). */
18
26
  export const HARNESS_SLOT_CPU_BESIDE_GATE = '1';
19
27
  /** POD-ADMIT-BY-USAGE — observed usage is scaled by this before it reserves memory. */
20
28
  const POD_USAGE_HEADROOM = 1.5;
29
+ /** New standard si-task admission only. Existing Running Pods keep their observed/requested reservation. */
30
+ export const POD_SI_ADMISSION_CAP_BYTES = siMemoryPolicy.admissionCapBytes;
21
31
  /** POD-ADMIT-BY-USAGE — a node keeps at least this share of allocatable memory free (real usage and bookkeeping). */
22
32
  const POD_NODE_FREE_FLOOR_RATIO = 0.1;
23
33
 
@@ -477,6 +487,7 @@ export function measurePoolLease(members: readonly PodPoolMember[], deps: { kube
477
487
  const leasedJobs = new Set<string>();
478
488
  const gateJobs = new Map<string, number>();
479
489
  const boundGateJobs = new Set<string>();
490
+ const commandJobs = new Set<string>();
480
491
  let jobsValid = true;
481
492
  for (const job of jobItems) {
482
493
  const meta = object(job.metadata);
@@ -484,6 +495,7 @@ export function measurePoolLease(members: readonly PodPoolMember[], deps: { kube
484
495
  if (object(meta.labels)?.['elanous.substrate'] !== 'pod') continue;
485
496
  if (typeof meta.name !== 'string' || !meta.name) { jobsValid = false; continue; }
486
497
  jobNames.add(meta.name);
498
+ if (object(meta.labels)?.['elanous.kind'] === 'command') commandJobs.add(meta.name);
487
499
  // GATE-RESERVE-AUTO: a release-gate shard Job (`gate-<uuid>`, kind=command) reserves its Pod template's CPU
488
500
  // from creation until it is terminal — also while its Pod is still Pending or not created yet.
489
501
  if (meta.name.startsWith('gate-') && object(meta.labels)?.['elanous.kind'] === 'command') {
@@ -521,7 +533,8 @@ export function measurePoolLease(members: readonly PodPoolMember[], deps: { kube
521
533
  } else reservationsValid = false;
522
534
  const ownerJob = String(labels?.['elanous.job'] ?? labels?.['job-name'] ?? '');
523
535
  const gatePod = metadata?.namespace === 'elanous-test' && gateJobs.has(ownerJob);
524
- const harness = metadata?.namespace === 'elanous-test' && labels?.['elanous.probe'] !== 'dns' && !gatePod &&
536
+ const commandPod = labels?.['elanous.kind'] === 'command' || commandJobs.has(ownerJob);
537
+ const harness = metadata?.namespace === 'elanous-test' && labels?.['elanous.probe'] !== 'dns' && !gatePod && !commandPod &&
525
538
  (labels?.['elanous.substrate'] === 'pod' || jobNames.has(String(labels?.['elanous.job'] ?? '')));
526
539
  const spec = object(pod.spec);
527
540
  let limit: number | null = null;
@@ -642,7 +655,7 @@ export function recommendConcurrency(measure: PoolLeaseMeasure, options: { capac
642
655
  if (goalBytes === null || goalBytes <= 0) return { ...empty, reason: '측정 불가: perGoalMemory' };
643
656
  const admission = admissionByUsage(measure, goalBytes, options.perGoalMemory !== undefined);
644
657
  if (!measure.members.length || measure.members.some((m) => m.running === null || m.pending === null || m.unleasedRunning === null || m.allocatableMemoryBytes === null || m.memoryLimitBytes === null || m.allocatableCpuMillicores === null || m.availableMemoryByNodeBytes === null)) {
645
- debug.log('pod-lease', 'admit-by-usage', { samples: admission.samples, p95Bytes: admission.p95Bytes, admitBytes: admission.admitBytes, fallback: admission.fallback, recommended: null, limitedBy: null, capacity: options.capacity, capacitySlots: null, memorySlots: null, running: null, pending: null });
658
+ debug.log('pod-lease', 'admit-by-usage', { samples: admission.samples, p95Bytes: admission.p95Bytes, admitBytes: admission.admitBytes, fallback: admission.fallback, requestMemory: siMemoryPolicy.requestMemory, admissionCapBytes: POD_SI_ADMISSION_CAP_BYTES, policySource: siMemoryPolicy.source, recommended: null, limitedBy: null, capacity: options.capacity, capacitySlots: null, memorySlots: null, running: null, pending: null });
646
659
  return { ...empty, reason: `측정 불가: cluster${measure.members.map((m) => m.reason ? ` ${m.context}: ${m.reason}` : '').join('')}` };
647
660
  }
648
661
  if (!Number.isSafeInteger(options.capacity) || options.capacity < 0) return { ...empty, reason: '측정 불가: capacity' };
@@ -694,7 +707,7 @@ export function recommendConcurrency(measure: PoolLeaseMeasure, options: { capac
694
707
  && m.reason !== 'dns' && m.running !== null && m.pending !== null && m.running + m.pending >= m.capacity);
695
708
  const limitedBy: PoolLeaseRecommendation['limitedBy'] = memoryBound < capacitySlots ? 'memory' : defaultCapFull ? 'pool-cap' : 'capacity';
696
709
  if (unleasedRunning > 0) debug.log('pod-lease', 'unleased', { unleasedRunning, running, recommended });
697
- debug.log('pod-lease', 'admit-by-usage', { samples: admission.samples, p95Bytes: admission.p95Bytes, admitBytes: admission.admitBytes, fallback: admission.fallback, recommended, limitedBy, capacity: options.capacity, capacitySlots, memorySlots, running, pending });
710
+ debug.log('pod-lease', 'admit-by-usage', { samples: admission.samples, p95Bytes: admission.p95Bytes, admitBytes: admission.admitBytes, fallback: admission.fallback, requestMemory: siMemoryPolicy.requestMemory, admissionCapBytes: POD_SI_ADMISSION_CAP_BYTES, policySource: siMemoryPolicy.source, recommended, limitedBy, capacity: options.capacity, capacitySlots, memorySlots, running, pending });
698
711
  return { recommended, limitedBy, reason: measure.members.some((m) => m.reason === 'dns') ? 'dns' : null, capacitySlots, memorySlots, liteMemorySlots, placeableSlots, accountSlots, running, pending, pendingJobs, unleasedRunning, admission };
699
712
  }
700
713
 
@@ -714,7 +727,8 @@ function admissionByUsage(measure: PoolLeaseMeasure, goalBytes: number, explicit
714
727
  : live.some((m) => m.harnessUsageBytes == null) ? (live.map((m) => m.harnessUsageBytes == null ? `${m.context}: ${m.usageReason ?? 'usage not measured'}` : '').filter(Boolean).join('; '))
715
728
  : p95Bytes === null ? 'no Running harness Pod usage samples'
716
729
  : null;
717
- const admitBytes = fallback !== null || p95Bytes === null ? goalBytes
718
- : Math.min(goalBytes, Math.max(requestBytes, Math.ceil(p95Bytes * POD_USAGE_HEADROOM)));
730
+ const observedBytes = p95Bytes === null ? null : Math.ceil(p95Bytes * POD_USAGE_HEADROOM);
731
+ const admitBytes = fallback !== null || observedBytes === null ? goalBytes
732
+ : Math.min(goalBytes, Math.max(requestBytes, POD_SI_ADMISSION_CAP_BYTES === null ? observedBytes : Math.min(observedBytes, POD_SI_ADMISSION_CAP_BYTES)));
719
733
  return { samples: samples.length, p95Bytes, admitBytes, fallback };
720
734
  }
@@ -0,0 +1,169 @@
1
+ /**
2
+ * POD-LIFECYCLE-STAGES (P0 · Pod 재사용 기준선) — Pod 하나가 끝날 때 «어느 단계에 시간이 갔나»를 한 줄로 낸다.
3
+ *
4
+ * 계약: category `pod.lifecycle` · event `stages` ·
5
+ * data `{ runId, podJob, host, kind, stages: [{ stage, start, end, clock }], outcome, missing }`.
6
+ * - 시각은 epoch ms 하나로 통일한다(Pod 시계의 초 값은 ×1000).
7
+ * - `clock` 은 그 값이 어느 시계에서 왔나 — `host`(이 프로세스) · `k8s`(API 서버 시각) · `pod`(Pod 안 `date +%s`) · `mixed`.
8
+ * - ⛔ «0초»와 «못 잼»을 섞지 않는다: 시작·끝 둘 다 잰 단계만 `stages` 에 들어가고, 나머지는 이름만 `missing` 에 간다.
9
+ * 끝이 시작보다 앞서면(시계가 어긋났다) 그 단계도 지어내지 않고 `missing` 이다.
10
+ *
11
+ * 관측만 한다 — 판정·rc·파일을 바꾸지 않고, 내보내기가 실패해도 삼킨다.
12
+ */
13
+ import { debug } from '../../debug/log.js';
14
+
15
+ export const POD_LIFECYCLE_STAGES = ['create', 'schedule', 'clone', 'mirror', 'deps', 'test', 'delete'] as const;
16
+ /** si-task Pod 은 시험이 아니라 `self implement`(또는 harness ask/say) 본체를 돈다 — 그 칸은 `test` 가 아니라 `run` 이다. */
17
+ export const SI_TASK_POD_LIFECYCLE_STAGES = ['create', 'schedule', 'clone', 'mirror', 'deps', 'run', 'delete'] as const;
18
+ export type PodLifecycleStageName = typeof POD_LIFECYCLE_STAGES[number] | typeof SI_TASK_POD_LIFECYCLE_STAGES[number];
19
+ export type PodLifecycleClock = 'host' | 'k8s' | 'pod' | 'mixed';
20
+ export type PodLifecycleKind = 'gate' | 'si-task';
21
+
22
+ export interface PodLifecycleStage { stage: PodLifecycleStageName; start: number; end: number; clock: PodLifecycleClock }
23
+
24
+ export interface PodLifecycleEvent {
25
+ runId: string | null;
26
+ podJob: string | null;
27
+ host: string | null;
28
+ kind: PodLifecycleKind;
29
+ stages: PodLifecycleStage[];
30
+ outcome: string;
31
+ missing: PodLifecycleStageName[];
32
+ [extra: string]: unknown;
33
+ }
34
+
35
+ type Measured = Partial<Record<PodLifecycleStageName, { start?: number; end?: number; clock: PodLifecycleClock }>>;
36
+
37
+ /** ISO 문자열 → epoch ms (못 읽으면 undefined — 지어내지 않는다). */
38
+ export function isoMs(value: string | undefined): number | undefined {
39
+ if (typeof value !== 'string') return undefined;
40
+ const ms = Date.parse(value);
41
+ return Number.isFinite(ms) ? ms : undefined;
42
+ }
43
+
44
+ /** Pod 시계의 epoch 초 → ms. */
45
+ function podSecondsMs(value: number | undefined): number | undefined {
46
+ return typeof value === 'number' && Number.isFinite(value) ? value * 1000 : undefined;
47
+ }
48
+
49
+ /** 잰 것만 stages 로, 나머지는 missing 으로 — 계약의 단계 순서대로. */
50
+ export function splitPodLifecycleStages(measured: Measured, order: readonly PodLifecycleStageName[] = POD_LIFECYCLE_STAGES): { stages: PodLifecycleStage[]; missing: PodLifecycleStageName[] } {
51
+ const stages: PodLifecycleStage[] = [];
52
+ const missing: PodLifecycleStageName[] = [];
53
+ for (const name of order) {
54
+ const item = measured[name];
55
+ if (item && item.start !== undefined && item.end !== undefined && item.end >= item.start) stages.push({ stage: name, start: item.start, end: item.end, clock: item.clock });
56
+ else missing.push(name);
57
+ }
58
+ return { stages, missing };
59
+ }
60
+
61
+ /**
62
+ * gate shard Pod — `pod-N.json` 에 쓰는 바로 그 값(phaseTimes · podStages · podLifecycle)에서 단계를 고른다.
63
+ * - create = 풀 자리 배정(없으면 입장 끝 · host) → Pod 생성(k8s)
64
+ * - schedule = Pod 생성 → PodScheduled (k8s)
65
+ * - clone = 자식 컨테이너 PID 1(prep-start) → 셸 첫 줄(clone-end) (pod)
66
+ * - deps = deps-start → deps-end (pod) · test = test-start → test-end (pod)
67
+ * - mirror · delete = gate 는 Pod 별로 재지 않는다 → 늘 missing.
68
+ */
69
+ export function gatePodLifecycleStages(input: {
70
+ phaseTimes?: Readonly<Record<string, string>>;
71
+ podStages?: Readonly<Record<string, number>>;
72
+ podLifecycle?: Readonly<Record<string, string>>;
73
+ }): { stages: PodLifecycleStage[]; missing: PodLifecycleStageName[] } {
74
+ const phase = input.phaseTimes ?? {};
75
+ const pod = input.podStages ?? {};
76
+ const k8s = input.podLifecycle ?? {};
77
+ return splitPodLifecycleStages({
78
+ create: { start: isoMs(phase.poolPlaced) ?? isoMs(phase.admissionEnd), end: isoMs(k8s.podCreated), clock: 'mixed' },
79
+ schedule: { start: isoMs(k8s.podCreated), end: isoMs(k8s.podScheduled), clock: 'k8s' },
80
+ clone: { start: podSecondsMs(pod['prep-start']), end: podSecondsMs(pod['clone-end']), clock: 'pod' },
81
+ deps: { start: podSecondsMs(pod['deps-start']), end: podSecondsMs(pod['deps-end']), clock: 'pod' },
82
+ test: { start: podSecondsMs(pod['test-start']), end: podSecondsMs(pod['test-end']), clock: 'pod' },
83
+ });
84
+ }
85
+
86
+ /** gate shard 의 끝 — 판정 코드와 같은 순서(잡 실패 → 정지 → 시간 초과 → 판정 없음 → rc). */
87
+ export function gatePodOutcome(o: { jobFailed: boolean; stalled: boolean; timedOut: boolean; rc: number | undefined }): string {
88
+ if (o.jobFailed) return 'job-failed';
89
+ if (o.stalled) return 'stall';
90
+ if (o.timedOut) return 'timeout';
91
+ if (o.rc === undefined) return 'no-verdict';
92
+ return o.rc === 0 ? 'pass' : 'fail';
93
+ }
94
+
95
+ export function gatePodLifecycleEvent(input: {
96
+ runId?: string | null;
97
+ podJob?: string | null;
98
+ host?: string | null;
99
+ shard: string;
100
+ attemptId?: string;
101
+ phaseTimes?: Readonly<Record<string, string>>;
102
+ podStages?: Readonly<Record<string, number>>;
103
+ podLifecycle?: Readonly<Record<string, string>>;
104
+ outcome: string;
105
+ }): PodLifecycleEvent {
106
+ const { stages, missing } = gatePodLifecycleStages(input);
107
+ return { runId: input.runId ?? null, podJob: input.podJob ?? null, host: input.host ?? null, kind: 'gate', stages, outcome: input.outcome, missing,
108
+ shard: input.shard, ...(input.attemptId ? { attemptId: input.attemptId } : {}) };
109
+ }
110
+
111
+ /** si-task Pod 셸이 찍는 단계 표지 — `ELANOUS_POD_STAGE <stage> <start|end> <epoch초>` (Pod 시계 · `date +%s`). */
112
+ /** Pod 셸이 실제로 표지를 찍는 단계만 — deps 는 si-task Job 에 설치 단계가 없어 읽지도 않는다(늘 missing). */
113
+ const SI_TASK_MARKED_STAGES = ['clone', 'mirror', 'run'] as const;
114
+ export type SiTaskMarkedStage = typeof SI_TASK_MARKED_STAGES[number];
115
+
116
+ /** Pod 셸 한 줄 — 단계 경계에서 Pod 시계(초)를 찍는다. 실패해도 셸을 끊지 않는다(`|| true`). */
117
+ export function siTaskPodStageShell(stage: SiTaskMarkedStage, edge: 'start' | 'end'): string {
118
+ return `printf 'ELANOUS_POD_STAGE ${stage} ${edge} %s\\n' "$(date +%s)" || true`;
119
+ }
120
+ const SI_TASK_STAGE_LINE = /^ELANOUS_POD_STAGE (clone|mirror|run) (start|end) (\d{1,12})\r?$/gm;
121
+
122
+ /**
123
+ * Job 로그에서 단계 표지를 읽는다 → `{ 'clone-start': 초, 'clone-end': 초, … }` (gate 의 podStages 와 같은 꼴).
124
+ * 같은 표지가 두 번 나오면 «첫» 값 — 셸 표지는 모두 자식 출력(`cat /tmp/si.out`)보다 앞에 찍히므로, 자식이 같은 모양의 줄을
125
+ * 내도 셸 값을 덮지 못한다(리뷰 should-fix). 모르는 단계·꼴은 버린다(지어내지 않는다).
126
+ */
127
+ export function parseSiTaskPodStages(logs: string | undefined): Record<string, number> {
128
+ const out: Record<string, number> = {};
129
+ if (!logs) return out;
130
+ for (const match of logs.matchAll(SI_TASK_STAGE_LINE)) {
131
+ const key = `${match[1]}-${match[2]}`;
132
+ if (!(key in out)) out[key] = Number(match[3]);
133
+ }
134
+ return out;
135
+ }
136
+
137
+ /**
138
+ * si-task Pod — Pod 셸이 찍은 단계 표지(`parseSiTaskPodStages`)로 clone · mirror · deps · run 을 채운다(pod 시계).
139
+ * - clone = 소스 받기(`podSourceScript`) 시작 → 그 스크립트 전체가 끝난 뒤(`cd repo` 포함 · 어느 줄에서든 exit 하면 끝이 없다 → missing)
140
+ * - mirror = 호스트 미러 `clone --shared` 시도 → 성공(미러를 건너뛰면 끝이 없다 → missing)
141
+ * - deps = si-task Job 셸엔 의존성 설치 단계가 없다 → 표지를 읽지 않는다(`deps` 표지 줄이 있어도 늘 missing)
142
+ * - run = 본체(`self implement` · `harness ask`/`say`) 띄움 → `wait` 가 돌아옴(SIGTERM 이면 끝이 없다 → missing)
143
+ * - create · schedule · delete = 호스트가 잴 값이 없다 → missing.
144
+ * 호스트가 지켜본 구간은 단계가 아닌 `watch`(host 시계)로 따로 싣는다 — 단계로 지어내지 않는다.
145
+ */
146
+ export function siTaskPodLifecycleEvent(input: {
147
+ runId?: string | null;
148
+ podJob: string;
149
+ host?: string | null;
150
+ watchStart?: number;
151
+ watchEnd?: number;
152
+ podStages?: Readonly<Record<string, number>>;
153
+ outcome: string;
154
+ }): PodLifecycleEvent {
155
+ const pod = input.podStages ?? {};
156
+ const measured: Measured = {};
157
+ for (const name of SI_TASK_MARKED_STAGES) measured[name] = { start: podSecondsMs(pod[`${name}-start`]), end: podSecondsMs(pod[`${name}-end`]), clock: 'pod' };
158
+ const { stages, missing } = splitPodLifecycleStages(measured, SI_TASK_POD_LIFECYCLE_STAGES);
159
+ const watched = input.watchStart !== undefined && input.watchEnd !== undefined && Number.isFinite(input.watchStart) && Number.isFinite(input.watchEnd) && input.watchEnd >= input.watchStart;
160
+ return { runId: input.runId ?? null, podJob: input.podJob, host: input.host ?? null, kind: 'si-task', stages, outcome: input.outcome, missing,
161
+ ...(watched ? { watch: { start: input.watchStart, end: input.watchEnd, clock: 'host' as const } } : {}) };
162
+ }
163
+
164
+ export type PodLifecycleLog = (category: string, event: string, data: Record<string, unknown>) => void;
165
+
166
+ /** 한 줄 낸다 — 관측은 fail-soft(판정 경로를 절대 깨지 않는다). */
167
+ export function emitPodLifecycle(event: PodLifecycleEvent, log: PodLifecycleLog = (category, name, data) => debug.log(category, name, data)): void {
168
+ try { log('pod.lifecycle', 'stages', event); } catch { /* observation is fail-soft */ }
169
+ }
@@ -72,5 +72,32 @@ export function seedMirrorOnce(o: { host: string; commit: string; cwd: string; p
72
72
  return pending;
73
73
  }
74
74
 
75
+ /**
76
+ * SI-SEED-DEFAULT — a default-source si-task Job (main HEAD, or `--base <branch>`) has no pinned sha, so the host resolves
77
+ * the branch tip once (`git ls-remote`, bounded) and makes sure the seeding tree has that commit (a bounded fetch of that
78
+ * one sha when it does not — objects only, no ref is moved). Never throws: a miss returns `reason` and the caller skips
79
+ * the seed (best effort — the Job keeps its GitHub path).
80
+ */
81
+ export async function resolveDefaultSourceHead(o: { repoUrl: string; base?: string; cwd: string; git?: Push; limitMs?: number }): Promise<{ sha?: string; reason?: string }> {
82
+ const git = o.git ?? gitWithin;
83
+ const limitMs = o.limitMs ?? 30_000;
84
+ const base = o.base || 'main';
85
+ if (base.startsWith('-') || !/^[\w./-]+$/.test(base) || o.repoUrl.startsWith('-')) return { reason: 'invalid-ref' };
86
+ try {
87
+ const listed = await git(['ls-remote', '--', o.repoUrl, `refs/heads/${base}`], o.cwd, limitMs);
88
+ if (listed.rc !== 0) return { reason: 'ls-remote-failed' };
89
+ // Only the exact ref asked for — never the first `refs/heads/*` line (a branch named `a/refs/heads/main` must not pass).
90
+ const want = `refs/heads/${base}`;
91
+ const sha = listed.output.split(/\r?\n/).map((line) => line.split('\t')).find(([hash, ref]) => ref === want && /^[0-9a-f]{40}$/.test(hash ?? ''))?.[0];
92
+ if (!sha) return { reason: 'ls-remote-no-sha' };
93
+ if ((await git(['cat-file', '-e', `${sha}^{commit}`], o.cwd, limitMs)).rc === 0) return { sha };
94
+ const fetched = await git(['fetch', '--quiet', '--no-write-fetch-head', '--', o.repoUrl, sha], o.cwd, GATE_MIRROR_SEED_LIMIT_MS);
95
+ if (fetched.rc !== 0) return { reason: 'commit-fetch-failed' };
96
+ return (await git(['cat-file', '-e', `${sha}^{commit}`], o.cwd, limitMs)).rc === 0 ? { sha } : { reason: 'commit-not-local' };
97
+ } catch {
98
+ return { reason: 'resolve-threw' };
99
+ }
100
+ }
101
+
75
102
  /** Test seam — forget the per-process seed memo. */
76
103
  export function resetMirrorSeedMemo(): void { seeded.clear(); }
@@ -0,0 +1,33 @@
1
+ // TA-POD-WARMBOOT-PARITY (10-15) — the Pod result marker (GATE-POD-RESULT-MARKER · #27815) for Jobs that do not go
2
+ // through runPodCommand (the si-task Job in self-implement-pod.ts).
3
+ //
4
+ // S: runPodCommand prints `\n<marker> <launch> rc=<n>\n` as the child's last line and ends its wait on it when the Job
5
+ // condition lags (k3d). The si-task Job builds its own manifest and waits only on the condition.
6
+ // A: reuse the marker format and split from pod-command-job.ts (no second format), and keep the one-poll judgement
7
+ // («no condition yet → read the tail → this launch's marker? → rc») as a pure function here.
8
+ // ⚠️ pod-command-job.ts imports self-implement-pod.ts statically, so self-implement-pod.ts loads this module with a
9
+ // dynamic import only (a static import back would hit the TDZ of the cycle).
10
+ // Later (after T1): runPodCommand's own loop can call podResultMarkerRc too.
11
+ import { podResultMarker, splitPodResultMarker } from './pod-command-job.js';
12
+
13
+
14
+ /** The child's final shell line: prints this launch's marker with the child rc (same bytes runPodCommand's child prints).
15
+ * null when the token is not a plain id (`[A-Za-z0-9_.:-]+`) — the Job then runs without a marker (condition path only). */
16
+ export function podResultMarkerShellLine(token: string): string | null {
17
+ if (!/^[\w.:-]+$/.test(token)) return null;
18
+ return `printf '\\n%s rc=%s\\n' '${podResultMarker(token)}' "$rc"`;
19
+ }
20
+
21
+ /**
22
+ * One poll, condition not there yet: the `kubectl logs … --tail=2` result → this launch's rc, or null.
23
+ * A failed read (Pod pending · init container), another launch's marker, or a bare marker word is null (keep waiting).
24
+ */
25
+ export function podResultMarkerRc(tail: { status: number | null; stdout: string }, token: string): number | null {
26
+ if (tail.status !== 0) return null;
27
+ return splitPodResultMarker(tail.stdout.startsWith('\n') ? tail.stdout : `\n${tail.stdout}`, token).rc;
28
+ }
29
+
30
+ /** Remove this launch's marker tail from the child logs before they are parsed (other bytes unchanged). */
31
+ export function stripPodResultMarker(logs: string, token: string): string {
32
+ return splitPodResultMarker(logs, token).logs;
33
+ }
@@ -0,0 +1,63 @@
1
+ /** Files and caches that survive between requests on a reusable Pod worker. */
2
+ export const POD_REUSE_CLEAN_EXCLUSIONS = [
3
+ { name: 'node_modules', pattern: 'node_modules' },
4
+ { name: 'apps/pwa/node_modules', pattern: 'apps/pwa/node_modules' },
5
+ // Bun's install cache and remote gate cache live outside the worker checkout.
6
+ { name: 'bun install cache', pattern: null },
7
+ { name: 'TypeScript build info', pattern: '*.tsbuildinfo' },
8
+ { name: 'remote gate TypeScript cache', pattern: null },
9
+ ] as const;
10
+
11
+ export type PodReuseCleanupOutcome = 'reuse' | 'discard';
12
+
13
+ export interface PodReuseCleanupDecisionInput {
14
+ base: string;
15
+ resetExitCode: number | null;
16
+ cleanExitCode: number | null;
17
+ statusExitCode: number | null;
18
+ statusPorcelain: string;
19
+ remainingPaths?: readonly string[];
20
+ }
21
+
22
+ export interface PodReuseCleanupDecision {
23
+ resetArgs: readonly string[];
24
+ cleanArgs: readonly string[];
25
+ statusArgs: readonly string[];
26
+ preserved: readonly string[];
27
+ outcome: PodReuseCleanupOutcome;
28
+ reason?: 'invalid-base' | 'reset-failed' | 'clean-failed' | 'status-failed' | 'worktree-not-clean';
29
+ remainingPaths: readonly string[];
30
+ }
31
+
32
+ /**
33
+ * Pure request-boundary decision: return argv for the caller to execute and
34
+ * decide whether the worker can be reused from the command results.
35
+ * Execution/wiring and lifecycle event emission intentionally live in a later slice.
36
+ */
37
+ export function decidePodReuseCleanup(input: PodReuseCleanupDecisionInput): PodReuseCleanupDecision {
38
+ const remainingPaths = (input.remainingPaths ?? []).slice(0, 5);
39
+ const resetArgs = ['reset', '--hard', input.base] as const;
40
+ const cleanArgs = [
41
+ 'clean', '-ffdx',
42
+ ...POD_REUSE_CLEAN_EXCLUSIONS.flatMap(({ pattern }) => pattern ? ['-e', pattern] : []),
43
+ ] as const;
44
+ const statusArgs = ['status', '--porcelain'] as const;
45
+ const preserved = POD_REUSE_CLEAN_EXCLUSIONS.map(({ name }) => name);
46
+
47
+ let reason: PodReuseCleanupDecision['reason'];
48
+ if (!/^[0-9a-f]{40}$/i.test(input.base)) reason = 'invalid-base';
49
+ else if (input.resetExitCode !== 0) reason = 'reset-failed';
50
+ else if (input.cleanExitCode !== 0) reason = 'clean-failed';
51
+ else if (input.statusExitCode !== 0) reason = 'status-failed';
52
+ else if (input.statusPorcelain.trim() !== '') reason = 'worktree-not-clean';
53
+
54
+ return {
55
+ resetArgs,
56
+ cleanArgs,
57
+ statusArgs,
58
+ preserved,
59
+ outcome: reason ? 'discard' : 'reuse',
60
+ ...(reason ? { reason } : {}),
61
+ remainingPaths,
62
+ };
63
+ }
@@ -2,6 +2,7 @@
2
2
  * sha·PR·커밋은 형식 검사 전에 스크립트에 넣지 않는다(셸 주입 차단). */
3
3
 
4
4
  import { execFileSync } from 'node:child_process';
5
+ import { siTaskPodStageShell } from './pod-lifecycle-stages.js';
5
6
 
6
7
  export type PodSource =
7
8
  | { kind: 'default'; base?: string }
@@ -52,17 +53,30 @@ function assertRemoteBase(repoUrl: string, base: string): void {
52
53
  }
53
54
  }
54
55
 
55
- /** Job 스크립트의 원천 단계. 성공 갈래는 `ELANOUS_POD_SOURCE <kind> <HEAD>` 로 끝난다. */
56
- export function podSourceScript(source: PodSource, repoUrl: string, mirrorPath = '/host-mirror'): string {
56
+ /** Job 스크립트의 원천 단계. 성공 갈래는 `ELANOUS_POD_SOURCE <kind> <HEAD>` 로 끝난다.
57
+ * `stageMarkers` — si-task Job 만: 호스트 미러 `clone --shared` 시도의 시작·성공을 `ELANOUS_POD_STAGE mirror start|end` 로 찍는다
58
+ * (건너뛰거나 실패하면 끝 표지가 없다 → 호스트가 missing 으로 읽는다). 기본은 꺼짐 — gate·pod-command Job 의 바이트는 그대로다. */
59
+ export function podSourceScript(source: PodSource, repoUrl: string, mirrorPath = '/host-mirror', options: { stageMarkers?: boolean } = {}): string {
60
+ const mark = (edge: 'start' | 'end'): string[] => options.stageMarkers ? [siTaskPodStageShell('mirror', edge)] : [];
57
61
  const quotedUrl = `'${repoUrl.replace(/'/g, `'\\''`)}'`;
58
62
  const quotedMirror = `'${mirrorPath.replace(/'/g, `'\\''`)}'`;
59
63
  const clone = `git clone -q --depth 50 ${quotedUrl} repo`;
60
64
  const start = [
61
65
  'if [ -e repo ]; then exit 5; fi',
62
- `if [ -d ${quotedMirror} ] && git -C ${quotedMirror} rev-parse --verify HEAD >/dev/null 2>&1 && git clone -q --shared -- ${quotedMirror} repo && git -C repo rev-parse --verify HEAD >/dev/null 2>&1; then`,
66
+ // TA-POD-WARMBOOT-PARITY (10-16): the mirror fallback used to be silent — 157 HARD gate Pods all said
67
+ // `VIA github` and nothing said why (the Job had no /host-mirror mount). Each failed step now names itself.
68
+ 'mirror_skip=',
69
+ ...mark('start'),
70
+ `if ! [ -d ${quotedMirror} ]; then mirror_skip=no-mount`,
71
+ `elif ! git -C ${quotedMirror} rev-parse --verify HEAD >/dev/null 2>&1; then mirror_skip=rev-parse-failed`,
72
+ `elif ! git clone -q --shared -- ${quotedMirror} repo || ! git -C repo rev-parse --verify HEAD >/dev/null 2>&1; then mirror_skip=clone-failed`,
73
+ 'fi',
74
+ 'if [ -z "$mirror_skip" ]; then',
75
+ ...mark('end').map((line) => ` ${line}`),
63
76
  ` git -C repo remote set-url origin ${quotedUrl} || exit 5`,
64
77
  ' source_via=mirror',
65
78
  'else',
79
+ " printf 'ELANOUS_POD_SOURCE_MIRROR_SKIP %s\\n' \"$mirror_skip\"",
66
80
  ' if [ -e repo ]; then rm -r -- repo || exit 5; fi',
67
81
  ` ${clone} || exit 5`,
68
82
  ' source_via=github',
@@ -143,7 +157,9 @@ export function podSourceScript(source: PodSource, repoUrl: string, mirrorPath =
143
157
  ' exit 3',
144
158
  'fi',
145
159
  'if [ -e repo ]; then exit 5; fi',
160
+ ...mark('start'),
146
161
  `if [ -d ${quotedMirror} ] && git -C ${quotedMirror} rev-parse --verify HEAD >/dev/null 2>&1 && git clone -q --shared -- ${quotedMirror} repo && git -C repo rev-parse --verify HEAD >/dev/null 2>&1; then`,
162
+ ...mark('end').map((line) => ` ${line}`),
147
163
  ` git -C repo remote set-url origin ${quotedUrl} || exit 5`,
148
164
  ' if git -C repo bundle verify /tmp/source.bundle >/dev/null 2>&1; then',
149
165
  ' git -C repo fetch /tmp/source.bundle "refs/elanous/pod-source/*:refs/remotes/bundle/*" || exit 5',
@@ -0,0 +1,141 @@
1
+ /**
2
+ * warm Pod 재사용 ⑤ — 소비자별 «실전 사용 가능?» 손잡이 (PLAN-warm-reuse-tonight-2026-10-15 §5).
3
+ *
4
+ * 관측: 판정마다 `debug.log('pod.warm', 'switch-decision', …)` 한 줄(소비자·live·reason·표본 수).
5
+ * 판단: config flag(`pod.warmReuse.{gateRecheck,land,ta}` · 기본 false) ⊕ 증거
6
+ * (lease 유효 · 카나리아 통과 · 같은 head/main/lock · 최근 mismatch 0 · 연속 일치 표본 ≥ minMatchSamples).
7
+ * 수복: live=false 면 소비자는 기존 fresh Job 경로를 그대로 탄다 — reason 이 «무엇이 모자랐나»를 넘긴다.
8
+ *
9
+ * ⛔ 이 모듈은 판정만 한다. 소비자 결선(pod-command-job · gate-node · self-implement-pod)은 각 소비자 PR 몫이다.
10
+ * ⛔ 못 잰 증거(undefined)는 «통과»가 아니라 «off» 다 — 모르면 켜지 않는다.
11
+ * 순서: gate-recheck → land → ta. 앞 소비자가 «자기 증거로» live 가 아니면 뒤 소비자도 live 가 아니다
12
+ * (증거는 소비자별 — 뒤 소비자의 증거로 앞 소비자를 대신 판정하지 않는다 · 앞 증거가 없으면 off).
13
+ */
14
+ import { debug } from '../../debug/log.js';
15
+
16
+ export type WarmReuseConsumer = 'gate-recheck' | 'land' | 'ta';
17
+
18
+ /** 켜는 순서 — 뒤 소비자는 바로 앞 소비자가 live 일 때만 live 가 될 수 있다. */
19
+ export const WARM_REUSE_CONSUMER_ORDER: readonly WarmReuseConsumer[] = ['gate-recheck', 'land', 'ta'];
20
+
21
+ /** 연속 일치 표본 기본 하한(보수적). */
22
+ export const DEFAULT_WARM_MIN_MATCH_SAMPLES = 3;
23
+
24
+ /** `pod.warmReuse` config 절 — 타입이 어긋난 값은 off/기본값으로 읽는다. */
25
+ export interface WarmReuseSwitchConfig {
26
+ gateRecheck?: boolean;
27
+ land?: boolean;
28
+ ta?: boolean;
29
+ minMatchSamples?: number;
30
+ }
31
+
32
+ /** warm 워커 신원 — 기대값(fresh Job 쪽)과 워커 쪽을 같은 칸끼리 대조한다. */
33
+ export interface WarmReuseIdentity {
34
+ head?: string;
35
+ main?: string;
36
+ lock?: string;
37
+ }
38
+
39
+ /** 판정 증거. 칸이 비면(undefined) «못 쟀다» = off. */
40
+ export interface WarmReuseEvidence {
41
+ /** 같은 파일 fresh Job 대비 연속 일치(rc ⊕ JUnit 실패 ID) 표본 수. */
42
+ consecutiveMatchSamples?: number;
43
+ /** 최근 창의 warm↔fresh mismatch 수 — 0 이어야 live. */
44
+ recentMismatchCount?: number;
45
+ /** Nexus `pod-worker-lease` 가 현재 generation 으로 유효한가. */
46
+ leaseValid?: boolean;
47
+ /** 정비 뒤 카나리아 통과. */
48
+ canaryPassed?: boolean;
49
+ expected?: WarmReuseIdentity;
50
+ worker?: WarmReuseIdentity;
51
+ }
52
+
53
+ /** 소비자별 증거 — 판정 대상 ⊕ 그 앞 소비자들의 증거가 모두 있어야 live 가 될 수 있다. */
54
+ export type WarmReuseEvidenceByConsumer = Partial<Record<WarmReuseConsumer, WarmReuseEvidence>>;
55
+
56
+ export interface WarmReuseDecision {
57
+ consumer: WarmReuseConsumer;
58
+ live: boolean;
59
+ /** `live` 이거나 첫 번째로 걸린 이유(예 `flag-off` · `predecessor-off:land` · `samples-short`). */
60
+ reason: string;
61
+ }
62
+
63
+ const FLAG_KEY: Record<WarmReuseConsumer, keyof Pick<WarmReuseSwitchConfig, 'gateRecheck' | 'land' | 'ta'>> = {
64
+ 'gate-recheck': 'gateRecheck',
65
+ land: 'land',
66
+ ta: 'ta',
67
+ };
68
+
69
+ const IDENTITY_FIELDS = ['head', 'main', 'lock'] as const;
70
+
71
+ function isRecord(value: unknown): value is Record<string, unknown> {
72
+ return !!value && typeof value === 'object' && !Array.isArray(value);
73
+ }
74
+
75
+ /** minMatchSamples — 양의 안전 정수만, 아니면 기본값. */
76
+ export function resolveWarmMinMatchSamples(config: unknown): number {
77
+ const raw = isRecord(config) ? config.minMatchSamples : undefined;
78
+ return typeof raw === 'number' && Number.isSafeInteger(raw) && raw > 0 ? raw : DEFAULT_WARM_MIN_MATCH_SAMPLES;
79
+ }
80
+
81
+ /** flag — 문자 그대로 `true` 만 켠다. */
82
+ export function warmReuseFlagOn(config: unknown, consumer: WarmReuseConsumer): boolean {
83
+ return isRecord(config) && config[FLAG_KEY[consumer]] === true;
84
+ }
85
+
86
+ function evidenceReason(evidence: WarmReuseEvidence | undefined, minSamples: number): string | null {
87
+ if (!evidence) return 'evidence-unmeasured';
88
+ if (evidence.leaseValid !== true) return evidence.leaseValid === false ? 'lease-invalid' : 'lease-unmeasured';
89
+ if (evidence.canaryPassed !== true) return evidence.canaryPassed === false ? 'canary-failed' : 'canary-unmeasured';
90
+ for (const field of IDENTITY_FIELDS) {
91
+ const want = evidence.expected?.[field];
92
+ const got = evidence.worker?.[field];
93
+ if (typeof want !== 'string' || !want || typeof got !== 'string' || !got) return `identity-unmeasured:${field}`;
94
+ // 원값 그대로 정확히 비교한다 — 앞뒤 공백 등 형식이 어긋난 값은 고쳐 읽지 않고 off.
95
+ if (want !== want.trim() || got !== got.trim()) return `identity-malformed:${field}`;
96
+ if (want !== got) return `identity-mismatch:${field}`;
97
+ }
98
+ const mismatches = evidence.recentMismatchCount;
99
+ if (typeof mismatches !== 'number' || !Number.isSafeInteger(mismatches) || mismatches < 0) return 'mismatch-unmeasured';
100
+ if (mismatches > 0) return 'recent-mismatch';
101
+ const samples = evidence.consecutiveMatchSamples;
102
+ if (typeof samples !== 'number' || !Number.isSafeInteger(samples) || samples < 0) return 'samples-unmeasured';
103
+ if (samples < minSamples) return 'samples-short';
104
+ return null;
105
+ }
106
+
107
+ function decide(consumer: WarmReuseConsumer, config: unknown, evidence: WarmReuseEvidenceByConsumer | undefined): string {
108
+ if (!warmReuseFlagOn(config, consumer)) return 'flag-off';
109
+ const index = WARM_REUSE_CONSUMER_ORDER.indexOf(consumer);
110
+ if (index > 0) {
111
+ const predecessor = WARM_REUSE_CONSUMER_ORDER[index - 1]!;
112
+ if (decide(predecessor, config, evidence) !== 'live') return `predecessor-off:${predecessor}`;
113
+ }
114
+ const own = isRecord(evidence) ? evidence[consumer] : undefined;
115
+ return evidenceReason(isRecord(own) ? own : undefined, resolveWarmMinMatchSamples(config)) ?? 'live';
116
+ }
117
+
118
+ /**
119
+ * 소비자 하나가 지금 warm 워커를 «실전»으로 써도 되나.
120
+ * `config` 는 `getUserConfig().pod?.warmReuse` 를 그대로 넘긴다(없으면 전부 off).
121
+ * `evidence` 는 소비자별 — 대상 소비자 ⊕ 그 앞 소비자들의 칸이 모두 있어야 한다(앞 칸이 비면 `predecessor-off:<앞>`).
122
+ */
123
+ export function decideWarmReuse(
124
+ consumer: WarmReuseConsumer,
125
+ config: WarmReuseSwitchConfig | undefined,
126
+ evidence: WarmReuseEvidenceByConsumer | undefined,
127
+ ): WarmReuseDecision {
128
+ const reason = decide(consumer, config, evidence);
129
+ const live = reason === 'live';
130
+ const own = isRecord(evidence) && isRecord(evidence[consumer]) ? evidence[consumer] : undefined;
131
+ debug.log('pod.warm', 'switch-decision', {
132
+ consumer,
133
+ live,
134
+ reason,
135
+ flag: warmReuseFlagOn(config, consumer),
136
+ minMatchSamples: resolveWarmMinMatchSamples(config),
137
+ samples: own?.consecutiveMatchSamples ?? null,
138
+ recentMismatchCount: own?.recentMismatchCount ?? null,
139
+ });
140
+ return { consumer, live, reason };
141
+ }