elanous 0.2.32 → 0.2.34

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 (633) 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/{IVKr3AlRUVLKsw2fh7eXF → L04y73dlwKeTW-GetaTF3}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/1162.c69abda138c7ce43.js +79 -0
  5. package/apps/pwa/out/_next/static/chunks/1449-1fede62af84c3e43.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1598-13fc845935e816df.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1639.62c5e16fa20d242f.js +94 -0
  8. package/apps/pwa/out/_next/static/chunks/{4174.92ed04922709ac9e.js → 1644.35dd25240ae24e4b.js} +2 -2
  9. package/apps/pwa/out/_next/static/chunks/1661-adb8b2babc9aaf8c.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/{2940-619bfd8ef337c611.js → 1895-60ed0e341c658bd4.js} +1 -1
  11. package/apps/pwa/out/_next/static/chunks/{5525.86debf231e181813.js → 2124.ed5bf480cea7f85b.js} +1 -1
  12. package/apps/pwa/out/_next/static/chunks/2147.107c0fc6146fb15a.js +23 -0
  13. package/apps/pwa/out/_next/static/chunks/2202-ef1c9b000c3074fc.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/2247.1e326638ee43e672.js +115 -0
  15. package/apps/pwa/out/_next/static/chunks/2268.56e1e3d58180abae.js +8 -0
  16. package/apps/pwa/out/_next/static/chunks/2469-606819c812414021.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/{6092.a7f99e68b188ca66.js → 2547.dd27303624cb4da8.js} +1 -1
  18. package/apps/pwa/out/_next/static/chunks/{2213.2ccbada92c33ea0f.js → 2554.3b5e200f5f5cfcd6.js} +16 -16
  19. package/apps/pwa/out/_next/static/chunks/2593.e53c1620c0bb3599.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/2690.e96671114222676a.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/2898-79ee1a5cc0cd9d13.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/298.26099a9f65e5c8ff.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/{5173-233188df2f42c626.js → 2987-30cf9c2eb7b8bdd5.js} +1 -1
  24. package/apps/pwa/out/_next/static/chunks/2992.51fc2a7295bd8139.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/{3541-ce3a5424ed5ad7f1.js → 3001-5a52788c813b6113.js} +1 -1
  26. package/apps/pwa/out/_next/static/chunks/3014-291245fba9df7b5b.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/3068.ee78201f0251a18d.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/3083.cd399a17e9365f78.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/3103.84470c8445962f04.js +34 -0
  30. package/apps/pwa/out/_next/static/chunks/{6433.fc573af22bbc9ab7.js → 3110.0a7065f25c841818.js} +17 -17
  31. package/apps/pwa/out/_next/static/chunks/3117-e1c474683fd1f824.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/3121.779fafe3e44612c5.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/3251-2c17bd5a91f8de2e.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/326.3fb32322bb2ddf6d.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/3411-f87314c6c66ebc73.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/{6157.0c9af021de4fb31b.js → 3414.172ad4ca2d9b08e8.js} +2 -2
  37. package/apps/pwa/out/_next/static/chunks/3421-7ff7fbaa156985a7.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/{4745.67d6f99c245af3f7.js → 3498.cf2a15a8a9e5b926.js} +3 -3
  39. package/apps/pwa/out/_next/static/chunks/381.f55dc63ea87735e6.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/3819.163d4777f0a597f6.js +32 -0
  41. package/apps/pwa/out/_next/static/chunks/{5543.a438b8ab5a13ea47.js → 3837.722e053fb9f321ad.js} +1 -1
  42. package/apps/pwa/out/_next/static/chunks/4129-ab0a6189f1778633.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/4361.93d14ba594b3a035.js +104 -0
  44. package/apps/pwa/out/_next/static/chunks/4364.7a7eb19cceb67d2f.js +286 -0
  45. package/apps/pwa/out/_next/static/chunks/4406.d71f506f7c3ee9d3.js +149 -0
  46. package/apps/pwa/out/_next/static/chunks/4461.2ad1514180ae7e29.js +148 -0
  47. package/apps/pwa/out/_next/static/chunks/4479.bd309cbe0e3842ee.js +64 -0
  48. package/apps/pwa/out/_next/static/chunks/4522-05966f777d66d13b.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/4543-9a3e9812e845de65.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/4557.f8f1d84a0d90d590.js +74 -0
  51. package/apps/pwa/out/_next/static/chunks/4589-79b11542179ef58c.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/4633-9f8f9f88ffbb8f5c.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/4831.23af4d10c0697b5e.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/4983.5951b85724f61f2a.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/{b313b242.cb1265eea908db61.js → 4fa33ebd.21c1bd5b294496e6.js} +1 -1
  56. package/apps/pwa/out/_next/static/chunks/{997ce8a5-2ad501927003b41b.js → 5014f970-22bef0b5210f7d60.js} +1 -1
  57. package/apps/pwa/out/_next/static/chunks/{2112-e4efc21a6505d5bc.js → 5028-f0722e6114c313e7.js} +1 -1
  58. package/apps/pwa/out/_next/static/chunks/{2898.fd781bc7cf01a7b7.js → 5249.96a848656b582a93.js} +1 -1
  59. package/apps/pwa/out/_next/static/chunks/5311.16e0ffbfa5468b80.js +62 -0
  60. package/apps/pwa/out/_next/static/chunks/{9407.e49124dedf0f5e72.js → 5446.edf141cff9ef647d.js} +1 -1
  61. package/apps/pwa/out/_next/static/chunks/{9002.e5658224e3dc1b6f.js → 5489.ec2dd1b5272a19dc.js} +1 -1
  62. package/apps/pwa/out/_next/static/chunks/{1335-6731f392c35216eb.js → 5576-82d3703636d5c4b9.js} +3 -3
  63. package/apps/pwa/out/_next/static/chunks/559.df87695df637920c.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/5618-5c88f2f70178c4ad.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/5764.05487bf0b73ddb6b.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/5930.e678826c3f831aab.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/5dfb57f7.f2e3305326202474.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/6053-d893974de92e6198.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/{2985.39ccdecd36848b12.js → 6086.913a69e6fa3e4a8b.js} +4 -4
  70. package/apps/pwa/out/_next/static/chunks/6200-cefd6f33f59d765e.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/6314-edeba650ae686ac9.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/6316-bc9a9b74d7842d79.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/6476-13b9779b73528008.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/666-04db3ac4c8e3c66f.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/670.682e55106ef915a6.js +77 -0
  76. package/apps/pwa/out/_next/static/chunks/{7862.30a8d6482bb238eb.js → 6740.43de73fc427c9fe2.js} +1 -1
  77. package/apps/pwa/out/_next/static/chunks/6816.b7ba9d0a1a4a46a7.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/690-fbbcc777f189af37.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/6989.0d3d4020e5cb3df6.js +90 -0
  80. package/apps/pwa/out/_next/static/chunks/{2871.0bca6137343b3f76.js → 7005.1623d2aea78eb85f.js} +1 -1
  81. package/apps/pwa/out/_next/static/chunks/7193-cf355cca5ba6c5d5.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/7217-d17393fe7af78fa6.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/7232.24edbd938a69c8d0.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/7295cd1e.b4b557908888eeee.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/7323.adb02de21991883c.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/7386.5d8e7e42e818dfb6.js +287 -0
  87. package/apps/pwa/out/_next/static/chunks/{7502.ccd5b8065545edd0.js → 7431.3051e9b8a4372d2d.js} +2 -2
  88. package/apps/pwa/out/_next/static/chunks/{3583.a05e88cc8d594628.js → 7448.478958ffbbba0ac0.js} +1 -1
  89. package/apps/pwa/out/_next/static/chunks/7537.ba2208c5c656b8a3.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/7562.948255cbc3e79d33.js +41 -0
  91. package/apps/pwa/out/_next/static/chunks/7596-962ae6db6cfc9c83.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/7671.cff51beac212a2ba.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/7883.3916734ee16d3e58.js +27 -0
  94. package/apps/pwa/out/_next/static/chunks/{9989.3694300e5f000b14.js → 7952.b192c31b6a0e00cc.js} +1 -1
  95. package/apps/pwa/out/_next/static/chunks/801.5b24e81eded22d1b.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/8176.8c9a8613f5a01ac1.js +36 -0
  97. package/apps/pwa/out/_next/static/chunks/819e78a6.fa3dd7f7da19bf61.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/8220-78978450c3183d8d.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/8221-eb60b5bc692a3361.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/826.e9a26064f474949e.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/{5307.60f2209e809bd627.js → 8368.014caa8de2f5af34.js} +1 -1
  102. package/apps/pwa/out/_next/static/chunks/8459-a351487a4480f584.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/8482-75823d513a4ce151.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8488.8da515248cda3f3a.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/{9221.7d22eaf353a2d4e7.js → 8548.036c344973968847.js} +1 -1
  106. package/apps/pwa/out/_next/static/chunks/8584.388a1779d9b203af.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/8637.a55e098e61ffefb8.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/8817.61bb7ffbba502ecb.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/{9398-6aed28f0a96d0d6f.js → 9131-fce7256c4180c4a5.js} +1 -1
  110. package/apps/pwa/out/_next/static/chunks/9207-8e6a12db30373f32.js +1 -0
  111. package/apps/pwa/out/_next/static/chunks/9263-00ae932c62696a86.js +1 -0
  112. package/apps/pwa/out/_next/static/chunks/9318.caafc3a130e8769c.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/932.da3fb55951882562.js +93 -0
  114. package/apps/pwa/out/_next/static/chunks/9433.002f8426ac75b6b7.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/9464.dd631a5a14b1d660.js +24 -0
  116. package/apps/pwa/out/_next/static/chunks/{3278.2e5d15b53a4091c8.js → 9550.d5f76828f972058c.js} +2 -2
  117. package/apps/pwa/out/_next/static/chunks/{2534.e85ae21ad0dac829.js → 9739.8d5f36ac67a3b236.js} +1 -1
  118. package/apps/pwa/out/_next/static/chunks/9743.beeabb9d5f86abe4.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/9843-a23d3f32dc43f715.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/994-28211077c7c587d5.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/{7a1887c4.f36b88fba31da8fd.js → 9a8cb466.cc9fc9e6e46038aa.js} +1 -1
  122. package/apps/pwa/out/_next/static/chunks/{0bb0ff36.b8d418980951e5ba.js → 9b48c871.204d5d13e7809bd0.js} +7 -7
  123. package/apps/pwa/out/_next/static/chunks/{409f1e57.d0649fc47691ebfe.js → abb4b98b.163e0f56e84ae35d.js} +1 -1
  124. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-3b4c947f174e33d8.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/approvals/page-369d5ce99483f859.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-63c0ed0eb26e5fce.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/board/page-62802c6003bc85cc.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/botlab/page-ecb41560d4e77be9.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/bots/page-7b65ad8130c93e95.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/ceo/page-8755477c15a9060c.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/chat/page-5725447dd972c471.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/consult/page-b979de0591a69375.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/control/page-6d0796bf4633751a.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/decisions/page-59c31dc0394e67b4.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/design-check/page-12dfc95f6cc5c718.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/editor/page-25788244e66aa4dd.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/exec/page-a2555dc705466322.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/field/page-4fb31eb476dcbad3.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/healthz/route-3b4c947f174e33d8.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/inside/page-578729adbeaa40cb.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/intake/page-a1d20fffbec35205.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/layout-4a558f94516b8004.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/live/page-9e3834705b86145e.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/live-run/page-d7bff0f0905a662c.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/loops/page-cb1c5fe746ea4014.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/market/page-9ee320507356582a.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66fd183b74835886.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/morning/page-3272a064a01a9927.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/not-found-f281b7a7f6ef2529.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/observatory/page-d45a65f8b7124fcd.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-e9c1fe684dd19a66.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-b8322932fc6e34de.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-82c66d9af2290575.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/outputs/page-63c6752009c97b05.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/page-bb33d42ebc358aec.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/reflection/page-7e4c9379197d7214.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-38bf1e9a7001732b.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/sessions/page-2e5b752012389d56.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-8c66a3b24a745630.js → page-398c0faa173203f7.js} +1 -1
  160. package/apps/pwa/out/_next/static/chunks/app/settings/page-002f53a965b6445c.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-7f75997f61a26269.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/setup/layout-3b4c947f174e33d8.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/setup/page-05631753294f23fb.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/share/page-edd700fd2cc3d7f8.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/showroom/page-105f83ecb4594365.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/tasks/page-5823e87d2017f19c.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/term/{page-04d00ae9b0890349.js → page-df3445b207ceceab.js} +1 -1
  168. package/apps/pwa/out/_next/static/chunks/app/today/page-b51ca09566c03de7.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/trace/page-8aacd5ded5dbac60.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/vault/page-bae3365a0b3c6a8e.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-9ed52b48ff1aa7fd.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/workflows/page-b6f8cad35fe00946.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/app/workspace/page-6d25ceda500d2457.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-b6401a5f2c962fe8.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/ebf6cf5e.0d2f887b040e80fb.js +122 -0
  176. package/apps/pwa/out/_next/static/chunks/f09ec7d7-d29abe23edcbb193.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/{ebc7d829.264a0bcb6c750b50.js → ffdc3a04.b9c23c1b28dd4a96.js} +1 -1
  178. package/apps/pwa/out/_next/static/chunks/framework-9322cb8a56ac081c.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-2fbab294e49a1561.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-app-f36729f56621a8f3.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_app-2a62e450b47c6114.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_error-5e17f7b445f34d43.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/webpack-b4263e2190ea7adf.js +1 -0
  184. package/apps/pwa/out/approvals/index.html +1 -1
  185. package/apps/pwa/out/approvals/index.txt +22 -22
  186. package/apps/pwa/out/autopilot/index.html +1 -1
  187. package/apps/pwa/out/autopilot/index.txt +22 -22
  188. package/apps/pwa/out/board/index.html +1 -1
  189. package/apps/pwa/out/board/index.txt +22 -22
  190. package/apps/pwa/out/botlab/index.html +1 -1
  191. package/apps/pwa/out/botlab/index.txt +21 -21
  192. package/apps/pwa/out/bots/index.html +1 -1
  193. package/apps/pwa/out/bots/index.txt +21 -21
  194. package/apps/pwa/out/ceo/index.html +1 -1
  195. package/apps/pwa/out/ceo/index.txt +22 -22
  196. package/apps/pwa/out/chat/index.html +1 -1
  197. package/apps/pwa/out/chat/index.txt +21 -21
  198. package/apps/pwa/out/consult/index.html +1 -1
  199. package/apps/pwa/out/consult/index.txt +22 -22
  200. package/apps/pwa/out/control/index.html +1 -1
  201. package/apps/pwa/out/control/index.txt +22 -22
  202. package/apps/pwa/out/decisions/index.html +1 -1
  203. package/apps/pwa/out/decisions/index.txt +21 -21
  204. package/apps/pwa/out/design-check/index.html +1 -1
  205. package/apps/pwa/out/design-check/index.txt +22 -22
  206. package/apps/pwa/out/editor/index.html +1 -1
  207. package/apps/pwa/out/editor/index.txt +22 -22
  208. package/apps/pwa/out/exec/index.html +1 -1
  209. package/apps/pwa/out/exec/index.txt +22 -22
  210. package/apps/pwa/out/field/index.html +1 -1
  211. package/apps/pwa/out/field/index.txt +22 -22
  212. package/apps/pwa/out/healthz +1 -1
  213. package/apps/pwa/out/index.html +1 -1
  214. package/apps/pwa/out/index.txt +21 -21
  215. package/apps/pwa/out/inside/index.html +1 -1
  216. package/apps/pwa/out/inside/index.txt +21 -21
  217. package/apps/pwa/out/intake/index.html +1 -1
  218. package/apps/pwa/out/intake/index.txt +22 -22
  219. package/apps/pwa/out/live/index.html +1 -1
  220. package/apps/pwa/out/live/index.txt +22 -22
  221. package/apps/pwa/out/live-run/index.html +1 -1
  222. package/apps/pwa/out/live-run/index.txt +22 -22
  223. package/apps/pwa/out/loops/index.html +1 -1
  224. package/apps/pwa/out/loops/index.txt +21 -21
  225. package/apps/pwa/out/market/index.html +1 -1
  226. package/apps/pwa/out/market/index.txt +21 -21
  227. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  228. package/apps/pwa/out/missions/placeholder/index.txt +21 -21
  229. package/apps/pwa/out/morning/index.html +1 -1
  230. package/apps/pwa/out/morning/index.txt +22 -22
  231. package/apps/pwa/out/observatory/index.html +1 -1
  232. package/apps/pwa/out/observatory/index.txt +22 -22
  233. package/apps/pwa/out/ops/checklist/index.html +1 -1
  234. package/apps/pwa/out/ops/checklist/index.txt +21 -21
  235. package/apps/pwa/out/ops/release/index.html +1 -1
  236. package/apps/pwa/out/ops/release/index.txt +21 -21
  237. package/apps/pwa/out/ops/seats/index.html +1 -1
  238. package/apps/pwa/out/ops/seats/index.txt +21 -21
  239. package/apps/pwa/out/outputs/index.html +1 -1
  240. package/apps/pwa/out/outputs/index.txt +21 -21
  241. package/apps/pwa/out/reflection/index.html +1 -1
  242. package/apps/pwa/out/reflection/index.txt +22 -22
  243. package/apps/pwa/out/scheduler/index.html +1 -1
  244. package/apps/pwa/out/scheduler/index.txt +22 -22
  245. package/apps/pwa/out/sessions/index.html +1 -1
  246. package/apps/pwa/out/sessions/index.txt +21 -21
  247. package/apps/pwa/out/settings/devices/index.html +1 -1
  248. package/apps/pwa/out/settings/devices/index.txt +22 -22
  249. package/apps/pwa/out/settings/index.html +1 -1
  250. package/apps/pwa/out/settings/index.txt +22 -22
  251. package/apps/pwa/out/setup/done/index.html +1 -1
  252. package/apps/pwa/out/setup/done/index.txt +22 -22
  253. package/apps/pwa/out/setup/index.html +1 -1
  254. package/apps/pwa/out/setup/index.txt +22 -22
  255. package/apps/pwa/out/share/index.html +1 -1
  256. package/apps/pwa/out/share/index.txt +22 -22
  257. package/apps/pwa/out/showroom/index.html +1 -1
  258. package/apps/pwa/out/showroom/index.txt +21 -21
  259. package/apps/pwa/out/tasks/index.html +1 -1
  260. package/apps/pwa/out/tasks/index.txt +22 -22
  261. package/apps/pwa/out/term/index.html +1 -1
  262. package/apps/pwa/out/term/index.txt +22 -22
  263. package/apps/pwa/out/today/index.html +1 -1
  264. package/apps/pwa/out/today/index.txt +22 -22
  265. package/apps/pwa/out/trace/index.html +1 -1
  266. package/apps/pwa/out/trace/index.txt +22 -22
  267. package/apps/pwa/out/vault/index.html +1 -1
  268. package/apps/pwa/out/vault/index.txt +22 -22
  269. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  270. package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
  271. package/apps/pwa/out/workflows/index.html +1 -1
  272. package/apps/pwa/out/workflows/index.txt +22 -22
  273. package/apps/pwa/out/workspace/index.html +1 -1
  274. package/apps/pwa/out/workspace/index.txt +22 -22
  275. package/apps/pwa/out/worktrees/index.html +1 -1
  276. package/apps/pwa/out/worktrees/index.txt +22 -22
  277. package/bin/elanous.mjs +9 -2
  278. package/docs/goal-context/README.md +1 -0
  279. package/graphs/default-loop.yaml +20 -3
  280. package/graphs/document-loop.yaml +21 -2
  281. package/graphs/implement-loop.yaml +6 -0
  282. package/graphs/journey/SCENARIOS.md +33 -0
  283. package/graphs/journey/recipes.yaml +1 -0
  284. package/graphs/journey/ta-journey.yaml +84 -0
  285. package/graphs/journey/units/author/author.yaml +20 -0
  286. package/graphs/journey/units/author/recipes.yaml +2 -0
  287. package/graphs/journey/units/green/green.yaml +20 -0
  288. package/graphs/journey/units/green/recipes.yaml +2 -0
  289. package/graphs/journey/units/pr/pr.yaml +20 -0
  290. package/graphs/journey/units/pr/recipes.yaml +2 -0
  291. package/graphs/journey/units/remember/recipes.yaml +2 -0
  292. package/graphs/journey/units/remember/remember.yaml +20 -0
  293. package/graphs/operate-loop.yaml +21 -2
  294. package/graphs/overlays/stop-heal-ladder.yaml +1 -1
  295. package/graphs/release-rehearsal/recipes.yaml +1 -1
  296. package/graphs/release-rehearsal/release-rehearsal.yaml +2 -2
  297. package/graphs/research-loop.yaml +23 -2
  298. package/graphs/units/dispatch-pod/dispatch-pod.yaml +50 -0
  299. package/graphs/units/dispatch-pod/recipes.yaml +14 -0
  300. package/graphs/units/entry/entry.yaml +50 -0
  301. package/graphs/units/entry/recipes.yaml +14 -0
  302. package/graphs/units/harvest/harvest.yaml +25 -0
  303. package/graphs/units/harvest/recipes.yaml +4 -0
  304. package/graphs/units/land/land.yaml +45 -0
  305. package/graphs/units/land/recipes.yaml +3 -0
  306. package/graphs/units/recipes.yaml +7 -0
  307. package/graphs/units/recover.yaml +39 -0
  308. package/graphs/units/reflect/recipes.yaml +3 -0
  309. package/graphs/units/reflect/reflect.yaml +15 -0
  310. package/graphs/units/task-agent-recover-review.yaml +17 -0
  311. package/graphs/units/task-agent-recover-wait.yaml +17 -0
  312. package/graphs/units/verbs/make-doc.yaml +34 -0
  313. package/graphs/units/verbs/recipes.yaml +33 -0
  314. package/graphs/units/verbs/verify.yaml +55 -0
  315. package/graphs/work/recipes.yaml +2 -0
  316. package/graphs/work/research-report.yaml +28 -0
  317. package/package.json +1 -1
  318. package/scripts/ci-typecheck-changed.ts +69 -16
  319. package/scripts/docs/generate-graph-anatomy.ts +19 -2
  320. package/scripts/graph-harvest-unit.ts +22 -0
  321. package/scripts/graph-units/journey-stub.ts +6 -0
  322. package/scripts/graph-units/land-enqueue.ts +78 -0
  323. package/scripts/graph-units/recover-plan.ts +15 -0
  324. package/scripts/graph-units/reflect-node.ts +88 -0
  325. package/scripts/graph-units/ta-journey-observe.ts +109 -0
  326. package/scripts/graph-units/verbs/headless-goal-loop.ts +152 -0
  327. package/scripts/graph-units/verbs/verify-stage.ts +75 -0
  328. package/scripts/graph-units/verbs-envelope.ts +139 -0
  329. package/scripts/hq/standby-snapshot.ts +25 -0
  330. package/scripts/public-export.ts +43 -1
  331. package/scripts/ratchets/train-side-invariants.sh +45 -0
  332. package/scripts/release-loop/auto-approve-node.ts +30 -16
  333. package/scripts/release-loop/gate-closure.ts +5 -1
  334. package/scripts/release-loop/gate-diff.ts +1 -1
  335. package/scripts/release-loop/gate-node.ts +498 -31
  336. package/scripts/release-loop/gate-partial.ts +3 -3
  337. package/scripts/release-loop/gate-radius-shadow.ts +138 -0
  338. package/scripts/release-loop/gate-traffic-mode.ts +128 -0
  339. package/scripts/release-loop/gate-triage-node.ts +5 -2
  340. package/scripts/release-loop/known-issues-0.2.33-approved.json +194 -0
  341. package/scripts/release-loop/known-issues-node.ts +77 -8
  342. package/scripts/release-loop/preflight.ts +93 -7
  343. package/scripts/release-loop/publish-node.ts +60 -9
  344. package/scripts/release-loop/resume-cap.ts +56 -0
  345. package/scripts/release-loop/resume-release.ts +114 -15
  346. package/scripts/release-loop/resume.ts +24 -1
  347. package/scripts/release-loop/tui-sim-node.ts +8 -2
  348. package/scripts/release-loop/unattended-release.ts +8 -2
  349. package/src/acp/core-turn-bridge.ts +20 -5
  350. package/src/acp/reasoning-thought-buffer.ts +45 -0
  351. package/src/agent-substrate/acp-reviewer.ts +52 -12
  352. package/src/agent-substrate/review-finding-key.ts +53 -0
  353. package/src/agent-substrate/self-review-cli.ts +126 -5
  354. package/src/ask-user-question/pending-questions.ts +1 -1
  355. package/src/autopilot/pr-manager.ts +192 -43
  356. package/src/chat/tool-render/dispatch.ts +62 -10
  357. package/src/cli/control-cli.ts +24 -7
  358. package/src/cli/guardian-cli.ts +10 -0
  359. package/src/cli/light-entry-route.ts +35 -0
  360. package/src/cli/llm-detect-cli.ts +23 -1
  361. package/src/cli/pr-cli.ts +76 -12
  362. package/src/cli/pr-land-base-read.ts +2 -0
  363. package/src/cli/pr-land-overlap-evidence.ts +154 -16
  364. package/src/cli/pr-land-recovery.ts +64 -0
  365. package/src/cli/pty-takeover-cli.ts +6 -5
  366. package/src/cli/release-cli.ts +120 -33
  367. package/src/cli/release-run-if-ready.ts +29 -28
  368. package/src/cli/release-single-flight.ts +178 -0
  369. package/src/cli/seat-cli.ts +37 -0
  370. package/src/cli/setup-cli.ts +40 -1
  371. package/src/cli/tasks-cli.ts +149 -5
  372. package/src/control/control-status.ts +8 -3
  373. package/src/control-plane/ledger.ts +5 -3
  374. package/src/control-plane/server.ts +5 -3
  375. package/src/core-turn/short-question.ts +5 -1
  376. package/src/dashboard/index.ts +36 -67
  377. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +3 -0
  378. package/src/dashboard/input/dashboard-plain-turn-stream.ts +4 -0
  379. package/src/dashboard/input/seat-autowake.ts +56 -3
  380. package/src/dashboard/seat-boot-preamble.ts +1 -1
  381. package/src/dashboard/slash-runtime/ask-clarification.ts +48 -0
  382. package/src/dashboard/slash-runtime/dashboard-delegated-slashes.ts +68 -0
  383. package/src/dashboard/slash-runtime/dashboard-handlers.ts +62 -322
  384. package/src/dashboard/slash-runtime/dashboard-log-slash.ts +208 -0
  385. package/src/dashboard/turn-preamble.ts +47 -1
  386. package/src/dashboard/turn-stream-formatter.ts +34 -1
  387. package/src/dashboard/turn-stream-runtime.ts +2 -0
  388. package/src/dashboard/turn-typeahead-prompt-zone.ts +67 -0
  389. package/src/domains/guardian-tick.ts +54 -2
  390. package/src/domains/outbound-alert.ts +3 -2
  391. package/src/extractors.ts +3 -1
  392. package/src/git-fs/retry.ts +4 -0
  393. package/src/git-fs/runner.ts +2 -0
  394. package/src/graph-runner/runner.ts +114 -44
  395. package/src/grok/credential.ts +5 -0
  396. package/src/harness/harness-cli-command.ts +39 -10
  397. package/src/harness/harness-incidents.ts +4 -0
  398. package/src/harness/harness-pod-dispatch.ts +38 -1
  399. package/src/harness/harness-salvage-cli.ts +14 -2
  400. package/src/index.ts +192 -8
  401. package/src/intake-plane/already-built-hint.ts +89 -0
  402. package/src/knowhow/pack.ts +199 -0
  403. package/src/light-cli.ts +95 -0
  404. package/src/llm.ts +55 -9
  405. package/src/loops/budget.ts +95 -0
  406. package/src/loops/msg-wake.ts +8 -2
  407. package/src/loops/orchestrator/traffic.ts +132 -1
  408. package/src/mss/logging/log-store.ts +4 -5
  409. package/src/nexus/api/graphs-api.ts +22 -3
  410. package/src/nexus/api/http-server.ts +1 -1
  411. package/src/nexus/api/ops-api.ts +12 -0
  412. package/src/oauth/claude-pty-maturity.ts +66 -0
  413. package/src/oauth/codex-access-mirror.ts +55 -0
  414. package/src/oauth/fallback-chain.ts +1 -1
  415. package/src/onboarding/first-run.ts +5 -3
  416. package/src/onboarding/non-interactive.ts +64 -13
  417. package/src/onboarding.ts +10 -2
  418. package/src/ops/overview-collect.ts +36 -1
  419. package/src/ops/overview.ts +11 -0
  420. package/src/pr-tool/census.ts +23 -5
  421. package/src/pr-tool/inflight-registry.ts +160 -7
  422. package/src/prompt-library/gpt6-family-addendum.ts +14 -1
  423. package/src/pty-shell/herdr-agent-report.ts +442 -0
  424. package/src/pty-shell/pty-control-ipc.ts +5 -5
  425. package/src/pty-shell/pty-write-outcome.ts +24 -6
  426. package/src/pty-shell/tui-run-state.ts +117 -3
  427. package/src/release-loop/checklist.ts +79 -5
  428. package/src/release-loop/feature-store.ts +19 -2
  429. package/src/release-loop/placement.ts +10 -3
  430. package/src/release-loop/rehearsal.ts +205 -25
  431. package/src/release-loop/rubric.ts +16 -2
  432. package/src/self-dev/draft-sweep.ts +52 -3
  433. package/src/self-dev/graph-journey-nodes.ts +10 -6
  434. package/src/self-dev/graph-journey-route.ts +60 -6
  435. package/src/self-dev/orchestrate.ts +79 -3
  436. package/src/self-dev/run-supervisor.ts +107 -12
  437. package/src/self-dev/run-types.ts +5 -1
  438. package/src/self-implement/gate-baseline.ts +49 -5
  439. package/src/self-implement/gate-remote.ts +9 -5
  440. package/src/self-implement/goal-author-identity.ts +66 -0
  441. package/src/self-implement/goal-author-preservation-obligation.ts +35 -0
  442. package/src/self-implement/goal-author.ts +4 -120
  443. package/src/self-implement/graph-overlay-yaml.ts +17 -2
  444. package/src/self-implement/graph-unit-flatten.ts +103 -0
  445. package/src/self-implement/graph-unit-resolver.ts +107 -0
  446. package/src/self-implement/graph-yaml.ts +151 -1
  447. package/src/self-implement/headless-elanous-driver.ts +1 -0
  448. package/src/self-implement/knowhow-pack.ts +103 -0
  449. package/src/self-implement/orchestrator.ts +8 -0
  450. package/src/self-implement/pod-warm-pool-profile.ts +45 -0
  451. package/src/self-implement/review-mustfix-class.ts +203 -0
  452. package/src/self-implement/run-stop.ts +8 -2
  453. package/src/self-implement/seams.ts +32 -1
  454. package/src/self-implement/stop-autoheal.ts +25 -1
  455. package/src/session/chat.ts +27 -0
  456. package/src/session-runtime/retry-policy.ts +2 -1
  457. package/src/skills/tools/schedule-wakeup.ts +66 -17
  458. package/src/steward/release-day.ts +7 -3
  459. package/src/steward/triage.ts +37 -4
  460. package/src/task-agent/harvest-unit.ts +141 -0
  461. package/src/task-agent/live-moves.ts +98 -12
  462. package/src/task-agent/parent-host.ts +93 -3
  463. package/src/task-agent/postland-card.ts +80 -1
  464. package/src/task-agent/rejudge-on-head.ts +112 -26
  465. package/src/task-agent/repair-branch-cas.ts +4 -1
  466. package/src/task-agent/repair-cycle.ts +16 -3
  467. package/src/task-agent/run-identity.ts +4 -3
  468. package/src/task-agent/shadow.ts +12 -2
  469. package/src/task-agent/task-hand.ts +54 -7
  470. package/src/task-agent/work-order-build.ts +22 -0
  471. package/src/task-orchestrator/surfaces/pod-account-broker.ts +39 -3
  472. package/src/task-orchestrator/surfaces/pod-command-job.ts +2 -1
  473. package/src/task-orchestrator/surfaces/pod-lifecycle-stages.ts +86 -8
  474. package/src/task-orchestrator/surfaces/pod-pool.ts +73 -17
  475. package/src/task-orchestrator/surfaces/pod-source-receive.ts +54 -4
  476. package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +26 -3
  477. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +389 -0
  478. package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +2 -1
  479. package/src/task-orchestrator/surfaces/self-implement-pod.ts +223 -38
  480. package/src/task-orchestrator/surfaces/self-implement.ts +83 -2
  481. package/src/tool-runtime/truncation-store.ts +4 -0
  482. package/src/tui-client/dashboard-session.ts +10 -1
  483. package/src/user-config/coercion.ts +36 -0
  484. package/src/user-config.ts +18 -41
  485. package/src/ux-sim/scenario.ts +72 -13
  486. package/src/version/packed-revision.json +1 -1
  487. package/apps/pwa/out/_next/static/chunks/1034-8b639cee57b8e4a8.js +0 -1
  488. package/apps/pwa/out/_next/static/chunks/1085-6c2d35526f11f840.js +0 -1
  489. package/apps/pwa/out/_next/static/chunks/1220.1ff7d5cdc34b1ae5.js +0 -27
  490. package/apps/pwa/out/_next/static/chunks/1247.5884ec81b39fe2e7.js +0 -287
  491. package/apps/pwa/out/_next/static/chunks/1302.87681521a7a4661e.js +0 -62
  492. package/apps/pwa/out/_next/static/chunks/1324-59dae3002413623b.js +0 -1
  493. package/apps/pwa/out/_next/static/chunks/1343-f40b5870ac1eda5a.js +0 -1
  494. package/apps/pwa/out/_next/static/chunks/146-b6cd56be11dda017.js +0 -1
  495. package/apps/pwa/out/_next/static/chunks/1654.32de4b324981a188.js +0 -23
  496. package/apps/pwa/out/_next/static/chunks/1834-98b64172bc1efe77.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/1849.1ce38efa6c527d9a.js +0 -149
  498. package/apps/pwa/out/_next/static/chunks/1913.7cc6fb6055b1ed3f.js +0 -41
  499. package/apps/pwa/out/_next/static/chunks/2001.6c7e0fd02190886c.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/2030.41fae98c7e907304.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/2099-08b2f2dfba742c9b.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/2134.ecee03a550ee7250.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/2236.66061832181cb745.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/2422.b0bd272e7020d7e6.js +0 -1
  505. package/apps/pwa/out/_next/static/chunks/3040.92ff9ffc9f971cb9.js +0 -115
  506. package/apps/pwa/out/_next/static/chunks/3144.6861096c7b4edef7.js +0 -64
  507. package/apps/pwa/out/_next/static/chunks/3267c0e6.d34854e7b3984a6e.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/3771.e928edf2867d28c0.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/3902.e1e71aaac850a045.js +0 -34
  510. package/apps/pwa/out/_next/static/chunks/4063-03284b5de59965f3.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/4111.2bfedbb233ad206b.js +0 -1
  512. package/apps/pwa/out/_next/static/chunks/4300-c7ec4eeabbb5f3f7.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/4483.646d5065199e1b55.js +0 -36
  514. package/apps/pwa/out/_next/static/chunks/4519.28199ff26b2fd80d.js +0 -1
  515. package/apps/pwa/out/_next/static/chunks/4581.ef2443578054544f.js +0 -1
  516. package/apps/pwa/out/_next/static/chunks/4608-8d894e9cbe4f08bf.js +0 -1
  517. package/apps/pwa/out/_next/static/chunks/4639.ae48a01d1bb50a93.js +0 -286
  518. package/apps/pwa/out/_next/static/chunks/4739.08742ac668aada67.js +0 -77
  519. package/apps/pwa/out/_next/static/chunks/4882-16cef71d6a1a32be.js +0 -1
  520. package/apps/pwa/out/_next/static/chunks/4999.c064a4000c2a9e94.js +0 -79
  521. package/apps/pwa/out/_next/static/chunks/5260-69f35419bca263e7.js +0 -1
  522. package/apps/pwa/out/_next/static/chunks/5272.b92d1b72a15c25fe.js +0 -1
  523. package/apps/pwa/out/_next/static/chunks/5304.04d24756f242a094.js +0 -148
  524. package/apps/pwa/out/_next/static/chunks/5415-997980f828667736.js +0 -1
  525. package/apps/pwa/out/_next/static/chunks/5458-16cd7a2b51b74c8c.js +0 -1
  526. package/apps/pwa/out/_next/static/chunks/5572-42c9084f27ca761e.js +0 -1
  527. package/apps/pwa/out/_next/static/chunks/5661-80470aac963d9f36.js +0 -1
  528. package/apps/pwa/out/_next/static/chunks/5723.421338408b5c30c4.js +0 -74
  529. package/apps/pwa/out/_next/static/chunks/5809.64c3664267c50bce.js +0 -1
  530. package/apps/pwa/out/_next/static/chunks/6000.64bc21ac8627f2f5.js +0 -94
  531. package/apps/pwa/out/_next/static/chunks/6013.39459b9c68eabf5f.js +0 -1
  532. package/apps/pwa/out/_next/static/chunks/607-06466dff012b03ef.js +0 -1
  533. package/apps/pwa/out/_next/static/chunks/60fb96c9.360b01d8d4927b7b.js +0 -1
  534. package/apps/pwa/out/_next/static/chunks/6192.a190b989b81e9743.js +0 -1
  535. package/apps/pwa/out/_next/static/chunks/6323.8672b4817a306928.js +0 -1
  536. package/apps/pwa/out/_next/static/chunks/6444.7d1fdcb9b09ae71c.js +0 -1
  537. package/apps/pwa/out/_next/static/chunks/6517.4147878ac06d584d.js +0 -90
  538. package/apps/pwa/out/_next/static/chunks/6532.e34302010b2f0b68.js +0 -32
  539. package/apps/pwa/out/_next/static/chunks/6544cb48.a87fe20ddf137be2.js +0 -1
  540. package/apps/pwa/out/_next/static/chunks/6546.74bc4ed82f19016d.js +0 -104
  541. package/apps/pwa/out/_next/static/chunks/6684-e7523ce7c2fad075.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/6726.ebdc82feb161431e.js +0 -1
  543. package/apps/pwa/out/_next/static/chunks/6739.00fb6f892e5e392b.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/6745.934c4921bf15513a.js +0 -1
  545. package/apps/pwa/out/_next/static/chunks/6767.ea607cf0c20a25af.js +0 -24
  546. package/apps/pwa/out/_next/static/chunks/6843.67bfd020da0926c5.js +0 -93
  547. package/apps/pwa/out/_next/static/chunks/69-0a2c8209c6d4a158.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/6948.1062d4594652b5bf.js +0 -1
  549. package/apps/pwa/out/_next/static/chunks/7138-eaf841fc1dc03463.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/7174-cda29ee13d7790e7.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/7304-3c07583f4e405f4f.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/7371.80c05c653b08e907.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/7391.ebae168025c91b6a.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/7557.e325503a090a4310.js +0 -8
  555. package/apps/pwa/out/_next/static/chunks/7569-9ce9432c7f10989f.js +0 -1
  556. package/apps/pwa/out/_next/static/chunks/7886-55854585fadeded1.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/7947-d0759430f8042240.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/8018.a32663f04a6fc5f9.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/8097-9530c03bcc550f02.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/8215-1661812b6e9ad71f.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/8260-19c921a51ca8ec3e.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/8440.3a5cf5c85aa76db8.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/8519-4599be2249161e68.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/8547.b028b90c5c01ace6.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/8587-cbe02a953a7f4990.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/877-2b977259c4b15655.js +0 -1
  567. package/apps/pwa/out/_next/static/chunks/9019-0a4b40c50c7314fb.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/9138.b6e80031afb1d973.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/9188-d36e4fc869ddc166.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/9230.44f5039e796e4ab4.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/9279-d28c5b7754d010ae.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/939.336acce73e98482c.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/9610.c07cce4bfcc14ed7.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/9667-ce78c2ce91050ff9.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-339fabb3499da0e1.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/app/approvals/page-504204b58c82c253.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-a9717a67c2eecbaf.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/app/board/page-5c9ec30aee62dfbf.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/app/botlab/page-584191533721fa16.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/app/bots/page-73c4b4bb3ac7ab5f.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/app/ceo/page-c0f1ca371f710b06.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/app/chat/page-46430d45a9e04eac.js +0 -1
  583. package/apps/pwa/out/_next/static/chunks/app/consult/page-3b2ebf1dc64ff0be.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/app/control/page-a27b1e5a0d1b308f.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/app/decisions/page-25654b82094f7d22.js +0 -1
  586. package/apps/pwa/out/_next/static/chunks/app/design-check/page-8ea49a4c6dd2c6e7.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/app/editor/page-ce5113aefea0a8d1.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/app/exec/page-0e5a41fac1c75396.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/app/field/page-31aaf1ed37c6df9a.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/app/healthz/route-339fabb3499da0e1.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/app/inside/page-3dcc1fa411e1f4e0.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/app/intake/page-c69247e7134d329d.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/app/layout-66dc5cd3a0999abe.js +0 -1
  594. package/apps/pwa/out/_next/static/chunks/app/live/page-26ff7ff651a7217d.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/app/live-run/page-617b3976981efa96.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/app/loops/page-d256f5d5d9552823.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/app/market/page-ce8f1907b8a6101c.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-33e182a316263116.js +0 -1
  599. package/apps/pwa/out/_next/static/chunks/app/morning/page-32866485913d6a04.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/app/not-found-7607cca9b84950c6.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/app/observatory/page-eeab86176b03dc3b.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-5b93dbae3432b996.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-77a6cb90a9cf2b10.js +0 -1
  604. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f47e768b5b242b5a.js +0 -1
  605. package/apps/pwa/out/_next/static/chunks/app/outputs/page-eb41aa3367fc2505.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/app/page-4d13ab0b8a267e3f.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/app/reflection/page-84dad32ff0fc0831.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-95306f892aa2d0ee.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/app/sessions/page-940fb6c217f6b470.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/app/settings/page-7b675dfd35c36fc9.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8bd0d3cf7665bcfc.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/app/setup/layout-339fabb3499da0e1.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/app/setup/page-c219f2ed171131be.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/app/share/page-40f8fa21bdae955b.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/app/showroom/page-468f8313e4f1fbd9.js +0 -1
  616. package/apps/pwa/out/_next/static/chunks/app/tasks/page-7bbb8fa353a246c4.js +0 -1
  617. package/apps/pwa/out/_next/static/chunks/app/today/page-4214b855d9233ea8.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/app/trace/page-936752c7aa58762e.js +0 -1
  619. package/apps/pwa/out/_next/static/chunks/app/vault/page-ec052ad359df1797.js +0 -1
  620. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-8305570551091338.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/app/workflows/page-fad650e4793adb65.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/app/workspace/page-d90e64743826e1d8.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-a16bcfd49af86a32.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/e5012ffe.f727e23c231f4342.js +0 -122
  625. package/apps/pwa/out/_next/static/chunks/ea695e3c-1f1d70d740cf3031.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/framework-906af9bd029e166c.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/main-a24847f9f684d469.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/main-app-7bdcd8aa7c8397d3.js +0 -1
  629. package/apps/pwa/out/_next/static/chunks/pages/_app-6ba46a5066fc226d.js +0 -1
  630. package/apps/pwa/out/_next/static/chunks/pages/_error-a8dc6d07591f2a25.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/webpack-f13df28c15c55eee.js +0 -1
  632. package/src/cli/g6a-registration-probe.ts +0 -16
  633. /package/apps/pwa/out/_next/static/{IVKr3AlRUVLKsw2fh7eXF → L04y73dlwKeTW-GetaTF3}/_ssgManifest.js +0 -0
@@ -6,13 +6,14 @@ import { constants, copyFileSync, existsSync, readFileSync, renameSync, writeFil
6
6
  import { join } from 'node:path';
7
7
  import { debug } from '../../src/debug/log.js';
8
8
  import { effectiveInstanceRoot, releaseLedgerRoot } from '../../src/instance/resolve.js';
9
- import { lastJsonObject, runGraph, type GraphRunState } from '../../src/graph-runner/runner.js';
9
+ import { lastJsonObject, runGraph, verifyReleaseCommandRoot, type GraphRunState } from '../../src/graph-runner/runner.js';
10
10
  import { withOpSeatFlock } from '../../src/steward/resync-tick.js';
11
11
  import { releaseRunUniverse } from './release-universe.js';
12
12
  import { isReleaseVersion } from './release-version.js';
13
13
  import { changedFilesBetween, clearPartialPlan, planPartialRegate, writePartialPlan } from './gate-partial.js';
14
- import { gitClosureTree, testsReachingChanged, type ClosureTree } from './gate-closure.js';
15
- import { releaseResumeAcceptedRegressions, releaseResumeGateExecutor, releaseResumeWaiver, type AcceptedRegression } from './resume.js';
14
+ import { gitClosureTree, testsReachingChanged, TRACEABLE_RELEASE_JSON, type ClosureTree } from './gate-closure.js';
15
+ import { releaseResumeAcceptedRegressions, releaseResumeGateExecutor, releaseResumeGatePodPool, releaseResumeWaiver, type AcceptedRegression } from './resume.js';
16
+ import { reserveReleaseResumeAttempt } from './resume-cap.js';
16
17
 
17
18
  const GRAPH = join(import.meta.dir, '../../graphs/release/release-loop.yaml');
18
19
 
@@ -33,6 +34,20 @@ export interface ResumeReleaseDeps {
33
34
  ledgerRoot?: string;
34
35
  /** GATE-REUSE-BY-DIFF: the tip tree for the import closure — default read from git objects of the tip commit. */
35
36
  closureTree?: (commit: string) => ClosureTree;
37
+ /** Source observation before the first resumed node. */
38
+ onSource?: (line: string) => void;
39
+ }
40
+
41
+ export interface ResumeReleaseOptions {
42
+ runId: string; from: string; partial?: boolean; waive?: string; reason?: string;
43
+ acceptedRegressions?: AcceptedRegression[]; gateExecutor?: string; gateHost?: string; gatePodPool?: string;
44
+ commandRoot?: string;
45
+ }
46
+
47
+ export interface ResumeReleasePlan {
48
+ runId: string; version: string; fromNode: string; commandRoot: string; tip: TipRefresh;
49
+ partial: PartialOutcome; gatePlan: { mode: 'not-run' | 'partial' | 'full'; reason?: string };
50
+ nextAttempt: number; attemptLimit: 2; preflight: 'passed';
36
51
  }
37
52
 
38
53
  /** CLI entry files: a production file that only names one (help text, a goal's command line) does not run it. */
@@ -66,6 +81,10 @@ function safeRunId(runId: string): string {
66
81
 
67
82
  /** Point the saved version-release output at the current release/<v> tip (fast-forward of the recorded commit only). */
68
83
  export function refreshReleaseBranchTip(runId: string, deps: ResumeReleaseDeps = {}, from?: string, waiverCommit?: string): TipRefresh {
84
+ return refreshReleaseBranchTipInternal(runId, deps, from, waiverCommit, false);
85
+ }
86
+
87
+ function refreshReleaseBranchTipInternal(runId: string, deps: ResumeReleaseDeps, from: string | undefined, waiverCommit: string | undefined, dryRun: boolean): TipRefresh {
69
88
  const root = deps.root ?? effectiveInstanceRoot();
70
89
  const statePath = join(root, 'graph-runs', 'release-loop', `${safeRunId(runId)}.json`);
71
90
  const state = JSON.parse(readFileSync(statePath, 'utf8')) as GraphRunState;
@@ -89,8 +108,14 @@ export function refreshReleaseBranchTip(runId: string, deps: ResumeReleaseDeps =
89
108
  const git = deps.git ?? gitRunner(deps.repo ?? process.cwd());
90
109
  const advertised = git(['ls-remote', '--heads', 'origin', `refs/heads/${branch}`]).split(/\s+/)[0] ?? '';
91
110
  if (!/^[0-9a-f]{40}$/i.test(advertised)) throw new Error(`${branch} is not on origin — this run was not cut as a release branch`);
92
- git(['fetch', 'origin', `+refs/heads/${branch}:refs/remotes/origin/${branch}`]);
93
- const to = git(['rev-parse', '--verify', `refs/remotes/origin/${branch}^{commit}`]);
111
+ let to: string;
112
+ if (dryRun) {
113
+ // Use the advertised object id directly: a preflight must not create/update local remote-tracking refs.
114
+ to = advertised;
115
+ } else {
116
+ git(['fetch', 'origin', `+refs/heads/${branch}:refs/remotes/origin/${branch}`]);
117
+ to = git(['rev-parse', '--verify', `refs/remotes/origin/${branch}^{commit}`]);
118
+ }
94
119
  if (waiverCommit && (recorded.toLowerCase() !== waiverCommit.toLowerCase() || to.toLowerCase() !== waiverCommit.toLowerCase() || advertised.toLowerCase() !== waiverCommit.toLowerCase())) {
95
120
  throw new Error('release branch moved — re-gate the new tip before waiving a node');
96
121
  }
@@ -114,6 +139,7 @@ export function refreshReleaseBranchTip(runId: string, deps: ResumeReleaseDeps =
114
139
  }
115
140
  const stamp = (deps.now?.() ?? new Date()).toISOString().replace(/[:.]/g, '-');
116
141
  const backup = `${statePath}.before-tip-${stamp}-${process.pid}-${to.slice(0, 12)}.bak`;
142
+ if (dryRun) return { version, branch, from: recorded, to, changed: true, statePath };
117
143
  // Exclusive: a retry never overwrites an earlier backup.
118
144
  copyFileSync(statePath, backup, constants.COPYFILE_EXCL);
119
145
  const refreshed = { ...output, commit: to, refreshedFrom: recorded };
@@ -134,10 +160,10 @@ export type PartialOutcome = { requested: false }
134
160
 
135
161
  /** Build (or clear) the partial re-gate plan for the new tip. Fail-closed: any doubt leaves a full gate.
136
162
  * GATE-REUSE-BY-DIFF: `--from gate` on a moved tip plans by default (`partial: undefined`); `partial: false` opts out. */
137
- function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean }, deps: ResumeReleaseDeps): PartialOutcome {
163
+ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean }, deps: ResumeReleaseDeps, dryRun = false): PartialOutcome {
138
164
  const root = deps.root ?? effectiveInstanceRoot();
139
165
  // Any resume starts from no plan: one without a partial plan (from whatever node) never inherits an older plan.
140
- clearPartialPlan(root, tip.version);
166
+ if (!dryRun) clearPartialPlan(root, tip.version);
141
167
  const auto = opts.partial === undefined;
142
168
  // The current graph checks public export before gate. A moved tip must resume there, so its gate still needs
143
169
  // the diff plan; in older saved graphs export-check followed gate and cannot consume one.
@@ -182,6 +208,18 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
182
208
  catch (error) { return fallback(`tip tree unreadable: ${String(error instanceof Error ? error.message : error).slice(0, 200)}`); }
183
209
  const tracked = new Set(tree.files);
184
210
  const tests = tree.files.filter((file) => GATE_TEST.test(file));
211
+ if (changed.includes(TRACEABLE_RELEASE_JSON)) {
212
+ // The one JSON exception is safe only while both the production reader and a test name this exact file at the tip.
213
+ // A renamed, computed or unreadable reader returns to the full gate instead of trusting a stale dependency map.
214
+ const reader = tree.read('scripts/release-loop/known-issues-node.ts');
215
+ if (!reader || !/readFileSync\(join\(import\.meta\.dir,\s*['"]known-issues-0\.2\.33-approved\.json['"]\)/.test(reader)) {
216
+ return fallback('known-issues JSON reader not statically verified — full gate');
217
+ }
218
+ const readerClosure = testsReachingChanged(tree, ['scripts/release-loop/known-issues-node.ts'], tests, { cliEntries: CLI_ENTRIES });
219
+ if (!readerClosure.ok || readerClosure.reached === 0) return fallback('known-issues JSON production reader reaches no verified test — full gate');
220
+ const jsonClosure = testsReachingChanged(tree, [TRACEABLE_RELEASE_JSON], tests, { cliEntries: CLI_ENTRIES });
221
+ if (!jsonClosure.ok || jsonClosure.reached === 0) return fallback('known-issues JSON reaches no verified test — full gate');
222
+ }
185
223
  const planned = planPartialRegate({ version: tip.version, priorCommit: tip.from, forCommit: tip.to, changedFiles: changed,
186
224
  priorFailures: prior.failures as string[], priorErrors: (prior.errors ?? []) as string[],
187
225
  existsAtTip: (file) => tracked.has(file),
@@ -201,9 +239,9 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
201
239
  }
202
240
  if (!clean) return fallback(`no clean gate verdict for ${tip.from.slice(0, 12)} — full gate`);
203
241
  }
204
- const path = writePartialPlan(root, planned.plan);
242
+ const path = dryRun ? undefined : writePartialPlan(root, planned.plan);
205
243
  debug.log('release-loop.resume', 'partial-planned', { version: tip.version, priorCommit: tip.from, commit: tip.to, files: planned.plan.files.length,
206
- tests: tests.length, auto, path, ...(planned.closure ? { reached: planned.closure.reached, opaque: planned.closure.opaque } : {}) });
244
+ tests: tests.length, auto, ...(path ? { path } : {}), dryRun, ...(planned.closure ? { reached: planned.closure.reached, opaque: planned.closure.opaque } : {}) });
207
245
  return { requested: true, applied: true, files: planned.plan.files.length, priorCommit: tip.from,
208
246
  ...(auto ? { auto: true as const } : {}), ...(planned.closure ? { closure: planned.closure } : {}) };
209
247
  }
@@ -217,34 +255,87 @@ function savedGraphPath(statePath: string): string | undefined {
217
255
  }
218
256
 
219
257
  /** Refresh the branch tip, then restart the failed run at `from` with the saved graph snapshot. */
220
- export async function resumeReleaseRun(opts: { runId: string; from: string; partial?: boolean; waive?: string; reason?: string; acceptedRegressions?: AcceptedRegression[]; gateExecutor?: string; gateHost?: string }, deps: ResumeReleaseDeps = {}): Promise<{ tip: TipRefresh; partial: PartialOutcome; state: GraphRunState }> {
258
+ export async function resumeReleaseRun(opts: ResumeReleaseOptions, deps: ResumeReleaseDeps = {}): Promise<{ tip: TipRefresh; partial: PartialOutcome; state: GraphRunState }> {
221
259
  // The lock root is the OP seat's (opSeatLockRoot), never the injected ledger root: a different `deps.root` must not
222
260
  // move this run onto a separate lock inode from the steward resync and the unattended release.
223
261
  return withOpSeatFlock(() => resumeReleaseRunLocked(opts, deps));
224
262
  }
225
263
 
226
- async function resumeReleaseRunLocked(opts: { runId: string; from: string; partial?: boolean; waive?: string; reason?: string; acceptedRegressions?: AcceptedRegression[]; gateExecutor?: string; gateHost?: string }, deps: ResumeReleaseDeps): Promise<{ tip: TipRefresh; partial: PartialOutcome; state: GraphRunState }> {
264
+ /** Validate a resume without changing the saved run, attempt ledger, or partial-gate plan. */
265
+ export async function planResumeReleaseRun(opts: ResumeReleaseOptions, deps: ResumeReleaseDeps = {}): Promise<ResumeReleasePlan> {
266
+ return withOpSeatFlock(() => planResumeReleaseRunLocked(opts, deps));
267
+ }
268
+
269
+ async function planResumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: ResumeReleaseDeps): Promise<ResumeReleasePlan> {
270
+ if (opts.from === 'version-release') throw new Error('--from version-release would cut again — resume at a later node');
271
+ deps = { ...deps, root: releaseRunUniverse({ root: deps.root }) };
272
+ const statePath = join(deps.root!, 'graph-runs', 'release-loop', `${safeRunId(opts.runId)}.json`);
273
+ const savedRun = JSON.parse(readFileSync(statePath, 'utf8')) as GraphRunState;
274
+ if (savedRun.graphId !== 'release-loop' || savedRun.runId !== opts.runId) throw new Error(`run identity mismatch: ${statePath}`);
275
+ if (opts.gateHost !== undefined && opts.gateExecutor === undefined) throw new Error('--gate-host requires --gate-executor host');
276
+ const hasOverrides = opts.waive !== undefined || opts.reason !== undefined || opts.acceptedRegressions !== undefined
277
+ || opts.gateExecutor !== undefined || opts.gatePodPool !== undefined;
278
+ const original = hasOverrides ? savedRun : undefined;
279
+ if (original && opts.gateExecutor !== undefined) releaseResumeGateExecutor(original, { from: opts.from, executor: opts.gateExecutor, host: opts.gateHost });
280
+ if (original && opts.gatePodPool !== undefined) releaseResumeGatePodPool(original, { from: opts.from, pool: opts.gatePodPool });
281
+ const waiver = original && (opts.waive !== undefined || opts.reason !== undefined) ? releaseResumeWaiver(original, opts) : undefined;
282
+ if (original && opts.acceptedRegressions !== undefined && !waiver)
283
+ releaseResumeAcceptedRegressions(original, opts.acceptedRegressions, opts.from);
284
+ const attempt = reserveReleaseResumeAttempt(savedRun, deps.root!, { now: deps.now, checkOnly: true, noDecision: true });
285
+ let waiverCommit: string | undefined;
286
+ if (waiver) {
287
+ const output = lastJsonObject(savedRun.nodes.find((node) => node.nodeId === 'version-release')?.output);
288
+ const version = output?.version, recorded = output?.commit;
289
+ if (typeof version !== 'string' || typeof recorded !== 'string' || !isReleaseVersion(version) || !/^[0-9a-f]{40}$/i.test(recorded)) throw new Error('release waiver requires a recorded release commit');
290
+ const git = deps.git ?? gitRunner(deps.repo ?? process.cwd());
291
+ const branch = `release/${version}`;
292
+ const advertised = git(['ls-remote', '--heads', 'origin', `refs/heads/${branch}`]).split(/\s+/)[0];
293
+ if (advertised?.toLowerCase() !== recorded.toLowerCase()) throw new Error('release branch moved — re-gate the new tip before waiving a node');
294
+ waiverCommit = recorded;
295
+ }
296
+ const tip = refreshReleaseBranchTipInternal(opts.runId, deps, opts.from, waiverCommit, true);
297
+ const commandRoot = verifyReleaseCommandRoot(opts.commandRoot ?? process.cwd(), tip.to, deps.git);
298
+ const partial = preparePartial(tip, opts, deps, true);
299
+ const fromIndex = savedRun.path.indexOf(opts.from);
300
+ const gateIndex = savedRun.path.indexOf('gate');
301
+ const gateWillRun = fromIndex >= 0 && gateIndex >= fromIndex;
302
+ const gatePlan = !gateWillRun ? { mode: 'not-run' as const }
303
+ : partial.requested && partial.applied ? { mode: 'partial' as const }
304
+ : { mode: 'full' as const, reason: partial.requested ? partial.reason : opts.partial === false ? 'partial recheck disabled' : 'no partial plan applies' };
305
+ return { runId: opts.runId, version: tip.version, fromNode: opts.from, commandRoot, tip, partial,
306
+ gatePlan, nextAttempt: attempt.attempt, attemptLimit: 2, preflight: 'passed' };
307
+ }
308
+
309
+ async function resumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: ResumeReleaseDeps): Promise<{ tip: TipRefresh; partial: PartialOutcome; state: GraphRunState }> {
227
310
  if (opts.from === 'version-release') throw new Error('--from version-release would cut again — resume at a later node');
228
311
  // RELEASE-LEDGER-UNIVERSE: decide the universe once; the run lookup and every resumed node use it.
229
312
  deps = { ...deps, root: releaseRunUniverse({ root: deps.root }) };
230
313
  const statePath = join(deps.root!, 'graph-runs', 'release-loop', `${safeRunId(opts.runId)}.json`);
314
+ const savedRun = JSON.parse(readFileSync(statePath, 'utf8')) as GraphRunState;
231
315
  let waiverCommit: string | undefined;
232
316
  if (opts.gateHost !== undefined && opts.gateExecutor === undefined) throw new Error('--gate-host requires --gate-executor host');
233
- const original = opts.waive !== undefined || opts.reason !== undefined || opts.acceptedRegressions !== undefined || opts.gateExecutor !== undefined
234
- ? JSON.parse(readFileSync(statePath, 'utf8')) as GraphRunState : undefined;
317
+ const original = opts.waive !== undefined || opts.reason !== undefined || opts.acceptedRegressions !== undefined || opts.gateExecutor !== undefined || opts.gatePodPool !== undefined
318
+ ? savedRun : undefined;
235
319
  // GATE-HOST-EXECUTOR: checked before the tip refresh writes anything; the runner checks it again under the resume lock.
236
320
  const gateExecutor = original && opts.gateExecutor !== undefined
237
321
  ? releaseResumeGateExecutor(original, { from: opts.from, executor: opts.gateExecutor, host: opts.gateHost }) : undefined;
322
+ const gatePodPool = original && opts.gatePodPool !== undefined
323
+ ? releaseResumeGatePodPool(original, { from: opts.from, pool: opts.gatePodPool }) : undefined;
238
324
  if (gateExecutor) debug.log('release-loop.resume', 'gate-executor', { runId: opts.runId, from: opts.from, executor: gateExecutor.set.gateExecutor, host: gateExecutor.set.gateRemote ?? null, previous: gateExecutor.previous });
325
+ if (gatePodPool) debug.log('release-loop.resume', 'gate-pod-pool', {
326
+ runId: opts.runId, from: opts.from, pool: gatePodPool.set.gatePodPool, previous: gatePodPool.previous,
327
+ });
239
328
  const waiver = original && (opts.waive !== undefined || opts.reason !== undefined)
240
329
  ? releaseResumeWaiver(original, opts) : undefined;
241
330
  if (waiver) debug.log('release-loop.resume', 'waiver', { runId: opts.runId, node: waiver.nodeId, reason: waiver.reason, unmeasured: waiver.unmeasured ?? null });
242
331
  const acceptedRegressions = original && opts.acceptedRegressions !== undefined && !waiver
243
332
  ? releaseResumeAcceptedRegressions(original, opts.acceptedRegressions, opts.from) : undefined;
333
+ if (savedRun.graphId !== 'release-loop' || savedRun.runId !== opts.runId) throw new Error(`run identity mismatch: ${statePath}`);
334
+ // Validate request arguments before the cap, then block the third attempt before tip refresh.
335
+ reserveReleaseResumeAttempt(savedRun, deps.root!, { now: deps.now, checkOnly: true });
244
336
  // A waived verdict may reuse its original commit. A moved tip still requires a full re-gate.
245
337
  if (waiver) {
246
- const saved = JSON.parse(readFileSync(statePath, 'utf8')) as GraphRunState;
247
- const versionRecord = saved.nodes.find((node) => node.nodeId === 'version-release');
338
+ const versionRecord = savedRun.nodes.find((node) => node.nodeId === 'version-release');
248
339
  const output = lastJsonObject(versionRecord?.output);
249
340
  const version = output?.version;
250
341
  const recorded = output?.commit;
@@ -257,13 +348,21 @@ async function resumeReleaseRunLocked(opts: { runId: string; from: string; parti
257
348
  }
258
349
  // Check the refreshed tip before refresh writes a new version-release record or backup.
259
350
  const tip = refreshReleaseBranchTip(opts.runId, deps, opts.from, waiverCommit);
351
+ const commandRoot = verifyReleaseCommandRoot(opts.commandRoot ?? process.cwd(), tip.to, deps.git);
260
352
  const partial = preparePartial(tip, opts, deps);
353
+ // A failed preflight does not consume one of the two release resume attempts.
354
+ const attempt = reserveReleaseResumeAttempt(savedRun, deps.root!, { now: deps.now });
355
+ debug.log('release-loop.resume', 'attempt', { runId: opts.runId, attempt: attempt.attempt, limit: 2, ledger: attempt.path });
261
356
  // Resume the graph at the path it was started from (its snapshot is used); only tests inject another path.
262
357
  const graphPath = deps.graphPath ?? savedGraphPath(tip.statePath) ?? GRAPH;
358
+ const savedSource = JSON.parse(readFileSync(tip.statePath, 'utf8')) as GraphRunState;
359
+ deps.onSource?.(`레시피 출처: graph=${graphPath} · graph sha=${savedSource.graphSnapshot?.graphSha?.slice(0, 12) ?? 'legacy'} · recipes sha=${savedSource.graphSnapshot?.recipesSha?.slice(0, 12) ?? 'legacy'} · command-root=${commandRoot} · 컷 tip=${tip.to.slice(0, 12)}`);
263
360
  const state = await (deps.graph ?? runGraph)(graphPath, { resumeRunId: opts.runId, fromNodeId: opts.from, pinChildUniverse: true,
361
+ releaseCommandSource: { root: commandRoot, cutTip: tip.to, ...(deps.git ? { git: deps.git } : {}) },
264
362
  ...(waiver ? { releaseWaiver: { ...waiver, requestedRegressions: opts.acceptedRegressions ?? [] } } : {}),
265
363
  ...(acceptedRegressions ? { releaseAcceptedRegressions: { requested: opts.acceptedRegressions!, acceptedRegressions } } : {}),
266
364
  ...(gateExecutor ? { releaseGateExecutor: { executor: gateExecutor.set.gateExecutor, ...(opts.gateHost !== undefined ? { host: opts.gateHost } : {}) } } : {}),
365
+ ...(gatePodPool ? { releaseGatePodPool: gatePodPool.set.gatePodPool } : {}),
267
366
  deps: { root: deps.root, ...(deps.runBash ? { runBash: deps.runBash } : {}) } });
268
367
  return { tip, partial, state };
269
368
  }
@@ -137,6 +137,28 @@ export interface GateExecutorOverride {
137
137
  previous: { gateExecutor: unknown; gateRemote: unknown };
138
138
  }
139
139
 
140
+ /** RESUME-GATE-POOL-OVERRIDE — replace the saved pool for a gate that will be re-run. */
141
+ export interface GatePodPoolOverride {
142
+ kind: 'gate-pod-pool';
143
+ at: string;
144
+ from: string;
145
+ set: { gatePodPool: string };
146
+ previous: { gatePodPool: unknown };
147
+ }
148
+
149
+ export function releaseResumeGatePodPool(state: GraphRunState, opts: { from: string; pool: unknown; at?: string }): GatePodPoolOverride {
150
+ const input = state.input;
151
+ if (!input || typeof input !== 'object' || Array.isArray(input) || typeof (input as { version?: unknown }).version !== 'string') throw new Error('release run has no input');
152
+ if (typeof opts.pool !== 'string' || !opts.pool.trim()) throw new Error('--gate-pod-pool must not be empty');
153
+ const start = state.path.indexOf(opts.from);
154
+ if (start < 0) throw new Error(`--from ${opts.from} is not a node on the saved path`);
155
+ const gate = state.path.indexOf('gate');
156
+ if (gate >= 0 && start > gate) throw new Error('--gate-pod-pool needs a resume that re-runs gate (--from gate or earlier)');
157
+ const saved = input as { gatePodPool?: unknown };
158
+ return { kind: 'gate-pod-pool', at: opts.at ?? new Date().toISOString(), from: opts.from,
159
+ set: { gatePodPool: opts.pool.trim() }, previous: { gatePodPool: saved.gatePodPool ?? null } };
160
+ }
161
+
140
162
  /**
141
163
  * Switch the gate executor of a resumed release run. Not a waiver: the gate node runs again (the restart must be at or
142
164
  * before gate) and produces a normal verdict; only where its sweep runs changes.
@@ -156,7 +178,8 @@ export function releaseResumeGateExecutor(state: GraphRunState, opts: { from: st
156
178
  // A restart after gate keeps the recorded verdict — switching its executor there would record a choice nothing used.
157
179
  if (gate >= 0 && start > gate) throw new Error('--gate-executor needs a resume that re-runs gate (--from gate or earlier)');
158
180
  // Back to pod: restore the gateRemote the run started with (an earlier host override replaced it).
159
- const firstHost = choice.executor === 'pod' ? state.inputOverrides?.find((item) => item.kind === 'gate-executor' && item.set.gateRemote !== undefined) : undefined;
181
+ const firstHost = choice.executor === 'pod' ? state.inputOverrides?.find((item): item is GateExecutorOverride =>
182
+ item.kind === 'gate-executor' && item.set.gateRemote !== undefined) : undefined;
160
183
  const restored = firstHost ? firstHost.previous.gateRemote : undefined;
161
184
  return {
162
185
  kind: 'gate-executor', at: opts.at ?? new Date().toISOString(), from: opts.from,
@@ -16,7 +16,7 @@ import { SPAWN_SYNC_MAX_BUFFER } from '../../src/util/spawn-sync-output.js';
16
16
  import { copyFileSync, existsSync, mkdtempSync, openSync, symlinkSync } from 'node:fs';
17
17
  import { tmpdir } from 'node:os';
18
18
  import { basename, join, resolve } from 'node:path';
19
- import { parseTuiRegress, regressWarning, tuiChecks, tuiVerdict, type TuiCheck, type TuiRegress } from './tui-node-checks';
19
+ import { parseTuiRegress, regressWarning, tuiChecks, tuiVerdict, validatedTuiRegress, type TuiCheck, type TuiRegress } from './tui-node-checks';
20
20
 
21
21
  const log = (msg: string) => process.stderr.write(`[tui-node] ${msg}\n`);
22
22
  const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
@@ -83,7 +83,13 @@ export function measureTuiRegress(worktree: string, runner: typeof spawnSync = s
83
83
  export function tuiNodeOutput(commit: string, attempts: Array<{ checks: TuiCheck[]; png?: string }>, regress: TuiRegress, failure?: string) {
84
84
  const last = attempts.at(-1);
85
85
  const verdict = !last ? 'error' : tuiVerdict(last.checks) === 'fail' ? 'fail' : attempts.length > 1 ? 'flaky' : 'pass';
86
- return { verdict, commit, attempts: attempts.length, checks: last?.checks ?? [], png: last?.png, regress, ...(failure ? { error: failure } : {}) };
86
+ const measured = validatedTuiRegress(regress);
87
+ const regressSummary = 'unmeasured' in measured
88
+ ? `regress unmeasured: ${measured.unmeasured}`
89
+ : `regress ${measured.pass} pass / ${measured.fail} fail${measured.fail ? ` (${measured.results.filter((r) => !r.ok).map((r) => r.id).join(',')})` : ''}`;
90
+ const checks = last?.checks ?? [];
91
+ const summary = `TUI ${verdict}: basic ${checks.filter((check) => check.pass).length}/${checks.length} · ${regressSummary}`;
92
+ return { verdict, summary, commit, attempts: attempts.length, checks, png: last?.png, regress: measured, ...(failure ? { error: failure } : {}) };
87
93
  }
88
94
 
89
95
  async function main(): Promise<number> {
@@ -10,6 +10,7 @@ import { isLandingFreezeRefusal, readLandingFreeze, LandingFrozenError } from '.
10
10
  import { debug } from '../../src/debug/log.js';
11
11
  import { runGraph, type GraphRunState } from '../../src/graph-runner/runner.js';
12
12
  import { withOpSeatFlock } from '../../src/steward/resync-tick.js';
13
+ import { ReleaseRunClaimConflict, findActiveReleaseRun } from '../../src/cli/release-single-flight.js';
13
14
  import { releaseRunUniverse } from './release-universe.js';
14
15
  import { baseVersion, isStableVersion, nextPrereleaseVersion, type PrereleaseKind } from './release-version.js';
15
16
  import { publishAtError } from './publish-at.js';
@@ -19,6 +20,8 @@ const VERSION = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/;
19
20
  const GRAPH = join(import.meta.dir, '../../graphs/release/release-loop.yaml');
20
21
 
21
22
  export interface ReleaseLoopConfig {
23
+ /** Mid-cycle full-gate rehearsal, hours before cut (default 12; 0 disables). */
24
+ rehearsalHardIntervalHours?: number;
22
25
  gatePodPool?: string;
23
26
  gatePodBunCache?: string;
24
27
  gatePodShards?: number;
@@ -138,7 +141,7 @@ export function buildReleaseRunInput(version: string, deps: UnattendedReleaseDep
138
141
 
139
142
  /** Fail closed at the entry boundary, before any graph node can change a release. */
140
143
  export async function runUnattendedRelease(
141
- opts: { version: string; dryRun?: boolean; cutCommit?: string; forceFreeze?: boolean; mainCut?: boolean; prerelease?: PrereleaseKind; publishAt?: string; gateExecutor?: string; gateHost?: string },
144
+ opts: { version: string; dryRun?: boolean; cutCommit?: string; forceFreeze?: boolean; forceNew?: boolean; mainCut?: boolean; prerelease?: PrereleaseKind; publishAt?: string; gateExecutor?: string; gateHost?: string },
142
145
  deps: UnattendedReleaseDeps = {},
143
146
  ): Promise<{ input: ReleaseRunInput; dryRun: boolean; state?: GraphRunState }> {
144
147
  // A dry run only builds the input and mutates nothing, so it neither creates nor waits on the OP-seat lock.
@@ -149,7 +152,7 @@ export async function runUnattendedRelease(
149
152
  }
150
153
 
151
154
  async function runUnattendedReleaseLocked(
152
- opts: { version: string; dryRun?: boolean; cutCommit?: string; forceFreeze?: boolean; mainCut?: boolean; prerelease?: PrereleaseKind; publishAt?: string; gateExecutor?: string; gateHost?: string },
155
+ opts: { version: string; dryRun?: boolean; cutCommit?: string; forceFreeze?: boolean; forceNew?: boolean; mainCut?: boolean; prerelease?: PrereleaseKind; publishAt?: string; gateExecutor?: string; gateHost?: string },
153
156
  deps: UnattendedReleaseDeps,
154
157
  ): Promise<{ input: ReleaseRunInput; dryRun: boolean; state?: GraphRunState }> {
155
158
  // RELEASE-BRANCH (10-06): the default cut is a release branch, so main keeps landing and the run no longer needs a
@@ -188,6 +191,9 @@ async function runUnattendedReleaseLocked(
188
191
  if (beforeGraph && !opts.forceFreeze) throw new LandingFrozenError(beforeGraph);
189
192
  // RELEASE-LEDGER-UNIVERSE: one universe for the run ledger, the release ledger and every node subprocess.
190
193
  const root = releaseRunUniverse({ root: deps.ledgerRoot });
194
+ // Resume and fresh runs share the OP-seat flock. Recheck under that flock so a
195
+ // resume that began after CLI preflight cannot slip between the ledger scan and graph start.
196
+ if (!opts.forceNew && findActiveReleaseRun(version, root)) throw new ReleaseRunClaimConflict();
191
197
  const state = await (deps.graph ?? runGraph)(GRAPH, { input, pinChildUniverse: true, deps: { root } });
192
198
  // A freeze switched on while the graph ran stops gate or publish inside it. Only a failed gate/publish node whose own
193
199
  // record is the freeze refusal is reported as the freeze; any other failure keeps its own reason.
@@ -54,6 +54,7 @@ import { readOriginSessionMeta } from './origin-session-meta.js';
54
54
  import { isFactQuestion } from './fact-question.js';
55
55
  import { classifyTuiChatIntent, formatTuiRouteLine, isSeatOpsDirective, type TuiChatRoute } from '../core-turn/short-question.js';
56
56
  import { HARNESS_ROLE_ENV } from '../harness/harness-space.js';
57
+ import { createReasoningThoughtBuffer } from './reasoning-thought-buffer.js';
57
58
 
58
59
  const FACT_QUESTION_INSTRUCTION = '사실·최신 정보 질문이다. WebSearch 를 1~2회 쓰고 검색 결과(제목·스니펫·URL)로 답하라. 출처 링크를 붙여라. 셸·브라우저·파일 도구는 검색 결과로 부족할 때만. 도구 예산에 닿으면 지금까지 근거로 답하고 끝에 «더 찾아볼까요?»를 붙여라.';
59
60
 
@@ -382,6 +383,16 @@ export function bridgeCoreTurnToAcp(
382
383
  return approvedDispatch(name, args, dispatchCtx);
383
384
  }
384
385
  : approvedDispatch;
386
+ // TUI-TURN-NARRATION 조각 2 — 추론 요약을 «문단» 단위로 모아 ACP `agent_thought_chunk`
387
+ // (`_meta.reasoning: true`)로 보낸다. 대시보드가 흐린 💭 한 줄로 그린다(`acp-chat.ts`).
388
+ // ⛔ 전에는 debug.log 로만 남아 화면엔 «무엇을 하는지»가 0줄이었다(결함 #87).
389
+ const reasoningThoughts = createReasoningThoughtBuffer((text) => {
390
+ debug.log('llm.reasoning', 'thought-emitted', { sessionId: turnCtx.sessionId, chars: text.length });
391
+ void turnCtx.pushSessionUpdate(
392
+ { sessionUpdate: 'agent_thought_chunk', content: { type: 'text', text } },
393
+ { reasoning: true },
394
+ );
395
+ });
385
396
  const dispatchResult = await runTurnDispatch({
386
397
  sessionId: turnCtx.sessionId,
387
398
  ...(originSessionId ? { originSessionId } : {}),
@@ -403,6 +414,7 @@ export function bridgeCoreTurnToAcp(
403
414
  // contract is synchronous onText. Chunks are delivered in
404
415
  // order because `sessionUpdate` is serialized per-
405
416
  // connection on the ACP SDK side.
417
+ if (delta.length > 0) reasoningThoughts.flush();
406
418
  streamedTextSum += delta;
407
419
  void turnCtx.push(delta);
408
420
  },
@@ -420,6 +432,7 @@ export function bridgeCoreTurnToAcp(
420
432
  toolKinds.add(call.name);
421
433
  observeToolBudget();
422
434
  }
435
+ reasoningThoughts.flush();
423
436
  void turnCtx.pushToolCall(call);
424
437
  },
425
438
  onToolResult: (call) => { void turnCtx.pushToolResult(call); },
@@ -435,15 +448,15 @@ export function bridgeCoreTurnToAcp(
435
448
  ...(resolvedRoute ? { routeProvider: resolvedRoute.provider } : {}),
436
449
  });
437
450
  },
438
- // Codex Responses API reasoning summary stream. Currently
439
- // surfaced via forensic only (debug.log) — acts as the
440
- // "did the model actually emit reasoning?" verification
441
- // signal before we wire the dashboard UI in a fast-follow.
451
+ // Codex Responses API reasoning summary stream. Surfaced to the
452
+ // dashboard as `agent_thought_chunk` via `reasoningThoughts`
453
+ // (TUI-TURN-NARRATION 조각 2) ⊕ the forensic debug.log below.
442
454
  // The forensic line carries the streamed delta and the
443
455
  // running summary index so a single grep gives the
444
456
  // reasoning trace in order. summary_part_added marks the
445
457
  // paragraph boundary; summary_delta is the text within.
446
458
  onReasoning: (event) => {
459
+ reasoningThoughts.onEvent(event);
447
460
  if (debug.enabled) {
448
461
  if (event.kind === 'summary_delta') {
449
462
  debug.log('llm.reasoning', 'codex.summary.delta', {
@@ -459,6 +472,7 @@ export function bridgeCoreTurnToAcp(
459
472
  }
460
473
  },
461
474
  onTurnComplete: async (newMessages) => {
475
+ reasoningThoughts.flush();
462
476
  // Surface runtime-injected synthesis to ACP. emitTurnComplete
463
477
  // pushes the final assistant text (which may be the W5-E/F/G
464
478
  // force-synthesis output) into history. Compare to what was
@@ -592,7 +606,8 @@ export function bridgeCoreTurnToAcp(
592
606
  }
593
607
  },
594
608
  },
595
- });
609
+ // 오류·취소로 끝나 onTurnComplete 가 안 불려도 마지막 추론 문단을 내보낸다(#28964 리뷰 FU-1 · 빈 버퍼면 무동작).
610
+ }).finally(() => reasoningThoughts.flush());
596
611
  // ⭐ 인증 거절은 코어 턴이 «던지지 않고» 돌려준다 — 여기서 안 읽으면 서버는 end_turn 을 내고
597
612
  // TUI 엔 «✔ 완료»만 남는다(TUI 결함 #22). 던져서 실패 경로(prompt-failed · 오류 줄)로 보낸다.
598
613
  const stopReason = (dispatchResult as { stopReason?: unknown } | null | undefined)?.stopReason;
@@ -0,0 +1,45 @@
1
+ // TUI-TURN-NARRATION 조각 2 — 추론 요약(reasoning summary)을 화면 한 줄로 낸다.
2
+ //
3
+ // 🩸 왜 있나 (결함 #87 · 2026-10-18): gpt-6 계열 TUI 턴은 도구 사이에 글을 0자 낸다
4
+ // (`llm.stream consume-end` `textChars=0`). 그런데 추론 요약은 라운드마다 24~37자씩 «온다»
5
+ // (`reasoningChars`). `core-turn-bridge` 의 `onReasoning` 이 debug.log 로만 남기고
6
+ // 화면으로 안 보내서, 사용자는 «무엇을 하는지» 한 줄도 못 봤다.
7
+ // 대시보드는 이미 `agent_thought_chunk` ⊕ `_meta.reasoning` 을 흐린 💭 줄로 그린다
8
+ // (`dashboard/chat/acp-chat.ts`) — 빠진 것은 «주는 자리» 하나였다.
9
+ //
10
+ // ⭐ 델타를 그대로 흘리지 않고 «문단» 단위로 모은다 — 델타마다 보내면 한 생각이 여러 줄로 찢긴다.
11
+ // 문단 경계 = 다음 `summary_part_added` · 도구 호출 · 답 글 시작 · 턴 끝.
12
+
13
+ export type ReasoningEvent =
14
+ | { kind: 'summary_part_added'; summaryIndex?: number }
15
+ | { kind: 'summary_delta'; delta: string; summaryIndex?: number };
16
+
17
+ export interface ReasoningThoughtBuffer {
18
+ onEvent(event: ReasoningEvent): void;
19
+ /** 모은 문단을 내보낸다(비었으면 아무것도 안 한다). */
20
+ flush(): void;
21
+ }
22
+
23
+ /** 요약 문단은 짧다(실측 24~37자). 폭주 방어로만 자른다. */
24
+ const MAX_THOUGHT_CHARS = 400;
25
+
26
+ export function createReasoningThoughtBuffer(emit: (text: string) => void): ReasoningThoughtBuffer {
27
+ let buf = '';
28
+ const flush = (): void => {
29
+ const text = buf.trim();
30
+ buf = '';
31
+ if (text.length === 0) return;
32
+ emit(text.length > MAX_THOUGHT_CHARS ? `${text.slice(0, MAX_THOUGHT_CHARS)}…` : text);
33
+ };
34
+ return {
35
+ onEvent(event) {
36
+ if (event.kind === 'summary_part_added') {
37
+ flush();
38
+ return;
39
+ }
40
+ // 모으는 단계에서도 상한을 지킨다 — 문단 경계 없이 긴 스트림이 와도 버퍼가 무한히 크지 않게.
41
+ if (buf.length <= MAX_THOUGHT_CHARS) buf += event.delta.slice(0, MAX_THOUGHT_CHARS + 1 - buf.length);
42
+ },
43
+ flush,
44
+ };
45
+ }
@@ -55,6 +55,9 @@ export interface AcpReviewerOpts {
55
55
  */
56
56
  export const DEFAULT_REVIEW_BACKEND = 'codex';
57
57
 
58
+ /** Stable failureReason prefix carried through reviewPullRequest's fail-soft result. */
59
+ export const GROK_ACP_REVIEW_TIMEOUT = 'ACP_REVIEW_TIMEOUT:grok:';
60
+
58
61
  // ACP ToolKind(zToolKind) 중 read-only(부작용 없음) — 리뷰어에게 허가할 계열.
59
62
  const READONLY_TOOL_KINDS = new Set(['read', 'search', 'fetch', 'think', 'switch_mode']);
60
63
  // 명시적 변경/실행 계열 — 리뷰(read-only)에선 절대 불허.
@@ -84,7 +87,7 @@ const TOOL_REVIEW_PREAMBLE = [
84
87
  * - model 지정 시 selectSessionModel 로 tier 고정(picked 관측).
85
88
  * - ⭐auto-allow permission approver — 리뷰어의 read-only 툴콜을 무조건 허가(대표 지시)해 Read 반복거부
86
89
  * 정체를 원천제거. 각 허가는 debug.log 로 각인(제1원칙 관측 관문 — 자동승인은 관측되는 결정).
87
- * - timeout: 초과 시 cancel→throw. 오류/타임아웃은 각인 후 rethrow → reviewPullRequest 가 fail-soft
90
+ * - timeout: 초과 시 실패를 확정하고 cancel·stop. 오류/타임아웃은 각인 후 rethrow → reviewPullRequest 가 fail-soft
88
91
  * (reviewed=false)로 흡수, CLI 가 reviewed=false 를 "리뷰 실패"로 표면화(부분결과 오판 pass 방지). */
89
92
  export function makeAcpReviewLLM(opts: AcpReviewerOpts): (prompt: string, images?: readonly ReviewImage[]) => Promise<string> {
90
93
  return async (prompt: string, images?: readonly ReviewImage[]): Promise<string> => {
@@ -117,8 +120,13 @@ export function makeAcpReviewLLM(opts: AcpReviewerOpts): (prompt: string, images
117
120
  model: opts.model ?? '(default)',
118
121
  promptChars: prompt.length,
119
122
  });
123
+ const startedAt = Date.now();
124
+ let phase = 'spawn';
125
+ let phaseStartedAt = startedAt;
126
+ const enterPhase = (next: string): void => { phase = next; phaseStartedAt = Date.now(); };
120
127
  let allowed = 0;
121
128
  let denied = 0;
129
+ const observedToolCalls = new Set<string>();
122
130
  let capabilitiesObserved = false;
123
131
  const agentOpts: Omit<AcpAgentOpts, 'backendId'> = {
124
132
  cwd: opts.cwd,
@@ -149,10 +157,12 @@ export function makeAcpReviewLLM(opts: AcpReviewerOpts): (prompt: string, images
149
157
  };
150
158
  // A review owns its manager and subprocess: the global manager caches
151
159
  // chat agents, whose callbacks and lifecycle must not be mutated here.
152
- const reviewManager = injected || opts.backendSpec ? undefined : new AcpAgentManager();
160
+ // Grok's start can hang before the manager registers the subprocess;
161
+ // own it directly so the handshake timeout can stop that child.
162
+ const directAgent = injected || Boolean(opts.backendSpec) || backend === 'grok';
163
+ const reviewManager = directAgent ? undefined : new AcpAgentManager();
153
164
  let agent: AcpAgent | undefined;
154
165
  let text = '';
155
- let timedOut = false;
156
166
  let timer: ReturnType<typeof setTimeout> | undefined;
157
167
  const handshakeTimeoutMs = opts.handshakeTimeoutMs ?? Math.min(timeoutMs, 60_000);
158
168
  const handshakeDeadline = Date.now() + handshakeTimeoutMs;
@@ -165,9 +175,16 @@ export function makeAcpReviewLLM(opts: AcpReviewerOpts): (prompt: string, images
165
175
  })]).finally(() => { if (handshakeTimer) clearTimeout(handshakeTimer); });
166
176
  try {
167
177
  if (opts.createAgent) agent = opts.createAgent({ backendId: requestedBackend, ...agentOpts });
168
- else if (opts.backendSpec) agent = new AcpAgent({ backendId: requestedBackend, ...agentOpts });
169
- else agent = await withinHandshake(reviewManager!.getAgent(requestedBackend, agentOpts));
170
- if (injected || opts.backendSpec) await withinHandshake(agent.start());
178
+ else if (directAgent) agent = new AcpAgent({ backendId: requestedBackend, ...agentOpts });
179
+ else {
180
+ // getAgent owns both spawn and initialize; neither step is independently observable here.
181
+ enterPhase('spawn-initialize');
182
+ agent = await withinHandshake(reviewManager!.getAgent(requestedBackend, agentOpts));
183
+ }
184
+ if (directAgent) {
185
+ enterPhase(injected ? 'initialize' : 'spawn-initialize');
186
+ await withinHandshake(agent.start());
187
+ }
171
188
  if (!capabilitiesObserved) {
172
189
  try {
173
190
  debug.log('acp-review', 'capabilities', {
@@ -181,12 +198,14 @@ export function makeAcpReviewLLM(opts: AcpReviewerOpts): (prompt: string, images
181
198
  // Observability must not interrupt a review.
182
199
  }
183
200
  }
201
+ enterPhase('session-new');
184
202
  const sid = await withinHandshake(agent.newSession());
185
203
  if (opts.model) {
204
+ enterPhase('model-select');
186
205
  const picked = await withinHandshake(agent.selectSessionModel(sid, opts.model));
187
206
  debug.log('acp-review', 'model', { requested: opts.model, picked: picked?.name ?? null, backend });
188
207
  }
189
- timer = setTimeout(() => { timedOut = true; agent!.cancel(sid).catch(() => { /* noop */ }); }, timeoutMs);
208
+ enterPhase('prompt');
190
209
  // ⭐⭐ `P4b` 멀티미디어 — 이미지를 «별도 ContentBlock» 으로 싣는다.
191
210
  // ⛔ 광고를 «먼저 본다** — 백엔드가 image 를 안 받는다고 말하면 안 보낸다. 보내 봐야
192
211
  // 거절이 이 층보다 훨씬 읽기 어려운 자리에서 나고, 그 거절은 리뷰 실패로 보인다.
@@ -211,23 +230,44 @@ export function makeAcpReviewLLM(opts: AcpReviewerOpts): (prompt: string, images
211
230
  // Observation must not prevent the review from running.
212
231
  }
213
232
  }
214
- await agent.prompt(sid, [{ type: 'text', text: TOOL_REVIEW_PREAMBLE + prompt }, ...imageBlocks], (u) => {
233
+ const promptAttempt = agent.prompt(sid, [{ type: 'text', text: TOOL_REVIEW_PREAMBLE + prompt }, ...imageBlocks], (u) => {
234
+ if (u.sessionUpdate === 'tool_call' || u.sessionUpdate === 'tool_call_update') {
235
+ observedToolCalls.add(u.toolCallId);
236
+ }
215
237
  if (u.sessionUpdate === 'agent_message_chunk') {
216
238
  const c = u.content;
217
239
  if (c.type === 'text' && c.text) text += c.text;
218
240
  }
219
241
  });
220
- if (timedOut) throw new Error(`ACP 리뷰 타임아웃(${timeoutMs}ms·툴콜 ${allowed})`);
242
+ // Do not rely on cancel causing prompt() to settle: a stalled ACP child may never answer it.
243
+ await Promise.race([promptAttempt, new Promise<never>((_, reject) => {
244
+ timer = setTimeout(() => {
245
+ // Commit the timeout before cancel can settle prompt with a partial response.
246
+ reject(new Error(backend === 'grok'
247
+ ? `${GROK_ACP_REVIEW_TIMEOUT} ${timeoutMs}ms·툴콜 ${observedToolCalls.size}`
248
+ : `ACP 리뷰 타임아웃(${timeoutMs}ms·툴콜 ${allowed})`));
249
+ void agent!.cancel(sid).catch(() => { /* noop */ });
250
+ }, timeoutMs);
251
+ })]);
221
252
  } catch (e) {
222
- debug.log('acp-review', 'error', { backend, model: opts.model ?? '(default)', message: (e as Error).message }, { level: 'error' });
223
- throw e;
253
+ const message = (e as Error).message;
254
+ const handshakeTimeout = message.startsWith('ACP handshake timeout (');
255
+ const failure = backend === 'grok' && handshakeTimeout
256
+ ? new Error(`${GROK_ACP_REVIEW_TIMEOUT} ${message}`) : e;
257
+ const timeout = message.startsWith(GROK_ACP_REVIEW_TIMEOUT) || message.startsWith('ACP 리뷰 타임아웃(') || handshakeTimeout;
258
+ debug.log('acp-review', 'error', {
259
+ backend, provider: backend, model: opts.model ?? '(default)', message: (failure as Error).message,
260
+ phase, elapsedMs: Date.now() - startedAt, phaseElapsedMs: Date.now() - phaseStartedAt,
261
+ toolCalls: observedToolCalls.size, allowed, denied, ...(timeout ? { classification: phase === 'prompt' && observedToolCalls.size === 0 ? 'pre-tool-timeout' : 'acp-timeout' } : {}),
262
+ }, { level: 'error' });
263
+ throw failure;
224
264
  } finally {
225
265
  if (timer) clearTimeout(timer);
226
266
  // Both paths are review-owned: injected agents retain their direct
227
267
  // cleanup seam, while the per-review manager releases its agents and
228
268
  // process hooks. An injected agent is external to the manager.
229
269
  try {
230
- if (injected || opts.backendSpec) await agent?.stop();
270
+ if (directAgent) await agent?.stop();
231
271
  else await reviewManager!.dispose();
232
272
  } catch (e) {
233
273
  debug.log('acp-review', 'stop-fail', { backend, message: (e as Error).message }, { level: 'error' });