elanous 0.2.37 → 0.2.40

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 (727) 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/{8kITumG9GAZ1LwT9POrAR → HjeK7zONHfR7op1hKXp4k}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{1746.3563617ea64023a3.js → 1060.014f1f01a17aec26.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/{9151.a2c51e93f9156dae.js → 1281.d81877e35b9068f4.js} +1 -1
  6. package/apps/pwa/out/_next/static/chunks/1350.59f40e39e02d91b1.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1415.79821ec7ae99acdb.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1452.162bb2d9d699e82a.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/147.f92f63bbc5e85fa5.js +286 -0
  10. package/apps/pwa/out/_next/static/chunks/1537-6bb97b5214928ea9.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/1661.fb425a1d8a065b72.js +149 -0
  12. package/apps/pwa/out/_next/static/chunks/1671-1c22b40b50b208f2.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/1758.0204d3f48783c49d.js +90 -0
  14. package/apps/pwa/out/_next/static/chunks/1806.027bf78b6a94d340.js +104 -0
  15. package/apps/pwa/out/_next/static/chunks/{3578.4e9f9948a9a6f2f4.js → 2047.8f23e210995d4548.js} +2 -2
  16. package/apps/pwa/out/_next/static/chunks/2061-7f2bbc53ae96c88b.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/{7424.c0fe2a595d4232b5.js → 2066.a77257c8c350770a.js} +1 -1
  18. package/apps/pwa/out/_next/static/chunks/2096-a938496bc996dcbd.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/{4931.95cc6dbd230774ca.js → 2177.df8547f9d7215c4e.js} +16 -16
  20. package/apps/pwa/out/_next/static/chunks/2200-9a453b8f99687d49.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/{6680.6a693c28aae4cd19.js → 2226.2cf61ec781827219.js} +2 -2
  22. package/apps/pwa/out/_next/static/chunks/2281-b8cede695c6c650e.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/2499.02fbfd18126c10c0.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/{6252.240578f10952e091.js → 2522.f2766b76d22c8bdf.js} +1 -1
  25. package/apps/pwa/out/_next/static/chunks/{1262.a89bb27a5b2f844e.js → 2566.beaffc3a5e1fcd2e.js} +1 -1
  26. package/apps/pwa/out/_next/static/chunks/261.0d471e8aded799f3.js +226 -0
  27. package/apps/pwa/out/_next/static/chunks/2615.3c9738153de3ef3f.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2727.e703d99ac414916d.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/2752.7904b8df0254d0ae.js +27 -0
  30. package/apps/pwa/out/_next/static/chunks/2762-5051a862dfa40e99.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/2767-31784bfedf8e245d.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/{2318-450e75d07605b8fe.js → 2774-719820e3f5b2a3eb.js} +1 -1
  33. package/apps/pwa/out/_next/static/chunks/2812-f016aa0dda149287.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/{4725-34bed25720cc326b.js → 2829-734fe8bf2eee7347.js} +1 -1
  35. package/apps/pwa/out/_next/static/chunks/311-32dcac79970985e8.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/3175-f93f7495cd28253c.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/3184.8da993f5502d2140.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/3231-d7c981db0ff8bdcc.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/3250.151309c5176d6b4b.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/3330.7443183b7e6fdbdf.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/3517.a5aa1797a9dbf2ee.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/3567-45f676877ed9e8a5.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/3746-f80a78f220bf37b5.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/3819.8a1de79b2e8c7298.js +24 -0
  45. package/apps/pwa/out/_next/static/chunks/39.9b37b0be4c128a83.js +77 -0
  46. package/apps/pwa/out/_next/static/chunks/{4337e36d.f42c3b391b3d8f20.js → 3b355b3d.6f73c98bf6f9c749.js} +1 -1
  47. package/apps/pwa/out/_next/static/chunks/4023.a19045970a9ce16a.js +36 -0
  48. package/apps/pwa/out/_next/static/chunks/{58edce7d.f1b0c72acf13b123.js → 409c787b.e48c5bec8ec10456.js} +1 -1
  49. package/apps/pwa/out/_next/static/chunks/413-24c8284bda08241d.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/4135.e45d6ebc29e20647.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/4283.65502f5d0e6ff6c0.js +287 -0
  52. package/apps/pwa/out/_next/static/chunks/4305-4e28fa92a7b34a5b.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/4335-50efa61fd7613142.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/4351.0f37a6db2b34d50b.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/{5046.7fd5cbd859299448.js → 470.51a72a2348306455.js} +2 -2
  56. package/apps/pwa/out/_next/static/chunks/4720.ecbd1693fa151068.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/{2375.bf364c85dbb6f085.js → 4769.9fd582071301f382.js} +1 -1
  58. package/apps/pwa/out/_next/static/chunks/483.725e4a61be922788.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/{de8446d3.31b73eb239fecd7b.js → 4864a2ca.1c1c280a7772c5e5.js} +1 -1
  60. package/apps/pwa/out/_next/static/chunks/4969-4b209d7307410c00.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/5068-c7f53862f83c0917.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/5315.bc96711c24dfefe2.js +74 -0
  63. package/apps/pwa/out/_next/static/chunks/{4383.18e5c54db3a22c88.js → 5383.bdfa5d886e39a826.js} +1 -1
  64. package/apps/pwa/out/_next/static/chunks/5436.fdf6fa8ae3e9588d.js +64 -0
  65. package/apps/pwa/out/_next/static/chunks/5542.efbf14f16baed4dd.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/5622.94893f5aea7185d0.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/5644.8f401a25601d05fc.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/5658.35f70eb9871a54bf.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/5721.7bdc0e904c76ab51.js +8 -0
  70. package/apps/pwa/out/_next/static/chunks/5790-eef0ca20884a6bdf.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/5802.ef0d8598ca68fe84.js +62 -0
  72. package/apps/pwa/out/_next/static/chunks/5890.fb974729788dcb71.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/5924.5ef25cddd36d5dbf.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/5979-e59c099ebf8d1f35.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/6020.714cfeada2315e2e.js +94 -0
  76. package/apps/pwa/out/_next/static/chunks/6027-dfe0349d8c50f698.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/{6745-5a63492933959dd9.js → 6051-0ae955eedb10adb9.js} +3 -3
  78. package/apps/pwa/out/_next/static/chunks/6178.bc4730c86cf74536.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/{2283.edd9627b7bdfb546.js → 6257.c0037b8949239f5e.js} +2 -2
  80. package/apps/pwa/out/_next/static/chunks/6273.d808b0dcb3c2b155.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/6292.6ef1df64fa6a4c8a.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/647-af8dd920ba46533c.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/6541-170dc5c59e135c0c.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/6624.981ef7caeb680560.js +32 -0
  85. package/apps/pwa/out/_next/static/chunks/6742-a1ad4a94ba7bc37a.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/{6819.be5c9c4d67f22922.js → 6749.0744ce6eb08d40e9.js} +16 -16
  87. package/apps/pwa/out/_next/static/chunks/6836-493dc55c39187949.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/6856-53ac421c06039a18.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/6908.06008c67f6380ea9.js +148 -0
  90. package/apps/pwa/out/_next/static/chunks/6915.096931864206f522.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/6925.5e181fb7dcf90193.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/6cc1d1d6.9a98dd4622f1afc6.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/7203-800e11037adc41eb.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/7219-15bdd9a80661af8e.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/7279.6170f591105936bb.js +93 -0
  96. package/apps/pwa/out/_next/static/chunks/7349-373165c7fbdcc3df.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/7514-a533ba0ca1cae4f4.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/7515.1ad6efe157fa4448.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/7521.d919cc1fab80c1d9.js +24 -0
  100. package/apps/pwa/out/_next/static/chunks/7538.90e97bbdf9178cff.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/7611.85f045bf232053ac.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/7825-39203196a12579b2.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/7869.5e1a9a12fd70d6c7.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8086.1ab9103c41be884f.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/8099.1cccf31c1baa7e1c.js +1 -0
  106. package/apps/pwa/out/_next/static/chunks/8179-e61fffbdb70f1152.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/8265.bdeb991b34db6a06.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/{2002-a43a3f380df873ec.js → 8272-025653884f9645d2.js} +1 -1
  109. package/apps/pwa/out/_next/static/chunks/{7867.e8725745ddd1d940.js → 8357.1aaa713b052a78b1.js} +4 -4
  110. package/apps/pwa/out/_next/static/chunks/8638-e42615f2166165b5.js +1 -0
  111. package/apps/pwa/out/_next/static/chunks/8639-92c11d069792429e.js +1 -0
  112. package/apps/pwa/out/_next/static/chunks/8691.54e22831d5e8ccf8.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/8d173e38.18a2f7b4e1848bdb.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/9069.657590a84afb8fac.js +41 -0
  115. package/apps/pwa/out/_next/static/chunks/9125-ae588d88a0293b93.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/{4313.76f232c304a15bde.js → 9175.2de5209a5ce6f68a.js} +1 -1
  117. package/apps/pwa/out/_next/static/chunks/9184.45afbf44fec87574.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/{8c58d09b.e8724a299658d86f.js → 9189505a.980427ac048c8c60.js} +1 -1
  119. package/apps/pwa/out/_next/static/chunks/9311.f9f641a46a508611.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/9371-a6fee8378ebfa3ee.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/9421-3aa9ff4fa7f63d8a.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/9992.07ce73c10bd5e6a1.js +79 -0
  123. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-76394dc618d322c8.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/approvals/page-1b948637c37b3abd.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-c63400f1bcf3e47c.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/board/page-95d0957267819586.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/botlab/page-ba957ff875a026a0.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/bots/page-bb6fd0714879cb87.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/ceo/page-8f9038520d2acbef.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/chat/page-d4b48397e05fce2c.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/consult/page-72d5744245dc0c27.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/control/page-37625dc8300fa43a.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/decisions/page-fbef9d160272dbc1.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/design-check/page-58424168df84928e.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/editor/page-72c12ba430ac57c0.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/exec/page-d53f3e9ed806d7e0.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/field/page-4d2fbd45feee0829.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/healthz/route-76394dc618d322c8.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/inside/page-8783dc0ed7590a2c.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/intake/page-3706cb390606e930.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/layout-9b0a86598e0111fd.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/live/{page-3337c6a1328c2674.js → page-4edb8df90f4537ce.js} +1 -1
  143. package/apps/pwa/out/_next/static/chunks/app/live-run/page-83d27757e2b8060a.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/loops/page-47398b9ba1cc5e1b.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/market/page-fb86f55cfe2e681d.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e22345fc8fd47b1b.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/morning/{page-1e5da297557d2354.js → page-fca2e59a81744d9e.js} +1 -1
  148. package/apps/pwa/out/_next/static/chunks/app/not-found-6e96bda63100b31e.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/observatory/page-4d17ad8e0db8cea4.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-121f92766c33beaa.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-43f14212c545f92d.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ccec2023e7a974ec.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/outputs/page-b04f33f548b89a94.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/page-c8792823c0917021.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/reflection/page-8c0e32782ceef4cb.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-94bd3922eb230ce8.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/sessions/page-bca565887bafa03c.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-ac1dffafe649fc6c.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/settings/page-52374edb5e23e455.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ad4ebca120d43ca7.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/layout-76394dc618d322c8.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/setup/page-718b89ce4b4ae4e4.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/share/page-204eca33eeeaddec.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/showroom/page-e58ba9bcdb9ac0f9.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/tasks/page-98f6dd50bafd7734.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/term/{page-ac2c1e6169cfce57.js → page-139d722de1dcd6c1.js} +1 -1
  167. package/apps/pwa/out/_next/static/chunks/app/today/page-5a4bdd72bff27057.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/trace/page-6f6ab17d44f984ce.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/vault/page-aabb7bd15fb72da9.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-e4322319164a4436.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workflows/page-e9efc0c8f32e2371.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/workspace/{page-c1c80e345fa9d6f5.js → page-ccde99f07aaf71f7.js} +1 -1
  173. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-80464aced9614390.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/{d9815f84.4918f682a45a8502.js → b84cad00.594027ebe0531fe2.js} +3 -3
  175. package/apps/pwa/out/_next/static/chunks/c696986b.5dc8bb1143af16d1.js +1 -0
  176. package/apps/pwa/out/_next/static/chunks/{709a918c.4fd3b4e2a32cb4e3.js → ca3702f8.2913182e73889fa2.js} +1 -1
  177. package/apps/pwa/out/_next/static/chunks/d5d3cefc-373c4eb722ed848b.js +1 -0
  178. package/apps/pwa/out/_next/static/chunks/{5f6af561-f90b7a67f4f75d13.js → d8de0de2-dd0f4ac8e5ab1832.js} +1 -1
  179. package/apps/pwa/out/_next/static/chunks/framework-002b472a66935d31.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-49b88d47292c0476.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/main-app-fff2549cb10a133f.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_app-32abbbba3f3b76b7.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/pages/_error-9d9cfef60c60dd7a.js +1 -0
  184. package/apps/pwa/out/_next/static/chunks/webpack-cc7f483fa294034a.js +1 -0
  185. package/apps/pwa/out/approvals/index.html +1 -1
  186. package/apps/pwa/out/approvals/index.txt +22 -22
  187. package/apps/pwa/out/autopilot/index.html +1 -1
  188. package/apps/pwa/out/autopilot/index.txt +22 -22
  189. package/apps/pwa/out/board/index.html +1 -1
  190. package/apps/pwa/out/board/index.txt +22 -22
  191. package/apps/pwa/out/botlab/index.html +1 -1
  192. package/apps/pwa/out/botlab/index.txt +21 -21
  193. package/apps/pwa/out/bots/index.html +1 -1
  194. package/apps/pwa/out/bots/index.txt +21 -21
  195. package/apps/pwa/out/ceo/index.html +1 -1
  196. package/apps/pwa/out/ceo/index.txt +22 -22
  197. package/apps/pwa/out/chat/index.html +1 -1
  198. package/apps/pwa/out/chat/index.txt +21 -21
  199. package/apps/pwa/out/consult/index.html +1 -1
  200. package/apps/pwa/out/consult/index.txt +22 -22
  201. package/apps/pwa/out/control/index.html +1 -1
  202. package/apps/pwa/out/control/index.txt +22 -22
  203. package/apps/pwa/out/decisions/index.html +1 -1
  204. package/apps/pwa/out/decisions/index.txt +21 -21
  205. package/apps/pwa/out/design-check/index.html +1 -1
  206. package/apps/pwa/out/design-check/index.txt +22 -22
  207. package/apps/pwa/out/editor/index.html +1 -1
  208. package/apps/pwa/out/editor/index.txt +22 -22
  209. package/apps/pwa/out/exec/index.html +1 -1
  210. package/apps/pwa/out/exec/index.txt +22 -22
  211. package/apps/pwa/out/field/index.html +1 -1
  212. package/apps/pwa/out/field/index.txt +22 -22
  213. package/apps/pwa/out/healthz +1 -1
  214. package/apps/pwa/out/index.html +1 -1
  215. package/apps/pwa/out/index.txt +21 -21
  216. package/apps/pwa/out/inside/index.html +1 -1
  217. package/apps/pwa/out/inside/index.txt +21 -21
  218. package/apps/pwa/out/intake/index.html +1 -1
  219. package/apps/pwa/out/intake/index.txt +22 -22
  220. package/apps/pwa/out/live/index.html +1 -1
  221. package/apps/pwa/out/live/index.txt +22 -22
  222. package/apps/pwa/out/live-run/index.html +1 -1
  223. package/apps/pwa/out/live-run/index.txt +22 -22
  224. package/apps/pwa/out/loops/index.html +1 -1
  225. package/apps/pwa/out/loops/index.txt +21 -21
  226. package/apps/pwa/out/market/index.html +1 -1
  227. package/apps/pwa/out/market/index.txt +21 -21
  228. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  229. package/apps/pwa/out/missions/placeholder/index.txt +21 -21
  230. package/apps/pwa/out/morning/index.html +1 -1
  231. package/apps/pwa/out/morning/index.txt +22 -22
  232. package/apps/pwa/out/observatory/index.html +1 -1
  233. package/apps/pwa/out/observatory/index.txt +22 -22
  234. package/apps/pwa/out/ops/checklist/index.html +1 -1
  235. package/apps/pwa/out/ops/checklist/index.txt +21 -21
  236. package/apps/pwa/out/ops/release/index.html +1 -1
  237. package/apps/pwa/out/ops/release/index.txt +21 -21
  238. package/apps/pwa/out/ops/seats/index.html +1 -1
  239. package/apps/pwa/out/ops/seats/index.txt +21 -21
  240. package/apps/pwa/out/outputs/index.html +1 -1
  241. package/apps/pwa/out/outputs/index.txt +21 -21
  242. package/apps/pwa/out/reflection/index.html +1 -1
  243. package/apps/pwa/out/reflection/index.txt +22 -22
  244. package/apps/pwa/out/scheduler/index.html +1 -1
  245. package/apps/pwa/out/scheduler/index.txt +22 -22
  246. package/apps/pwa/out/sessions/index.html +1 -1
  247. package/apps/pwa/out/sessions/index.txt +21 -21
  248. package/apps/pwa/out/settings/devices/index.html +1 -1
  249. package/apps/pwa/out/settings/devices/index.txt +22 -22
  250. package/apps/pwa/out/settings/index.html +1 -1
  251. package/apps/pwa/out/settings/index.txt +22 -22
  252. package/apps/pwa/out/setup/done/index.html +1 -1
  253. package/apps/pwa/out/setup/done/index.txt +22 -22
  254. package/apps/pwa/out/setup/index.html +1 -1
  255. package/apps/pwa/out/setup/index.txt +22 -22
  256. package/apps/pwa/out/share/index.html +1 -1
  257. package/apps/pwa/out/share/index.txt +22 -22
  258. package/apps/pwa/out/showroom/index.html +1 -1
  259. package/apps/pwa/out/showroom/index.txt +21 -21
  260. package/apps/pwa/out/tasks/index.html +1 -1
  261. package/apps/pwa/out/tasks/index.txt +22 -22
  262. package/apps/pwa/out/term/index.html +1 -1
  263. package/apps/pwa/out/term/index.txt +22 -22
  264. package/apps/pwa/out/today/index.html +1 -1
  265. package/apps/pwa/out/today/index.txt +22 -22
  266. package/apps/pwa/out/trace/index.html +1 -1
  267. package/apps/pwa/out/trace/index.txt +22 -22
  268. package/apps/pwa/out/vault/index.html +1 -1
  269. package/apps/pwa/out/vault/index.txt +22 -22
  270. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  271. package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
  272. package/apps/pwa/out/workflows/index.html +1 -1
  273. package/apps/pwa/out/workflows/index.txt +22 -22
  274. package/apps/pwa/out/workspace/index.html +1 -1
  275. package/apps/pwa/out/workspace/index.txt +22 -22
  276. package/apps/pwa/out/worktrees/index.html +1 -1
  277. package/apps/pwa/out/worktrees/index.txt +22 -22
  278. package/graphs/demo/persona-marketer/fixtures/marketer-scenario.yaml +10 -0
  279. package/graphs/demo/persona-marketer/persona-marketer.yaml +25 -0
  280. package/graphs/demo/persona-marketer/recipes.yaml +19 -0
  281. package/graphs/release/recipes.yaml +9 -0
  282. package/graphs/release/release-loop.yaml +26 -18
  283. package/graphs/video/explainer-line.yaml +4 -1
  284. package/package.json +1 -1
  285. package/scripts/ask-op-inbox-once.ts +17 -0
  286. package/scripts/brand/check.ts +13 -4
  287. package/scripts/dead-code/report.ts +169 -0
  288. package/scripts/gate-0226-accepted-remaining-90.txt +91 -0
  289. package/scripts/gate-0226-failure-census.ts +39 -0
  290. package/scripts/intake-collect-telegram-saved-stage.ts +48 -0
  291. package/scripts/lib/morning-report-options.ts +3 -4
  292. package/scripts/lib/tui-regress-evidence.ts +56 -0
  293. package/scripts/morning-report.ts +5 -1
  294. package/scripts/provision-export-redactions.ts +15 -0
  295. package/scripts/public-copy-lint.ts +122 -0
  296. package/scripts/public-export.ts +77 -2
  297. package/scripts/ratchets/next-tui-entry-budget.ts +124 -0
  298. package/scripts/ratchets/train-side-invariants.sh +7 -1
  299. package/scripts/release-loop/cut-branch.ts +61 -12
  300. package/scripts/release-loop/export-check-node.ts +21 -3
  301. package/scripts/release-loop/gate-node.ts +527 -60
  302. package/scripts/release-loop/gate-partial.ts +82 -3
  303. package/scripts/release-loop/gate-prewarm-plan.ts +35 -0
  304. package/scripts/release-loop/gate-radius-live.ts +4 -2
  305. package/scripts/release-loop/gate-shard-dispatch.ts +128 -0
  306. package/scripts/release-loop/gate-shard-receipts.ts +89 -0
  307. package/scripts/release-loop/gate-wall.ts +5 -5
  308. package/scripts/release-loop/preflight.ts +6 -2
  309. package/scripts/release-loop/resume-cap.ts +2 -1
  310. package/scripts/release-loop/resume-release.ts +317 -13
  311. package/scripts/release-loop/resume.ts +61 -5
  312. package/scripts/release-loop/traffic-node.ts +12 -4
  313. package/scripts/release-loop/traffic-transition.ts +53 -13
  314. package/scripts/release-loop/version-node.ts +35 -5
  315. package/scripts/release-story/draft.ts +1 -1
  316. package/scripts/research-bench/README.md +53 -0
  317. package/scripts/research-bench/questions.json +66 -0
  318. package/scripts/research-bench/rubric.md +33 -0
  319. package/scripts/research-bench/run.ts +523 -0
  320. package/scripts/tui-regress.ts +85 -10
  321. package/scripts/ux-sim.ts +1 -1
  322. package/scripts/video-pipeline.ts +2 -2
  323. package/src/acp/core-turn-bridge.ts +14 -1
  324. package/src/agent/autonomous-tools.ts +10 -3
  325. package/src/agent/monad-agent-turn.ts +4 -1
  326. package/src/agent/registry.ts +26 -2
  327. package/src/agent/run-context.ts +1 -1
  328. package/src/agent/shared-app-tools.ts +1 -1
  329. package/src/agent/surface-ux/build.ts +12 -3
  330. package/src/agent/surface-ux/types.ts +1 -1
  331. package/src/agent/task-notification.ts +78 -7
  332. package/src/agent/types.ts +3 -0
  333. package/src/agent-mission/driver.ts +29 -3
  334. package/src/agent-mission/mission-cli.ts +2 -1
  335. package/src/agent-substrate/review-context-pack.ts +365 -0
  336. package/src/agent-substrate/review-observation.ts +10 -0
  337. package/src/agent-substrate/self-review-cli.ts +419 -42
  338. package/src/ask-user-question/index.ts +6 -0
  339. package/src/ask-user-question/inline-resolver.ts +49 -0
  340. package/src/auto-research/question-ladder-contract.ts +156 -0
  341. package/src/auto-research/research-turn.ts +467 -0
  342. package/src/autopilot/agent-loop.ts +1 -1
  343. package/src/autopilot/discovery/doc-lint.ts +1 -1
  344. package/src/autopilot/pty-control-loop.ts +8 -1
  345. package/src/boot/daemon-tools/index.ts +4 -3
  346. package/src/boot/daemon-tools/self-implement.ts +270 -2
  347. package/src/boot/daemon-tools/types.ts +2 -0
  348. package/src/bots/screen-probe.ts +1 -1
  349. package/src/budget/llm-cost.ts +44 -5
  350. package/src/budget/unified-usage.ts +17 -1
  351. package/src/capture/tui-self-report.ts +24 -2
  352. package/src/chat/index.ts +34 -7
  353. package/src/chat/tool-render/dispatch.ts +8 -1
  354. package/src/cli/agent-cli.ts +43 -8
  355. package/src/cli/guardian-cli.ts +3 -1
  356. package/src/cli/intake-cli.ts +70 -13
  357. package/src/cli/lesson-cli.ts +86 -10
  358. package/src/cli/log-event-names.ts +1 -0
  359. package/src/cli/pod-cli.ts +17 -2
  360. package/src/cli/pr-land-overlap-evidence.ts +38 -5
  361. package/src/cli/pr-land-refusal.ts +1 -1
  362. package/src/cli/pty-takeover-cli.ts +125 -12
  363. package/src/cli/release-checklist-read-cli.ts +5 -2
  364. package/src/cli/release-cli.ts +99 -15
  365. package/src/cli/repo-cli.ts +2 -1
  366. package/src/cli/review-carry.ts +77 -0
  367. package/src/cli/self-update.ts +1 -1
  368. package/src/cli/tasks-card-show.ts +31 -1
  369. package/src/cli/tasks-cli.ts +98 -32
  370. package/src/cli/tasks-read-cli.ts +13 -5
  371. package/src/context-bus/context-now.ts +86 -2
  372. package/src/control-plane/ledger.ts +21 -4
  373. package/src/core-turn/run-core-turn.ts +51 -3
  374. package/src/core-turn/run-goal-loop.ts +152 -8
  375. package/src/core-turn/types.ts +12 -0
  376. package/src/dashboard/chat/acp-chat.ts +2 -2
  377. package/src/dashboard/classic-sigquit.ts +16 -0
  378. package/src/dashboard/index.ts +339 -44
  379. package/src/dashboard/input/chat-main-interaction-opts.ts +3 -0
  380. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +7 -2
  381. package/src/dashboard/input/chat-main-slash-command.ts +7 -0
  382. package/src/dashboard/input/dashboard-plain-turn-stream.ts +4 -0
  383. package/src/dashboard/input/dashboard-priority-key-route.ts +21 -5
  384. package/src/dashboard/input/external-input-host.ts +2 -0
  385. package/src/dashboard/input/terminal-modal-router.ts +8 -159
  386. package/src/dashboard/side-basic-runtime.ts +61 -0
  387. package/src/dashboard/sidetab-runtime.ts +228 -0
  388. package/src/dashboard/slash-runtime/ask-clarification.ts +7 -1
  389. package/src/dashboard/slash-runtime/dashboard-handlers.ts +22 -8
  390. package/src/dashboard/slash-runtime/help-from-registry.ts +2 -2
  391. package/src/dashboard/term-slash-runtime.ts +18 -18
  392. package/src/dashboard/turn-preamble.ts +30 -4
  393. package/src/domains/guardian-tick.ts +9 -0
  394. package/src/domains/knowledge.ts +1 -1
  395. package/src/esc-abort-gate.ts +33 -1
  396. package/src/execution-loop/launch-gate.ts +206 -1
  397. package/src/git-fs/watcher.ts +27 -22
  398. package/src/git-fs/worktree.ts +19 -3
  399. package/src/graph-runner/graph-cli.ts +6 -4
  400. package/src/graph-runner/graph-grow.ts +37 -9
  401. package/src/graph-runner/runner.ts +48 -11
  402. package/src/graph-wizard/generate.ts +16 -7
  403. package/src/graph-wizard/mission-compose.ts +201 -0
  404. package/src/grok/agent-search.ts +8 -2
  405. package/src/grok/credential.ts +112 -36
  406. package/src/grounding/ref-index.ts +11 -0
  407. package/src/grounding/ref-vector.ts +61 -0
  408. package/src/grounding/sources-cli.ts +2 -2
  409. package/src/grounding/sources.ts +15 -1
  410. package/src/harness/harness-cli-command.ts +38 -9
  411. package/src/harness/harness-pod-dispatch.ts +7 -1
  412. package/src/hq/fence-wrapper.ts +1 -1
  413. package/src/hq/transfer.ts +1 -1
  414. package/src/index.ts +60 -16
  415. package/src/input/external-input-queue.ts +10 -0
  416. package/src/intake-plane/collect-telegram-saved.ts +90 -6
  417. package/src/intake-plane/intake-to-tasks.ts +246 -3
  418. package/src/interactive-terminal-modal.ts +3 -0
  419. package/src/keybindings.ts +17 -22
  420. package/src/lessons/friction-ledger.ts +154 -0
  421. package/src/live-supply/collector.ts +149 -0
  422. package/src/live-supply/probes.ts +52 -0
  423. package/src/live-supply/snapshot.ts +40 -0
  424. package/src/llm/provider-credentials.ts +1 -1
  425. package/src/llm-tool-schema.ts +20 -0
  426. package/src/llm.ts +486 -61
  427. package/src/loops/orchestrator/inbox-once.ts +28 -0
  428. package/src/loops/orchestrator/inbox.ts +130 -1
  429. package/src/loops/orchestrator/tick.ts +14 -3
  430. package/src/maturity/feature-maturity.ts +1 -0
  431. package/src/nexus/api/channel-ask-op.ts +96 -0
  432. package/src/nexus/api/discord-trigger-bot.ts +28 -1
  433. package/src/nexus/api/harness-api.ts +2 -1
  434. package/src/nexus/api/telegram-trigger-bot.ts +24 -0
  435. package/src/nexus/api/terminals.ts +28 -6
  436. package/src/patents/ledger.ts +18 -1
  437. package/src/preview/terminal.ts +4 -2
  438. package/src/prompt-cache/anthropic.ts +2 -0
  439. package/src/prompt-enhance/enhance.ts +5 -0
  440. package/src/pty/snapshot.py +132 -0
  441. package/src/pty-shell/pty-control-ipc.ts +29 -2
  442. package/src/pty-shell/pty-manifest.ts +75 -5
  443. package/src/pty-shell/registry.ts +2 -0
  444. package/src/pty-shell/tui-run-state.ts +80 -3
  445. package/src/release-loop/gate-host-reservation.ts +13 -4
  446. package/src/release-loop/gate-shard-ledger.ts +68 -0
  447. package/src/release-loop/gate-shards.ts +61 -0
  448. package/src/release-loop/light-rc.ts +7 -4
  449. package/src/release-loop/merged-pr-checklist.ts +25 -2
  450. package/src/release-loop/rehearsal-shard-plan.ts +6 -1
  451. package/src/release-loop/rehearsal.ts +28 -1
  452. package/src/repl/index.ts +10 -0
  453. package/src/research/axis-fanout.ts +133 -0
  454. package/src/research/edge-decision.ts +197 -0
  455. package/src/research/gap-followup.ts +249 -0
  456. package/src/research/lenses/counter-evidence.ts +100 -0
  457. package/src/research/lenses/working-backwards.ts +89 -0
  458. package/src/research/output-router.ts +127 -0
  459. package/src/research/plugin-discovery.ts +242 -0
  460. package/src/research-bridge/format.ts +16 -1
  461. package/src/research-bridge/index.ts +1 -0
  462. package/src/research-bridge/invoke.ts +45 -7
  463. package/src/research-bridge/types.ts +2 -0
  464. package/src/self-dev/ask-launch-flow.ts +34 -106
  465. package/src/self-dev/decompose.ts +45 -9
  466. package/src/self-dev/dev-cli.ts +2 -0
  467. package/src/self-dev/dev-pipeline.ts +8 -4
  468. package/src/self-dev/draft-residue.ts +221 -2
  469. package/src/self-dev/draft-sweep.ts +29 -16
  470. package/src/self-dev/graph-journey-route.ts +10 -2
  471. package/src/self-dev/launch-preflight-text.ts +2 -2
  472. package/src/self-dev/launch-preflight.ts +38 -7
  473. package/src/self-dev/orchestrate-cli.ts +1 -0
  474. package/src/self-dev/orchestrate.ts +1 -0
  475. package/src/self-dev/run-supervisor.ts +41 -7
  476. package/src/self-dev/shard-run.ts +260 -3
  477. package/src/self-implement/comment-only-source.ts +54 -0
  478. package/src/self-implement/device-first.ts +8 -2
  479. package/src/self-implement/doctor-visit.ts +100 -57
  480. package/src/self-implement/gate-artifacts.ts +5 -2
  481. package/src/self-implement/gate-baseline.ts +119 -13
  482. package/src/self-implement/gate-cli.ts +466 -93
  483. package/src/self-implement/gate-failure-signature.ts +44 -0
  484. package/src/self-implement/gate-receipt-registry.ts +180 -0
  485. package/src/self-implement/gate-remote.ts +147 -22
  486. package/src/self-implement/goal-author-clarification.ts +178 -24
  487. package/src/self-implement/goal-author-decompose.ts +24 -15
  488. package/src/self-implement/goal-author.ts +82 -7
  489. package/src/self-implement/goal-doc/section.ts +22 -0
  490. package/src/self-implement/goal-supervisor-fill.ts +38 -4
  491. package/src/self-implement/host-regate.ts +120 -45
  492. package/src/self-implement/impl-findings-feedback.ts +188 -0
  493. package/src/self-implement/knowhow-pack.ts +301 -9
  494. package/src/self-implement/l8-merge-queue.ts +198 -32
  495. package/src/self-implement/off-diff-evidence.ts +147 -0
  496. package/src/self-implement/orchestrator.ts +137 -16
  497. package/src/self-implement/pod-inplace-worktree.ts +82 -0
  498. package/src/self-implement/pod-warm-pool-profile.ts +2 -2
  499. package/src/self-implement/premise-review.ts +7 -5
  500. package/src/self-implement/run-ledger.ts +304 -8
  501. package/src/self-implement/run-stop.ts +12 -0
  502. package/src/self-implement/seams.ts +39 -13
  503. package/src/self-implement/self-implement-cli.ts +30 -4
  504. package/src/session/channel-ask-op-envelope.ts +36 -0
  505. package/src/session/channel-op-reply-route.ts +200 -0
  506. package/src/session/chat.ts +92 -15
  507. package/src/session/index.ts +2 -0
  508. package/src/session-runtime/index.ts +9 -22
  509. package/src/session-runtime/provider-circuit.ts +140 -0
  510. package/src/shell-runner/file-engine.ts +1 -0
  511. package/src/shell-runner/types.ts +4 -0
  512. package/src/sidetab-core/model.ts +176 -0
  513. package/src/skills/runner.ts +7 -3
  514. package/src/skills/tools/dev-harness.ts +2 -2
  515. package/src/skills/tools/schedule-wakeup.ts +24 -7
  516. package/src/skills/tools/shell.ts +51 -5
  517. package/src/status/bar.ts +3 -3
  518. package/src/steward/directive-audit.ts +354 -0
  519. package/src/storage/file-lock.ts +17 -4
  520. package/src/surface/child-process.ts +31 -0
  521. package/src/surface/tui-stderr-owner.ts +48 -0
  522. package/src/task-agent/adopt-pr.ts +63 -2
  523. package/src/task-agent/cover.ts +30 -12
  524. package/src/task-agent/evidence-command-normalize.ts +42 -0
  525. package/src/task-agent/export-redactions-provision.ts +43 -0
  526. package/src/task-agent/judge.ts +56 -0
  527. package/src/task-agent/parent-host.ts +59 -12
  528. package/src/task-agent/postland-card.ts +35 -2
  529. package/src/task-agent/rejudge-on-head.ts +46 -2
  530. package/src/task-agent/repair-cycle.ts +68 -9
  531. package/src/task-agent/repair-worker.ts +37 -1
  532. package/src/task-agent/task-hand.ts +226 -33
  533. package/src/task-agent/tier-split.ts +142 -24
  534. package/src/task-orchestrator/surfaces/pod-command-job.ts +330 -2
  535. package/src/task-orchestrator/surfaces/pod-config-snapshot.ts +123 -0
  536. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +57 -5
  537. package/src/task-orchestrator/surfaces/pod-mirror-seed.ts +11 -8
  538. package/src/task-orchestrator/surfaces/pod-pool.ts +27 -4
  539. package/src/task-orchestrator/surfaces/pod-si-warm-protocol.ts +1 -1
  540. package/src/task-orchestrator/surfaces/pod-source-receive.ts +74 -6
  541. package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +2 -1
  542. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +56 -15
  543. package/src/task-orchestrator/surfaces/self-implement-pod.ts +310 -79
  544. package/src/task-orchestrator/surfaces/self-implement.ts +33 -2
  545. package/src/terminal/session-registry.ts +1 -0
  546. package/src/terminal/terminal-modal-router.ts +156 -0
  547. package/src/tool-hints/gate.ts +6 -4
  548. package/src/tool-hints/signals.ts +33 -1
  549. package/src/tool-runtime/skill-exec-runtime.ts +95 -5
  550. package/src/tui/chat/background-job-status.ts +8 -2
  551. package/src/tui/entry.ts +483 -32
  552. package/src/tui/inline-choice.ts +188 -0
  553. package/src/tui/sidetab/attach.ts +304 -0
  554. package/src/tui/sidetab/list.ts +2 -0
  555. package/src/tui/sidetab/nl-tool.ts +136 -0
  556. package/src/tui/slash/resume-command.ts +23 -0
  557. package/src/tui/slash/resume.ts +3 -23
  558. package/src/tui/slash/term-next.ts +2 -1
  559. package/src/tui/slash/term.ts +12 -1
  560. package/src/tui-client/dashboard-session.ts +7 -1
  561. package/src/tui-client/local-chat-turn.ts +29 -8
  562. package/src/user-config.ts +42 -3
  563. package/src/ux-sim/classic-tui-seat-scenarios.ts +1 -7
  564. package/src/ux-sim/fake-land-queue.ts +88 -0
  565. package/src/ux-sim/fake-llm.ts +11 -5
  566. package/src/ux-sim/gate-shard-host-reroute.ts +82 -0
  567. package/src/ux-sim/goal-interim-trace.ts +24 -0
  568. package/src/ux-sim/next-tui-seat-scenarios.ts +61 -14
  569. package/src/ux-sim/task-orchestrator-surfaces-self-implement-pod-device.ts +4 -1
  570. package/src/version/packed-revision.json +1 -1
  571. package/src/video-pipeline/recipes/explainer.ts +54 -2
  572. package/src/web-search/grok.ts +32 -6
  573. package/src/web-search/index.ts +8 -7
  574. package/src/workflow-runtime/node-catalog.ts +53 -4
  575. package/apps/pwa/out/_next/static/chunks/1041-5841aae848fb6403.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/1087.3177e54f672dc526.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/1105.efe1e13503ac7a33.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/1224.2ceca6c180d5130b.js +0 -62
  579. package/apps/pwa/out/_next/static/chunks/1417-1768233ac92f8a5d.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/1612.81bbc19af06af16c.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/1766.728d42053070bb2d.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/1773.d38e945481e04125.js +0 -1
  583. package/apps/pwa/out/_next/static/chunks/1974-c12f9600da3d3c26.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/1ed5e8af.cf424815c7c0efad.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/2061.7232dc9dc058417f.js +0 -1
  586. package/apps/pwa/out/_next/static/chunks/210.90b2cbd64926cf34.js +0 -32
  587. package/apps/pwa/out/_next/static/chunks/244-a810d282a20f1783.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/2479-fe7e188bd9402d14.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/2514.03b60f266531a015.js +0 -148
  590. package/apps/pwa/out/_next/static/chunks/2526-a657538869888604.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/2533-6f0341ff075809e9.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/2676.e7994c695ef0f30d.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/2734.3ffdf54254fb6f99.js +0 -27
  594. package/apps/pwa/out/_next/static/chunks/2770-23252bf3a37880b9.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/2969.66b8b545139d1544.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/3128.a9210d08800ea6a8.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/3199.6a1a9b0e6cd01dc6.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/3212.12179fd3eee8cef1.js +0 -1
  599. package/apps/pwa/out/_next/static/chunks/3760-2909ddb9279281cd.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/3825.1921b5901bf33dce.js +0 -36
  601. package/apps/pwa/out/_next/static/chunks/3994-63eb884ed0547011.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/4036.d54e55e1ebf5acdc.js +0 -90
  603. package/apps/pwa/out/_next/static/chunks/4115.5b438b99f0ae729f.js +0 -226
  604. package/apps/pwa/out/_next/static/chunks/4269.d8f9f34bb0fc6c43.js +0 -287
  605. package/apps/pwa/out/_next/static/chunks/431-4eed5bd2c5fe1719.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/4324.c99ba3e83eb1b691.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/4599-96eff3f2a9c242c1.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/4825-a9d9d4e9f5892309.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/4833-7934c592c5569f00.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/4879-68518f547a207aff.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/4896.f9b70285363b27b2.js +0 -24
  612. package/apps/pwa/out/_next/static/chunks/4937-28d437999b77c9dd.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/49aa54ab.80f39398e0ed0f30.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/5056.482687319b82638e.js +0 -104
  615. package/apps/pwa/out/_next/static/chunks/506.16b83d56a437a9ee.js +0 -1
  616. package/apps/pwa/out/_next/static/chunks/5260-20593b0a25d0afbc.js +0 -1
  617. package/apps/pwa/out/_next/static/chunks/5404.cc4e9fec26fd5925.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/576.485e83e3f3dec19a.js +0 -1
  619. package/apps/pwa/out/_next/static/chunks/5832-a6be14e663f65726.js +0 -1
  620. package/apps/pwa/out/_next/static/chunks/5854-56648a9fe646b9a0.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/5925.3b1f8b135710249b.js +0 -93
  622. package/apps/pwa/out/_next/static/chunks/5952.294fc574a8625184.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/6168.17749aa3905fa073.js +0 -74
  624. package/apps/pwa/out/_next/static/chunks/6416.5d50b8ec9986ca98.js +0 -1
  625. package/apps/pwa/out/_next/static/chunks/6758.e7fbf532953c0783.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/6795.0cb8a59730d99703.js +0 -149
  627. package/apps/pwa/out/_next/static/chunks/6877-84d47b7e85d0c41e.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/6971-fb5f9dc0a2cb5d2d.js +0 -1
  629. package/apps/pwa/out/_next/static/chunks/717.522b08685979b814.js +0 -1
  630. package/apps/pwa/out/_next/static/chunks/7191.e41eb8f455c7f089.js +0 -41
  631. package/apps/pwa/out/_next/static/chunks/7233.8e744b7513119409.js +0 -1
  632. package/apps/pwa/out/_next/static/chunks/7238-97c2226f80504bd8.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/7285.307963afcf7e7660.js +0 -24
  634. package/apps/pwa/out/_next/static/chunks/7299-fbe4b8c47a87614a.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/7314.df0d95930b0c3f50.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/7406.c0589fdf2ee66629.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/752-90e3e7daad3d8e39.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/7524-bcd3d9928c6842d9.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/7549.9a4de3bf231cfc52.js +0 -1
  640. package/apps/pwa/out/_next/static/chunks/757-4fbaf50942b8c424.js +0 -1
  641. package/apps/pwa/out/_next/static/chunks/765.76ae970ceb597b5b.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/7893-e444a1b0809cd1e4.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/7956-33748cabb3a05562.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/7c5ed21f-f286708e57cf88f7.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/8030-aa66b478b53dbfca.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/8051.c88c07dacbbc5f21.js +0 -8
  647. package/apps/pwa/out/_next/static/chunks/8080-40d781e7704d3671.js +0 -1
  648. package/apps/pwa/out/_next/static/chunks/8149.80e402d4345e8dae.js +0 -77
  649. package/apps/pwa/out/_next/static/chunks/8205.50a5268f4e12ba0b.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/8231.a128feaa6f8b6079.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/85.4ddb1dda2ea3ffa2.js +0 -286
  652. package/apps/pwa/out/_next/static/chunks/8537.26517a09e8247beb.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/8595-e9b7081ab773a61f.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/8821-30cc65a61a60951f.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/8905.b3385aadb7b133e6.js +0 -79
  656. package/apps/pwa/out/_next/static/chunks/8983.246bb3a1ac38f50f.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/9072.18342f5941a1f3ec.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/9123.9df04866b2d6e00f.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/9175-7d383df15a82e2be.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/9181-bf89c07ce5b8f599.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/920-429bbc4504b45f71.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/9207.625edf45d656151c.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/9378.d2575c6ffd6dd756.js +0 -94
  664. package/apps/pwa/out/_next/static/chunks/957.db5b5c4d32a35240.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/958.53a8dcf4729e6997.js +0 -64
  666. package/apps/pwa/out/_next/static/chunks/9647.8f0cd0898c592a29.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/9665-26abb02665704c2c.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/9703.e7d81b6faef1050a.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/9882-7a9164b3c2dacbe5.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/9998.b74e033efadecd10.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c604fb980a634d88.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/app/approvals/page-ec2d8a4979ca9075.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-cb6add08ad3c7660.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/app/board/page-e2eaa321ec223fe6.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/app/botlab/page-5b8a09c475b5f844.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/app/bots/page-91a77daedfbac191.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/app/ceo/page-f37fc5e974bda39b.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/app/chat/page-91429a0b78415a94.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/app/consult/page-b95798b58263b910.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/app/control/page-763d53e175e17aa1.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/app/decisions/page-d55023204055b12f.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/app/design-check/page-89d2140d78abc810.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/app/editor/page-82dc069abfe27f55.js +0 -1
  684. package/apps/pwa/out/_next/static/chunks/app/exec/page-25d9eee7beb552c0.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/app/field/page-770b4594c713ee97.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/app/healthz/route-c604fb980a634d88.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/app/inside/page-ba8e0dcae4f83985.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/app/intake/page-5c112c6d7c33656e.js +0 -1
  689. package/apps/pwa/out/_next/static/chunks/app/layout-09788e1aa9a87db6.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/app/live-run/page-89acd5d68c0bc560.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/app/loops/page-5c13e2c76371d797.js +0 -1
  692. package/apps/pwa/out/_next/static/chunks/app/market/page-653a9f18642825c6.js +0 -1
  693. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-fe986fe79b107546.js +0 -1
  694. package/apps/pwa/out/_next/static/chunks/app/not-found-91ea9f5869a40943.js +0 -1
  695. package/apps/pwa/out/_next/static/chunks/app/observatory/page-7824e21b999962e2.js +0 -1
  696. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-53d00a60cc75a922.js +0 -1
  697. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-88a1edbff62eaf1d.js +0 -1
  698. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4e931248b3f9574b.js +0 -1
  699. package/apps/pwa/out/_next/static/chunks/app/outputs/page-443364c9e4a23b31.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/app/page-b9af2ac1c4737ce8.js +0 -1
  701. package/apps/pwa/out/_next/static/chunks/app/reflection/page-25ab9c614c14129b.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-dbcdcfac0a395a8a.js +0 -1
  703. package/apps/pwa/out/_next/static/chunks/app/sessions/page-bf1de6645089ec58.js +0 -1
  704. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c4201761a5e839c9.js +0 -1
  705. package/apps/pwa/out/_next/static/chunks/app/settings/page-c304f469ea5d70e4.js +0 -1
  706. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8e5a238cf5e50c9c.js +0 -1
  707. package/apps/pwa/out/_next/static/chunks/app/setup/layout-c604fb980a634d88.js +0 -1
  708. package/apps/pwa/out/_next/static/chunks/app/setup/page-32b526069ee50b3c.js +0 -1
  709. package/apps/pwa/out/_next/static/chunks/app/share/page-fb9e639c107f5f72.js +0 -1
  710. package/apps/pwa/out/_next/static/chunks/app/showroom/page-84220c776608cedc.js +0 -1
  711. package/apps/pwa/out/_next/static/chunks/app/tasks/page-e475618cbfd5ca2b.js +0 -1
  712. package/apps/pwa/out/_next/static/chunks/app/today/page-fccd3020af2f8806.js +0 -1
  713. package/apps/pwa/out/_next/static/chunks/app/trace/page-9c0d7198ff7b5a91.js +0 -1
  714. package/apps/pwa/out/_next/static/chunks/app/vault/page-d54b1ec9b9c6430d.js +0 -1
  715. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-8c4bef1791d732ca.js +0 -1
  716. package/apps/pwa/out/_next/static/chunks/app/workflows/page-46fcc0b0bbc6fbfe.js +0 -1
  717. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-7dfda00afd172f5b.js +0 -1
  718. package/apps/pwa/out/_next/static/chunks/f21fd540.63ac75328926ed30.js +0 -1
  719. package/apps/pwa/out/_next/static/chunks/framework-77fdcc873519a6cd.js +0 -1
  720. package/apps/pwa/out/_next/static/chunks/main-app-d0d620d2ce235713.js +0 -1
  721. package/apps/pwa/out/_next/static/chunks/main-e0ef79c79337ba90.js +0 -1
  722. package/apps/pwa/out/_next/static/chunks/pages/_app-84b1bc71e3f9ce6a.js +0 -1
  723. package/apps/pwa/out/_next/static/chunks/pages/_error-2acb61f1363128cb.js +0 -1
  724. package/apps/pwa/out/_next/static/chunks/webpack-462ff5727ceb7824.js +0 -1
  725. package/scripts/migrate-monad-to-elanous.ts +0 -195
  726. package/scripts/rebrand-elanous.ts +0 -242
  727. /package/apps/pwa/out/_next/static/{8kITumG9GAZ1LwT9POrAR → HjeK7zONHfR7op1hKXp4k}/_ssgManifest.js +0 -0
@@ -33,6 +33,7 @@ import { mintRunId, normalizeRunId } from '../../harness/harness-space.js';
33
33
  import { TA_LAND_ENV } from '../../task-agent/task-hand.js';
34
34
  import { appendRunLedgerEntry, loadRunLedger, runLedgerDir, runLedgerPath, type RunLedgerEntry } from '../../self-implement/run-ledger.js';
35
35
  import { recordRunStop } from '../../self-implement/run-stop.js';
36
+ import { gateFailureReceipt, gateFailureReceiptLines } from '../../self-implement/gate-failure-signature.js';
36
37
  import { runSuffixKey } from '../../self-implement/salvage-branches.js';
37
38
  import type { GoalExecutionRecord } from '../../self-implement/orchestrator.js';
38
39
  import { hostRepositoryUnavailable, runHostRegate, type HostRegateResult } from '../../self-implement/host-regate.js';
@@ -46,7 +47,7 @@ import { executeSiWarmTransport, type SiWarmFixedRunner,
46
47
  type SiWarmPodIdentity } from './pod-si-warm-transport.js';
47
48
 
48
49
  export const POD_CONTROL_INBOX_DIR = '/tmp/elanous-control.inbox';
49
- /** Pod Job 수명 상한(`activeDeadlineSeconds`) — 대표 2026-09-26 90분 → 180분(90분에 `DeadlineExceeded` 로 죽은 런 둘). */
50
+ /** Pod Job 수명 상한(`activeDeadlineSeconds`) — 2026-09-26 대표 지시로 90분 → 180분(90분에 `DeadlineExceeded` 로 죽은 런 둘). */
50
51
  /** Newest pod log bytes returned when the export passes the 5MB artifact limit (headroom under it). */
51
52
  export const POD_LOGS_KEEP_BYTES = 4_500_000;
52
53
 
@@ -60,6 +61,34 @@ export const POD_RESULT_FLATTEN = `ELANOUS_CHILD_RC="$rc" bun -e ' const fs=requ
60
61
 
61
62
  export const POD_JOB_DEADLINE_SECONDS = 10_800;
62
63
 
64
+ /** Record the source-stage IO marker emitted by podSourceScript. */
65
+ export function recordPodSourceIoSummary(logs: string, job: string, runId: string | null | undefined,
66
+ runIdSource: 'explicit-child-id' | 'pod-launch-child-run-id',
67
+ log: typeof debug.log = (category, event, data, options) => debug.log(category, event, data, options),
68
+ hostOriginCalls: number | null = 0): void {
69
+ const ioSummary = /^ELANOUS_POD_SOURCE_IO (\{[^\r\n]+\})\r?$/m.exec(logs)?.[1];
70
+ if (!ioSummary) return;
71
+ try {
72
+ const measured = JSON.parse(ioSummary) as Record<string, unknown>;
73
+ log('pod.source', 'io-summary', { job, runId, runIdSource, ...measured, hostOriginCalls });
74
+ } catch (error) {
75
+ log('pod.source', 'io-summary-invalid', { job, runId, runIdSource, reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' });
76
+ }
77
+ }
78
+
79
+ /** Select a timestamp for a DeadlineExceeded observation without making detector time look like Job completion.
80
+ * `podFinishedAt` must come from readPodTermination for the same verified Job uid. */
81
+ export function podJobFinishedTiming(input: { jobCreatedAt: string | null; jobCompletedAt: string | null;
82
+ podFinishedAt: string | null; observedAt: string }): { finishedAt: string; finishedAtSource: 'job-completion' | 'pod-termination' | 'observed-at'; elapsedMs: number | null } {
83
+ const valid = (value: string | null): value is string => typeof value === 'string' && Number.isFinite(Date.parse(value));
84
+ const selected = valid(input.jobCompletedAt) ? { finishedAt: input.jobCompletedAt, finishedAtSource: 'job-completion' as const }
85
+ : valid(input.podFinishedAt) ? { finishedAt: input.podFinishedAt, finishedAtSource: 'pod-termination' as const }
86
+ : { finishedAt: input.observedAt, finishedAtSource: 'observed-at' as const };
87
+ const elapsedMs = valid(input.jobCreatedAt) && Number.isFinite(Date.parse(selected.finishedAt))
88
+ ? Math.max(0, Date.parse(selected.finishedAt) - Date.parse(input.jobCreatedAt)) : null;
89
+ return { ...selected, elapsedMs };
90
+ }
91
+
63
92
  /** 자식 컨테이너 «요청» — 🩸 2026-09-27: limits 만 두면 k8s 가 requests=limits(cpu 4)로 잡아, 32코어 노드에
64
93
  * «자리 요청»이 88% 차서 잡이 Pending 인데 실사용은 14%였다(풀 25 자리 중 ~8 만 떴다). 상한(limits)은 그대로 두고
65
94
  * 예약만 실사용에 맞춘다. */
@@ -86,20 +115,21 @@ import { enterJourneyNode, exitJourneyNode, passJourneyNode } from '../../self-d
86
115
  import { routeJourneyEdge } from '../../self-dev/graph-journey-route.js';
87
116
  import { selectLiveDetail } from '../../live/detail-switch.js';
88
117
  import { authStorePath } from '../../oauth/store.js';
89
- import { grokAuthFilePath, isGrokSubscriptionExpiring, refreshGrokSubscriptionToken, resolveGrokCredential, type GrokRefreshOutcome } from '../../grok/credential.js';
118
+ import { grokAuthFilePath, isGrokSubscriptionExpiring, readSelectedGrokScope, refreshGrokSubscriptionToken, resolveGrokCredential, type GrokRefreshOutcome } from '../../grok/credential.js';
90
119
  import { defaultGrokModel } from '../../grok/models.js';
91
120
  import { resolveHostId } from '../../platform/host-id.js';
92
121
  import { BUDGET_SNAPSHOT_ENV, encodeBudgetSnapshot, harnessLaunchBudgetDecision, type BudgetSnapshot } from '../../execution-loop/launch-gate.js';
93
122
  import type { ReadBudgetInputsDeps } from '../../self-implement/budget-gate.js';
94
123
  import { envLiteral } from '../../platform/env-literal.js';
95
124
  import { LLM_TIER_MAP_BY_PROVIDER, lookupLlmTierSpec, type LlmTierProvider } from '../../model-tier/llm-tier-map.js';
96
- import { parseSelfImplementJson, type SelfImplementJobDone, type SelfImplementJobSpawn } from './self-implement.js';
125
+ import { parseSelfImplementJson, type SelfImplementDisposition, type SelfImplementJobDone, type SelfImplementJobSpawn } from './self-implement.js';
97
126
  import { codexQuotaPolicyFromConfig } from '../../oauth/codex-account-store.js';
98
127
  import { normalizeFallbackChain, type FallbackStep } from '../../oauth/fallback-chain.js';
99
128
  import { goalTypeOf, type GoalType } from '../../../scripts/measure-pod-memory-by-goal.js';
100
129
  import { declaredGoalType, type GoalType as DeclaredGoalType } from '../../self-implement/goal-author.js';
101
130
  import { readPodMemoryAdvice, type PodMemoryAdvice } from '../../cli/pod-memory-advice.js';
102
131
  import { DEFAULT_BUDGET_GATE_MAX_USED_PERCENT, getUserConfig, roleLlmEnvName, type UserConfig } from '../../user-config.js';
132
+ import { worktreeAddTimeoutSec } from '../../git-fs/worktree.js';
103
133
  import { podChildProviderOf, podChildProviderRefusal, type PodChildProvider } from './pod-child-providers.js';
104
134
  import { BEDROCK_POD_EGRESS_LABEL, bedrockPodEnv, checkBedrockPodSecret, type BedrockPodSpec } from './pod-bedrock-child.js';
105
135
  import { resolveBedrockRegion, validBedrockRegion } from '../../llm/bedrock.js';
@@ -107,7 +137,7 @@ import { ZAI_POD_EGRESS_LABEL, checkZaiPodSecret, countActiveZaiJobs, decideZaiP
107
137
  import { ZAI_CODING_DEFAULT_MODEL, ZAI_OPENROUTER_FALLBACK_MODEL, zaiSettingsFromConfig, type ZaiCodingPlanSettings } from '../../llm/zai-coding-plan.js';
108
138
  import { normalizePlanningFallbackReason, selectEnhanceLlm } from '../../prompt-enhance/enhance.js';
109
139
  import { inferProviderFromModel } from '../../llm.js';
110
- import { classifyPodExit, extractPodFailureReason, podChildFailureAttribution, podTerminalRow, POD_TERMINATION_JOB_JSONPATH, POD_TERMINATION_PODS_JSONPATH, readPodTermination, type PodExitClass, type PodTermination } from './pod-failure-reason.js';
140
+ import { classifyPodExit, extractPodFailureReason, podChildFailureAttribution, podDeadlineResultMarker, podJobConditionReason, podTerminalRow, POD_TERMINATION_JOB_JSONPATH, POD_TERMINATION_PODS_JSONPATH, readPodTermination, type PodExitClass, type PodTermination } from './pod-failure-reason.js';
111
141
  import { kubectlTransientReason, retryKubectlTransient } from './kubectl-transient-retry.js';
112
142
  import { OLD_DOOR_STAMP_ENV } from '../../self-dev/old-door.js';
113
143
  import { resolveDefaultSourceHead, seedMirrorOnce, type MirrorSeedResult } from './pod-mirror-seed.js';
@@ -199,7 +229,7 @@ export interface PodSpawnOptions {
199
229
  warmPodRequest?: WarmPodRequestHook;
200
230
  /** SI-SEED-DEFAULT — resolves a default source's branch tip for the mirror seed (test seam · default
201
231
  * {@link resolveDefaultSourceHead}; none with an injected kubectl unless given). */
202
- resolveSourceHead?: (o: { repoUrl: string; base?: string; cwd: string }) => Promise<{ sha?: string; reason?: string }>;
232
+ resolveSourceHead?: (o: { repoUrl: string; base?: string; cwd: string }) => Promise<{ sha?: string; reason?: string; originCalls?: number }>;
203
233
  /** 설정 읽기(시험 주입). kubectl 주입 시험에서는 기본 사용자 설정을 읽지 않는다. */
204
234
  configHostMirror?: () => string | undefined;
205
235
  /** AUTHOR-POD2 host config seam; Pod itself has no host config. */
@@ -511,7 +541,7 @@ export function hostCredentials(account: string, storePath: string = authStorePa
511
541
  * (POD_GROK_MIN_JOB_SECONDS) plus a margin left, and that launch's `activeDeadlineSeconds` is cut to the token's
512
542
  * remaining life minus the margin (`fitPodDeadlineToGrok`) — so a long run ends as a classified DeadlineExceeded
513
543
  * instead of a silent mid-run 401. With the host credential relay (pod.groundingUrl) the Pod renews mid-run and
514
- * nothing is cut. 90 min = the Job cap before 대표 09-26 raised it (only two runs had reached it). */
544
+ * nothing is cut. 90 min = the Job cap before the 09-26 decision raised it (only two runs had reached it). */
515
545
  export const POD_GROK_MIN_JOB_SECONDS = 90 * 60;
516
546
  /** Slack between the fitted Job end and the token expiry (clock skew · Pod scheduling). */
517
547
  export const POD_GROK_EXPIRY_MARGIN_MS = 15 * 60_000;
@@ -526,15 +556,6 @@ export function formatGrokRemaining(ms: number | null): string {
526
556
  return m >= 60 ? `${Math.floor(m / 60)}h${String(m % 60).padStart(2, '0')}m` : `${m}m`;
527
557
  }
528
558
 
529
- /** Latest `expires_at` among scopes that carry an access key (the token the Pod would get). */
530
- function grokSubscriptionExpiresAtMs(home?: string): number | null {
531
- try {
532
- const scopes = JSON.parse(readFileSync(grokAuthFilePath(home), 'utf8')) as Record<string, { key?: unknown; expires_at?: unknown }>;
533
- const all = Object.values(scopes).flatMap((s) => (s && typeof s.key === 'string' && s.key && typeof s.expires_at === 'string' && Number.isFinite(Date.parse(s.expires_at)) ? [Date.parse(s.expires_at)] : []));
534
- return all.length ? Math.max(...all) : null;
535
- } catch { return null; }
536
- }
537
-
538
559
  /** POD-GROK-VALIDITY-FIT — this launch's Job deadline for a refresh-less grok subscription copy.
539
560
  * · relay (the Pod can renew from the host) or unknown expiry → the requested deadline as is.
540
561
  * · otherwise → min(requested, remaining − margin); below the minimum Job (time passed since the credential check)
@@ -574,38 +595,46 @@ export function podGhLoginRetryBackoffMs(job: string): number {
574
595
  export function hostGrokCredentials(opts: {
575
596
  home?: string; env?: NodeJS.ProcessEnv; apiKeyOptIn?: boolean; ghToken?: () => string;
576
597
  /** Test seams — the host refresh (read-only `grok models`) and the expiry probe. */
577
- refresh?: () => void; isExpiring?: () => boolean;
598
+ refresh?: () => void; isExpiring?: () => boolean; grokPath?: string;
578
599
  /** Required remaining token life (default POD_GROK_MIN_VALIDITY_MS) — a caller whose Job is not fitted passes its own deadline. */
579
600
  minValidityMs?: number; now?: () => number;
580
601
  } = {}): { grokAuth?: string; grokApiKey?: string; ghToken: string } {
581
602
  let credential = resolveGrokCredential({ home: opts.home, env: opts.env });
582
603
  if (credential?.kind === 'subscription') {
604
+ const initialScope = readSelectedGrokScope(opts.home);
605
+ if (!initialScope) throw new Error('grok: 선택된 구독 scope 없음 — 발사를 거부');
583
606
  const minValidityMs = opts.minValidityMs ?? POD_GROK_MIN_VALIDITY_MS;
584
- const expiring = opts.isExpiring ?? (() => isGrokSubscriptionExpiring({ home: opts.home, bufferMs: minValidityMs, ...(opts.now ? { now: opts.now } : {}) }));
585
- if (expiring()) {
586
- try { (opts.refresh ?? (() => { refreshGrokSubscriptionToken({ home: opts.home }); }))(); } catch { /* judged below */ }
587
- if (expiring()) {
588
- const at = grokSubscriptionExpiresAtMs(opts.home);
589
- const remainingMs = at === null ? null : at - (opts.now ?? Date.now)();
590
- try { debug.log('pod.grok-credentials', 'refused', { remainingMs, minValidityMs, expiresAt: at === null ? null : new Date(at).toISOString() }); } catch { /* fail-soft */ }
591
- throw new Error(`grok: 구독 access 토큰 남은 시간 ${formatGrokRemaining(remainingMs)} < Pod 발사 기준 ${formatGrokRemaining(minValidityMs)} — 호스트 갱신(grok models)도 못 늘렸다 · 다음: 호스트에서 grok login --device-auth 후 다시`);
607
+ const clock = opts.now ?? Date.now;
608
+ const initialExpiry = initialScope.expiresAt ? Date.parse(initialScope.expiresAt) : NaN;
609
+ const expiring = opts.isExpiring ? opts.isExpiring()
610
+ : !Number.isFinite(initialExpiry) || initialExpiry - clock() < minValidityMs;
611
+ if (expiring) {
612
+ if (!initialScope.hasRefreshToken) {
613
+ throw new Error('grok: 선택된 구독 scope에 refresh token 없음 — 다른 계정으로 대체하지 않고 발사를 거부');
592
614
  }
593
- credential = resolveGrokCredential({ home: opts.home, env: opts.env });
594
- if (credential?.kind !== 'subscription') throw new Error('grok: 갱신 뒤 구독 자격을 다시 읽지 못했다');
615
+ try {
616
+ if (opts.refresh) opts.refresh();
617
+ else refreshGrokSubscriptionToken({ home: opts.home, requireRefreshToken: true,
618
+ requiredValidityMs: minValidityMs, nowMs: clock(), ...(opts.grokPath ? { grokPath: opts.grokPath } : {}) });
619
+ } catch { /* Final snapshot below is the launch decision. */ }
595
620
  }
596
- const scopes = JSON.parse(readFileSync(grokAuthFilePath(opts.home), 'utf8')) as Record<string, unknown>;
597
- const redacted: Record<string, { key: string; expires_at?: string; user_id?: string }> = {};
598
- for (const [name, value] of Object.entries(scopes)) {
599
- if (!value || typeof value !== 'object' || Array.isArray(value)) continue;
600
- const scope = value as Record<string, unknown>;
601
- if (scope.key !== credential.token) continue;
602
- redacted[name] = {
603
- key: scope.key,
604
- ...(typeof scope.expires_at === 'string' ? { expires_at: scope.expires_at } : {}),
605
- ...(typeof scope.user_id === 'string' ? { user_id: scope.user_id } : {}),
606
- };
621
+ const selected = readSelectedGrokScope(opts.home);
622
+ if (!selected || selected.scopeName !== initialScope.scopeName) {
623
+ throw new Error('grok: 자격 확인 중 선택된 구독 scope가 사라졌거나 바뀜 — 발사를 거부');
624
+ }
625
+ const selectedExpiry = selected.expiresAt ? Date.parse(selected.expiresAt) : NaN;
626
+ if (!Number.isFinite(selectedExpiry) || selectedExpiry - clock() < minValidityMs) {
627
+ const remainingMs = Number.isFinite(selectedExpiry) ? selectedExpiry - clock() : null;
628
+ try { debug.log('pod.grok-credentials', 'refused', { remainingMs, minValidityMs, expiresAt: selected.expiresAt }); } catch { /* fail-soft */ }
629
+ throw new Error(`grok: 구독 access 토큰 남은 시간 ${formatGrokRemaining(remainingMs)} < Pod 발사 기준 ${formatGrokRemaining(minValidityMs)} — 호스트 갱신(grok models)도 못 늘렸다 · 다음: 호스트에서 grok login --device-auth 후 다시`);
607
630
  }
608
- if (!Object.keys(redacted).length) throw new Error('grok: 구독 access 토큰 없음');
631
+ const redacted: Record<string, { key: string; expires_at?: string; user_id?: string }> = {
632
+ [selected.scopeName]: {
633
+ key: selected.key,
634
+ ...(selected.expiresAt ? { expires_at: selected.expiresAt } : {}),
635
+ ...(selected.userId ? { user_id: selected.userId } : {}),
636
+ },
637
+ };
609
638
  return { grokAuth: JSON.stringify(redacted), ghToken: (opts.ghToken ?? defaultGhToken)() };
610
639
  }
611
640
  if (credential?.kind === 'api_key' && opts.apiKeyOptIn === true) {
@@ -959,7 +988,7 @@ export function podRunLabels(o: { runId?: string; parentRunId?: string }): Recor
959
988
  return { ...(run ? { 'elanous.run': run } : {}), ...(child ? { 'elanous.child-run': child } : {}) };
960
989
  }
961
990
 
962
- /** Pod 메모리 «등급» — 대표 09-27: PWA Pod 골이 16Gi 에서 OOM 으로 죽는다 ⇒ «high» 등급을 옵션으로 고른다.
991
+ /** Pod 메모리 «등급» — 09-27 결정: PWA Pod 골이 16Gi 에서 OOM 으로 죽는다 ⇒ «high» 등급을 옵션으로 고른다.
963
992
  * 등급 = `standard`(`ELANOUS_POD_MEMORY` 또는 16Gi) · `high`(`ELANOUS_POD_MEMORY_HIGH` 또는 32Gi · standard 보다 작아지지 않는다).
964
993
  * 고르는 순서(앞이 이긴다): ① 발사 옵션 `--pod-memory <등급>`(→ `ELANOUS_POD_MEMORY_TIER`) ② 조각 문면 `Pod 메모리: lite|standard|high` ③ 조각의 `apps/pwa/` ④ 부모의 명시 메모리 줄·PWA 경로 ⑤ 실측 권고 ⑥ 권고 없는 research/document 는 lite ⑦ standard.
965
994
  * node-b 노드 할당 가능 약 251Gi. */
@@ -1192,7 +1221,30 @@ export function podSigtermTrapScript(seqFile: string = POD_USAGE_PARTIAL_SEQ_FIL
1192
1221
  return `trap '{ [ -n "$si_pid" ] && kill "$si_pid" 2>/dev/null || true; [ -n "\${usage_partial_pid:-}" ] && kill "$usage_partial_pid" 2>/dev/null || true; si_seq=$(cat ${seqFile} 2>/dev/null || true); case "$si_seq" in ""|*[!0-9]*) si_seq=0;; esac; si_seq=$((10#$si_seq + 1)); { [ -f scripts/usage-rollup.ts ] && bun scripts/usage-rollup.ts --since 12h --partial --seq "$si_seq"; } & si_rollup_pid=$!; for si_i in 1 2 3 4 5 6 7 8; do kill -0 "$si_rollup_pid" 2>/dev/null || break; sleep 1; done; kill "$si_rollup_pid" 2>/dev/null || true; wait "$si_rollup_pid" 2>/dev/null || true; exit 143; }' TERM`;
1193
1222
  }
1194
1223
 
1195
- export function podJobManifest(o: { name: string; namespace: string; image: string; workIdentity?: PodWorkIdentity; boundPrNumber?: number; /** 레지스트리 이미지면 IfNotPresent(노드가 pull) · 반입 이미지면 Never. */ imagePullPolicy?: 'Never' | 'IfNotPresent'; repoUrl: string; source?: PodSource; repairCommitOnly?: { pr: number; baseHead: string }; hostMirror?: string; args: readonly string[]; passEnv: readonly string[]; deadlineSeconds: number; runId?: string; parentRunId?: string; armEnv?: Readonly<Record<string, string>>; hostId?: string; imageCommit?: string | null; skillEnvs?: readonly string[]; memoryLimit?: string; memoryRequest?: string; goalDoc?: string; /** AUTHOR-POD2 — run `harness say` on /creds/feature inside the Pod (authoring happens off the host). */ authorSentence?: boolean; authorGrade?: 'full' | 'lite'; grokCredential?: 'subscription' | 'api_key'; codexAccounts?: readonly string[]; appCredential?: boolean; /** Test seam — default POD_GH_STALE_SECONDS. */ githubStaleSeconds?: number; /** Which goal execution and attempt this Job is — a resumed host verifies it before following the Job (POD9). */ execution?: { key: string; attempt: number }; hostLeaseAdmitted?: boolean; /** POD-ROLE-LLM — OpenRouter 자식 Pod 에 리뷰어용 codex 자격을 깐다. */ reviewerCodexAuth?: boolean; /** BEDROCK-PROVIDER ② — bedrock 자식만: 이름 있는 AWS Secret 의 키(secretKeyRef) ⊕ 리전 ⊕ 이그레스 라벨. */ bedrock?: BedrockPodSpec; /** ZAI-POD-CHILD — glm(ZAI) 자식만: 이름 있는 ZAI Secret 의 키(secretKeyRef) ⊕ 이그레스·동시 상한 라벨(Pod ⊕ Job). */ zai?: ZaiPodSpec; /** Codex Job may use the same named Secret only for an explicitly admitted ZAI fallback. */ zaiFallback?: ZaiPodSpec; fallbackChain?: { steps: readonly FallbackStep[]; source: 'host' | 'default' }; /** TA-POD-WARMBOOT-PARITY — the child's final line (podResultMarkerShellLine), just before `exit $rc`. */ resultMarkerLine?: string; /** POD-LAUNCH-PROVENANCE — the shared provenance set (labels ⊕ annotations ⊕ child env) for this si-task Job. */ provenance?: PodProvenanceMetadata }): Record<string, unknown> {
1224
+ /** Leave enough time for the Pod wrapper to print its result and push a salvage branch before Kubernetes kills the Job. */
1225
+ export function podDeadlineWatchdogScript(input: { deadlineSeconds: number; job: string; runId?: string; prNumber?: number }): string {
1226
+ const leadSeconds = Math.max(1, Math.min(600, Math.floor(input.deadlineSeconds / 10)));
1227
+ const waitSeconds = Math.max(1, input.deadlineSeconds - leadSeconds);
1228
+ const job = /^[A-Za-z0-9_.:-]+$/.test(input.job) ? input.job : 'unknown-job';
1229
+ const runId = input.runId && /^[A-Za-z0-9_.:-]+$/.test(input.runId) ? input.runId : 'unknown-run';
1230
+ const pr = input.prNumber && Number.isSafeInteger(input.prNumber) && input.prNumber > 0 ? String(input.prNumber) : 'unknown';
1231
+ const jsonPr = pr === 'unknown' ? 'null' : pr;
1232
+ return `printf 'clone\\n' > /tmp/si.stage
1233
+ (sleep ${waitSeconds}; [ -f /tmp/si.finished ] && exit 0
1234
+ stage=$(cat /tmp/si.stage 2>/dev/null || true); case "$stage" in clone|setup|run) ;; *) stage=unknown;; esac
1235
+ branch=$(git -C repo branch --show-current 2>/dev/null || true); case "$branch" in ''|*[!A-Za-z0-9_./-]*) branch=unknown;; esac
1236
+ [ -f /tmp/si.finished ] && exit 0
1237
+ si_target=$(cat /tmp/si.pid 2>/dev/null || true)
1238
+ case "$si_target" in ''|*[!0-9]*) ;; *) kill -0 "$si_target" 2>/dev/null || exit 0;; esac
1239
+ printf 'ELANOUS_POD_DEADLINE_RESULT ${job} ${runId} stop-cause=deadline stage=%s pr=${pr} branch=%s\\n' "$stage" "$branch" | tee /tmp/si.deadline-marker
1240
+ printf '{"stage":"deadline","ok":false,"error":"Pod deadline preemption","stopCause":"deadline","prNumber":${jsonPr},"branch":"%s"}\\n' "$branch" > /tmp/si.deadline-result
1241
+ case "$si_target" in ''|*[!0-9]*) ;; *) kill -TERM "$si_target" 2>/dev/null || true;; esac
1242
+ ) & si_deadline_watch_pid=$!`;
1243
+ }
1244
+
1245
+ export const POD_DEADLINE_AFTER_WAIT = `if [ "$rc" -ne 0 ] && [ -f /tmp/si.deadline-result ]; then printf '\\n' >> /tmp/si.out; cat /tmp/si.deadline-result >> /tmp/si.out; rc=124; fi`;
1246
+
1247
+ export function podJobManifest(o: { name: string; namespace: string; image: string; workIdentity?: PodWorkIdentity; boundPrNumber?: number; /** 레지스트리 이미지면 IfNotPresent(노드가 pull) · 반입 이미지면 Never. */ imagePullPolicy?: 'Never' | 'IfNotPresent'; repoUrl: string; source?: PodSource; repairCommitOnly?: { pr: number; baseHead: string }; hostMirror?: string; args: readonly string[]; passEnv: readonly string[]; deadlineSeconds: number; runId?: string; parentRunId?: string; armEnv?: Readonly<Record<string, string>>; hostId?: string; imageCommit?: string | null; skillEnvs?: readonly string[]; memoryLimit?: string; memoryRequest?: string; goalDoc?: string; /** AUTHOR-POD2 — run `harness say` on /creds/feature inside the Pod (authoring happens off the host). */ authorSentence?: boolean; authorGrade?: 'full' | 'lite'; grokCredential?: 'subscription' | 'api_key'; codexAccounts?: readonly string[]; appCredential?: boolean; /** Test seam — default POD_GH_STALE_SECONDS. */ githubStaleSeconds?: number; /** Which goal execution and attempt this Job is — a resumed host verifies it before following the Job (POD9). */ execution?: { key: string; attempt: number }; hostLeaseAdmitted?: boolean; /** POD-ROLE-LLM — OpenRouter 자식 Pod 에 리뷰어용 codex 자격을 깐다. */ reviewerCodexAuth?: boolean; /** BEDROCK-PROVIDER ② — bedrock 자식만: 이름 있는 AWS Secret 의 키(secretKeyRef) ⊕ 리전 ⊕ 이그레스 라벨. */ bedrock?: BedrockPodSpec; /** ZAI-POD-CHILD — glm(ZAI) 자식만: 이름 있는 ZAI Secret 의 키(secretKeyRef) ⊕ 이그레스·동시 상한 라벨(Pod ⊕ Job). */ zai?: ZaiPodSpec; /** Codex Job may use the same named Secret only for an explicitly admitted ZAI fallback. */ zaiFallback?: ZaiPodSpec; fallbackChain?: { steps: readonly FallbackStep[]; source: 'host' | 'default' }; configSnapshotHash?: string; /** TA-POD-WARMBOOT-PARITY — the child's final line (podResultMarkerShellLine), just before `exit $rc`. */ resultMarkerLine?: string; /** POD-LAUNCH-PROVENANCE — the shared provenance set (labels ⊕ annotations ⊕ child env) for this si-task Job. */ provenance?: PodProvenanceMetadata }): Record<string, unknown> {
1196
1248
  if (o.repairCommitOnly && (o.source?.kind !== 'pr' || o.source.number !== o.repairCommitOnly.pr || !/^[0-9a-f]{40}$/.test(o.repairCommitOnly.baseHead) || o.args.includes('--open-pr') || o.args.includes('--merge-by-host') || o.args.includes('--auto-merge'))) {
1197
1249
  throw new Error('repair commit-only requires matching PR source and no PR/merge flags');
1198
1250
  }
@@ -1223,7 +1275,10 @@ export function podJobManifest(o: { name: string; namespace: string; image: stri
1223
1275
  const script = [
1224
1276
  'set -u',
1225
1277
  ...(o.fallbackChain ? [
1226
- 'mkdir -p "$HOME/.elanous" && install -m 600 /creds/llm-fallback-config.json "$HOME/.elanous/config.json" || exit 6',
1278
+ ...(o.configSnapshotHash ? [
1279
+ 'bun -e \'import { createHash } from "node:crypto"; import { readFileSync } from "node:fs"; const e = JSON.parse(readFileSync("/creds/config-snapshot.json", "utf8")); const c = JSON.parse(readFileSync("/creds/llm-fallback-config.json", "utf8")); const h = createHash("sha256").update(JSON.stringify({ version: e.version, keys: e.keys, sources: e.sources })).digest("hex"); if (h !== e.hash || JSON.stringify(c) !== JSON.stringify(e.keys)) process.exit(1);\' || exit 6',
1280
+ ] : []),
1281
+ 'mkdir -p "$HOME/.elanous" && { [ -e "$HOME/.elanous/config.json" ] || [ -L "$HOME/.elanous/config.json" ] || install -m 600 /creds/llm-fallback-config.json "$HOME/.elanous/config.json"; } || exit 6',
1227
1282
  `echo "ELANOUS_POD_FALLBACK_CHAIN source=${o.fallbackChain.source} steps=${o.fallbackChain.steps.join(',')}"`,
1228
1283
  ] : []),
1229
1284
  openGatewayModel
@@ -1252,9 +1307,11 @@ export function podJobManifest(o: { name: string; namespace: string; image: stri
1252
1307
  // SI-POD-STAGE-MARKERS — Pod 시계 단계 표지(`ELANOUS_POD_STAGE <stage> <start|end> <epoch초>`): 호스트가 Job 로그에서 읽어
1253
1308
  // pod.lifecycle stages 의 clone · mirror · run 을 채운다. 실패한 단계는 끝 표지가 없다(→ missing · 지어내지 않는다).
1254
1309
  // clone 끝 = podSourceScript 전체가 끝난 뒤(갈래마다 마지막 줄이 `ELANOUS_POD_SOURCE` 또는 `cd repo` — 둘 다 소스 받기의 일부).
1310
+ podDeadlineWatchdogScript({ deadlineSeconds: o.deadlineSeconds, job: o.name, runId: o.runId, prNumber: o.boundPrNumber }),
1255
1311
  siTaskPodStageShell('clone', 'start'),
1256
1312
  podSourceScript(o.source ?? { kind: 'default' }, o.repoUrl, undefined, { stageMarkers: true }),
1257
1313
  siTaskPodStageShell('clone', 'end'),
1314
+ `printf 'setup\\n' > /tmp/si.stage`,
1258
1315
  // This image already contains runtime dependencies; there is no child install command.
1259
1316
  // Record that fact before the main command, without treating clone completion as install completion.
1260
1317
  `printf 'ELANOUS_POD_DEPS skipped %s image-prepared-no-install\\n' "$(date +%s)" || true`,
@@ -1278,14 +1335,15 @@ export function podJobManifest(o: { name: string; namespace: string; image: stri
1278
1335
  // 마지막 줄 JSON 이 «맨 끝»이어야 한다(parseSelfImplementJson) — rollup 은 그 앞에.
1279
1336
  // ⛔ `--author-grade` 는 Pod 안 `elanous`(이미지에 깔린 판)가 모를 수 있어 넘기지 않는다 — 10-06 «unknown option» 즉사(#24445 되돌림).
1280
1337
  // AUTHOR-LITE2-POD — 등급은 환경변수 `ELANOUS_AUTHOR_GRADE` 로 싣는다(옛 Pod elanous 는 모르는 환경변수를 무시하고 full 로 돈다).
1338
+ `printf 'run\\n' > /tmp/si.stage`,
1281
1339
  siTaskPodStageShell('run', 'start'),
1282
1340
  o.repairCommitOnly
1283
- ? `export ${OLD_DOOR_STAMP_ENV}=self-implement; elanous self implement "$(cat /creds/feature)" --json --no-open-pr ${quoted} > /tmp/si.out 2>&1 & si_pid=$!; wait "$si_pid"; rc=$?; ${RUN_END}; if elanous self repair-commit-export '${o.repairCommitOnly.pr}' '${o.repairCommitOnly.baseHead}' /tmp/si.out "$PWD" "$HOME/outbox"; then rc=0; elif [ "$rc" -eq 0 ]; then rc=8; fi`
1341
+ ? `export ${OLD_DOOR_STAMP_ENV}=self-implement; elanous self implement "$(cat /creds/feature)" --json --no-open-pr ${quoted} > /tmp/si.out 2>&1 & si_pid=$!; printf '%s\\n' "$si_pid" > /tmp/si.pid; wait "$si_pid"; rc=$?; touch /tmp/si.finished; ${RUN_END}; rm -f /tmp/si.pid; if elanous self repair-commit-export '${o.repairCommitOnly.pr}' '${o.repairCommitOnly.baseHead}' /tmp/si.out "$PWD" "$HOME/outbox"; then rc=0; elif [ "$rc" -eq 0 ]; then rc=8; fi; ${POD_DEADLINE_AFTER_WAIT}`
1284
1342
  : o.authorSentence
1285
- ? `echo "ELANOUS_AUTHOR_ON_POD started host=$(hostname) at=$(date -u +%Y-%m-%dT%H:%M:%SZ)"; author_t0=$(date +%s); ${o.authorGrade === 'lite' || o.authorGrade === 'full' ? `ELANOUS_AUTHOR_GRADE=${o.authorGrade} ` : ''}elanous harness say --substrate local --json${quotedAsk}${quotedChildLlm} -- "$(cat /creds/feature)" > /tmp/si.out 2>&1 & si_pid=$!; wait "$si_pid"; rc=$?; ${RUN_END}; echo "ELANOUS_AUTHOR_ON_POD finished host=$(hostname) rc=$rc seconds=$(( $(date +%s) - author_t0 ))"; ${POD_RESULT_FLATTEN}`
1343
+ ? `echo "ELANOUS_AUTHOR_ON_POD started host=$(hostname) at=$(date -u +%Y-%m-%dT%H:%M:%SZ)"; author_t0=$(date +%s); ${o.authorGrade === 'lite' || o.authorGrade === 'full' ? `ELANOUS_AUTHOR_GRADE=${o.authorGrade} ` : ''}elanous harness say --substrate local --json${quotedAsk}${quotedChildLlm} -- "$(cat /creds/feature)" > /tmp/si.out 2>&1 & si_pid=$!; printf '%s\\n' "$si_pid" > /tmp/si.pid; wait "$si_pid"; rc=$?; touch /tmp/si.finished; ${RUN_END}; rm -f /tmp/si.pid; ${POD_DEADLINE_AFTER_WAIT}; echo "ELANOUS_AUTHOR_ON_POD finished host=$(hostname) rc=$rc seconds=$(( $(date +%s) - author_t0 ))"; ${POD_RESULT_FLATTEN}`
1286
1344
  : goalPath
1287
- ? `elanous harness ask ${goalPath} --json${quotedAsk}${quotedChildLlm} > /tmp/si.out 2>&1 & si_pid=$!; wait "$si_pid"; rc=$?; ${RUN_END}; ${POD_RESULT_FLATTEN}`
1288
- : `export ${OLD_DOOR_STAMP_ENV}=self-implement; elanous self implement "$(cat /creds/feature)" --json ${quoted} > /tmp/si.out 2>&1 & si_pid=$!; wait "$si_pid"; rc=$?; ${RUN_END}; ${POD_RESULT_FLATTEN}`,
1345
+ ? `elanous harness ask ${goalPath} --json${quotedAsk}${quotedChildLlm} > /tmp/si.out 2>&1 & si_pid=$!; printf '%s\\n' "$si_pid" > /tmp/si.pid; wait "$si_pid"; rc=$?; touch /tmp/si.finished; ${RUN_END}; rm -f /tmp/si.pid; ${POD_DEADLINE_AFTER_WAIT}; ${POD_RESULT_FLATTEN}`
1346
+ : `export ${OLD_DOOR_STAMP_ENV}=self-implement; elanous self implement "$(cat /creds/feature)" --json ${quoted} > /tmp/si.out 2>&1 & si_pid=$!; printf '%s\\n' "$si_pid" > /tmp/si.pid; wait "$si_pid"; rc=$?; touch /tmp/si.finished; ${RUN_END}; rm -f /tmp/si.pid; ${POD_DEADLINE_AFTER_WAIT}; ${POD_RESULT_FLATTEN}`,
1289
1347
  'cat /tmp/si.out',
1290
1348
  ...(o.appCredential && !o.repairCommitOnly ? ['kill "$gh_watch_pid" 2>/dev/null || true'] : []),
1291
1349
  ...(o.repairCommitOnly ? [] : [podSalvageScript()]),
@@ -1352,7 +1410,9 @@ if [ "$found" -eq 0 ]; then echo ELANOUS_RUN_LEDGER_NONE; fi`,
1352
1410
  '[ -n "${usage_partial_pid:-}" ] && kill "$usage_partial_pid" 2>/dev/null || true',
1353
1411
  'wait "$mem_sampler_pid" 2>/dev/null || true',
1354
1412
  '[ -n "${usage_partial_pid:-}" ] && wait "$usage_partial_pid" 2>/dev/null || true',
1413
+ 'if [ "$rc" -ne 0 ] && [ -f /tmp/si.deadline-result ]; then rc=124; fi',
1355
1414
  'tail -n 1 /tmp/si.out',
1415
+ 'touch /tmp/si.finished; kill "$si_deadline_watch_pid" 2>/dev/null || true; [ -f /tmp/si.deadline-marker ] && cat /tmp/si.deadline-marker || true',
1356
1416
  // TA-POD-WARMBOOT-PARITY: the last line says «the child is done» so the host need not wait for the Job condition.
1357
1417
  ...(o.resultMarkerLine ? [o.resultMarkerLine] : []),
1358
1418
  'exit $rc',
@@ -1361,7 +1421,7 @@ if [ "$found" -eq 0 ]; then echo ELANOUS_RUN_LEDGER_NONE; fi`,
1361
1421
  apiVersion: 'batch/v1',
1362
1422
  kind: 'Job',
1363
1423
  metadata: { name: o.name, namespace: o.namespace, labels: { 'elanous.substrate': 'pod', 'elanous.job': o.name, ...podRunLabels(o), ...(o.workIdentity ? podWorkLabels(o.workIdentity) : {}), ...(o.zai || o.zaiFallback ? { [ZAI_POD_EGRESS_LABEL]: 'true' } : {}), ...o.provenance?.labels },
1364
- ...((o.execution || o.hostLeaseAdmitted || o.provenance) ? { annotations: { ...o.provenance?.annotations, ...(o.execution ? { [POD_EXECUTION_KEY_ANNOTATION]: o.execution.key, [POD_ATTEMPT_ANNOTATION]: String(o.execution.attempt) } : {}), ...(o.hostLeaseAdmitted ? { [POD_HOST_LEASE_ANNOTATION]: 'true' } : {}) } } : {}) },
1424
+ ...((o.execution || o.hostLeaseAdmitted || o.provenance || o.configSnapshotHash) ? { annotations: { ...o.provenance?.annotations, ...(o.configSnapshotHash ? { 'elanous.config-snapshot-sha256': o.configSnapshotHash } : {}), ...(o.execution ? { [POD_EXECUTION_KEY_ANNOTATION]: o.execution.key, [POD_ATTEMPT_ANNOTATION]: String(o.execution.attempt) } : {}), ...(o.hostLeaseAdmitted ? { [POD_HOST_LEASE_ANNOTATION]: 'true' } : {}) } } : {}) },
1365
1425
  spec: {
1366
1426
  backoffLimit: 0,
1367
1427
  ttlSecondsAfterFinished: POD_JOB_TTL_AFTER_FINISHED_SECONDS,
@@ -1388,6 +1448,9 @@ if [ "$found" -eq 0 ]; then echo ELANOUS_RUN_LEDGER_NONE; fi`,
1388
1448
  ? [{ name: 'ELANOUS_TASK_BOUND_PR', value: String(o.boundPrNumber) }] : []),
1389
1449
  ...(o.workIdentity?.slotId ? [{ name: 'ELANOUS_POD_SLOT_ID', value: o.workIdentity.slotId }] : []),
1390
1450
  { name: 'ELANOUS_SUBSTRATE', value: 'pod' },
1451
+ // POD-INPLACE-WORKTREE: explicit host opt-in only; one Job runs one implementation child.
1452
+ { name: 'ELANOUS_POD_SINGLE_CHILD', value: '1' },
1453
+ { name: 'ELANOUS_POD_INPLACE_WORKTREE', value: process.env.ELANOUS_POD_INPLACE_WORKTREE === '1' ? '1' : '0' },
1391
1454
  ...Object.entries(controlInboxEnv(POD_CONTROL_INBOX_DIR)).map(([name, value]) => ({ name, value })),
1392
1455
  // ⭐ 런 출처(🅣 RFC run-origin §A3 · #20457/#20468 칸 이름) — Pod 는 자기 elanous_id 를 쓰지 않고 «띄운 감독기»의 hostId 를 물려받는다.
1393
1456
  { name: 'ELANOUS_POD_NAME', valueFrom: { fieldRef: { fieldPath: 'metadata.name' } } },
@@ -1910,6 +1973,35 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
1910
1973
  try {
1911
1974
  // Every attempt, including an OOM retry, must acquire a pool slot before applying a Job.
1912
1975
  const stallMs = options.launchStallMs ?? (getUserConfig().harness?.launchStallMinutes ?? 15) * 60_000;
1976
+ const slotNow = options.now ?? Date.now;
1977
+ let slotAttemptNumber = 0;
1978
+ let lastSlotReasonCode: string | undefined;
1979
+ let lastSlotAttempt: { observationKey: string; requestedAtMs: number; requestedAt: string } | undefined;
1980
+ const slotReasonCode = (reason: string | null | undefined): string => {
1981
+ if (!reason) return 'not-measured-yet';
1982
+ if (/no free slot/iu.test(reason)) return 'no-free-slot';
1983
+ if (/memory/iu.test(reason)) return 'memory-capacity';
1984
+ if (/capacity|lease/iu.test(reason)) return 'capacity';
1985
+ return 'pool-slot-unavailable';
1986
+ };
1987
+ const recordSlotObservation = (attempt: { observationKey: string; requestedAtMs: number; requestedAt: string },
1988
+ outcome: 'requested' | 'queued' | 'admitted' | 'rejected', reasonCode: string): void => {
1989
+ const pool = options.pool;
1990
+ let occupants: Record<string, number> | null = null;
1991
+ try { occupants = pool ? pool.snapshot() : null; } catch { /* observation must not change scheduling */ }
1992
+ const poolCapacity = pool?.members?.reduce((total, member) => total + Math.max(0, member.capacity), 0) ?? null;
1993
+ const data = {
1994
+ observationKey: attempt.observationKey, runId: childRunId, host: resolveHostId(env),
1995
+ requestedAt: attempt.requestedAt, waitedMs: Math.max(0, slotNow() - attempt.requestedAtMs),
1996
+ queued: outcome === 'queued', poolCapacity, occupants, reasonCode, parentId: ledgerRunId, outcome,
1997
+ };
1998
+ debug.log('pod.slot', outcome, data, { level: outcome === 'admitted' || outcome === 'requested' ? 'info' : 'warn' });
1999
+ try {
2000
+ appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-slot-observation', data }, ledgerDir);
2001
+ if (outcome === 'rejected') appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-slot-incident', data }, ledgerDir);
2002
+ }
2003
+ catch (error) { debug.log('pod.slot', 'ledger-unavailable', { ...data, writeError: error instanceof Error ? error.message : String(error) }, { level: 'warn' }); }
2004
+ };
1913
2005
  // LAUNCH-STALL (10-06): a stage that waits past the cap says so once — today Pods sat 22 min to 5 h with no event.
1914
2006
  const stalledStages = new Set<string>();
1915
2007
  // pool-slot reason = what THIS launch saw on its last empty tryAcquire; admission reason = the pool-wide lease measurement (one pool, one reading).
@@ -1947,25 +2039,42 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
1947
2039
  };
1948
2040
  const acquireSlot = async (): Promise<PodPoolMember | null> => {
1949
2041
  if (!options.pool) return null;
2042
+ // Each actual acquisition is a separate observation, including the OOM retry in this
2043
+ // same child run. The ordinal keeps keys distinct even with a fixed test clock.
2044
+ const requestedAtMs = slotNow();
2045
+ const attempt = { observationKey: `${childRunId}:pod-slot:${requestedAtMs}:${++slotAttemptNumber}`,
2046
+ requestedAtMs, requestedAt: new Date(requestedAtMs).toISOString() };
2047
+ let firstSlotReasonCode: string | undefined;
2048
+ let slotQueuedRecorded = false;
1950
2049
  const stop = watchStall('pool-slot');
1951
2050
  // MINIH-MODE (0.2.37): the location is declared by the launch env — absent/unknown keeps the configured
1952
2051
  // capacities, so this launch path behaves exactly as before unless the declaration is there.
1953
2052
  const minihLocation = minihLocationFrom(env.ELANOUS_MINIH_LOCATION);
1954
2053
  try {
2054
+ recordSlotObservation(attempt, 'requested', 'pending');
1955
2055
  for (;;) {
1956
2056
  if (input.signal?.aborted) return null;
1957
2057
  // The reason comes back inside this call, so a concurrent launch cannot overwrite it.
1958
2058
  const kind = podPlacementKind(oomRetried ? retryTier : memoryTier, selectedGoalType, requestKind);
1959
- const acquired = await options.pool.tryAcquire(unfitContexts, (reason) => { stageReason['pool-slot'] = reason; },
2059
+ const acquired = await options.pool.tryAcquire(unfitContexts, (reason) => {
2060
+ stageReason['pool-slot'] = reason;
2061
+ firstSlotReasonCode ??= slotReasonCode(reason);
2062
+ },
1960
2063
  { kind, ...(minihLocation ? { minihLocation } : {}), onPlacement: (placement) => { lastPlacement = placement; } });
1961
2064
  if (acquired) {
2065
+ recordSlotObservation(attempt, 'admitted', firstSlotReasonCode ?? 'admitted');
1962
2066
  debug.log('self-implement.pod', 'pool-slot', { spaceId: input.spaceId, context: acquired.context, inflight: options.pool.snapshot(), ...(lastPlacement ? { placement: lastPlacement } : {}) });
1963
2067
  if (lastPlacement) console.warn(`[pod] 배치: ${lastPlacement.reason}`);
1964
2068
  return acquired;
1965
2069
  }
2070
+ firstSlotReasonCode ??= slotReasonCode(stageReason['pool-slot']);
2071
+ if (!slotQueuedRecorded) {
2072
+ slotQueuedRecorded = true;
2073
+ recordSlotObservation(attempt, 'queued', firstSlotReasonCode);
2074
+ }
1966
2075
  await sleep(options.pollMs ?? 15_000);
1967
2076
  }
1968
- } finally { stop(); }
2077
+ } finally { lastSlotReasonCode = firstSlotReasonCode; lastSlotAttempt = attempt; stop(); }
1969
2078
  };
1970
2079
  const acquireAdmission = async (): Promise<boolean> => {
1971
2080
  if (!options.pool || typeof options.pool.acquireAdmission !== 'function') return true;
@@ -2001,8 +2110,9 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
2001
2110
  let member: PodPoolMember | null;
2002
2111
  try { member = await acquireSlot(); } catch (error) { podAdmitError(error); throw error; }
2003
2112
  if (options.pool && !member) {
2113
+ if (lastSlotAttempt) recordSlotObservation(lastSlotAttempt, 'rejected', lastSlotReasonCode ?? 'aborted');
2004
2114
  exitJourneyNode('pool-admit', { provenance: 'self-implement-pod', outcome: 'refused', data: { spaceId: input.spaceId, reason: 'no-slot' } });
2005
- return { exitCode: null, output: '', error: { code: 'aborted', message: 'aborted before a pool slot opened' } };
2115
+ return { exitCode: null, output: '', error: { code: 'aborted', message: 'aborted before a pool slot opened', ...(lastSlotReasonCode ? { reasonCode: lastSlotReasonCode } : {}) } };
2006
2116
  }
2007
2117
  exitJourneyNode('pool-admit', { provenance: 'self-implement-pod', outcome: 'admitted', data: { spaceId: input.spaceId, ...(member ? { context: member.context } : {}) } });
2008
2118
  const currentContext = member ? null : baseKubectl(['config', 'current-context']);
@@ -2030,6 +2140,8 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
2030
2140
  const cleanupSecret = () => { kubectl(['-n', namespace, 'delete', 'secret', `${name}-creds`, '--ignore-not-found']); };
2031
2141
  let repairResultAnnounced = false;
2032
2142
  let repairArtifactReady = false;
2143
+ let hostOriginCalls: number | null = 0;
2144
+ let sourceBaseSha: string | undefined;
2033
2145
  const collectFullLogs = (unavailableEvent: string, replace?: ReadonlySet<string>, onEvent?: (event: string, data: Record<string, unknown>) => void, childId?: string): { status: 'ok' | 'unavailable' | 'incomplete'; childLedgerIncomplete: boolean; samples: ReturnType<typeof parseMemSamples> } => {
2034
2146
  let full: ReturnType<Kubectl>;
2035
2147
  try {
@@ -2046,6 +2158,8 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
2046
2158
  const imageSkip = /^ELANOUS_POD_SOURCE_IMAGE_SKIP ([^\r\n]+)\r?$/m.exec(full.stdout)?.[1];
2047
2159
  const delta = /^ELANOUS_POD_SOURCE_DELTA (mirror|origin)\r?$/m.exec(full.stdout)?.[1];
2048
2160
  if (via) debug.log('self-implement.pod', 'source-via', { job: name, via, ...(mirrorSkip ? { mirrorSkip } : {}), ...(imageSkip ? { imageSkip } : {}), ...(delta ? { delta } : {}) });
2161
+ recordPodSourceIoSummary(full.stdout, name, childId ?? childRunId, childId ? 'explicit-child-id' : 'pod-launch-child-run-id',
2162
+ (category, event, data, logOptions) => debug.log(category, event, data, logOptions), hostOriginCalls);
2049
2163
  let ledgerIncomplete = false;
2050
2164
  let childLedgerIncomplete = false;
2051
2165
  let artifactIncomplete = false;
@@ -2323,20 +2437,43 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
2323
2437
  }
2324
2438
  const now = options.now ?? Date.now;
2325
2439
  const issueVerifiedAppCredential = async (fresh = false): Promise<{ token: string; expiresAt: number } | null> => {
2326
- if (!repository) return null;
2440
+ if (!repository) {
2441
+ debug.log('self-implement.pod', 'github-app-decision', { job: name, reason: 'repository-unavailable', fresh });
2442
+ return null;
2443
+ }
2327
2444
  // This is the same repository-scoped, contents+pull_requests installation request on launch and refresh.
2328
- const candidate = (options.githubInstallation ?? ((repo, o) => coalescedInstallationCredential({ repository: repo.split('/')[1]! }, { fresh: o?.fresh })))(repository, { fresh });
2329
- if (!candidate?.token) return null;
2445
+ let candidate: ReturnType<NonNullable<typeof options.githubInstallation>>;
2446
+ try { candidate = (options.githubInstallation ?? ((repo, o) => coalescedInstallationCredential({ repository: repo.split('/')[1]! }, { fresh: o?.fresh })))(repository, { fresh }); }
2447
+ catch {
2448
+ debug.log('self-implement.pod', 'github-app-decision', { job: name, repository, reason: 'mint-threw', fresh });
2449
+ return null;
2450
+ }
2451
+ if (!candidate?.token) {
2452
+ debug.log('self-implement.pod', 'github-app-decision', { job: name, repository, reason: 'mint-unavailable', fresh });
2453
+ return null;
2454
+ }
2330
2455
  const expiry = candidate.expires_at ?? ('expiresAt' in candidate ? candidate.expiresAt : undefined);
2331
2456
  const expiresAt = typeof expiry === 'number' ? expiry : Date.parse(expiry ?? '');
2332
- if (!Number.isFinite(expiresAt) || expiresAt <= now()) return null;
2333
- const available = await (options.githubRepositories ?? installationRepositories)(candidate.token);
2334
- return available?.length === 1 && available[0]?.toLowerCase() === repository.toLowerCase()
2335
- ? { token: candidate.token, expiresAt } : null;
2457
+ if (!Number.isFinite(expiresAt) || expiresAt <= now()) {
2458
+ debug.log('self-implement.pod', 'github-app-decision', { job: name, repository, reason: Number.isFinite(expiresAt) ? 'expired' : 'expiry-unavailable', expiresAt: Number.isFinite(expiresAt) ? new Date(expiresAt).toISOString() : null, fresh });
2459
+ return null;
2460
+ }
2461
+ let available: Awaited<ReturnType<NonNullable<typeof options.githubRepositories>>>;
2462
+ try { available = await (options.githubRepositories ?? installationRepositories)(candidate.token); }
2463
+ catch {
2464
+ debug.log('self-implement.pod', 'github-app-decision', { job: name, repository, reason: 'verification-threw', expiresAt: new Date(expiresAt).toISOString(), fresh });
2465
+ return null;
2466
+ }
2467
+ if (available?.length !== 1 || available[0]?.toLowerCase() !== repository.toLowerCase()) {
2468
+ debug.log('self-implement.pod', 'github-app-decision', { job: name, repository, reason: 'repository-verification-failed', availableCount: available?.length ?? null, expiresAt: new Date(expiresAt).toISOString(), fresh });
2469
+ return null;
2470
+ }
2471
+ debug.log('self-implement.pod', 'github-app-decision', { job: name, repository, reason: 'verified', expiresAt: new Date(expiresAt).toISOString(), fresh });
2472
+ return { token: candidate.token, expiresAt };
2336
2473
  };
2337
2474
  let appCredential: Awaited<ReturnType<typeof issueVerifiedAppCredential>> = null;
2338
2475
  try { appCredential = await issueVerifiedAppCredential(ghLoginRetried); }
2339
- catch { debug.log('self-implement.pod', 'github-app-unavailable', { job: name, reason: 'mint-or-verification-failed' }); }
2476
+ catch { debug.log('self-implement.pod', 'github-app-unavailable', { job: name, repository, reason: 'mint-or-verification-threw' }); }
2340
2477
  // PODCRED1: a short token is still shipped — the host renews it in the Pod on the first live poll — but it is
2341
2478
  // observed, so an exit-7 burst can be checked against the remaining life the Pods actually started with.
2342
2479
  if (appCredential && appCredential.expiresAt - now() < POD_APP_TOKEN_MIN_START_MS) {
@@ -2352,6 +2489,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
2352
2489
  // The host polling loop is a refresh path even when the Pod cannot reach the relay.
2353
2490
  const hostRefresh = Boolean(appCredential);
2354
2491
  const podGhToken = hostRefresh ? appCredential!.token : creds.ghToken;
2492
+ debug.log('self-implement.pod', 'github-credential-source', { job: name, repository, source: hostRefresh ? 'verified-app' : 'human-fallback', expiresAt: hostRefresh ? new Date(appCredential!.expiresAt).toISOString() : null });
2355
2493
  const githubPassKeys = hostRefresh ? passKeys.filter((key) => !['GH_TOKEN', 'GITHUB_TOKEN', 'GH_CONFIG_DIR'].includes(key)) : passKeys;
2356
2494
  let ghTokenExpiresAt = hostRefresh ? appCredential!.expiresAt : null;
2357
2495
  const requestedGoalDoc = env.ELANOUS_POD_GOAL_DOC;
@@ -2483,6 +2621,12 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
2483
2621
  if (!zaiFallbackSpec) fallbackSteps = fallbackSteps.filter(step => step !== 'zai');
2484
2622
  }
2485
2623
  const fallbackChain = { steps: fallbackSteps, source: normalizedFallback.usedDefault ? 'default' as const : 'host' as const };
2624
+ const { buildPodConfigSnapshot } = await import('./pod-config-snapshot.js');
2625
+ const configSnapshot = buildPodConfigSnapshot(hostConfig, {
2626
+ steps: fallbackChain.steps, overage: zaiOverage,
2627
+ worktreeAddTimeoutSec: worktreeAddTimeoutSec(hostConfig.harness?.worktreeAddTimeoutSec) ?? 1200,
2628
+ });
2629
+ debug.log('self-implement.pod', 'config-snapshot', { job: name, hash: configSnapshot.hash, keyCount: configSnapshot.keyCount });
2486
2630
  debug.log('self-implement.pod', 'fallback-chain-carried', {
2487
2631
  job: name, source: fallbackChain.source, steps: fallbackChain.steps,
2488
2632
  ...(normalizedFallback.dropped.length ? { droppedCount: normalizedFallback.dropped.length } : {}),
@@ -2501,7 +2645,8 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
2501
2645
  ? Object.fromEntries(codexCredentials.map((candidate, i) => [`codex-${i}.json`, JSON.stringify({ elanousAuth: candidate.elanousAuth, codexAuth: candidate.codexAuth })]))
2502
2646
  : { 'elanous-auth.json': (creds as ReturnType<typeof hostCredentials>).elanousAuth, 'codex-auth.json': (creds as ReturnType<typeof hostCredentials>).codexAuth }),
2503
2647
  'gh-token': podGhToken, feature: input.feature,
2504
- 'llm-fallback-config.json': JSON.stringify({ llm: { fallbackChain: fallbackChain.steps, zaiCodingPlan: { overage: zaiOverage } } }),
2648
+ 'llm-fallback-config.json': configSnapshot.configJson,
2649
+ 'config-snapshot.json': configSnapshot.envelopeJson,
2505
2650
  ...(goalDocument !== undefined ? { 'goal-doc': goalDocument } : {}),
2506
2651
  ...Object.fromEntries(Object.entries(skillEnvs).map(([n, text]) => [`skillenv-${n}`, text])),
2507
2652
  ...(grounding ? { [`env-${GROUNDING_TOKEN_ENV}`]: grounding.token } : {}),
@@ -2515,7 +2660,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
2515
2660
  if (missing.length) debug.log('self-implement.pod', 'pass-env-missing', { job: name, missing }, { level: 'warn' });
2516
2661
  // ☸️ 재개(P3 · 2026-09-26): 같은 이름의 Job 이 이미 있으면 «지우지 않고» 붙는다 — 호스트가 끊긴 동안에도 원격은 계속 돌았다.
2517
2662
  // 🩸 종전엔 여기서 delete 부터 해서, 재개가 원격에서 멀쩡히 돌던 Job 을 죽였다. 실패로 끝난 Job 만 지우고 다시 만든다.
2518
- // ☸️ 노드 레지스트리에 이 판이 있으면 그것을 pull 한다(델타 · 대표 2026-09-26) — 라벨 판정은 로컬 이미지 이름으로 한다.
2663
+ // ☸️ 노드 레지스트리에 이 판이 있으면 그것을 pull 한다(델타 · 2026-09-26) — 라벨 판정은 로컬 이미지 이름으로 한다.
2519
2664
  const jobImage = member?.imageRef ?? image;
2520
2665
  // A pool member may pull a different image than the host's local one.
2521
2666
  // The local Docker label cannot prove that member image's commit.
@@ -2697,12 +2842,21 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
2697
2842
  slotId: env.ELANOUS_POD_SLOT_ID,
2698
2843
  work: !goalDoc && env.ELANOUS_POD_AUTHOR_ON_POD === '1' && !shard ? 'authoring' : (oomRetried ? retryTier : memoryTier) === 'lite' ? 'lite' : 'harness',
2699
2844
  };
2700
- // TA-POD-WARMBOOT-PARITY shadow only: record the decision on the existing fresh-Job path.
2701
- // Until a shared warm-candidate contract exists, evidence stays unmeasured and this never changes execution.
2845
+ // TA-POD-WARMBOOT-PARITY shadow only: read injected evidence, then keep the fresh-Job path.
2846
+ // This boundary must not open host configuration or credential files for a warm decision.
2702
2847
  if (workIdentity.taskCard) {
2703
- const warmReuseConfig = options.warmReuseConfig?.() ?? (options.kubectl ? undefined : getUserConfig().pod?.warmReuse);
2704
- const decision = decideWarmReuse('ta', warmReuseConfig, options.warmReuseEvidence?.());
2705
- debug.log('pod.warm', 'ta-shadow-observation', { ...decision, execution: 'fresh-job' });
2848
+ let stage: 'config' | 'evidence' | 'decision' = 'config';
2849
+ try {
2850
+ const config = options.warmReuseConfig?.();
2851
+ stage = 'evidence';
2852
+ const evidence = options.warmReuseEvidence?.();
2853
+ stage = 'decision';
2854
+ const decision = decideWarmReuse('ta', config, evidence);
2855
+ debug.log('pod.warm', 'ta-shadow-observation', { ...decision, execution: 'fresh-job' });
2856
+ } catch {
2857
+ debug.log('pod.warm', 'ta-shadow-unavailable', { spaceId: input.spaceId,
2858
+ reason: stage === 'decision' ? 'warm-decision-failed' : `injected-${stage}-failed`, execution: 'fresh-job' }, { level: 'warn' });
2859
+ }
2706
2860
  }
2707
2861
  // TA-POD-WARMBOOT-PARITY (GATE-WARM-BOOT parity · #27805): a commit-pinned source on a pool host — put that commit into
2708
2862
  // the host mirror first (once per host·commit in this process) so podSourceScript finds it instead of fetching
@@ -2721,12 +2875,13 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
2721
2875
  else if (seedSource.kind !== 'default') seedSkip = `source-${seedSource.kind}`;
2722
2876
  else {
2723
2877
  const resolveHead = options.resolveSourceHead ?? (options.kubectl ? undefined : resolveDefaultSourceHead);
2724
- let head: { sha?: string; reason?: string } | undefined;
2878
+ let head: { sha?: string; reason?: string; originCalls?: number } | undefined;
2725
2879
  try { head = resolveHead ? await resolveHead({ repoUrl, ...(seedSource.base ? { base: seedSource.base } : {}), cwd: seedCwd }) : undefined; }
2726
2880
  catch { head = { reason: 'resolve-threw' }; }
2727
2881
  if (!resolveHead) seedSkip = 'no-head-resolver';
2728
- else if (head?.sha && /^[0-9a-f]{40}$/.test(head.sha)) seedCommit = head.sha;
2729
- else seedSkip = `default-head-${head?.reason ?? 'unresolved'}`;
2882
+ else if (head) hostOriginCalls = typeof head.originCalls === 'number' && Number.isSafeInteger(head.originCalls) && head.originCalls >= 0 ? head.originCalls : null;
2883
+ if (head?.sha && /^[0-9a-f]{40}$/.test(head.sha)) { seedCommit = head.sha; sourceBaseSha = head.sha; }
2884
+ else if (resolveHead) seedSkip = `default-head-${head?.reason ?? 'unresolved'}`;
2730
2885
  }
2731
2886
  if (seedMirror && seedCommit && member?.sshHost) {
2732
2887
  const seedHost = member.sshHost;
@@ -2737,7 +2892,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
2737
2892
  } catch (error) {
2738
2893
  seed = { host: seedHost, ok: false, ms: 0, detail: String(error).slice(0, 300) };
2739
2894
  }
2740
- debug.log('self-implement.pod', 'mirror-seed', { job: name, host: seedHost, commit: seedCommit, sourceKind: seedSource.kind, ok: seed?.ok ?? false, ms: seed?.ms ?? 0, ...(seed?.detail ? { detail: seed.detail } : {}) }, seed?.ok ? undefined : { level: 'warn' });
2895
+ debug.log('self-implement.pod', 'mirror-seed', { job: name, host: seedHost, commit: seedCommit, sourceKind: seedSource.kind, ...(sourceBaseSha ? { sourceBaseSha } : {}), ok: seed?.ok ?? false, ms: seed?.ms ?? 0, ...(seed?.detail ? { detail: seed.detail } : {}) }, seed?.ok ? undefined : { level: 'warn' });
2741
2896
  } else {
2742
2897
  debug.log('self-implement.pod', 'mirror-seed-skipped', { job: name, reason: seedSkip ?? 'unknown', sourceKind: seedSource.kind, ...(member?.sshHost ? { host: member.sshHost } : {}) });
2743
2898
  }
@@ -2772,7 +2927,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
2772
2927
  cleanupSecret();
2773
2928
  return { exitCode: 1, output: '', error: { code: 'pod-provenance', message } };
2774
2929
  }
2775
- const job = podJobManifest({ provenance, name, namespace, fallbackChain, ...(resultMarkerLine ? { resultMarkerLine } : {}), image: jobImage, workIdentity, ...(env.ELANOUS_TASK_BOUND_PR && /^[1-9]\d*$/.test(env.ELANOUS_TASK_BOUND_PR) && Number.isSafeInteger(Number(env.ELANOUS_TASK_BOUND_PR)) ? { boundPrNumber: Number(env.ELANOUS_TASK_BOUND_PR) } : {}), ...(member?.imageRef ? { imagePullPolicy: 'IfNotPresent' as const } : {}), repoUrl, ...(options.source ? { source: options.source } : {}), ...(options.repairCommitOnly ? { repairCommitOnly: options.repairCommitOnly } : {}), ...(hostMirror ? { hostMirror } : {}), args: jobArgs, passEnv: jobPassEnv, deadlineSeconds: jobDeadlineSeconds, ...(goalDoc ? { goalDoc } : env.ELANOUS_POD_AUTHOR_ON_POD === '1' && !shard ? { authorSentence: true, ...(env.ELANOUS_POD_AUTHOR_GRADE === 'lite' || env.ELANOUS_POD_AUTHOR_GRADE === 'full' ? { authorGrade: env.ELANOUS_POD_AUTHOR_GRADE } : {}) } : {}), runId: launchRunId, ...(parentRunId ? { parentRunId } : {}), ...(jobArmEnv ? { armEnv: jobArmEnv } : {}), hostId: resolveHostId(env), skillEnvs: Object.keys(skillEnvs), memoryLimit, memoryRequest, imageCommit: jobImageCommit, ...(grok ? { grokCredential: 'grokAuth' in creds && creds.grokAuth ? 'subscription' as const : 'api_key' as const } : {}), ...(codexAccounts ? { codexAccounts } : {}), ...(hostRefresh ? { appCredential: true } : {}), execution: { key: executionKey, attempt: oomRetried ? 2 : 1 }, hostLeaseAdmitted: !!releaseAdmission, ...(roleCodexCredential ? { reviewerCodexAuth: true } : {}), ...(bedrockSpec ? { bedrock: bedrockSpec } : {}), ...(zaiSpec ? { zai: zaiSpec } : {}), ...(zaiFallbackSpec ? { zaiFallback: zaiFallbackSpec } : {}) }); // kubectl 주입(=시험)이면 docker 를 부르지 않는다
2930
+ const job = podJobManifest({ provenance, name, namespace, fallbackChain, configSnapshotHash: configSnapshot.hash, ...(resultMarkerLine ? { resultMarkerLine } : {}), image: jobImage, workIdentity, ...(env.ELANOUS_TASK_BOUND_PR && /^[1-9]\d*$/.test(env.ELANOUS_TASK_BOUND_PR) && Number.isSafeInteger(Number(env.ELANOUS_TASK_BOUND_PR)) ? { boundPrNumber: Number(env.ELANOUS_TASK_BOUND_PR) } : {}), ...(member?.imageRef ? { imagePullPolicy: 'IfNotPresent' as const } : {}), repoUrl, ...((seedSource.kind === 'default' && sourceBaseSha) ? { source: { ...seedSource, baseSha: sourceBaseSha } } : options.source ? { source: options.source } : {}), ...(options.repairCommitOnly ? { repairCommitOnly: options.repairCommitOnly } : {}), ...(hostMirror ? { hostMirror } : {}), args: jobArgs, passEnv: jobPassEnv, deadlineSeconds: jobDeadlineSeconds, ...(goalDoc ? { goalDoc } : env.ELANOUS_POD_AUTHOR_ON_POD === '1' && !shard ? { authorSentence: true, ...(env.ELANOUS_POD_AUTHOR_GRADE === 'lite' || env.ELANOUS_POD_AUTHOR_GRADE === 'full' ? { authorGrade: env.ELANOUS_POD_AUTHOR_GRADE } : {}) } : {}), runId: launchRunId, ...(parentRunId ? { parentRunId } : {}), ...(jobArmEnv ? { armEnv: jobArmEnv } : {}), hostId: resolveHostId(env), skillEnvs: Object.keys(skillEnvs), memoryLimit, memoryRequest, imageCommit: jobImageCommit, ...(grok ? { grokCredential: 'grokAuth' in creds && creds.grokAuth ? 'subscription' as const : 'api_key' as const } : {}), ...(codexAccounts ? { codexAccounts } : {}), ...(hostRefresh ? { appCredential: true } : {}), execution: { key: executionKey, attempt: oomRetried ? 2 : 1 }, hostLeaseAdmitted: !!releaseAdmission, ...(roleCodexCredential ? { reviewerCodexAuth: true } : {}), ...(bedrockSpec ? { bedrock: bedrockSpec } : {}), ...(zaiSpec ? { zai: zaiSpec } : {}), ...(zaiFallbackSpec ? { zaiFallback: zaiFallbackSpec } : {}) }); // kubectl 주입(=시험)이면 docker 를 부르지 않는다
2776
2931
  const carriedBranch = goalDoc ? 'goal-doc' as const : env.ELANOUS_POD_AUTHOR_ON_POD === '1' && !shard ? 'author' as const : undefined;
2777
2932
  const carriedChildLlm = carriedBranch ? podDelegatedChildLlm(jobArgs) : undefined;
2778
2933
  jobApplyStartedAt = Date.now();
@@ -2780,6 +2935,13 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
2780
2935
  zaiPendingApply.delete(name); // apply 뒤엔 Job 라벨이 센다(성공) · 실패면 셀 것이 없다
2781
2936
  if (a.status !== 0) { cleanupSecret(); return { exitCode: 1, output: a.stderr, error: { code: 'pod-apply', message: a.stderr.trim() } }; }
2782
2937
  releaseAdmission?.applied?.(name, context, namespace);
2938
+ try { appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-config-snapshot', data: {
2939
+ job: name, hash: configSnapshot.hash, keyCount: configSnapshot.keyCount,
2940
+ } }, ledgerDir); } catch (error) {
2941
+ debug.log('self-implement.pod', 'config-snapshot-ledger-unavailable', {
2942
+ job: name, reason: error instanceof Error ? error.message : String(error),
2943
+ }, { level: 'warn' });
2944
+ }
2783
2945
  const imageFacts = { job: name, childRunId: liveChildRunId, image: jobImage, imageCommit: jobImageCommit, context };
2784
2946
  // POD-LAUNCH-PROVENANCE — append-only «started» row: who · why · when, on the parent's existing run ledger.
2785
2947
  const startedRow = { job: name, run: launchRunId, attempt: oomRetried ? 2 : 1, provenance: provenanceRowFromAnnotations(provenance.annotations) };
@@ -2859,11 +3021,36 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
2859
3021
  debug.log('self-implement.pod', 'job-applied', { job: name, namespace, ...(member ? { context: member.context } : {}), image: jobImage, spaceId: input.spaceId, passEnv, ...(openrouterCredentialRequired ? { openrouterKeyPresent: passEnv.includes('OPENROUTER_API_KEY') } : {}), ...(openGateway ? { openGatewayKeyPresent: passEnv.includes('OPENGATEWAY_API_KEY') } : {}), ...(anthropic ? { anthropicKeyPresent: passEnv.includes('ANTHROPIC_API_KEY') } : {}), ...(bedrockSpec ? { bedrockSecret: bedrockSpec.secretName, bedrockSecretKeys: bedrockSpec.keys, bedrockRegionFrom: bedrockSpec.region.from } : {}), ...(zaiRoute ? { zaiRoute, ...(zaiSpec ? { zaiSecret: zaiSpec.secretName } : {}) } : {}), extraArgs: options.extraArgs ?? [], ...(options.armEnv?.ELANOUS_ARM_ID ? { armId: options.armEnv.ELANOUS_ARM_ID } : {}) });
2860
3022
  let state: 'complete' | 'failed' | 'aborted' = 'failed';
2861
3023
  let failedReason = '';
3024
+ let deadlineJobCreatedAt: string | null = null;
3025
+ let deadlineJobCompletedAt: string | null = null;
2862
3026
  let containerReason: string | null = null;
2863
3027
  let containerExitCode: number | null = null;
2864
3028
  // JOB-FAILURE-TERMINATION-CAPTURE — this Job's Kubernetes facts, read once at the failure verdict (before GC).
2865
3029
  // Observation only: failedReason · containerReason · containerExitCode keep driving the classification.
2866
3030
  let podTermination: PodTermination | null = null;
3031
+ const refreshPodTermination = async (): Promise<void> => {
3032
+ if (state !== 'failed' || !podTermination || (podTermination.podLookup === 'terminated'
3033
+ && podTermination.containerReason !== 'unknown' && podTermination.exitCode !== 'unknown'
3034
+ && podTermination.finishedAt !== 'unknown')) return;
3035
+ // The result marker and the Job Failed condition can precede container termination status.
3036
+ // Re-read this launch's Pod once; keep the verdict's Job facts even if the Job has since been GC'd.
3037
+ await sleep(1_000);
3038
+ const retry = readPodTermination({ kubectl, namespace, job: name, path: podTermination.path,
3039
+ jobUid: lifecycleJobUid, jobRead: null });
3040
+ const observed = retry.podLookup === 'unavailable' ? podTermination : retry;
3041
+ const complete = observed.podLookup === 'terminated' && observed.containerReason !== 'unknown'
3042
+ && observed.exitCode !== 'unknown' && observed.finishedAt !== 'unknown';
3043
+ const collectionFailureReason: PodTermination['collectionFailureReason'] | undefined = complete ? undefined
3044
+ : retry.podLookup === 'unavailable' ? 'pod-read-unavailable-after-retry'
3045
+ : retry.podLookup === 'absent' ? 'pod-absent-after-retry'
3046
+ : retry.podLookup === 'running' || retry.podLookup === 'pending' ? 'pod-still-running-after-retry'
3047
+ : retry.podLookup === 'terminated' ? 'terminated-fields-missing-after-retry'
3048
+ : 'pod-state-unknown-after-retry';
3049
+ podTermination = { ...podTermination, observedAt: retry.observedAt,
3050
+ podLookup: observed.podLookup, containerReason: observed.containerReason, exitCode: observed.exitCode,
3051
+ finishedAt: observed.finishedAt, oomKilled: observed.oomKilled, collectionAttempts: 2,
3052
+ ...(collectionFailureReason ? { collectionFailureReason } : {}) };
3053
+ };
2867
3054
  // METER-POD-ROLLUP-GAP — abort 때 Job 삭제 «전»에 거둔 로그 사본(삭제 뒤엔 PARTIAL 줄도 못 읽는다).
2868
3055
  let abortLogs = '';
2869
3056
  // ⭐ 런 «도중» 원장 증분 회수 — 호스트 슈퍼바이저가 Pod 걸음을 실시간으로 본다(🅣 요청 · 힐 루프 runtime 성형의 전제).
@@ -2950,7 +3137,13 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
2950
3137
  let jobReadError: unknown;
2951
3138
  try { jobRead = kubectl(['-n', namespace, 'get', 'job', name, '-o', `jsonpath=${POD_TERMINATION_JOB_JSONPATH}`]); }
2952
3139
  catch (error) { jobReadError = error; }
2953
- failedReason = jobRead?.status === 0 ? (jobRead.stdout.split('\t')[0] ?? '').trim() : '';
3140
+ const observedJobReason = jobRead?.status === 0 ? podJobConditionReason(jobRead.stdout.split('\t')[0]) : 'unknown';
3141
+ failedReason = observedJobReason === 'unknown' ? '' : observedJobReason;
3142
+ if (jobRead?.status === 0) {
3143
+ const [, , , createdText, completedText] = jobRead.stdout.replace(/\r?\n$/, '').split('\t');
3144
+ deadlineJobCreatedAt = createdText && Number.isFinite(Date.parse(createdText)) ? createdText : null;
3145
+ deadlineJobCompletedAt = completedText && Number.isFinite(Date.parse(completedText)) ? completedText : null;
3146
+ }
2954
3147
  // One Pods read serves both: its first three columns are the classification layout, the rest the termination capture.
2955
3148
  let podsRead: { status: number | null; stdout: string } | null = null;
2956
3149
  try {
@@ -2973,6 +3166,7 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
2973
3166
  captureFailedJob(liveChildRunId);
2974
3167
  if (jobReadError !== undefined) {
2975
3168
  // Today's outcome stays (the throw ends the spawn as pod-error); the capture is in the ledger first.
3169
+ await refreshPodTermination();
2976
3170
  try { appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-child-evidence', data: { childEvidence: null, childRunId: liveChildRunId, job: name, podTermination } }, ledgerDir); }
2977
3171
  catch (error) { debug.log('self-implement.pod', 'child-evidence-write-failed', { job: name, childRunId: liveChildRunId, reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' }); }
2978
3172
  debug.log('self-implement.pod', 'pod-termination-captured', { job: name, podTermination, jobReadThrew: true }, { level: 'warn' });
@@ -3058,6 +3252,14 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
3058
3252
  if (!logs && abortLogs) logs = abortLogs;
3059
3253
  // TA-POD-WARMBOOT-PARITY: the marker line is not child output — remove it so the last-line result parse sees the same bytes.
3060
3254
  if (markerWait && markerToken && logs) logs = markerWait.stripPodResultMarker(logs, markerToken);
3255
+ const deadlineMarker = podDeadlineResultMarker(logs, name, liveChildRunId);
3256
+ if (deadlineMarker && state !== 'aborted' && state !== 'complete') {
3257
+ state = 'failed';
3258
+ failedReason = 'DeadlineExceeded';
3259
+ debug.log('self-implement.pod', 'deadline-result-marker', { job: name, childRunId: liveChildRunId, ...deadlineMarker });
3260
+ try { appendRunLedgerEntry({ runId: ledgerRunId, event: 'pod-deadline-result', data: { job: name, childRunId: liveChildRunId, stopCause: 'deadline', ...deadlineMarker } }, ledgerDir); }
3261
+ catch { /* A full ledger must not hide the Pod's terminal marker. */ }
3262
+ }
3061
3263
  let ledgerReason = '';
3062
3264
  let ledgerCollected = false;
3063
3265
  const collection = collectFullLogs('ledger-collect-incomplete', follower.owned ? new Set([liveChildRunId]) : undefined, (event, data) => {
@@ -3117,7 +3319,9 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
3117
3319
  continue;
3118
3320
  }
3119
3321
  // Pod 안 경로는 호스트에서 쓸 수 없다.
3120
- let disposition = parsed ? { ...parsed, worktreePath: undefined } : undefined;
3322
+ let disposition: SelfImplementDisposition | undefined = parsed ? { ...parsed, worktreePath: undefined } : deadlineMarker
3323
+ ? { stage: 'deadline', ok: false, ...(deadlineMarker.pr ? { prNumber: deadlineMarker.pr } : {}), ...(deadlineMarker.branch ? { branch: deadlineMarker.branch } : {}) }
3324
+ : undefined;
3121
3325
  if (options.repairCommitOnly && state === 'complete' && !repairArtifactReady) {
3122
3326
  disposition = { ...disposition, worktreePath: undefined, ok: false, stage: 'repair-artifact-missing', detail: 'Pod repair result or bundle was not collected' };
3123
3327
  }
@@ -3261,8 +3465,8 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
3261
3465
  // the terminal result line can be followed by the useful stderr that explains the actual exit.
3262
3466
  let noResultLogEvidence: Record<string, unknown> | null = null;
3263
3467
  const shouldCaptureFailureLog = state === 'failed' && !oomKilled && failedReason !== 'DeadlineExceeded';
3264
- if (shouldCaptureFailureLog || noResultDiagnostic?.includes('stage=unknown')) {
3265
- const missingStage = noResultDiagnostic?.includes('stage=unknown') ?? false;
3468
+ const missingStage = noResultDiagnostic?.includes('stage=unknown') ?? false;
3469
+ if (shouldCaptureFailureLog || missingStage) {
3266
3470
  const logRead = readPodNoResultLogTail({ run: kubectl, namespace, job: name, maxLines: POD_NO_RESULT_LOG_TAIL_LINES });
3267
3471
  if ('tail' in logRead) {
3268
3472
  noResultLogEvidence = missingStage
@@ -3272,11 +3476,13 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
3272
3476
  job: name, childRunId: liveChildRunId, diagnostic: noResultDiagnostic, lines: logRead.tail.length, logTail: logRead.tail,
3273
3477
  }, { compact: { stringMax: 500 } });
3274
3478
  } else {
3479
+ const unavailable = `log-unavailable: ${logRead.unavailable}`;
3275
3480
  noResultLogEvidence = missingStage
3276
- ? { stderr: `log-unavailable: ${logRead.unavailable}` }
3481
+ ? { stderr: unavailable }
3277
3482
  : { podLogUnavailable: logRead.unavailable };
3278
3483
  debug.log('self-implement.pod', missingStage ? 'no-result-log-tail' : 'failed-pod-log-tail', {
3279
- job: name, childRunId: liveChildRunId, diagnostic: noResultDiagnostic, logUnavailable: logRead.unavailable,
3484
+ job: name, childRunId: liveChildRunId, diagnostic: noResultDiagnostic,
3485
+ logUnavailable: missingStage ? unavailable : logRead.unavailable,
3280
3486
  }, { level: 'warn' });
3281
3487
  }
3282
3488
  }
@@ -3284,6 +3490,9 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
3284
3490
  if (noResultDiagnostic && !noResultDiagnostic.includes('stage=unknown')) {
3285
3491
  debug.log('self-implement.pod', 'no-result-ledger-diagnostic', { job: name, childRunId: liveChildRunId, diagnostic: noResultDiagnostic }, { compact: { stringMax: 500 } });
3286
3492
  }
3493
+ // The marker can precede the container status by several seconds. Read the Pod after
3494
+ // collecting the child logs, before the first terminal evidence row is emitted.
3495
+ await refreshPodTermination();
3287
3496
  // POD-LAUNCH-PROVENANCE — append-only «finished» row paired with its started row on job·run.
3288
3497
  // Keyed on launchRunId like the started row; a reattached Job's inherited child run rides along as childRun.
3289
3498
  // exitCode stays null when the container's code was not read (never filled in as 0).
@@ -3359,7 +3568,10 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
3359
3568
  if (unfitRetry) return unfitRetry;
3360
3569
  if (!await acquireAdmission()) return admissionFailure();
3361
3570
  member = await acquireSlot();
3362
- if (options.pool && !member) return { exitCode: null, output: '', error: { code: 'aborted', message: 'aborted before a pool slot opened' } };
3571
+ if (options.pool && !member) {
3572
+ if (lastSlotAttempt) recordSlotObservation(lastSlotAttempt, 'rejected', lastSlotReasonCode ?? 'aborted');
3573
+ return { exitCode: null, output: '', error: { code: 'aborted', message: 'aborted before a pool slot opened', ...(lastSlotReasonCode ? { reasonCode: lastSlotReasonCode } : {}) } };
3574
+ }
3363
3575
  if (member) context = member.context;
3364
3576
  if (input.signal?.aborted) return { exitCode: null, output: '', error: { code: 'aborted', message: 'aborted before OOM retry' } };
3365
3577
  continue;
@@ -3428,6 +3640,17 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
3428
3640
  const deadlineExceeded = state === 'failed' && failedReason === 'DeadlineExceeded';
3429
3641
  const deadlineSeconds = jobDeadlineSeconds;
3430
3642
  if (deadlineExceeded) {
3643
+ const jobCreatedAt = deadlineJobCreatedAt;
3644
+ const timing = podJobFinishedTiming({ jobCreatedAt, jobCompletedAt: deadlineJobCompletedAt,
3645
+ podFinishedAt: typeof podTermination?.finishedAt === 'string' && Number.isFinite(Date.parse(podTermination.finishedAt))
3646
+ ? podTermination.finishedAt : null,
3647
+ observedAt: new Date((options.now ?? Date.now)()).toISOString() });
3648
+ const lastStage = deadlineMarker?.stage ?? disposition?.stage ?? null;
3649
+ debug.log('pod.job', 'finished', {
3650
+ job: name, jobCreatedAt, finishedAt: timing.finishedAt, finishedAtSource: timing.finishedAtSource,
3651
+ elapsedMs: timing.elapsedMs,
3652
+ deadlineSec: deadlineSeconds, tier: oomRetried ? retryTier : memoryTier, lastStage,
3653
+ });
3431
3654
  debug.log('self-implement.pod', 'deadline-exceeded', {
3432
3655
  job: name,
3433
3656
  deadlineSeconds,
@@ -3455,6 +3678,12 @@ export function podSelfImplementSpawn(options: PodSpawnOptions = {}): SelfImplem
3455
3678
  const salvageOutcome = /^ELANOUS_POD_SALVAGE \S+ \S+$/m.test(logs) ? 'pushed' : logs.includes('ELANOUS_POD_SALVAGE_NONE') ? 'none' : 'unknown';
3456
3679
  passJourneyNode('salvage', { provenance: 'self-implement-pod', outcome: salvageOutcome, data: { job: name } });
3457
3680
  }
3681
+ const repairGateReceipt = disposition?.stage === 'gate-failed' ? gateFailureReceipt(disposition.gate) : null;
3682
+ if (repairGateReceipt) {
3683
+ for (const line of gateFailureReceiptLines(repairGateReceipt)) console.log(line);
3684
+ debug.log('self-implement.pod', 'gate-failure-receipt', { job: name, childRunId: liveChildRunId,
3685
+ signature: repairGateReceipt.signature, tests: repairGateReceipt.tests });
3686
+ }
3458
3687
  return {
3459
3688
  exitCode: podExitCode,
3460
3689
  output: tail,
@@ -3610,7 +3839,9 @@ export function podNoResultLogTailLines(source: string, maxLines: number = POD_N
3610
3839
  let chars = 0;
3611
3840
  for (const line of lines.slice(-Math.max(0, maxLines)).reverse()) {
3612
3841
  if (chars >= POD_NO_RESULT_LOG_TAIL_MAX_CHARS) break;
3613
- const bounded = boundedPodLogText(line, Math.min(POD_NO_RESULT_LOG_TAIL_LINE_CHARS, POD_NO_RESULT_LOG_TAIL_MAX_CHARS - chars));
3842
+ const redacted = redactSecretText(line);
3843
+ const limit = Math.min(POD_NO_RESULT_LOG_TAIL_LINE_CHARS, POD_NO_RESULT_LOG_TAIL_MAX_CHARS - chars);
3844
+ const bounded = redacted.length > limit ? `${redacted.slice(0, limit)}…[truncated]` : redacted;
3614
3845
  if (!bounded) continue;
3615
3846
  result.push(bounded);
3616
3847
  chars += bounded.length;
@@ -3632,7 +3863,7 @@ export function readPodNoResultLogTail(args: {
3632
3863
  }): PodNoResultLogRead {
3633
3864
  const maxLines = args.maxLines ?? POD_NO_RESULT_LOG_TAIL_LINES;
3634
3865
  try {
3635
- const result = args.run(['-n', args.namespace, 'logs', `job/${args.job}`, `--tail=${maxLines}`]);
3866
+ const result = args.run(['-n', args.namespace, 'logs', `job/${args.job}`, '-c', 'child', `--tail=${maxLines}`]);
3636
3867
  if (result.status !== 0) {
3637
3868
  const reason = boundedPodLogText(result.stderr || result.stdout || '', POD_NO_RESULT_LOG_REASON_MAX_CHARS);
3638
3869
  return { unavailable: reason || `kubectl logs exit ${result.status}` };
@@ -3928,7 +4159,7 @@ export async function rebuildLocalPodImageOnce(deps: {
3928
4159
  });
3929
4160
  });
3930
4161
  });
3931
- const result = await build('docker/harness/build.sh');
4162
+ const result = await build(['docker', 'harness', 'build.sh'].join(sep));
3932
4163
  if (result.status !== 0) {
3933
4164
  return { image, action: 'failed', error: `--substrate pod: 이미지 굽기 실패 rc=${result.status}${result.error ? ` (${(result.error as NodeJS.ErrnoException).code ?? result.error.message})` : ''}: ${(result.stdout + result.stderr).slice(-400)}` };
3934
4165
  }