elanous 0.2.22 → 0.2.23

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 (705) hide show
  1. package/NOTICE +7 -0
  2. package/README.md +32 -22
  3. package/apps/pwa/out/404/index.html +1 -1
  4. package/apps/pwa/out/404.html +1 -1
  5. package/apps/pwa/out/_next/static/chunks/{119fc678.6f591cdb84ca5791.js → 0673cc54.8e2ef1c23e49e190.js} +1 -1
  6. package/apps/pwa/out/_next/static/chunks/{a9f2b54d-40c16bd0efdb04b8.js → 0a09e062-34f747a17d8d8c2e.js} +1 -1
  7. package/apps/pwa/out/_next/static/chunks/{f8626309.a4e5d362b1d55e59.js → 0e3acd34.d42b2ef87fdf4234.js} +1 -1
  8. package/apps/pwa/out/_next/static/chunks/1168.a0fb3d9db76c00bd.js +77 -0
  9. package/apps/pwa/out/_next/static/chunks/{7175-350f4ce2873bc038.js → 1325-dc3f3c21eb181cb4.js} +1 -1
  10. package/apps/pwa/out/_next/static/chunks/1357.a843c41d2cd7ec31.js +34 -0
  11. package/apps/pwa/out/_next/static/chunks/1430.53f49882b17607c2.js +24 -0
  12. package/apps/pwa/out/_next/static/chunks/1459-be6509b3419d8a9f.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/{6767.8e6c5f6e744fcd9f.js → 1505.5826671109111e75.js} +2 -2
  14. package/apps/pwa/out/_next/static/chunks/153.0a4e3cd516930491.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/1572.7bc291352e241a45.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/1593-d07ad068c120dfb0.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/1608.b7c900baa56cab32.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/1615-4d8156febaac4b35.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/1646.e03f0fbc26bc72f6.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/{7327-2d69fb321ba73389.js → 1669-da843ed73aa301f5.js} +1 -1
  21. package/apps/pwa/out/_next/static/chunks/176.13ba0659e3e96fd4.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/1990.9c60f495ef33c070.js +36 -0
  23. package/apps/pwa/out/_next/static/chunks/2042e469.f86158dd5c1e51a1.js +122 -0
  24. package/apps/pwa/out/_next/static/chunks/2528.0af5c0e1fad88df6.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/2543-28ec6aeb80a5e1e1.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/2561.43c99d6ee914ee6a.js +62 -0
  27. package/apps/pwa/out/_next/static/chunks/2638.b39ab2f674caa1a5.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/272-b23d2e92161d11fd.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/{7899.597af4a9996c907f.js → 2721.bc9a8f36bd85c07a.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/2778.67e0c1d484189ccf.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/2841.10ff309f6a3168af.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/2886-9639f97d523d752a.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/2978.57fb87ce21cc5a85.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/3664.15f968c39d15bd28.js +149 -0
  35. package/apps/pwa/out/_next/static/chunks/3786-935eb3fcde90e496.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/3813.df4a01ae89c6f63f.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/3961-fbef064c213b2b18.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/3992.f79c70480e8c4077.js +309 -0
  39. package/apps/pwa/out/_next/static/chunks/40-413e8641de9ed1e5.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/4054-521f11a503daed64.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/4315.e7356f34c7d8ec79.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/4489-bf629de6f04f7681.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/4540.557733cc88c6a82b.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/462.0275b4338c5b6337.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/4658-7ed9266098a787e4.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/4945-0f4046cfc3228ea3.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/{d70879db.c66c56a95a91efc6.js → 4c5e95ce.83e9b16add6fcd8c.js} +1 -1
  48. package/apps/pwa/out/_next/static/chunks/5105-18b4165c5d3a86b0.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/5205-6992210fe31f45f4.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/5287-72898b126d722457.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/{6158.566c890adc06a459.js → 5313.1697309c532b9856.js} +2 -2
  52. package/apps/pwa/out/_next/static/chunks/{79b78388.663188133449d707.js → 53746282.1be36038d1abd949.js} +1 -1
  53. package/apps/pwa/out/_next/static/chunks/5474.991b90688c3cb321.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/5544-871146192d095594.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/5578.ac963634373b28c6.js +286 -0
  56. package/apps/pwa/out/_next/static/chunks/5935.d5e8ca3e096772e8.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/5967-c0a80f8ffe5d4050.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/{1268.0840ff91e083fcbd.js → 5974.360f8441eb0ac1ec.js} +1 -1
  59. package/apps/pwa/out/_next/static/chunks/6006.84e8b418a7a0db97.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/6013-1267ba7454d6764b.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/6038-1e0f7ee1086da4cf.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/617.709dd0019fe99796.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/6261.fb9bf94947094636.js +74 -0
  64. package/apps/pwa/out/_next/static/chunks/6411.1966866fae31ffa9.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/6481-b829d99aa8d158f5.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/6541.2b7daa043100d0fa.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/6571-926ddf024a6ad5df.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/6597.2d7a6d77ab485b27.js +64 -0
  69. package/apps/pwa/out/_next/static/chunks/67.5dd2dc543c2fa3de.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/{9832.2ab2e818013574c7.js → 6702.ebbd165d5e4a4a14.js} +1 -1
  71. package/apps/pwa/out/_next/static/chunks/6777-312496b0d774719b.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/681.0116f7036cfb4e8d.js +32 -0
  73. package/apps/pwa/out/_next/static/chunks/6871.30b2bf23b3126e36.js +24 -0
  74. package/apps/pwa/out/_next/static/chunks/6882.737d366d54e29d83.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/7035.54c2c535164b1c9b.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/7086-8bfa7e8f796a03d9.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/7126.5e5599f251726b67.js +93 -0
  78. package/apps/pwa/out/_next/static/chunks/7203-47ff0740a293e284.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/7268.017f49dafb5f0ff3.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/7355.5d65a87e9567ae08.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/7392.4018c2a62177f296.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/{6110.1297d4093fdfbb0d.js → 740.90b31555290236a9.js} +2 -2
  83. package/apps/pwa/out/_next/static/chunks/7471.3a3d1be50559cfb8.js +148 -0
  84. package/apps/pwa/out/_next/static/chunks/7500.73489a345cef2201.js +226 -0
  85. package/apps/pwa/out/_next/static/chunks/{9070.067c106528ec1ae6.js → 7518.49161e8f5814abdb.js} +1 -1
  86. package/apps/pwa/out/_next/static/chunks/7565.c2f72a15c1ed0396.js +27 -0
  87. package/apps/pwa/out/_next/static/chunks/7651.6d1c984d3d8587a4.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/7713.7ab863ed59b2d661.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/7793.d7cd2026767ddd54.js +115 -0
  90. package/apps/pwa/out/_next/static/chunks/7837.c7c267e5dceadb11.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/7891.09266382daf70d1c.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/7905.b230b9d06cb4ad5d.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/7990-cbf6a01bcbdcb7e6.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/8119-819c549e0c197786.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/8186-74ea19a3700d04a8.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/{8194.484f51e273d9abd2.js → 8264.00e434fbd02e9311.js} +16 -16
  97. package/apps/pwa/out/_next/static/chunks/8267-e7b3262b735ae106.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/8386.86518e55b75f7cf5.js +8 -0
  99. package/apps/pwa/out/_next/static/chunks/{088d8e8a.8069543d082bec12.js → 83cfc5c8.349535543c1548b6.js} +1 -1
  100. package/apps/pwa/out/_next/static/chunks/8437.ba6eeeb29590042f.js +23 -0
  101. package/apps/pwa/out/_next/static/chunks/{1948.5f35b39eef222a94.js → 8438.ee7f0df3b3f6fcb4.js} +1 -1
  102. package/apps/pwa/out/_next/static/chunks/8469-1500be57e582296f.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/8483-7164cecdaf178ca6.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/{4588.ad3364cb7fb97ada.js → 8532.a86e99acf9b761cb.js} +2 -2
  105. package/apps/pwa/out/_next/static/chunks/8569.b853a8daa8f31d12.js +1 -0
  106. package/apps/pwa/out/_next/static/chunks/8722-4c940f02c496ad36.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/887-41d0dcf5af48c628.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/8925-2abfb48af477fb7f.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/{88729267.9c6c000157c83f2f.js → 89849432.9eaa2f4c9c010965.js} +3 -3
  110. package/apps/pwa/out/_next/static/chunks/9039.da86361496797522.js +90 -0
  111. package/apps/pwa/out/_next/static/chunks/905.d5745a25884754d4.js +94 -0
  112. package/apps/pwa/out/_next/static/chunks/9089-d909f3d4478b0628.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/9096.dc5c7a267f83790c.js +41 -0
  114. package/apps/pwa/out/_next/static/chunks/93.a567257b6bdb7fe4.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/9320.419acb9665b46597.js +287 -0
  116. package/apps/pwa/out/_next/static/chunks/9356.b8ceda6634612ff8.js +79 -0
  117. package/apps/pwa/out/_next/static/chunks/94.e9e1043b35cd3eed.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/9400-7498ea90b2e6ee8f.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/{3522-1de1319d028ac89c.js → 9502-32c5dc22e8f120f5.js} +3 -3
  120. package/apps/pwa/out/_next/static/chunks/9930-58644af9c813d6e1.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/9932.367cf4725fedd6e2.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/9935.1628b904cdc8ce0e.js +104 -0
  123. package/apps/pwa/out/_next/static/chunks/{7630.2b9a68659053ff4e.js → 9976.8f28864317639fe6.js} +16 -16
  124. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-a2aa8709d193ee0d.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d5341ac96203a983.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-0160ab83c8685a12.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/board/page-55c631c032dcc47e.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/botlab/page-ad3bd8084048bee4.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/bots/page-e6784759a2531031.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/ceo/page-5c5d0e370f495ef6.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/chat/page-c325e21543cd48a8.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/consult/{page-80ed511e02c81412.js → page-6168a7c43e6ef8f5.js} +1 -1
  133. package/apps/pwa/out/_next/static/chunks/app/control/page-31417d3604154a6d.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/decisions/page-e9606dc0bdec9305.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/design-check/page-a648bc464c9dc754.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/editor/page-25bb425afdb27541.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/exec/page-32d72fdef1afe11a.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/field/page-d27b794c8ba76bbb.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/healthz/route-a2aa8709d193ee0d.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/inside/page-2bd9ce9c020d02d4.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/intake/page-a34096245e63b9f2.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/layout-22170f4f00a15496.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/live/page-4cc66d22ca4ec502.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/live-run/page-83b14c729fcea837.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/loops/page-6e2781c523dad761.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/market/page-26dd415c8f13f341.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-ccdb83ed40c8d724.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/morning/page-7ad3de8fa06fdea9.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/not-found-015c02c4fec4023e.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/observatory/page-6b0ae5b5c42f6d2f.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-786111f30b6f0969.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-48fb31810693c357.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4e17cce5e2ed0fee.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/outputs/page-cb154acadb025414.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/page-16258e956ea93fc2.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/reflection/page-c2dd2b19c93f90ab.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f35dd1dd96ae87.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/sessions/page-d3c007c8a0ddd7ee.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-7837ce2aacb97b04.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/settings/page-7350ee93f4fe1033.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-00723458575f2b19.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/setup/layout-a2aa8709d193ee0d.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/setup/page-745d9b6c015076f8.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/share/page-736d33c7414a8a1a.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/showroom/page-119be9a0444d2482.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/tasks/page-fa806c018833e332.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/term/page-c782041b5f5199ba.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/today/page-d431fbdea6ad93dd.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/trace/page-0c056abff82b9891.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/vault/page-242d668e4fbf72c4.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-847c3de6ca8a4416.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/workflows/page-81dd0d8ae923ed72.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/app/workspace/page-74e69ba2c8e970d4.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-70038f41ae7cf931.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/b4e12462-5fab4a813a63dadd.js +1 -0
  176. package/apps/pwa/out/_next/static/chunks/cd39fa05.fac4545f42a3ef70.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/{f6b7c9ba.64a895b8b2fedbdc.js → ce383d63.d2ede338788ed18e.js} +1 -1
  178. package/apps/pwa/out/_next/static/chunks/{framework-5c169757551134ed.js → framework-b2763b10dc6b3836.js} +1 -1
  179. package/apps/pwa/out/_next/static/chunks/main-app-27893e93b0ec193d.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-f280047194e1daf8.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_app-e90eff2d0b70564c.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_error-70f2d654b3435d34.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/webpack-4794f053bd5e4230.js +1 -0
  184. package/apps/pwa/out/_next/static/css/5619d59e87eddf66.css +1 -0
  185. package/apps/pwa/out/_next/static/css/d0a48dc099ed86cf.css +1 -0
  186. package/apps/pwa/out/_next/static/{aBi6EyMtLsrl7J8eH4kF7 → rfkMm4movqkqMebBrCXiR}/_buildManifest.js +1 -1
  187. package/apps/pwa/out/approvals/index.html +1 -1
  188. package/apps/pwa/out/approvals/index.txt +23 -23
  189. package/apps/pwa/out/autopilot/index.html +1 -1
  190. package/apps/pwa/out/autopilot/index.txt +23 -23
  191. package/apps/pwa/out/board/index.html +1 -1
  192. package/apps/pwa/out/board/index.txt +23 -23
  193. package/apps/pwa/out/botlab/index.html +1 -1
  194. package/apps/pwa/out/botlab/index.txt +22 -22
  195. package/apps/pwa/out/bots/index.html +1 -1
  196. package/apps/pwa/out/bots/index.txt +22 -22
  197. package/apps/pwa/out/ceo/index.html +1 -1
  198. package/apps/pwa/out/ceo/index.txt +23 -23
  199. package/apps/pwa/out/chat/index.html +1 -1
  200. package/apps/pwa/out/chat/index.txt +22 -22
  201. package/apps/pwa/out/consult/index.html +1 -1
  202. package/apps/pwa/out/consult/index.txt +23 -23
  203. package/apps/pwa/out/control/index.html +1 -1
  204. package/apps/pwa/out/control/index.txt +23 -23
  205. package/apps/pwa/out/decisions/index.html +1 -1
  206. package/apps/pwa/out/decisions/index.txt +22 -22
  207. package/apps/pwa/out/design-check/index.html +1 -1
  208. package/apps/pwa/out/design-check/index.txt +23 -23
  209. package/apps/pwa/out/editor/index.html +1 -1
  210. package/apps/pwa/out/editor/index.txt +24 -23
  211. package/apps/pwa/out/exec/index.html +1 -1
  212. package/apps/pwa/out/exec/index.txt +23 -23
  213. package/apps/pwa/out/field/index.html +1 -1
  214. package/apps/pwa/out/field/index.txt +23 -23
  215. package/apps/pwa/out/healthz +1 -1
  216. package/apps/pwa/out/index.html +1 -1
  217. package/apps/pwa/out/index.txt +22 -22
  218. package/apps/pwa/out/inside/index.html +1 -1
  219. package/apps/pwa/out/inside/index.txt +22 -22
  220. package/apps/pwa/out/intake/index.html +1 -1
  221. package/apps/pwa/out/intake/index.txt +23 -23
  222. package/apps/pwa/out/live/index.html +1 -1
  223. package/apps/pwa/out/live/index.txt +23 -23
  224. package/apps/pwa/out/live-run/index.html +1 -1
  225. package/apps/pwa/out/live-run/index.txt +24 -23
  226. package/apps/pwa/out/loops/index.html +1 -1
  227. package/apps/pwa/out/loops/index.txt +22 -22
  228. package/apps/pwa/out/market/index.html +1 -1
  229. package/apps/pwa/out/market/index.txt +22 -22
  230. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  231. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  232. package/apps/pwa/out/morning/index.html +1 -1
  233. package/apps/pwa/out/morning/index.txt +23 -23
  234. package/apps/pwa/out/observatory/index.html +1 -1
  235. package/apps/pwa/out/observatory/index.txt +23 -23
  236. package/apps/pwa/out/ops/checklist/index.html +1 -1
  237. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  238. package/apps/pwa/out/ops/release/index.html +1 -1
  239. package/apps/pwa/out/ops/release/index.txt +22 -22
  240. package/apps/pwa/out/ops/seats/index.html +1 -1
  241. package/apps/pwa/out/ops/seats/index.txt +22 -22
  242. package/apps/pwa/out/outputs/index.html +1 -1
  243. package/apps/pwa/out/outputs/index.txt +22 -22
  244. package/apps/pwa/out/reflection/index.html +1 -1
  245. package/apps/pwa/out/reflection/index.txt +23 -23
  246. package/apps/pwa/out/scheduler/index.html +1 -1
  247. package/apps/pwa/out/scheduler/index.txt +23 -23
  248. package/apps/pwa/out/sessions/index.html +1 -1
  249. package/apps/pwa/out/sessions/index.txt +22 -22
  250. package/apps/pwa/out/settings/devices/index.html +1 -1
  251. package/apps/pwa/out/settings/devices/index.txt +23 -23
  252. package/apps/pwa/out/settings/index.html +1 -1
  253. package/apps/pwa/out/settings/index.txt +23 -23
  254. package/apps/pwa/out/setup/done/index.html +1 -1
  255. package/apps/pwa/out/setup/done/index.txt +23 -23
  256. package/apps/pwa/out/setup/index.html +1 -1
  257. package/apps/pwa/out/setup/index.txt +23 -23
  258. package/apps/pwa/out/share/index.html +1 -1
  259. package/apps/pwa/out/share/index.txt +23 -23
  260. package/apps/pwa/out/showroom/index.html +1 -1
  261. package/apps/pwa/out/showroom/index.txt +22 -22
  262. package/apps/pwa/out/tasks/index.html +1 -1
  263. package/apps/pwa/out/tasks/index.txt +23 -23
  264. package/apps/pwa/out/term/index.html +1 -1
  265. package/apps/pwa/out/term/index.txt +23 -23
  266. package/apps/pwa/out/today/index.html +1 -1
  267. package/apps/pwa/out/today/index.txt +23 -23
  268. package/apps/pwa/out/trace/index.html +1 -1
  269. package/apps/pwa/out/trace/index.txt +23 -23
  270. package/apps/pwa/out/vault/index.html +1 -1
  271. package/apps/pwa/out/vault/index.txt +23 -23
  272. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  273. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  274. package/apps/pwa/out/workflows/index.html +1 -1
  275. package/apps/pwa/out/workflows/index.txt +23 -23
  276. package/apps/pwa/out/workspace/index.html +1 -1
  277. package/apps/pwa/out/workspace/index.txt +23 -23
  278. package/apps/pwa/out/worktrees/index.html +1 -1
  279. package/apps/pwa/out/worktrees/index.txt +23 -23
  280. package/catalog/models/anthropic/claude-haiku-5-5.yaml +32 -0
  281. package/catalog/models/anthropic/claude-opus-5-5.yaml +5 -0
  282. package/catalog/models/anthropic/claude-sonnet-5-5.yaml +31 -0
  283. package/catalog/models/anthropic/claude-sonnet-5.yaml +8 -2
  284. package/catalog/models/openai/gpt-6-astra.yaml +6 -0
  285. package/catalog/models/openai/gpt-6-luna.yaml +6 -0
  286. package/catalog/models/openai/gpt-6-sol.yaml +6 -0
  287. package/graphs/log-horizon/log-horizon-watch.yaml +25 -0
  288. package/graphs/log-horizon/recipes.yaml +3 -0
  289. package/graphs/release/recipes.yaml +1 -1
  290. package/graphs/release/release-loop.yaml +6 -2
  291. package/graphs/release-rehearsal/recipes.yaml +12 -0
  292. package/graphs/release-rehearsal/release-rehearsal.yaml +17 -0
  293. package/package.json +4 -1
  294. package/scripts/bench-report.ts +82 -3
  295. package/scripts/ci-typecheck-changed.ts +10 -1
  296. package/scripts/hq/hq-fence-wrapper.sh +148 -0
  297. package/scripts/lib/typecheck-ratchet.ts +51 -0
  298. package/scripts/openai-relay-server.ts +5 -1
  299. package/scripts/release-loop/docs-node.ts +258 -43
  300. package/scripts/release-loop/gate-method.ts +59 -0
  301. package/scripts/release-loop/gate-node.ts +73 -7
  302. package/scripts/release-loop/gate-wall.ts +208 -0
  303. package/scripts/release-loop/known-issues-node.ts +53 -8
  304. package/scripts/release-loop/publish-at.ts +14 -0
  305. package/scripts/release-loop/publish-node.ts +17 -2
  306. package/scripts/release-loop/resume-release.ts +34 -4
  307. package/scripts/release-loop/resume.ts +53 -0
  308. package/scripts/release-loop/shard-plan.ts +3 -11
  309. package/scripts/release-loop/tui-node-checks.ts +22 -0
  310. package/scripts/release-loop/unattended-release.ts +8 -1
  311. package/scripts/rhythm/daily.ts +51 -2
  312. package/scripts/rhythm/weekly.ts +3 -2
  313. package/scripts/tsc-main-safety-net.ts +189 -0
  314. package/scripts/usage-rollup.ts +8 -4
  315. package/src/acp/codex-plugins.ts +48 -6
  316. package/src/agent/monad-agent-turn.ts +67 -5
  317. package/src/agent/run-context.ts +10 -0
  318. package/src/agent/tool-profile.ts +28 -2
  319. package/src/agent-mission/acp-judge.ts +8 -2
  320. package/src/agent-mission/capabilities-cli.ts +16 -1
  321. package/src/agent-mission/driver.ts +1 -1
  322. package/src/agent-mission/pr-review-watch.ts +6 -2
  323. package/src/agent-mission/review-loop.ts +33 -5
  324. package/src/agent-substrate/acp-reviewer.ts +17 -8
  325. package/src/agent-substrate/pr-reviewer.ts +164 -28
  326. package/src/agent-substrate/review-intent.ts +6 -4
  327. package/src/agent-substrate/self-review-cli.ts +111 -16
  328. package/src/agent-substrate/shard-boundary-intent.ts +92 -26
  329. package/src/ask-user-question/pending-questions.ts +8 -2
  330. package/src/autopilot/build/llm-conflict-merge.ts +216 -12
  331. package/src/autopilot/codebase-grounding.ts +44 -0
  332. package/src/autopilot/llm-control-brain.ts +13 -1
  333. package/src/autopilot/mission-codebase-gate.ts +8 -77
  334. package/src/autopilot/pr-manager.ts +12 -3
  335. package/src/boot/acp-server.ts +4 -7
  336. package/src/boot/acp-socket-path.ts +7 -0
  337. package/src/briefing/brief-items.ts +21 -13
  338. package/src/chat/index.ts +12 -3
  339. package/src/cli/agent-cli.ts +20 -0
  340. package/src/cli/brief-cli.ts +27 -21
  341. package/src/cli/cli-user-error.ts +9 -2
  342. package/src/cli/log-event-names.ts +3 -0
  343. package/src/cli/logs-cli.ts +1 -0
  344. package/src/cli/mcp-login.ts +111 -37
  345. package/src/cli/plugin-cli.ts +21 -1
  346. package/src/cli/pod-cli.ts +26 -0
  347. package/src/cli/pr-cli.ts +30 -2
  348. package/src/cli/pr-land-overlap-evidence.ts +131 -0
  349. package/src/cli/release-cli.ts +53 -11
  350. package/src/cli/self-update.ts +173 -10
  351. package/src/cli/skills-cli.ts +12 -3
  352. package/src/cli/tasks-cli.ts +133 -27
  353. package/src/context-bus/context-first.ts +51 -2
  354. package/src/context-bus/context-now-surfaces.ts +13 -2
  355. package/src/context-bus/long-term-memory.ts +3 -1
  356. package/src/context-bus/nightly-condense.ts +3 -2
  357. package/src/core-turn/run-goal-loop.ts +86 -2
  358. package/src/dashboard/chat-fold.ts +20 -0
  359. package/src/dashboard/first-screen-band.ts +151 -1
  360. package/src/dashboard/index.ts +27 -5
  361. package/src/dashboard/slash-runtime/dashboard-handlers.ts +5 -3
  362. package/src/dashboard/stale-install-check.ts +13 -2
  363. package/src/dashboard/turn-stream-formatter.ts +25 -0
  364. package/src/debug/log-diet.ts +159 -0
  365. package/src/debug/log.ts +52 -17
  366. package/src/display/coordinator.ts +25 -2
  367. package/src/domains/coo-admin-tool.ts +27 -0
  368. package/src/domains/cron-match.ts +1 -0
  369. package/src/domains/outbound-alert.ts +17 -6
  370. package/src/domains/release-tool.ts +119 -10
  371. package/src/domains/schedule-registry.ts +10 -7
  372. package/src/domains/schedule-runner.ts +1 -0
  373. package/src/elanous-config-dir.ts +2 -2
  374. package/src/elanous-daemon.ts +3 -3
  375. package/src/execution-loop/launch-gate.ts +1 -1
  376. package/src/expression/i18n/messages.en.ts +5 -0
  377. package/src/expression/i18n/messages.ja.ts +5 -0
  378. package/src/expression/i18n/messages.ko.ts +5 -0
  379. package/src/expression/i18n/messages.zh.ts +5 -0
  380. package/src/expression/i18n/types.ts +8 -0
  381. package/src/github/pr-labels.ts +1 -0
  382. package/src/graph-runner/runner.ts +54 -5
  383. package/src/harness/dispatch-backpressure.ts +259 -0
  384. package/src/harness/harness-cli-command.ts +107 -28
  385. package/src/harness/harness-seams.ts +2 -20
  386. package/src/harness/harness-substrate-default.ts +72 -1
  387. package/src/harness/nested-elanous-policy.ts +4 -2
  388. package/src/hq/fence-wrapper.ts +7 -0
  389. package/src/hq/hq.ts +33 -2
  390. package/src/hq/lease.ts +13 -0
  391. package/src/hq/seats.ts +114 -18
  392. package/src/index.ts +163 -64
  393. package/src/intake-plane/collect-github.ts +19 -1
  394. package/src/intake-plane/digest.ts +45 -20
  395. package/src/intake-plane/lens.ts +36 -8
  396. package/src/intelligence-map/model-alias.ts +6 -0
  397. package/src/intelligence-map/model-catalog.ts +41 -0
  398. package/src/knowledge/kgs/pack.ts +5 -2
  399. package/src/knowledge/kgs/sqlite-store.ts +15 -0
  400. package/src/knowledge/tools/knowledge-query.ts +20 -2
  401. package/src/knowledge/write.ts +234 -2
  402. package/src/llm/bedrock-provider.ts +175 -0
  403. package/src/llm/bedrock.ts +345 -0
  404. package/src/llm/provider-credentials.ts +3 -1
  405. package/src/llm.ts +306 -35
  406. package/src/loops/budget.ts +150 -0
  407. package/src/loops/orchestrator/seat-share.ts +6 -49
  408. package/src/loops/orchestrator/traffic.ts +5 -81
  409. package/src/market/signed-index.ts +42 -0
  410. package/src/mcp/mcp-oauth.ts +136 -17
  411. package/src/model-tier/llm-tier-map.ts +48 -4
  412. package/src/models/prompts.ts +22 -1
  413. package/src/mss/logging/horizon-watch.ts +168 -0
  414. package/src/mss/logging/log-store.ts +204 -15
  415. package/src/native-tool-catalog.ts +1 -1
  416. package/src/nexus/api/consult-requests.ts +7 -10
  417. package/src/nexus/api/discord-trigger-bot.ts +6 -3
  418. package/src/nexus/api/error-reports.ts +30 -8
  419. package/src/nexus/api/http-server.ts +8 -0
  420. package/src/nexus/api/loop-resources.ts +42 -0
  421. package/src/nexus/api/outbound-report.ts +8 -3
  422. package/src/nexus/api/telegram-trigger-bot.ts +6 -3
  423. package/src/nexus/index.ts +10 -0
  424. package/src/nexus/outbound/format.ts +2 -0
  425. package/src/nexus/outbound/router.ts +198 -13
  426. package/src/oauth/codex-account-rotation.ts +16 -18
  427. package/src/oauth/codex-account-store.ts +134 -5
  428. package/src/oauth/fallback-chain.ts +28 -6
  429. package/src/onboarding.ts +2 -1
  430. package/src/plugins/adapters/claude-package.ts +4 -12
  431. package/src/plugins/core/capability-policy.ts +141 -0
  432. package/src/plugins/core/host.ts +21 -8
  433. package/src/plugins/core/manifest.ts +19 -2
  434. package/src/plugins/core/trust-store.ts +15 -0
  435. package/src/plugins/core/types.ts +12 -0
  436. package/src/plugins/install/market-fetch.ts +68 -16
  437. package/src/plugins/install/plugin-install.ts +187 -16
  438. package/src/plugins/maker/plugin-maker.ts +25 -20
  439. package/src/plugins/maker/wizard-generate.ts +16 -2
  440. package/src/policy/llm-policy.ts +14 -1
  441. package/src/process/pid-liveness.ts +1 -1
  442. package/src/prompt-cache/openai.ts +10 -6
  443. package/src/prompt-cache/openrouter.ts +94 -0
  444. package/src/prompt-enhance/enhance.ts +46 -5
  445. package/src/prompt-library/family-prompt-coverage.ts +1 -0
  446. package/src/prompt-library/universal-preamble.ts +2 -2
  447. package/src/registry/discovery/openrouter-live-resolve.ts +122 -0
  448. package/src/registry/loader.ts +6 -1
  449. package/src/release-loop/checklist.ts +24 -9
  450. package/src/release-loop/feature-store.ts +3 -0
  451. package/src/release-loop/gate-shards.ts +48 -4
  452. package/src/release-loop/landed-to-green.ts +81 -0
  453. package/src/release-loop/light-rc.ts +11 -1
  454. package/src/release-loop/merged-pr-checklist.ts +10 -0
  455. package/src/release-loop/placement.ts +91 -22
  456. package/src/release-loop/rehearsal.ts +317 -0
  457. package/src/release-loop/release-schedule.ts +13 -1
  458. package/src/release-loop/rubric-types.ts +1 -0
  459. package/src/release-loop/rubric.ts +2 -2
  460. package/src/retro/daily.ts +23 -9
  461. package/src/seat-loop/seat-loop.ts +34 -1
  462. package/src/seat-loop/traffic-signal.ts +44 -0
  463. package/src/self-dev/ask-launch-flow.ts +2 -2
  464. package/src/self-dev/dev-cli.ts +67 -3
  465. package/src/self-dev/dev-pipeline.ts +4 -6
  466. package/src/self-dev/draft-residue.ts +463 -0
  467. package/src/self-dev/draft-sweep.ts +151 -10
  468. package/src/self-dev/draft-triage-rules.ts +27 -1
  469. package/src/self-dev/launch-preflight-text.ts +549 -0
  470. package/src/self-dev/launch-preflight.ts +10 -545
  471. package/src/self-dev/orchestrate-cli.ts +57 -2
  472. package/src/self-dev/orchestrate-summary-line.ts +13 -0
  473. package/src/self-dev/orchestrate.ts +8 -135
  474. package/src/self-dev/review-provider-fallback.ts +115 -19
  475. package/src/self-dev/run-store.ts +1 -3
  476. package/src/self-dev/run-supervisor.ts +3 -22
  477. package/src/self-dev/run-types.ts +178 -0
  478. package/src/self-dev/shard-aggregate.ts +61 -13
  479. package/src/self-dev/shard-plan.ts +5 -1
  480. package/src/self-dev/shard-run.ts +23 -5
  481. package/src/self-dev/supervisor-verdict-edges.ts +2 -4
  482. package/src/self-implement/blocked-draft-policy.ts +3 -19
  483. package/src/self-implement/draft-on-stop-mode.ts +18 -0
  484. package/src/self-implement/follow-up-goals.ts +114 -38
  485. package/src/self-implement/gate-baseline-reap.ts +157 -0
  486. package/src/self-implement/gate-baseline.ts +78 -16
  487. package/src/self-implement/gate-cli.ts +12 -2
  488. package/src/self-implement/gate-remote.ts +17 -6
  489. package/src/self-implement/goal-author.ts +24 -4
  490. package/src/self-implement/goal-digest.ts +19 -0
  491. package/src/self-implement/graph-overlay-condition.ts +4 -0
  492. package/src/self-implement/headless-elanous-driver.ts +11 -9
  493. package/src/self-implement/host-regate.ts +120 -29
  494. package/src/self-implement/l8-merge-queue.ts +67 -5
  495. package/src/self-implement/merge-train.ts +131 -0
  496. package/src/self-implement/off-diff-evidence.ts +9 -0
  497. package/src/self-implement/orchestrator.ts +133 -16
  498. package/src/self-implement/review-finding-recurrence.ts +28 -0
  499. package/src/self-implement/rework-policy.ts +2 -1
  500. package/src/self-implement/round-summary-card.ts +63 -0
  501. package/src/self-implement/run-ledger.ts +4 -3
  502. package/src/self-implement/run-step-projection.ts +147 -0
  503. package/src/self-implement/run-stop.ts +1 -1
  504. package/src/self-implement/seams.ts +36 -3
  505. package/src/self-implement/self-implement-cli.ts +3 -1
  506. package/src/self-implement/train-gate-remote.ts +158 -0
  507. package/src/session-runtime/index.ts +10 -1
  508. package/src/session-runtime/retry-api.ts +11 -1
  509. package/src/skills/gift-install.ts +119 -0
  510. package/src/skills/runner.ts +59 -2
  511. package/src/skills/skill-feedback.ts +31 -1
  512. package/src/skills/skill-tier.ts +1 -0
  513. package/src/smart-wizard/capability-index.ts +231 -0
  514. package/src/smart-wizard/rung.ts +74 -0
  515. package/src/steward/launch.ts +20 -9
  516. package/src/steward/triage.ts +4 -2
  517. package/src/storage/file-lock.ts +8 -1
  518. package/src/task-agent/actions.ts +4 -0
  519. package/src/task-agent/card-evidence.ts +59 -13
  520. package/src/task-agent/judge.ts +4 -3
  521. package/src/task-agent/land-worktree.ts +201 -0
  522. package/src/task-agent/live-move-history.ts +133 -0
  523. package/src/task-agent/live-moves.ts +106 -19
  524. package/src/task-agent/parent-host.ts +117 -8
  525. package/src/task-agent/rejudge-on-head.ts +297 -0
  526. package/src/task-agent/review-result-capture.ts +218 -0
  527. package/src/task-agent/shadow.ts +103 -7
  528. package/src/task-agent/task-hand.ts +136 -11
  529. package/src/task-orchestrator/surfaces/pod-child-providers.ts +29 -0
  530. package/src/task-orchestrator/surfaces/pod-command-job.ts +6 -3
  531. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +16 -2
  532. package/src/task-orchestrator/surfaces/pod-image-source.ts +212 -0
  533. package/src/task-orchestrator/surfaces/pod-lease.ts +93 -12
  534. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +25 -2
  535. package/src/task-orchestrator/surfaces/pod-pool.ts +66 -14
  536. package/src/task-orchestrator/surfaces/self-implement-pod.ts +259 -36
  537. package/src/task-orchestrator/surfaces/self-implement.ts +6 -2
  538. package/src/telegram-service.ts +5 -1
  539. package/src/telegram.ts +17 -4
  540. package/src/time/format.ts +13 -4
  541. package/src/tool-runtime/goal-author-runtime.ts +1 -0
  542. package/src/tool-runtime/self-cognition-catalog.ts +23 -0
  543. package/src/tool-runtime/self-cognition-runtimes.ts +2 -24
  544. package/src/user-config.ts +132 -24
  545. package/src/ux-sim/slash-menu-frames.ts +168 -0
  546. package/src/version/packed-revision.json +1 -1
  547. package/src/web-terminal/preview-tap-registry.ts +5 -2
  548. package/src/workflow-runtime/triggers/scheduler.ts +1 -0
  549. package/apps/pwa/out/_next/static/chunks/1109.78a7a3288c0259d9.js +0 -148
  550. package/apps/pwa/out/_next/static/chunks/115.1c6690b8604f81c4.js +0 -32
  551. package/apps/pwa/out/_next/static/chunks/1262.76a3f08e67c5f816.js +0 -309
  552. package/apps/pwa/out/_next/static/chunks/1307-08b716476f1d50af.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/1358d292.5c87f12cad6eaa91.js +0 -122
  554. package/apps/pwa/out/_next/static/chunks/1438.27a1a7b8f2308004.js +0 -149
  555. package/apps/pwa/out/_next/static/chunks/1442.02d85cd88f4c1eac.js +0 -79
  556. package/apps/pwa/out/_next/static/chunks/1501-1aad662d82ba43f7.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/1584.9e2dd53da6e6d4b1.js +0 -24
  558. package/apps/pwa/out/_next/static/chunks/1586.b989b0614afb77e6.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/1609.2ad355062870151f.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/1612-f6df0fc0eed7157b.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/1923.37f1d5fe7ebe3860.js +0 -27
  563. package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/1993.3fd074a2a246234e.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/211-9c9090c59e2c5907.js +0 -1
  567. package/apps/pwa/out/_next/static/chunks/2140.d2daf9ee96e295d1.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/2212.0359851457473efc.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/224.583e562365a52f74.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/2328.0f37465646bafa7d.js +0 -36
  572. package/apps/pwa/out/_next/static/chunks/2372.bf71372184991937.js +0 -286
  573. package/apps/pwa/out/_next/static/chunks/2489-d238d3e2eb5b2bf5.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/2549-a8515c86f9e8cb96.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/2655.72cc6a97276c5acc.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/2743-ef6e8c3ced74cce5.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/2840-5458ad50c49cac6a.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/2856.f75d5baef7f49b74.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +0 -1
  583. package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/3344.0de4b0ec5a454b7a.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/335.a13f339e97893437.js +0 -94
  586. package/apps/pwa/out/_next/static/chunks/3394.646348f34b2500be.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/3443.6276909b53d72d53.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/3495.abd4254d808bf2dc.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/3721.ba5b82db0d9a5572.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/3857-5985236d9307f676.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/3919.9ff7dc25afefee33.js +0 -74
  594. package/apps/pwa/out/_next/static/chunks/3998.bd9a69d9b50e9bd2.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/4003.ab6db1dc266576c9.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/4144-fad095b48a1862ee.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/4216.e8ac94b9fe6b96ab.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/4327.c44dc2b55cb43ee5.js +0 -34
  599. package/apps/pwa/out/_next/static/chunks/4401.94257d497b6d26bc.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/4559.17ca2cdbaa8d414f.js +0 -115
  601. package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/4706.97e734bd4c9816ca.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/4795-f372490b178583fc.js +0 -1
  604. package/apps/pwa/out/_next/static/chunks/4982.6e44352f751b9a1b.js +0 -77
  605. package/apps/pwa/out/_next/static/chunks/5342.49c5c5a530e62b50.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/5360-08a80abdf3a4da99.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/5431.66c58ab1327ee0b7.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/5633-d6852385000738ef.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/5717.bcfcb96e701b82e0.js +0 -90
  610. package/apps/pwa/out/_next/static/chunks/5755-30d102586789bf7f.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/5957.3f1a4012388dd962.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/5977-5b9e8a45a5b0c823.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/602.9bf02ac8e9502598.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/6020.3a1b347e2d2dfacc.js +0 -8
  615. package/apps/pwa/out/_next/static/chunks/6375.fce1d174ba6d0324.js +0 -64
  616. package/apps/pwa/out/_next/static/chunks/6546.92a828aef441271f.js +0 -1
  617. package/apps/pwa/out/_next/static/chunks/658.957fdb0a8c6c6c8e.js +0 -287
  618. package/apps/pwa/out/_next/static/chunks/6706.323590aabb8b1293.js +0 -226
  619. package/apps/pwa/out/_next/static/chunks/6747.b67947f293a4c02d.js +0 -1
  620. package/apps/pwa/out/_next/static/chunks/6868.000b24a3045c5574.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/6946.bba74e9c96050f30.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/7033.a558f1b2f6912a53.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/7065.46029c26c53eac50.js +0 -1
  625. package/apps/pwa/out/_next/static/chunks/7087.6fae577d09249bc1.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/722.de8d9801e0f6d414.js +0 -41
  627. package/apps/pwa/out/_next/static/chunks/726.83532d69452ee24b.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +0 -1
  629. package/apps/pwa/out/_next/static/chunks/7497.57981e9d7a75a52a.js +0 -24
  630. package/apps/pwa/out/_next/static/chunks/7672.4af122ad892117c7.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/771.6ab889a233e7dae7.js +0 -1
  632. package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/8096.f7da0776cfde6300.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/8401-35e94e9091f5eb8d.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/8769.815ca69879f608b4.js +0 -104
  640. package/apps/pwa/out/_next/static/chunks/8857-aa1cc4ac90af4d40.js +0 -1
  641. package/apps/pwa/out/_next/static/chunks/8f835b4f.048eeb02299e3e12.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/9068.5f2764fccf86c1b2.js +0 -93
  643. package/apps/pwa/out/_next/static/chunks/9103-60fbef33e66f0a46.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/9247-55bcfbb0e8057f59.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/935.4ddaa99fc1d1126c.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/9451.ebfb0dcc9c02250f.js +0 -23
  647. package/apps/pwa/out/_next/static/chunks/9559.e648369b1d57cf9a.js +0 -62
  648. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/app/approvals/page-88459e0903da2803.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-d85c43f46f58716b.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/app/board/page-4e645600579ae952.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/app/botlab/page-87edbfd19117823b.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/app/bots/page-7f0c2b7801ac6a54.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/app/ceo/page-bf1f030a9d1adffd.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/app/chat/page-cbaf66b1e1804adf.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/app/control/page-9f5610caaa06178c.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/app/decisions/page-ec8f3575e339de74.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/app/design-check/page-86d753e460f1a989.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/app/editor/page-4166a7d51cf0a57d.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/app/exec/page-2a19c5cd3b562580.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/app/field/page-b6a09cde2e07521a.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/app/inside/page-f7eae1518ca0c353.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/app/intake/page-456a8573ad811381.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/app/layout-8462d6a543cbb2d9.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/app/live/page-2974cdfb87c0f9c9.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/app/live-run/page-f927ffa25f3c86d1.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/app/loops/page-abd86cd832aaa011.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/app/market/page-79daf68f6256adaa.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8c3f168fc3f91939.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/app/morning/page-786fb4504d0f2fa9.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/app/not-found-75209f14597dcb8f.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/app/observatory/page-bf30affe3a85af7e.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1187a44b7515434b.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-3d0910185a66ed54.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f401ee1dd5a26e25.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/app/outputs/page-5dfe7604fd842784.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/app/page-76cf838050a8d681.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/app/reflection/page-7ec8e289b4c14df6.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-8c8ac17d52be2a79.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/app/sessions/page-d359ab048d63a9d9.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-b8b345c880bf78d1.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/app/settings/page-f151cc10ad026940.js +0 -1
  684. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-9875f103057da433.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/app/setup/page-9789328af9a9aa62.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/app/share/page-bf495a1e284f4e00.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/app/showroom/page-a263d94bca4be22a.js +0 -1
  689. package/apps/pwa/out/_next/static/chunks/app/tasks/page-3962eab8ec58d16e.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/app/term/page-9abcfac07e02a542.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/app/today/page-bee49f927c333abb.js +0 -1
  692. package/apps/pwa/out/_next/static/chunks/app/trace/page-bac2d56dcb02d955.js +0 -1
  693. package/apps/pwa/out/_next/static/chunks/app/vault/page-937cbcc13176295c.js +0 -1
  694. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d09495594f7f480d.js +0 -1
  695. package/apps/pwa/out/_next/static/chunks/app/workflows/page-69cb93d1a2e57e0e.js +0 -1
  696. package/apps/pwa/out/_next/static/chunks/app/workspace/page-72ec543928d67c3e.js +0 -1
  697. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-82906eeda0186a7b.js +0 -1
  698. package/apps/pwa/out/_next/static/chunks/f6231284-45726550aba08fca.js +0 -1
  699. package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +0 -1
  701. package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +0 -1
  703. package/apps/pwa/out/_next/static/chunks/webpack-f2b4016b9b981bc8.js +0 -1
  704. package/apps/pwa/out/_next/static/css/f2bd5257e6af88d8.css +0 -1
  705. /package/apps/pwa/out/_next/static/{aBi6EyMtLsrl7J8eH4kF7 → rfkMm4movqkqMebBrCXiR}/_ssgManifest.js +0 -0
package/src/cli/pr-cli.ts CHANGED
@@ -57,6 +57,7 @@ import { branchLineageSlug, findSiblingPrs } from './pr-lineage.js';
57
57
  import { parseReleaseNoteSection } from '../release-loop/release-note.js';
58
58
  import { landingFreezeMessage } from '../release-loop/landing-freeze.js';
59
59
  import { admitLandingMerge } from '../self-implement/frozen-merges.js';
60
+ import { verifyOverlapEvidence } from './pr-land-overlap-evidence.js';
60
61
  import { enqueueL8ShadowQueue, runL8ShadowQueue, shadowPrNumber, statusL8ShadowQueue, type L8ShadowQueueDeps } from '../self-implement/l8-merge-queue.js';
61
62
 
62
63
  export { branchLineageSlug, findSiblingPrs };
@@ -83,6 +84,8 @@ export interface PrLandOpts {
83
84
  forceFreeze?: string;
84
85
  /** Land even though changed files would leak into the public export (the reason goes in --land-reason). */
85
86
  allowPublicLeak?: boolean;
87
+ /** Pinned landing only: JSON evidence (rebased · re-gated · reviewed head) that replaces the unavailable non-interactive overlap approval. */
88
+ overlapEvidence?: string;
86
89
  }
87
90
 
88
91
  export interface PrGranularityOpts {
@@ -998,6 +1001,7 @@ function emitLandingOverlapAdvisory(
998
1001
  base: string,
999
1002
  currentFiles: readonly string[],
1000
1003
  out: { log: (message: string) => void },
1004
+ overlappingLandings: string[] = [],
1001
1005
  ): boolean {
1002
1006
  if (currentFiles.length === 0) {
1003
1007
  record('overlap', true, { outcome: 'no-changed-files' });
@@ -1009,6 +1013,8 @@ function emitLandingOverlapAdvisory(
1009
1013
  return false;
1010
1014
  }
1011
1015
  const overlaps = overlapWithCurrentChanges(commits, currentFiles);
1016
+ const current = new Set(currentFiles);
1017
+ overlappingLandings.push(...commits.filter((commit) => commit.files.some((file) => current.has(file))).map((commit) => commit.hash));
1012
1018
  const line = formatOverlapAdvisory(overlaps);
1013
1019
  if (line) {
1014
1020
  record('overlap', true, {
@@ -1184,6 +1190,10 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
1184
1190
  const cwd = opts.cwd ?? process.cwd();
1185
1191
  const run = deps.run ?? defaultCmdRunner;
1186
1192
  const pinned = opts.pr !== undefined || opts.expectedHead !== undefined;
1193
+ if (opts.overlapEvidence !== undefined && (!pinned || !opts.overlapEvidence.trim())) {
1194
+ out.error('✗ overlap-evidence: --overlap-evidence <file> 는 --pr ⊕ --expected-head 머리 고정 착지에서만 쓴다.');
1195
+ return 1;
1196
+ }
1187
1197
  let pinnedBranch: string | undefined;
1188
1198
  let pinnedUrl: string | undefined;
1189
1199
  if (pinned) {
@@ -1562,7 +1572,8 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
1562
1572
  return 1;
1563
1573
  }
1564
1574
 
1565
- const overlapFound = emitLandingOverlapAdvisory(run, cwd, base, changedPaths, out);
1575
+ const overlappingLandings: string[] = [];
1576
+ const overlapFound = emitLandingOverlapAdvisory(run, cwd, base, changedPaths, out, overlappingLandings);
1566
1577
  const siblingFound = emitSiblingPrAdvisory(
1567
1578
  branch,
1568
1579
  deps.listOpenPrs ?? (() => lookupOpenPrs(run, cwd)),
@@ -1593,7 +1604,23 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
1593
1604
  });
1594
1605
  }
1595
1606
  const hold = !!opts.hold || overlapDecision?.outcome === 'user-hold';
1596
- if (pinned && overlapDecision && overlapDecision.outcome !== 'user-merge') {
1607
+ // TA-LAND-OVERLAP — 비대화형 «승인 불가»만 증거로 대신한다(사람의 hold 는 증거로 뒤집지 않는다) · 증거가 없거나 어긋나면 종전 거부 그대로.
1608
+ let overlapEvidenceAccepted = false;
1609
+ if (pinned && overlapDecision?.outcome === 'unavailable' && opts.overlapEvidence !== undefined) {
1610
+ const ref = resolveRemoteBranchRef(run, cwd, base);
1611
+ const verdict = verifyOverlapEvidence({
1612
+ evidencePath: opts.overlapEvidence, pr: Number(opts.pr), expectedHead: opts.expectedHead!,
1613
+ remote: ref.remote, branch: ref.branch, cwd, run, readFile, overlappingLandings,
1614
+ });
1615
+ overlapEvidenceAccepted = verdict.ok;
1616
+ if (verdict.ok) {
1617
+ out.log(verdict.mode === 'already-ancestor'
1618
+ ? `✓ overlap-evidence: already-ancestor — 겹친 최근 착지 ${overlappingLandings.length}개가 전부 head ${opts.expectedHead!.slice(0, 12)} 의 조상 · review pass must-fix 0 (${verdict.reviewResult})`
1619
+ : `✓ 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})`);
1620
+ }
1621
+ else out.error(`✗ overlap-evidence: ${verdict.reason}`);
1622
+ }
1623
+ if (pinned && overlapDecision && overlapDecision.outcome !== 'user-merge' && !overlapEvidenceAccepted) {
1597
1624
  out.error('✗ expected-head: overlap merge approval unavailable for the reviewed head');
1598
1625
  return 1;
1599
1626
  }
@@ -1852,6 +1879,7 @@ export function registerPrCommands(program: Command, deps: PrLandDeps = {}, queu
1852
1879
  .option('--land-reason <text>', '권고를 보고도 지금 내는 이유(예: 남이 기다리는 차단 해제)')
1853
1880
  .option('--force-freeze <reason>', '동결을 넘겨 병합할 명시적 이유(필수)')
1854
1881
  .option('--allow-public-leak', '바뀐 파일이 공개본에 사적 흔적을 실어도 착지(이유는 --land-reason)')
1882
+ .option('--overlap-evidence <file>', '머리 고정 착지 전용: 비대화형 겹침 승인을 대신할 증거 JSON {pr, head, gate:{head,baseCommit,passed}, reviewResult} — 재기반·재게이트·리뷰를 CLI 가 다시 잰다(겹친 착지가 전부 머리의 조상이면 리뷰만 · gate 생략 가능)')
1855
1883
  .action(async (opts: PrLandOpts) => {
1856
1884
  // ⛔⭐⭐⭐ **관측 sink 를 먼저 건다** — 라이브 도그푸드가 잡은 결함(2026-08-03).
1857
1885
  // 단위 테스트는 `debug.log` 가 **불렸다**를 단언하지만, standalone CLI 는 sink 를 등록하지
@@ -0,0 +1,131 @@
1
+ /**
2
+ * TA-LAND-OVERLAP — 머리 고정 착지(`pr land --pr --expected-head`)가 «1일 겹침» 관문에서 비대화형으로 영영 멎던 것을
3
+ * «증거가 붙은 경우에만» 통과시킨다.
4
+ *
5
+ * - 왜: 겹침 승인은 터미널 확인뿐이라 비대화형(TA)에서는 늘 `unavailable` → 머리 고정 착지는 «항상» 거부됐다.
6
+ * (10-10 #25845 · 10-11 #25974 가 같은 관문에 걸려 머리 고정을 풀고 착지했다.)
7
+ * - 무엇을: `--overlap-evidence <file>` 의 JSON 을 «믿지 않고» 잰다.
8
+ * ① 재기반 — 원격 base 의 «지금» 끝(`git ls-remote`)이 고정 머리의 조상인가(`git merge-base --is-ancestor`).
9
+ * ② 재게이트 — 증거의 게이트 기록이 «그 머리»를 «그 base 끝» 위에서 통과했다고 말하는가(머리·base 끝이 ① 과 맞물린다).
10
+ * ③ 리뷰 — 증거가 가리키는 `review-results/<card>-<pr>-<head12>.json`(TA 가 받는 `self review --json` stdout)이
11
+ * 같은 PR·같은 머리에 대해 `reviewed:true` · `verdict:pass` · must-fix 0 인가.
12
+ * ⓐ 이미 조상(TC #26019 실측) — 겹친 최근 착지 커밋이 «전부» 고정 머리의 조상이면 그 겹침은 이미 그 머리의 게이트·리뷰에
13
+ * 반영됐다 ⇒ ①② 없이 ③ 만으로 통과(관측 reason `already-ancestor`). 하나라도 조상이 아니면 ①②③ 전부.
14
+ * 하나라도 못 재거나 어긋나면 거부 — 호출부는 종전 거부를 그대로 낸다(fail-closed).
15
+ * - 관측: `pr.land` · `overlap-evidence` (decision · reason · 잰 값).
16
+ */
17
+ import { basename, dirname } from 'node:path';
18
+ import { debug } from '../debug/log.js';
19
+ import type { CmdRunner } from '../autopilot/pr-manager.js';
20
+ import { parseReviewResultOutput } from '../task-agent/review-result-capture.js';
21
+
22
+ const SHA40 = /^[0-9a-f]{40}$/i;
23
+
24
+ interface OverlapEvidenceFile {
25
+ pr: number;
26
+ head: string;
27
+ /** ①② 경로에서만 필수 — 겹친 착지가 전부 조상(already-ancestor)이면 생략해도 된다. */
28
+ gate?: { head: string; baseCommit: string; passed: boolean };
29
+ /** `self review --json` stdout 을 받은 파일(TA: `<state>/review-results/<card>-<pr>-<head12>.json`). */
30
+ reviewResult: string;
31
+ }
32
+
33
+ export type OverlapEvidenceVerdict =
34
+ | { ok: true; mode: 'already-ancestor'; reviewResult: string }
35
+ | { ok: true; mode: 'rebased'; baseTip: string; reviewResult: string }
36
+ | { ok: false; reason: string };
37
+
38
+ export interface VerifyOverlapEvidenceInput {
39
+ evidencePath: string;
40
+ pr: number;
41
+ expectedHead: string;
42
+ remote: string;
43
+ branch: string;
44
+ cwd: string;
45
+ run: CmdRunner;
46
+ readFile: (path: string) => string;
47
+ /** 겹침 관문이 찾은 «겹친» 최근 착지 커밋들 — 전부 머리의 조상이면 already-ancestor. */
48
+ overlappingLandings?: readonly string[];
49
+ }
50
+
51
+ type Checked = { ok: false; reason: string; baseTip?: string } | (OverlapEvidenceVerdict & { ok: true; baseTip?: string });
52
+
53
+ function checkReview(input: VerifyOverlapEvidenceInput, e: Partial<OverlapEvidenceFile>, head: string, baseTip?: string): { ok: false; reason: string; baseTip?: string } | { ok: true; reviewResult: string } {
54
+ if (typeof e.reviewResult !== 'string' || !e.reviewResult) return { ok: false, reason: 'review-missing', baseTip };
55
+ // TA 가 받는 자리(`<state>/review-results/<card>-<pr>-<head12>.json` · reviewResultPath)만 — 아무 디렉터리의 같은 이름은 안 받는다.
56
+ if (basename(dirname(e.reviewResult)) !== 'review-results'
57
+ || !new RegExp(`^[A-Za-z0-9][A-Za-z0-9._-]*-${input.pr}-${head.slice(0, 12)}\\.json$`, 'i').test(basename(e.reviewResult))) {
58
+ return { ok: false, reason: 'review-path-not-for-this-pr-head', baseTip };
59
+ }
60
+ let text: string;
61
+ try { text = input.readFile(e.reviewResult); } catch { return { ok: false, reason: 'review-unreadable', baseTip }; }
62
+ const review = parseReviewResultOutput(text);
63
+ if (!review) return { ok: false, reason: 'review-no-result', baseTip };
64
+ if (review.pr !== input.pr) return { ok: false, reason: 'review-pr-mismatch', baseTip };
65
+ if (!review.head || review.head.toLowerCase() !== head) return { ok: false, reason: 'review-head-mismatch', baseTip };
66
+ if (review.error) return { ok: false, reason: 'review-error', baseTip };
67
+ if (review.reviewed !== true) return { ok: false, reason: 'review-not-reviewed', baseTip };
68
+ if (review.verdict !== 'pass') return { ok: false, reason: `review-verdict-${review.verdict ?? 'null'}`, baseTip };
69
+ if (review.mustFix !== 0) return { ok: false, reason: review.mustFix === null ? 'review-must-fix-unknown' : `review-must-fix-${review.mustFix}`, baseTip };
70
+ return { ok: true, reviewResult: e.reviewResult };
71
+ }
72
+
73
+ function check(input: VerifyOverlapEvidenceInput): Checked {
74
+ const head = input.expectedHead.toLowerCase();
75
+ let raw: unknown;
76
+ try { raw = JSON.parse(input.readFile(input.evidencePath)); } catch (error) {
77
+ return { ok: false, reason: `evidence-unreadable: ${String((error as { message?: string })?.message ?? error).slice(0, 200)}` };
78
+ }
79
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return { ok: false, reason: 'evidence-not-object' };
80
+ const e = raw as Partial<OverlapEvidenceFile>;
81
+ if (e.pr !== input.pr) return { ok: false, reason: `evidence-pr-mismatch: ${String(e.pr)} ≠ ${input.pr}` };
82
+ if (typeof e.head !== 'string' || e.head.toLowerCase() !== head) return { ok: false, reason: 'evidence-head-mismatch' };
83
+
84
+ // ⓐ 이미 조상 — 겹친 착지가 전부 머리 안에 있으면 그 머리의 게이트·리뷰가 이미 그것을 보고 쟀다.
85
+ const landings = input.overlappingLandings ?? [];
86
+ if (landings.length > 0 && landings.every((sha) => /^[0-9a-f]{7,40}$/i.test(sha)
87
+ && input.run('git', ['merge-base', '--is-ancestor', sha, head], { cwd: input.cwd }).ok)) {
88
+ const review = checkReview(input, e, head);
89
+ return review.ok ? { ok: true, mode: 'already-ancestor', reviewResult: review.reviewResult } : review;
90
+ }
91
+
92
+ // ① 재기반 — 원격의 «지금» 끝을 읽기 전용으로 받는다(로컬 ref 를 안 바꾼다).
93
+ const remote = input.run('git', ['ls-remote', input.remote, `refs/heads/${input.branch}`], { cwd: input.cwd });
94
+ const baseTip = remote.ok ? remote.out.trim().split(/\s+/)[0] ?? '' : '';
95
+ if (!SHA40.test(baseTip)) return { ok: false, reason: `rebase-unmeasured: ls-remote ${input.remote} ${input.branch} failed` };
96
+ const ancestor = input.run('git', ['merge-base', '--is-ancestor', baseTip, head], { cwd: input.cwd });
97
+ if (!ancestor.ok) return { ok: false, reason: `not-rebased: ${input.remote}/${input.branch} ${baseTip.slice(0, 12)} is not an ancestor of ${head.slice(0, 12)}`, baseTip };
98
+
99
+ // ② 재게이트 — 그 머리를, ① 의 그 base 끝 위에서.
100
+ const gate = e.gate;
101
+ if (!gate || typeof gate !== 'object') return { ok: false, reason: 'gate-missing', baseTip };
102
+ if (gate.passed !== true) return { ok: false, reason: 'gate-not-passed', baseTip };
103
+ if (typeof gate.head !== 'string' || gate.head.toLowerCase() !== head) return { ok: false, reason: 'gate-head-mismatch', baseTip };
104
+ if (typeof gate.baseCommit !== 'string' || gate.baseCommit.toLowerCase() !== baseTip.toLowerCase()) {
105
+ return { ok: false, reason: `gate-base-stale: gate ${String(gate.baseCommit).slice(0, 12)} ≠ ${input.remote}/${input.branch} ${baseTip.slice(0, 12)}`, baseTip };
106
+ }
107
+
108
+ // ③ 리뷰 — 그 PR · 그 머리의 pass ⊕ must-fix 0.
109
+ const review = checkReview(input, e, head, baseTip);
110
+ return review.ok ? { ok: true, mode: 'rebased', baseTip, reviewResult: review.reviewResult } : review;
111
+ }
112
+
113
+ /** 던지지 않는다 — 어떤 실패도 거부(fail-closed)이고 관측 한 줄을 남긴다. */
114
+ export function verifyOverlapEvidence(input: VerifyOverlapEvidenceInput): OverlapEvidenceVerdict {
115
+ let verdict: Checked;
116
+ try { verdict = check(input); } catch (error) { verdict = { ok: false, reason: `verify-threw: ${String(error).slice(0, 200)}` }; }
117
+ debug.log('pr.land', 'overlap-evidence', {
118
+ decision: verdict.ok ? 'accepted' : 'rejected',
119
+ reason: verdict.ok ? (verdict.mode === 'already-ancestor' ? 'already-ancestor' : 'rebased+gated+reviewed') : verdict.reason,
120
+ overlappingLandings: input.overlappingLandings ?? [],
121
+ pr: input.pr,
122
+ head: input.expectedHead,
123
+ base: `${input.remote}/${input.branch}`,
124
+ baseTip: verdict.baseTip ?? null,
125
+ evidencePath: input.evidencePath,
126
+ });
127
+ if (!verdict.ok) return { ok: false, reason: verdict.reason };
128
+ return verdict.mode === 'already-ancestor'
129
+ ? { ok: true, mode: 'already-ancestor', reviewResult: verdict.reviewResult }
130
+ : { ok: true, mode: 'rebased', baseTip: verdict.baseTip!, reviewResult: verdict.reviewResult };
131
+ }
@@ -5,9 +5,10 @@
5
5
  // elanous release tag --version <x.y.z> --source <commit> [--yes]
6
6
  // elanous release verify [--version <x.y.z>]
7
7
  // elanous release notes --from <ref> [--to <ref>]
8
- // elanous release run --version <x.y.z> [--prerelease rc] [--main-cut | --cut-commit <sha>] [--dry-run] [--json] [--if-ready]
9
- // elanous release resume --run <runId> --from <node> [--json] (release/<v> 수리 뒤 새 끝으로 이어 달리기)
8
+ // elanous release run --version <x.y.z> [--prerelease rc] [--main-cut | --cut-commit <sha>] [--publish-at <iso>] [--dry-run] [--json] [--if-ready]
9
+ // elanous release resume --run <runId> --from <node> [--waive <node> --reason <text>] [--accepted-regressions <json>] [--json]
10
10
  // elanous release auto-start [--window-minutes <n>] [--apply] [--json]
11
+ // elanous release rehearsal tick [--apply] [--checkout <dir>] [--json]
11
12
  // elanous release preflight --version <x.y.z> [--publish-at <iso>] [--json] (컷 30분 전 사전 점검 · 읽기 전용 · ⛔ 있으면 exit 1)
12
13
  // elanous release cut-branch --version <x.y.z> --base <sha> --pick <sha>... [--dry-run]
13
14
  //
@@ -39,14 +40,17 @@ import { hqCliWriteAllowed, type HqDeps } from '../hq/hq.js';
39
40
  import { fileLeaseStore } from '../hq/lease.js';
40
41
  import { isIsolatedLedgerWriteRoot } from '../hq/ledger-write-target.js';
41
42
  import { runUnattendedRelease, type UnattendedReleaseDeps } from '../../scripts/release-loop/unattended-release.js';
43
+ import { publishAtError } from '../../scripts/release-loop/publish-at.js';
42
44
  import { cutReleaseBranch } from '../../scripts/release-loop/cut-branch.js';
43
45
  import { resumeReleaseRun } from '../../scripts/release-loop/resume-release.js';
46
+ import type { AcceptedRegression } from '../../scripts/release-loop/resume.js';
44
47
  import type { PrereleaseKind } from '../../scripts/release-loop/release-version.js';
45
48
  import { releaseReadiness } from '../../scripts/release-loop/release-readiness.js';
46
49
  import { runReleaseIfReady } from './release-run-if-ready.js';
47
50
  import { formatPreflight, releasePreflight } from '../../scripts/release-loop/preflight.js';
48
51
  import { defaultReleaseGraphRunsRoot, formatPublishProposal, measurePublishWindow, proposePublishAt, publishWindowWarning } from '../../scripts/release-loop/publish-window.js';
49
52
  import { autoStartScheduledRelease, type AutoStartDeferral } from '../release-loop/auto-start.js';
53
+ import { runRehearsalTick, type RehearsalDeps } from '../release-loop/rehearsal.js';
50
54
  import { landingFreezeMessage, LandingFrozenError, readLandingFreeze } from '../release-loop/landing-freeze.js';
51
55
  import { formatLightRc, runLightRc, type LightRcDeps } from '../release-loop/light-rc.js';
52
56
 
@@ -596,7 +600,7 @@ function compareReleaseVersions(a: string, b: string): number {
596
600
  return 0;
597
601
  }
598
602
 
599
- export function registerReleaseCommands(program: Command, releaseRunDeps: UnattendedReleaseDeps = {}, landedDeps: LandedButYellowDeps = {}, hqDeps: HqDeps = {}, lightRcDeps: LightRcDeps = {}): void {
603
+ export function registerReleaseCommands(program: Command, releaseRunDeps: UnattendedReleaseDeps = {}, landedDeps: LandedButYellowDeps = {}, hqDeps: HqDeps = {}, lightRcDeps: LightRcDeps = {}, rehearsalDeps: RehearsalDeps = {}): void {
600
604
  const release = program.command('release').description('공개 배포 한 판 — prepare(로컬) → publish(--yes) → verify (docs/manual/MANUAL-versioning-and-release-2026-09-25.md)');
601
605
  const schedule = release.command('schedule').description('판별 컷·착지 마감 조회·갱신');
602
606
  const scheduleLedgerRoot = () => releaseRunDeps.ledgerRoot ?? releaseLedgerRoot();
@@ -811,7 +815,7 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
811
815
  process.exitCode = 1;
812
816
  }
813
817
  });
814
- withContext(checklist.command('add <id> <title>').description('칸 추가')).option('--owner <owner>', '담당').option('--kind <kind>', 'screen = 다섯 화면 짝 칸 · 근거에 짝: PWA … · 데스크톱 … · 폴드 … · 아이폰 … · 아이패드 … 한 줄')
818
+ withContext(checklist.command('add <id> <title>').description('칸 추가')).option('--owner <owner>', '담당').option('--kind <kind>', 'screen = 다섯 화면 짝 칸 · 근거에 짝: PWA … · 데스크톱 … · 폴드 … · 아이폰 … · 아이패드 … 한 줄 · postpub = 발행 뒤 실측 칸 — 판 관문이 발행 전 판정에서 빼고 따로 센다')
815
819
  .option('--priority <priority>', 'P0|P1|P2').option('--deadline-version <v>', '마감 판').option('--predecessor <id...>', '선행 칸 id')
816
820
  .option('--ceo-minutes <minutes>', '대표 손 분량(분)').option('--ceo-date <date>', '대표 손 날짜 YYYY-MM-DD (없으면 판 착지일 KST)')
817
821
  .option('--accelerator', '가속 등급 — 다른 칸의 처리량을 올리는 칸(자율성·효율성·동시성). 생략하면 등급 없음')
@@ -825,7 +829,7 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
825
829
  });
826
830
  withContext(checklist.command('set <id>').description('칸 상태·근거·담당·처분 갱신'))
827
831
  .option('--status <status>', 'green|yellow|red|done').option('--evidence <evidence>', '근거').option('--owner <owner>', '담당')
828
- .option('--disposition <disposition>', 'move|known-issue|block').option('--kind <kind>', 'screen = 다섯 화면 짝 칸 · 근거에 짝: PWA … · 데스크톱 … · 폴드 … · 아이폰 … · 아이패드 … 한 줄')
832
+ .option('--disposition <disposition>', 'move|known-issue|block').option('--kind <kind>', 'screen = 다섯 화면 짝 칸 · 근거에 짝: PWA … · 데스크톱 … · 폴드 … · 아이폰 … · 아이패드 … 한 줄 · postpub = 발행 뒤 실측 칸 — 판 관문이 발행 전 판정에서 빼고 따로 센다')
829
833
  .option('--priority <priority>', 'P0|P1|P2').option('--deadline-version <v>', '마감 판').option('--predecessor <id...>', '선행 칸 id').option('--no-predecessor', '선행 칸 비우기(진짜 의존이 아니면 · CHECKLIST-PRED-CLEAR)')
830
834
  .option('--ceo-minutes <minutes>', '대표 손 분량(분)').option('--ceo-date <date>', '대표 손 날짜 YYYY-MM-DD')
831
835
  .option('--accelerator', '가속 등급 부여').option('--no-accelerator', '가속 등급 해제(기본은 없음)')
@@ -985,7 +989,7 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
985
989
  .action(async (o: { version: string; base: string; pick: string[]; append?: boolean; dryRun?: boolean; json?: boolean }) => {
986
990
  await jsonAction(o.json, async (log) => cutReleaseBranch({ ...o, log }), () => true);
987
991
  });
988
- const runAction = async (o: { version: string; cutCommit?: string; mainCut?: boolean; prerelease?: string; dryRun?: boolean; ifReady?: boolean; forceFreeze?: boolean; json?: boolean }, logRun = console.log): Promise<boolean | AutoStartDeferral | undefined> => {
992
+ const runAction = async (o: { version: string; cutCommit?: string; mainCut?: boolean; prerelease?: string; dryRun?: boolean; ifReady?: boolean; publishAt?: string; forceFreeze?: boolean; json?: boolean }, logRun = console.log): Promise<boolean | AutoStartDeferral | undefined> => {
989
993
  // Only the rc rehearsal channel is operated for now (alpha/beta stay valid version shapes, not run options).
990
994
  if (o.prerelease !== undefined && o.prerelease !== 'rc') {
991
995
  const message = `--prerelease supports rc only: ${o.prerelease}`;
@@ -994,6 +998,14 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
994
998
  process.exitCode = 1;
995
999
  return false;
996
1000
  }
1001
+ // A malformed --publish-at is refused before readiness, so --if-ready cannot defer it as a success.
1002
+ const publishAtInvalid = o.publishAt === undefined ? null : publishAtError(o.publishAt);
1003
+ if (publishAtInvalid) {
1004
+ if (o.json) await writeStdoutJson(`${JSON.stringify({ ok: false, error: publishAtInvalid })}\n`);
1005
+ else console.error(`⛔ ${publishAtInvalid}`);
1006
+ process.exitCode = 1;
1007
+ return false;
1008
+ }
997
1009
  const prerelease = o.prerelease as PrereleaseKind | undefined;
998
1010
  if (!o.dryRun) {
999
1011
  try {
@@ -1026,7 +1038,7 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
1026
1038
  const outcome = await runReleaseIfReady(o.version, {
1027
1039
  ledgerRoot: releaseRunDeps.ledgerRoot,
1028
1040
  readiness: (version, options) => releaseReadiness(version, { ledgerRoot: releaseRunDeps.ledgerRoot, checklist: releaseRunDeps.checklist, ...options }),
1029
- run: () => runUnattendedRelease({ version: o.version, cutCommit: o.cutCommit, mainCut: o.mainCut, prerelease, forceFreeze: o.forceFreeze }, releaseRunDeps),
1041
+ run: () => runUnattendedRelease({ version: o.version, cutCommit: o.cutCommit, mainCut: o.mainCut, prerelease, forceFreeze: o.forceFreeze, publishAt: o.publishAt }, releaseRunDeps),
1030
1042
  });
1031
1043
  if (outcome.skipped) {
1032
1044
  if (o.json) await writeStdoutJson(`${JSON.stringify(outcome)}\n`);
@@ -1054,7 +1066,7 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
1054
1066
  }
1055
1067
  }
1056
1068
  await jsonAction(o.json, async (log) => {
1057
- const result = await runUnattendedRelease({ version: o.version, dryRun: o.dryRun, cutCommit: o.cutCommit, mainCut: o.mainCut, prerelease, forceFreeze: o.forceFreeze }, releaseRunDeps);
1069
+ const result = await runUnattendedRelease({ version: o.version, dryRun: o.dryRun, cutCommit: o.cutCommit, mainCut: o.mainCut, prerelease, forceFreeze: o.forceFreeze, publishAt: o.publishAt }, releaseRunDeps);
1058
1070
  log(`${result.dryRun ? '· 드라이런' : '▶ 릴리스 루프'} ${result.input.version} · 입력 ${JSON.stringify(result.input)}`);
1059
1071
  return result;
1060
1072
  }, (r) => r.dryRun || r.state?.status === 'done' || r.state?.status === 'awaiting-approval');
@@ -1067,9 +1079,10 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
1067
1079
  .option('--prerelease <kind>', '미리보기 판(rc) — <v>-rc.<다음 빈 번호> · npm next · GitHub pre-release · docs-land/ops-upgrade/dev-bump 건너뜀')
1068
1080
  .option('--dry-run', '체크리스트·그래프 실행 없이 전체 입력 보기')
1069
1081
  .option('--if-ready', '준비되지 않았으면 사유를 출력하고 성공으로 건너뛴다')
1082
+ .option('--publish-at <iso>', '승인·선행 노드 통과 뒤 지정 시각까지 발행 대기(오프셋 포함 ISO 시각)')
1070
1083
  .option('--force-freeze', '동결 중 게이트·발행 강행(관측 기록) — 가지 컷은 동결이 필요 없다 · 동결은 비상 스위치로 남는다')
1071
1084
  .option('--json', '결과 한 줄 JSON(stdout) · 사람 줄은 stderr')
1072
- .action(async (o: { version: string; cutCommit?: string; mainCut?: boolean; prerelease?: string; dryRun?: boolean; ifReady?: boolean; forceFreeze?: boolean; json?: boolean }) => { await runAction(o); });
1085
+ .action(async (o: { version: string; cutCommit?: string; mainCut?: boolean; prerelease?: string; dryRun?: boolean; ifReady?: boolean; publishAt?: string; forceFreeze?: boolean; json?: boolean }) => { await runAction(o); });
1073
1086
  release.command('preflight')
1074
1087
  .description('컷 30분 전 사전 점검(읽기 전용) — 체크리스트 게이트 모의 · 발행 뒤 칸 · run --dry-run 입력 · 동결 · 일정/게이트 실측. ⛔ 있으면 exit 1')
1075
1088
  .requiredOption('--version <v>', '점검할 판(x.y.z)')
@@ -1112,10 +1125,25 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
1112
1125
  .requiredOption('--run <runId>', '실패한 release-loop 런 ID')
1113
1126
  .requiredOption('--from <node>', '다시 시작할 노드(예: gate)')
1114
1127
  .option('--partial', 'GATE-PARTIAL — --from gate 에서 앞 게이트 뒤 바뀐 시험 파일 ⊕ 앞 실패 파일만 다시 돈다(시험·문서만 바뀐 경우 · 아니면 전체 게이트로)')
1128
+ .option('--waive <node>', '저장된 실패 노드 한 개를 면제(--from 은 그 노드 그대로)')
1129
+ .option('--reason <text>', '면제 사유 — 공개 알려진 문제에 들어갈 사용자 영향 설명')
1130
+ .option('--accepted-regressions <json>', '알려진 문제 추가 입력 JSON 배열 [{"id":"…","note":"…"}]')
1115
1131
  .option('--json', '결과 한 줄 JSON(stdout)')
1116
- .action(async (o: { run: string; from: string; partial?: boolean; json?: boolean }) => {
1132
+ .action(async (o: { run: string; from: string; partial?: boolean; waive?: string; reason?: string; acceptedRegressions?: string; json?: boolean }) => {
1117
1133
  await jsonAction(o.json, async (log) => {
1118
- const result = await resumeReleaseRun({ runId: o.run, from: o.from, partial: o.partial });
1134
+ let acceptedRegressions: AcceptedRegression[] | undefined;
1135
+ if (o.acceptedRegressions !== undefined) {
1136
+ try { acceptedRegressions = JSON.parse(o.acceptedRegressions) as AcceptedRegression[]; }
1137
+ catch { throw new Error('--accepted-regressions must be a JSON array of {id, note}'); }
1138
+ if (!Array.isArray(acceptedRegressions) || acceptedRegressions.some((issue) => !issue ||
1139
+ typeof issue.id !== 'string' || !issue.id.trim() || typeof issue.note !== 'string' || !issue.note.trim())) {
1140
+ throw new Error('acceptedRegressions must be an array of {id, note}');
1141
+ }
1142
+ }
1143
+ const result = await resumeReleaseRun({ runId: o.run, from: o.from, partial: o.partial,
1144
+ ...(o.waive === undefined ? {} : { waive: o.waive }),
1145
+ ...(o.reason === undefined ? {} : { reason: o.reason }),
1146
+ ...(o.acceptedRegressions === undefined ? {} : { acceptedRegressions }) });
1119
1147
  if (result.partial.requested) log(result.partial.applied
1120
1148
  ? `◐ 부분 재검: ${result.partial.files}파일 · 나머지는 앞 게이트(${result.partial.priorCommit.slice(0, 12)}) 결과를 잇는다`
1121
1149
  : `● 전체 게이트로: ${result.partial.reason}`);
@@ -1123,6 +1151,20 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
1123
1151
  return { tip: result.tip, partial: result.partial, status: result.state.status, runId: result.state.runId };
1124
1152
  }, (r) => r.status === 'done' || r.status === 'awaiting-approval');
1125
1153
  });
1154
+ release.command('rehearsal').description('판 일정에서 컷 전 가벼운 RC 두 단계').command('tick')
1155
+ .description('15분 틱: 다음 판 컷 −7h · −150m RC 계획(기본 보기만)')
1156
+ .option('--apply', '도장 선점 후 해당 단계 실행')
1157
+ .option('--checkout <dir>', '깨끗한 git 체크아웃(기본 ELANOUS_REHEARSAL_GIT_CWD)')
1158
+ .option('--json', '결과 한 줄 JSON(stdout)')
1159
+ .action(async (o: { apply?: boolean; checkout?: string; json?: boolean }) => {
1160
+ await jsonAction(o.json, async (log) => {
1161
+ const ledgerRoot = scheduleLedgerRoot();
1162
+ if (o.apply && !mayWriteLedger('release rehearsal tick', false, ledgerRoot)) throw new Error('release rehearsal tick: ledger write denied');
1163
+ const result = await runRehearsalTick({ apply: o.apply, checkout: o.checkout, ledgerRoot }, rehearsalDeps);
1164
+ log(`· 리허설 ${result.status}${'version' in result ? ` ${result.version}` : ''}${result.status === 'due' ? ` · 실행 ${result.run ?? '-'} · 건너뜀 ${result.supersede.join(', ') || '-'}` : ''}`);
1165
+ return result;
1166
+ }, (result) => !result.notificationFailures && !result.stamps?.some((stamp) => !['ok', 'superseded'].includes(stamp.outcome)));
1167
+ });
1126
1168
  release.command('auto-start')
1127
1169
  .description('판 일정의 컷 창에서 릴리스 시작을 미리 본다 · --apply 로 release run --if-ready 실행')
1128
1170
  .option('--window-minutes <n>', '컷 뒤 선택 창(분)', '15')
@@ -1,5 +1,5 @@
1
1
  import { existsSync, mkdirSync, readFileSync, realpathSync, readlinkSync, readdirSync, statSync, rmSync, symlinkSync, renameSync, writeFileSync, lstatSync } from 'node:fs';
2
- import { resolve, join, basename, dirname } from 'node:path';
2
+ import { resolve, join, basename, dirname, sep } from 'node:path';
3
3
  import { homedir, platform, userInfo } from 'node:os';
4
4
  import { spawnSync } from 'node:child_process';
5
5
  import { decideRestartNeeded, type RestartNeededResult } from './nexus-restart-needed.js';
@@ -455,14 +455,172 @@ export function planVersionPrune(
455
455
  return { removed: all.filter((n) => !kept.includes(n)), kept };
456
456
  }
457
457
 
458
- function defaultPruneVersions(plan: { current: string; daemonSha: string; keep: number }): VersionPruneOutcome {
459
- const dir = join(homedir(), '.local/share/elanous/versions');
458
+ function isInsideVersion(path: string, version: string): boolean {
459
+ const normalized = path.replace(/ \(deleted\)$/, '');
460
+ let actual = normalized;
461
+ try { actual = realpathSync(normalized); } catch { /* A process can exit between observation and resolution. */ }
462
+ return actual === version || actual.startsWith(`${version}${sep}`)
463
+ || normalized === version || normalized.startsWith(`${version}${sep}`);
464
+ }
465
+
466
+ /** Whether a command line names a path inside the version folder (as a whole path token, not a sibling prefix). */
467
+ function argvReferencesVersion(argv: string, version: string): boolean {
468
+ const escaped = version.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
469
+ return new RegExp(`(?:^|[\\s=:'"])${escaped}(?=$|[\\/\\s:'"])`).test(argv);
470
+ }
471
+
472
+ /** Linux /proc/<pid>/stat: a zombie has released its cwd/exe, and a kernel thread (PF_KTHREAD) never had them. */
473
+ export function procStatHoldsNothing(stat: string): boolean {
474
+ // After the comm field: state ppid pgrp session tty_nr tpgid flags …
475
+ const fields = stat.slice(stat.lastIndexOf(')') + 2).split(' ');
476
+ return fields[0] === 'Z' || (Number(fields[6]) & 0x00200000) !== 0;
477
+ }
478
+
479
+ type ProcessListing = () => { status: number | null; stdout: string; stderr: string; error?: Error };
480
+ type ProcessScan = (pid: string) => { status: number | null; stdout: string; stderr: string; error?: Error };
481
+
482
+ /** Inspect live cwd and executable references before deleting any candidate; an unavailable scan must not authorize deletion. */
483
+ function pruneUnusedVersions(dir: string, outcome: VersionPruneOutcome, listProcesses: ProcessListing = () => spawnSync('ps', ['-axww', '-o', 'pid=,uid=,args='], { encoding: 'utf8', maxBuffer: SPAWN_MAX_BUFFER }), scanProcess?: ProcessScan): VersionPruneOutcome {
484
+ if (!outcome.removed.length) return outcome;
485
+ const probe = scanProcess ?? ((pid: string) => spawnSync('lsof', ['-nP', '-F', 'pfn', '-p', pid], { encoding: 'utf8', maxBuffer: SPAWN_MAX_BUFFER }));
486
+ const candidates = outcome.removed.map((name) => ({ name, path: realpathSync(join(dir, name)) }));
487
+ const references: Array<{ path: string; kind: string; pid: string }> = [
488
+ { path: process.cwd(), kind: 'cwd', pid: String(process.pid) },
489
+ { path: process.execPath, kind: 'exe', pid: String(process.pid) },
490
+ ];
491
+ let scanError = '';
492
+ // Other-UID processes: unreadable without privilege (root alone is ~200 on a Mac),
493
+ // so requiring them would disable pruning entirely. Counted and observed instead.
494
+ // Their argv is still readable (ps on macOS, /proc/<pid>/cmdline on Linux): an argv naming
495
+ // a candidate folder holds it. Only processes whose argv is unreadable too are count-only.
496
+ const uninspected = new Set<string>();
497
+ const otherArgv: Array<{ pid: string; argv: string }> = [];
498
+ const otherUid = (pid: string, argv: string | undefined): void => {
499
+ if (argv === undefined || !argv.trim() || /^\(.*\)$/.test(argv.trim())) uninspected.add(pid);
500
+ else otherArgv.push({ pid, argv });
501
+ };
502
+ if (platform() === 'linux') {
503
+ const argvRead = new Set<string>();
504
+ try {
505
+ for (const pid of readdirSync('/proc').filter((entry) => /^\d+$/.test(entry))) {
506
+ for (const [link, kind] of [['cwd', 'cwd'], ['exe', 'exe']] as const) {
507
+ try { references.push({ path: readlinkSync(join('/proc', pid, link)), kind, pid }); }
508
+ catch (error) {
509
+ const code = (error as NodeJS.ErrnoException).code;
510
+ // Only a vanished /proc/<pid> proves exit: a live process whose main thread
511
+ // ended can also report ENOENT for cwd/exe while worker threads still run.
512
+ let owner: number | undefined;
513
+ // Only ENOENT/ESRCH proves the process exited; any other failure leaves the scan incomplete.
514
+ const exited = (failure: unknown): boolean => ['ENOENT', 'ESRCH'].includes(String((failure as NodeJS.ErrnoException).code));
515
+ let stat: string;
516
+ try { owner = statSync(join('/proc', pid)).uid; stat = readFileSync(join('/proc', pid, 'stat'), 'utf8'); }
517
+ catch (failure) { if (exited(failure) && !existsSync(join('/proc', pid))) continue; throw failure; }
518
+ if (procStatHoldsNothing(stat)) continue;
519
+ // Our own links must be readable. Another UID's link failure (denied, or ENOENT from a live process) falls back to its argv.
520
+ if ((code === 'EACCES' || code === 'EPERM' || code === 'ENOENT') && owner !== process.getuid?.()) {
521
+ if (!argvRead.has(pid)) {
522
+ argvRead.add(pid);
523
+ let argv: string | undefined;
524
+ try { argv = readFileSync(join('/proc', pid, 'cmdline'), 'utf8').split('\0').join(' '); } catch { argv = undefined; }
525
+ otherUid(pid, argv);
526
+ }
527
+ continue;
528
+ }
529
+ throw error;
530
+ }
531
+ }
532
+ }
533
+ } catch (error) { scanError = String(error); }
534
+ } else if (platform() === 'darwin') {
535
+ // Bulk lsof may omit individual files. Only a complete scan of every
536
+ // still-live process of our own UID permits deletion; other UIDs count
537
+ // via positive bulk sightings only (lsof cannot inspect them unprivileged).
538
+ try {
539
+ const listing = listProcesses();
540
+ const rows = listing.stdout.trim().split('\n').filter(Boolean);
541
+ const parsed = rows.map((row) => /^\s*(\d+)\s+(-?\d+)(?:\s+(.*?))?\s*$/.exec(row));
542
+ if (listing.error || listing.status !== 0 || !rows.length || parsed.some((match) => !match)) {
543
+ scanError = `lsof scope incomplete (ps unavailable: ${listing.error?.message || listing.stderr.trim() || listing.status})`;
544
+ } else {
545
+ const sections = new Map<string, string>();
546
+ if (!scanProcess) {
547
+ const bulk = spawnSync('lsof', ['-nP', '-F', 'pfn'], { encoding: 'utf8', maxBuffer: SPAWN_MAX_BUFFER });
548
+ if (bulk.error || bulk.status !== 0) scanError = `lsof scope incomplete: ${bulk.error?.message || bulk.stderr?.trim() || bulk.status}`;
549
+ else {
550
+ let id = '';
551
+ for (const line of bulk.stdout.split('\n')) {
552
+ if (/^p\d+$/.test(line)) { id = line.slice(1); sections.set(id, ''); }
553
+ else if (id) sections.set(id, `${sections.get(id)}${line}\n`);
554
+ }
555
+ }
556
+ }
557
+ const complete = (lines: string): boolean => /(?:^|\n)fcwd\nn[^\n]+\n/.test(lines) && /(?:^|\n)ftxt\nn[^\n]+\n/.test(lines);
558
+ const readReferences = (id: string, lines: string): void => {
559
+ let kind = '';
560
+ for (const line of lines.split('\n')) {
561
+ if (line.startsWith('f')) kind = line.slice(1);
562
+ else if (line.startsWith('n') && (kind === 'cwd' || kind === 'txt')) references.push({ path: line.slice(1), kind: kind === 'txt' ? 'exe' : 'cwd', pid: id });
563
+ }
564
+ };
565
+ // Positive bulk sightings matter even if another UID cannot be inspected.
566
+ for (const [id, lines] of sections) readReferences(id, lines);
567
+ for (const match of parsed) {
568
+ const id = match![1]!;
569
+ // Compare against the complete ps list; lsof may omit even our own UID.
570
+ if (Number(match![2]) !== process.getuid?.()) { otherUid(id, match![3]); continue; }
571
+ if (complete(sections.get(id) ?? '')) continue;
572
+ const state = spawnSync('ps', ['-p', id, '-o', 'stat='], { encoding: 'utf8', maxBuffer: SPAWN_MAX_BUFFER });
573
+ if (state.error || (state.status !== 0 && state.status !== 1) || (state.status === 0 && !state.stdout.trim())) {
574
+ scanError = `lsof scope incomplete (pid=${id} state unavailable: ${state.error?.message || state.stderr?.trim() || state.status})`;
575
+ break;
576
+ }
577
+ if (state.status === 1) {
578
+ // ps can fail to show another UID as well as a process that exited.
579
+ // Only ESRCH proves the latter; EPERM/unknown must hold candidates.
580
+ try { process.kill(Number(id), 0); }
581
+ catch (error) {
582
+ if ((error as NodeJS.ErrnoException).code === 'ESRCH') continue;
583
+ scanError = `lsof scope incomplete (pid=${id} existence unavailable: ${String(error)})`;
584
+ break;
585
+ }
586
+ scanError = `lsof scope incomplete (pid=${id} still alive but ps unavailable)`;
587
+ break;
588
+ }
589
+ if (state.stdout.trim().startsWith('Z')) continue;
590
+ const scan = probe(id);
591
+ if (scan.error || scan.status !== 0 || !scan.stdout.startsWith(`p${id}\n`) || !complete(scan.stdout)) {
592
+ scanError = `lsof scope incomplete (pid=${id} cwd/executable unavailable: ${scan.error?.message || scan.stderr?.trim() || scan.status})`;
593
+ break;
594
+ }
595
+ readReferences(id, scan.stdout.slice(`p${id}\n`.length));
596
+ }
597
+ }
598
+ } catch (error) { scanError = `lsof scope incomplete: ${String(error)}`; }
599
+ } else scanError = `unsupported platform: ${platform()}`;
600
+ const removed: string[] = [];
601
+ const kept = [...outcome.kept];
602
+ for (const candidate of candidates) {
603
+ const literal = join(dir, candidate.name);
604
+ const ref = references.find((entry) => isInsideVersion(entry.path, candidate.path))
605
+ ?? otherArgv.filter((entry) => argvReferencesVersion(entry.argv, candidate.path) || argvReferencesVersion(entry.argv, literal))
606
+ .map((entry) => ({ path: entry.argv, kind: 'argv', pid: entry.pid }))[0];
607
+ if (scanError || ref) {
608
+ kept.push(candidate.name);
609
+ console.error(`self-update: prune kept ${candidate.name} — ${ref ? `live pid=${ref.pid} ${ref.kind}` : `process scan unavailable: ${scanError}`}`);
610
+ } else {
611
+ rmSync(join(dir, candidate.name), { recursive: true, force: true });
612
+ removed.push(candidate.name);
613
+ }
614
+ }
615
+ if (uninspected.size) console.error(`self-update: prune removed ${removed.join(', ') || 'none'} — ${uninspected.size} other-UID process(es) not inspectable without privilege (argv unreadable too)`);
616
+ return { ...outcome, removed, kept };
617
+ }
618
+
619
+ export function defaultPruneVersions(plan: { current: string; daemonSha: string; keep: number }, dir: string = VERSIONS_DIR(), listProcesses?: ProcessListing, scanProcess?: ProcessScan): VersionPruneOutcome {
460
620
  const entries = readdirSync(dir, { withFileTypes: true })
461
621
  .filter((d) => d.isDirectory())
462
622
  .map((d) => ({ name: d.name, mtimeMs: statSync(join(dir, d.name)).mtimeMs }));
463
- const outcome = planVersionPrune(entries, plan.current, plan.daemonSha, plan.keep);
464
- for (const name of outcome.removed) rmSync(join(dir, name), { recursive: true, force: true });
465
- return outcome;
623
+ return pruneUnusedVersions(dir, planVersionPrune(entries, plan.current, plan.daemonSha, plan.keep), listProcesses, scanProcess);
466
624
  }
467
625
 
468
626
  /**
@@ -502,11 +660,15 @@ export interface ReleaseUpdateDeps {
502
660
  out?: { log: (text: string) => void; error: (text: string) => void };
503
661
  alert?: (text: string) => void;
504
662
  pruneVersions?: (plan: { current: string; previous: string; keep: number; prefix: string }) => VersionPruneOutcome;
663
+ /** Process scope probe for pruning; when absent the host's process list is used. */
664
+ listPruneProcesses?: ProcessListing;
665
+ /** Per-process cwd/executable probe; when absent the host's lsof is used. */
666
+ scanPruneProcess?: ProcessScan;
505
667
  /** 릴리스 기준 URL 주입 — 환경변수와 설치 출처보다 우선한다. */
506
668
  releaseBase?: string;
507
669
  }
508
670
 
509
- function pruneReleaseVersions(plan: { current: string; previous: string; keep: number; prefix: string }): VersionPruneOutcome {
671
+ function pruneReleaseVersions(plan: { current: string; previous: string; keep: number; prefix: string }, listProcesses?: ProcessListing, scanProcess?: ProcessScan): VersionPruneOutcome {
510
672
  const dir = join(plan.prefix, 'versions');
511
673
  const entries = readdirSync(dir, { withFileTypes: true })
512
674
  .filter((entry) => entry.isDirectory())
@@ -519,8 +681,7 @@ function pruneReleaseVersions(plan: { current: string; previous: string; keep: n
519
681
  const newest = [...entries].sort((a, b) => b.mtimeMs - a.mtimeMs).slice(0, plan.keep).map((entry) => entry.name);
520
682
  const kept = all.filter((name) => name === plan.current || name === plan.previous || newest.includes(name));
521
683
  const removed = all.filter((name) => !kept.includes(name));
522
- for (const name of removed) rmSync(join(dir, name), { recursive: true, force: true });
523
- return { removed, kept };
684
+ return pruneUnusedVersions(dir, { removed, kept }, listProcesses, scanProcess);
524
685
  }
525
686
 
526
687
  /** The build identity of an install.json — version, version folder and commit; empty when it cannot be read. */
@@ -617,7 +778,8 @@ export async function runReleaseUpdate(options: ReleaseUpdateOptions = {}, deps:
617
778
  try {
618
779
  const current = typeof metadata === 'object' && metadata !== null && 'versionDir' in metadata && typeof metadata.versionDir === 'string'
619
780
  && /^versions\/[a-zA-Z0-9._-]+$/.test(metadata.versionDir) && !metadata.versionDir.includes('..') ? basename(metadata.versionDir) : '';
620
- prune = (deps.pruneVersions ?? pruneReleaseVersions)({ current, previous, keep: options.keep ?? 3, prefix });
781
+ // runReleaseUpdate → pruneReleaseVersions → pruneUnusedVersions (all-UID cwd/executable guard).
782
+ prune = (deps.pruneVersions ?? ((plan) => pruneReleaseVersions(plan, deps.listPruneProcesses, deps.scanPruneProcess)))({ current, previous, keep: options.keep ?? 3, prefix });
621
783
  } catch (error) {
622
784
  prune = { removed: [], kept: [], skipped: `정리 실패: ${String(error)}` };
623
785
  }
@@ -999,6 +1161,7 @@ export async function runSelfUpdate(options: SelfUpdateOptions = {}, deps: SelfU
999
1161
  }
1000
1162
  let prune: VersionPruneOutcome;
1001
1163
  try {
1164
+ // runSelfUpdate → defaultPruneVersions → pruneUnusedVersions (all-UID cwd/executable guard).
1002
1165
  prune = (deps.pruneVersions ?? defaultPruneVersions)({ current: installedVersion, daemonSha: decision.from ?? '', keep: options.keep ?? 3 });
1003
1166
  } catch (error) {
1004
1167
  prune = { removed: [], kept: [], skipped: `정리 실패: ${String(error)}` };