elanous 0.2.28 → 0.2.29

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 (609) 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/{y1ltt7oCZxVpC6zVPhZ2O → cIpv5nU9AycwYWZI9H589}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{ab4d57c2.e90bc2cb31949045.js → 058a98ba.7eb28d5e6dc03565.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/{4565-67e4c620597965c8.js → 1015-9cba22a5a4554797.js} +1 -1
  6. package/apps/pwa/out/_next/static/chunks/1084-4e66af7376a18db7.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1106.3a6410b7b0c70ad4.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1127.32fc642f70423506.js +8 -0
  9. package/apps/pwa/out/_next/static/chunks/{1516.83dd2c1b86b4e507.js → 1142.65d002789ab3af7d.js} +2 -2
  10. package/apps/pwa/out/_next/static/chunks/1189-d9b8e1995a831fae.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/1224.6da98c7ad0eb7dd8.js +34 -0
  12. package/apps/pwa/out/_next/static/chunks/1370.4e3e2558f111fd5d.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/1514-b7da377fdc4eb17a.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/1516.dfaceff3fd2d0e0d.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/1577.3c24c32584e10d70.js +286 -0
  16. package/apps/pwa/out/_next/static/chunks/161.130a569855c4d68c.js +24 -0
  17. package/apps/pwa/out/_next/static/chunks/1782-59db1b5aa06df38a.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/{1743.563d3168e95825ef.js → 1852.7f7b53991fcb99ef.js} +2 -2
  19. package/apps/pwa/out/_next/static/chunks/1864.16da9556e78a9236.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/1b6f6b2a.aff090122be3726e.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/2341.e8a9135e5b910dd5.js +36 -0
  22. package/apps/pwa/out/_next/static/chunks/257.10afdbe3520f2fc2.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/2658.87550e692dc493f6.js +64 -0
  24. package/apps/pwa/out/_next/static/chunks/2673.85e2f02efae7a85f.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/2766-bee623b37472ed63.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/2794.6d836872b0bf43e2.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/2806.eb57ced1a528ed39.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/283.7a121c4910e6dad7.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/{65ebff95.081daeff622d238e.js → 285adb1a.5654e605dddf86d2.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/2870.1dc29aa5fc01f09f.js +115 -0
  31. package/apps/pwa/out/_next/static/chunks/293.fd38e77ce5d48103.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/2993-5952a1cd3fd5d441.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/3160-95c96f4d2a21dd26.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/32-a523427c5bd5ed00.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/3280.af38002a6424a905.js +90 -0
  36. package/apps/pwa/out/_next/static/chunks/328bb3ab-6628a1114fe3fb3b.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/3292-5ddffdb2f517b44f.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/{9917-9e26abc5f7772735.js → 3396-97b97928c49190c9.js} +1 -1
  39. package/apps/pwa/out/_next/static/chunks/{6196.34d6b229079e9e28.js → 3403.7199205e65124486.js} +1 -1
  40. package/apps/pwa/out/_next/static/chunks/3523.d3231daaf7aba993.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/3763.577de3181e1e6ea0.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/4138-cc56b0938ef72783.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/419.736c0d6f426249a7.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/4255-8350c43d307a8875.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/{6def8a8e.c81f9ca41028eccf.js → 425cfc4d.fb91158e3971c39f.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/4352.a4954693499c2714.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/4421-f2dcbc6fd80ec013.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/4513-51b94aeee37c9905.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/{16e57f67.3ea19e893461e611.js → 46bd8bea.2f4b4099172fcdb7.js} +1 -1
  50. package/apps/pwa/out/_next/static/chunks/{5081.a240b63fad28663a.js → 4759.1f76713aefb87db5.js} +1 -1
  51. package/apps/pwa/out/_next/static/chunks/4928-c3b72f99aa588699.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/4986-f9fc0283fb325219.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/5014.671a7b9f69c2b7b1.js +94 -0
  54. package/apps/pwa/out/_next/static/chunks/5137.d398e24710b3feb6.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/5298.0bea74885cabe754.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/5315.74c53a39739b11c4.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/5317-8b1df8d02105cd07.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/5358-33051f41a2b239af.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/5388.a2ec02501f714ad5.js +62 -0
  60. package/apps/pwa/out/_next/static/chunks/5395.e6ac948a2d15058d.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/5409-f282bf533c151849.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/5466.d0f4aba9d52acfa8.js +27 -0
  63. package/apps/pwa/out/_next/static/chunks/5564.3d28b353470fe305.js +104 -0
  64. package/apps/pwa/out/_next/static/chunks/{9974.e77b4ff4884557b3.js → 5591.a35e7a217734a8b9.js} +2 -2
  65. package/apps/pwa/out/_next/static/chunks/569.b0cbfb4dee4a167a.js +93 -0
  66. package/apps/pwa/out/_next/static/chunks/5835-72465b73a8bb8d74.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/5941.045d6093139e1f7a.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/{5492-e05e26a033ba80d2.js → 6013-2f769370d8b4c2c6.js} +3 -3
  69. package/apps/pwa/out/_next/static/chunks/6228.47cabb83564dfe54.js +24 -0
  70. package/apps/pwa/out/_next/static/chunks/6457-8d1993002a87d0fe.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/6513.ef54cfae52351982.js +2 -0
  72. package/apps/pwa/out/_next/static/chunks/{5408.377c8c543635b81f.js → 6534.1ab00f370db7b105.js} +1 -1
  73. package/apps/pwa/out/_next/static/chunks/6582.13d245bf87df69f1.js +148 -0
  74. package/apps/pwa/out/_next/static/chunks/6655.c33f6acc2c61bb89.js +149 -0
  75. package/apps/pwa/out/_next/static/chunks/6731-9d7195af289c8d4c.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/6733.0be05b1db9279fc5.js +79 -0
  77. package/apps/pwa/out/_next/static/chunks/6742-90937146253a8b9e.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/{8578.dff22d988cc2b106.js → 6767.1fcfc9b8be83975f.js} +16 -16
  79. package/apps/pwa/out/_next/static/chunks/6776.06ff1e3b464cc607.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/{9311.24c8f5ea793253dd.js → 6853.cc5174a9dee59593.js} +1 -1
  81. package/apps/pwa/out/_next/static/chunks/687-816858f77a396a4e.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/6888.72c7b8cfb02a7e61.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/6993.c17c828ac3ef3253.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/7076.9f27ef666b0cefdd.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/7113.1403c5d451804e8c.js +287 -0
  86. package/apps/pwa/out/_next/static/chunks/7169.9dd603c726c2cd7c.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/7247-e3ec21f761f86710.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/7261-a47b72f96f5199df.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/7335-5cda81ac90310077.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/7356-c2e4af090546e2d4.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/{94.4cfed21c0b781a06.js → 7375.050ea0d72968e49f.js} +16 -16
  92. package/apps/pwa/out/_next/static/chunks/738-59518742078fda71.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/7385.a7375f10db73243a.js +77 -0
  94. package/apps/pwa/out/_next/static/chunks/7445.cf1cf2fe109bcd96.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/7505.a2057432ab6ef99a.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/7515.31f45fac1f9c70cb.js +74 -0
  97. package/apps/pwa/out/_next/static/chunks/7658-d4ce8c72a9548a91.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/7725-4f83d5541fbdb81b.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/8107-0b1ae46bc5bcb788.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/8132.bfea2f016ce8d992.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/8332.6f0f49df772e163b.js +23 -0
  102. package/apps/pwa/out/_next/static/chunks/{7483.ba01c6c209b81bba.js → 8398.73a19b39715eda04.js} +1 -1
  103. package/apps/pwa/out/_next/static/chunks/{2846.d6a433b8663819a5.js → 8471.8d2a8c74f09c36ff.js} +3 -3
  104. package/apps/pwa/out/_next/static/chunks/8672.e299832bc7d18ee2.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/8735.f1ef798a5f260f6f.js +226 -0
  106. package/apps/pwa/out/_next/static/chunks/{5785-ea8d2c633b6688e3.js → 8767-2f6a634f5abeaa01.js} +1 -1
  107. package/apps/pwa/out/_next/static/chunks/8774-88f67f7d8d53d299.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/8782.252e5a405d86e4ed.js +32 -0
  109. package/apps/pwa/out/_next/static/chunks/8795.32573a185af18c7b.js +41 -0
  110. package/apps/pwa/out/_next/static/chunks/909-9ed7aa0dba804316.js +1 -0
  111. package/apps/pwa/out/_next/static/chunks/9100.22b0301256827007.js +1 -0
  112. package/apps/pwa/out/_next/static/chunks/9140-dde77575570bc71e.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/{4593.10f5b9bd702d4a6c.js → 9143.ff14585db1d275ff.js} +1 -1
  114. package/apps/pwa/out/_next/static/chunks/9292-a7ad9a01c7044b93.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/9517.00627dc51b02821c.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/9725.dcbb585652d8e9c5.js +1 -0
  117. package/apps/pwa/out/_next/static/chunks/9761.a3ebf5c389c24ddc.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/{4204.a409ab6250c0f6a8.js → 9907.49be0fcadaf214b5.js} +1 -1
  119. package/apps/pwa/out/_next/static/chunks/9972.834d2b2b7d48f1ed.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/9995-1a90486f5ae01546.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/{973cbf1b.3bbcf2b41548eca7.js → a435ad95.577add051b4facb2.js} +1 -1
  122. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-83372d8b5863ebf7.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d69256d7613c140a.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-352c5e9ba3f9b858.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/board/page-69f1d725524201a8.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-fa46d4588e8442c2.js → page-a5635eba0d73b84e.js} +1 -1
  127. package/apps/pwa/out/_next/static/chunks/app/bots/page-ad9181d84285e4ea.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/ceo/{page-66ad46ed0279feb9.js → page-86feb08080261a2f.js} +1 -1
  129. package/apps/pwa/out/_next/static/chunks/app/chat/page-ffa15987e4756d03.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/consult/page-11a3ab44ac327cc1.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/control/page-6309b11c4264d24d.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/decisions/page-8800472079612e87.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/design-check/page-d387bf3f2301882a.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/editor/page-048f88d8c8658da6.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/exec/page-6c14f9739b7f6880.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/field/page-2834f44f64e98d74.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/healthz/route-83372d8b5863ebf7.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/inside/page-a13df6f88e824887.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/intake/page-8a4397248754cf2b.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/layout-daf0bd329360a336.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/live/page-f7ef1c28764cb476.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/live-run/page-61e74f28b9353dac.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/loops/{page-646b50f63b6fe732.js → page-8aaa6dedc4c10dfb.js} +1 -1
  144. package/apps/pwa/out/_next/static/chunks/app/market/page-294602ba649be829.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-5fa5f41ed3fce323.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/morning/{page-6f131b51193a36f9.js → page-33bf667a5fb297bf.js} +1 -1
  147. package/apps/pwa/out/_next/static/chunks/app/not-found-5ae3cbd6e5dba515.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/observatory/page-eb867d16ad6fdede.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1ab789fa68682f7e.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-6fd41b5b6ff13a71.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fb3d5f7642c99fba.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/outputs/page-70f705b2b5ed42ca.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/page-c9c93b358a7667b5.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/reflection/page-1563c556b81c96ed.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-ce89c20c45b3a673.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/sessions/page-423ae63d786f4ee9.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-591a5378a4c4447c.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/settings/page-a250496b19a5824b.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-c36a9277febc5911.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/setup/layout-83372d8b5863ebf7.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/page-29900ed0011b2c83.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/share/page-a5de57edc6d7f942.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/showroom/page-16bdd4c9f40498df.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/tasks/page-75e946e6e71cc6d8.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/term/page-9eb56166774eb256.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/today/page-1c5f9ef35dbb22c5.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/trace/page-657ff9985e508498.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/vault/page-22dc72499da68fb2.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-9f92737b8766009a.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/workflows/page-094ad334e40f8169.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workspace/page-8bcd379852ff6287.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-6b04413f280cd344.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/{f4dfbfc0.a69fb12f854882aa.js → bc324b7c.02a09dd93f6a399b.js} +1 -1
  174. package/apps/pwa/out/_next/static/chunks/de70ab42-d28d47437208d458.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/f800c5e3.46dbd7f2af8c3fce.js +1 -0
  176. package/apps/pwa/out/_next/static/chunks/f8bc19ad.47579f6d684b0760.js +122 -0
  177. package/apps/pwa/out/_next/static/chunks/framework-ca9ac4609fb9fed8.js +1 -0
  178. package/apps/pwa/out/_next/static/chunks/main-app-0dd08d32d15a030f.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-f0399d33955c4e01.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/pages/_app-d42caca6f8ac2211.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_error-42e713c13aa41ca8.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/webpack-8fbf8f61a377aa16.js +1 -0
  183. package/apps/pwa/out/approvals/index.html +1 -1
  184. package/apps/pwa/out/approvals/index.txt +22 -22
  185. package/apps/pwa/out/autopilot/index.html +1 -1
  186. package/apps/pwa/out/autopilot/index.txt +22 -22
  187. package/apps/pwa/out/board/index.html +1 -1
  188. package/apps/pwa/out/board/index.txt +22 -22
  189. package/apps/pwa/out/botlab/index.html +1 -1
  190. package/apps/pwa/out/botlab/index.txt +21 -21
  191. package/apps/pwa/out/bots/index.html +1 -1
  192. package/apps/pwa/out/bots/index.txt +21 -21
  193. package/apps/pwa/out/ceo/index.html +1 -1
  194. package/apps/pwa/out/ceo/index.txt +22 -22
  195. package/apps/pwa/out/chat/index.html +1 -1
  196. package/apps/pwa/out/chat/index.txt +21 -21
  197. package/apps/pwa/out/consult/index.html +1 -1
  198. package/apps/pwa/out/consult/index.txt +22 -22
  199. package/apps/pwa/out/control/index.html +1 -1
  200. package/apps/pwa/out/control/index.txt +22 -22
  201. package/apps/pwa/out/decisions/index.html +1 -1
  202. package/apps/pwa/out/decisions/index.txt +21 -21
  203. package/apps/pwa/out/design-check/index.html +1 -1
  204. package/apps/pwa/out/design-check/index.txt +22 -22
  205. package/apps/pwa/out/editor/index.html +1 -1
  206. package/apps/pwa/out/editor/index.txt +22 -22
  207. package/apps/pwa/out/exec/index.html +1 -1
  208. package/apps/pwa/out/exec/index.txt +22 -22
  209. package/apps/pwa/out/field/index.html +1 -1
  210. package/apps/pwa/out/field/index.txt +22 -22
  211. package/apps/pwa/out/healthz +1 -1
  212. package/apps/pwa/out/index.html +1 -1
  213. package/apps/pwa/out/index.txt +21 -21
  214. package/apps/pwa/out/inside/index.html +1 -1
  215. package/apps/pwa/out/inside/index.txt +21 -21
  216. package/apps/pwa/out/intake/index.html +1 -1
  217. package/apps/pwa/out/intake/index.txt +22 -22
  218. package/apps/pwa/out/live/index.html +1 -1
  219. package/apps/pwa/out/live/index.txt +22 -22
  220. package/apps/pwa/out/live-run/index.html +1 -1
  221. package/apps/pwa/out/live-run/index.txt +22 -22
  222. package/apps/pwa/out/loops/index.html +1 -1
  223. package/apps/pwa/out/loops/index.txt +21 -21
  224. package/apps/pwa/out/market/index.html +1 -1
  225. package/apps/pwa/out/market/index.txt +21 -21
  226. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  227. package/apps/pwa/out/missions/placeholder/index.txt +21 -21
  228. package/apps/pwa/out/morning/index.html +1 -1
  229. package/apps/pwa/out/morning/index.txt +22 -22
  230. package/apps/pwa/out/observatory/index.html +1 -1
  231. package/apps/pwa/out/observatory/index.txt +22 -22
  232. package/apps/pwa/out/ops/checklist/index.html +1 -1
  233. package/apps/pwa/out/ops/checklist/index.txt +21 -21
  234. package/apps/pwa/out/ops/release/index.html +1 -1
  235. package/apps/pwa/out/ops/release/index.txt +21 -21
  236. package/apps/pwa/out/ops/seats/index.html +1 -1
  237. package/apps/pwa/out/ops/seats/index.txt +21 -21
  238. package/apps/pwa/out/outputs/index.html +1 -1
  239. package/apps/pwa/out/outputs/index.txt +21 -21
  240. package/apps/pwa/out/reflection/index.html +1 -1
  241. package/apps/pwa/out/reflection/index.txt +22 -22
  242. package/apps/pwa/out/scheduler/index.html +1 -1
  243. package/apps/pwa/out/scheduler/index.txt +22 -22
  244. package/apps/pwa/out/sessions/index.html +1 -1
  245. package/apps/pwa/out/sessions/index.txt +21 -21
  246. package/apps/pwa/out/settings/devices/index.html +1 -1
  247. package/apps/pwa/out/settings/devices/index.txt +22 -22
  248. package/apps/pwa/out/settings/index.html +1 -1
  249. package/apps/pwa/out/settings/index.txt +22 -22
  250. package/apps/pwa/out/setup/done/index.html +1 -1
  251. package/apps/pwa/out/setup/done/index.txt +22 -22
  252. package/apps/pwa/out/setup/index.html +1 -1
  253. package/apps/pwa/out/setup/index.txt +22 -22
  254. package/apps/pwa/out/share/index.html +1 -1
  255. package/apps/pwa/out/share/index.txt +22 -22
  256. package/apps/pwa/out/showroom/index.html +1 -1
  257. package/apps/pwa/out/showroom/index.txt +21 -21
  258. package/apps/pwa/out/tasks/index.html +1 -1
  259. package/apps/pwa/out/tasks/index.txt +22 -22
  260. package/apps/pwa/out/term/index.html +1 -1
  261. package/apps/pwa/out/term/index.txt +22 -22
  262. package/apps/pwa/out/today/index.html +1 -1
  263. package/apps/pwa/out/today/index.txt +22 -22
  264. package/apps/pwa/out/trace/index.html +1 -1
  265. package/apps/pwa/out/trace/index.txt +22 -22
  266. package/apps/pwa/out/vault/index.html +1 -1
  267. package/apps/pwa/out/vault/index.txt +22 -22
  268. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  269. package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
  270. package/apps/pwa/out/workflows/index.html +1 -1
  271. package/apps/pwa/out/workflows/index.txt +22 -22
  272. package/apps/pwa/out/workspace/index.html +1 -1
  273. package/apps/pwa/out/workspace/index.txt +22 -22
  274. package/apps/pwa/out/worktrees/index.html +1 -1
  275. package/apps/pwa/out/worktrees/index.txt +22 -22
  276. package/graphs/release/release-loop.yaml +6 -2
  277. package/package.json +1 -1
  278. package/plugins/agent-team/agents/research.md +1 -0
  279. package/scripts/ci-typecheck-changed.ts +3 -2
  280. package/scripts/coord-channel-watch.sh +26 -6
  281. package/scripts/coord-role-pack.ts +143 -0
  282. package/scripts/lib/morning-report-options.ts +41 -0
  283. package/scripts/pr-land-extra-remote-gates.ts +49 -0
  284. package/scripts/release-loop/gate-batch-safe.ts +23 -0
  285. package/scripts/release-loop/gate-history.ts +422 -0
  286. package/scripts/release-loop/gate-node.ts +310 -46
  287. package/scripts/release-loop/gate-triage-node.ts +190 -0
  288. package/scripts/release-loop/spawn-lane.ts +68 -0
  289. package/scripts/release-loop/tui-node-checks.ts +23 -0
  290. package/scripts/release-loop/version-node.ts +62 -4
  291. package/scripts/test-deterministic.ts +41 -12
  292. package/scripts/usage-rollup.ts +11 -3
  293. package/src/acp/core-turn-bridge.ts +98 -22
  294. package/src/acp/elanous-extensions.ts +2 -0
  295. package/src/acp/server.ts +2 -0
  296. package/src/agent/ref-grounding.ts +18 -0
  297. package/src/agent-substrate/pr-reviewer.ts +29 -21
  298. package/src/budget/pty-usage-reemit.ts +7 -0
  299. package/src/budget/run-usage-rollup.ts +9 -0
  300. package/src/budget/usage-daily-store.ts +98 -0
  301. package/src/capture/tui-self-report.ts +22 -4
  302. package/src/chat/index.ts +31 -10
  303. package/src/chat/pickers/modal-runtime.ts +1 -0
  304. package/src/chat/pickers/modals.ts +3 -1
  305. package/src/chat/turn-typeahead.ts +123 -16
  306. package/src/cli/dashboard-name-flag.ts +48 -0
  307. package/src/cli/doctor-cli.ts +80 -0
  308. package/src/cli/log-event-names.ts +12 -0
  309. package/src/cli/msg-cli.ts +45 -2
  310. package/src/cli/pr-cli.ts +382 -105
  311. package/src/cli/pr-land-heavy-remote.ts +233 -0
  312. package/src/cli/pr-land-overlap-evidence.ts +104 -5
  313. package/src/cli/pty-takeover-cli.ts +125 -16
  314. package/src/cli/release-cli.ts +44 -2
  315. package/src/cli/remote-resolve.ts +2 -1
  316. package/src/cli/seat-cli.ts +26 -16
  317. package/src/cli/tasks-cli.ts +8 -2
  318. package/src/cli/usage-cli.ts +146 -12
  319. package/src/cli/usage-daily-cli.ts +84 -0
  320. package/src/context-bus/context-now.ts +82 -5
  321. package/src/core-turn/auth-rejected-notice.ts +41 -0
  322. package/src/core-turn/run-core-turn.ts +13 -2
  323. package/src/core-turn/run-goal-loop.ts +17 -1
  324. package/src/core-turn/short-question.ts +45 -12
  325. package/src/core-turn/types.ts +7 -0
  326. package/src/dashboard/acp-active-model.ts +40 -0
  327. package/src/dashboard/auto-compact-runtime.ts +2 -0
  328. package/src/dashboard/boot-banner.ts +80 -0
  329. package/src/dashboard/dashboard-status-lines.ts +11 -0
  330. package/src/dashboard/detail-viewer-runtime.ts +15 -2
  331. package/src/dashboard/event-loop-stall-monitor.ts +4 -0
  332. package/src/dashboard/goal-loop-thinking.ts +16 -0
  333. package/src/dashboard/hitl-boot.ts +9 -4
  334. package/src/dashboard/index.ts +228 -387
  335. package/src/dashboard/input/chat-main-entry-runtime.ts +2 -0
  336. package/src/dashboard/input/chat-main-log-actions.ts +4 -0
  337. package/src/dashboard/input/chat-main-log-command.ts +12 -1
  338. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +172 -3
  339. package/src/dashboard/input/chat-main-session.ts +6 -2
  340. package/src/dashboard/input/chat-main-slash-command.ts +7 -0
  341. package/src/dashboard/input/chat-main-turn.ts +2 -1
  342. package/src/dashboard/input/compact-prepass-recovery.ts +97 -0
  343. package/src/dashboard/input/dashboard-chord-actions.ts +10 -1
  344. package/src/dashboard/input/dashboard-chord-runtime.ts +2 -0
  345. package/src/dashboard/input/external-input-host.ts +45 -0
  346. package/src/dashboard/input/log-fold-binding.ts +18 -0
  347. package/src/dashboard/input/mouse-wiring.ts +17 -4
  348. package/src/dashboard/input/turn-steer-bridge.ts +94 -0
  349. package/src/dashboard/log-click-runtime.ts +5 -3
  350. package/src/dashboard/running-build-status.ts +74 -0
  351. package/src/dashboard/seat-boot-preamble.ts +113 -0
  352. package/src/dashboard/seat-local-instructions.ts +305 -0
  353. package/src/dashboard/slash-runtime/dashboard-handlers.ts +100 -28
  354. package/src/dashboard/slash-runtime/model-choices.ts +35 -0
  355. package/src/dashboard/term-slash-runtime.ts +15 -0
  356. package/src/dashboard/turn-preamble.ts +30 -2
  357. package/src/dashboard/turn-session-layers.ts +176 -0
  358. package/src/dashboard/turn-stream-formatter.ts +14 -6
  359. package/src/dashboard/turn-stream-presentation-applier.ts +8 -0
  360. package/src/domains/release-tool.ts +8 -9
  361. package/src/domains/surface-events.ts +4 -1
  362. package/src/domains/watch-default-scheduler.ts +8 -9
  363. package/src/fold-stack.ts +24 -1
  364. package/src/git-fs/dirty.ts +32 -4
  365. package/src/git-fs/index.ts +43 -1
  366. package/src/graph-runner/runner.ts +1 -0
  367. package/src/harness/harness-gc.ts +32 -5
  368. package/src/index.ts +14 -1
  369. package/src/input/external-input-queue.ts +219 -0
  370. package/src/interactive-terminal-modal.ts +13 -0
  371. package/src/llm/route-decision.ts +25 -0
  372. package/src/llm-stream-retry-display.ts +59 -0
  373. package/src/llm.ts +253 -55
  374. package/src/log-entry.ts +3 -3
  375. package/src/log-pane/search.ts +29 -4
  376. package/src/loops/agent-reply.ts +256 -0
  377. package/src/loops/budget.ts +37 -7
  378. package/src/loops/loop-cli.ts +3 -2
  379. package/src/loops/msg-address.ts +57 -0
  380. package/src/loops/msg-wake.ts +282 -0
  381. package/src/loops/orchestrator/inbox.ts +60 -3
  382. package/src/loops/orchestrator/tick.ts +24 -4
  383. package/src/loops/registry.ts +42 -4
  384. package/src/memory.ts +20 -5
  385. package/src/native-tool-catalog.ts +125 -0
  386. package/src/nexus/api/http-server.ts +5 -0
  387. package/src/nexus/api/ops-api.ts +2 -0
  388. package/src/nexus/api/project-autopsy.ts +127 -0
  389. package/src/nexus/api/terminals.ts +3 -0
  390. package/src/nexus/api/workflow-approvals.ts +2 -0
  391. package/src/nexus/onboarding/first-boot-wizard.ts +56 -2
  392. package/src/oauth/codex-account-store.ts +15 -3
  393. package/src/oauth/codex-billing-route.ts +31 -0
  394. package/src/ops/overview-collect.ts +19 -2
  395. package/src/panes/log-pane.ts +67 -8
  396. package/src/persona/presets.ts +44 -0
  397. package/src/plugins/maker/wizard-generate.ts +6 -4
  398. package/src/preview/terminal.ts +14 -1
  399. package/src/prompt-cache/types.ts +2 -0
  400. package/src/prompt-enhance/enhance.ts +24 -8
  401. package/src/prompt-library/universal-preamble.ts +273 -56
  402. package/src/pty-shell/pty-control-ipc.ts +4 -1
  403. package/src/pty-shell/pty-manifest.ts +77 -5
  404. package/src/pty-shell/registry.ts +19 -2
  405. package/src/pty-shell/tui-run-state.ts +253 -0
  406. package/src/release-loop/light-rc.ts +28 -3
  407. package/src/release-loop/rehearsal.ts +1 -1
  408. package/src/render.ts +5 -1
  409. package/src/seat-loop/seat-report.ts +42 -2
  410. package/src/self-dev/run-supervisor.ts +71 -5
  411. package/src/self-dev/shard-aggregate.ts +2 -0
  412. package/src/self-dev/shard-run.ts +23 -2
  413. package/src/self-health/signatures.ts +132 -0
  414. package/src/self-implement/doctor-visit.ts +118 -0
  415. package/src/self-implement/gate-cli.ts +11 -2
  416. package/src/self-implement/goal-author-decompose.ts +80 -16
  417. package/src/self-implement/goal-author.ts +114 -72
  418. package/src/self-implement/goal-authoring-grounding.ts +1 -1
  419. package/src/self-implement/goal-file-name.ts +65 -0
  420. package/src/self-implement/ground-goal.ts +2 -1
  421. package/src/self-implement/host-regate.ts +14 -0
  422. package/src/self-implement/review-mustfix-class.ts +115 -0
  423. package/src/self-implement/run-ledger.ts +111 -7
  424. package/src/self-implement/running-runs.ts +10 -6
  425. package/src/self-implement/self-implement-runtime.ts +32 -0
  426. package/src/session/pending-input.ts +62 -11
  427. package/src/session-runtime/index.ts +7 -1
  428. package/src/skills/tools/ops-ledger.ts +707 -0
  429. package/src/skills/tools/persistent-grounding.ts +47 -7
  430. package/src/skills/tools/shell.ts +117 -1
  431. package/src/status/bar.ts +32 -2
  432. package/src/status/status-provider-info.ts +58 -0
  433. package/src/task-agent/rejudge-on-head.ts +4 -1
  434. package/src/task-agent/remote-rejudge-bind.ts +34 -5
  435. package/src/task-agent/repair-cycle.ts +13 -0
  436. package/src/task-orchestrator/surfaces/kubectl-transient-retry.ts +51 -0
  437. package/src/task-orchestrator/surfaces/pod-command-job.ts +63 -7
  438. package/src/task-orchestrator/surfaces/pod-install-slots.ts +6 -11
  439. package/src/task-orchestrator/surfaces/pod-mirror-seed.ts +76 -0
  440. package/src/task-orchestrator/surfaces/self-implement-pod.ts +107 -33
  441. package/src/terminal/session-registry.ts +6 -0
  442. package/src/thinking-line.ts +12 -3
  443. package/src/tool-runtime/index.ts +4 -0
  444. package/src/tool-runtime/ops-ledger-runtimes.ts +84 -0
  445. package/src/tool-runtime/ref-consult-runtime.ts +17 -2
  446. package/src/tool-runtime/run-shell-runtime.ts +4 -1
  447. package/src/tool-runtime/self-cognition-runtimes.ts +5 -1
  448. package/src/tui.ts +5 -0
  449. package/src/user-config.ts +54 -6
  450. package/src/ux-sim/fake-llm.ts +29 -0
  451. package/src/ux-sim/in-process-cli.ts +175 -0
  452. package/src/version/packed-revision.json +1 -1
  453. package/apps/pwa/out/_next/static/chunks/1027.a0f04663d24c2779.js +0 -1
  454. package/apps/pwa/out/_next/static/chunks/1038.676007ae505822e7.js +0 -1
  455. package/apps/pwa/out/_next/static/chunks/1113.a37eadf7f740d955.js +0 -1
  456. package/apps/pwa/out/_next/static/chunks/112-fa7c526c20ce1405.js +0 -1
  457. package/apps/pwa/out/_next/static/chunks/1144.5fa1fa1d48dbcd12.js +0 -36
  458. package/apps/pwa/out/_next/static/chunks/1146.80d15ea7c6e6ee95.js +0 -1
  459. package/apps/pwa/out/_next/static/chunks/1157.f08bf2b70915e176.js +0 -148
  460. package/apps/pwa/out/_next/static/chunks/119b405e.8fdd0e44d1ff37aa.js +0 -1
  461. package/apps/pwa/out/_next/static/chunks/1296.04560d98e73fbf51.js +0 -1
  462. package/apps/pwa/out/_next/static/chunks/1314-f604ba013ec535b0.js +0 -1
  463. package/apps/pwa/out/_next/static/chunks/1454-29d1d773d5996a49.js +0 -1
  464. package/apps/pwa/out/_next/static/chunks/1464.f3209af0d8ae4ff2.js +0 -1
  465. package/apps/pwa/out/_next/static/chunks/1729-d73dd25e564679d4.js +0 -1
  466. package/apps/pwa/out/_next/static/chunks/1843.5924c24295243261.js +0 -27
  467. package/apps/pwa/out/_next/static/chunks/1987.e28149724b92918b.js +0 -1
  468. package/apps/pwa/out/_next/static/chunks/2066-5ca18ffa5a4be68b.js +0 -1
  469. package/apps/pwa/out/_next/static/chunks/2240.d10a66695155fd93.js +0 -1
  470. package/apps/pwa/out/_next/static/chunks/2391.e2cbc98d4abbb41a.js +0 -1
  471. package/apps/pwa/out/_next/static/chunks/2528.3868070ef1be69f9.js +0 -24
  472. package/apps/pwa/out/_next/static/chunks/2566-734cb83bbd405886.js +0 -1
  473. package/apps/pwa/out/_next/static/chunks/2697.76ca2ac3efef6f3b.js +0 -1
  474. package/apps/pwa/out/_next/static/chunks/275.0e6bad414729bfa0.js +0 -32
  475. package/apps/pwa/out/_next/static/chunks/2803-33bdae771855a8d1.js +0 -1
  476. package/apps/pwa/out/_next/static/chunks/3143.257997a5f1ed79df.js +0 -34
  477. package/apps/pwa/out/_next/static/chunks/3259-86f3be56e43eaf6a.js +0 -1
  478. package/apps/pwa/out/_next/static/chunks/3280-e177c33bfbdcfb0a.js +0 -1
  479. package/apps/pwa/out/_next/static/chunks/3294-2b6a6d45a46a6c1c.js +0 -1
  480. package/apps/pwa/out/_next/static/chunks/3309.364573ceedbdfd5f.js +0 -1
  481. package/apps/pwa/out/_next/static/chunks/3522.758840fc5e7fe630.js +0 -287
  482. package/apps/pwa/out/_next/static/chunks/3525-a7c989c1a661e880.js +0 -1
  483. package/apps/pwa/out/_next/static/chunks/3694.b1a66387756458c0.js +0 -1
  484. package/apps/pwa/out/_next/static/chunks/370.124f9e678a76fc01.js +0 -1
  485. package/apps/pwa/out/_next/static/chunks/3797-9643105335e660fe.js +0 -1
  486. package/apps/pwa/out/_next/static/chunks/4084.15ecaf4240bd9e77.js +0 -286
  487. package/apps/pwa/out/_next/static/chunks/4089-b3382b8843114eee.js +0 -1
  488. package/apps/pwa/out/_next/static/chunks/4293.928a8d573c16aab7.js +0 -1
  489. package/apps/pwa/out/_next/static/chunks/4402.f9238a3b18a7bd43.js +0 -1
  490. package/apps/pwa/out/_next/static/chunks/4490-9b090b0f9427f902.js +0 -1
  491. package/apps/pwa/out/_next/static/chunks/4548.13502d0ca3b4f862.js +0 -8
  492. package/apps/pwa/out/_next/static/chunks/4599-548bc96cda44a5a7.js +0 -1
  493. package/apps/pwa/out/_next/static/chunks/4625.206574e33a9e7ae8.js +0 -104
  494. package/apps/pwa/out/_next/static/chunks/4862-392c9a71f0d8dfb4.js +0 -1
  495. package/apps/pwa/out/_next/static/chunks/5054.2906d1b02542edc5.js +0 -149
  496. package/apps/pwa/out/_next/static/chunks/5102-c822bbbee5a022a4.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/5194-8c93f2e7e4b6f717.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/5305-bacd7eca0b9a5848.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/5311.9071ee3f44fd7ff5.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/5369.9f34353442daa421.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/589-49d730cb8138b397.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/5904-264a6412ae3c9028.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/5980.728531d5c566535c.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/599-56f2c0a5cc4a7295.js +0 -1
  505. package/apps/pwa/out/_next/static/chunks/6011.f525002e21bef0ac.js +0 -23
  506. package/apps/pwa/out/_next/static/chunks/6132.4381ab93683f1acf.js +0 -1
  507. package/apps/pwa/out/_next/static/chunks/6149.2e698635e454d8f6.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/6356-0afafc4230cf9767.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/6406.64756073f7df35b8.js +0 -77
  510. package/apps/pwa/out/_next/static/chunks/6479-16e62aedafa0e310.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/6599.b5246b46a2453329.js +0 -64
  512. package/apps/pwa/out/_next/static/chunks/6610.5c26eec011565acb.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/666.37d18474edc1c552.js +0 -2
  514. package/apps/pwa/out/_next/static/chunks/6904.a0b5b1ed4f2d2c20.js +0 -1
  515. package/apps/pwa/out/_next/static/chunks/7247-1f4aba94aba50d9f.js +0 -1
  516. package/apps/pwa/out/_next/static/chunks/7274-e71a7c5b7760cd02.js +0 -1
  517. package/apps/pwa/out/_next/static/chunks/7290.1fcaeef3adb5d0d4.js +0 -1
  518. package/apps/pwa/out/_next/static/chunks/7477-2c64e42760f82f33.js +0 -1
  519. package/apps/pwa/out/_next/static/chunks/7505-47002b76ff1aef6c.js +0 -1
  520. package/apps/pwa/out/_next/static/chunks/7600-11884ca4da68e5d9.js +0 -1
  521. package/apps/pwa/out/_next/static/chunks/7607.40d8bb3689697ccb.js +0 -62
  522. package/apps/pwa/out/_next/static/chunks/7752.5cbdd0b433c54af9.js +0 -1
  523. package/apps/pwa/out/_next/static/chunks/7763.f51fcc19ca1414ec.js +0 -1
  524. package/apps/pwa/out/_next/static/chunks/7785.846140c859b1adf6.js +0 -1
  525. package/apps/pwa/out/_next/static/chunks/7807-a0fdac6393e0bf36.js +0 -1
  526. package/apps/pwa/out/_next/static/chunks/7837-20a63b4b7e9342b1.js +0 -1
  527. package/apps/pwa/out/_next/static/chunks/800.7815414cf280545d.js +0 -1
  528. package/apps/pwa/out/_next/static/chunks/8085-1e5fce59500a2cc2.js +0 -1
  529. package/apps/pwa/out/_next/static/chunks/8100.415f2105faa63e97.js +0 -1
  530. package/apps/pwa/out/_next/static/chunks/8290.701aa42ed13aa767.js +0 -226
  531. package/apps/pwa/out/_next/static/chunks/83a80514-b897b64df43ce108.js +0 -1
  532. package/apps/pwa/out/_next/static/chunks/8444.d9c74214a3145a43.js +0 -93
  533. package/apps/pwa/out/_next/static/chunks/8569.98c915534928c7f9.js +0 -24
  534. package/apps/pwa/out/_next/static/chunks/8591.b1c7f876e5ec7cfb.js +0 -1
  535. package/apps/pwa/out/_next/static/chunks/8715-f63756021d3ab989.js +0 -1
  536. package/apps/pwa/out/_next/static/chunks/8879.776c2ec7732aa114.js +0 -115
  537. package/apps/pwa/out/_next/static/chunks/8881.ddca15b3c82c490c.js +0 -1
  538. package/apps/pwa/out/_next/static/chunks/8946.5da5c87f32a2e1a8.js +0 -41
  539. package/apps/pwa/out/_next/static/chunks/9117.080b9b75be90e14d.js +0 -74
  540. package/apps/pwa/out/_next/static/chunks/9167.f3ecb28f367d6e69.js +0 -94
  541. package/apps/pwa/out/_next/static/chunks/9224.66095370ee28c93b.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/9238-0682b013c83d6449.js +0 -1
  543. package/apps/pwa/out/_next/static/chunks/9449.d3ed795da4befb1c.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/949.1e7d45ccb71580aa.js +0 -90
  545. package/apps/pwa/out/_next/static/chunks/9638-d268329f2c6f4f8d.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/9842-d6644eb53d3dc6bf.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/9863.94ca3263e760696e.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/9938.b23e4d4dfa0114cd.js +0 -79
  549. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d9babd8795f396a5.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/app/approvals/page-5ab1e47978577c5e.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-24453f6a302816b4.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/app/board/page-6c131d30bb1cf871.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/app/bots/page-dc0772b11861af51.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/app/chat/page-5159dc8752c6cd29.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/app/consult/page-c5a2478861c08b87.js +0 -1
  556. package/apps/pwa/out/_next/static/chunks/app/control/page-f722a9dec084e08b.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/app/decisions/page-256ffd365d32e100.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/app/design-check/page-e0de78ae7b99f01c.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/app/editor/page-cfbdc44b708d9801.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/app/exec/page-4c741eb760f2f77c.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/app/field/page-e9ba98f5f1440a21.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/app/healthz/route-d9babd8795f396a5.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/app/inside/page-dafc901bb1b72b5f.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/app/intake/page-2f6f303fa114ec24.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/app/layout-1634081c5272006c.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/app/live/page-ced0bcfc6542b3fc.js +0 -1
  567. package/apps/pwa/out/_next/static/chunks/app/live-run/page-bb1f05cd51c38944.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/app/market/page-db069b48a59c8c00.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-f736324afa2117c1.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/app/not-found-435f5e2faf02bc01.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/app/observatory/page-497e0eedf20e7890.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-a8d28fdf995e16ea.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8966452228753c5e.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fd5d18656cb432b6.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/app/outputs/page-5663779cb1a37609.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/app/page-66a67eb19ebd976b.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/app/reflection/page-3b492aa78cf3c4a6.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-c71267e7681da982.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/app/sessions/page-a21d1e1797172692.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-cb249e81ed339318.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/app/settings/page-781cfc5114e097da.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-478157214db6a023.js +0 -1
  583. package/apps/pwa/out/_next/static/chunks/app/setup/layout-d9babd8795f396a5.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/app/setup/page-86141f12cf090b7f.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/app/share/page-7256c73bb1c36751.js +0 -1
  586. package/apps/pwa/out/_next/static/chunks/app/showroom/page-5c738ca7adef3de5.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/app/tasks/page-0138c8091ec17224.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/app/term/page-cc2f24f16d85e735.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/app/today/page-979d318a534e92da.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/app/trace/page-182d394f7e6e096b.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/app/vault/page-21050928a697ed7a.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-44b9d807005a9030.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/app/workflows/page-601fb77340e63c9a.js +0 -1
  594. package/apps/pwa/out/_next/static/chunks/app/workspace/page-75d3fb5d47af0edf.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-11bf9e5725a72c22.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/dcf24383-2f82e35f2d827393.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/e117f801.e92bf10242653497.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/e87d89b9.2e71c0ae475d28a1.js +0 -122
  599. package/apps/pwa/out/_next/static/chunks/framework-bf135b997155e90e.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/main-5571537543bea91c.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/main-app-25c51c7df7f3a442.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/pages/_app-e3b2c1f5d82abd65.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/pages/_error-083f2e23e9bc3641.js +0 -1
  604. package/apps/pwa/out/_next/static/chunks/webpack-e94d22b7a028217b.js +0 -1
  605. package/src/dashboard/bench-slash-runtime.ts +0 -40
  606. package/src/dashboard/input/search-modal-router.ts +0 -9
  607. package/src/dashboard/modals/pane-key-router.ts +0 -27
  608. package/src/dashboard/view-slash-runtime.ts +0 -48
  609. /package/apps/pwa/out/_next/static/{y1ltt7oCZxVpC6zVPhZ2O → cIpv5nU9AycwYWZI9H589}/_ssgManifest.js +0 -0
package/src/cli/pr-cli.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
1
+ import { existsSync, realpathSync, readdirSync, readFileSync, statSync } from 'node:fs';
2
2
  import { basename, dirname, join, relative, resolve } from 'node:path';
3
+ import { loadavg } from 'node:os';
3
4
  import { createInterface } from 'node:readline';
4
5
  import type { Command } from 'commander';
5
6
  import {
@@ -59,6 +60,7 @@ import { landingFreezeMessage } from '../release-loop/landing-freeze.js';
59
60
  import { admitLandingMerge } from '../self-implement/frozen-merges.js';
60
61
  import { verifyAncestorAutoPass, verifyOverlapEvidence } from './pr-land-overlap-evidence.js';
61
62
  import { observePrLandRefusal } from './pr-land-refusal.js';
63
+ import { ensurePrLandHeavyRemote, type HeavyRemoteInput, type HeavyRemoteResult } from './pr-land-heavy-remote.js';
62
64
  import { taskAgentStatePath } from '../task-agent/task-hand.js';
63
65
  import { PR_LABEL_NAMES } from '../github/pr-labels.js';
64
66
  import { enqueueL8ShadowQueue, runL8ShadowQueue, shadowPrNumber, statusL8ShadowQueue, type L8ShadowQueueDeps } from '../self-implement/l8-merge-queue.js';
@@ -89,6 +91,8 @@ export interface PrLandOpts {
89
91
  allowPublicLeak?: boolean;
90
92
  /** Pinned landing only: JSON evidence (rebased · re-gated · reviewed head) that replaces the unavailable non-interactive overlap approval. */
91
93
  overlapEvidence?: string;
94
+ /** Pinned landing opt-in: run the heavy changed-file gate on node-b at the reviewed head and current main tip. */
95
+ heavyRemote?: boolean;
92
96
  }
93
97
 
94
98
  export interface PrGranularityOpts {
@@ -126,6 +130,8 @@ export interface PrLandDeps {
126
130
  runPublicLeakGate?: (changedFiles: readonly string[], out: { log: (message: string) => void; error: (message: string) => void }) => number;
127
131
  /** Test seam — the post-transform export leak check on changed files. */
128
132
  runExportLeakCheck?: (changedFiles: readonly string[], cwd: string) => ExportLeakCheck;
133
+ /** Test seam — local 1-minute and 5-minute load around a heavy remote gate. Default `os.loadavg`. */
134
+ loadavg?: () => readonly [number, number, number];
129
135
  /** Test seam — included files must not import a module the public export excludes. */
130
136
  runExportImportCheck?: (changedFiles: readonly string[], cwd: string) => ExportImportCheck | Promise<ExportImportCheck>;
131
137
  /** 공개 문서의 `elanous …` 호출 ↔ 실제 `--help` 대조(경고 전용) — 바뀐 공개 문서 경로를 받아 어긋남 목록을 돌려준다. */
@@ -139,6 +145,7 @@ export interface PrLandDeps {
139
145
  /** iOS 시험 게이트 심(시험 주입용). */
140
146
  runIosGate?: (out: { log: (message: string) => void; error: (message: string) => void }, changedFiles: readonly string[]) => boolean;
141
147
  runPwaGate?: (out: { log: (message: string) => void; error: (message: string) => void }, changedFiles: readonly string[]) => boolean;
148
+ runHeavyRemote?: (input: HeavyRemoteInput) => HeavyRemoteResult;
142
149
  isInteractive?: () => boolean;
143
150
  /** Clock for the recent-landings window (test seam · default Date.now). */
144
151
  now?: () => number;
@@ -1146,9 +1153,9 @@ const GITHUB_MERGEABLE = new Set(['MERGEABLE', 'CONFLICTING', 'UNKNOWN']);
1146
1153
  const GITHUB_MERGE_STATE = new Set(['BEHIND', 'BLOCKED', 'CLEAN', 'DIRTY', 'DRAFT', 'HAS_HOOKS', 'UNKNOWN', 'UNSTABLE']);
1147
1154
 
1148
1155
  /** Read-only identity check shared by pinned landing and its dry-run. Later landing gates still run only on a real landing. */
1149
- function inspectPinnedPr(run: CmdRunner, cwd: string, pr: number, expectedHead: string) {
1150
- const viewed = run('gh', ['pr', 'view', String(pr), '--json', 'headRefOid,headRefName,state,isDraft,url'], { cwd });
1151
- let value: { headRefOid?: string; headRefName?: string; state?: string; isDraft?: boolean; url?: string } = {};
1156
+ function inspectPinnedPr(run: CmdRunner, cwd: string, pr: number, expectedHead: string, requireMainBase = false) {
1157
+ const viewed = run('gh', ['pr', 'view', String(pr), '--json', 'headRefOid,headRefName,baseRefName,state,isDraft,url'], { cwd });
1158
+ let value: { headRefOid?: string; headRefName?: string; baseRefName?: string; state?: string; isDraft?: boolean; url?: string } = {};
1152
1159
  try {
1153
1160
  if (viewed.ok) {
1154
1161
  const parsed: unknown = JSON.parse(viewed.out);
@@ -1162,13 +1169,24 @@ function inspectPinnedPr(run: CmdRunner, cwd: string, pr: number, expectedHead:
1162
1169
  if (value.isDraft !== false) reasons.push(value.isDraft === true ? 'PR is draft' : 'PR ready status is unavailable');
1163
1170
  if (value.headRefOid?.toLowerCase() !== expectedHead.toLowerCase()) reasons.push('PR head differs from --expected-head or is unavailable');
1164
1171
  if (!value.url?.endsWith(`/pull/${pr}`) || !value.headRefName) reasons.push('PR identity or head branch is unavailable');
1172
+ // Heavy-remote evidence is an exact head/main pair. A retarget, or an unreadable base, is not that pair.
1173
+ if (requireMainBase && value.baseRefName !== 'main') reasons.push(value.baseRefName ? `PR base is ${value.baseRefName}, not main` : 'PR base is unavailable');
1165
1174
  if (!local.ok || local.out.trim().toLowerCase() !== expectedHead.toLowerCase()) reasons.push('local HEAD differs from --expected-head or is unavailable');
1166
1175
  if (!dirty.ok || dirty.out.trim()) reasons.push(dirty.ok ? 'local worktree is dirty' : 'local worktree status is unavailable');
1167
- return { ok: reasons.length === 0, reasons, branch: value.headRefName, url: value.url };
1176
+ return { ok: reasons.length === 0, reasons, branch: value.headRefName, url: value.url, baseRefName: value.baseRefName };
1168
1177
  }
1169
1178
 
1170
1179
  export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): Promise<number> {
1171
1180
  const out = deps.out ?? liveOut;
1181
+ const landStartedAt = Date.now();
1182
+ const stage = (name: string, startedAt: number, ok: boolean, extra: Record<string, unknown> = {}): void => {
1183
+ if (!opts.heavyRemote) return;
1184
+ debug.log('pr.land', 'stage-timing', {
1185
+ pr: Number(opts.pr), stage: name, startedAt: new Date(startedAt).toISOString(),
1186
+ endedAt: new Date().toISOString(), elapsedMs: Math.max(0, Date.now() - startedAt),
1187
+ totalElapsedMs: Math.max(0, Date.now() - landStartedAt), ok, ...extra,
1188
+ });
1189
+ };
1172
1190
  // First slice: observe pinned identity/branch, overlap decision, and GitHub merge refusals.
1173
1191
  // Option syntax and prerequisite gate errors do not describe a merge recovery edge.
1174
1192
  const refuse = (message: string, context: { isDraft?: boolean } = {}): void => {
@@ -1195,6 +1213,10 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
1195
1213
  out.error('✗ active-run-files: --include-active-run-files와 --exclude-active-run-files는 함께 사용할 수 없습니다.');
1196
1214
  return 1;
1197
1215
  }
1216
+ if (opts.heavyRemote && (opts.pr === undefined || opts.expectedHead === undefined || opts.hold)) {
1217
+ out.error('✗ heavy-remote: --pr ⊕ --expected-head 머리 고정 착지에서만 쓴다.');
1218
+ return 1;
1219
+ }
1198
1220
 
1199
1221
  const readFile = deps.readFile ?? ((path: string) => readFileSync(path, 'utf8'));
1200
1222
  let title = opts.title;
@@ -1315,9 +1337,12 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
1315
1337
  out.error('✗ expected-head: --pr and --expected-head (full SHA) are required together without --hold');
1316
1338
  return 1;
1317
1339
  }
1318
- const inspected = inspectPinnedPr(run, cwd, pr, opts.expectedHead!);
1340
+ const inspected = inspectPinnedPr(run, cwd, pr, opts.expectedHead!, opts.heavyRemote === true);
1319
1341
  if (!inspected.ok) {
1320
- refuse('✗ expected-head: PR OPEN non-draft, matching local HEAD and clean worktree required', {
1342
+ const baseReason = inspected.reasons.find((reason) => reason.startsWith('PR base'));
1343
+ refuse(baseReason
1344
+ ? `✗ heavy-remote: ${baseReason} — 재타기팅된 PR은 통과한 head/main 쌍이 아니다. 병합하지 않는다.`
1345
+ : '✗ expected-head: PR OPEN non-draft, matching local HEAD and clean worktree required', {
1321
1346
  isDraft: inspected.reasons.includes('PR is draft'),
1322
1347
  });
1323
1348
  return 1;
@@ -1542,7 +1567,238 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
1542
1567
  }
1543
1568
  }
1544
1569
 
1545
- const typecheckOk = (deps.runTypecheckGate ?? ((o) => runPrLandTypecheckGate(o, cwd)))(out);
1570
+ const currentMain = (): string | undefined => {
1571
+ // The landing pair names the remote's current tip, not a possibly stale tracking ref.
1572
+ const fetched = run('git', ['fetch', '--quiet', 'origin', 'main'], { cwd });
1573
+ if (!fetched.ok) return undefined;
1574
+ const tip = run('git', ['rev-parse', '--verify', 'origin/main'], { cwd });
1575
+ return tip.ok && /^[a-f0-9]{40}$/i.test(tip.out.trim()) ? tip.out.trim() : undefined;
1576
+ };
1577
+ const currentPinnedBase = (): string | undefined => {
1578
+ if (opts.heavyRemote) return currentMain();
1579
+ const ref = resolveRemoteBranchRef(run, cwd, base);
1580
+ const fetched = run('git', ['fetch', '--quiet', ref.remote, ref.branch], { cwd });
1581
+ if (!fetched.ok) return undefined;
1582
+ const tip = run('git', ['rev-parse', '--verify', `${ref.remote}/${ref.branch}`], { cwd });
1583
+ return tip.ok && /^[a-f0-9]{40}$/i.test(tip.out.trim()) ? tip.out.trim() : undefined;
1584
+ };
1585
+ let overlapCheckedBase: string | undefined;
1586
+ if (pinned) {
1587
+ overlapCheckedBase = currentPinnedBase();
1588
+ if (!overlapCheckedBase) {
1589
+ out.error(`✗ overlap-preflight: ${base} 최신 SHA를 가져오지 못했다. 겹침 조상을 판정하지 않고 착지하지 않는다.`);
1590
+ return 1;
1591
+ }
1592
+ }
1593
+ // Pinned landings reject a known non-ancestor before local or remote heavy gates.
1594
+ // Unpinned landings keep their existing gate and advisory order.
1595
+ let changedPaths = pinned ? currentChangePaths(run, cwd, staged, base, out) : [];
1596
+ let hold = !!opts.hold;
1597
+ const checkOverlap = async (): Promise<number | undefined> => {
1598
+ const overlappingLandings: string[] = [];
1599
+ const overlapFound = emitLandingOverlapAdvisory(run, cwd, base, changedPaths, out, overlappingLandings);
1600
+ const siblingFound = emitSiblingPrAdvisory(
1601
+ branch,
1602
+ deps.listOpenPrs ?? (() => lookupOpenPrs(run, cwd)),
1603
+ out,
1604
+ );
1605
+ const advisoryShown = recentAdvisoryShown || overlapFound || siblingFound;
1606
+ const reason = opts.landReason;
1607
+ const landReasonNotice = formatLandReasonNotice({ reason, advisoryShown });
1608
+ if (landReasonNotice) out.log(landReasonNotice);
1609
+ record('land-reason', true, {
1610
+ advisoryShown,
1611
+ hasReason: !!reason,
1612
+ reasonLength: reason ? codePointLength(reason) : 0,
1613
+ reasonLengthUnit: 'codepoint',
1614
+ });
1615
+ let overlapDecision: OverlapLandingDecision | undefined;
1616
+ if (overlapFound && !opts.hold && !opts.dryRun) {
1617
+ overlapDecision = await (deps.decideOverlapLanding ?? (() => decideOverlapLanding({
1618
+ isInteractive: deps.isInteractive,
1619
+ requestConfirmation: deps.requestConfirmation,
1620
+ channels: deps.overlapConfirmChannels,
1621
+ timeoutMs: deps.overlapConfirmTimeoutMs,
1622
+ })))();
1623
+ record('overlap-decision', true, {
1624
+ outcome: overlapDecision.outcome,
1625
+ channel: overlapDecision.channel,
1626
+ interactive: overlapDecision.interactive,
1627
+ });
1628
+ }
1629
+ hold = !!opts.hold || overlapDecision?.outcome === 'user-hold';
1630
+ // TA-LAND-OVERLAP — 비대화형 «승인 불가»만 증거로 대신한다(사람의 hold 는 증거로 뒤집지 않는다) · 증거가 없거나 어긋나면 종전 거부 그대로.
1631
+ let overlapEvidenceAccepted = false;
1632
+ let ancestorResyncNeeded = false;
1633
+ if (pinned && overlapDecision?.outcome === 'unavailable' && opts.overlapEvidence !== undefined) {
1634
+ const ref = resolveRemoteBranchRef(run, cwd, base);
1635
+ const verdict = verifyOverlapEvidence({
1636
+ evidencePath: opts.overlapEvidence, pr: Number(opts.pr), expectedHead: opts.expectedHead!,
1637
+ remote: ref.remote, branch: ref.branch, cwd, run, readFile,
1638
+ reviewResultsDir: deps.reviewResultsDir ?? join(dirname(taskAgentStatePath()), 'review-results'),
1639
+ listDir: (dir) => readdirSync(dir), reviewMtime: (path) => statSync(path).mtimeMs,
1640
+ reviewRealPath: (path) => realpathSync(path), overlappingLandings,
1641
+ });
1642
+ overlapEvidenceAccepted = verdict.ok;
1643
+ if (verdict.ok) {
1644
+ out.log(verdict.mode === 'already-ancestor'
1645
+ ? `✓ overlap-evidence: already-ancestor — 겹친 최근 착지 ${overlappingLandings.length}개가 전부 head ${opts.expectedHead!.slice(0, 12)} 의 조상 · review pass must-fix 0 (${verdict.reviewResult})`
1646
+ : verdict.mode === 'base-advanced-disjoint'
1647
+ ? `✓ overlap-evidence: base-advanced-disjoint — gate base ${verdict.gateBase.slice(0, 12)} ⊂ head ${opts.expectedHead!.slice(0, 12)} · ${ref.remote}/${ref.branch} ${verdict.baseTip.slice(0, 12)} 까지 ${verdict.windowCommits}개 커밋이 PR 파일을 안 건드린다 · review pass|warn must-fix 0 (${verdict.reviewResult})`
1648
+ : `✓ overlap-evidence: ${ref.remote}/${ref.branch} ${verdict.baseTip.slice(0, 12)} ⊂ head ${opts.expectedHead!.slice(0, 12)} · gate passed on that base · review pass must-fix 0 (${verdict.reviewResult})`);
1649
+ }
1650
+ else refuse(`✗ overlap-evidence: ${verdict.reason}`);
1651
+ } else if (pinned && overlapDecision?.outcome === 'unavailable') {
1652
+ // 증거 파일이 없을 때 — 겹친 착지가 전부 머리의 조상이고 표준 자리의 리뷰가 pass|warn ⊕ must-fix 0 이면 통과(아니면 종전 거부).
1653
+ const verdict = verifyAncestorAutoPass({
1654
+ pr: Number(opts.pr), expectedHead: opts.expectedHead!, cwd, run, readFile, overlappingLandings,
1655
+ reviewResultsDir: deps.reviewResultsDir ?? join(dirname(taskAgentStatePath()), 'review-results'),
1656
+ listDir: (dir) => readdirSync(dir),
1657
+ });
1658
+ overlapEvidenceAccepted = verdict.ok;
1659
+ if (verdict.ok) {
1660
+ out.log(`✓ overlap-evidence: already-ancestor-auto — 겹친 최근 착지 ${overlappingLandings.length}개가 전부 head ${opts.expectedHead!.slice(0, 12)} 의 조상 · review pass|warn must-fix 0 (${verdict.reviewResult})`);
1661
+ }
1662
+ else {
1663
+ refuse(`✗ ancestor-auto: ${verdict.reason}`);
1664
+ ancestorResyncNeeded = verdict.reason.startsWith('ancestor-auto-not-ancestor:');
1665
+ }
1666
+ }
1667
+ if (pinned && overlapDecision && overlapDecision.outcome !== 'user-merge' && !overlapEvidenceAccepted) {
1668
+ // A clean GitHub PR can still be behind a recently landed overlapping head. Preserve the cue
1669
+ // across hosts; the current steward tick acts only if it can also prove local run ownership.
1670
+ if (ancestorResyncNeeded && !opts.dryRun) {
1671
+ const marked = run('gh', ['pr', 'edit', String(opts.pr), '--add-label', PR_LABEL_NAMES.needsRebase], { cwd });
1672
+ record('resync-marker', marked.ok, { pr: Number(opts.pr), reason: 'ancestor-auto-not-ancestor' });
1673
+ if (!marked.ok) out.error('⚠ resync-marker: needs-rebase 라벨을 남기지 못했습니다');
1674
+ }
1675
+ out.error('✗ expected-head: overlap merge approval unavailable for the reviewed head');
1676
+ return 1;
1677
+ }
1678
+ return undefined;
1679
+ };
1680
+ if (pinned && (await checkOverlap()) !== undefined) return 1;
1681
+
1682
+
1683
+ const readLoad = deps.loadavg ?? loadavg;
1684
+ // gh pr merge pins only the PR head. A long gate — and the UNKNOWN merge wait — can
1685
+ // outlive the base it measured, so re-read origin/main after every gate and rerun until
1686
+ // the pair is stable. The bound fails closed; it is not an atomic base pin GitHub does not expose.
1687
+ const HEAVY_BASE_STABILITY_RETRIES = 3;
1688
+ let heavyInterferenceMeasured: boolean | null = null;
1689
+ let heavyGateHost: string | undefined;
1690
+ const heavyGate = (main: string, phase: 'initial' | 'late-recheck' | 'post-merge'): HeavyRemoteResult | undefined => {
1691
+ const before = readLoad();
1692
+ const started = Date.now();
1693
+ const verdict = (deps.runHeavyRemote ?? ensurePrLandHeavyRemote)({
1694
+ pr: Number(opts.pr), head: opts.expectedHead!, main, repo: cwd,
1695
+ });
1696
+ if (verdict.ok) {
1697
+ heavyGateHost = verdict.host;
1698
+ heavyInterferenceMeasured = verdict.interferenceMeasured ?? null;
1699
+ if (heavyInterferenceMeasured === false) out.error(`⚠ test-interference-gate: ${verdict.host ?? 'remote'} 검사 미측정 — 경고 전용이므로 착지는 계속한다.`);
1700
+ if (heavyInterferenceMeasured === null) out.error(`⚠ test-interference-gate: ${verdict.host ?? 'remote'} 검사 상태 미상 — 경고 전용이므로 착지는 계속한다.`);
1701
+ }
1702
+ const after = readLoad();
1703
+ // One paired observation per gate, including a late rerun after main moved.
1704
+ // These are the local mbp loads around the remote call, not a load sample from the selected host.
1705
+ debug.log('pr.land', 'heavy-remote-load', {
1706
+ pr: Number(opts.pr), head: opts.expectedHead, main, host: verdict.host ?? null, attemptedHost: verdict.attemptedHost ?? null, phase,
1707
+ elapsedMs: Math.max(0, Date.now() - started), gateElapsedMs: verdict.elapsedMs,
1708
+ mbpLoad1Before: before[0], mbpLoad5Before: before[1],
1709
+ mbpLoad1After: after[0], mbpLoad5After: after[1],
1710
+ source: verdict.source, ok: verdict.ok,
1711
+ });
1712
+ record('heavy-remote', verdict.ok, {
1713
+ host: verdict.host ?? null, attemptedHost: verdict.attemptedHost ?? null, head: opts.expectedHead, main, phase, source: verdict.source,
1714
+ elapsedMs: verdict.elapsedMs, reason: verdict.reason, rootPlan: verdict.rootPlan ?? null,
1715
+ });
1716
+ stage(`heavy-remote-${phase}`, started, verdict.ok, { head: opts.expectedHead, main, source: verdict.source });
1717
+ return verdict;
1718
+ };
1719
+ let verifiedHeavyBase: string | undefined;
1720
+ if (opts.heavyRemote) {
1721
+ const main = overlapCheckedBase!;
1722
+ const verdict = heavyGate(main, 'initial');
1723
+ if (!verdict?.ok) { out.error(`✗ heavy-remote: 원격 게이트 ${verdict?.reason ?? 'unmeasured'} — 로컬 게이트로 폴백하지 않는다.`); return 1; }
1724
+ verifiedHeavyBase = main;
1725
+ out.log(`✓ heavy-remote: host=${verdict.host ?? 'unknown'} head=${opts.expectedHead!.slice(0, 12)} main=${main.slice(0, 12)} source=${verdict.source} elapsed=${verdict.elapsedMs}ms`);
1726
+ out.log(`✓ typecheck-roots: ${JSON.stringify(verdict.rootPlan ?? { mode: 'not-applicable', roots: null, promoted: false, reason: 'no-root-plan-in-gate-output' })}`);
1727
+ }
1728
+ // Call immediately before the first mergePrOutcome and again before every UNKNOWN retry.
1729
+ // A move during the wait is the same race as a move during the gate: rerun or fail closed.
1730
+ // gh merge pins the head only. A retargeted PR would land on a branch the heavy gate never measured.
1731
+ // Unreadable baseRefName is not "main". Normal pr land (no --heavy-remote) does not use this check.
1732
+ const confirmHeavyPrBase = (): number | undefined => {
1733
+ if (!opts.heavyRemote) return undefined;
1734
+ const viewed = run('gh', ['pr', 'view', String(opts.pr), '--json', 'baseRefName,url'], { cwd });
1735
+ // JSON only. A bare "main" from another view (tests, --jq) is not this answer; unreadable is not main.
1736
+ let baseName = '';
1737
+ try {
1738
+ if (viewed.ok) {
1739
+ const parsed: unknown = JSON.parse(viewed.out);
1740
+ const name = parsed && typeof parsed === 'object' && !Array.isArray(parsed)
1741
+ ? (parsed as { baseRefName?: unknown }).baseRefName : undefined;
1742
+ if (typeof name === 'string') baseName = name.trim();
1743
+ }
1744
+ } catch { /* unreadable is not main */ }
1745
+ if (baseName !== 'main') {
1746
+ record('heavy-remote-pr-base', false, { pr: Number(opts.pr), baseRefName: baseName || null });
1747
+ out.error(`✗ heavy-remote: PR base=${baseName || 'unreadable'} ≠ main — 재타기팅된 PR은 통과한 head/main 쌍이 아니다. 병합하지 않는다.`);
1748
+ return 1;
1749
+ }
1750
+ return undefined;
1751
+ };
1752
+ const recheckOverlapAtBase = async (currentBase: string): Promise<number | undefined> => {
1753
+ if (currentBase === overlapCheckedBase) return undefined;
1754
+ changedPaths = currentChangePaths(run, cwd, staged, base, out);
1755
+ const refused = await checkOverlap();
1756
+ if (refused !== undefined) return refused;
1757
+ overlapCheckedBase = currentBase;
1758
+ return undefined;
1759
+ };
1760
+ const confirmHeavyBaseStable = async (): Promise<number | undefined> => {
1761
+ if (!opts.heavyRemote) return undefined;
1762
+ const retargeted = confirmHeavyPrBase();
1763
+ if (retargeted !== undefined) return retargeted;
1764
+ let stable = false;
1765
+ for (let attempt = 0; attempt <= HEAVY_BASE_STABILITY_RETRIES; attempt += 1) {
1766
+ const main = currentMain();
1767
+ if (!main) { out.error('✗ heavy-remote: 병합 직전 origin/main SHA를 읽지 못했다.'); return 1; }
1768
+ if (main.toLowerCase() === verifiedHeavyBase?.toLowerCase()) { stable = true; break; }
1769
+ if (attempt === HEAVY_BASE_STABILITY_RETRIES) break;
1770
+ // A new overlap can deterministically refuse this head. Check the new base
1771
+ // before spending another remote gate on a pair that cannot land.
1772
+ const staleOverlap = await recheckOverlapAtBase(main);
1773
+ if (staleOverlap !== undefined) return staleOverlap;
1774
+ const verdict = heavyGate(main, 'late-recheck');
1775
+ if (!verdict?.ok) {
1776
+ out.error(`✗ heavy-remote: 병합 직전 원격 게이트 ${verdict?.reason ?? 'unmeasured'} — 로컬 게이트로 폴백하지 않는다.`);
1777
+ return 1;
1778
+ }
1779
+ verifiedHeavyBase = main;
1780
+ out.log(`✓ heavy-remote: merge-pair head=${opts.expectedHead!.slice(0, 12)} main=${main.slice(0, 12)} source=${verdict.source}`);
1781
+ }
1782
+ if (!stable) {
1783
+ out.error('✗ heavy-remote: origin/main 이 게이트 동안 계속 움직여 통과한 head/main 쌍으로 병합할 수 없다.');
1784
+ return 1;
1785
+ }
1786
+ // A late regate can take minutes. Re-read baseRefName after that wait and before success:
1787
+ // the pre-loop check is stale if the PR was retargeted while origin/main was being remeasured.
1788
+ const retargetedAfter = confirmHeavyPrBase();
1789
+ if (retargetedAfter !== undefined) return retargetedAfter;
1790
+ return undefined;
1791
+ };
1792
+ const confirmPinnedOverlapFresh = async (): Promise<number | undefined> => {
1793
+ if (!pinned) return undefined;
1794
+ const currentBase = opts.heavyRemote ? verifiedHeavyBase : currentPinnedBase();
1795
+ if (!currentBase) {
1796
+ out.error(`✗ overlap-preflight: 병합 직전 ${base} 최신 SHA를 가져오지 못했다.`);
1797
+ return 1;
1798
+ }
1799
+ return recheckOverlapAtBase(currentBase);
1800
+ };
1801
+ const typecheckOk = opts.heavyRemote || (deps.runTypecheckGate ?? ((o) => runPrLandTypecheckGate(o, cwd)))(out);
1546
1802
  record('typecheck', typecheckOk);
1547
1803
  if (!typecheckOk) {
1548
1804
  // ⛔⭐ 「왜 막혔나」를 «단정하지 않는다» — 이 게이트는 불리언만 돌려주므로 여기서는 이유를 모른다.
@@ -1556,7 +1812,9 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
1556
1812
  out.error('✗ typecheck: scripts/ci-typecheck-changed.ts blocked pr land.');
1557
1813
  return 1;
1558
1814
  }
1559
- out.log('✓ typecheck: scripts/ci-typecheck-changed.ts PASS — changed files have no new type errors.');
1815
+ out.log(opts.heavyRemote
1816
+ ? '✓ typecheck: node-b exact-head heavy gate PASS — changed files have no new type errors.'
1817
+ : '✓ typecheck: scripts/ci-typecheck-changed.ts PASS — changed files have no new type errors.');
1560
1818
 
1561
1819
  const isolation = gateVerdict('isolation-gate', deps.runIsolationGate ?? ((o) => runPrLandIsolationGate(o, cwd)), out);
1562
1820
  // ⭐ 관측에 «잰 것»과 «못 잰 것»을 다른 값으로 남긴다 — 둘이 같은 모양이면 그 자는 거짓을 낸다.
@@ -1602,7 +1860,7 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
1602
1860
  out.log('✓ daemon-port-gate: scripts/ci-daemon-port-gate.ts PASS — no new daemon port literals.');
1603
1861
  }
1604
1862
 
1605
- const changedPaths = currentChangePaths(run, cwd, staged, base, out);
1863
+ if (!pinned) changedPaths = currentChangePaths(run, cwd, staged, base, out);
1606
1864
  nextMdWarning(body ?? '', changedPaths, out);
1607
1865
  if (!conflictMarkerBlock(changedPaths, cwd, out)) return 1;
1608
1866
  publicLeakWarning(changedPaths, deps.runPublicLeakGate ?? ((files, sink) => runPrLandPublicLeakGate(files, sink, cwd, base)), out);
@@ -1613,27 +1871,29 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
1613
1871
  const docsRoot = top.ok && top.out.trim() ? top.out.trim() : cwd;
1614
1872
  docsCliWarning(publicDocPaths(changedPaths, (f) => existsSync(join(docsRoot, f))), deps.runDocsCliCheck ?? runPrLandDocsCliCheck(docsRoot), out);
1615
1873
  }
1616
- const testInterference = await testInterferenceGateVerdict(
1617
- deps.runTestInterferenceGate ?? ((o, files) => runTestInterferenceGate({ args: ['--changed-files', ...files], log: o.log, cwd })),
1618
- changedPaths,
1619
- out,
1620
- );
1621
- record('test-interference-gate', true, { measured: testInterference.measured });
1874
+ const testInterference = opts.heavyRemote
1875
+ ? { measured: heavyInterferenceMeasured }
1876
+ : await testInterferenceGateVerdict(
1877
+ deps.runTestInterferenceGate ?? ((o, files) => runTestInterferenceGate({ args: ['--changed-files', ...files], log: o.log, cwd })),
1878
+ changedPaths,
1879
+ out,
1880
+ );
1881
+ record('test-interference-gate', true, { measured: testInterference.measured, ...(opts.heavyRemote ? { host: heavyGateHost ?? null } : {}) });
1622
1882
  // LAND-GLOBAL-CHECKS — 바뀐 파일의 경로 꼴이 부르는 «전역 검사»를 끌어와 돌린다(표 = scripts/land-global-checks.ts).
1623
- // 시험이 실패하면 막는다 · 러너가 못 돌았으면 「검사 못 함」으로 말하고 통과시킨다(gateVerdict).
1624
- const landGlobal = gateVerdict(
1883
+ // --heavy-remote 는 같은 head/main 통합 트리에서 이미 측정했다. 원격의 실패·미측정은 heavyGate 가 막는다.
1884
+ const landGlobal = opts.heavyRemote ? { ok: true, measured: true } : gateVerdict(
1625
1885
  'land-global-checks',
1626
1886
  deps.runLandGlobalChecks
1627
1887
  ? (o) => deps.runLandGlobalChecks!(o, changedPaths)
1628
1888
  : (o) => runLandGlobalChecksGate({ changedFiles: changedPaths, cwd, log: o.log, error: o.error }),
1629
1889
  out,
1630
1890
  );
1631
- record('land-global-checks', landGlobal.ok, { measured: landGlobal.measured });
1891
+ record('land-global-checks', landGlobal.ok, { measured: landGlobal.measured, ...(opts.heavyRemote ? { host: heavyGateHost ?? null } : {}) });
1632
1892
  if (!landGlobal.ok) {
1633
1893
  out.error('✗ land-global-checks: scripts/land-global-checks.ts blocked pr land — 바뀐 파일이 끌어온 전역 시험이 실패했다.');
1634
1894
  return 1;
1635
1895
  }
1636
- if (landGlobal.measured && landGlobalChecks(changedPaths).length > 0) {
1896
+ if (!opts.heavyRemote && landGlobal.measured && landGlobalChecks(changedPaths).length > 0) {
1637
1897
  out.log(`✓ land-global-checks: scripts/land-global-checks.ts PASS — 끌려온 전역 시험 ${landGlobalChecks(changedPaths).join(' · ')} 실패 0.`);
1638
1898
  }
1639
1899
 
@@ -1672,7 +1932,7 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
1672
1932
  }
1673
1933
 
1674
1934
  // PWA 는 bun test/tsc 밖의 정적 번들이다. 측정 불가도 착지 불가로 판정한다.
1675
- const pwaGate = gateVerdict('pwa-gate', deps.runPwaGate
1935
+ const pwaGate = gateVerdict('pwa-gate', opts.heavyRemote ? () => true : deps.runPwaGate
1676
1936
  ? (o) => deps.runPwaGate!(o, changedPaths)
1677
1937
  : (o) => runPwaBuildGate({ args: ['--changed-files', ...changedPaths], cwd, log: o.log, error: o.error }) === 0, out, true);
1678
1938
  record('pwa-gate', pwaGate.ok && pwaGate.measured, { measured: pwaGate.measured });
@@ -1681,83 +1941,8 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
1681
1941
  return 1;
1682
1942
  }
1683
1943
 
1684
- const overlappingLandings: string[] = [];
1685
- const overlapFound = emitLandingOverlapAdvisory(run, cwd, base, changedPaths, out, overlappingLandings);
1686
- const siblingFound = emitSiblingPrAdvisory(
1687
- branch,
1688
- deps.listOpenPrs ?? (() => lookupOpenPrs(run, cwd)),
1689
- out,
1690
- );
1691
- const advisoryShown = recentAdvisoryShown || overlapFound || siblingFound;
1692
- const reason = opts.landReason;
1693
- const landReasonNotice = formatLandReasonNotice({ reason, advisoryShown });
1694
- if (landReasonNotice) out.log(landReasonNotice);
1695
- record('land-reason', true, {
1696
- advisoryShown,
1697
- hasReason: !!reason,
1698
- reasonLength: reason ? codePointLength(reason) : 0,
1699
- reasonLengthUnit: 'codepoint',
1700
- });
1701
- let overlapDecision: OverlapLandingDecision | undefined;
1702
- if (overlapFound && !opts.hold && !opts.dryRun) {
1703
- overlapDecision = await (deps.decideOverlapLanding ?? (() => decideOverlapLanding({
1704
- isInteractive: deps.isInteractive,
1705
- requestConfirmation: deps.requestConfirmation,
1706
- channels: deps.overlapConfirmChannels,
1707
- timeoutMs: deps.overlapConfirmTimeoutMs,
1708
- })))();
1709
- record('overlap-decision', true, {
1710
- outcome: overlapDecision.outcome,
1711
- channel: overlapDecision.channel,
1712
- interactive: overlapDecision.interactive,
1713
- });
1714
- }
1715
- const hold = !!opts.hold || overlapDecision?.outcome === 'user-hold';
1716
- // TA-LAND-OVERLAP — 비대화형 «승인 불가»만 증거로 대신한다(사람의 hold 는 증거로 뒤집지 않는다) · 증거가 없거나 어긋나면 종전 거부 그대로.
1717
- let overlapEvidenceAccepted = false;
1718
- let ancestorResyncNeeded = false;
1719
- if (pinned && overlapDecision?.outcome === 'unavailable' && opts.overlapEvidence !== undefined) {
1720
- const ref = resolveRemoteBranchRef(run, cwd, base);
1721
- const verdict = verifyOverlapEvidence({
1722
- evidencePath: opts.overlapEvidence, pr: Number(opts.pr), expectedHead: opts.expectedHead!,
1723
- remote: ref.remote, branch: ref.branch, cwd, run, readFile, overlappingLandings,
1724
- });
1725
- overlapEvidenceAccepted = verdict.ok;
1726
- if (verdict.ok) {
1727
- out.log(verdict.mode === 'already-ancestor'
1728
- ? `✓ overlap-evidence: already-ancestor — 겹친 최근 착지 ${overlappingLandings.length}개가 전부 head ${opts.expectedHead!.slice(0, 12)} 의 조상 · review pass must-fix 0 (${verdict.reviewResult})`
1729
- : verdict.mode === 'base-advanced-disjoint'
1730
- ? `✓ overlap-evidence: base-advanced-disjoint — gate base ${verdict.gateBase.slice(0, 12)} ⊂ head ${opts.expectedHead!.slice(0, 12)} · ${ref.remote}/${ref.branch} ${verdict.baseTip.slice(0, 12)} 까지 ${verdict.windowCommits}개 커밋이 PR 파일을 안 건드린다 · review pass|warn must-fix 0 (${verdict.reviewResult})`
1731
- : `✓ overlap-evidence: ${ref.remote}/${ref.branch} ${verdict.baseTip.slice(0, 12)} ⊂ head ${opts.expectedHead!.slice(0, 12)} · gate passed on that base · review pass must-fix 0 (${verdict.reviewResult})`);
1732
- }
1733
- else refuse(`✗ overlap-evidence: ${verdict.reason}`);
1734
- } else if (pinned && overlapDecision?.outcome === 'unavailable') {
1735
- // 증거 파일이 없을 때 — 겹친 착지가 전부 머리의 조상이고 표준 자리의 리뷰가 pass|warn ⊕ must-fix 0 이면 통과(아니면 종전 거부).
1736
- const verdict = verifyAncestorAutoPass({
1737
- pr: Number(opts.pr), expectedHead: opts.expectedHead!, cwd, run, readFile, overlappingLandings,
1738
- reviewResultsDir: deps.reviewResultsDir ?? join(dirname(taskAgentStatePath()), 'review-results'),
1739
- listDir: (dir) => readdirSync(dir),
1740
- });
1741
- overlapEvidenceAccepted = verdict.ok;
1742
- if (verdict.ok) {
1743
- out.log(`✓ overlap-evidence: already-ancestor-auto — 겹친 최근 착지 ${overlappingLandings.length}개가 전부 head ${opts.expectedHead!.slice(0, 12)} 의 조상 · review pass|warn must-fix 0 (${verdict.reviewResult})`);
1744
- }
1745
- else {
1746
- refuse(`✗ ancestor-auto: ${verdict.reason}`);
1747
- ancestorResyncNeeded = verdict.reason.startsWith('ancestor-auto-not-ancestor:');
1748
- }
1749
- }
1750
- if (pinned && overlapDecision && overlapDecision.outcome !== 'user-merge' && !overlapEvidenceAccepted) {
1751
- // A clean GitHub PR can still be behind a recently landed overlapping head. Preserve the cue
1752
- // across hosts; the current steward tick acts only if it can also prove local run ownership.
1753
- if (ancestorResyncNeeded && !opts.dryRun) {
1754
- const marked = run('gh', ['pr', 'edit', String(opts.pr), '--add-label', PR_LABEL_NAMES.needsRebase], { cwd });
1755
- record('resync-marker', marked.ok, { pr: Number(opts.pr), reason: 'ancestor-auto-not-ancestor' });
1756
- if (!marked.ok) out.error('⚠ resync-marker: needs-rebase 라벨을 남기지 못했습니다');
1757
- }
1758
- out.error('✗ expected-head: overlap merge approval unavailable for the reviewed head');
1759
- return 1;
1760
- }
1944
+
1945
+ if (!pinned && (await checkOverlap()) !== undefined) return 1;
1761
1946
 
1762
1947
  const manager = deps.manager ?? makePrManager(run);
1763
1948
  const existing = manager.findPrForBranchOutcome(branch, cwd);
@@ -1938,35 +2123,74 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
1938
2123
  }
1939
2124
  const landing = admission;
1940
2125
  if (pinned) {
1941
- const view = run('gh', ['pr', 'view', String(opts.pr), '--json', 'headRefOid,state,isDraft'], { cwd });
1942
- let current: { headRefOid?: string; state?: string; isDraft?: boolean } = {};
2126
+ const view = run('gh', ['pr', 'view', String(opts.pr), '--json', 'headRefOid,state,isDraft,baseRefName'], { cwd });
2127
+ let current: { headRefOid?: string; state?: string; isDraft?: boolean; baseRefName?: string } = {};
1943
2128
  try { if (view.ok) current = JSON.parse(view.out) as typeof current; } catch { /* fail closed */ }
1944
2129
  const finalHead = run('git', ['rev-parse', 'HEAD'], { cwd });
1945
2130
  if (current.headRefOid?.toLowerCase() !== opts.expectedHead!.toLowerCase() || current.state !== 'OPEN' || current.isDraft !== false
2131
+ || (opts.heavyRemote && current.baseRefName !== 'main')
1946
2132
  || !finalHead.ok || finalHead.out.trim().toLowerCase() !== opts.expectedHead!.toLowerCase()) {
1947
2133
  landing.end(false);
1948
2134
  out.error('✗ expected-head: PR head/state changed before merge');
1949
2135
  return 1;
1950
2136
  }
1951
2137
  }
1952
- let merged: ReturnType<typeof manager.mergePrOutcome>;
2138
+ // gh pr merge pins only the PR head (--match-head-commit). It exposes no atomic base
2139
+ // precondition. Re-read origin/main as close as possible to every mergePrOutcome,
2140
+ // including UNKNOWN retries after the wait. The last read and the merge call are
2141
+ // still not one atomic pin; a move inside that gap is caught after merge.
2142
+ {
2143
+ const started = Date.now();
2144
+ const unstable = await confirmHeavyBaseStable();
2145
+ stage('pre-merge-base-check', started, unstable === undefined, { verifiedBase: verifiedHeavyBase ?? null });
2146
+ if (unstable !== undefined) { landing.end(false); return unstable; }
2147
+ const staleOverlap = await confirmPinnedOverlapFresh();
2148
+ if (staleOverlap !== undefined) { landing.end(false); return staleOverlap; }
2149
+ }
2150
+ let merged: ReturnType<typeof manager.mergePrOutcome> | undefined;
2151
+ let heavyRetryRefused = false;
2152
+ let mergeAttempt = 0;
2153
+ const mergeOnce = (): ReturnType<typeof manager.mergePrOutcome> => {
2154
+ const started = Date.now();
2155
+ const attempt = ++mergeAttempt;
2156
+ let outcome: ReturnType<typeof manager.mergePrOutcome> | undefined;
2157
+ try {
2158
+ outcome = manager.mergePrOutcome(upsert.url, pinned ? opts.expectedHead : undefined, pinned ? cwd : undefined);
2159
+ return outcome;
2160
+ } finally {
2161
+ stage('merge', started, outcome?.ok === true, { attempt, verifiedBase: verifiedHeavyBase ?? null });
2162
+ }
2163
+ };
1953
2164
  try {
1954
- merged = manager.mergePrOutcome(upsert.url, pinned ? opts.expectedHead : undefined, pinned ? cwd : undefined);
2165
+ merged = mergeOnce();
1955
2166
  // `GIT-S14` — 병합이 «미완료(OPEN)»로 끝났고 GitHub 가 아직 `mergeable=UNKNOWN`(병합 가능 여부 계산 중)이면
1956
2167
  // 짧게 기다렸다 다시 병합한다. 표본 셋(08-25 · 09-24 🅢 #20233 등)이 모두 «같은 명령 재시도에 병합»이었다.
1957
2168
  // ⛔ UNKNOWN 일 때만 재시도한다 — CONFLICTING 등 다른 값은 기다려도 안 풀린다(그대로 실패를 낸다).
1958
2169
  const sleep = deps.sleep ?? ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)));
1959
2170
  // pinned 도 재시도한다 — mergePrOutcome 이 머리를 다시 확인하고 --match-head-commit 으로 병합하므로 바뀐 머리로는 안 들어간다.
2171
+ // heavy-remote 는 대기 중 main 이 움직일 수 있으므로 재병합 직전에 같은 안정성 검사를 다시 탄다.
2172
+ // 그 검사가 실패하면 두 번째 병합을 하지 않고, 이미 찍힌 게이트 오류를 유지한 채 반환한다.
1960
2173
  for (let attempt = 1; !merged.ok && merged.kind === 'not-merged' && merged.state === 'OPEN' && attempt <= PR_LAND_MERGEABLE_UNKNOWN_RETRIES; attempt += 1) {
1961
2174
  const mergeable = run('gh', ['pr', 'view', upsert.url, '--json', 'mergeable', '--jq', '.mergeable'], { cwd });
1962
2175
  const value = mergeable.ok ? mergeable.out.trim() : '';
1963
2176
  if (value !== 'UNKNOWN') break;
1964
2177
  record('merge-retry', true, { attempt, mergeable: value, waitMs: PR_LAND_MERGEABLE_UNKNOWN_WAIT_MS });
1965
2178
  out.log(`⏳ merge: mergeable=UNKNOWN — GitHub 가 병합 가능 여부를 아직 계산 중입니다 · ${PR_LAND_MERGEABLE_UNKNOWN_WAIT_MS / 1000}초 뒤 다시 병합 (${attempt}/${PR_LAND_MERGEABLE_UNKNOWN_RETRIES})`);
2179
+ const waitStarted = Date.now();
1966
2180
  await sleep(PR_LAND_MERGEABLE_UNKNOWN_WAIT_MS);
1967
- merged = manager.mergePrOutcome(upsert.url, pinned ? opts.expectedHead : undefined, pinned ? cwd : undefined);
1968
- }
1969
- } finally { landing.end(merged!?.ok === true); }
2181
+ stage('merge-retry-wait', waitStarted, true, { attempt });
2182
+ const checkStarted = Date.now();
2183
+ const unstable = await confirmHeavyBaseStable();
2184
+ stage('pre-merge-base-check', checkStarted, unstable === undefined, { attempt, verifiedBase: verifiedHeavyBase ?? null });
2185
+ if (unstable !== undefined) { heavyRetryRefused = true; break; }
2186
+ const staleOverlap = await confirmPinnedOverlapFresh();
2187
+ if (staleOverlap !== undefined) { heavyRetryRefused = true; break; }
2188
+ merged = mergeOnce();
2189
+ }
2190
+ } finally {
2191
+ landing.end(!heavyRetryRefused && merged?.ok === true);
2192
+ }
2193
+ if (heavyRetryRefused || !merged) return 1;
1970
2194
  if (!merged.ok) {
1971
2195
  // `GIT-S76` — 「미완료(OPEN)」만으로는 «충돌»과 «아직 계산 중»이 안 갈린다. GitHub 의 병합 가능 상태를 같이 싣는다.
1972
2196
  // 되읽기에 실패하면 종전 문면 그대로다(못 읽은 것을 「충돌 아님」으로 적지 않는다).
@@ -2000,6 +2224,58 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
2000
2224
  if (merged.remoteBranchDeletion) {
2001
2225
  out.log(`⚠ remote branch deletion: ${merged.remoteBranchDeletion.detail}`);
2002
2226
  }
2227
+ if (opts.heavyRemote && verifiedHeavyBase) {
2228
+ // Squash merge has one parent: the base GitHub actually merged onto. Compare it with the
2229
+ // base that passed the heavy gate. Do not revert a landed commit; a mismatch means the
2230
+ // non-atomic gap between the last re-read and gh merge moved main.
2231
+ const viewed = run('gh', ['pr', 'view', upsert.url, '--json', 'mergeCommit,state'], { cwd });
2232
+ let mergeOid = '';
2233
+ let state = '';
2234
+ try {
2235
+ if (viewed.ok) {
2236
+ const parsed = JSON.parse(viewed.out) as { state?: unknown; mergeCommit?: { oid?: unknown } | null };
2237
+ state = typeof parsed.state === 'string' ? parsed.state : '';
2238
+ mergeOid = typeof parsed.mergeCommit?.oid === 'string' ? parsed.mergeCommit.oid : '';
2239
+ }
2240
+ } catch { /* unreadable is not a match */ }
2241
+ let firstParent = '';
2242
+ if (/^[a-f0-9]{40}$/i.test(mergeOid)) {
2243
+ const fetched = run('git', ['fetch', '--quiet', 'origin', mergeOid], { cwd });
2244
+ if (fetched.ok) {
2245
+ const parent = run('git', ['rev-parse', '--verify', `${mergeOid}^1`], { cwd });
2246
+ if (parent.ok && /^[a-f0-9]{40}$/i.test(parent.out.trim())) firstParent = parent.out.trim();
2247
+ }
2248
+ }
2249
+ if (state === 'MERGED' && firstParent.toLowerCase() === verifiedHeavyBase.toLowerCase()) {
2250
+ record('heavy-remote-base', true, { verifiedBase: verifiedHeavyBase, mergedOnto: firstParent, mergeCommit: mergeOid });
2251
+ } else {
2252
+ const reason = state !== 'MERGED' ? 'merge-state-unconfirmed'
2253
+ : !firstParent ? 'merge-parent-unreadable' : 'merged-base-mismatch';
2254
+ record('heavy-remote-base', false, {
2255
+ verifiedBase: verifiedHeavyBase, mergedOnto: firstParent || null, mergeCommit: mergeOid || null, state: state || null, reason,
2256
+ });
2257
+ debug.log('pr.land', 'post-merge-gate-required', {
2258
+ pr: Number(opts.pr), head: opts.expectedHead, verifiedBase: verifiedHeavyBase,
2259
+ mergedOnto: firstParent || null, mergeCommit: mergeOid || null, verifiedHost: heavyGateHost ?? null, reason,
2260
+ });
2261
+ out.log(`post-merge gate required: verified=${verifiedHeavyBase.slice(0, 12)} mergedOnto=${firstParent ? firstParent.slice(0, 12) : 'unreadable'} (${reason})`);
2262
+ // A missing first parent must not be gated against the stale verified base — that
2263
+ // would claim coverage of a pair that was never measured. Merge already happened.
2264
+ if (!firstParent) {
2265
+ record('heavy-remote', false, { host: heavyGateHost ?? null, phase: 'post-merge', reason: 'unmeasured', mergedOnto: null });
2266
+ out.error('✗ heavy-remote: post-merge 원격 게이트 unmeasured — squash 첫 부모를 읽지 못해 사후 게이트를 돌리지 않는다. 병합은 되돌리지 않는다.');
2267
+ out.log(`✓ merge: squash ${upsert.url} → ${base}`);
2268
+ return 1;
2269
+ }
2270
+ const post = heavyGate(firstParent, 'post-merge');
2271
+ if (!post?.ok) {
2272
+ out.error(`✗ heavy-remote: post-merge 원격 게이트 ${post?.reason ?? 'unmeasured'} — 병합은 되돌리지 않는다.`);
2273
+ out.log(`✓ merge: squash ${upsert.url} → ${base}`);
2274
+ return 1;
2275
+ }
2276
+ out.log(`✓ heavy-remote: post-merge host=${post.host ?? 'unknown'} head=${opts.expectedHead!.slice(0, 12)} main=${firstParent.slice(0, 12)} source=${post.source}`);
2277
+ }
2278
+ }
2003
2279
  const baseRef = upsert.reused
2004
2280
  ? run('gh', ['pr', 'view', upsert.url, '--json', 'baseRefName', '--jq', '.baseRefName'], { cwd })
2005
2281
  : undefined;
@@ -2037,6 +2313,7 @@ export function registerPrCommands(program: Command, deps: PrLandDeps = {}, queu
2037
2313
  .option('--cwd <path>', '작업 트리 경로')
2038
2314
  .option('--pr <number>', '기존 PR 번호 고정 (--expected-head 와 함께)')
2039
2315
  .option('--expected-head <sha>', '리뷰된 PR 머리 SHA 고정 (--pr 와 함께)')
2316
+ .option('--heavy-remote', '머리 고정 착지의 무거운 게이트를 node-b·node-c 중 빈 호스트에서 실행; head⊕main 증거가 없거나 실패하면 거부(로컬 폴백 없음)')
2040
2317
  .option('--dry-run', '변경 없이 landing 계획만 출력')
2041
2318
  .option('--hold', 'PR을 생성 또는 갱신한 뒤 병합하지 않고 쌓아 둔다')
2042
2319
  .option('--title <title>', 'PR title')