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
@@ -1,8 +1,26 @@
1
+ import { spawnSync } from 'node:child_process';
1
2
  import { existsSync, readFileSync, readdirSync } from 'node:fs';
2
3
  import { homedir } from 'node:os';
3
4
  import { join } from 'node:path';
4
5
 
5
- type Agent = 'claude' | 'codex' | 'grok';
6
+ type Agent = 'claude' | 'codex' | 'grok' | 'gemini';
7
+
8
+ function versionOf(binary: string): string | null {
9
+ const result = spawnSync(binary, ['--version'], { encoding: 'utf8', timeout: 3000 });
10
+ const line = `${result.stdout ?? ''}${result.stderr ?? ''}`.trim().split(/\r?\n/, 1)[0];
11
+ return result.status === 0 && line && /^[\w .+()-]{1,100}$/.test(line) ? line : null;
12
+ }
13
+
14
+ // Plugin and marketplace keys are `name@marketplace`; check each part instead of redacting every scoped name.
15
+ function safePluginName(name: string): string {
16
+ const parts = name.split('@');
17
+ return parts.length <= 2 && parts.every(part => part !== '' && safeIdentifier(part) === part) ? name : '<redacted>';
18
+ }
19
+
20
+ function safeNames(path: string): string[] | '«없음»' {
21
+ return !existsSync(path) ? '«없음»' : readdirSync(path, { withFileTypes: true })
22
+ .filter(entry => entry.isDirectory()).map(entry => safeIdentifier(entry.name) ?? '<redacted>').sort();
23
+ }
6
24
  type Presence = 'present' | '«없음»';
7
25
 
8
26
  function present(path: string): Presence {
@@ -54,7 +72,7 @@ function collectMcp(
54
72
  servers: unknown,
55
73
  source: McpSource,
56
74
  ): void {
57
- for (const [name, server] of Object.entries(record(servers))) into[name] = mcpEntry(server, source);
75
+ for (const [name, server] of Object.entries(record(servers))) into[safeIdentifier(name) ?? '<redacted>'] = mcpEntry(server, source);
58
76
  }
59
77
 
60
78
  function hookGroups(hooks: Record<string, unknown>): Record<string, unknown[]> {
@@ -86,6 +104,7 @@ function claude(home: string): Record<string, unknown> {
86
104
  const skillsDir = join(dir, 'skills');
87
105
  const team = join(home, '.teamclaude');
88
106
  const teamManifest = json(join(team, 'package.json'));
107
+ const marketplaces = record(data.extraKnownMarketplaces);
89
108
  const teamVersion = typeof teamManifest?.version === 'string' && /^\d+\.\d+\.\d+(?:[-+.][a-zA-Z0-9.-]+)?$/.test(teamManifest.version)
90
109
  ? teamManifest.version : undefined;
91
110
  const mcp: Record<string, unknown> = {};
@@ -98,6 +117,7 @@ function claude(home: string): Record<string, unknown> {
98
117
  }
99
118
  const mcpFilesMissing = settings === null && user === null;
100
119
  return {
120
+ version: versionOf('claude'),
101
121
  settings: settings === null ? '«없음»' : 'present',
102
122
  userConfig: user === null ? '«없음»' : 'present',
103
123
  permissionMode: safeIdentifier(record(data.permissions).defaultMode),
@@ -106,10 +126,10 @@ function claude(home: string): Record<string, unknown> {
106
126
  command: safeCommand(statusLine.command) ?? null,
107
127
  },
108
128
  hooks: settings === null ? '«없음»' : hookGroups(hooks),
109
- enabledPlugins: settings === null ? '«없음»' : Object.fromEntries(Object.entries(plugins).map(([name, enabled]) => [name, enabled === true])),
129
+ enabledPlugins: settings === null ? '«없음»' : Object.fromEntries(Object.entries(plugins).map(([name, enabled]) => [safePluginName(name), enabled === true])),
130
+ marketplaces: settings === null ? '«없음»' : Object.keys(marketplaces).map(name => safeIdentifier(name) ?? '<redacted>').sort(),
110
131
  mcpServers: mcpFilesMissing ? '«없음»' : mcp,
111
- skills: !existsSync(skillsDir) ? '«없음»' : readdirSync(skillsDir, { withFileTypes: true })
112
- .filter(entry => entry.isDirectory()).map(entry => entry.name).sort(),
132
+ skills: safeNames(skillsDir),
113
133
  teamclaude: { installed: present(team), version: teamVersion ?? null },
114
134
  auth: present(join(dir, 'auth.json')),
115
135
  credentials: present(join(dir, '.credentials.json')),
@@ -30,7 +30,7 @@ import { resolvePtyWebAddress } from '../cli/pty-web-address.js';
30
30
  import { verdictForFinalPtyScreen } from '../cli/pty-drive-verdict.js';
31
31
  import { reemitPtyUsage } from '../budget/pty-usage-reemit.js';
32
32
  import { classifyAuthError } from '../oauth/codex.js';
33
- import type { FallbackStep } from '../oauth/fallback-chain.js';
33
+ import { normalizeFallbackChain, type FallbackStep } from '../oauth/fallback-chain.js';
34
34
  import { runPtyControlLoop, controlDepsForHandle, type ControlDecision, type ControlObservation, type RunSupervisor } from '../autopilot/pty-control-loop.js';
35
35
  import { probeControlStance, stanceBlocksWrite } from '../pty-shell/pty-control-stance.js';
36
36
  import { mapControlStance, supervisionObservationFields } from '../self-implement/supervision-vocabulary.js';
@@ -441,7 +441,7 @@ export function agentBackendNames(): string[] {
441
441
  *
442
442
  * ⛔ 불변식 셋(윗 모듈에서 그대로 이어받는다):
443
443
  * ① **사람이 이름을 «명시»했으면 이 함수는 «안» 불린다** — 의도가 이긴다(호출자가 위에서 갈린다).
444
- * ② **기본 체인은 `['codex-rotate']`** 이라 옵션을 안 켠 사용자는 «항상 codex» — 무변경.
444
+ * ② **기본 체인은 `['codex-rotate', 'grok']`** 이며 claude-pty 는 명시 구성에서만 선택된다.
445
445
  * ③ **조용히 바꾸지 않는다** — 바꿨으면 관측에 남긴다. 조용한 폴백은 이 파일이 이미
446
446
  * 거부한 형태다(위 `resolveBackend` 주석: *"조용한 codex 폴백은 애그노스틱 위반"*).
447
447
  *
@@ -460,9 +460,9 @@ export function resolveDefaultBackend(
460
460
  return resolveRunFallback();
461
461
  });
462
462
  const decision = decide();
463
- if (decision.action === 'switch-backend' && decision.backend === 'grok') {
464
- debug.log('agent-mission.backend', 'fallback-switch', { from: 'codex', to: 'grok', reason: 'codex-exhausted' });
465
- return grokBackend;
463
+ if (decision.action === 'switch-backend' && (decision.backend === 'grok' || decision.backend === 'claude')) {
464
+ debug.log('agent-mission.backend', 'fallback-switch', { from: 'codex', to: decision.backend, reason: 'codex-exhausted' });
465
+ return decision.backend === 'claude' ? claudeBackend : grokBackend;
466
466
  }
467
467
  } catch (err) {
468
468
  debug.log('agent-mission.backend', 'fallback-failed', { message: (err as Error)?.message }, { level: 'warn' });
@@ -503,6 +503,20 @@ interface RuntimeFallbackContext extends RuntimeFallbackState {
503
503
  readonly fallbackEligible: boolean;
504
504
  }
505
505
 
506
+ /** 초기 백엔드의 체인 위치부터 남은 칸만 재시도한다. 실제 전환은 각 칸의 판정이 허용해야 한다. */
507
+ export function initialRuntimeFallback(backend: AgentBackend, chain: readonly FallbackStep[]): RuntimeFallbackContext {
508
+ const step: FallbackStep = backend.name === 'grok' ? 'grok' : backend.name === 'claude' ? 'claude-pty' : 'codex-rotate';
509
+ const position = chain.indexOf(step);
510
+ return {
511
+ fallbackEligible: true,
512
+ attemptedSteps: new Set<FallbackStep>([step]),
513
+ descents: 0,
514
+ // 초기 칸이 체인에 «없으면»(예: chain=['grok'] 인데 기본 codex 로 떴다) 체인 전체가 남은 칸이다 —
515
+ // 종전(codex 면 1칸)보다 줄이지 않는다.
516
+ maxDescents: position < 0 ? chain.filter((s) => s !== step).length : chain.length - position - 1,
517
+ };
518
+ }
519
+
506
520
  /** 한도 실패에만 재판정한다. 새·미시도 체인 칸과 최초 남은 칸 상한을 모두 만족해야 이동한다. */
507
521
  export function decideRuntimeFallback(
508
522
  failure: unknown,
@@ -512,8 +526,10 @@ export function decideRuntimeFallback(
512
526
  ): AgentBackend | null {
513
527
  if (classifyAuthError(failure).errorKind !== 'rate-limited' || state.descents >= state.maxDescents) return null;
514
528
  const decision = decide({ currentStep, currentCredentialRateLimited: true });
515
- if (decision.action !== 'switch-backend' || decision.backend !== 'grok' || state.attemptedSteps.has('grok')) return null;
516
- return grokBackend;
529
+ if (decision.action !== 'switch-backend') return null;
530
+ const nextStep = decision.backend === 'grok' ? 'grok' : decision.backend === 'claude' ? 'claude-pty' : null;
531
+ if (!nextStep || state.attemptedSteps.has(nextStep)) return null;
532
+ return nextStep === 'claude-pty' ? claudeBackend : grokBackend;
517
533
  }
518
534
 
519
535
  export interface AgentMissionSpec {
@@ -1586,10 +1602,8 @@ async function runAgentMissionBody(spec: AgentMissionSpec, deps: AgentMissionDep
1586
1602
  if (spec.plugin && (spec.headless || (backend.name !== 'codex' && backend.name !== 'claude'))) throw new Error('--plugin 은 codex/claude PTY 전용입니다');
1587
1603
  if (spec.workdir && spec.chain) throw new Error('workdir 모드는 넘기기 사슬을 쓰지 않는다');
1588
1604
  const runtimeFallback = deps.runtimeFallback ?? {
1605
+ ...initialRuntimeFallback(backend, normalizeFallbackChain(getUserConfig().llm?.fallbackChain).chain),
1589
1606
  fallbackEligible: spec.agent === undefined && !spec.chain,
1590
- attemptedSteps: new Set<FallbackStep>([backend.name === 'grok' ? 'grok' : 'codex-rotate']),
1591
- descents: 0,
1592
- maxDescents: backend.name === 'codex' ? 1 : 0,
1593
1607
  };
1594
1608
  const spawnPty = deps.startPty ?? startPty;
1595
1609
  const createMissionWorktree = deps.createWorktree ?? createWorktree;
@@ -2279,7 +2293,7 @@ async function runAgentMissionBody(spec: AgentMissionSpec, deps: AgentMissionDep
2279
2293
  const { resolveRunFallback: resolve } = require('../oauth/codex-account-store.js') as typeof import('../oauth/codex-account-store.js');
2280
2294
  return resolve(process.env, input);
2281
2295
  });
2282
- const currentStep: FallbackStep = backend.name === 'grok' ? 'grok' : 'codex-rotate';
2296
+ const currentStep: FallbackStep = backend.name === 'grok' ? 'grok' : backend.name === 'claude' ? 'claude-pty' : 'codex-rotate';
2283
2297
  const nextBackend = runtimeFallback.fallbackEligible ? decideRuntimeFallback(
2284
2298
  failure,
2285
2299
  currentStep,
@@ -2296,7 +2310,7 @@ async function runAgentMissionBody(spec: AgentMissionSpec, deps: AgentMissionDep
2296
2310
  });
2297
2311
  if (nextBackend && !deps.isMissionAborted?.()) {
2298
2312
  decisionEvent({ step: 'recover', text: `Switching ${backend.name} to ${nextBackend.name}`, detail: { blocked: control.termination.message, action: `Retry with ${nextBackend.name}` } });
2299
- const nextStep: FallbackStep = nextBackend.name === 'grok' ? 'grok' : 'codex-rotate';
2313
+ const nextStep: FallbackStep = nextBackend.name === 'grok' ? 'grok' : nextBackend.name === 'claude' ? 'claude-pty' : 'codex-rotate';
2300
2314
  stopLive();
2301
2315
  if (h.pid) await (deps.terminateProcessTree ?? terminateMissionProcessTree)(h.pid);
2302
2316
  try { h.kill(); } catch { /* noop */ }
@@ -69,8 +69,17 @@ function linkedRows(rows: readonly SupervisorLogRow[], missionIds: ReadonlySet<s
69
69
  const linked: SupervisorLogRow[] = [];
70
70
  let unlinked = false;
71
71
  for (const row of rows) {
72
- const key = missionKey(row.data);
73
- if (key && missionIds.has(key)) linked.push(row);
72
+ // The driver writes its runId into the ambient log payload and the PTY decision's
73
+ // sessionId; the decision emitter may replace missionId with a display alias.
74
+ // Without a runId, either retained identifier can identify an existing result.
75
+ const runId = row.data?.runId;
76
+ const sessionId = row.category === DECISION_CATEGORY ? row.data?.sessionId : undefined;
77
+ const key = typeof runId === 'string' && runId.trim() ? runId.trim() : null;
78
+ const sessionKey = typeof sessionId === 'string' && sessionId.trim() ? sessionId.trim() : null;
79
+ const missionId = typeof row.data?.missionId === 'string' && row.data.missionId.trim() ? row.data.missionId.trim() : null;
80
+ const matches = key ? missionIds.has(key)
81
+ : (missionId !== null && missionIds.has(missionId)) || (sessionKey !== null && missionIds.has(sessionKey));
82
+ if (matches) linked.push(row);
74
83
  else unlinked = true;
75
84
  }
76
85
  return { linked, unlinked };
@@ -123,6 +132,12 @@ export function aggregateSupervisorMetrics(
123
132
  ? unmeasured('questionsAnswered', '대화형 질문에 답해 넘긴 수', UNLINKED_REASON)
124
133
  : answers.linked.length === 0
125
134
  ? unmeasured('questionsAnswered', '대화형 질문에 답해 넘긴 수', '못 잼 · pty.decision answer 기록 없음')
135
+ : answers.linked.some((row) => {
136
+ const detail = row.data?.detail;
137
+ return !detail || typeof detail !== 'object' || !('answer' in detail)
138
+ || typeof detail.answer !== 'string' || !detail.answer.trim();
139
+ })
140
+ ? unmeasured('questionsAnswered', '대화형 질문에 답해 넘긴 수', '못 잼 · 답변 본문 기록 없음')
126
141
  : {
127
142
  id: 'questionsAnswered',
128
143
  label: '대화형 질문에 답해 넘긴 수',
@@ -25,6 +25,10 @@ export interface AcpReviewerOpts {
25
25
  cwd: string;
26
26
  /** ACP 백엔드 id (기본 claude = Claude Code via claude-code-acp). */
27
27
  backend?: string;
28
+ /** Per-review ACP spawn specification (subscription executor). */
29
+ backendSpec?: import('../acp/backend-registry.js').AcpBackendSpec;
30
+ /** Subscription child environment after billing credentials are removed. */
31
+ env?: Record<string, string>;
28
32
  /** 모델 tier 별칭 (opus|sonnet|haiku 등). 지정 시 session/set_model 로 고정(백엔드 CLI 기본 위임 아님). */
29
33
  model?: string;
30
34
  /** 리뷰 1턴 최대 대기(ms). 초과 시 cancel→throw(부분 리뷰로 오판 pass 방지). 기본 300000. */
@@ -96,7 +100,7 @@ export function makeAcpReviewLLM(opts: AcpReviewerOpts): (prompt: string, images
96
100
  // ⇒ 이 한 줄만 자기 catch 로 감싼다(전체 try 를 앞당기면 `start` 관측이 그 안으로 들어간다).
97
101
  let transport: string | null;
98
102
  try {
99
- transport = injected ? null : getAcpBackend(backend).transport ?? 'acp';
103
+ transport = injected ? null : opts.backendSpec?.transport ?? getAcpBackend(backend).transport ?? 'acp';
100
104
  } catch (e) {
101
105
  debug.log('acp-review', 'error', {
102
106
  requestedBackend, backend, stage: 'resolve-backend', message: (e as Error).message,
@@ -116,6 +120,7 @@ export function makeAcpReviewLLM(opts: AcpReviewerOpts): (prompt: string, images
116
120
  let capabilitiesObserved = false;
117
121
  const agentOpts: Omit<AcpAgentOpts, 'backendId'> = {
118
122
  cwd: opts.cwd,
123
+ ...(opts.backendSpec ? { backendSpec: opts.backendSpec, env: opts.env } : {}),
119
124
  log: (message) => debug.log('acp-review', 'agent-log', { backend, message }),
120
125
  onCapabilities: (capabilities) => {
121
126
  capabilitiesObserved = true;
@@ -142,7 +147,7 @@ export function makeAcpReviewLLM(opts: AcpReviewerOpts): (prompt: string, images
142
147
  };
143
148
  // A review owns its manager and subprocess: the global manager caches
144
149
  // chat agents, whose callbacks and lifecycle must not be mutated here.
145
- const reviewManager = injected ? undefined : new AcpAgentManager();
150
+ const reviewManager = injected || opts.backendSpec ? undefined : new AcpAgentManager();
146
151
  let agent: AcpAgent | undefined;
147
152
  let text = '';
148
153
  let timedOut = false;
@@ -150,8 +155,10 @@ export function makeAcpReviewLLM(opts: AcpReviewerOpts): (prompt: string, images
150
155
  try {
151
156
  agent = opts.createAgent
152
157
  ? opts.createAgent({ backendId: requestedBackend, ...agentOpts })
153
- : await reviewManager!.getAgent(requestedBackend, agentOpts);
154
- if (injected) await agent.start();
158
+ : opts.backendSpec
159
+ ? new AcpAgent({ backendId: requestedBackend, ...agentOpts })
160
+ : await reviewManager!.getAgent(requestedBackend, agentOpts);
161
+ if (injected || opts.backendSpec) await agent.start();
155
162
  if (!capabilitiesObserved) {
156
163
  try {
157
164
  debug.log('acp-review', 'capabilities', {
@@ -211,7 +218,7 @@ export function makeAcpReviewLLM(opts: AcpReviewerOpts): (prompt: string, images
211
218
  // cleanup seam, while the per-review manager releases its agents and
212
219
  // process hooks. An injected agent is external to the manager.
213
220
  try {
214
- if (injected) await agent?.stop();
221
+ if (injected || opts.backendSpec) await agent?.stop();
215
222
  else await reviewManager!.dispose();
216
223
  } catch (e) {
217
224
  debug.log('acp-review', 'stop-fail', { backend, message: (e as Error).message }, { level: 'error' });
@@ -59,6 +59,8 @@ export interface ReviewResult {
59
59
  * ⚠️ 미주입(`llmReview` 없음)은 «실패가 아니라 미검토»라 이 칸을 안 채운다 — 둘을 구분한다.
60
60
  */
61
61
  failureReason?: string;
62
+ /** Subscription reviewer unavailable: the default reviewer produced this verdict. */
63
+ reviewerFallback?: string;
62
64
  }
63
65
 
64
66
  /** 모든 리뷰 관측 생산자가 공유하는 diff 예산 필드 계약. 예산 부재는 빈 객체로 보존한다.
@@ -300,7 +302,7 @@ export function planReviewChunks(
300
302
  */
301
303
  export function foldReviewResults(
302
304
  results: readonly ReviewResult[],
303
- ): Pick<ReviewResult, 'verdict' | 'mustFix' | 'shouldFix' | 'requirements' | 'reviewed' | 'failureReason'> {
305
+ ): Pick<ReviewResult, 'verdict' | 'mustFix' | 'shouldFix' | 'requirements' | 'reviewed' | 'failureReason' | 'reviewerFallback'> {
304
306
  const dedupe = (values: readonly string[]): string[] => [...new Set(values)];
305
307
  const collect = (pick: (r: ReviewResult) => readonly string[] | undefined): string[] =>
306
308
  dedupe(results.flatMap((r) => [...(pick(r) ?? [])]));
@@ -311,6 +313,7 @@ export function foldReviewResults(
311
313
  const shouldFix = collect((r) => r.shouldFix).slice(0, 6);
312
314
  const requirements = collect((r) => r.requirements);
313
315
  const failed = results.find((r) => r.reviewed !== true);
316
+ const reviewerFallback = results.find((r) => r.reviewerFallback)?.reviewerFallback;
314
317
  return {
315
318
  verdict,
316
319
  mustFix,
@@ -318,6 +321,7 @@ export function foldReviewResults(
318
321
  ...(requirements.length ? { requirements } : {}),
319
322
  reviewed: results.length > 0 && failed === undefined,
320
323
  ...(failed?.failureReason ? { failureReason: failed.failureReason } : {}),
324
+ ...(reviewerFallback ? { reviewerFallback } : {}),
321
325
  };
322
326
  }
323
327
 
@@ -560,6 +564,9 @@ export function buildReviewPrompt(
560
564
 
561
565
  return [
562
566
  'You are a strict staff-engineer reviewing an ALREADY-OPENED pull request from an autonomous coding agent.',
567
+ // ⚠️ 무표식 절단 금지(리뷰 must-fix) — 잘렸으면 **어느 절부터**인지 리뷰어가 알아야 한다.
568
+ phaseIntentHeading, phaseIntent,
569
+ ...(input.acceptance ? ['## Acceptance criteria', input.acceptance.slice(0, 1000)] : []),
563
570
  'This is a POST-PR review of the produced artifact. Judge whether the PR actually',
564
571
  'delivers the phase intent CORRECTLY and soundly: (1) correctness/latent bugs, (2) does it meet the stated',
565
572
  'acceptance criteria, (3) design/regression risk, (4) missing wiring (new code never called), (5) tests present',
@@ -584,9 +591,6 @@ export function buildReviewPrompt(
584
591
  ' and check that the error names that site; use values the real caller produces instead of synthetic input.',
585
592
  'Do NOT rubber-stamp; an empty review is a failed review — always surface at least a watch item.',
586
593
  '',
587
- // ⚠️ 무표식 절단 금지(리뷰 must-fix) — 잘렸으면 **어느 절부터**인지 리뷰어가 알아야 한다.
588
- phaseIntentHeading, phaseIntent,
589
- ...(input.acceptance ? ['## Acceptance criteria', input.acceptance.slice(0, 1000)] : []),
590
594
  ...(input.workingMemory ? ['## Prior decisions / reuse boundaries (working memory)', input.workingMemory.slice(0, 1200)] : []),
591
595
  // ⭐ 증거가 무엇인지 먼저 말한다 — 규칙 (7) 이 "의도를 넘어선 변경" 을 판정하려면
592
596
  // diff 에 담긴 것이 **이 PR 이 저작한 것인지**를 알아야 한다.
@@ -634,10 +638,12 @@ export function parseReviewResult(text: string): ReviewResult {
634
638
  const first = shouldFix.shift() ?? fallbackFinding;
635
639
  mustFix.push(first ?? 'PR 리뷰 FAIL — 재작업 필요(구체 지적 미파싱).');
636
640
  }
641
+ const fallback = text.match(/\n\[reviewer fallback: ([^\r\n]+)\]$/);
637
642
  return {
638
643
  verdict,
639
644
  mustFix: mustFix.slice(0, 6),
640
645
  shouldFix: shouldFix.slice(0, 6),
646
+ ...(fallback ? { reviewerFallback: fallback[1] } : {}),
641
647
  ...(requirements.length ? { requirements } : {}),
642
648
  };
643
649
  }
@@ -830,6 +836,7 @@ export function renderReview(r: ReviewResult): string {
830
836
  const icon = r.verdict === 'pass' ? '✅' : r.verdict === 'warn' ? '⚠️' : '⛔';
831
837
  const head = `${icon} 자율 PR 리뷰: ${r.verdict.toUpperCase()}`;
832
838
  const parts = [head];
839
+ if (r.reviewerFallback) parts.push(`[reviewer fallback: ${r.reviewerFallback}]`);
833
840
  if (r.mustFix.length) parts.push('**Must-fix (blockers)**', ...r.mustFix.map((f) => `- ${f}`));
834
841
  if (r.shouldFix.length) parts.push('**Should-fix**', ...r.shouldFix.map((f) => `- ${f}`));
835
842
  if (r.mustFix.length === 0 && r.shouldFix.length === 0) parts.push('— 리뷰 통과(블로커·비블로커 지적 없음).');
@@ -525,6 +525,73 @@ function goalFileBlock(goalFile: string | undefined): Block | undefined {
525
525
  : undefined;
526
526
  }
527
527
 
528
+ function isLightweightGoal(goal: string): boolean {
529
+ try {
530
+ const tree = unified().use(remarkParse).use(remarkGfm).parse(goal) as MarkdownNode;
531
+ const nodes = tree.children ?? [];
532
+ const headings = new Set<string>();
533
+ const declarations = new Set<string>();
534
+ for (let index = 0; index < nodes.length; index += 1) {
535
+ const node = nodes[index]!;
536
+ if (node.type !== 'heading' || node.depth !== 2) continue;
537
+ const start = node.position?.start.offset;
538
+ const end = node.position?.end.offset;
539
+ if (start === undefined || end === undefined) return false;
540
+ const heading = goal.slice(start, end).trim();
541
+ headings.add(heading);
542
+ if (heading !== '## 답하지 못하는 것' && heading !== '## 불변식') continue;
543
+ const next = nodes[index + 1];
544
+ if (next?.type !== 'list' || next.children?.length !== 1) continue;
545
+ const item = next.children[0];
546
+ const itemStart = item?.position?.start.offset;
547
+ const itemEnd = item?.position?.end.offset;
548
+ if (itemStart === undefined || itemEnd === undefined) continue;
549
+ if (goal.slice(itemStart, itemEnd).trim() === '- No persistent grounding was performed in lite authoring.') {
550
+ declarations.add(heading);
551
+ }
552
+ }
553
+ return headings.has('## WHAT TO BUILD') && headings.has('## ACCEPTANCE CRITERIA')
554
+ && declarations.has('## 답하지 못하는 것') && declarations.has('## 불변식');
555
+ } catch {
556
+ return false;
557
+ }
558
+ }
559
+
560
+ function lightweightGoalContext(goal: string, goalFile: string): Block {
561
+ const nodes = (unified().use(remarkParse).use(remarkGfm).parse(goal) as MarkdownNode).children ?? [];
562
+ const firstLine = (title: string): string | undefined => {
563
+ const headingIndex = nodes.findIndex((node) => {
564
+ if (node.type !== 'heading' || node.depth !== 2) return false;
565
+ const start = node.position?.start.offset;
566
+ const end = node.position?.end.offset;
567
+ return start !== undefined && end !== undefined && goal.slice(start, end).trim() === `## ${title}`;
568
+ });
569
+ if (headingIndex < 0) return undefined;
570
+ for (const node of nodes.slice(headingIndex + 1)) {
571
+ if (node.type === 'heading' && (node.depth ?? 0) <= 2) break;
572
+ if (node.type === 'code') continue;
573
+ const start = node.position?.start.offset;
574
+ const end = node.position?.end.offset;
575
+ if (start === undefined || end === undefined) continue;
576
+ const line = goal.slice(start, end).split('\n').find((item) => item.trim());
577
+ if (line) return line.replace(/^[-*]\s*/, '').replace(/^(?:무엇|왜):\s*/, '').trim();
578
+ }
579
+ return undefined;
580
+ };
581
+ const brief = (text: string): string => text.length > 350 ? `${text.slice(0, 350)}…[골 원문 참조]` : text;
582
+ const what = firstLine('WHAT TO BUILD');
583
+ const why = firstLine('PROBLEM');
584
+ return {
585
+ title: '무엇을 왜',
586
+ text: [
587
+ '무엇을 왜',
588
+ ...(what ? [`무엇: ${brief(what)}`] : []),
589
+ ...(why ? [`왜: ${brief(why)}`] : []),
590
+ `골 원문·긴 참조: ${goalFile}`,
591
+ ].join('\n'),
592
+ };
593
+ }
594
+
528
595
  export function buildReviewIntent(i: Readonly<ReviewIntentInput>): string {
529
596
  // ① 조망 — 명시값이 없으면 **골 텍스트에서 추출**한다(생산자는 골 자체다·LLM 없음).
530
597
  const mined = extractIntentBlocks(i.goal);
@@ -537,7 +604,9 @@ export function buildReviewIntent(i: Readonly<ReviewIntentInput>): string {
537
604
  // 후순위 블록(스코프 경계)이 불필요하게 잘린다.
538
605
  const identity = parseShardIdentity(i.goal);
539
606
  const shardBoundary = identity ? { title: '조각 경계', text: `조각 경계\n${shardBoundaryBlock(identity)}` } : undefined;
540
- const goalBody = stripExtractedSections(identity ? withoutShardIdentity(i.goal) : i.goal).trim();
607
+ const authoredLiteGoal = isLightweightGoal(i.goal);
608
+ const liteGoal = authoredLiteGoal && Boolean(i.goalFile?.trim());
609
+ const goalBody = liteGoal ? '' : stripExtractedSections(identity ? withoutShardIdentity(i.goal) : i.goal).trim();
541
610
  const goal = goalBody ? { title: '목표', text: `목표\n${goalBody}` } : undefined;
542
611
  // ⭐ **자식이 낸 증거**(diff 밖 이행 · base 적색 · 직전 반영분)를 한 묶음으로 잡아 둔다 — 아래 예산
543
612
  // 배분에서 **목표 본문보다 먼저** 채우기 위해서다. 제목 문자열이 아니라 **동일성**으로 가른다.
@@ -553,6 +622,20 @@ export function buildReviewIntent(i: Readonly<ReviewIntentInput>): string {
553
622
  const coverage = evidenceCoverageBlock(i.evidenceCoverage);
554
623
  const goalFile = goalFileBlock(i.goalFile);
555
624
  const runFacts = runFactsBlock(i);
625
+ // Lite authoring keeps its original ask in a fenced block under WHAT TO BUILD.
626
+ // Review the authored criteria and short context; the goal document is the pointer to the long source.
627
+ if (liteGoal) {
628
+ const acceptanceBlock = listBlock('수용기준', acceptance);
629
+ const context = lightweightGoalContext(i.goal, i.goalFile!.trim());
630
+ const boundaryBlock = listBlock('의도적 스코프 경계', boundaries);
631
+ const liteBlocks = [
632
+ acceptanceBlock, context, boundaryBlock, priorFindings, applied,
633
+ runFacts, goalType, shardBoundary, preexisting, importerTestsNotRun,
634
+ gateEvidence, designCheck, designGate, shardSiblings, coverage, claims,
635
+ listBlock('검증 못 한 경계 후보 (결정 아님 — 이것만으로 must-fix 를 면제하지도, 만들지도 말 것)', unverifiedBoundaryCandidates),
636
+ ].filter((block): block is Block => Boolean(block));
637
+ return fitReviewIntentBlocks(liteBlocks, liteBlocks, liteBlocks);
638
+ }
556
639
  // ⛔⭐⭐⭐ **런 사실이 «맨 앞»이다 — 라이브 실측으로 옮겼다**(2026-08-07).
557
640
  // 📏 `#7556`(실물 하니스 PR · 본문 37,354자)을 프로덕션 `intentFromPr` 에 태우니
558
641
  // ***`runId` 도 커밋도 변경 파일도 «하나도» 안 남았다***(4,000자 상한 · 살아남은 것은 `목표`·`수용기준`).
@@ -645,7 +728,7 @@ function sectionPriority(title: string): 0 | 1 | 2 {
645
728
  const PREAMBLE_TITLE = '(머리말)';
646
729
 
647
730
  type MarkdownPosition = { readonly start: { readonly offset?: number }; readonly end: { readonly offset?: number } };
648
- type MarkdownNode = { readonly type: string; readonly position?: MarkdownPosition; readonly children?: readonly MarkdownNode[] };
731
+ type MarkdownNode = { readonly type: string; readonly depth?: number; readonly position?: MarkdownPosition; readonly children?: readonly MarkdownNode[] };
649
732
  type MarkdownUnit = { readonly text: string; readonly start: number; readonly end: number };
650
733
 
651
734
  /** 비교 가능한 CommonMark 단위와 그 원문 범위. 코드 펜스는 원문에 남기되 비교 후보에서는 제외한다. */
@@ -829,6 +912,18 @@ function isTableHeader(line: string, next: string | undefined): boolean {
829
912
  return /^\s*\|(\s*:?-{2,}:?\s*\|)+\s*$/.test(next ?? '');
830
913
  }
831
914
 
915
+ function topLevelCodeLines(goal: string): ReadonlySet<number> {
916
+ const lines = new Set<number>();
917
+ try {
918
+ const tree = unified().use(remarkParse).use(remarkGfm).parse(goal);
919
+ for (const node of tree.children) {
920
+ if (node.type !== 'code' || !node.position) continue;
921
+ for (let index = node.position.start.line - 1; index < node.position.end.line; index += 1) lines.add(index);
922
+ }
923
+ } catch { /* unknown syntax: keep existing extraction behavior */ }
924
+ return lines;
925
+ }
926
+
832
927
  /** 골에서 **추출된 것만** 걷어낸 나머지(목표 서술). 추출 내용이 목표 블록에 중복되지 않게 한다.
833
928
  *
834
929
  * ⚠️ **산문은 남긴다**(리뷰 must-fix) — 초판은 인식된 절을 만나면 다음 헤더까지 **통째로** 버려,
@@ -839,7 +934,9 @@ export function stripExtractedSections(goal: string): string {
839
934
  const mined = extractIntentBlocks(goal);
840
935
  const out: string[] = [];
841
936
  let extractedSection: 'acceptance' | 'boundary' | null = null;
842
- for (const line of goal.split('\n')) {
937
+ const fencedLines = topLevelCodeLines(goal);
938
+ for (const [index, line] of goal.split('\n').entries()) {
939
+ if (fencedLines.has(index)) { out.push(line); continue; }
843
940
  const kind = headKind(line);
844
941
  if (kind === 'acceptance' || kind === 'boundary') { extractedSection = kind; continue; } // 헤더만 제거
845
942
  if (kind === 'other') extractedSection = null;
@@ -921,6 +1018,7 @@ function isUnverifiedBoundaryCandidate(item: string): boolean {
921
1018
  }
922
1019
 
923
1020
  export function extractIntentBlocks(goal: string, includeSections = false): ExtractedIntentBlocks {
1021
+ const fencedLines = topLevelCodeLines(goal);
924
1022
  const acceptanceItems: string[] = [];
925
1023
  const scopeBoundaryItems: string[] = [];
926
1024
  const unverifiedBoundaryCandidateItems: string[] = [];
@@ -945,6 +1043,7 @@ export function extractIntentBlocks(goal: string, includeSections = false): Extr
945
1043
  push();
946
1044
  }
947
1045
  for (const [idx, line] of lines.entries()) {
1046
+ if (fencedLines.has(idx)) continue;
948
1047
  const kind = headKind(line);
949
1048
  if (kind === 'acceptance') { cur = 'acceptance'; continue; }
950
1049
  if (kind === 'boundary') { cur = 'boundary'; continue; }
@@ -16,8 +16,9 @@ import { collectMergeIntent, collectSiblingPrIntents, defaultIntentGit, type Int
16
16
 
17
17
  function deterministicNextMd(git: MergeGitSeam, worktreePath: string, file: string): string | null {
18
18
  try {
19
+ // stage 1 = base, stage 2 = ours (the checked-out run), stage 3 = theirs (the merged-in main) —
20
+ // the resolver's contract is (base, ours appends only, theirs may drop released lines on dev-bump).
19
21
  const resolved = resolveNextMdConflict(git.readIndexStage(worktreePath, 1, file), git.readIndexStage(worktreePath, 2, file), git.readIndexStage(worktreePath, 3, file));
20
- void import('../../debug/log.js').then(({ debug }) => debug.log('self-dev.merge', 'next-md-deterministic', { resolved: resolved !== null })).catch(() => {});
21
22
  return resolved;
22
23
  } catch {
23
24
  return null; // a stage is missing (added/deleted on one side) — the usual resolver decides
@@ -58,9 +59,9 @@ export type LlmMergeErrorStep = 'fetch' | 'merge' | 'conflicted-files' | 'commit
58
59
  /** git stderr 첫 줄을 관측에 실을 때 쓰는 길이 상한. 구현이 정한다. */
59
60
  export const GIT_ERROR_DETAIL_MAX_CHARS = 240;
60
61
 
61
- /** merge 결과. llm-resolved=충돌을 LLM 이 종합 해결·커밋. conflict-unresolved=LLM 도 못 풀어 abort(base 유지). */
62
+ /** merge 결과. llm-resolved=충돌을 LLM 이 종합 해결·커밋. deterministic-resolved=release/next.md 만 결정적 해결기로 커밋(LLM 0). conflict-unresolved=LLM 도 못 풀어 abort(base 유지). */
62
63
  export interface LlmMergeOutcome {
63
- status: 'merged' | 'up-to-date' | 'llm-resolved' | 'conflict-unresolved' | 'error';
64
+ status: 'merged' | 'up-to-date' | 'llm-resolved' | 'deterministic-resolved' | 'conflict-unresolved' | 'error';
64
65
  resolvedFiles?: string[];
65
66
  sizeChange?: LlmMergeSizeChange;
66
67
  testDeclarationLoss?: Array<{ file: string; ours: number; theirs: number; merged: number }>;
@@ -118,7 +119,9 @@ function addSizeChange(size: LlmMergeSizeChange, file: string, before: string, a
118
119
  }
119
120
 
120
121
  export function formatLlmMergeOutcome(outcome: LlmMergeOutcome): string {
121
- const files = outcome.resolvedFiles?.length ? ` (LLM 종합 ${outcome.resolvedFiles.length}파일: ${outcome.resolvedFiles.join(', ')})` : '';
122
+ const files = outcome.resolvedFiles?.length
123
+ ? ` (${outcome.status === 'deterministic-resolved' ? '결정적 해결' : 'LLM 종합'} ${outcome.resolvedFiles.length}파일: ${outcome.resolvedFiles.join(', ')})`
124
+ : '';
122
125
  if (outcome.status === 'conflict-unresolved') {
123
126
  // ⛔ reason·failedFile 은 «선택» 칸이다 — 공급자 오류·시험 선언 유실·크기 붕괴 출구는 계약상 reason 이 없다.
124
127
  // 있는 칸만 싣고, 그 출구들은 자기 실제 결과(어느 판정이 멈췄나)를 싣는다 — 「사유 undefined」 금지.
@@ -133,7 +136,7 @@ export function formatLlmMergeOutcome(outcome: LlmMergeOutcome): string {
133
136
  ];
134
137
  return `${outcome.status}${files}${parts.length ? `; ${parts.join(' · ')}` : ''}`;
135
138
  }
136
- if (outcome.status !== 'llm-resolved') return `${outcome.status}${files}`;
139
+ if (outcome.status !== 'llm-resolved' && outcome.status !== 'deterministic-resolved') return `${outcome.status}${files}`;
137
140
  const size = outcome.sizeChange;
138
141
  if (size === undefined) return `${outcome.status}${files}; 규모 변화: 못 쟀다`;
139
142
  const delta = size.totalDeltaLines >= 0 ? `+${size.totalDeltaLines}` : `${size.totalDeltaLines}`;
@@ -233,6 +236,8 @@ export async function mergeMainWithLlmResolve(
233
236
  const resolvedFiles: string[] = [];
234
237
  const sizeChange: LlmMergeSizeChange = { files: [], totalBeforeLines: 0, totalAfterLines: 0, totalDeltaLines: 0 };
235
238
  const testDeclarationUnmeasured: string[] = [];
239
+ let deterministicResolved = 0;
240
+ let llmResolved = 0;
236
241
  const measuredOutcome = <T extends object>(outcome: T): T & Pick<LlmMergeOutcome, 'testDeclarationUnmeasured'> => ({
237
242
  ...outcome,
238
243
  ...(testDeclarationUnmeasured.length ? { testDeclarationUnmeasured } : {}),
@@ -245,6 +250,7 @@ export async function mergeMainWithLlmResolve(
245
250
  const abs = join(worktreePath, f);
246
251
  let conflicted: string;
247
252
  let merged: string;
253
+ let resolvedDeterministically = false;
248
254
  try {
249
255
  conflicted = git.readFile(abs);
250
256
  } catch {
@@ -252,9 +258,15 @@ export async function mergeMainWithLlmResolve(
252
258
  return unresolved({ status: 'conflict-unresolved', reason: 'conflict-file-unreadable', failedFile: f, resolvedFiles });
253
259
  }
254
260
  try {
255
- // release/next.md: concurrent landings each append a line — keep both without asking the LLM (REL7c).
261
+ // release/next.md: concurrent landings each append a line — keep both before the LLM sees the file.
256
262
  const deterministic = f === NEXT_MD_PATH ? deterministicNextMd(git, worktreePath, f) : null;
257
- merged = deterministic ?? await resolve(f, conflicted);
263
+ if (deterministic !== null) {
264
+ merged = deterministic;
265
+ resolvedDeterministically = true;
266
+ debug.log('self-implement.main-sync', 'next-md-resolved', { deterministic: true });
267
+ } else {
268
+ merged = await resolve(f, conflicted);
269
+ }
258
270
  } catch (error) {
259
271
  git.abort(worktreePath); // LLM 예외 → base 유지
260
272
  return unresolved({ status: 'conflict-unresolved', resolvedFiles, failedFile: f,
@@ -299,11 +311,13 @@ export async function mergeMainWithLlmResolve(
299
311
  git.writeFile(abs, merged);
300
312
  git.add(worktreePath, f);
301
313
  resolvedFiles.push(f);
314
+ if (resolvedDeterministically) deterministicResolved += 1;
315
+ else llmResolved += 1;
302
316
  }
303
317
  const committed = seamOk(git.commit(worktreePath));
304
318
  if (!committed.ok) { git.abort(worktreePath); return measuredOutcome(errorOutcome('commit', committed.errorDetail, resolvedFiles)); }
305
319
  return measuredOutcome({
306
- status: 'llm-resolved',
320
+ status: llmResolved === 0 && deterministicResolved > 0 ? 'deterministic-resolved' : 'llm-resolved',
307
321
  resolvedFiles,
308
322
  sizeChange,
309
323
  });