elanous 0.2.20 → 0.2.22

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 (456) 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/aBi6EyMtLsrl7J8eH4kF7/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/088d8e8a.8069543d082bec12.js +160 -0
  5. package/apps/pwa/out/_next/static/chunks/1109.78a7a3288c0259d9.js +148 -0
  6. package/apps/pwa/out/_next/static/chunks/115.1c6690b8604f81c4.js +32 -0
  7. package/apps/pwa/out/_next/static/chunks/119fc678.6f591cdb84ca5791.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1262.76a3f08e67c5f816.js +309 -0
  9. package/apps/pwa/out/_next/static/chunks/1307-08b716476f1d50af.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/1358d292.5c87f12cad6eaa91.js +122 -0
  11. package/apps/pwa/out/_next/static/chunks/1438.27a1a7b8f2308004.js +149 -0
  12. package/apps/pwa/out/_next/static/chunks/1442.02d85cd88f4c1eac.js +79 -0
  13. package/apps/pwa/out/_next/static/chunks/1501-1aad662d82ba43f7.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/1584.9e2dd53da6e6d4b1.js +24 -0
  15. package/apps/pwa/out/_next/static/chunks/1586.b989b0614afb77e6.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/1609.2ad355062870151f.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/1612-f6df0fc0eed7157b.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/1923.37f1d5fe7ebe3860.js +27 -0
  19. package/apps/pwa/out/_next/static/chunks/1948.5f35b39eef222a94.js +5 -0
  20. package/apps/pwa/out/_next/static/chunks/1993.3fd074a2a246234e.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/2140.d2daf9ee96e295d1.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/2212.0359851457473efc.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/224.583e562365a52f74.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/2328.0f37465646bafa7d.js +36 -0
  25. package/apps/pwa/out/_next/static/chunks/2372.bf71372184991937.js +286 -0
  26. package/apps/pwa/out/_next/static/chunks/{2489-ff55a35b202c0f94.js → 2489-d238d3e2eb5b2bf5.js} +1 -1
  27. package/apps/pwa/out/_next/static/chunks/2549-a8515c86f9e8cb96.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2655.72cc6a97276c5acc.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/2743-ef6e8c3ced74cce5.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/2840-5458ad50c49cac6a.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/2856.f75d5baef7f49b74.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/3344.0de4b0ec5a454b7a.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/335.a13f339e97893437.js +94 -0
  34. package/apps/pwa/out/_next/static/chunks/3394.646348f34b2500be.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/3443.6276909b53d72d53.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/3495.abd4254d808bf2dc.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/3522-1de1319d028ac89c.js +25 -0
  38. package/apps/pwa/out/_next/static/chunks/3721.ba5b82db0d9a5572.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/3857-5985236d9307f676.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/3919.9ff7dc25afefee33.js +74 -0
  41. package/apps/pwa/out/_next/static/chunks/3998.bd9a69d9b50e9bd2.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/4003.ab6db1dc266576c9.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/4216.e8ac94b9fe6b96ab.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/4327.c44dc2b55cb43ee5.js +34 -0
  45. package/apps/pwa/out/_next/static/chunks/4401.94257d497b6d26bc.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/4559.17ca2cdbaa8d414f.js +115 -0
  47. package/apps/pwa/out/_next/static/chunks/4588.ad3364cb7fb97ada.js +86 -0
  48. package/apps/pwa/out/_next/static/chunks/4706.97e734bd4c9816ca.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/4982.6e44352f751b9a1b.js +77 -0
  50. package/apps/pwa/out/_next/static/chunks/5342.49c5c5a530e62b50.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/5360-08a80abdf3a4da99.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/5431.66c58ab1327ee0b7.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/5633-d6852385000738ef.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/5717.bcfcb96e701b82e0.js +90 -0
  55. package/apps/pwa/out/_next/static/chunks/5755-30d102586789bf7f.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/5957.3f1a4012388dd962.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/5977-5b9e8a45a5b0c823.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/602.9bf02ac8e9502598.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/6020.3a1b347e2d2dfacc.js +8 -0
  60. package/apps/pwa/out/_next/static/chunks/{6158.af2e93b7adff709b.js → 6158.566c890adc06a459.js} +1 -1
  61. package/apps/pwa/out/_next/static/chunks/6375.fce1d174ba6d0324.js +64 -0
  62. package/apps/pwa/out/_next/static/chunks/6546.92a828aef441271f.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/658.957fdb0a8c6c6c8e.js +287 -0
  64. package/apps/pwa/out/_next/static/chunks/6706.323590aabb8b1293.js +226 -0
  65. package/apps/pwa/out/_next/static/chunks/6747.b67947f293a4c02d.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/6767.8e6c5f6e744fcd9f.js +38 -0
  67. package/apps/pwa/out/_next/static/chunks/6868.000b24a3045c5574.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/6946.bba74e9c96050f30.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/7033.a558f1b2f6912a53.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/7065.46029c26c53eac50.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/7087.6fae577d09249bc1.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/7175-350f4ce2873bc038.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/722.de8d9801e0f6d414.js +41 -0
  74. package/apps/pwa/out/_next/static/chunks/7497.57981e9d7a75a52a.js +24 -0
  75. package/apps/pwa/out/_next/static/chunks/7630.2b9a68659053ff4e.js +193 -0
  76. package/apps/pwa/out/_next/static/chunks/7672.4af122ad892117c7.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/771.6ab889a233e7dae7.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/7899.597af4a9996c907f.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/79b78388.663188133449d707.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/8096.f7da0776cfde6300.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/8194.484f51e273d9abd2.js +217 -0
  82. package/apps/pwa/out/_next/static/chunks/8769.815ca69879f608b4.js +104 -0
  83. package/apps/pwa/out/_next/static/chunks/8857-aa1cc4ac90af4d40.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/88729267.9c6c000157c83f2f.js +62 -0
  85. package/apps/pwa/out/_next/static/chunks/8f835b4f.048eeb02299e3e12.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/9068.5f2764fccf86c1b2.js +93 -0
  87. package/apps/pwa/out/_next/static/chunks/9247-55bcfbb0e8057f59.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/9451.ebfb0dcc9c02250f.js +23 -0
  89. package/apps/pwa/out/_next/static/chunks/9559.e648369b1d57cf9a.js +62 -0
  90. package/apps/pwa/out/_next/static/chunks/9832.2ab2e818013574c7.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/a9f2b54d-40c16bd0efdb04b8.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/app/approvals/{page-c855c24de7db74fe.js → page-88459e0903da2803.js} +1 -1
  93. package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-85b316bb4235980b.js → page-d85c43f46f58716b.js} +1 -1
  94. package/apps/pwa/out/_next/static/chunks/app/board/{page-9b2b26a33cb2cd42.js → page-4e645600579ae952.js} +1 -1
  95. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-c9955fde67b1bcb5.js → page-87edbfd19117823b.js} +1 -1
  96. package/apps/pwa/out/_next/static/chunks/app/bots/page-7f0c2b7801ac6a54.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/app/ceo/page-bf1f030a9d1adffd.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/app/chat/{page-4ba4abe096b6c2a8.js → page-cbaf66b1e1804adf.js} +1 -1
  99. package/apps/pwa/out/_next/static/chunks/app/consult/{page-575d4a8b4f7becb2.js → page-80ed511e02c81412.js} +1 -1
  100. package/apps/pwa/out/_next/static/chunks/app/control/{page-f5d56ff5f45ad164.js → page-9f5610caaa06178c.js} +1 -1
  101. package/apps/pwa/out/_next/static/chunks/app/decisions/{page-2260e9eef0357421.js → page-ec8f3575e339de74.js} +1 -1
  102. package/apps/pwa/out/_next/static/chunks/app/design-check/page-86d753e460f1a989.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/app/editor/page-4166a7d51cf0a57d.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/app/exec/{page-466bf0ee16ad1432.js → page-2a19c5cd3b562580.js} +1 -1
  105. package/apps/pwa/out/_next/static/chunks/app/field/{page-a23bc71713bcc08f.js → page-b6a09cde2e07521a.js} +1 -1
  106. package/apps/pwa/out/_next/static/chunks/app/inside/page-f7eae1518ca0c353.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/app/intake/page-456a8573ad811381.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/app/layout-8462d6a543cbb2d9.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/app/live/page-2974cdfb87c0f9c9.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/app/live-run/page-f927ffa25f3c86d1.js +1 -0
  111. package/apps/pwa/out/_next/static/chunks/app/loops/{page-35bfec2e631d3eae.js → page-abd86cd832aaa011.js} +1 -1
  112. package/apps/pwa/out/_next/static/chunks/app/market/page-79daf68f6256adaa.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-f9ccb1b1adbea14d.js → page-8c3f168fc3f91939.js} +1 -1
  114. package/apps/pwa/out/_next/static/chunks/app/morning/{page-ec102c9b8b691e37.js → page-786fb4504d0f2fa9.js} +1 -1
  115. package/apps/pwa/out/_next/static/chunks/app/not-found-75209f14597dcb8f.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/app/observatory/page-bf30affe3a85af7e.js +1 -0
  117. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1187a44b7515434b.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-3d0910185a66ed54.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f401ee1dd5a26e25.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/app/outputs/page-5dfe7604fd842784.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/app/page-76cf838050a8d681.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/app/reflection/{page-db03ae3589a81c9e.js → page-7ec8e289b4c14df6.js} +1 -1
  123. package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-4a6463b97f0ad94f.js → page-8c8ac17d52be2a79.js} +1 -1
  124. package/apps/pwa/out/_next/static/chunks/app/sessions/{page-75bffecc9aa1004e.js → page-d359ab048d63a9d9.js} +1 -1
  125. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-b8b345c880bf78d1.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/settings/{page-31d5b875760bb62d.js → page-f151cc10ad026940.js} +1 -1
  127. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-9875f103057da433.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/setup/page-9789328af9a9aa62.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/share/{page-46d5201d2207a511.js → page-bf495a1e284f4e00.js} +1 -1
  130. package/apps/pwa/out/_next/static/chunks/app/showroom/page-a263d94bca4be22a.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/tasks/page-3962eab8ec58d16e.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/term/{page-cf0a4b4abead71ea.js → page-9abcfac07e02a542.js} +1 -1
  133. package/apps/pwa/out/_next/static/chunks/app/today/page-bee49f927c333abb.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/trace/page-bac2d56dcb02d955.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/vault/page-937cbcc13176295c.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d09495594f7f480d.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/workflows/{page-0acc8e198f7b0dfb.js → page-69cb93d1a2e57e0e.js} +1 -1
  138. package/apps/pwa/out/_next/static/chunks/app/workspace/{page-34882c84f84021c5.js → page-72ec543928d67c3e.js} +1 -1
  139. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-82906eeda0186a7b.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/d70879db.c66c56a95a91efc6.js +66 -0
  141. package/apps/pwa/out/_next/static/chunks/f8626309.a4e5d362b1d55e59.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/webpack-f2b4016b9b981bc8.js +1 -0
  143. package/apps/pwa/out/_next/static/css/{d3427996da11e480.css → b9d8151e48998417.css} +1 -1
  144. package/apps/pwa/out/_next/static/css/f2bd5257e6af88d8.css +1 -0
  145. package/apps/pwa/out/approvals/index.html +1 -1
  146. package/apps/pwa/out/approvals/index.txt +14 -14
  147. package/apps/pwa/out/autopilot/index.html +1 -1
  148. package/apps/pwa/out/autopilot/index.txt +14 -14
  149. package/apps/pwa/out/board/index.html +1 -1
  150. package/apps/pwa/out/board/index.txt +14 -14
  151. package/apps/pwa/out/botlab/index.html +1 -1
  152. package/apps/pwa/out/botlab/index.txt +14 -14
  153. package/apps/pwa/out/bots/index.html +1 -1
  154. package/apps/pwa/out/bots/index.txt +14 -14
  155. package/apps/pwa/out/ceo/index.html +1 -1
  156. package/apps/pwa/out/ceo/index.txt +14 -14
  157. package/apps/pwa/out/chat/index.html +1 -1
  158. package/apps/pwa/out/chat/index.txt +14 -14
  159. package/apps/pwa/out/consult/index.html +1 -1
  160. package/apps/pwa/out/consult/index.txt +14 -14
  161. package/apps/pwa/out/control/index.html +1 -1
  162. package/apps/pwa/out/control/index.txt +14 -14
  163. package/apps/pwa/out/decisions/index.html +1 -1
  164. package/apps/pwa/out/decisions/index.txt +14 -14
  165. package/apps/pwa/out/design-check/index.html +1 -1
  166. package/apps/pwa/out/design-check/index.txt +14 -14
  167. package/apps/pwa/out/editor/index.html +1 -1
  168. package/apps/pwa/out/editor/index.txt +15 -15
  169. package/apps/pwa/out/exec/index.html +1 -1
  170. package/apps/pwa/out/exec/index.txt +14 -14
  171. package/apps/pwa/out/field/index.html +1 -1
  172. package/apps/pwa/out/field/index.txt +14 -14
  173. package/apps/pwa/out/healthz +1 -1
  174. package/apps/pwa/out/index.html +1 -1
  175. package/apps/pwa/out/index.txt +14 -14
  176. package/apps/pwa/out/inside/index.html +1 -1
  177. package/apps/pwa/out/inside/index.txt +15 -15
  178. package/apps/pwa/out/intake/index.html +1 -1
  179. package/apps/pwa/out/intake/index.txt +14 -14
  180. package/apps/pwa/out/live/index.html +1 -1
  181. package/apps/pwa/out/live/index.txt +14 -14
  182. package/apps/pwa/out/live-run/index.html +1 -0
  183. package/apps/pwa/out/live-run/index.txt +35 -0
  184. package/apps/pwa/out/loops/index.html +1 -1
  185. package/apps/pwa/out/loops/index.txt +14 -14
  186. package/apps/pwa/out/market/index.html +1 -1
  187. package/apps/pwa/out/market/index.txt +14 -14
  188. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  189. package/apps/pwa/out/missions/placeholder/index.txt +14 -14
  190. package/apps/pwa/out/morning/index.html +1 -1
  191. package/apps/pwa/out/morning/index.txt +14 -14
  192. package/apps/pwa/out/observatory/index.html +1 -1
  193. package/apps/pwa/out/observatory/index.txt +14 -14
  194. package/apps/pwa/out/ops/checklist/index.html +1 -1
  195. package/apps/pwa/out/ops/checklist/index.txt +14 -14
  196. package/apps/pwa/out/ops/release/index.html +1 -1
  197. package/apps/pwa/out/ops/release/index.txt +14 -14
  198. package/apps/pwa/out/ops/seats/index.html +1 -1
  199. package/apps/pwa/out/ops/seats/index.txt +14 -14
  200. package/apps/pwa/out/outputs/index.html +1 -1
  201. package/apps/pwa/out/outputs/index.txt +14 -14
  202. package/apps/pwa/out/reflection/index.html +1 -1
  203. package/apps/pwa/out/reflection/index.txt +15 -15
  204. package/apps/pwa/out/scheduler/index.html +1 -1
  205. package/apps/pwa/out/scheduler/index.txt +14 -14
  206. package/apps/pwa/out/sessions/index.html +1 -1
  207. package/apps/pwa/out/sessions/index.txt +14 -14
  208. package/apps/pwa/out/settings/devices/index.html +1 -1
  209. package/apps/pwa/out/settings/devices/index.txt +14 -14
  210. package/apps/pwa/out/settings/index.html +1 -1
  211. package/apps/pwa/out/settings/index.txt +14 -14
  212. package/apps/pwa/out/setup/done/index.html +1 -1
  213. package/apps/pwa/out/setup/done/index.txt +14 -14
  214. package/apps/pwa/out/setup/index.html +1 -1
  215. package/apps/pwa/out/setup/index.txt +14 -14
  216. package/apps/pwa/out/share/index.html +1 -1
  217. package/apps/pwa/out/share/index.txt +14 -14
  218. package/apps/pwa/out/showroom/index.html +1 -1
  219. package/apps/pwa/out/showroom/index.txt +14 -14
  220. package/apps/pwa/out/tasks/index.html +1 -1
  221. package/apps/pwa/out/tasks/index.txt +14 -14
  222. package/apps/pwa/out/term/index.html +1 -1
  223. package/apps/pwa/out/term/index.txt +14 -14
  224. package/apps/pwa/out/today/index.html +1 -1
  225. package/apps/pwa/out/today/index.txt +14 -14
  226. package/apps/pwa/out/trace/index.html +1 -1
  227. package/apps/pwa/out/trace/index.txt +14 -14
  228. package/apps/pwa/out/vault/index.html +1 -1
  229. package/apps/pwa/out/vault/index.txt +14 -14
  230. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  231. package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
  232. package/apps/pwa/out/workflows/index.html +1 -1
  233. package/apps/pwa/out/workflows/index.txt +14 -14
  234. package/apps/pwa/out/workspace/index.html +1 -1
  235. package/apps/pwa/out/workspace/index.txt +14 -14
  236. package/apps/pwa/out/worktrees/index.html +1 -1
  237. package/apps/pwa/out/worktrees/index.txt +14 -14
  238. package/graphs/catalog/catalog.yaml +1 -1
  239. package/graphs/drafts/recipes.yaml +6 -6
  240. package/graphs/implement-loop.yaml +277 -5
  241. package/graphs/test-diet/recipes.yaml +3 -0
  242. package/graphs/test-diet/test-diet.yaml +3 -1
  243. package/package.json +1 -1
  244. package/scripts/coord-channel-watch.sh +31 -4
  245. package/scripts/docs-lint.ts +4 -1
  246. package/scripts/f-session-monitor.ts +69 -0
  247. package/scripts/hq/standby-snapshot.ts +7 -3
  248. package/scripts/knowledge-compress.ts +11 -0
  249. package/scripts/marketing/generate-ax-brief.ts +18 -1
  250. package/scripts/measure-nl-routing-corpus.ts +11 -3
  251. package/scripts/nightly-docops.ts +90 -3
  252. package/scripts/release-loop/gate-node.ts +5 -5
  253. package/scripts/release-loop/shard-plan.ts +50 -5
  254. package/scripts/research-coo-admin-dates.ts +39 -0
  255. package/scripts/rhythm/daily.ts +2 -1
  256. package/scripts/skills/public-check.ts +7 -2
  257. package/scripts/smart-merge/resync-shadow.ts +5 -3
  258. package/scripts/test-diet/lib.ts +81 -1
  259. package/scripts/test-diet/node.ts +48 -2
  260. package/scripts/verify-coo-admin-evidence.ts +83 -0
  261. package/src/acp/client.ts +4 -2
  262. package/src/agent/run-context.ts +4 -0
  263. package/src/agent-env/profile.ts +25 -5
  264. package/src/agent-mission/driver.ts +26 -12
  265. package/src/agent-mission/supervisor-metrics.ts +17 -2
  266. package/src/agent-substrate/acp-reviewer.ts +12 -5
  267. package/src/agent-substrate/pr-reviewer.ts +11 -4
  268. package/src/agent-substrate/review-intent.ts +102 -3
  269. package/src/autopilot/build/llm-conflict-merge.ts +22 -8
  270. package/src/autopilot/pr-manager.ts +18 -8
  271. package/src/away/release-watch.ts +47 -8
  272. package/src/briefing/brief-items.ts +104 -11
  273. package/src/browser-cdp/client.ts +10 -5
  274. package/src/chat/index.ts +1 -0
  275. package/src/cli/brief-cli.ts +20 -2
  276. package/src/cli/config-classify.ts +83 -0
  277. package/src/cli/control-cli.ts +14 -0
  278. package/src/cli/decisions-cli.ts +26 -16
  279. package/src/cli/logs-model-input.ts +1 -1
  280. package/src/cli/ops-cli.ts +11 -0
  281. package/src/cli/ops-overview-cli.ts +50 -0
  282. package/src/cli/pod-cli.ts +1 -0
  283. package/src/cli/pr-cli.ts +65 -8
  284. package/src/cli/release-cli.ts +21 -4
  285. package/src/cli/tasks-cli.ts +214 -1
  286. package/src/connectors/types.ts +6 -6
  287. package/src/context-bus/context-now-surfaces.ts +18 -0
  288. package/src/context-bus/coord-events.ts +32 -4
  289. package/src/context-bus/long-term-memory.ts +23 -6
  290. package/src/context-bus/nightly-condense.ts +169 -21
  291. package/src/control/control-status.ts +87 -0
  292. package/src/control-plane/light-member.ts +5 -3
  293. package/src/control-plane/member.ts +172 -7
  294. package/src/core-turn/run-goal-loop.ts +22 -1
  295. package/src/dashboard/index.ts +40 -4
  296. package/src/dashboard/slash-runtime/dashboard-handlers.ts +9 -0
  297. package/src/decisions/decision-cards.ts +20 -1
  298. package/src/decisions/decision-ledger.ts +11 -8
  299. package/src/decisions/web-push-decision-cards.ts +19 -3
  300. package/src/discord-self-message.ts +2 -2
  301. package/src/domains/coo-admin-dates.evidence.json +876 -0
  302. package/src/domains/coo-admin-tool.ts +48 -14
  303. package/src/domains/ops-status.ts +53 -0
  304. package/src/domains/outbound-alert.ts +24 -1
  305. package/src/esc-abort-gate.ts +3 -1
  306. package/src/exec-requests/planner.ts +6 -0
  307. package/src/graph-kinds/registry.ts +6 -6
  308. package/src/graph-runner/graph-cli.ts +91 -2
  309. package/src/graph-runner/graph-run-api.ts +20 -0
  310. package/src/graph-runner/graph-tick.ts +73 -12
  311. package/src/graph-runner/runner.ts +43 -11
  312. package/src/graph-unify/wf-to-graph.ts +79 -0
  313. package/src/graph-wizard/archetypes.ts +88 -0
  314. package/src/graph-wizard/generate.ts +577 -0
  315. package/src/graph-wizard/saved-steps.ts +120 -0
  316. package/src/graph-wizard/steps.ts +206 -0
  317. package/src/harness/harness-cli-command.ts +186 -32
  318. package/src/harness/harness-queue.ts +58 -14
  319. package/src/harness/launch-advice.ts +23 -0
  320. package/src/index.ts +43 -11
  321. package/src/intake-plane/digest.ts +18 -11
  322. package/src/knowledge/conclusion-compress.ts +93 -0
  323. package/src/knowledge/know-find.ts +9 -2
  324. package/src/knowledge/query.ts +20 -0
  325. package/src/llm.ts +26 -16
  326. package/src/loops/neighbor-runtime.ts +88 -0
  327. package/src/loops/orchestrator/author-depth.ts +29 -11
  328. package/src/loops/orchestrator/overlap.ts +7 -1
  329. package/src/loops/orchestrator/resync-shadow.ts +1 -1
  330. package/src/loops/orchestrator/tick.ts +35 -2
  331. package/src/loops/orchestrator/traffic.ts +2 -0
  332. package/src/loops/registry.ts +5 -0
  333. package/src/market/publish.ts +17 -1
  334. package/src/maturity/feature-maturity.ts +4 -2
  335. package/src/mss/logging/redaction.ts +17 -0
  336. package/src/nexus/api/draft-metrics.ts +128 -0
  337. package/src/nexus/api/graph-peer-edit.ts +77 -0
  338. package/src/nexus/api/graph-wizard-api.ts +61 -0
  339. package/src/nexus/api/graphs-api.ts +193 -17
  340. package/src/nexus/api/harness-traversal.ts +205 -0
  341. package/src/nexus/api/http-server.ts +122 -6
  342. package/src/nexus/api/plugins-market.ts +24 -0
  343. package/src/nexus/index.ts +3 -2
  344. package/src/nexus/outbound/router.ts +28 -8
  345. package/src/oauth/claude-pty-maturity.ts +24 -0
  346. package/src/oauth/fallback-chain.ts +27 -14
  347. package/src/onboarding/first-run.ts +3 -0
  348. package/src/ops/overview-collect.ts +345 -0
  349. package/src/ops/overview-seat-era.ts +90 -0
  350. package/src/ops/overview.ts +223 -0
  351. package/src/plugins/maker/market-bundle.ts +9 -0
  352. package/src/plugins/maker/plugin-maker.ts +9 -3
  353. package/src/plugins/maker/wizard-generate.ts +73 -9
  354. package/src/policy/llm-policy.ts +11 -6
  355. package/src/release-loop/checklist-board.ts +27 -0
  356. package/src/release-loop/checklist.ts +38 -0
  357. package/src/release-loop/gate-shards.ts +7 -2
  358. package/src/seat-address/seat-address.ts +1 -0
  359. package/src/seat-loop/seat-loop.ts +174 -20
  360. package/src/seat-loop/seat-report.ts +29 -1
  361. package/src/self-dev/ask-launch-flow.ts +4 -2
  362. package/src/self-dev/ask-launch-io.ts +3 -1
  363. package/src/self-dev/dev-cli.ts +90 -8
  364. package/src/self-dev/dev-pipeline.ts +20 -1
  365. package/src/self-dev/draft-sweep.ts +45 -8
  366. package/src/self-dev/graph-journey-nodes.ts +157 -0
  367. package/src/self-dev/graph-journey-route.ts +105 -0
  368. package/src/self-dev/launch-preflight.ts +43 -3
  369. package/src/self-dev/orchestrate.ts +3 -0
  370. package/src/self-dev/release-path-guard.ts +7 -4
  371. package/src/self-dev/review-provider-fallback.ts +83 -0
  372. package/src/self-dev/run-supervisor.ts +5 -1
  373. package/src/self-implement/frozen-merges.ts +23 -0
  374. package/src/self-implement/goal-author.ts +14 -0
  375. package/src/self-implement/harness-policy-gates.ts +7 -5
  376. package/src/self-implement/heal-intake.ts +3 -0
  377. package/src/self-implement/host-regate.ts +28 -14
  378. package/src/self-implement/l8-merge-queue.ts +80 -8
  379. package/src/self-implement/orchestrator.ts +84 -28
  380. package/src/self-implement/seams.ts +8 -3
  381. package/src/self-implement/self-implement-cli.ts +4 -0
  382. package/src/self-implement/self-implement-runtime.ts +12 -0
  383. package/src/session-runtime/index.ts +5 -3
  384. package/src/shell-env-bootstrap.ts +35 -6
  385. package/src/steward/triage.ts +25 -8
  386. package/src/storage/file-lock.ts +33 -4
  387. package/src/task-agent/card-evidence.ts +6 -0
  388. package/src/task-agent/completion-evidence.ts +31 -3
  389. package/src/task-agent/cover.ts +61 -0
  390. package/src/task-agent/live-moves.ts +126 -3
  391. package/src/task-agent/parent-host.ts +315 -0
  392. package/src/task-agent/shadow.ts +19 -4
  393. package/src/task-agent/task-hand.ts +11 -2
  394. package/src/task-orchestrator/surfaces/pod-command-job.ts +41 -6
  395. package/src/task-orchestrator/surfaces/pod-gate-deps.ts +123 -0
  396. package/src/task-orchestrator/surfaces/pod-pool.ts +138 -15
  397. package/src/task-orchestrator/surfaces/self-implement-pod.ts +39 -6
  398. package/src/task-orchestrator/surfaces/self-implement.ts +7 -0
  399. package/src/telegram-commands.ts +8 -0
  400. package/src/telegram-project-command.ts +6 -0
  401. package/src/telegram.ts +2 -2
  402. package/src/tool-runtime/browser-runtime.ts +87 -3
  403. package/src/tool-runtime/index.ts +1 -0
  404. package/src/typecheck-ratchet.ts +2 -1
  405. package/src/user-config.ts +55 -8
  406. package/src/version/packed-revision.json +1 -1
  407. package/src/workflow-runtime/executor.ts +24 -2
  408. package/src/workflow-runtime/triggers/registry.ts +27 -0
  409. package/src/workflow-runtime/triggers/schedule-source.ts +30 -3
  410. package/apps/pwa/out/_next/static/Bam1YziK2iH9bN4K0utGk/_buildManifest.js +0 -1
  411. package/apps/pwa/out/_next/static/chunks/1307-77fad80cf54477c6.js +0 -1
  412. package/apps/pwa/out/_next/static/chunks/1612-f60c0c93ec773f81.js +0 -1
  413. package/apps/pwa/out/_next/static/chunks/1844-9ec4b1d12f6e80eb.js +0 -1
  414. package/apps/pwa/out/_next/static/chunks/1866-e7a6999f25548b30.js +0 -1
  415. package/apps/pwa/out/_next/static/chunks/1948.15f356b15a625597.js +0 -5
  416. package/apps/pwa/out/_next/static/chunks/2116-6429c188eb846ab8.js +0 -1
  417. package/apps/pwa/out/_next/static/chunks/224-e7be8f762fe74986.js +0 -1
  418. package/apps/pwa/out/_next/static/chunks/2549-89c518a4c8ed4d00.js +0 -1
  419. package/apps/pwa/out/_next/static/chunks/2957-96ffab3d521fb08f.js +0 -1
  420. package/apps/pwa/out/_next/static/chunks/3857-995ea9f5ef33d71e.js +0 -1
  421. package/apps/pwa/out/_next/static/chunks/5360-be1f1d8f9fcc9755.js +0 -1
  422. package/apps/pwa/out/_next/static/chunks/5755-ea7ed88eb956f47b.js +0 -1
  423. package/apps/pwa/out/_next/static/chunks/5977-94ed6f78e9814b2b.js +0 -1
  424. package/apps/pwa/out/_next/static/chunks/7748-6e51bfe927019f5c.js +0 -25
  425. package/apps/pwa/out/_next/static/chunks/9247-c1753a059e7bac08.js +0 -1
  426. package/apps/pwa/out/_next/static/chunks/a9f2b54d-33c0689b70ed8cf3.js +0 -1
  427. package/apps/pwa/out/_next/static/chunks/app/bots/page-7a852956c74015a3.js +0 -1
  428. package/apps/pwa/out/_next/static/chunks/app/ceo/page-c5a5c621be0e04ae.js +0 -1
  429. package/apps/pwa/out/_next/static/chunks/app/design-check/page-7c058069c5e7fa63.js +0 -1
  430. package/apps/pwa/out/_next/static/chunks/app/editor/page-dc5da0b8a2edb8a4.js +0 -1
  431. package/apps/pwa/out/_next/static/chunks/app/inside/page-ed4a6f799f005d09.js +0 -1
  432. package/apps/pwa/out/_next/static/chunks/app/intake/page-9cd565005b93aca6.js +0 -1
  433. package/apps/pwa/out/_next/static/chunks/app/layout-55f0f40c6db95f15.js +0 -1
  434. package/apps/pwa/out/_next/static/chunks/app/live/page-474cb5888ea0e8f3.js +0 -1
  435. package/apps/pwa/out/_next/static/chunks/app/market/page-9ca21f9ad3208a61.js +0 -1
  436. package/apps/pwa/out/_next/static/chunks/app/not-found-a9c2cfa8dda922a5.js +0 -1
  437. package/apps/pwa/out/_next/static/chunks/app/observatory/page-6da35144878feb6d.js +0 -1
  438. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-f6055495e9ed7ea8.js +0 -1
  439. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-25204694fa9984a5.js +0 -1
  440. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-a60e035f151d84d3.js +0 -1
  441. package/apps/pwa/out/_next/static/chunks/app/outputs/page-189c587f36874538.js +0 -1
  442. package/apps/pwa/out/_next/static/chunks/app/page-c56634bde705a65d.js +0 -1
  443. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a8d2fe0c669e0de4.js +0 -1
  444. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-c26fa10aac6c21c4.js +0 -1
  445. package/apps/pwa/out/_next/static/chunks/app/setup/page-68793c6e10331d66.js +0 -1
  446. package/apps/pwa/out/_next/static/chunks/app/showroom/page-d81c07a7b4f618ae.js +0 -1
  447. package/apps/pwa/out/_next/static/chunks/app/tasks/page-be1c4e8697f22360.js +0 -1
  448. package/apps/pwa/out/_next/static/chunks/app/today/page-a4717e2c4bf7a28f.js +0 -1
  449. package/apps/pwa/out/_next/static/chunks/app/trace/page-0fd895fab9b62c1b.js +0 -1
  450. package/apps/pwa/out/_next/static/chunks/app/vault/page-8b6c1454a8e8f306.js +0 -1
  451. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-c6e4364d9de8f717.js +0 -1
  452. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-0506a1e0e4bdb2f5.js +0 -1
  453. package/apps/pwa/out/_next/static/chunks/webpack-c260571513bf43c2.js +0 -1
  454. package/apps/pwa/out/_next/static/css/c23d959d3ee15e57.css +0 -1
  455. /package/apps/pwa/out/_next/static/{Bam1YziK2iH9bN4K0utGk → aBi6EyMtLsrl7J8eH4kF7}/_ssgManifest.js +0 -0
  456. /package/skills/project-onboarding/{skill.md → SKILL.md} +0 -0
@@ -72,6 +72,7 @@ import { basename, isAbsolute, join, relative, resolve, sep } from 'node:path';
72
72
  import { homedir } from 'node:os';
73
73
  import { enqueueControlMemo, readSoftStopRequestStatus, type SoftStopRequestRead } from '../harness/control-inbox.js';
74
74
  import { debug } from '../debug/log.js';
75
+ import { enterJourneyNode, exitJourneyNode, journeyJoinFields, withJourneyNode } from '../self-dev/graph-journey-nodes.js';
75
76
  import { classifyGateEnvDeficit, DEFAULT_GATE_ENV_RETRY, GATE_ENV_DEFICIT_HARVESTABLE_MARK, type GateEnvDeficitKind } from './gate-env-retry.js';
76
77
  import { recordFailureEvent } from './heal-intake.js';
77
78
  import { classifyReworkCapExhaustion, type ReworkCapHealDecision } from './heal-triage.js';
@@ -433,7 +434,7 @@ function resolveDefaultBranchTarget(
433
434
  * 🔑 실패일 때만 두 칸을 더한다 — 성공 경로 payload 를 바꾸면 기존 소비자가 흔들린다.
434
435
  * `moduleLoadFailure` 는 ***「산출이 상했다」와 「그 순간 트리가 불일치했다」***를 가르는 값이다.
435
436
  */
436
- type PostSyncStatus = 'merged' | 'llm-resolved';
437
+ type PostSyncStatus = 'merged' | 'llm-resolved' | 'deterministic-resolved';
437
438
 
438
439
  type PostSyncGatePolicy = {
439
440
  exempted: boolean;
@@ -494,7 +495,7 @@ function postSyncGatePolicy(
494
495
  && !introduced && childResponsibility === 'none'
495
496
  && syncStatus === 'llm-resolved';
496
497
  const exempted = !passed
497
- && (timeoutOnly || (!introduced && childResponsibility === 'none' && syncStatus === 'merged'));
498
+ && (timeoutOnly || (!introduced && childResponsibility === 'none' && (syncStatus === 'merged' || syncStatus === 'deterministic-resolved')));
498
499
  return {
499
500
  exempted,
500
501
  exemptionWithheld,
@@ -975,6 +976,8 @@ export interface SelfImplementSeams {
975
976
  readSoftStopRequestStatus?: (spaceId: string) => SoftStopRequestRead;
976
977
  /** Parent harness space whose durable stop marker is read. Tests inject this; production uses `getHarnessSpace()`. */
977
978
  parentSoftStopSpaceId?: string;
979
+ /** Real, created worktree's soft-stop target; observation only, never guesses a space. */
980
+ onSoftStopSpaceReady?: (spaceId: string) => void;
978
981
  /** disposable worktree 생성 → 경로·브랜치 ⊕ **어디서 갈랐나**(관측용).
979
982
  * ⚠️ 관측 3필드는 **선택**이다 — 시임은 주입점이고, 테스트 더블에게 git SHA 를 지어내라고
980
983
  * 요구하면 계약이 아니라 부담이 된다. 실 시임(`defaultSeams`)은 항상 채운다.
@@ -2253,6 +2256,8 @@ export interface SelfImplementResult {
2253
2256
  branch?: string;
2254
2257
  gate?: SelfImplementGateResult;
2255
2258
  review?: SelfImplementReview;
2259
+ /** Worktree commit observed while this review ran; not inferred from the later PR head. */
2260
+ reviewedHeadCommit?: string;
2256
2261
  completionDisposition?: SelfImplementCompletionDisposition;
2257
2262
  /** Explicit supervisor rework verdict retained independently of workflow execution verdicts. */
2258
2263
  supervisorVerdict?: ReworkBudgetVerdict;
@@ -2359,7 +2364,18 @@ function truncatePrTitle(text: string): string {
2359
2364
  return points.length <= 72 ? text : `${points.slice(0, 69).join('')}...`;
2360
2365
  }
2361
2366
 
2362
- export function prTitle(feature: string): string {
2367
+ export function goalChecklistIds(feature: string, goalFile?: string): string[] {
2368
+ const document = goalFile ? goalDocumentForReleaseNote(goalFile) : feature;
2369
+ const declaration = document.split(/\r?\n/).find((line) => line.startsWith('- 칸 id: '));
2370
+ const declared = declaration?.slice('- 칸 id: '.length).trim();
2371
+ if (declared && declared !== 'not declared') {
2372
+ return [...new Set(declared.split(' · ').map((id) => id.trim()).filter(Boolean))];
2373
+ }
2374
+ const firstLine = feature.split(/\r?\n/, 1)[0] ?? '';
2375
+ return [...new Set(Array.from(firstLine.matchAll(/\b[A-Z][A-Z0-9]+-[A-Z0-9]+\b/g), (match) => match[0]))];
2376
+ }
2377
+
2378
+ function prTitleWithoutChecklistId(feature: string): string {
2363
2379
  const proseTitle = parseAskProseTitle(feature);
2364
2380
  if (proseTitle) return truncatePrTitle(proseTitle);
2365
2381
 
@@ -2402,6 +2418,13 @@ export function prTitle(feature: string): string {
2402
2418
  return truncatePrTitle(firstFileName);
2403
2419
  }
2404
2420
 
2421
+ export function prTitle(feature: string, goalFile?: string): string {
2422
+ const title = prTitleWithoutChecklistId(feature);
2423
+ const id = goalChecklistIds(feature, goalFile)[0];
2424
+ if (!id) return title;
2425
+ return truncatePrTitle(title.startsWith(`${id}: `) ? title : `${id}: ${title}`);
2426
+ }
2427
+
2405
2428
  const BLOCKED_DRAFT_PR_HUMAN_JUDGMENT_ANCHOR = '## 사람 판단 필요';
2406
2429
 
2407
2430
  /** 사람이 읽는 중단 사유 상한. 관측 `reason.slice(0, 240)` 과 같은 길이라 표시가 앞부분을 버리지 않는다. */
@@ -2662,6 +2685,7 @@ function prBody(feature: string, implSummary: string, gateLog?: string, review?:
2662
2685
  ? ['', '## 감독 수용 기준 완화', `- 대상: ${planRevision.relaxation.target}`, `- 이전: ${planRevision.relaxation.expected}`, `- 완화: ${planRevision.relaxation.replacement}`, `- 이유: ${planRevision.reason}`, `- 적용: ${planRevision.application?.status ?? 'failed'}${planRevision.application?.detail ? ` (${planRevision.application.detail})` : ''}`, ...(planRevision.disposition ? [`- 충돌 처분: ${planRevision.disposition}`] : [])]
2663
2686
  : [];
2664
2687
  return [
2688
+ `칸: ${goalChecklistIds(feature, goalFile).join(' · ') || '없음'}`,
2665
2689
  '## 요청',
2666
2690
  ...(goalFile ? [`- 골 파일: ${formatGoalFileForPrBody(goalFile)}`] : []),
2667
2691
  feature.trim(),
@@ -2879,6 +2903,7 @@ export function formatBlockedDraftClassificationSection(
2879
2903
 
2880
2904
  function blockedDraftPrBody(feature: string, implSummary: string, state: BlockedDraftPrState, goalFile?: string): string {
2881
2905
  return [
2906
+ `칸: ${goalChecklistIds(feature, goalFile).join(' · ') || '없음'}`,
2882
2907
  BLOCKED_DRAFT_PR_HUMAN_JUDGMENT_ANCHOR,
2883
2908
  state.salvageStatusExpected
2884
2909
  ? BLOCKED_DRAFT_PR_SALVAGE_STATUS_INSTRUCTION
@@ -3635,7 +3660,7 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
3635
3660
  }
3636
3661
  try {
3637
3662
  const pushed = opts.seams.preserveBlockedBranch
3638
- ? await opts.seams.preserveBlockedBranch({ cwd: worktreePath, branch, title: prTitle(opts.feature), commit: preSyncCommit })
3663
+ ? await opts.seams.preserveBlockedBranch({ cwd: worktreePath, branch, title: prTitle(opts.feature, opts.goalFile), commit: preSyncCommit })
3639
3664
  : runGitCommand(worktreePath, ['push', '-u', 'origin', `${preSyncCommit}:refs/heads/${branch}`]).status === 0;
3640
3665
  if (!pushed) throw new Error('branch push failed');
3641
3666
  // The worktree is left untouched: a late merge may still be writing it. The harvest source is the pinned commit.
@@ -3805,6 +3830,8 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
3805
3830
  const attemptOrdinal = readRunAttemptOrdinal(runId);
3806
3831
  const identified = {
3807
3832
  ...graphIdentity,
3833
+ // HARNESS-FULL-GRAPH — 앞단 여정 걸음(runId 없음)과 이 런의 걸음을 잇는 조인 키.
3834
+ ...journeyJoinFields(),
3808
3835
  ...data,
3809
3836
  attemptOrdinal,
3810
3837
  };
@@ -4958,7 +4985,7 @@ async function runSelfImplementInner(
4958
4985
  role: describeRole({ role: 'executor', executorKind: 'self' }),
4959
4986
  entry: siEntry, capabilities: [...siCaps.active], enhanceActive,
4960
4987
  });
4961
- progress('start', `🔨 self-implement 시작 — ${prTitle(opts.feature)}`);
4988
+ progress('start', `🔨 self-implement 시작 — ${prTitle(opts.feature, opts.goalFile)}`);
4962
4989
 
4963
4990
  let sessionId: string | undefined;
4964
4991
  if (opts.parentSessionId && s.forkSession) {
@@ -5019,6 +5046,9 @@ async function runSelfImplementInner(
5019
5046
  // 그래서 getHarnessSpace() 만 보면 운영에서 늘 undefined 이고 정지 확인이 «한 번도» 안 돈다(#20040 착지 직후 실측).
5020
5047
  // `self send <space> --stop` 이 겨냥하는 것은 자식의 공간 = 워크트리 이름이다(아래 activeChildInboxId 와 같은 식 · dev-cli.ts recordRestart 폴백과 같다).
5021
5048
  const parentSoftStopSpaceId = s.parentSoftStopSpaceId || getHarnessSpace()?.id || normalizeSpaceId(basename(wt.path)) || undefined;
5049
+ if (parentSoftStopSpaceId) {
5050
+ try { s.onSoftStopSpaceReady?.(parentSoftStopSpaceId); } catch { /* UI registration must not break execution */ }
5051
+ }
5022
5052
  const readParentSoftStop = s.readSoftStopRequestStatus ?? readSoftStopRequestStatus;
5023
5053
  const honorParentSoftStop = (
5024
5054
  beforeNode: 'gate' | 'review' | 'rework' | 'open-pr',
@@ -5194,6 +5224,7 @@ async function runSelfImplementInner(
5194
5224
  /** GATE-ENV-RETRY: 재시도 뒤에도 남은 환경 결손의 종류. 정의되면 이 라운드의 gate 실패는 «수확 대기»로 끝난다. */
5195
5225
  let gateEnvDeficitKind: GateEnvDeficitKind | undefined;
5196
5226
  let review: SelfImplementReview | undefined;
5227
+ let reviewedHeadCommit: string | undefined;
5197
5228
  /** Review-budget leftovers and the body that carried them, so a later merge drafts from the same text. */
5198
5229
  let preparedReviewBudgetPrBody: string | undefined;
5199
5230
  const reviewMustFixCountHistory: number[] = [];
@@ -5783,11 +5814,11 @@ async function runSelfImplementInner(
5783
5814
  throw new Error(`worktree not found: ${wt.path}`);
5784
5815
  }
5785
5816
  if (s.preserveBlockedBranch) {
5786
- pushed = await s.preserveBlockedBranch({ cwd: wt.path, branch: wt.branch, title: prTitle(opts.feature) });
5817
+ pushed = await s.preserveBlockedBranch({ cwd: wt.path, branch: wt.branch, title: prTitle(opts.feature, opts.goalFile) });
5787
5818
  if (!pushed) throw new Error('브랜치 push가 성공하지 않았음');
5788
5819
  } else {
5789
5820
  preservationErrorStep = 'commit';
5790
- const commit = commitWorktree(wt.path, prTitle(opts.feature));
5821
+ const commit = commitWorktree(wt.path, prTitle(opts.feature, opts.goalFile));
5791
5822
  if (!commit.ok && !/nothing to commit/.test(commit.out)) throw new Error(commit.out);
5792
5823
  committed = commit.ok;
5793
5824
  if (runGitCommand(wt.path, ['remote', 'get-url', 'origin']).status === 0) {
@@ -5867,10 +5898,10 @@ async function runSelfImplementInner(
5867
5898
  let salvageError: string | undefined;
5868
5899
  try {
5869
5900
  if (s.preserveBlockedBranch) {
5870
- if (!await s.preserveBlockedBranch({ cwd: wt.path, branch: salvageBranch, title: prTitle(opts.feature) })) throw new Error('수확 가지 push가 성공하지 않았음');
5901
+ if (!await s.preserveBlockedBranch({ cwd: wt.path, branch: salvageBranch, title: prTitle(opts.feature, opts.goalFile) })) throw new Error('수확 가지 push가 성공하지 않았음');
5871
5902
  } else {
5872
5903
  if (!existsSync(wt.path)) throw new Error(`worktree not found: ${wt.path}`);
5873
- const commit = commitWorktree(wt.path, prTitle(opts.feature));
5904
+ const commit = commitWorktree(wt.path, prTitle(opts.feature, opts.goalFile));
5874
5905
  if (!commit.ok && !/nothing to commit/.test(commit.out)) throw new Error(`로컬 커밋 실패: ${commit.out}`);
5875
5906
  const push = runGitCommand(wt.path, ['push', 'origin', `HEAD:refs/heads/${salvageBranch}`]);
5876
5907
  if (push.status !== 0) throw new Error(push.stderr || push.stdout || 'git push failed');
@@ -5907,10 +5938,10 @@ async function runSelfImplementInner(
5907
5938
  let pushed = false;
5908
5939
  try {
5909
5940
  if (s.preserveBlockedBranch) {
5910
- pushed = await s.preserveBlockedBranch({ cwd: wt.path, branch: wt.branch, title: prTitle(opts.feature) });
5941
+ pushed = await s.preserveBlockedBranch({ cwd: wt.path, branch: wt.branch, title: prTitle(opts.feature, opts.goalFile) });
5911
5942
  } else {
5912
5943
  if (commitFirst) {
5913
- const commit = commitWorktree(wt.path, prTitle(opts.feature));
5944
+ const commit = commitWorktree(wt.path, prTitle(opts.feature, opts.goalFile));
5914
5945
  if (!commit.ok && !/nothing to commit/.test(commit.out)) throw new Error(`로컬 브랜치 커밋 실패: ${commit.out}`);
5915
5946
  }
5916
5947
  const push = runGitCommand(wt.path, ['push', '-u', 'origin', `HEAD:refs/heads/${wt.branch}`]);
@@ -5934,7 +5965,7 @@ async function runSelfImplementInner(
5934
5965
  if (stage === 'review-blocked' && podReviewBlockedDraft) {
5935
5966
  let commitError: string | undefined;
5936
5967
  try {
5937
- const commit = commitWorktree(wt.path, prTitle(opts.feature));
5968
+ const commit = commitWorktree(wt.path, prTitle(opts.feature, opts.goalFile));
5938
5969
  if (!commit.ok && !/nothing to commit/.test(commit.out)) commitError = commit.out.trim() || 'git commit failed';
5939
5970
  } catch (error) {
5940
5971
  commitError = safeErrorDescription(error);
@@ -5946,7 +5977,7 @@ async function runSelfImplementInner(
5946
5977
  let pr: { url: string; number: number };
5947
5978
  try {
5948
5979
  pr = await withStepTimeout(s.openPr({
5949
- title: prTitle(opts.feature),
5980
+ title: prTitle(opts.feature, opts.goalFile),
5950
5981
  body: assembled.body,
5951
5982
  head: wt.branch,
5952
5983
  ...(opts.base ? { base: opts.base } : {}),
@@ -6102,7 +6133,7 @@ async function runSelfImplementInner(
6102
6133
  );
6103
6134
  preparedReviewBudgetPrBody = preparedPrBody.body;
6104
6135
  const pr = await withStepTimeout(s.openPr({
6105
- title: prTitle(opts.feature),
6136
+ title: prTitle(opts.feature, opts.goalFile),
6106
6137
  body: preparedPrBody.body,
6107
6138
  head: wt.branch,
6108
6139
  ...(opts.base ? { base: opts.base } : {}),
@@ -6818,7 +6849,7 @@ async function runSelfImplementInner(
6818
6849
  && targets.some((target) => changedFileIsInsideDeclaredTarget(path, target)));
6819
6850
  if (eligible.length) {
6820
6851
  inspectStaleReverts(revertGuardTargetPaths(opts.feature, goalDocument));
6821
- const message = `self-implement: ${prTitle(opts.feature)}`;
6852
+ const message = `self-implement: ${prTitle(opts.feature, opts.goalFile)}`;
6822
6853
  let outcome: { ok: boolean; out: string };
6823
6854
  try {
6824
6855
  outcome = (s.commitFinishedChildWork ?? ((cwd, paths, title) => {
@@ -7339,6 +7370,7 @@ async function runSelfImplementInner(
7339
7370
  lastReviewContext = reviewContext;
7340
7371
  reviewIntent = buildReviewIntent(reviewContext);
7341
7372
 
7373
+ reviewedHeadCommit = undefined;
7342
7374
  if (s.reviewDiff) {
7343
7375
  try {
7344
7376
  const stoppedBeforeReview = honorParentSoftStop('review');
@@ -7348,7 +7380,18 @@ async function runSelfImplementInner(
7348
7380
  }
7349
7381
  onNodeEntry(node('review'), round);
7350
7382
  progress('reviewing', '내부 리뷰 (리뷰어 비평)…');
7383
+ const readReviewHead = s.headCommit ?? ((cwd: string) => {
7384
+ const commit = runGitCommand(cwd, ['rev-parse', '--verify', 'HEAD'], { encoding: 'utf8', timeout: 10_000 });
7385
+ return commit.status === 0 ? commit.stdout.trim() : undefined;
7386
+ });
7387
+ const headBeforeReview = readReviewHead(wt.path);
7388
+ const statusBeforeReview = runGitCommand(wt.path, ['status', '--porcelain'], { encoding: 'utf8', timeout: 10_000 });
7351
7389
  review = await withStepTimeout(s.reviewDiff(wt.path, reviewContext), T.review, 'review');
7390
+ const statusAfterReview = runGitCommand(wt.path, ['status', '--porcelain'], { encoding: 'utf8', timeout: 10_000 });
7391
+ reviewedHeadCommit = review.reviewed && headBeforeReview && /^[0-9a-f]{40}$/i.test(headBeforeReview)
7392
+ && statusBeforeReview.status === 0 && statusBeforeReview.stdout.trim() === ''
7393
+ && statusAfterReview.status === 0 && statusAfterReview.stdout.trim() === ''
7394
+ && headBeforeReview === readReviewHead(wt.path) ? headBeforeReview : undefined;
7352
7395
  } catch (error) {
7353
7396
  finalizeSupervisorDeliveries(error instanceof StepTimeoutError ? 'next-round-review-timeout' : 'next-round-review-failed');
7354
7397
  throw error;
@@ -7587,6 +7630,7 @@ async function runSelfImplementInner(
7587
7630
  ? { completionDisposition }
7588
7631
  : {};
7589
7632
  const reviewReal = !!review?.reviewed;
7633
+ const reviewedHeadResult = reviewReal && reviewedHeadCommit ? { reviewedHeadCommit } : {};
7590
7634
  const reviewDiffComplete = review?.diffTruncated === false;
7591
7635
  const requiredEvidenceMissing = evidenceCoverage !== undefined && evidenceCoverage.uncoveredCount > 0;
7592
7636
  const { latestSignalIncomplete } = signalIncompleteState(roundClassifications);
@@ -7672,7 +7716,7 @@ async function runSelfImplementInner(
7672
7716
  try { goalDocument = readFileSync(opts.goalFile, 'utf8'); } catch { /* no documented targets */ }
7673
7717
  }
7674
7718
  inspectStaleReverts(revertGuardTargetPaths(opts.feature, goalDocument));
7675
- s.commitWork(wt.path, prTitle(opts.feature));
7719
+ s.commitWork(wt.path, prTitle(opts.feature, opts.goalFile));
7676
7720
  // ⛔⭐ resume 경로와 «같은» 해석기를 쓴다 — 두 자리가 갈리면 같은 원인이 다른 얼굴로 나온다.
7677
7721
  const resolvedTarget = resolveDefaultBranchTarget(s.defaultBranchRef ?? defaultBranchRef, wt.path);
7678
7722
  const mergeTarget = resolvedTarget.target;
@@ -7717,9 +7761,9 @@ async function runSelfImplementInner(
7717
7761
  && await preserveMergeHarvest(sync.reason, sync.inputChars);
7718
7762
  return { ok: false, stage: 'merge-conflict', node: 'main-sync', ...resolveRunOutcome({ termination: 'abandoned' }), ...decisionSignalResult, sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), ...(harvestable ? { harvestable: true } : {}), detail: harvestable ? sync.reason === 'conflict-input-too-large' ? `충돌 큼 — 사람 수확 (${wt.branch}, 입력 ${sync.inputChars}자)` : `충돌 해결 중단 — 사람 수확 (${wt.branch})` : `pre-PR main sync ${sync.status}` };
7719
7763
  }
7720
- if (sync.status === 'llm-resolved' || sync.status === 'merged') {
7764
+ if (sync.status === 'llm-resolved' || sync.status === 'merged' || sync.status === 'deterministic-resolved') {
7721
7765
  preservationBase = opts.base ?? mergeTarget;
7722
- progress('gating', sync.status === 'llm-resolved'
7766
+ progress('gating', sync.status === 'llm-resolved' || sync.status === 'deterministic-resolved'
7723
7767
  ? `${mergeTarget} 충돌해결됨 — ${formatLlmMergeOutcome(sync)} — 통합 결과 full 재-gate…`
7724
7768
  : `${mergeTarget} 정합됨 — 통합 결과 full 재-gate…`);
7725
7769
  onNodeEntry('regate', round);
@@ -7872,7 +7916,7 @@ async function runSelfImplementInner(
7872
7916
  let pr: { url: string; number: number };
7873
7917
  try {
7874
7918
  pr = await withStepTimeout(s.openPr({
7875
- title: prTitle(opts.feature),
7919
+ title: prTitle(opts.feature, opts.goalFile),
7876
7920
  body: preparedPrBody.body,
7877
7921
  head: wt.branch,
7878
7922
  ...(opts.base ? { base: opts.base } : {}),
@@ -7894,6 +7938,11 @@ async function runSelfImplementInner(
7894
7938
  progress('aborted', formatPrOpenFailedProgress(location, prErrorFirstLine));
7895
7939
  throw new Error(`${location}\n${prError}`);
7896
7940
  }
7941
+ // PR creation may commit generated body/release notes or sync the base after review. Never bind an old review to that new head.
7942
+ if (reviewedHeadCommit) {
7943
+ const current = runGitCommand(wt.path, ['rev-parse', '--verify', 'HEAD'], { encoding: 'utf8', timeout: 10_000 });
7944
+ if (current.status !== 0 || current.stdout.trim() !== reviewedHeadCommit) delete reviewedHeadResult.reviewedHeadCommit;
7945
+ }
7897
7946
  observe('pr-opened', {
7898
7947
  url: pr.url,
7899
7948
  number: pr.number,
@@ -7943,7 +7992,7 @@ async function runSelfImplementInner(
7943
7992
  }
7944
7993
  recordMergeDecision();
7945
7994
  progress('pr-opened', `OP 승인 대기 (#${pr.number}): ${releaseHoldPath}`);
7946
- return { ok: true, stage: 'pr-opened', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), mergeReason, prUrl: pr.url, prNumber: pr.number, detail: `OP approval required: ${releaseHoldPath}` };
7995
+ return { ok: true, stage: 'pr-opened', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), ...reviewedHeadResult, mergeReason, prUrl: pr.url, prNumber: pr.number, detail: `OP approval required: ${releaseHoldPath}` };
7947
7996
  }
7948
7997
  if (!releasePathInspectionError && labels?.includes(AUTO_REVIEW_LABEL)) {
7949
7998
  try {
@@ -7961,7 +8010,7 @@ async function runSelfImplementInner(
7961
8010
  : 'release-path-inspection-failed';
7962
8011
  recordMergeDecision();
7963
8012
  progress('pr-opened', `OP 승인 대기 (#${pr.number}): PR 경로 조회 실패`);
7964
- return { ok: true, stage: 'pr-opened', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), mergeReason, prUrl: pr.url, prNumber: pr.number };
8013
+ return { ok: true, stage: 'pr-opened', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), ...reviewedHeadResult, mergeReason, prUrl: pr.url, prNumber: pr.number };
7965
8014
  }
7966
8015
 
7967
8016
  if (canAuto && (s.mergePr || opts.mergeByHost)) {
@@ -8011,12 +8060,12 @@ async function runSelfImplementInner(
8011
8060
  // 판정(mergeReason)이 아니라 «집행 직전 가드»가 막았으므로 그 사실을 값으로 말한다.
8012
8061
  // ⛔ 안 그러면 마지막 줄이 침묵하고, 그건 이 착지가 없애려던 상태다(리뷰 must-fix 2R).
8013
8062
  const guardSkipReason = mergeSkipReason ?? (guardFailure ? 'merge-guard-unevaluated' : 'docs-deletion-threshold');
8014
- return { ok: true, stage: 'pr-opened', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), mergeReason: guardSkipReason, prUrl: pr.url, prNumber: pr.number, detail };
8063
+ return { ok: true, stage: 'pr-opened', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), ...reviewedHeadResult, mergeReason: guardSkipReason, prUrl: pr.url, prNumber: pr.number, detail };
8015
8064
  }
8016
8065
  if (opts.mergeByHost) {
8017
8066
  observe('merge-ready', { number: pr.number, checkedHeadCommit });
8018
8067
  progress('pr-opened', `호스트 재게이트 대기 (#${pr.number})`);
8019
- return { ok: true, stage: 'merge-ready', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), checkedHeadCommit: checkedHeadCommit!, prUrl: pr.url, prNumber: pr.number };
8068
+ return { ok: true, stage: 'merge-ready', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), ...reviewedHeadResult, checkedHeadCommit: checkedHeadCommit!, prUrl: pr.url, prNumber: pr.number };
8020
8069
  }
8021
8070
  // Landing freeze: the in-flight marker is written before the freeze is read, so a `freeze on` racing this merge waits.
8022
8071
  // The resume sweep merges from the repository that owns the run worktree (the worktree itself may be cleaned up).
@@ -8028,13 +8077,13 @@ async function runSelfImplementInner(
8028
8077
  };
8029
8078
  const landing = admitLandingMerge(heldEntry, undefined, {}, { prNumber: pr.number, repoRoot: owningRepoRoot(wt.path) ?? wt.path, ...(checkedHeadCommit ? { headCommit: checkedHeadCommit } : {}) });
8030
8079
  if (landing.kind === 'held' && !(checkedHeadCommit && /^[0-9a-f]{40}$/i.test(checkedHeadCommit))) {
8031
- return { ok: true, stage: 'pr-opened', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), mergeReason: 'merge-guard-unevaluated', prUrl: pr.url, prNumber: pr.number, detail: 'freeze: checked head SHA unavailable; PR left ready for review' };
8080
+ return { ok: true, stage: 'pr-opened', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), ...reviewedHeadResult, mergeReason: 'merge-guard-unevaluated', prUrl: pr.url, prNumber: pr.number, detail: 'freeze: checked head SHA unavailable; PR left ready for review' };
8032
8081
  }
8033
8082
  if (landing.kind !== 'merge') {
8034
8083
  const detail = landing.kind === 'held' ? landingFreezeMessage(landing.freeze) : 'freeze lifted; a resume sweep is merging this PR';
8035
8084
  debug.log('harness.merge', 'frozen', { pr: pr.number, ...(landing.kind === 'held' ? { reason: landing.freeze.reason, until: landing.freeze.until } : { resumedElsewhere: true }) });
8036
8085
  progress('pr-opened', `#${pr.number} ready · ${detail}`);
8037
- return { ok: true, stage: 'pr-opened', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), mergeReason: landing.kind === 'held' ? 'frozen' : 'resumed-elsewhere', prUrl: pr.url, prNumber: pr.number, detail };
8086
+ return { ok: true, stage: 'pr-opened', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), ...reviewedHeadResult, mergeReason: landing.kind === 'held' ? 'frozen' : 'resumed-elsewhere', prUrl: pr.url, prNumber: pr.number, detail };
8038
8087
  }
8039
8088
  // The marker and claim live until the merge call itself settles — a step timeout does not stop a merge already
8040
8089
  // sent — and anything that throws before the merge call is made releases them.
@@ -8088,7 +8137,14 @@ async function runSelfImplementInner(
8088
8137
  }
8089
8138
  const defaultBranch = resolveDefaultBranchTarget(s.defaultBranchRef ?? defaultBranchRef, wt.path).target ?? undefined;
8090
8139
  progress('merged', formatAutoMergeSuccessMessage({ prNumber: pr.number, mergedBase, defaultBranch }));
8091
- await runPostMergeCleanup(s.postMergeCleanup, wt.path, wt.branch, opts.goalFile, mergedBase && defaultBranch && displayBranchName(mergedBase) === displayBranchName(defaultBranch) ? defaultBranch : undefined, observe);
8140
+ // HARNESS-FULL-GRAPH — 회수도 그래프 노드(cleanup)다. 결과 = completed | preserved | failed.
8141
+ let cleanupOutcome = 'completed';
8142
+ enterJourneyNode('cleanup', { provenance: 'post-merge', runId });
8143
+ await withJourneyNode('cleanup', { provenance: 'post-merge', runId }, () => runPostMergeCleanup(s.postMergeCleanup, wt.path, wt.branch, opts.goalFile, mergedBase && defaultBranch && displayBranchName(mergedBase) === displayBranchName(defaultBranch) ? defaultBranch : undefined, (event, data) => {
8144
+ if (event.startsWith('post-merge-cleanup-')) cleanupOutcome = event.slice('post-merge-cleanup-'.length);
8145
+ observe(event, data);
8146
+ }));
8147
+ exitJourneyNode('cleanup', { provenance: 'post-merge', runId, outcome: cleanupOutcome });
8092
8148
  const mergedLedger = (() => {
8093
8149
  try { return loadRunLedger(runId); } catch { return null; }
8094
8150
  })();
@@ -8117,10 +8173,10 @@ async function runSelfImplementInner(
8117
8173
  // 그대로 두면 마지막 산출 줄이 «침묵»한다 — 이 착지가 없애려던 바로 그 상태다(리뷰 must-fix).
8118
8174
  // ⇒ 판정이 아니라 «집행»에서 막힌 것이므로 그 사실을 별도 값으로 말한다.
8119
8175
  const mergeAttemptSkipReason = mergeSkipReason ?? 'merge-attempt-failed';
8120
- return { ok: true, stage: 'pr-opened', node: 'merge', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), mergeReason: mergeAttemptSkipReason, merged: false, prUrl: pr.url, prNumber: pr.number, ...(m.detail ? { detail: m.detail } : {}) };
8176
+ return { ok: true, stage: 'pr-opened', node: 'merge', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), ...reviewedHeadResult, mergeReason: mergeAttemptSkipReason, merged: false, prUrl: pr.url, prNumber: pr.number, ...(m.detail ? { detail: m.detail } : {}) };
8121
8177
  }
8122
8178
  progress('pr-opened', `✅ PR 개설 (#${pr.number})`);
8123
- return { ok: true, stage: 'pr-opened', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), ...(lastSupervisorReason ? { supervisorReason: lastSupervisorReason } : {}), ...(mergeSkipReason ? { mergeReason: mergeSkipReason } : {}), prUrl: pr.url, prNumber: pr.number };
8179
+ return { ok: true, stage: 'pr-opened', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), ...reviewedHeadResult, ...(lastSupervisorReason ? { supervisorReason: lastSupervisorReason } : {}), ...(mergeSkipReason ? { mergeReason: mergeSkipReason } : {}), prUrl: pr.url, prNumber: pr.number };
8124
8180
  }
8125
8181
 
8126
8182
  /** 결정적 요청 거부 — 같은 요청을 다시 보내면 «같은» 답이 온다(BACKLOG B6).
@@ -972,6 +972,8 @@ export interface DefaultSeamsOptions {
972
972
  signal?: AbortSignal;
973
973
  /** Parent-surface progress receiver supplied by the dev pipeline. */
974
974
  onProgress?: SelfImplementSeams['onProgress'];
975
+ /** Relay the actual worktree soft-stop target to the invoking surface. */
976
+ onSoftStopSpaceReady?: SelfImplementSeams['onSoftStopSpaceReady'];
975
977
  /** ⑤ HITL — PR 승인 게이트. ★fail-closed: 생략 시 PR 안 열림(자동 승인 없음). 실전 필수. */
976
978
  approvePr?: SelfImplementSeams['approvePr'];
977
979
  /** gate 스텝(기본 ['test']). */
@@ -984,7 +986,7 @@ export interface DefaultSeamsOptions {
984
986
  runAndroidUnitTestGate?: (io: { args: readonly string[]; cwd: string }) => number;
985
987
  runIosUnitTestGate?: (io: { args: readonly string[]; cwd: string }) => number;
986
988
  /** Source-policy gate adapter for focused seam tests. */
987
- runHarnessPolicyGates?: typeof runHarnessPolicyGates;
989
+ runHarnessPolicyGates?: (input: Parameters<typeof runHarnessPolicyGates>[0]) => Awaited<ReturnType<typeof runHarnessPolicyGates>> | ReturnType<typeof runHarnessPolicyGates>;
988
990
  runExposeGate?: typeof runExposeGate;
989
991
  /** Dependency-wide validation adapter for focused seam tests. */
990
992
  runDependencyChangeGate?: typeof runDependencyChangeGate;
@@ -1018,6 +1020,8 @@ export interface DefaultSeamsOptions {
1018
1020
  * agent-substrate·staged 하니스와 동일 엔진) 배선. 미주입 시 reviewDiff 미노출(gate 통과=바로 병합결정·
1019
1021
  * 리뷰 스킵). dev-harness 의 llmReview 와 동일 형태(streamLLM 래퍼). */
1020
1022
  llmReview?: (prompt: string) => Promise<string>;
1023
+ /** Optional independent reviewer for reviewDiff only; other judgment calls retain llmReview. */
1024
+ subscriptionReviewLLM?: (prompt: string) => Promise<string>;
1021
1025
  /**
1022
1026
  * 이 리뷰 심이 파일을 스스로 읽을 수 있나. **선택 선언** — 안 주면 reviewDiff 가 칸을 만들지 않고
1023
1027
  * 오케스트레이터 관측은 `'unknown'` 이다(`false` 와 같은 값으로 접지 않는다).
@@ -1394,6 +1398,7 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
1394
1398
  };
1395
1399
  return {
1396
1400
  ...(o.onProgress ? { onProgress: o.onProgress } : {}),
1401
+ ...(o.onSoftStopSpaceReady ? { onSoftStopSpaceReady: o.onSoftStopSpaceReady } : {}),
1397
1402
  ...(escalateGoalClarifications ? { escalateGoalClarifications } : {}),
1398
1403
  queryChildProviderErrors,
1399
1404
  persistPrBodyArtifact: (input) => createArtifactStore().put('block', input.body, {
@@ -2070,7 +2075,7 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
2070
2075
  // tsc-깨짐(F1 refFacts 미정의)을 결정론 차단. 변경 전체(.ts·테스트 포함)를 tsc 로 검사(변경 경로만 카운트).
2071
2076
  const tc = changedFileTypecheck(cwd, changedAll);
2072
2077
  debug.log('self-implement', 'gate.tsc', { checked: tc.checked, passed: tc.passed, errors: tc.errors, exempted: tc.exempted, noInspectionReason: tc.noInspectionReason }, { level: tc.passed ? 'info' : 'warn' });
2073
- const policy = (o.runHarnessPolicyGates ?? runHarnessPolicyGates)({ cwd, changedFiles: changedAll });
2078
+ const policy = await (o.runHarnessPolicyGates ?? runHarnessPolicyGates)({ cwd, changedFiles: changedAll });
2074
2079
  debug.log('self-implement', 'gate.policy', {
2075
2080
  passed: policy.passed, failures: policy.failures.map((failure) => failure.gate), skipped: policy.skipped,
2076
2081
  });
@@ -2527,7 +2532,7 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
2527
2532
  ...(ctx.round !== undefined ? { round: ctx.round } : {}),
2528
2533
  },
2529
2534
  } : {}),
2530
- }, o.llmReview!);
2535
+ }, o.subscriptionReviewLLM ?? o.llmReview!);
2531
2536
  debug.log('self-implement', 'review.done', {
2532
2537
  verdict: rr.verdict, reviewed: rr.reviewed ?? false, mustFix: rr.mustFix.length, shouldFix: rr.shouldFix.length,
2533
2538
  gateEvidenceLines: evidenceNote?.split(/\r?\n/).length ?? 0,
@@ -111,6 +111,7 @@ export function singleRunAsJobResult(feature: string, r: SelfImplementResultWith
111
111
  ...(r.harvestable === true && r.branch ? { harvestable: true } : {}),
112
112
  ...(r.worktreePath ? { worktreePath: r.worktreePath } : {}),
113
113
  ...(r.prUrl ? { prUrl: r.prUrl } : {}),
114
+ ...(r.prNumber !== undefined ? { prNumber: r.prNumber } : {}),
114
115
  // TA-LIVE-REVIEW-POD — 작업 트리 없는 런(Pod)의 live review 가 «런이 낸 PR 머리»를 확인하는 근거.
115
116
  ...(r.checkedHeadCommit !== undefined ? { checkedHeadCommit: r.checkedHeadCommit } : {}),
116
117
  ...(r.merged !== undefined ? { merged: r.merged } : {}),
@@ -121,6 +122,9 @@ export function singleRunAsJobResult(feature: string, r: SelfImplementResultWith
121
122
  ...(r.decomposeProposal ? { decomposeProposal: r.decomposeProposal } : {}),
122
123
  ...(r.goalPlanRevision ? { goalPlanRevision: r.goalPlanRevision } : {}),
123
124
  ...(r.salvage !== undefined ? { salvage: r.salvage } : {}),
125
+ ...(r.review?.reviewed === true && r.review.verdict === 'pass'
126
+ && r.reviewedHeadCommit && /^[0-9a-f]{40}$/i.test(r.reviewedHeadCommit)
127
+ ? { selfReview: { verdict: r.review.verdict, head: r.reviewedHeadCommit } } : {}),
124
128
  ...(r.review?.reviewed !== undefined ? { reviewed: r.review.reviewed } : {}),
125
129
  ...(r.review?.failureReason !== undefined ? { reviewReason: r.review.failureReason } : {}),
126
130
  // ⭐ 대표 지시(2026-09-08) — 걸음을 슈퍼바이저까지 나른다.
@@ -55,6 +55,13 @@ interface SelfImplementRuntimeDeps {
55
55
  }
56
56
  let runtimeDeps: SelfImplementRuntimeDeps = {};
57
57
 
58
+ // TUI-only observer. The tool call id is furnished by the actual dispatcher;
59
+ // other surfaces and calls without an id cannot register a stop target.
60
+ let softStopSpaceObserver: ((toolCallId: string, spaceId: string) => void) | null = null;
61
+ export function setSelfImplementSoftStopSpaceObserver(observer: typeof softStopSpaceObserver): void {
62
+ softStopSpaceObserver = observer;
63
+ }
64
+
58
65
  /** 자식 헤드리스 elanous 의 config/state 격리 + 구현 대기 상한을 주입. */
59
66
  export function setSelfImplementRuntimeDeps(d: SelfImplementRuntimeDeps | null): void {
60
67
  runtimeDeps = d ?? {};
@@ -711,6 +718,11 @@ export const selfImplementRuntime: ToolRuntime<SelfImplementRuntimeRequest, Self
711
718
  ...(runtimeDeps.implementMaxWaitSec ? { implementMaxWaitSec: runtimeDeps.implementMaxWaitSec } : {}),
712
719
  ...(approvePr ? { approvePr } : {}),
713
720
  onProgress: resolveHarnessProgressSink(ctx),
721
+ ...(ctx.surface === 'tui' && ctx.toolCallId && softStopSpaceObserver ? {
722
+ onSoftStopSpaceReady: (spaceId: string) => {
723
+ try { softStopSpaceObserver?.(ctx.toolCallId!, spaceId); } catch { /* UI observer cannot break harness */ }
724
+ },
725
+ } : {}),
714
726
  });
715
727
 
716
728
  const runnableDocumentReferences = documentReferences.filter(({ result }) => result.kind !== 'outside-repository');
@@ -162,6 +162,8 @@ export interface SessionPostureSnapshot {
162
162
 
163
163
  export interface SessionRuntimeDispatchDeps {
164
164
  signal?: AbortSignal;
165
+ /** Current LLM tool call, for correlating a foreground harness stop target with the TUI call. */
166
+ toolCallId?: string;
165
167
  userText?: string;
166
168
  modelFamily?: ModelFamily;
167
169
  /** Parent Agent capability context for an inline runtime fallback. */
@@ -183,7 +185,7 @@ export interface SessionRuntimeDispatchDeps {
183
185
  // scheduler-retirement R1 의 LLM tool unwire 후 모두 no-op stub 였고
184
186
  // 본 PR 에서 정식 폐기.
185
187
  getToolRuntime(name: string): ToolRuntime<Record<string, unknown>, ToolRunResult> | undefined;
186
- dispatchToolRuntime(name: string, args: Record<string, unknown>): Promise<unknown>;
188
+ dispatchToolRuntime(name: string, args: Record<string, unknown>, toolCallId?: string): Promise<unknown>;
187
189
  dispatchPluginTool(name: string, args: Record<string, unknown>): Promise<
188
190
  { ok: true; result: unknown } | { ok: false; error: string }
189
191
  >;
@@ -2166,7 +2168,7 @@ export async function dispatchSessionRuntimeTool(
2166
2168
  // registered runtimes) and drops `pathPolicy` on the floor.
2167
2169
  const nativeRuntime = nativeRule?.preferRuntime ? deps.getToolRuntime(name) : undefined;
2168
2170
  if (nativeRuntime) {
2169
- const result = await deps.dispatchToolRuntime(name, normalizedArgs);
2171
+ const result = await deps.dispatchToolRuntime(name, normalizedArgs, deps.toolCallId);
2170
2172
  rememberCandidateListingResult(name, normalizedArgs, result, scopedAnalysis, deps.searchPlannerState);
2171
2173
  return result;
2172
2174
  }
@@ -2201,7 +2203,7 @@ export async function dispatchSessionRuntimeTool(
2201
2203
  if (rt) {
2202
2204
  const isPtyTool = name.startsWith('PtyShell') || name.startsWith('pty_shell');
2203
2205
  if (!isPtyTool || deps.ptyDashboardOn) {
2204
- const result = await deps.dispatchToolRuntime(name, normalizedArgs);
2206
+ const result = await deps.dispatchToolRuntime(name, normalizedArgs, deps.toolCallId);
2205
2207
  rememberCandidateListingResult(name, normalizedArgs, result, scopedAnalysis, deps.searchPlannerState);
2206
2208
  return result;
2207
2209
  }
@@ -55,11 +55,22 @@ import { basename } from 'node:path';
55
55
  import { debug } from './debug/log.js';
56
56
 
57
57
  const TIMEOUT_MS = 3000;
58
+ // An interactive shell (`-i`) ignores SIGTERM, so the default timeout signal never ends it: the
59
+ // capture then blocks until the rc files finish. 10-08 TUI-BOOT-60: a stale pyenv rehash lock made
60
+ // every `zsh -l -i` wait 60 s, and the «3 s» capture held the TUI boot for ~65 s. SIGKILL makes the
61
+ // timeout real.
62
+ const TIMEOUT_SIGNAL = 'SIGKILL' as const;
58
63
  const POSIX_SHELLS = new Set(['zsh', 'bash', 'fish', 'sh', 'dash', 'ksh']);
59
64
 
60
65
  let cached: Record<string, string> | null = null;
61
66
  let captured = false;
62
67
 
68
+ // Both the daemon warm path (nexus/index.ts) and PTY's first synchronous capture
69
+ // pass through here. Keep the existing capture events intact; these are additive timings.
70
+ function logBootPhase(phase: string, start: number, outcome: string): void {
71
+ debug.log('boot.phase', phase, { ms: performance.now() - start, outcome });
72
+ }
73
+
63
74
  /** Parse `KEY=VALUE\n` output from `printenv`. Tolerates values that
64
75
  * themselves contain `=`; does not attempt to unescape newlines
65
76
  * (bash/zsh `printenv` emits them literally, which is fine for
@@ -116,26 +127,33 @@ function acceptCapture(stdout: string): Record<string, string> | null {
116
127
  }
117
128
 
118
129
  export function runPrintenvCapture(): Record<string, string> | null {
130
+ const seedStart = performance.now();
119
131
  const seed = captureSeed();
132
+ logBootPhase('login-env.seed', seedStart, seed ? 'ready' : 'unavailable');
120
133
  if (!seed) return null;
121
134
  const { shell, seedEnv } = seed;
122
135
 
136
+ const shellStart = performance.now();
123
137
  let res: ReturnType<typeof spawnSync>;
124
138
  try {
125
139
  res = spawnSync(shell, ['-l', '-i', '-c', 'printenv'], {
126
140
  env: seedEnv,
127
141
  encoding: 'utf8',
128
142
  timeout: TIMEOUT_MS,
143
+ killSignal: TIMEOUT_SIGNAL,
129
144
  stdio: ['ignore', 'pipe', 'pipe'],
130
145
  // Don't inherit our stdio — rc files that run `clear` or issue
131
146
  // OSC sequences would corrupt our TUI output otherwise.
132
147
  });
133
148
  } catch {
149
+ logBootPhase('login-env.shell', shellStart, 'error');
134
150
  return null;
135
151
  }
136
- if (res.status !== 0 && res.status !== null) return null;
137
- if (res.signal) return null;
138
- return acceptCapture(typeof res.stdout === 'string' ? res.stdout : '');
152
+ // ETIMEDOUT with status 0 = the shell outlived the timeout; never trust a late capture.
153
+ const parsed = !res.error && (res.status === 0 || res.status === null) && !res.signal
154
+ ? acceptCapture(typeof res.stdout === 'string' ? res.stdout : '') : null;
155
+ logBootPhase('login-env.shell', shellStart, parsed ? 'ok' : 'failed');
156
+ return parsed;
139
157
  }
140
158
 
141
159
  /** Fill the login-env cache off the event loop (daemon boot). The first web-terminal spawn otherwise ran the
@@ -144,15 +162,25 @@ export function runPrintenvCapture(): Record<string, string> | null {
144
162
  export async function warmCapturedEnv(spawn: typeof Bun.spawn = Bun.spawn): Promise<boolean> {
145
163
  if (captured) return cached !== null;
146
164
  if (process.env.ELANOUS_SKIP_LOGIN_ENV === '1' || process.env.TERM_PROGRAM === 'monad-agent-nested') return false;
165
+ const seedStart = performance.now();
147
166
  const seed = captureSeed();
167
+ logBootPhase('login-env.seed', seedStart, seed ? 'ready' : 'unavailable');
148
168
  if (!seed) return false;
149
169
  const start = Date.now();
170
+ const shellStart = performance.now();
150
171
  try {
151
172
  const proc = spawn([seed.shell, '-l', '-i', '-c', 'printenv'], { env: seed.seedEnv as Record<string, string>, stdin: 'ignore', stdout: 'pipe', stderr: 'ignore' });
152
- const timer = setTimeout(() => proc.kill(), TIMEOUT_MS);
153
- const [out, code] = await Promise.all([new Response(proc.stdout as ReadableStream).text(), proc.exited]);
173
+ // A grandchild (e.g. a pyenv rehash) can keep the stdout pipe open after the shell is killed,
174
+ // so the timeout settles the race itself instead of waiting for EOF.
175
+ let timer: ReturnType<typeof setTimeout> | undefined;
176
+ const timedOut = new Promise<null>((resolve) => {
177
+ timer = setTimeout(() => { proc.kill(TIMEOUT_SIGNAL); resolve(null); }, TIMEOUT_MS);
178
+ });
179
+ const finished = Promise.all([new Response(proc.stdout as ReadableStream).text(), proc.exited]);
180
+ const result = await Promise.race([finished, timedOut]);
154
181
  clearTimeout(timer);
155
- const parsed = code === 0 ? acceptCapture(out) : null;
182
+ const parsed = result && result[1] === 0 ? acceptCapture(result[0]) : null;
183
+ logBootPhase('login-env.shell', shellStart, parsed ? 'ok' : result ? 'failed' : 'timeout');
156
184
  if (captured) return cached !== null; // a synchronous capture won the race
157
185
  if (!parsed) return false;
158
186
  cached = parsed;
@@ -160,6 +188,7 @@ export async function warmCapturedEnv(spawn: typeof Bun.spawn = Bun.spawn): Prom
160
188
  debug.log('shell.envbootstrap.capture', 'warmed', { ok: true, ms: Date.now() - start, keyCount: Object.keys(parsed).length });
161
189
  return true;
162
190
  } catch {
191
+ logBootPhase('login-env.shell', shellStart, 'error');
163
192
  return false;
164
193
  }
165
194
  }