elanous 0.2.36 → 0.2.38

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 (741) 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/{uC-N0emedeNOFuaBQFv-I → RrjXTwz-urRAbChP1bX02}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{9250-072df22eaadaa6b5.js → 1026-44010f405704050c.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/1118.a8a74ea29aac3e46.js +27 -0
  6. package/apps/pwa/out/_next/static/chunks/1204-2aa830479adf80f4.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1341-43b7d2dc77d45c9d.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1440-c3ea8882badfd9bf.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/{4304.e9b2512834a9e23e.js → 1632.7d87f998e714bede.js} +1 -1
  10. package/apps/pwa/out/_next/static/chunks/1698-9c11cbf97f13717e.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/1770-3b1fc92253ef9f2f.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/1923-5c1695332240246a.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/1938.0e6c60ee91c4d7f5.js +148 -0
  14. package/apps/pwa/out/_next/static/chunks/{7784.1f0ac4a16d190cf4.js → 2104.70e086d5174a4736.js} +1 -1
  15. package/apps/pwa/out/_next/static/chunks/213-20675f20673ba22a.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/{6566.926a1d1b7c911442.js → 214.186a430da28338f1.js} +1 -1
  17. package/apps/pwa/out/_next/static/chunks/2277.3bd4c0ac7f1b5996.js +93 -0
  18. package/apps/pwa/out/_next/static/chunks/240.e09f433f71c78ff4.js +104 -0
  19. package/apps/pwa/out/_next/static/chunks/{5711.957b095705c12560.js → 2415.738b777642a02233.js} +1 -1
  20. package/apps/pwa/out/_next/static/chunks/2417.02151393605a7cd0.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/2463.cd2641a76daaa9ff.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/2576.b9e9c629c4b84c52.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/265-c021de53f0865bd9.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/2998-b1cc13956cd6a6e7.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/2e4782d3.ba2f0ab2b63a53e7.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/{4286.b70c4520306817e8.js → 3086.fb14b60ce98b53d3.js} +1 -1
  27. package/apps/pwa/out/_next/static/chunks/{3148-98efd4869d660a87.js → 3124-561f7f037943cde2.js} +1 -1
  28. package/apps/pwa/out/_next/static/chunks/3130.626b54382b4dc379.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/3221-1bb3f99a55088613.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/3280.24edb1ffd03a7f3f.js +24 -0
  31. package/apps/pwa/out/_next/static/chunks/3288-eb3c22a1cbf2ae68.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/{3727.7949ccaacaa71242.js → 3291.f6c1005387d514bd.js} +2 -2
  33. package/apps/pwa/out/_next/static/chunks/33.ef99e8374f96c6c4.js +36 -0
  34. package/apps/pwa/out/_next/static/chunks/3341-f09b9efbd7a82f70.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/3389.012c70767a872f9b.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/3410-d7c0f14b329d6fda.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/3421.c87d6313a7d670f7.js +287 -0
  38. package/apps/pwa/out/_next/static/chunks/3422-83a3953f9bd67e26.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/{8904.7d51af2dc033965c.js → 3528.e4b8ab70d339f965.js} +2 -2
  40. package/apps/pwa/out/_next/static/chunks/3587-002e24e368c515a9.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/3644.f12a7d821ab940af.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/3660.d7516588e2d875c3.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/3695.b82e2610f4cd4ea9.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/3858.ca1f5bc0f577eac9.js +94 -0
  45. package/apps/pwa/out/_next/static/chunks/3959-e6ac4a139d9d4bcc.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/3f0de3de-2340c532c00395db.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/4032.53f62176514ec0ea.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/{3031-85328a92fcce4da6.js → 4199-f7c6eb40cd0322f8.js} +1 -1
  49. package/apps/pwa/out/_next/static/chunks/4204-ef8484cbecd5d62f.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/4259-163129ca79913c4b.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/428-eaa77df3d0c85969.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/4432-97e400ffa1e161f5.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/449-aa427cf2cbb75f50.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/4491-39a0fd32bbaf3840.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/4588.b7cf06d382953979.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/4690.70ec8060825366ab.js +32 -0
  57. package/apps/pwa/out/_next/static/chunks/4804.6012b5cd33c22792.js +90 -0
  58. package/apps/pwa/out/_next/static/chunks/4881.e4d22d730e3b9df5.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/4937.ce2e340dc29cad3e.js +79 -0
  60. package/apps/pwa/out/_next/static/chunks/5-bb1c08d84bd71df3.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/5005.de98df23b37cdf3c.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/5035.dc64a89cad5573cc.js +74 -0
  63. package/apps/pwa/out/_next/static/chunks/5041-5aada92ca215ffdd.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/{528.38ce20ef8aa251ff.js → 5203.51e5d8b5f1dcfe53.js} +16 -16
  65. package/apps/pwa/out/_next/static/chunks/5207.1a224035455790fd.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/5255-1b60d50eabe9f057.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/5269.d0436ee6d773382a.js +286 -0
  68. package/apps/pwa/out/_next/static/chunks/5300.677c97e0393742e6.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/{2683.c9773a0c589b031b.js → 5307.d7328020227880c2.js} +4 -4
  70. package/apps/pwa/out/_next/static/chunks/5309.3367c4d50f52b489.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/552-689827d49780fa00.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/5539.34cc9d60622cb69b.js +8 -0
  73. package/apps/pwa/out/_next/static/chunks/5568.218dea0c00ca2ec7.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/{552.b7d061ad91eeac51.js → 5638.107314dc479e2f37.js} +2 -2
  75. package/apps/pwa/out/_next/static/chunks/5645.b7e3b055455a04ea.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/5662.18cc0f30353cbd96.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/5671.da24fc9dc5e98325.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/5b141171.324c40fd49cb6821.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/6142.913b44e260a3f925.js +64 -0
  80. package/apps/pwa/out/_next/static/chunks/6175-d4621f6a10b0cfab.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/6290.3eb53a20746b72ce.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/6415-9307f3eafa90837f.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/{6925.562d532110879886.js → 6493.27ad77de7aac7966.js} +1 -1
  84. package/apps/pwa/out/_next/static/chunks/6579-3de35d00aacd71b5.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/680-2f324c4e039a5b67.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/6845-331b3c7481da3ba3.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/7000.28157393caaf5343.js +62 -0
  88. package/apps/pwa/out/_next/static/chunks/{275-02ea4c2a33980422.js → 7161-8f22db3ec5ed005e.js} +3 -3
  89. package/apps/pwa/out/_next/static/chunks/7311-60a7ea80d23c7521.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/7401.525d0a12f7d02c06.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/7438.c2cf72b66dca6aa4.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/{3136.30aaaa5fa03662a3.js → 7488.966b116789f3d41a.js} +1 -1
  93. package/apps/pwa/out/_next/static/chunks/{4116.a84a92a2492c080e.js → 7524.ce973525d30b3d52.js} +1 -1
  94. package/apps/pwa/out/_next/static/chunks/7573.2376e13c9c21f1f8.js +77 -0
  95. package/apps/pwa/out/_next/static/chunks/7577.ea6e9ac72e035520.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/{3324.a09703033abcbd2f.js → 7628.6b16863ac3972333.js} +1 -1
  97. package/apps/pwa/out/_next/static/chunks/7657.8d1fb3d2f7ebe273.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/7903-1435d5d2f0dd7807.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/{3410.7f9af5ab77cbe65f.js → 7954.4f7a8832663ee979.js} +1 -1
  100. package/apps/pwa/out/_next/static/chunks/7978-30a91192c5dbe3c2.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/{771.c3cdb6c1f7f47c02.js → 8019.21ca554a821dad01.js} +16 -16
  102. package/apps/pwa/out/_next/static/chunks/8025-8795821525cf1a07.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/8191.3674a00f0b565c23.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8217-604955d8885c5ef2.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/8298-eed6f5f2585a63f4.js +1 -0
  106. package/apps/pwa/out/_next/static/chunks/{4615.8525eb0add35eba6.js → 8487.291666583bebe640.js} +1 -1
  107. package/apps/pwa/out/_next/static/chunks/8509.3a54d4e61893d7cb.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/8758.ac421f726a189125.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/{6943.c6b24124853a47b2.js → 8879.ac06755bcf3e44a1.js} +1 -1
  110. package/apps/pwa/out/_next/static/chunks/891.d6c723a51a067b36.js +149 -0
  111. package/apps/pwa/out/_next/static/chunks/{575.d929793a53f27c4b.js → 8911.bf5e712acb25fb6c.js} +1 -1
  112. package/apps/pwa/out/_next/static/chunks/{9f31e478.fefcbcd3c388ebc5.js → 8994d93e.33ba491f95345fc2.js} +1 -1
  113. package/apps/pwa/out/_next/static/chunks/{da5ecc5c-b005ad8cac737d57.js → 89d6c04f-6b8ab341748e468a.js} +1 -1
  114. package/apps/pwa/out/_next/static/chunks/{599.c39c571b6733f2a2.js → 9223.cbcc88e0e9950426.js} +2 -2
  115. package/apps/pwa/out/_next/static/chunks/9299.24e2a7d9b6d1f91b.js +226 -0
  116. package/apps/pwa/out/_next/static/chunks/9317.6a9869039c231bb3.js +24 -0
  117. package/apps/pwa/out/_next/static/chunks/9383.e4755f275b3c11ff.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/{8037.e254fd797aa224d1.js → 9581.da63ddf41acfad46.js} +1 -1
  119. package/apps/pwa/out/_next/static/chunks/9715.8c5529a0a5c41a36.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/9817.3009d65f0889ef1f.js +2 -0
  121. package/apps/pwa/out/_next/static/chunks/{ceba0382.650712b56b85b582.js → 9fe1b809.738da85c8b17e3fb.js} +1 -1
  122. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-76873dd80da4f715.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/app/approvals/page-ed32b6ec5dea8f23.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-fdd40e15babf3d25.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/board/page-384efc78e85e400b.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/botlab/page-866aac5ff226a7f3.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/bots/page-d91809c5aeed319a.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/ceo/page-128f3522f89a81ab.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/chat/page-595961abf3b43ad1.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/consult/page-f41dccb76d8c0263.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/control/page-a3205c2e28b7d0d9.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/decisions/page-c39a80277814a3f4.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/design-check/page-1ab3f98194513195.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/editor/page-a5246f7e254d77d0.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/exec/page-0f92287bdb81217d.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/field/page-5a37131ff63793ef.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/healthz/route-76873dd80da4f715.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/inside/page-ac4bd14bb2348bfd.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/intake/page-c348a3959c5b5fc6.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/layout-2addd81c0d3e2906.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/live/page-da6b765f58a3eefb.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/live-run/page-0661e40f8780c32c.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/loops/page-b89af1b1c8e4875b.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/market/page-da8aeb82ba751cdb.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-cc71317b239439db.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/morning/page-bde7fc1d64ffe369.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/not-found-e2797214f713fa58.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/observatory/page-2a35bcda70defaf6.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-3af03deedde45032.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-d1d1210f99f620ae.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-46d1b5500885861d.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/outputs/{page-e7abc34a48a6f71e.js → page-291ab19fe2acb173.js} +1 -1
  153. package/apps/pwa/out/_next/static/chunks/app/page-7f569e533ddeecba.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/reflection/page-990129c9fee46d8f.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-7737f785ebb1cdaf.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/sessions/page-9f3c73a4f7ffd2f8.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-03be7fb347f01058.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/settings/page-96b0280625e4df2b.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-56855545049eb56d.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/setup/layout-76873dd80da4f715.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/page-79a909c9981f0338.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/share/page-a9d9a3e66459c1e0.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/showroom/page-c383cbf2013f7069.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f57c6a2e54dbdca4.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/term/{page-c15958835534404b.js → page-6b140f77593576eb.js} +1 -1
  166. package/apps/pwa/out/_next/static/chunks/app/today/page-6b767ae2d9d0adfc.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/trace/page-116d6198d5da0e3d.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/vault/page-b9c9b778c7501370.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-a6c78392ab8f6335.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/workflows/page-455183deb4ed0cfe.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workspace/page-6a63e99526bd63d0.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31c22caafe788c08.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/{3a3d9966.cc4d4585a2708e7c.js → b7459770.6539b11860eb8a9c.js} +7 -7
  174. package/apps/pwa/out/_next/static/chunks/{053a429e.9a63436527cc751c.js → b7478fef.5917716286213fcc.js} +3 -3
  175. package/apps/pwa/out/_next/static/chunks/bb6987f9.3647e1a23b0e3217.js +122 -0
  176. package/apps/pwa/out/_next/static/chunks/{2812ca6a.3b9f4a7a08885744.js → de8b930d.d94c0f42acf807fc.js} +1 -1
  177. package/apps/pwa/out/_next/static/chunks/{873d6c12.09bd499f57171543.js → eaad0cba.771200940c529df7.js} +1 -1
  178. package/apps/pwa/out/_next/static/chunks/framework-4b12a946f352cee2.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-941155250ed2644b.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-app-f8b6765abed6d149.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_app-78432194c85388f9.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_error-cdf7390829512b1b.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/webpack-d5f0964a9c2e92d2.js +1 -0
  184. package/apps/pwa/out/_next/static/css/{b5d88eba78a3d7bf.css → be9019f696217521.css} +1 -1
  185. package/apps/pwa/out/approvals/index.html +1 -1
  186. package/apps/pwa/out/approvals/index.txt +23 -23
  187. package/apps/pwa/out/autopilot/index.html +1 -1
  188. package/apps/pwa/out/autopilot/index.txt +23 -23
  189. package/apps/pwa/out/board/index.html +1 -1
  190. package/apps/pwa/out/board/index.txt +23 -23
  191. package/apps/pwa/out/botlab/index.html +1 -1
  192. package/apps/pwa/out/botlab/index.txt +22 -22
  193. package/apps/pwa/out/bots/index.html +1 -1
  194. package/apps/pwa/out/bots/index.txt +22 -22
  195. package/apps/pwa/out/ceo/index.html +1 -1
  196. package/apps/pwa/out/ceo/index.txt +23 -23
  197. package/apps/pwa/out/chat/index.html +1 -1
  198. package/apps/pwa/out/chat/index.txt +22 -22
  199. package/apps/pwa/out/consult/index.html +1 -1
  200. package/apps/pwa/out/consult/index.txt +23 -23
  201. package/apps/pwa/out/control/index.html +1 -1
  202. package/apps/pwa/out/control/index.txt +23 -23
  203. package/apps/pwa/out/decisions/index.html +1 -1
  204. package/apps/pwa/out/decisions/index.txt +22 -22
  205. package/apps/pwa/out/design-check/index.html +1 -1
  206. package/apps/pwa/out/design-check/index.txt +23 -23
  207. package/apps/pwa/out/editor/index.html +1 -1
  208. package/apps/pwa/out/editor/index.txt +23 -23
  209. package/apps/pwa/out/exec/index.html +1 -1
  210. package/apps/pwa/out/exec/index.txt +23 -23
  211. package/apps/pwa/out/field/index.html +1 -1
  212. package/apps/pwa/out/field/index.txt +23 -23
  213. package/apps/pwa/out/healthz +1 -1
  214. package/apps/pwa/out/index.html +1 -1
  215. package/apps/pwa/out/index.txt +22 -22
  216. package/apps/pwa/out/inside/index.html +1 -1
  217. package/apps/pwa/out/inside/index.txt +22 -22
  218. package/apps/pwa/out/intake/index.html +1 -1
  219. package/apps/pwa/out/intake/index.txt +23 -23
  220. package/apps/pwa/out/live/index.html +1 -1
  221. package/apps/pwa/out/live/index.txt +23 -23
  222. package/apps/pwa/out/live-run/index.html +1 -1
  223. package/apps/pwa/out/live-run/index.txt +23 -23
  224. package/apps/pwa/out/loops/index.html +1 -1
  225. package/apps/pwa/out/loops/index.txt +22 -22
  226. package/apps/pwa/out/market/index.html +1 -1
  227. package/apps/pwa/out/market/index.txt +22 -22
  228. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  229. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  230. package/apps/pwa/out/morning/index.html +1 -1
  231. package/apps/pwa/out/morning/index.txt +23 -23
  232. package/apps/pwa/out/observatory/index.html +1 -1
  233. package/apps/pwa/out/observatory/index.txt +23 -23
  234. package/apps/pwa/out/ops/checklist/index.html +1 -1
  235. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  236. package/apps/pwa/out/ops/release/index.html +1 -1
  237. package/apps/pwa/out/ops/release/index.txt +22 -22
  238. package/apps/pwa/out/ops/seats/index.html +1 -1
  239. package/apps/pwa/out/ops/seats/index.txt +22 -22
  240. package/apps/pwa/out/outputs/index.html +1 -1
  241. package/apps/pwa/out/outputs/index.txt +22 -22
  242. package/apps/pwa/out/reflection/index.html +1 -1
  243. package/apps/pwa/out/reflection/index.txt +23 -23
  244. package/apps/pwa/out/scheduler/index.html +1 -1
  245. package/apps/pwa/out/scheduler/index.txt +23 -23
  246. package/apps/pwa/out/sessions/index.html +1 -1
  247. package/apps/pwa/out/sessions/index.txt +22 -22
  248. package/apps/pwa/out/settings/devices/index.html +1 -1
  249. package/apps/pwa/out/settings/devices/index.txt +23 -23
  250. package/apps/pwa/out/settings/index.html +1 -1
  251. package/apps/pwa/out/settings/index.txt +23 -23
  252. package/apps/pwa/out/setup/done/index.html +1 -1
  253. package/apps/pwa/out/setup/done/index.txt +23 -23
  254. package/apps/pwa/out/setup/index.html +1 -1
  255. package/apps/pwa/out/setup/index.txt +23 -23
  256. package/apps/pwa/out/share/index.html +1 -1
  257. package/apps/pwa/out/share/index.txt +23 -23
  258. package/apps/pwa/out/showroom/index.html +1 -1
  259. package/apps/pwa/out/showroom/index.txt +22 -22
  260. package/apps/pwa/out/tasks/index.html +1 -1
  261. package/apps/pwa/out/tasks/index.txt +23 -23
  262. package/apps/pwa/out/term/index.html +1 -1
  263. package/apps/pwa/out/term/index.txt +23 -23
  264. package/apps/pwa/out/today/index.html +1 -1
  265. package/apps/pwa/out/today/index.txt +23 -23
  266. package/apps/pwa/out/trace/index.html +1 -1
  267. package/apps/pwa/out/trace/index.txt +23 -23
  268. package/apps/pwa/out/vault/index.html +1 -1
  269. package/apps/pwa/out/vault/index.txt +23 -23
  270. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  271. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  272. package/apps/pwa/out/workflows/index.html +1 -1
  273. package/apps/pwa/out/workflows/index.txt +23 -23
  274. package/apps/pwa/out/workspace/index.html +1 -1
  275. package/apps/pwa/out/workspace/index.txt +23 -23
  276. package/apps/pwa/out/worktrees/index.html +1 -1
  277. package/apps/pwa/out/worktrees/index.txt +23 -23
  278. package/graphs/drafts/recipes.yaml +27 -7
  279. package/graphs/release/recipes.yaml +9 -0
  280. package/graphs/release/release-loop.yaml +26 -18
  281. package/graphs/work/draft-close.yaml +54 -0
  282. package/graphs/work/green-close.yaml +37 -0
  283. package/graphs/work/recipes.yaml +46 -1
  284. package/graphs/work/run-research.yaml +35 -0
  285. package/package.json +1 -1
  286. package/scripts/__fixtures__/authoring-paths-10x3.json +32 -0
  287. package/scripts/ask-op-inbox-once.ts +17 -0
  288. package/scripts/boundary-proof-mapping-audit.ts +88 -0
  289. package/scripts/brand/check.ts +13 -4
  290. package/scripts/codex-fallback-live-probe.ts +17 -15
  291. package/scripts/fleet/node-setup.sh +32 -2
  292. package/scripts/fleet/run-light-worker.sh +9 -0
  293. package/scripts/gate-0226-accepted-remaining-90.txt +91 -0
  294. package/scripts/gate-0226-failure-census.ts +546 -0
  295. package/scripts/goals/authoring-metrics.ts +258 -0
  296. package/scripts/graph-units/draft-close-node.ts +222 -0
  297. package/scripts/hook-order-sweep.ts +7 -4
  298. package/scripts/intake-collect-telegram-saved-stage.ts +48 -0
  299. package/scripts/land-global-checks.ts +69 -1
  300. package/scripts/measure-nl-routing-corpus.ts +207 -2
  301. package/scripts/provision-export-redactions.ts +15 -0
  302. package/scripts/public-copy-lint.ts +122 -0
  303. package/scripts/public-export.ts +69 -2
  304. package/scripts/ratchets/next-tui-entry-budget.ts +124 -0
  305. package/scripts/ratchets/train-side-invariants.sh +7 -1
  306. package/scripts/release-loop/cut-branch.ts +61 -12
  307. package/scripts/release-loop/docs-node.ts +140 -0
  308. package/scripts/release-loop/gate-closure.ts +48 -0
  309. package/scripts/release-loop/gate-diff.ts +2 -1
  310. package/scripts/release-loop/gate-node.ts +161 -25
  311. package/scripts/release-loop/gate-partial.ts +82 -3
  312. package/scripts/release-loop/gate-prewarm-plan.ts +35 -0
  313. package/scripts/release-loop/gate-radius-live.ts +4 -2
  314. package/scripts/release-loop/known-issues-node.ts +6 -4
  315. package/scripts/release-loop/notes-repair.ts +216 -0
  316. package/scripts/release-loop/prepare-node.ts +61 -5
  317. package/scripts/release-loop/resume-cap.ts +2 -1
  318. package/scripts/release-loop/resume-release.ts +406 -7
  319. package/scripts/release-loop/resume-test-traffic.ts +10 -0
  320. package/scripts/release-loop/resume.ts +13 -5
  321. package/scripts/release-loop/traffic-node.ts +12 -4
  322. package/scripts/release-loop/traffic-transition.ts +53 -13
  323. package/scripts/release-loop/version-node.ts +109 -15
  324. package/scripts/train/green-leg.ts +176 -0
  325. package/scripts/tui-continue-real-evidence.ts +47 -0
  326. package/scripts/ux-sim.ts +1 -1
  327. package/scripts/video-pipeline.ts +2 -2
  328. package/src/acp/client.ts +31 -5
  329. package/src/acp/core-turn-bridge.ts +12 -1
  330. package/src/acp/stderr-summary.ts +49 -0
  331. package/src/agent/monad-agent-turn.ts +1 -0
  332. package/src/agent/run-context.ts +1 -1
  333. package/src/agent/task-notification.ts +3 -1
  334. package/src/agent/terminal-surface.ts +217 -0
  335. package/src/agent-substrate/pr-reviewer.ts +11 -3
  336. package/src/agent-substrate/review-observation.ts +10 -0
  337. package/src/agent-substrate/self-review-cli.ts +537 -57
  338. package/src/auto-research/question-ladder-contract.ts +137 -0
  339. package/src/auto-research/research-turn.ts +134 -0
  340. package/src/autopilot/discovery/doc-lint.ts +1 -1
  341. package/src/autopilot/lineage/sources.ts +1 -1
  342. package/src/autopilot/pr-manager.ts +64 -13
  343. package/src/boot/daemon-runtime.ts +35 -3
  344. package/src/boot/daemon-tools/pty-shell.ts +2 -1
  345. package/src/bots/screen-probe.ts +1 -1
  346. package/src/budget/run-usage-rollup.ts +12 -7
  347. package/src/budget/unified-usage.ts +20 -0
  348. package/src/capture/tui-self-report.ts +42 -5
  349. package/src/chat/index.ts +1 -0
  350. package/src/chat/tool-render/dispatch.ts +8 -1
  351. package/src/cli/autopilot-cli.ts +24 -9
  352. package/src/cli/decisions-cli.ts +7 -4
  353. package/src/cli/doctor-cli.ts +116 -7
  354. package/src/cli/guardian-cli.ts +3 -1
  355. package/src/cli/intake-cli.ts +48 -12
  356. package/src/cli/lesson-cli.ts +69 -9
  357. package/src/cli/logs-cli.ts +357 -2
  358. package/src/cli/memory-cli.ts +30 -1
  359. package/src/cli/msg-cli.ts +15 -11
  360. package/src/cli/pr-granularity.ts +5 -3
  361. package/src/cli/pr-land-overlap-evidence.ts +38 -5
  362. package/src/cli/pr-land-refusal.ts +1 -1
  363. package/src/cli/pty-takeover-cli.ts +58 -5
  364. package/src/cli/release-checklist-read-cli.ts +112 -4
  365. package/src/cli/release-cli.ts +89 -18
  366. package/src/cli/review-carry.ts +77 -0
  367. package/src/cli/self-update.ts +24 -6
  368. package/src/cli/self-utterances-cli.ts +13 -16
  369. package/src/cli/tasks-card-show.ts +31 -1
  370. package/src/cli/tasks-cli.ts +370 -33
  371. package/src/cli/tasks-read-cli.ts +13 -5
  372. package/src/cli/usage-cli.ts +12 -10
  373. package/src/context-bus/claude-memory-import.ts +99 -0
  374. package/src/context-bus/context-now.ts +86 -2
  375. package/src/context-bus/long-term-memory.ts +125 -0
  376. package/src/core-turn/run-core-turn.ts +9 -2
  377. package/src/core-turn/run-goal-loop.ts +5 -2
  378. package/src/core-turn/types.ts +5 -0
  379. package/src/dashboard/chat/acp-chat.ts +9 -0
  380. package/src/dashboard/index.ts +115 -30
  381. package/src/dashboard/input/agent-wake.ts +43 -0
  382. package/src/dashboard/input/chat-main-acp-dispatch.ts +21 -1
  383. package/src/dashboard/input/terminal-modal-router.ts +8 -159
  384. package/src/dashboard/replay-preview.ts +25 -0
  385. package/src/dashboard/side-basic-runtime.ts +61 -0
  386. package/src/dashboard/slash-runtime/dashboard-handlers.ts +19 -8
  387. package/src/dashboard/synchronized-frame-output.ts +74 -0
  388. package/src/dashboard/term-slash-runtime.ts +2 -0
  389. package/src/dashboard/turn-preamble.ts +28 -4
  390. package/src/display/coordinator.ts +45 -12
  391. package/src/display/resize-listener.ts +41 -20
  392. package/src/display/synchronized-output-region.ts +60 -0
  393. package/src/domains/guardian-tick.ts +9 -0
  394. package/src/domains/surface-events.ts +80 -1
  395. package/src/exec-requests/planner.ts +37 -2
  396. package/src/execution-loop/launch-gate.ts +4 -3
  397. package/src/git-fs/cli-stderr.ts +45 -0
  398. package/src/git-fs/gh-cli.ts +6 -3
  399. package/src/git-fs/git-cli.ts +11 -5
  400. package/src/git-fs/watcher.ts +27 -22
  401. package/src/git-fs/worktree.ts +19 -3
  402. package/src/graph-runner/graph-cli.ts +26 -2
  403. package/src/graph-runner/graph-grow.ts +37 -9
  404. package/src/graph-runner/graph-run-api.ts +3 -2
  405. package/src/graph-runner/runner.ts +34 -7
  406. package/src/graph-wizard/generate.ts +29 -6
  407. package/src/grok/credential.ts +107 -36
  408. package/src/harness/harness-cli-command.ts +179 -33
  409. package/src/harness/review-adapter.ts +26 -6
  410. package/src/hq/fence-wrapper.ts +1 -1
  411. package/src/hq/transfer.ts +1 -1
  412. package/src/index.ts +151 -18
  413. package/src/instance/test-write-guard-real-transport.fixture.ts +52 -0
  414. package/src/intake-plane/collect-telegram-saved.ts +54 -5
  415. package/src/interactive-terminal-modal.ts +3 -0
  416. package/src/keybindings.ts +17 -22
  417. package/src/llm/provider-credentials.ts +1 -1
  418. package/src/llm/route-decision.ts +13 -0
  419. package/src/llm-tool-name.ts +37 -0
  420. package/src/llm-tool-schema.ts +55 -0
  421. package/src/llm.ts +203 -21
  422. package/src/loops/checker.ts +100 -0
  423. package/src/loops/consensus.ts +26 -2
  424. package/src/loops/loop-cli.ts +9 -4
  425. package/src/loops/orchestrator/anomaly-cluster.ts +243 -0
  426. package/src/loops/orchestrator/inbox-once.ts +28 -0
  427. package/src/loops/orchestrator/inbox.ts +130 -1
  428. package/src/loops/orchestrator/tick.ts +55 -7
  429. package/src/maturity/feature-maturity.ts +1 -0
  430. package/src/model-tier/llm-tier-map.ts +12 -17
  431. package/src/msg/msg-store.ts +17 -0
  432. package/src/native-tool-catalog.ts +1 -1
  433. package/src/nexus/api/channel-ask-op.ts +96 -0
  434. package/src/nexus/api/discord-trigger-bot.ts +28 -1
  435. package/src/nexus/api/graph-wizard-api.ts +27 -2
  436. package/src/nexus/api/grid.ts +24 -4
  437. package/src/nexus/api/http-server.ts +1 -1
  438. package/src/nexus/api/ops-api.ts +14 -6
  439. package/src/nexus/api/telegram-trigger-bot.ts +24 -0
  440. package/src/nexus/api/terminals.ts +28 -6
  441. package/src/nexus/state/state.ts +7 -1
  442. package/src/oauth/codex-account-store.ts +34 -1
  443. package/src/ops/loops-map.ts +46 -0
  444. package/src/ops/overview-cache.ts +22 -4
  445. package/src/ops/overview.ts +41 -5
  446. package/src/patents/ledger.ts +18 -1
  447. package/src/policy/llm-policy.ts +3 -1
  448. package/src/preview/terminal.ts +4 -2
  449. package/src/prompt-cache/anthropic.ts +2 -0
  450. package/src/prompt-enhance/enhance.ts +9 -4
  451. package/src/pty-shell/availability.ts +7 -0
  452. package/src/pty-shell/herdr-agent-report.ts +40 -31
  453. package/src/pty-shell/herdr-prune-stale.ts +17 -0
  454. package/src/pty-shell/herdr-sequence-lease.ts +60 -0
  455. package/src/pty-shell/pty-control-ipc.ts +187 -18
  456. package/src/pty-shell/pty-manifest.ts +16 -2
  457. package/src/pty-shell/registry.ts +25 -15
  458. package/src/pty-shell/tui-run-state.ts +235 -1
  459. package/src/release-loop/checklist.ts +50 -5
  460. package/src/release-loop/feature-store.ts +9 -9
  461. package/src/release-loop/gate-shards.ts +61 -0
  462. package/src/release-loop/landed-but-yellow.ts +93 -12
  463. package/src/release-loop/light-rc.ts +7 -4
  464. package/src/release-loop/rehearsal.ts +1 -1
  465. package/src/repl/index.ts +18 -0
  466. package/src/research/axis-fanout.ts +133 -0
  467. package/src/research/edge-decision.ts +88 -0
  468. package/src/research/output-router.ts +127 -0
  469. package/src/research-bridge/format.ts +16 -1
  470. package/src/research-bridge/index.ts +1 -0
  471. package/src/research-bridge/invoke.ts +41 -5
  472. package/src/seat-loop/seat-report.ts +1 -0
  473. package/src/self-dev/ask-launch-flow.ts +86 -10
  474. package/src/self-dev/decompose-proposal.ts +29 -18
  475. package/src/self-dev/decompose.ts +45 -9
  476. package/src/self-dev/dev-pipeline.ts +29 -7
  477. package/src/self-dev/draft-residue.ts +221 -2
  478. package/src/self-dev/draft-sweep.ts +254 -16
  479. package/src/self-dev/draft-triage-rules.ts +19 -5
  480. package/src/self-dev/orchestrate.ts +1 -0
  481. package/src/self-dev/review-provider-fallback.ts +45 -7
  482. package/src/self-dev/run-supervisor.ts +22 -7
  483. package/src/self-dev/shard-plan.ts +48 -0
  484. package/src/self-dev/shard-run.ts +310 -6
  485. package/src/self-implement/comment-only-source.ts +54 -0
  486. package/src/self-implement/doctor-visit.ts +108 -0
  487. package/src/self-implement/gate-artifacts.ts +6 -2
  488. package/src/self-implement/gate-baseline.ts +99 -1
  489. package/src/self-implement/gate-cli.ts +231 -82
  490. package/src/self-implement/gate-failure-signature.ts +44 -0
  491. package/src/self-implement/gate-remote.ts +30 -4
  492. package/src/self-implement/gate-scope.ts +13 -1
  493. package/src/self-implement/goal-author.ts +407 -21
  494. package/src/self-implement/goal-doc/section.ts +22 -0
  495. package/src/self-implement/goal-supervisor-fill.ts +110 -27
  496. package/src/self-implement/importer-test-index.ts +215 -2
  497. package/src/self-implement/knowhow-pack.ts +125 -12
  498. package/src/self-implement/l8-merge-queue.ts +198 -32
  499. package/src/self-implement/off-diff-evidence.ts +23 -0
  500. package/src/self-implement/orchestrator.ts +369 -33
  501. package/src/self-implement/pod-inplace-worktree.ts +82 -0
  502. package/src/self-implement/pod-warm-pool-profile.ts +2 -2
  503. package/src/self-implement/premise-review.ts +7 -5
  504. package/src/self-implement/review-round-brief.ts +300 -0
  505. package/src/self-implement/rework-policy.ts +14 -0
  506. package/src/self-implement/run-ledger.ts +428 -6
  507. package/src/self-implement/run-stop.ts +1 -1
  508. package/src/self-implement/running-runs.ts +17 -3
  509. package/src/self-implement/seams.ts +45 -16
  510. package/src/self-implement/self-implement-cli.ts +26 -2
  511. package/src/self-implement/stop-autoheal.ts +1 -0
  512. package/src/self-implement/test-seams.ts +13 -1
  513. package/src/self-implement/train-reach-grouping.ts +8 -2
  514. package/src/session/channel-ask-op-envelope.ts +36 -0
  515. package/src/session/channel-op-reply-route.ts +200 -0
  516. package/src/session/chat.ts +10 -1
  517. package/src/session/id-prefix.ts +60 -0
  518. package/src/session/index.ts +6 -0
  519. package/src/session-runtime/index.ts +18 -23
  520. package/src/skills/runner.ts +7 -3
  521. package/src/skills/tools/dev-harness.ts +2 -2
  522. package/src/skills/tools/pty.ts +5 -0
  523. package/src/skills/tools/schedule-wakeup.ts +1 -6
  524. package/src/status/bar.ts +3 -3
  525. package/src/status/metrics.ts +12 -1
  526. package/src/steward/rejudge-tick.ts +12 -5
  527. package/src/steward/resync-tick.ts +31 -4
  528. package/src/storage/file-lock.ts +17 -4
  529. package/src/task-agent/actions.ts +199 -4
  530. package/src/task-agent/adopt-pr.ts +63 -2
  531. package/src/task-agent/boundary-proof-mapping.ts +12 -0
  532. package/src/task-agent/boundary-proof-producer.ts +147 -0
  533. package/src/task-agent/completion-evidence.ts +2 -1
  534. package/src/task-agent/evidence-command-normalize.ts +42 -0
  535. package/src/task-agent/export-redactions-provision.ts +43 -0
  536. package/src/task-agent/green-close-recipe.ts +239 -0
  537. package/src/task-agent/green-sweep.ts +64 -10
  538. package/src/task-agent/live-moves.ts +48 -3
  539. package/src/task-agent/map-host-adapter.ts +134 -0
  540. package/src/task-agent/map-reduce.ts +158 -0
  541. package/src/task-agent/map-run-ledger.ts +226 -0
  542. package/src/task-agent/parent-host.ts +74 -17
  543. package/src/task-agent/rejudge-on-head.ts +72 -5
  544. package/src/task-agent/repair-cycle.ts +108 -8
  545. package/src/task-agent/repair-next-move.ts +12 -2
  546. package/src/task-agent/repair-worker.ts +37 -1
  547. package/src/task-agent/review-prior-fix.ts +12 -2
  548. package/src/task-agent/shadow.ts +188 -5
  549. package/src/task-agent/task-hand.ts +253 -20
  550. package/src/task-agent/task-parent-role-llm.ts +9 -0
  551. package/src/task-agent/tier-split.ts +40 -5
  552. package/src/task-agent/work-order.ts +53 -2
  553. package/src/task-orchestrator/dispatcher.ts +46 -1
  554. package/src/task-orchestrator/generator-prompt.ts +6 -0
  555. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +16 -6
  556. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +31 -4
  557. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +8 -6
  558. package/src/task-orchestrator/surfaces/pod-ledger-events.ts +97 -0
  559. package/src/task-orchestrator/surfaces/pod-pool.ts +77 -8
  560. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +15 -9
  561. package/src/task-orchestrator/surfaces/self-implement-pod.ts +338 -79
  562. package/src/task-orchestrator/surfaces/self-implement.ts +37 -2
  563. package/src/terminal/session-registry.ts +1 -0
  564. package/src/terminal/terminal-modal-router.ts +156 -0
  565. package/src/testing/linux-env.ts +177 -0
  566. package/src/tool-hints/gate.ts +6 -4
  567. package/src/tool-hints/signals.ts +33 -1
  568. package/src/tui/chat/background-job-status.ts +8 -2
  569. package/src/tui/chat/goal-interim.ts +5 -1
  570. package/src/tui/entry.ts +722 -30
  571. package/src/tui/input/at-picker.ts +55 -0
  572. package/src/tui/sidetab/attach.ts +304 -0
  573. package/src/tui/sidetab/list.ts +176 -0
  574. package/src/tui/sidetab/nl-tool.ts +136 -0
  575. package/src/tui/slash/resume-command.ts +23 -0
  576. package/src/tui/slash/resume.ts +38 -35
  577. package/src/tui/slash/signals.ts +114 -0
  578. package/src/tui/slash/term-next.ts +72 -0
  579. package/src/tui-client/local-chat-turn.ts +31 -9
  580. package/src/user-config.ts +30 -3
  581. package/src/ux-sim/boot-daemon-tools-pty-shell-device.ts +136 -0
  582. package/src/ux-sim/classic-tui-seat-scenarios.ts +158 -4
  583. package/src/ux-sim/fake-gh.ts +174 -0
  584. package/src/ux-sim/fake-kubectl.ts +142 -0
  585. package/src/ux-sim/fake-llm.ts +17 -6
  586. package/src/ux-sim/next-tui-seat-scenarios.ts +176 -6
  587. package/src/ux-sim/resume-picker-device.ts +173 -0
  588. package/src/ux-sim/session-id-seed.ts +60 -0
  589. package/src/ux-sim/skills-tools-shell-device.ts +156 -0
  590. package/src/ux-sim/task-agent-parent-host-device.ts +264 -0
  591. package/src/ux-sim/task-agent-rejudge-on-head-device.ts +114 -0
  592. package/src/version/packed-revision.json +1 -1
  593. package/apps/pwa/out/_next/static/chunks/109.f196abf3484bb97f.js +0 -287
  594. package/apps/pwa/out/_next/static/chunks/1201.afbfc5b1c5e5cbe4.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/1517.9275b89572098ff3.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/1574.ad11f8db8396ab45.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/1614.7a8c5c40aee0261f.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/1871-f1eb29d8dbb4065b.js +0 -1
  599. package/apps/pwa/out/_next/static/chunks/190.285c832bd9626f8c.js +0 -64
  600. package/apps/pwa/out/_next/static/chunks/2193.0ea24c4a7aa1d004.js +0 -36
  601. package/apps/pwa/out/_next/static/chunks/223-ad865c1e80491d78.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/2265-f0d90b6dbec05f81.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/2267.eb4a62a209f09ec4.js +0 -149
  604. package/apps/pwa/out/_next/static/chunks/2341-86cd06c0cf01aa33.js +0 -1
  605. package/apps/pwa/out/_next/static/chunks/2352.9abc42422bade876.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/2397-c1653a816061842d.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/2500.cbc1257b774aae52.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/2709.8e1889034255fc5f.js +0 -24
  609. package/apps/pwa/out/_next/static/chunks/284.ac77c0db55bfd3a8.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/2941.3c3546f96bc6efa7.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/2976.b000698a3c6512dc.js +0 -24
  612. package/apps/pwa/out/_next/static/chunks/2986.45d7652e4f13557e.js +0 -2
  613. package/apps/pwa/out/_next/static/chunks/3155.2587a55196ecb394.js +0 -226
  614. package/apps/pwa/out/_next/static/chunks/3277.785db1cf09f98601.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/3320-f9c77381f85a2c0a.js +0 -1
  616. package/apps/pwa/out/_next/static/chunks/3386-91049feabff9a94e.js +0 -1
  617. package/apps/pwa/out/_next/static/chunks/339.80afb7e8839372af.js +0 -8
  618. package/apps/pwa/out/_next/static/chunks/3538.a445e50bb359d068.js +0 -32
  619. package/apps/pwa/out/_next/static/chunks/3540c645.2595a6af01a97672.js +0 -1
  620. package/apps/pwa/out/_next/static/chunks/3621.581b2054cc14bee4.js +0 -286
  621. package/apps/pwa/out/_next/static/chunks/3676.8c28d16fd84ca843.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/375-b84540853523fe52.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/3757-180308bc61021b05.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/379.ee262e37586fe1ae.js +0 -74
  625. package/apps/pwa/out/_next/static/chunks/3840-5825d56d46976a92.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/3969-81054381eaec4c4c.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/4067-bf9aa60547972937.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/4176.e5729c93c29151eb.js +0 -104
  629. package/apps/pwa/out/_next/static/chunks/4227-e6c84686bc8946ca.js +0 -1
  630. package/apps/pwa/out/_next/static/chunks/4327.b9e08a71c0009348.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/4946.bd106eafd668f248.js +0 -148
  632. package/apps/pwa/out/_next/static/chunks/5085-bce4904931767ca6.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/5125.2eb9968b83767193.js +0 -93
  634. package/apps/pwa/out/_next/static/chunks/5128-75b733fd74a9a1eb.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/5489f3fe.4bb1458b09f6dead.js +0 -122
  636. package/apps/pwa/out/_next/static/chunks/5560-2a4648f6a30f16e7.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/5772-e29e14f8ff127122.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/5887.5b2f5b44ce0f7e9a.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/5899-4c112a2d908cf97f.js +0 -1
  640. package/apps/pwa/out/_next/static/chunks/6041.1b65efb796ebc2a5.js +0 -79
  641. package/apps/pwa/out/_next/static/chunks/6057-765c0a26028c86b2.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/6142.f289e0d039f453a6.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/6199-6bcd61d2a3910e04.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/6236.cdf7293e710a9b2d.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/6387-237e079dac9a3ae6.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/6448-b6d272d074276e14.js +0 -1
  647. package/apps/pwa/out/_next/static/chunks/6466-92e65a48490b68a9.js +0 -1
  648. package/apps/pwa/out/_next/static/chunks/6580-cdc0e4a634ce4708.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/6659-b242dc31b20b3f29.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/6690.c5d9b321b1f79384.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/671-e9b6ad0c771c5514.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/6862.bcbf89dd0f4496b3.js +0 -27
  653. package/apps/pwa/out/_next/static/chunks/6912-7b375ee48732c767.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/727.7deb87749b0a343a.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/7481.39f7a4654907bf6e.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/7545.b1aa42cd15503319.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/7551-416d298c2dc4a9ab.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/7870-309de463786ac1fc.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/7888-e29b151bb6a3bb8b.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/7891.03f910124a737985.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/7905-6ba78d9234608f79.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/8207.f527886b7c464529.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/856.50bda61439d465e6.js +0 -62
  664. package/apps/pwa/out/_next/static/chunks/9031-92e9b487edb681ee.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/9060.5410d9f6a95f6287.js +0 -90
  666. package/apps/pwa/out/_next/static/chunks/914.8abefd48d579547b.js +0 -94
  667. package/apps/pwa/out/_next/static/chunks/9187-c87a2207d4ac5d46.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/9289.3e92f278821e18e0.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/9319.7816d3cc741cd34f.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/9327-e016bb9f1674d24f.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/9365.3de48bf75193ff9a.js +0 -77
  672. package/apps/pwa/out/_next/static/chunks/9383.5b5e9aa56b5fea97.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/9387-cb929573ef436f39.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/9489.c27becacc3283dec.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/9514.131c52cc75fe6439.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/9536.28962663c7734376.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/9686-12286f5bbf15a5a0.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/9776.289d40cc5a4c342a.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/9789.60f723d18097ec30.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/989.f0c17523c5e61f65.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/9972-1df393aa8d4f7d52.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-96750aba7430959f.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/app/approvals/page-4105e5bbbb043024.js +0 -1
  684. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-5d4fa75c2acb113e.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/app/board/page-a79a9bdb3b2142bf.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/app/botlab/page-f1c2047484992a35.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/app/bots/page-15ffc49b2d8c70e2.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/app/ceo/page-67d53a3fc8734135.js +0 -1
  689. package/apps/pwa/out/_next/static/chunks/app/chat/page-e30c8dc696491336.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/app/consult/page-2136e04fa77696e2.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/app/control/page-fecd2814790c2e12.js +0 -1
  692. package/apps/pwa/out/_next/static/chunks/app/decisions/page-280aa4a98fc5bb44.js +0 -1
  693. package/apps/pwa/out/_next/static/chunks/app/design-check/page-c14e96b65fb4b883.js +0 -1
  694. package/apps/pwa/out/_next/static/chunks/app/editor/page-187dac74825ed6bf.js +0 -1
  695. package/apps/pwa/out/_next/static/chunks/app/exec/page-6bc7246ea8c92448.js +0 -1
  696. package/apps/pwa/out/_next/static/chunks/app/field/page-083747f1d4518f61.js +0 -1
  697. package/apps/pwa/out/_next/static/chunks/app/healthz/route-96750aba7430959f.js +0 -1
  698. package/apps/pwa/out/_next/static/chunks/app/inside/page-15ca95a16ec96e1f.js +0 -1
  699. package/apps/pwa/out/_next/static/chunks/app/intake/page-a8ac031b3810eb64.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/app/layout-8835f93110494cda.js +0 -1
  701. package/apps/pwa/out/_next/static/chunks/app/live/page-e2de2e59111c9aad.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/app/live-run/page-f2b2e4bf6e981450.js +0 -1
  703. package/apps/pwa/out/_next/static/chunks/app/loops/page-5b92b7f75df20948.js +0 -1
  704. package/apps/pwa/out/_next/static/chunks/app/market/page-468eddf2b1a84ecb.js +0 -1
  705. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-2af16e62c12f22df.js +0 -1
  706. package/apps/pwa/out/_next/static/chunks/app/morning/page-18b40867a77a5793.js +0 -1
  707. package/apps/pwa/out/_next/static/chunks/app/not-found-fe934f2979f5a4a2.js +0 -1
  708. package/apps/pwa/out/_next/static/chunks/app/observatory/page-3eec435537357170.js +0 -1
  709. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-ff88cbfd2aa99fc8.js +0 -1
  710. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-a5b39fb780c70ab5.js +0 -1
  711. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ae465dde1d1b3933.js +0 -1
  712. package/apps/pwa/out/_next/static/chunks/app/page-cab9674fde9ead26.js +0 -1
  713. package/apps/pwa/out/_next/static/chunks/app/reflection/page-1da49bc8712284d0.js +0 -1
  714. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-5e8aa657551ac912.js +0 -1
  715. package/apps/pwa/out/_next/static/chunks/app/sessions/page-fcc36f36c6dc1fe2.js +0 -1
  716. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-d22db22f2a7296ca.js +0 -1
  717. package/apps/pwa/out/_next/static/chunks/app/settings/page-d12dc7b1773878f0.js +0 -1
  718. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-99debec9ea7fbec0.js +0 -1
  719. package/apps/pwa/out/_next/static/chunks/app/setup/layout-96750aba7430959f.js +0 -1
  720. package/apps/pwa/out/_next/static/chunks/app/setup/page-2a088a78bad7e924.js +0 -1
  721. package/apps/pwa/out/_next/static/chunks/app/share/page-2e8ed57db7e69993.js +0 -1
  722. package/apps/pwa/out/_next/static/chunks/app/showroom/page-4b3a16213c2ae8fe.js +0 -1
  723. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f098747ddf38c98b.js +0 -1
  724. package/apps/pwa/out/_next/static/chunks/app/today/page-68279e3c843c1fd7.js +0 -1
  725. package/apps/pwa/out/_next/static/chunks/app/trace/page-a34a74453367a39d.js +0 -1
  726. package/apps/pwa/out/_next/static/chunks/app/vault/page-8767a7db2155a5bd.js +0 -1
  727. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-bd5976683460d7f2.js +0 -1
  728. package/apps/pwa/out/_next/static/chunks/app/workflows/page-b7364835e7f2ef54.js +0 -1
  729. package/apps/pwa/out/_next/static/chunks/app/workspace/page-311af16b3ee0ffca.js +0 -1
  730. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-fb670757274ae990.js +0 -1
  731. package/apps/pwa/out/_next/static/chunks/ccae1c4b-0cf414f5eff1b10d.js +0 -1
  732. package/apps/pwa/out/_next/static/chunks/f9a61e4d.1fc2c1dbdd49fe46.js +0 -1
  733. package/apps/pwa/out/_next/static/chunks/framework-6d497cbf1eef04c6.js +0 -1
  734. package/apps/pwa/out/_next/static/chunks/main-app-012f5edec932f337.js +0 -1
  735. package/apps/pwa/out/_next/static/chunks/main-da5aeb809edc94a2.js +0 -1
  736. package/apps/pwa/out/_next/static/chunks/pages/_app-5ee319af6f749dfe.js +0 -1
  737. package/apps/pwa/out/_next/static/chunks/pages/_error-4bc84653a718d6c6.js +0 -1
  738. package/apps/pwa/out/_next/static/chunks/webpack-8a96cf6775c73d6e.js +0 -1
  739. package/scripts/migrate-monad-to-elanous.ts +0 -195
  740. package/scripts/rebrand-elanous.ts +0 -242
  741. /package/apps/pwa/out/_next/static/{uC-N0emedeNOFuaBQFv-I → RrjXTwz-urRAbChP1bX02}/_ssgManifest.js +0 -0
@@ -32,6 +32,7 @@ import {
32
32
  type AcceptanceReport,
33
33
  } from './acceptance.js';
34
34
  import { recordOpsEventSafe, type OpsEventInput } from '../domains/ops-log.js';
35
+ import { defaultAnomalyCluster, extractContainerExit, type AnomalyCluster } from '../loops/orchestrator/anomaly-cluster.js';
35
36
 
36
37
  // ───────────────────────── Concurrency caps ────────────────────────
37
38
 
@@ -82,6 +83,9 @@ export interface DispatcherOptions {
82
83
  * acceptance.checks reference fs or subprocess (file-exists /
83
84
  * file-contains / shell-zero). */
84
85
  acceptanceIo?: AcceptanceIo;
86
+ /** ANOMALY-CLUSTER — grouping state written at the failure record point; while a signature has 3+ runs
87
+ * and no cause verdict, relaunches of that goal·host are deferred instead of spawned. */
88
+ anomalyCluster?: AnomalyCluster;
85
89
  }
86
90
 
87
91
  export interface DispatchTickResult {
@@ -95,7 +99,7 @@ export interface DispatchTickResult {
95
99
  }>;
96
100
  /** Ready tasks we skipped this tick due to caps. Caller may `tick()`
97
101
  * again after any adapter completes. */
98
- deferred: Array<{ taskId: string; reason: 'cap' | 'no-adapter' | 'intake-sequential' }>;
102
+ deferred: Array<{ taskId: string; reason: 'cap' | 'no-adapter' | 'intake-sequential' | 'anomaly-hold' }>;
99
103
  }
100
104
 
101
105
  export class TaskDispatcher {
@@ -103,10 +107,13 @@ export class TaskDispatcher {
103
107
  private readonly activeByKind = new Map<TaskSurfaceKind, number>();
104
108
  private readonly abortByTask = new Map<string, AbortController>();
105
109
  private readonly now: () => number;
110
+ /** Production default is the process-wide cluster so tick-recorded failures gate relaunch here. */
111
+ private readonly anomaly: AnomalyCluster;
106
112
 
107
113
  constructor(private readonly opts: DispatcherOptions) {
108
114
  this.caps = { ...DEFAULT_CONCURRENCY_CAPS, ...(opts.concurrencyCaps ?? {}) };
109
115
  this.now = opts.now ?? Date.now;
116
+ this.anomaly = opts.anomalyCluster ?? defaultAnomalyCluster();
110
117
  }
111
118
 
112
119
  /**
@@ -147,6 +154,12 @@ export class TaskDispatcher {
147
154
  deferred.push({ taskId: task.id, reason: 'cap' });
148
155
  continue;
149
156
  }
157
+ // ANOMALY-CLUSTER relaunch gate: the task's goal·host relaunch is deferred while its signature has
158
+ // 3+ runs and no cause verdict has cleared it. Goal = task id, host = surface kind.
159
+ if (this.anomaly.hold(task.id, kind).hold) {
160
+ deferred.push({ taskId: task.id, reason: 'anomaly-hold' });
161
+ continue;
162
+ }
150
163
  const result = this.spawn(task, adapter);
151
164
  dispatched.push({
152
165
  taskId: task.id,
@@ -220,6 +233,24 @@ export class TaskDispatcher {
220
233
  }
221
234
  }
222
235
 
236
+ /** Records one failed attempt into the anomaly cluster (goal·host key, host excluded from signature). */
237
+ private recordAnomalyFailure(task: Task, observation: { runId: string; kind: string; stage: string; lastLines: string[] }): void {
238
+ try {
239
+ this.anomaly.record({
240
+ goal: task.id,
241
+ host: task.surface.kind,
242
+ runId: observation.runId,
243
+ kind: observation.kind,
244
+ containerExit: extractContainerExit(observation.lastLines.join('\n')),
245
+ stage: observation.stage,
246
+ lastLines: observation.lastLines,
247
+ at: new Date(this.now()).toISOString(),
248
+ });
249
+ } catch {
250
+ // Observability must not alter dispatch admission or completion.
251
+ }
252
+ }
253
+
223
254
  private recordOpsEvent(input: OpsEventInput): void {
224
255
  try {
225
256
  (this.opts.recordOpsEvent ?? recordOpsEventSafe)(input);
@@ -322,6 +353,8 @@ export class TaskDispatcher {
322
353
  this.recordOps(task, 'cycle_end', nextStatus, { execStatus: exec.status, durationMs: exec.durationMs });
323
354
 
324
355
  if (nextStatus === 'done') {
356
+ // Cause verdict — the same goal·host produced a clean run, so the relaunch hold is cleared.
357
+ this.anomaly.release(task.id, kind, '원인 판정: 실행이 정상 완료됨');
325
358
  this.opts.bus?.emit({
326
359
  kind: 'task-completed',
327
360
  taskId: task.id,
@@ -337,6 +370,12 @@ export class TaskDispatcher {
337
370
  reason: 'user',
338
371
  });
339
372
  } else {
373
+ this.recordAnomalyFailure(task, {
374
+ runId: exec.id,
375
+ kind: exec.status === 'timeout' ? 'timeout' : 'exit',
376
+ stage: 'monitor',
377
+ lastLines: [exec.error?.message ?? '', `Reason: ${exec.error?.code ?? 'UNKNOWN'}`],
378
+ });
340
379
  this.opts.bus?.emit({
341
380
  kind: 'task-failed',
342
381
  taskId: task.id,
@@ -368,6 +407,12 @@ export class TaskDispatcher {
368
407
  this.opts.store?.saveTask(this.opts.graph.getTask(task.id)!);
369
408
  } catch { /* ignore illegal transition */ }
370
409
  this.recordOps(task, 'cycle_end', 'failed', { errorCode: 'ADAPTER_ERROR', error: err instanceof Error ? err.message.slice(0, 120) : String(err) });
410
+ this.recordAnomalyFailure(task, {
411
+ runId: res.executionId,
412
+ kind: 'adapter-error',
413
+ stage: 'monitor',
414
+ lastLines: [err instanceof Error ? err.message : String(err)],
415
+ });
371
416
  this.opts.bus?.emit({
372
417
  kind: 'task-failed',
373
418
  taskId: task.id,
@@ -82,6 +82,8 @@ export function buildDecomposePrompt(
82
82
 
83
83
  const lines: string[] = [];
84
84
  const isGoalAuthorCoarse = opts.profile === 'goal-author-coarse';
85
+ const measuredDeviceInventory = input.context?.activeSummary?.match(/^Device inventory \(measured at goal-author repository root\): .*$/mu)?.[0];
86
+ const measuredDevice = measuredDeviceInventory?.match(/^Device inventory \(measured at goal-author repository root\): (missing|existing): (src\/ux-sim\/[^\s,]+)$/u);
85
87
  lines.push(
86
88
  'You are the monad-agent Task Orchestrator decomposition planner.',
87
89
  isGoalAuthorCoarse
@@ -205,6 +207,10 @@ export function buildDecomposePrompt(
205
207
  '## Device-first decomposition (DEVICE-FIRST-AUTHORING — follow strictly)',
206
208
  '### Existing device inventory (read from this checkout; use these names before proposing a new device)',
207
209
  ...deviceInventory(),
210
+ ...(measuredDeviceInventory
211
+ ? ['### Goal-author device check (measured at the target root; takes precedence over checkout inventory)',
212
+ measuredDeviceInventory,
213
+ ...(measuredDevice ? [`- Use the measured ${measuredDevice[1]} status for ${measuredDevice[2]} instead of this checkout's inventory when choosing the device branch.`] : [])] : []),
208
214
  '- The objective\'s verification plan (검증 계획/TDD) section picks a stage and ladder tier (무대·층) — e.g. a `src/ux-sim/*` stage at L2 (fake-llm·fake-bin·fake-clock). BEFORE writing slices, CHECK whether the mock device or injected event definition that this verification plan needs ALREADY EXISTS: look in `src/ux-sim/*`, the §1 device table of `docs/manual/MANUAL-ux-simulation-devices-2026-08-23.md`, and the event-monkey event kinds.',
209
215
  '- Two branches — exactly one applies per needed device:',
210
216
  ' - Device EXISTS: its name MUST go into the verification plan wording — the slice that tests the feature carries the device\'s exact name (device file / §1 table row / event kind) in its acceptance criteria and reuses the device; never re-create it.',
@@ -7,6 +7,8 @@ import { fromJsonl, type LogRecord } from '../../mss/logging/record.js';
7
7
 
8
8
  const FILE_LIMIT = 5 * 1024 * 1024;
9
9
  const TOTAL_LIMIT = 20 * 1024 * 1024;
10
+ export const POD_SALVAGE_BUNDLE_FILE_LIMIT = 160 * 1024 * 1024;
11
+ export const POD_SALVAGE_BUNDLE_TOTAL_LIMIT = 180 * 1024 * 1024;
10
12
 
11
13
  type Artifact = { path: string; bytes: Buffer };
12
14
  type Incomplete = { path: string; reason: string };
@@ -50,13 +52,16 @@ export function parsePodArtifactChunks(logs: string): ParseResult {
50
52
  }
51
53
  const artifacts: Artifact[] = [];
52
54
  const error: Incomplete[] = [];
53
- let bytesSoFar = 0;
55
+ let artifactBytes = 0;
56
+ let salvageBundleBytes = 0;
54
57
  for (const [token, group] of groups) {
55
58
  let path = token;
56
59
  let reason = group.reason;
57
60
  try {
58
61
  path = decodePath(token);
59
62
  if (!safeRelativePath(path)) throw new Error('unsafe relative path');
63
+ const salvageBundle = /^pod-salvage\/[a-zA-Z0-9._-]+\.bundle$/.test(path);
64
+ const fileLimit = salvageBundle ? POD_SALVAGE_BUNDLE_FILE_LIMIT : FILE_LIMIT;
60
65
  if (!reason && group.chunks.size !== group.total) reason = 'missing chunk';
61
66
  if (!reason) {
62
67
  let encoded = '';
@@ -64,15 +69,20 @@ export function parsePodArtifactChunks(logs: string): ParseResult {
64
69
  const chunk = group.chunks.get(i);
65
70
  if (!chunk) { reason = 'missing chunk'; break; }
66
71
  encoded += chunk;
67
- if (encoded.length > 8 * 1024 * 1024) { reason = 'file too large'; break; }
72
+ if (encoded.length > Math.ceil(fileLimit * 4 / 3) + 1024 * 1024) { reason = 'file too large'; break; }
68
73
  }
69
74
  if (!reason) {
70
75
  const compressed = Buffer.from(encoded, 'base64');
71
76
  if (compressed.toString('base64') !== encoded) throw new Error('invalid base64');
72
- const bytes = gunzipSync(compressed, { maxOutputLength: FILE_LIMIT + 1 });
73
- if (bytes.length > FILE_LIMIT) reason = 'file too large';
74
- else if (bytesSoFar + bytes.length > TOTAL_LIMIT) reason = 'total too large';
75
- else { artifacts.push({ path, bytes }); bytesSoFar += bytes.length; }
77
+ const bytes = gunzipSync(compressed, { maxOutputLength: fileLimit + 1 });
78
+ if (bytes.length > fileLimit) reason = 'file too large';
79
+ else if ((salvageBundle ? salvageBundleBytes : artifactBytes) + bytes.length >
80
+ (salvageBundle ? POD_SALVAGE_BUNDLE_TOTAL_LIMIT : TOTAL_LIMIT)) reason = 'total too large';
81
+ else {
82
+ artifacts.push({ path, bytes });
83
+ if (salvageBundle) salvageBundleBytes += bytes.length;
84
+ else artifactBytes += bytes.length;
85
+ }
76
86
  }
77
87
  }
78
88
  } catch (e) { reason = e instanceof Error ? e.message : String(e); }
@@ -334,10 +334,31 @@ export interface PodTermination {
334
334
  finishedAt: string | PodTerminationUnknown;
335
335
  /** From `state.terminated.reason === 'OOMKilled'` only (never from exit code 137). */
336
336
  oomKilled: boolean | PodTerminationUnknown;
337
+ /** A terminal verdict can race Kubernetes container status; one bounded Pod re-read records the outcome. */
338
+ collectionAttempts?: 2;
339
+ collectionFailureReason?: 'pod-still-running-after-retry' | 'pod-absent-after-retry' |
340
+ 'pod-read-unavailable-after-retry' | 'terminated-fields-missing-after-retry' | 'pod-state-unknown-after-retry';
337
341
  }
338
342
 
339
- /** Job Failed condition reason first: the classification takes column one, the capture all three, from one read. */
340
- export const POD_TERMINATION_JOB_JSONPATH = '{.status.conditions[?(@.type=="Failed")].reason}{"\\t"}{.spec.activeDeadlineSeconds}{"\\t"}{.metadata.uid}';
343
+ /** FailureTarget can precede Failed at the deadline. Read both in the same Job snapshot. */
344
+ export const POD_TERMINATION_JOB_JSONPATH = '{.status.conditions[?(@.type=="Failed")].reason}{"|"}{.status.conditions[?(@.type=="FailureTarget")].reason}{"\\t"}{.spec.activeDeadlineSeconds}{"\\t"}{.metadata.uid}';
345
+
346
+ export function podJobConditionReason(value: string | undefined): string {
347
+ const [failed, target] = (value ?? '').split('|');
348
+ return terminationWord(failed || target);
349
+ }
350
+
351
+ /** Emitted by the Pod wrapper before the Job's hard deadline, scoped to this Job and child run. */
352
+ export function podDeadlineResultMarker(logs: string, job: string, runId: string): { stage: string; pr: number | null; branch: string | null } | null {
353
+ if (!/^[A-Za-z0-9_.:-]+$/.test(job) || !/^[A-Za-z0-9_.:-]+$/.test(runId)) return null;
354
+ const prefix = `ELANOUS_POD_DEADLINE_RESULT ${job} ${runId} stop-cause=deadline `;
355
+ for (const line of logs.split('\n').reverse()) {
356
+ if (!line.startsWith(prefix)) continue;
357
+ const match = /^stage=(clone|setup|run|unknown) pr=(unknown|[1-9]\d*) branch=(unknown|[A-Za-z0-9_./-]+)$/.exec(line.slice(prefix.length).trim());
358
+ if (match) return { stage: match[1]!, pr: match[2] === 'unknown' ? null : Number(match[2]), branch: match[3] === 'unknown' ? null : match[3]! };
359
+ }
360
+ return null;
361
+ }
341
362
  /**
342
363
  * One row per Pod of this Job name. The first three columns keep the classification read's layout
343
364
  * (`creation · terminated.reason · terminated.exitCode`, empty when the child has no status yet), so the
@@ -384,7 +405,7 @@ export function readPodTermination(input: { kubectl: PodTerminationKubectl; name
384
405
  const jobUid = input.jobUid && JOB_UID.test(input.jobUid) ? input.jobUid : undefined;
385
406
  // A read without a uid, or with another uid, is not this launch's Job.
386
407
  const jobOk = jobRead?.status === 0 && jobUid !== undefined && readUid === jobUid;
387
- const jobConditionReason = jobOk ? terminationWord(reasonText) : 'unknown';
408
+ const jobConditionReason = jobOk ? podJobConditionReason(reasonText) : 'unknown';
388
409
  const podsRead = 'podsRead' in input ? input.podsRead ?? null
389
410
  : read(['-n', namespace, 'get', 'pods', '-l', `job-name=${job}`, '-o', `jsonpath=${POD_TERMINATION_PODS_JSONPATH}`]);
390
411
  let podLookup: PodTermination['podLookup'] = 'unavailable';
@@ -444,7 +465,11 @@ export function parsePodTermination(value: unknown, job: string): PodTermination
444
465
  || !oneOf(row.podLookup, ['terminated', 'running', 'pending', 'unknown', 'absent', 'unavailable'])
445
466
  || !wordOrUnknown(row.containerReason) || !numberOrUnknown(row.exitCode)
446
467
  || !(row.finishedAt === 'unknown' || (typeof row.finishedAt === 'string' && terminationInstant(row.finishedAt) !== 'unknown'))
447
- || !boolOrUnknown(row.oomKilled)) return undefined;
468
+ || !boolOrUnknown(row.oomKilled)
469
+ || (row.collectionAttempts !== undefined && row.collectionAttempts !== 2)
470
+ || (row.collectionFailureReason !== undefined && !oneOf(row.collectionFailureReason, [
471
+ 'pod-still-running-after-retry', 'pod-absent-after-retry', 'pod-read-unavailable-after-retry',
472
+ 'terminated-fields-missing-after-retry', 'pod-state-unknown-after-retry']))) return undefined;
448
473
  // Facts are attributed through the uid only: a record claiming Job or Pod facts without one is contradictory.
449
474
  if (row.jobUid === 'unknown' && (row.jobLookup !== 'unavailable' || row.podLookup !== 'unavailable')) return undefined;
450
475
  return {
@@ -455,5 +480,7 @@ export function parsePodTermination(value: unknown, job: string): PodTermination
455
480
  podLookup: row.podLookup as PodTermination['podLookup'], containerReason: row.containerReason as string,
456
481
  exitCode: row.exitCode as PodTermination['exitCode'], finishedAt: row.finishedAt as string,
457
482
  oomKilled: row.oomKilled as PodTermination['oomKilled'],
483
+ ...(row.collectionAttempts === 2 ? { collectionAttempts: 2 as const } : {}),
484
+ ...(row.collectionFailureReason ? { collectionFailureReason: row.collectionFailureReason as PodTermination['collectionFailureReason'] } : {}),
458
485
  };
459
486
  }
@@ -14,6 +14,7 @@ type ParseResult = Ledger[] | { error: Incomplete[]; ledgers: Ledger[] };
14
14
  const NON_PROGRESS_EVENTS = new Set(['progress-delivery-outcome']);
15
15
  const REEMIT_EXCLUDED_PREFIX = 'progress-delivery';
16
16
  type Emit = (category: string, event: string, data: Record<string, unknown>) => void;
17
+ export interface PodLedgerMeasurement { ledgerRead: boolean; activityMeasured: boolean }
17
18
  /** Heal-loop starter seam — defaults to the real `startHealLoop` inside `recordFailureEvent`. */
18
19
  type StartHealLoop = Parameters<typeof recordFailureEvent>[2];
19
20
 
@@ -174,7 +175,7 @@ export function createPodLedgerFollower(opts: {
174
175
  stallMinutes?: number;
175
176
  now?: () => number;
176
177
  onStall?: (message: string) => void;
177
- }): { poll(): void; readonly owned: boolean } {
178
+ }): { poll(): PodLedgerMeasurement; readonly owned: boolean } {
178
179
  const dir = opts.dir ?? runLedgerDir();
179
180
  const log = opts.log ?? defaultPodReemit();
180
181
  const emit: Emit = opts.emit ?? defaultPodReemit();
@@ -208,17 +209,17 @@ export function createPodLedgerFollower(opts: {
208
209
  return {
209
210
  get owned() { return owned; },
210
211
  poll() {
211
- if (disabled) return;
212
+ if (disabled) return { ledgerRead: true, activityMeasured: false };
212
213
  if (!owned && existsSync(path)) {
213
214
  disabled = true;
214
215
  log('self-implement.pod', 'ledger-live-skipped', { runId: opts.runId, reason: 'host-ledger-exists' });
215
- return;
216
+ return { ledgerRead: true, activityMeasured: false };
216
217
  }
217
218
  const since = `${Math.floor(lastActivityMeasuredAt / 1000)}.${String(lastActivityMeasuredAt % 1000).padStart(3, '0')}`;
218
219
  const measurementStartedAt = now();
219
220
  const until = `${Math.floor(measurementStartedAt / 1000)}.${String(measurementStartedAt % 1000).padStart(3, '0')}`;
220
221
  const r = opts.exec(`s="\${ELANOUS_STATE_DIR:-$HOME/.elanous}"; f="$s/run-ledger/${opts.runId}.jsonl"; w="$s/worktrees"; if [ -f "$f" ]; then tail -c +${offset + 1} "$f" || exit 1; fi; target=''; matches=0; for tree in "$w"/*/*.worktrees/*; do [ -e "$tree/.git" ] || continue; owner=$(git -C "$tree" config --worktree --get elanous.harness.owner 2>/dev/null) || continue; [ "$owner" = 'dev:${opts.runId}' ] || continue; target="$tree"; matches=$((matches + 1)); done; if [ "$matches" -eq 1 ]; then activity=$(find "$target" -type d \\( -name .git -o -name node_modules \\) -prune -o -type f -newermt '@${since}' ! -newermt '@${until}' -printf . 2>/dev/null) && printf '\\nELANOUS_ACTIVITY %s\\n' "\${#activity}" || printf '\\nELANOUS_ACTIVITY_ERROR\\n'; else printf '\\nELANOUS_ACTIVITY_ERROR\\n'; fi`);
221
- if (r.status !== 0) { log('self-implement.pod', 'ledger-live-unavailable', { runId: opts.runId, status: r.status, stderr: r.stderr.trim().slice(0, 200) }); checkStall(false, false); return; }
222
+ if (r.status !== 0) { log('self-implement.pod', 'ledger-live-unavailable', { runId: opts.runId, status: r.status, stderr: r.stderr.trim().slice(0, 200) }); checkStall(false, false); return { ledgerRead: false, activityMeasured: false }; }
222
223
  const marker = r.stdout.lastIndexOf('\nELANOUS_ACTIVITY');
223
224
  const activityLine = marker < 0 ? '' : r.stdout.slice(marker + 1);
224
225
  const activityMatch = /^ELANOUS_ACTIVITY (0|[1-9]\d*)\n$/.exec(activityLine);
@@ -241,11 +242,11 @@ export function createPodLedgerFollower(opts: {
241
242
  }
242
243
  }
243
244
  }
244
- if (!ledgerBytes) { checkStall(true, activityMeasured); return; }
245
+ if (!ledgerBytes) { checkStall(true, activityMeasured); return { ledgerRead: true, activityMeasured }; }
245
246
  offset += Buffer.byteLength(ledgerBytes);
246
247
  const text = pending + ledgerBytes;
247
248
  const cut = text.lastIndexOf('\n');
248
- if (cut < 0) { pending = text; checkStall(true, activityMeasured); return; }
249
+ if (cut < 0) { pending = text; checkStall(true, activityMeasured); return { ledgerRead: true, activityMeasured }; }
249
250
  const complete = text.slice(0, cut + 1);
250
251
  pending = text.slice(cut + 1);
251
252
  mkdirSync(dir, { recursive: true });
@@ -278,6 +279,7 @@ export function createPodLedgerFollower(opts: {
278
279
  nextStallMinute = stallMinutes;
279
280
  }
280
281
  checkStall(true, activityMeasured);
282
+ return { ledgerRead: true, activityMeasured };
281
283
  },
282
284
  };
283
285
  }
@@ -1,3 +1,100 @@
1
+ import { mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ /** Capture a failed Job against its recorded placement, never the caller's current kubectl context. */
5
+ export function captureFailedPodJobs(input: {
6
+ runId: string;
7
+ job: string;
8
+ childRunId?: string;
9
+ ledgerPath: string;
10
+ artifactDir: string;
11
+ kubectl: (host: string, args: readonly string[]) => { status: number | null; stdout: string; stderr: string };
12
+ record: (event: string, data: Record<string, unknown>) => void;
13
+ now?: () => number;
14
+ }): void {
15
+ const { runId, job, ledgerPath, artifactDir, kubectl, record } = input;
16
+ const capturedAt = new Date((input.now ?? Date.now)()).toISOString();
17
+ const base = { runId, job, capturedAt };
18
+ const finish = (outcome: 'collected' | 'failed' | 'vanished', reason: string, extra: Record<string, unknown> = {}) =>
19
+ record('pod-failure-capture', { ...base, ...extra, outcome, reason });
20
+ if (!/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(job)) { finish('failed', 'job-invalid'); return; }
21
+ if (!/^[a-zA-Z0-9_-]+$/.test(runId)) { finish('failed', 'run-id-invalid'); return; }
22
+ let rows: Array<{ runId?: string; event?: string; data?: Record<string, unknown> }>;
23
+ try {
24
+ rows = readFileSync(ledgerPath, 'utf8').trim().split('\n').map(line => JSON.parse(line));
25
+ if (rows.some(row => !row || typeof row !== 'object' || Array.isArray(row))) throw new Error('invalid ledger row');
26
+ } catch { finish('failed', 'ledger-unavailable'); return; }
27
+ if (rows.some(row => row.runId !== runId)) { finish('failed', 'ledger-run-mismatch'); return; }
28
+ const placements = rows.filter(row => row.event === 'pod-job-applied' && row.data?.job === job).map(row => row.data!);
29
+ if (!placements.length || placements.some(p => !['host', 'context', 'namespace', 'childRunId'].every(key => typeof p[key] === 'string' && /^[a-zA-Z0-9][a-zA-Z0-9.-]*$/.test(p[key] as string))
30
+ || typeof p.jobUid !== 'string' || !/^[a-zA-Z0-9][a-zA-Z0-9-]*$/.test(p.jobUid))) {
31
+ finish('failed', 'placement-incomplete'); return;
32
+ }
33
+ const selected = input.childRunId ? placements.filter(p => p.childRunId === input.childRunId) : placements;
34
+ if (!selected.length) { finish('failed', 'placement-incomplete'); return; }
35
+ const first = selected[0]!;
36
+ if (placements.some(p => ['host', 'context', 'namespace'].some(key => p[key] !== first[key]))
37
+ || selected.some(p => ['childRunId', 'jobUid'].some(key => p[key] !== first[key]))) {
38
+ finish('failed', 'placement-conflict'); return;
39
+ }
40
+ const location = { host: first.host, context: first.context, namespace: first.namespace, childRunId: first.childRunId, jobUid: first.jobUid };
41
+ const evidenceDir = join(artifactDir, 'pod-failure-capture', runId, job, first.childRunId as string);
42
+ const previous = rows.find(row => row.event === 'pod-failure-capture' && row.data?.job === job && row.data?.outcome === 'collected'
43
+ && row.data.childRunId === first.childRunId && row.data.jobUid === first.jobUid
44
+ && row.data.host === first.host && row.data.context === first.context && row.data.namespace === first.namespace);
45
+ // A ledger row describes a prior collection; it does not guarantee that its files survived.
46
+ // Check all three expected paths before treating it as complete, then recapture any missing evidence.
47
+ if (previous && ([['jobEvidence', 'job.json'], ['podEvidence', 'pods.json'], ['logEvidence', 'child.log']] as const)
48
+ .every(([field, filename]) => {
49
+ const path = join(evidenceDir, filename);
50
+ if (previous.data?.[field] !== path) return false;
51
+ try { return statSync(path).isFile(); } catch { return false; }
52
+ })) return;
53
+ const args = ['--context', first.context as string, '-n', first.namespace as string] as const;
54
+ try {
55
+ const jobResult = kubectl(first.host as string, [...args, 'get', 'job', job, '-o', 'json']);
56
+ if (jobResult.status !== 0) {
57
+ finish(/\bNotFound\b|not found/i.test(jobResult.stderr) ? 'vanished' : 'failed',
58
+ /\bNotFound\b|not found/i.test(jobResult.stderr) ? 'job-not-found' : 'job-read-failed', location);
59
+ return;
60
+ }
61
+ const jobBody = JSON.parse(jobResult.stdout) as { metadata?: { name?: string; uid?: string } };
62
+ if (jobBody.metadata?.name !== job || jobBody.metadata.uid !== first.jobUid) { finish('failed', 'job-identity-mismatch', location); return; }
63
+ mkdirSync(evidenceDir, { recursive: true });
64
+ const save = (filename: string, bytes: string, sameJob?: (existing: string) => boolean): string => {
65
+ const path = join(evidenceDir, filename);
66
+ try { writeFileSync(path, bytes, { flag: 'wx', mode: 0o600 }); }
67
+ catch (error) {
68
+ if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
69
+ const existing = readFileSync(path, 'utf8');
70
+ if (existing !== bytes && !sameJob?.(existing)) throw error;
71
+ }
72
+ return path;
73
+ };
74
+ const jobEvidence = save('job.json', jobResult.stdout, existing => {
75
+ try {
76
+ const metadata = (JSON.parse(existing) as { metadata?: { name?: string; uid?: string } }).metadata;
77
+ return metadata?.name === job && metadata.uid === first.jobUid;
78
+ } catch { return false; }
79
+ });
80
+ const podResult = kubectl(first.host as string, [...args, 'get', 'pods', '-l', `job-name=${job}`, '-o', 'json']);
81
+ if (podResult.status !== 0) { finish('failed', 'pods-read-failed', { ...location, jobEvidence }); return; }
82
+ const pods = JSON.parse(podResult.stdout) as { items?: Array<{ metadata?: { name?: string; ownerReferences?: Array<{ uid?: string }> } }> };
83
+ if (!pods.items?.length || pods.items.some(p => !p.metadata?.ownerReferences?.some(owner => owner.uid === jobBody.metadata?.uid))) {
84
+ finish('failed', 'pod-owner-mismatch', { ...location, jobEvidence }); return;
85
+ }
86
+ const podEvidence = save('pods.json', podResult.stdout);
87
+ const podName = pods.items[0]?.metadata?.name;
88
+ if (pods.items.length !== 1 || !podName || !/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(podName)) {
89
+ finish('failed', 'pod-ambiguous', { ...location, jobEvidence, podEvidence }); return;
90
+ }
91
+ const logs = kubectl(first.host as string, [...args, 'logs', `pod/${podName}`, '-c', 'child']);
92
+ if (logs.status !== 0) { finish('failed', 'logs-read-failed', { ...location, jobEvidence, podEvidence }); return; }
93
+ const logEvidence = save('child.log', logs.stdout);
94
+ finish('collected', 'job-failed', { ...location, jobEvidence, podEvidence, logEvidence });
95
+ } catch { finish('failed', 'capture-error', location); }
96
+ }
97
+
1
98
  const SECRET_FIELD = /token|secret|password|authorization|key/i;
2
99
  const SAFE_FIELD = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;
3
100
  const SECRET_VALUE = /(?:\b(?:bearer|basic)\s+\S+|\b(?:token|secret|password|authorization|api[-_]?key|access[-_]?key|private[-_]?key)\b\s*(?:is|[=:])\s*["']?\S+|\b(?:sk-[A-Za-z0-9_-]{8,}|gh[pousr]_[A-Za-z0-9_]{8,}))\b/i;
@@ -193,7 +193,7 @@ export function parsePodPool(spec: string): PodPoolMember[] {
193
193
  // `:N` 이 없으면 «상한 없음»이 아니라 기본 상한 2 다 — 그 사실(`capacityDefaulted`)을 멤버에 남긴다.
194
194
  const capacityDefaulted = m[3] === undefined;
195
195
  const capacity = capacityDefaulted ? POD_POOL_DEFAULT_CAPACITY : Number(m[3]);
196
- // POOL-LITE (10-07): `:0` means «this member is off» — a `node-c:0` entry once threw here and broke every launch.
196
+ // POOL-LITE (10-07): `:0` means «this member is off» — a zero-capacity entry once threw here and broke every launch.
197
197
  // It is dropped below (never chosen); only a pool with no live member left is an error.
198
198
  if (!Number.isInteger(capacity) || capacity < 0) throw new Error(`--pod-pool: 상한은 1 이상 — 「${part}」`);
199
199
  if (m[5] && Number(m[5]) > 65535) throw new Error(`--pod-pool: 레지스트리 포트는 65535 이하 — 「${part}」`);
@@ -243,14 +243,27 @@ export function podWorkLabels(identity: PodWorkIdentity): Record<string, string>
243
243
  };
244
244
  }
245
245
 
246
- /** Minih standard-Job policy, independent of the configured pool member capacity. Not wired into allocation. */
246
+ /**
247
+ * MINIH-MODE (0.2.37) — where this pool's standard Jobs run: `home` (1 standard Job) or `external` (2).
248
+ * The location is declared explicitly by the launch (`minihLocationFrom` reads `ELANOUS_MINIH_LOCATION`); when it
249
+ * is absent the configured member capacities decide, exactly as before.
250
+ */
247
251
  export type MinihLocation = 'home' | 'external';
248
252
 
253
+ /** The declared location for a launch: `home` / `external` only. Absent, empty or unknown → null (configured capacity). */
254
+ export function minihLocationFrom(value: string | undefined | null): MinihLocation | null {
255
+ const normalized = value?.trim().toLowerCase();
256
+ return normalized === 'home' || normalized === 'external' ? normalized : null;
257
+ }
258
+
249
259
  export function minihStandardCapacity(location: MinihLocation): number {
250
260
  return location === 'home' ? 1 : 2;
251
261
  }
252
262
 
253
- /** A drain withholds new slots until occupancy falls to the target; it never evicts Running Pods. */
263
+ /**
264
+ * A drain withholds new slots until occupancy falls to the target; it never evicts Running Pods.
265
+ * `PodPoolScheduler.tryAcquire` applies this to new standard-Job admission when a location is declared.
266
+ */
254
267
  export function decideMinihDrain(location: MinihLocation, runningPods: number, pendingPods = 0): {
255
268
  capacity: number;
256
269
  runningPods: number;
@@ -281,8 +294,8 @@ export interface PodPlacement {
281
294
  }
282
295
 
283
296
  /**
284
- * Preferred members for a placement kind: lite → the smallest-capacity member(s) (node-c), standard → the largest (node-b).
285
- * Capacity is the operator's own statement of member size (`pool-node-b@node-b:40,pool-node-c@node-c:6`), so no new spec syntax.
297
+ * Preferred members for a placement kind: lite → the smallest-capacity member(s), standard → the largest.
298
+ * Capacity is the operator's own statement of member size (`pool-node-a@node-a:40,pool-node-b@node-b:6`), so no new spec syntax.
286
299
  * When every member has the same capacity there is no small/big member and no preference (empty list).
287
300
  */
288
301
  export function preferredMembers(members: readonly PodPoolMember[], kind: PodPlacementKind | null | undefined): string[] {
@@ -582,6 +595,26 @@ export class PodPoolScheduler {
582
595
  recommended: Math.min(this.members.reduce((n, item) => n + item.capacity, 0), snapshots.reduce((n, item) => n + item.recommended, 0)),
583
596
  waiting: snapshots.flatMap((item) => item.waiting) };
584
597
  }
598
+ /**
599
+ * MINIH-MODE — withhold NEW standard-Job admission while the declared location has no free standard slot.
600
+ * Occupancy = the measured node-c Pods (Running ⊕ Pending; a Pending Pod already holds a slot) ⊕ this process's
601
+ * node-c inflight slots. Nothing here deletes, evicts or re-schedules a Pod: an excess above the location's capacity
602
+ * leaves only as those Pods finish, and until then new admissions wait. `lite` Jobs are not the standard
603
+ * admission this policy gates, and no declared location keeps the configured member capacities.
604
+ */
605
+ private minihHold(kind: PodPlacementKind | null, location: MinihLocation | null, reading: Record<string, MemberOccupancy>): { capacity: number; occupied: number; draining: boolean } | null {
606
+ if (!location || kind === 'lite') return null;
607
+ // A launch pool can contain node-b and WSL beside node-c. Their Pods do not occupy node-c's physical slots.
608
+ const minihMembers = this.members.filter((m) => m.capacity > 0 && (m.sshHost === 'node-c' || m.context === 'pool-node-c'));
609
+ if (minihMembers.length === 0) return null;
610
+ // Lite Jobs also hold a physical node-c slot while running, even though this policy never blocks their admission.
611
+ const inflight = minihMembers.reduce((count, m) => count + (this.inflight.get(m.context) ?? 0), 0);
612
+ // The measurement reports Running and Pending as one occupied count, and decideMinihDrain judges only their sum.
613
+ const occupied = minihMembers
614
+ .reduce((count, m) => count + (reading[m.context]?.occupied ?? 0), inflight);
615
+ const decision = decideMinihDrain(location, occupied);
616
+ return decision.availableSlots > 0 ? null : { capacity: decision.capacity, occupied, draining: decision.drainNewAdmissions };
617
+ }
585
618
  /**
586
619
  * Pick the member with the largest free share: (capacity − measured cluster occupancy − this process's inflight) / capacity.
587
620
  * POOL-SPREAD (10-06): comparing absolute free slots always picked the big member (node-b 25 vs node-c 4), so the small
@@ -593,9 +626,10 @@ export class PodPoolScheduler {
593
626
  * member the 0.2.15 gate launched its shards one at a time.
594
627
  * Ties keep the configured member order. Returns null when every readable member is full.
595
628
  */
596
- async tryAcquire(skip?: ReadonlySet<string>, onEmpty?: (reason: string) => void, mode: { selfCapped?: boolean; kind?: PodPlacementKind | null; onPlacement?: (placement: PodPlacement) => void; signal?: AbortSignal } = {}): Promise<PodPoolMember | null> {
629
+ async tryAcquire(skip?: ReadonlySet<string>, onEmpty?: (reason: string) => void, mode: { selfCapped?: boolean; kind?: PodPlacementKind | null; /** MINIH-MODE: a declared location gates new standard-Job admission (home 1 · external 2). */ minihLocation?: MinihLocation | null; onPlacement?: (placement: PodPlacement) => void; signal?: AbortSignal } = {}): Promise<PodPoolMember | null> {
597
630
  const selfCapped = mode.selfCapped === true;
598
631
  const kind = mode.kind ?? null;
632
+ const minihLocation = mode.minihLocation ?? null;
599
633
  // POOL-LITE: lite goals try the small member first, implementation goals the big one; full → overflow by free share.
600
634
  const preferred = new Set(preferredMembers(this.members, kind));
601
635
  let reading: Record<string, MemberOccupancy>;
@@ -608,6 +642,9 @@ export class PodPoolScheduler {
608
642
  }
609
643
  // A caller may abandon a slow occupancy read. Never acquire its lease after cancellation.
610
644
  if (mode.signal?.aborted) throw mode.signal.reason ?? new Error('pod pool acquisition aborted');
645
+ // MINIH-MODE: only node-c candidates are held. Other pool members retain their own configured capacity.
646
+ // Running/Pending Pods already in the pool stay exactly as they are.
647
+ const minihHeld = this.minihHold(kind, minihLocation, reading);
611
648
  const free: Record<string, number> = {};
612
649
  const measured: Record<string, boolean> = {};
613
650
  let best: { member: PodPoolMember; slots: number; measured: boolean; preferred: boolean } | null = null;
@@ -621,6 +658,10 @@ export class PodPoolScheduler {
621
658
  for (const m of this.members) {
622
659
  // A capacity-0 member is off — never chosen (and no division by zero in the share).
623
660
  if (m.capacity <= 0) continue;
661
+ if (minihHeld && (m.sshHost === 'node-c' || m.context === 'pool-node-c')) {
662
+ free[m.context] = 0;
663
+ continue;
664
+ }
624
665
  // LAUNCH-STALL: a member this launch cannot fit is skipped for this call only (other launches may fit it).
625
666
  if (skip?.has(m.context)) continue;
626
667
  const inflight = this.inflight.get(m.context) ?? 0;
@@ -635,7 +676,12 @@ export class PodPoolScheduler {
635
676
  if (better(cand)) best = cand;
636
677
  }
637
678
  if (!best) {
638
- onEmpty?.(`no free slot · free=${JSON.stringify(free)} · measured=${JSON.stringify(measured)}${selfCapped ? ' · selfCapped' : ''}${skip?.size ? ` · skipped=${[...skip].join(',')}` : ''}${kind ? ` · kind=${kind}` : ''}`);
679
+ if (minihHeld) {
680
+ const reason = `node-c 드레인: ${minihLocation} 표준 상한 ${minihHeld.capacity} · 점유 ${minihHeld.occupied}${minihHeld.draining
681
+ ? ' — 초과분이 빠질 때까지 새 입장 보류(기존 Pod 유지)' : ' — 상한이 찼다'}`;
682
+ debug.log('pod.pool', 'node-c-drain-hold', { location: minihLocation, capacity: minihHeld.capacity, occupied: minihHeld.occupied, draining: minihHeld.draining, kind });
683
+ onEmpty?.(reason);
684
+ } else onEmpty?.(`no free slot · free=${JSON.stringify(free)} · measured=${JSON.stringify(measured)}${selfCapped ? ' · selfCapped' : ''}${skip?.size ? ` · skipped=${[...skip].join(',')}` : ''}${kind ? ` · kind=${kind}` : ''}`);
639
685
  return null;
640
686
  }
641
687
  const chosen: { member: PodPoolMember; slots: number; measured: boolean; preferred: boolean } = best;
@@ -842,6 +888,29 @@ export function syncPoolImage(member: PodPoolMember, image: string, localCommit:
842
888
 
843
889
  export type PoolKubectl = (args: readonly string[], input?: string) => { status: number | null; stdout: string; stderr: string };
844
890
 
891
+ /** Read identities for a shadow decision; this never reserves or changes a Pod. */
892
+ export function snapshotWarmPoolMembers(members: readonly PodPoolMember[], kubectl: PoolKubectl):
893
+ Record<string, Array<{ name: string; uid: string; node: string | null }>> | null {
894
+ const result: Record<string, Array<{ name: string; uid: string; node: string | null }>> = {};
895
+ for (const member of members) {
896
+ const observed = kubectl(['--context', member.context, '-n', 'elanous-test', 'get', 'pods', '-l', 'elanous.kind=warm-worker', '-o', 'json']);
897
+ if (observed.status !== 0) return null;
898
+ try {
899
+ const parsed: unknown = JSON.parse(observed.stdout);
900
+ if (!parsed || typeof parsed !== 'object' || !('items' in parsed) || !Array.isArray(parsed.items)) return null;
901
+ const rows: Array<{ name: string; uid: string; node: string | null }> = [];
902
+ for (const item of parsed.items) {
903
+ if (!item || typeof item !== 'object' || !item.metadata || typeof item.metadata.name !== 'string'
904
+ || typeof item.metadata.uid !== 'string' || !item.metadata.name || !item.metadata.uid) return null;
905
+ const node = typeof item.spec?.nodeName === 'string' ? item.spec.nodeName : null;
906
+ rows.push({ name: item.metadata.name, uid: item.metadata.uid, node });
907
+ }
908
+ result[member.context] = rows.sort((a, b) => a.uid.localeCompare(b.uid));
909
+ } catch { return null; }
910
+ }
911
+ return result;
912
+ }
913
+
845
914
  /**
846
915
  * 발사 전 점검 — 노드마다 «컨텍스트가 닿나 · elanous-test 네임스페이스가 있나».
847
916
  * ⛔ 안 되는 노드는 «조용히» 빼지 않는다 — 이유를 낸다. 하나도 안 되면 ok=false.
@@ -926,7 +995,7 @@ export async function syncPoolImages(members: readonly PodPoolMember[], image: s
926
995
  const remoteBuild = deps.remoteBuild ?? (script ? defaultRemoteBuild(script, image) : null);
927
996
  // The registry-less build fallback below calls this ship path, including its remote import result check.
928
997
  const ship = deps.ship ?? ((m: PodPoolMember, img: string, commit: string | null, remote: RemoteRun) =>
929
- syncPoolImage(m, img, commit, { run: remote, inspect: deps.inspect, transfer: deps.transfer }));
998
+ syncPoolImage(m, img, commit, { run: remote, inspect: deps.inspect, transfer: deps.transfer, lock: deps.lock }));
930
999
  const results = new Map<string, PoolImageSync>();
931
1000
  // pack 한 번 ⊕ 나눠 쓰기 — 원격 멤버가 둘 이상일 때만(하나뿐이면 종전대로 build.sh 가 스스로 pack 한다).
932
1001
  // 주입된 remoteBuild(시험)에는 진짜 pack 을 붙이지 않는다 — pack 도 주입해야 쓴다.
@@ -1,9 +1,9 @@
1
1
  /** Fenced warm-Pod lease bridge. The Job remains authoritative. */
2
2
  import { spawn, spawnSync } from 'node:child_process';
3
3
  import { isAbsolute, join } from 'node:path';
4
- import { constants, closeSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
4
+ import { constants, closeSync, existsSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
5
5
  import { tmpdir, userInfo } from 'node:os';
6
- import { dlopen, read } from 'bun:ffi';
6
+ import { dlopen, read, type Pointer } from 'bun:ffi';
7
7
  import { createHash, randomUUID } from 'node:crypto';
8
8
  import { fileURLToPath } from 'node:url';
9
9
  import { debug } from '../../debug/log.js';
@@ -165,7 +165,8 @@ const prepareLockLib = process.platform === 'linux' || process.platform === 'dar
165
165
  [process.platform === 'linux' ? '__errno_location' : '__error']: { args: [], returns: 'ptr' },
166
166
  }) : null;
167
167
  const prepareFlock = prepareLockLib?.symbols.flock ?? null;
168
- const prepareErrno = prepareLockLib?.symbols[process.platform === 'linux' ? '__errno_location' : '__error'];
168
+ const prepareErrnoLocation = prepareLockLib?.symbols[process.platform === 'linux' ? '__errno_location' : '__error'];
169
+ const readPrepareErrno = (): number => (prepareErrnoLocation ? read.i32(prepareErrnoLocation() as Pointer) : -1);
169
170
  const LOCK_EX = 2;
170
171
  const LOCK_NB = 4;
171
172
  const LOCK_UN = 8;
@@ -190,7 +191,7 @@ async function withPrepareLease<T>(candidate: WarmWorkerCandidate, directory: st
190
191
  while (!acquired) {
191
192
  if (signal?.aborted) throw new Error('warm prepare lock wait cancelled');
192
193
  if (prepareFlock(fd, LOCK_EX | LOCK_NB) === 0) { acquired = true; break; }
193
- const errno = prepareErrno ? read.i32(prepareErrno()) : -1;
194
+ const errno = readPrepareErrno();
194
195
  // EWOULDBLOCK/EAGAIN: Linux 11, Darwin 35. EINTR is safe to retry.
195
196
  if (errno !== 11 && errno !== 35 && errno !== 4)
196
197
  throw new Error(`warm prepare flock failed (errno ${errno})`);
@@ -818,11 +819,16 @@ if (import.meta.main) {
818
819
  // Read-only: join the cold CLI legs and the warm gate legs both processes wrote to logs.db.
819
820
  try {
820
821
  const storeMod = await import('../../mss/logging/log-store.js');
821
- const store = storeMod.LogStore.openReadOnly(storeMod.logsDbPath());
822
- try { process.stdout.write(JSON.stringify({ warmReuseTimePairs: readWarmReuseTimePairs(store) }) + '\n'); }
823
- finally { store.close(); }
824
- } catch {
825
- process.stdout.write(JSON.stringify({ warmReuseTimePairs: null, reason: 'logs-unavailable' }) + '\n');
822
+ const dbPath = storeMod.logsDbPath();
823
+ // No logs.db yet means no leg was ever measured: an empty pair list, not a failed query.
824
+ if (!existsSync(dbPath)) process.stdout.write(JSON.stringify({ warmReuseTimePairs: [] }) + '\n');
825
+ else {
826
+ const store = storeMod.LogStore.openReadOnly(dbPath);
827
+ try { process.stdout.write(JSON.stringify({ warmReuseTimePairs: readWarmReuseTimePairs(store) }) + '\n'); }
828
+ finally { store.close(); }
829
+ }
830
+ } catch (error) {
831
+ process.stdout.write(JSON.stringify({ warmReuseTimePairs: null, reason: 'logs-unavailable', detail: String(error) }) + '\n');
826
832
  process.exitCode = 2;
827
833
  }
828
834
  }