elanous 0.2.37 → 0.2.38

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 (588) hide show
  1. package/apps/pwa/out/404/index.html +1 -1
  2. package/apps/pwa/out/404.html +1 -1
  3. package/apps/pwa/out/_next/static/{8kITumG9GAZ1LwT9POrAR → RrjXTwz-urRAbChP1bX02}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{2002-a43a3f380df873ec.js → 1026-44010f405704050c.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/1118.a8a74ea29aac3e46.js +27 -0
  6. package/apps/pwa/out/_next/static/chunks/1204-2aa830479adf80f4.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1341-43b7d2dc77d45c9d.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1440-c3ea8882badfd9bf.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/{7424.c0fe2a595d4232b5.js → 1632.7d87f998e714bede.js} +1 -1
  10. package/apps/pwa/out/_next/static/chunks/1698-9c11cbf97f13717e.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/1770-3b1fc92253ef9f2f.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/{4937-28d437999b77c9dd.js → 1923-5c1695332240246a.js} +1 -1
  13. package/apps/pwa/out/_next/static/chunks/1938.0e6c60ee91c4d7f5.js +148 -0
  14. package/apps/pwa/out/_next/static/chunks/2104.70e086d5174a4736.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/213-20675f20673ba22a.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/214.186a430da28338f1.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/2277.3bd4c0ac7f1b5996.js +93 -0
  18. package/apps/pwa/out/_next/static/chunks/240.e09f433f71c78ff4.js +104 -0
  19. package/apps/pwa/out/_next/static/chunks/{3199.6a1a9b0e6cd01dc6.js → 2415.738b777642a02233.js} +1 -1
  20. package/apps/pwa/out/_next/static/chunks/2417.02151393605a7cd0.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/2463.cd2641a76daaa9ff.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/2576.b9e9c629c4b84c52.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/265-c021de53f0865bd9.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/2998-b1cc13956cd6a6e7.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/2e4782d3.ba2f0ab2b63a53e7.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/{1262.a89bb27a5b2f844e.js → 3086.fb14b60ce98b53d3.js} +1 -1
  27. package/apps/pwa/out/_next/static/chunks/3124-561f7f037943cde2.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/3130.626b54382b4dc379.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/3221-1bb3f99a55088613.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/3280.24edb1ffd03a7f3f.js +24 -0
  31. package/apps/pwa/out/_next/static/chunks/3288-eb3c22a1cbf2ae68.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/{2283.edd9627b7bdfb546.js → 3291.f6c1005387d514bd.js} +2 -2
  33. package/apps/pwa/out/_next/static/chunks/33.ef99e8374f96c6c4.js +36 -0
  34. package/apps/pwa/out/_next/static/chunks/{6877-84d47b7e85d0c41e.js → 3341-f09b9efbd7a82f70.js} +1 -1
  35. package/apps/pwa/out/_next/static/chunks/3389.012c70767a872f9b.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/3410-d7c0f14b329d6fda.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/3421.c87d6313a7d670f7.js +287 -0
  38. package/apps/pwa/out/_next/static/chunks/3422-83a3953f9bd67e26.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/{6680.6a693c28aae4cd19.js → 3528.e4b8ab70d339f965.js} +1 -1
  40. package/apps/pwa/out/_next/static/chunks/3587-002e24e368c515a9.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/3644.f12a7d821ab940af.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/3660.d7516588e2d875c3.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/3695.b82e2610f4cd4ea9.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/3858.ca1f5bc0f577eac9.js +94 -0
  45. package/apps/pwa/out/_next/static/chunks/3959-e6ac4a139d9d4bcc.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/3f0de3de-2340c532c00395db.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/4032.53f62176514ec0ea.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/4199-f7c6eb40cd0322f8.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/4204-ef8484cbecd5d62f.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/4259-163129ca79913c4b.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/428-eaa77df3d0c85969.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/4432-97e400ffa1e161f5.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/449-aa427cf2cbb75f50.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/4491-39a0fd32bbaf3840.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/4588.b7cf06d382953979.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/4690.70ec8060825366ab.js +32 -0
  57. package/apps/pwa/out/_next/static/chunks/{4036.d54e55e1ebf5acdc.js → 4804.6012b5cd33c22792.js} +2 -2
  58. package/apps/pwa/out/_next/static/chunks/4881.e4d22d730e3b9df5.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/4937.ce2e340dc29cad3e.js +79 -0
  60. package/apps/pwa/out/_next/static/chunks/5-bb1c08d84bd71df3.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/5005.de98df23b37cdf3c.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/5035.dc64a89cad5573cc.js +74 -0
  63. package/apps/pwa/out/_next/static/chunks/5041-5aada92ca215ffdd.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/{6819.be5c9c4d67f22922.js → 5203.51e5d8b5f1dcfe53.js} +16 -16
  65. package/apps/pwa/out/_next/static/chunks/5207.1a224035455790fd.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/{757-4fbaf50942b8c424.js → 5255-1b60d50eabe9f057.js} +1 -1
  67. package/apps/pwa/out/_next/static/chunks/5269.d0436ee6d773382a.js +286 -0
  68. package/apps/pwa/out/_next/static/chunks/5300.677c97e0393742e6.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/{7867.e8725745ddd1d940.js → 5307.d7328020227880c2.js} +4 -4
  70. package/apps/pwa/out/_next/static/chunks/5309.3367c4d50f52b489.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/552-689827d49780fa00.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/5539.34cc9d60622cb69b.js +8 -0
  73. package/apps/pwa/out/_next/static/chunks/5568.218dea0c00ca2ec7.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/{5046.7fd5cbd859299448.js → 5638.107314dc479e2f37.js} +1 -1
  75. package/apps/pwa/out/_next/static/chunks/5645.b7e3b055455a04ea.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/5662.18cc0f30353cbd96.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/5671.da24fc9dc5e98325.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/5b141171.324c40fd49cb6821.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/6142.913b44e260a3f925.js +64 -0
  80. package/apps/pwa/out/_next/static/chunks/6175-d4621f6a10b0cfab.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/6290.3eb53a20746b72ce.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/6415-9307f3eafa90837f.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/6493.27ad77de7aac7966.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/6579-3de35d00aacd71b5.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/680-2f324c4e039a5b67.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/6845-331b3c7481da3ba3.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/{1224.2ceca6c180d5130b.js → 7000.28157393caaf5343.js} +1 -1
  88. package/apps/pwa/out/_next/static/chunks/{6745-5a63492933959dd9.js → 7161-8f22db3ec5ed005e.js} +3 -3
  89. package/apps/pwa/out/_next/static/chunks/{2479-fe7e188bd9402d14.js → 7311-60a7ea80d23c7521.js} +1 -1
  90. package/apps/pwa/out/_next/static/chunks/7401.525d0a12f7d02c06.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/7438.c2cf72b66dca6aa4.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/{5952.294fc574a8625184.js → 7488.966b116789f3d41a.js} +1 -1
  93. package/apps/pwa/out/_next/static/chunks/7524.ce973525d30b3d52.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/7573.2376e13c9c21f1f8.js +77 -0
  95. package/apps/pwa/out/_next/static/chunks/7577.ea6e9ac72e035520.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/{6252.240578f10952e091.js → 7628.6b16863ac3972333.js} +1 -1
  97. package/apps/pwa/out/_next/static/chunks/7657.8d1fb3d2f7ebe273.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/7903-1435d5d2f0dd7807.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/{1746.3563617ea64023a3.js → 7954.4f7a8832663ee979.js} +1 -1
  100. package/apps/pwa/out/_next/static/chunks/7978-30a91192c5dbe3c2.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/{4931.95cc6dbd230774ca.js → 8019.21ca554a821dad01.js} +16 -16
  102. package/apps/pwa/out/_next/static/chunks/8025-8795821525cf1a07.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/8191.3674a00f0b565c23.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8217-604955d8885c5ef2.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/8298-eed6f5f2585a63f4.js +1 -0
  106. package/apps/pwa/out/_next/static/chunks/{2375.bf364c85dbb6f085.js → 8487.291666583bebe640.js} +1 -1
  107. package/apps/pwa/out/_next/static/chunks/8509.3a54d4e61893d7cb.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/8758.ac421f726a189125.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/{4383.18e5c54db3a22c88.js → 8879.ac06755bcf3e44a1.js} +1 -1
  110. package/apps/pwa/out/_next/static/chunks/891.d6c723a51a067b36.js +149 -0
  111. package/apps/pwa/out/_next/static/chunks/{9151.a2c51e93f9156dae.js → 8911.bf5e712acb25fb6c.js} +1 -1
  112. package/apps/pwa/out/_next/static/chunks/{58edce7d.f1b0c72acf13b123.js → 8994d93e.33ba491f95345fc2.js} +1 -1
  113. package/apps/pwa/out/_next/static/chunks/{5f6af561-f90b7a67f4f75d13.js → 89d6c04f-6b8ab341748e468a.js} +1 -1
  114. package/apps/pwa/out/_next/static/chunks/9223.cbcc88e0e9950426.js +41 -0
  115. package/apps/pwa/out/_next/static/chunks/9299.24e2a7d9b6d1f91b.js +226 -0
  116. package/apps/pwa/out/_next/static/chunks/9317.6a9869039c231bb3.js +24 -0
  117. package/apps/pwa/out/_next/static/chunks/9383.e4755f275b3c11ff.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/9581.da63ddf41acfad46.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/9715.8c5529a0a5c41a36.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/9817.3009d65f0889ef1f.js +2 -0
  121. package/apps/pwa/out/_next/static/chunks/{de8446d3.31b73eb239fecd7b.js → 9fe1b809.738da85c8b17e3fb.js} +1 -1
  122. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-76873dd80da4f715.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/app/approvals/page-ed32b6ec5dea8f23.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-fdd40e15babf3d25.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/board/page-384efc78e85e400b.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/botlab/page-866aac5ff226a7f3.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/bots/page-d91809c5aeed319a.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/ceo/page-128f3522f89a81ab.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/chat/page-595961abf3b43ad1.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/consult/page-f41dccb76d8c0263.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/control/page-a3205c2e28b7d0d9.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/decisions/page-c39a80277814a3f4.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/design-check/page-1ab3f98194513195.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/editor/page-a5246f7e254d77d0.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/exec/page-0f92287bdb81217d.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/field/page-5a37131ff63793ef.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/healthz/route-76873dd80da4f715.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/inside/page-ac4bd14bb2348bfd.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/intake/{page-5c112c6d7c33656e.js → page-c348a3959c5b5fc6.js} +1 -1
  140. package/apps/pwa/out/_next/static/chunks/app/layout-2addd81c0d3e2906.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/live/page-da6b765f58a3eefb.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/live-run/page-0661e40f8780c32c.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/loops/page-b89af1b1c8e4875b.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/market/page-da8aeb82ba751cdb.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-cc71317b239439db.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/morning/page-bde7fc1d64ffe369.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/not-found-e2797214f713fa58.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/observatory/page-2a35bcda70defaf6.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-3af03deedde45032.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-88a1edbff62eaf1d.js → page-d1d1210f99f620ae.js} +1 -1
  151. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-46d1b5500885861d.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/outputs/page-291ab19fe2acb173.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/{page-b9af2ac1c4737ce8.js → page-7f569e533ddeecba.js} +1 -1
  154. package/apps/pwa/out/_next/static/chunks/app/reflection/page-990129c9fee46d8f.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-7737f785ebb1cdaf.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/sessions/page-9f3c73a4f7ffd2f8.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-03be7fb347f01058.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/settings/page-96b0280625e4df2b.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-56855545049eb56d.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/setup/layout-76873dd80da4f715.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/page-79a909c9981f0338.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/share/{page-fb9e639c107f5f72.js → page-a9d9a3e66459c1e0.js} +1 -1
  163. package/apps/pwa/out/_next/static/chunks/app/showroom/page-c383cbf2013f7069.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f57c6a2e54dbdca4.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/term/page-6b140f77593576eb.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/today/page-6b767ae2d9d0adfc.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/trace/page-116d6198d5da0e3d.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/vault/page-b9c9b778c7501370.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-a6c78392ab8f6335.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/workflows/page-455183deb4ed0cfe.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workspace/page-6a63e99526bd63d0.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31c22caafe788c08.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/{709a918c.4fd3b4e2a32cb4e3.js → b7459770.6539b11860eb8a9c.js} +7 -7
  174. package/apps/pwa/out/_next/static/chunks/{d9815f84.4918f682a45a8502.js → b7478fef.5917716286213fcc.js} +3 -3
  175. package/apps/pwa/out/_next/static/chunks/{4337e36d.f42c3b391b3d8f20.js → bb6987f9.3647e1a23b0e3217.js} +1 -1
  176. package/apps/pwa/out/_next/static/chunks/de8b930d.d94c0f42acf807fc.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/{8c58d09b.e8724a299658d86f.js → eaad0cba.771200940c529df7.js} +1 -1
  178. package/apps/pwa/out/_next/static/chunks/framework-4b12a946f352cee2.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-941155250ed2644b.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-app-f8b6765abed6d149.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_app-78432194c85388f9.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_error-cdf7390829512b1b.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/webpack-d5f0964a9c2e92d2.js +1 -0
  184. package/apps/pwa/out/approvals/index.html +1 -1
  185. package/apps/pwa/out/approvals/index.txt +22 -22
  186. package/apps/pwa/out/autopilot/index.html +1 -1
  187. package/apps/pwa/out/autopilot/index.txt +22 -22
  188. package/apps/pwa/out/board/index.html +1 -1
  189. package/apps/pwa/out/board/index.txt +22 -22
  190. package/apps/pwa/out/botlab/index.html +1 -1
  191. package/apps/pwa/out/botlab/index.txt +21 -21
  192. package/apps/pwa/out/bots/index.html +1 -1
  193. package/apps/pwa/out/bots/index.txt +21 -21
  194. package/apps/pwa/out/ceo/index.html +1 -1
  195. package/apps/pwa/out/ceo/index.txt +22 -22
  196. package/apps/pwa/out/chat/index.html +1 -1
  197. package/apps/pwa/out/chat/index.txt +21 -21
  198. package/apps/pwa/out/consult/index.html +1 -1
  199. package/apps/pwa/out/consult/index.txt +22 -22
  200. package/apps/pwa/out/control/index.html +1 -1
  201. package/apps/pwa/out/control/index.txt +22 -22
  202. package/apps/pwa/out/decisions/index.html +1 -1
  203. package/apps/pwa/out/decisions/index.txt +21 -21
  204. package/apps/pwa/out/design-check/index.html +1 -1
  205. package/apps/pwa/out/design-check/index.txt +22 -22
  206. package/apps/pwa/out/editor/index.html +1 -1
  207. package/apps/pwa/out/editor/index.txt +22 -22
  208. package/apps/pwa/out/exec/index.html +1 -1
  209. package/apps/pwa/out/exec/index.txt +22 -22
  210. package/apps/pwa/out/field/index.html +1 -1
  211. package/apps/pwa/out/field/index.txt +22 -22
  212. package/apps/pwa/out/healthz +1 -1
  213. package/apps/pwa/out/index.html +1 -1
  214. package/apps/pwa/out/index.txt +21 -21
  215. package/apps/pwa/out/inside/index.html +1 -1
  216. package/apps/pwa/out/inside/index.txt +21 -21
  217. package/apps/pwa/out/intake/index.html +1 -1
  218. package/apps/pwa/out/intake/index.txt +22 -22
  219. package/apps/pwa/out/live/index.html +1 -1
  220. package/apps/pwa/out/live/index.txt +22 -22
  221. package/apps/pwa/out/live-run/index.html +1 -1
  222. package/apps/pwa/out/live-run/index.txt +22 -22
  223. package/apps/pwa/out/loops/index.html +1 -1
  224. package/apps/pwa/out/loops/index.txt +21 -21
  225. package/apps/pwa/out/market/index.html +1 -1
  226. package/apps/pwa/out/market/index.txt +21 -21
  227. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  228. package/apps/pwa/out/missions/placeholder/index.txt +21 -21
  229. package/apps/pwa/out/morning/index.html +1 -1
  230. package/apps/pwa/out/morning/index.txt +22 -22
  231. package/apps/pwa/out/observatory/index.html +1 -1
  232. package/apps/pwa/out/observatory/index.txt +22 -22
  233. package/apps/pwa/out/ops/checklist/index.html +1 -1
  234. package/apps/pwa/out/ops/checklist/index.txt +21 -21
  235. package/apps/pwa/out/ops/release/index.html +1 -1
  236. package/apps/pwa/out/ops/release/index.txt +21 -21
  237. package/apps/pwa/out/ops/seats/index.html +1 -1
  238. package/apps/pwa/out/ops/seats/index.txt +21 -21
  239. package/apps/pwa/out/outputs/index.html +1 -1
  240. package/apps/pwa/out/outputs/index.txt +21 -21
  241. package/apps/pwa/out/reflection/index.html +1 -1
  242. package/apps/pwa/out/reflection/index.txt +22 -22
  243. package/apps/pwa/out/scheduler/index.html +1 -1
  244. package/apps/pwa/out/scheduler/index.txt +22 -22
  245. package/apps/pwa/out/sessions/index.html +1 -1
  246. package/apps/pwa/out/sessions/index.txt +21 -21
  247. package/apps/pwa/out/settings/devices/index.html +1 -1
  248. package/apps/pwa/out/settings/devices/index.txt +22 -22
  249. package/apps/pwa/out/settings/index.html +1 -1
  250. package/apps/pwa/out/settings/index.txt +22 -22
  251. package/apps/pwa/out/setup/done/index.html +1 -1
  252. package/apps/pwa/out/setup/done/index.txt +22 -22
  253. package/apps/pwa/out/setup/index.html +1 -1
  254. package/apps/pwa/out/setup/index.txt +22 -22
  255. package/apps/pwa/out/share/index.html +1 -1
  256. package/apps/pwa/out/share/index.txt +22 -22
  257. package/apps/pwa/out/showroom/index.html +1 -1
  258. package/apps/pwa/out/showroom/index.txt +21 -21
  259. package/apps/pwa/out/tasks/index.html +1 -1
  260. package/apps/pwa/out/tasks/index.txt +22 -22
  261. package/apps/pwa/out/term/index.html +1 -1
  262. package/apps/pwa/out/term/index.txt +22 -22
  263. package/apps/pwa/out/today/index.html +1 -1
  264. package/apps/pwa/out/today/index.txt +22 -22
  265. package/apps/pwa/out/trace/index.html +1 -1
  266. package/apps/pwa/out/trace/index.txt +22 -22
  267. package/apps/pwa/out/vault/index.html +1 -1
  268. package/apps/pwa/out/vault/index.txt +22 -22
  269. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  270. package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
  271. package/apps/pwa/out/workflows/index.html +1 -1
  272. package/apps/pwa/out/workflows/index.txt +22 -22
  273. package/apps/pwa/out/workspace/index.html +1 -1
  274. package/apps/pwa/out/workspace/index.txt +22 -22
  275. package/apps/pwa/out/worktrees/index.html +1 -1
  276. package/apps/pwa/out/worktrees/index.txt +22 -22
  277. package/graphs/release/recipes.yaml +9 -0
  278. package/graphs/release/release-loop.yaml +26 -18
  279. package/package.json +1 -1
  280. package/scripts/ask-op-inbox-once.ts +17 -0
  281. package/scripts/brand/check.ts +13 -4
  282. package/scripts/gate-0226-accepted-remaining-90.txt +91 -0
  283. package/scripts/gate-0226-failure-census.ts +39 -0
  284. package/scripts/intake-collect-telegram-saved-stage.ts +48 -0
  285. package/scripts/provision-export-redactions.ts +15 -0
  286. package/scripts/public-copy-lint.ts +122 -0
  287. package/scripts/public-export.ts +69 -2
  288. package/scripts/ratchets/next-tui-entry-budget.ts +124 -0
  289. package/scripts/ratchets/train-side-invariants.sh +7 -1
  290. package/scripts/release-loop/cut-branch.ts +61 -12
  291. package/scripts/release-loop/gate-node.ts +161 -25
  292. package/scripts/release-loop/gate-partial.ts +82 -3
  293. package/scripts/release-loop/gate-prewarm-plan.ts +35 -0
  294. package/scripts/release-loop/gate-radius-live.ts +4 -2
  295. package/scripts/release-loop/resume-cap.ts +2 -1
  296. package/scripts/release-loop/resume-release.ts +297 -8
  297. package/scripts/release-loop/resume.ts +13 -5
  298. package/scripts/release-loop/traffic-node.ts +12 -4
  299. package/scripts/release-loop/traffic-transition.ts +53 -13
  300. package/scripts/release-loop/version-node.ts +29 -4
  301. package/scripts/ux-sim.ts +1 -1
  302. package/scripts/video-pipeline.ts +2 -2
  303. package/src/acp/core-turn-bridge.ts +6 -1
  304. package/src/agent/run-context.ts +1 -1
  305. package/src/agent/task-notification.ts +3 -1
  306. package/src/agent-substrate/review-observation.ts +10 -0
  307. package/src/agent-substrate/self-review-cli.ts +195 -27
  308. package/src/auto-research/question-ladder-contract.ts +137 -0
  309. package/src/auto-research/research-turn.ts +134 -0
  310. package/src/autopilot/discovery/doc-lint.ts +1 -1
  311. package/src/bots/screen-probe.ts +1 -1
  312. package/src/capture/tui-self-report.ts +10 -2
  313. package/src/chat/index.ts +1 -0
  314. package/src/chat/tool-render/dispatch.ts +8 -1
  315. package/src/cli/guardian-cli.ts +3 -1
  316. package/src/cli/intake-cli.ts +48 -12
  317. package/src/cli/lesson-cli.ts +69 -9
  318. package/src/cli/pr-land-overlap-evidence.ts +38 -5
  319. package/src/cli/pr-land-refusal.ts +1 -1
  320. package/src/cli/pty-takeover-cli.ts +58 -5
  321. package/src/cli/release-checklist-read-cli.ts +5 -2
  322. package/src/cli/release-cli.ts +47 -9
  323. package/src/cli/review-carry.ts +77 -0
  324. package/src/cli/self-update.ts +1 -1
  325. package/src/cli/tasks-card-show.ts +31 -1
  326. package/src/cli/tasks-cli.ts +62 -23
  327. package/src/cli/tasks-read-cli.ts +13 -5
  328. package/src/context-bus/context-now.ts +86 -2
  329. package/src/core-turn/run-core-turn.ts +9 -2
  330. package/src/core-turn/types.ts +3 -0
  331. package/src/dashboard/index.ts +18 -17
  332. package/src/dashboard/input/terminal-modal-router.ts +8 -159
  333. package/src/dashboard/side-basic-runtime.ts +61 -0
  334. package/src/dashboard/slash-runtime/dashboard-handlers.ts +2 -0
  335. package/src/dashboard/term-slash-runtime.ts +2 -0
  336. package/src/dashboard/turn-preamble.ts +28 -4
  337. package/src/domains/guardian-tick.ts +9 -0
  338. package/src/git-fs/watcher.ts +27 -22
  339. package/src/git-fs/worktree.ts +19 -3
  340. package/src/graph-runner/graph-grow.ts +37 -9
  341. package/src/graph-runner/runner.ts +17 -2
  342. package/src/grok/credential.ts +107 -36
  343. package/src/hq/fence-wrapper.ts +1 -1
  344. package/src/hq/transfer.ts +1 -1
  345. package/src/index.ts +44 -14
  346. package/src/intake-plane/collect-telegram-saved.ts +54 -5
  347. package/src/interactive-terminal-modal.ts +3 -0
  348. package/src/keybindings.ts +17 -22
  349. package/src/llm/provider-credentials.ts +1 -1
  350. package/src/llm-tool-schema.ts +20 -0
  351. package/src/llm.ts +81 -11
  352. package/src/loops/orchestrator/inbox-once.ts +28 -0
  353. package/src/loops/orchestrator/inbox.ts +130 -1
  354. package/src/loops/orchestrator/tick.ts +6 -3
  355. package/src/maturity/feature-maturity.ts +1 -0
  356. package/src/nexus/api/channel-ask-op.ts +96 -0
  357. package/src/nexus/api/discord-trigger-bot.ts +28 -1
  358. package/src/nexus/api/telegram-trigger-bot.ts +24 -0
  359. package/src/nexus/api/terminals.ts +28 -6
  360. package/src/patents/ledger.ts +18 -1
  361. package/src/preview/terminal.ts +4 -2
  362. package/src/prompt-cache/anthropic.ts +2 -0
  363. package/src/prompt-enhance/enhance.ts +5 -0
  364. package/src/pty-shell/pty-manifest.ts +16 -2
  365. package/src/release-loop/gate-shards.ts +61 -0
  366. package/src/release-loop/light-rc.ts +7 -4
  367. package/src/release-loop/rehearsal.ts +1 -1
  368. package/src/repl/index.ts +10 -0
  369. package/src/research/axis-fanout.ts +133 -0
  370. package/src/research/edge-decision.ts +88 -0
  371. package/src/research/output-router.ts +127 -0
  372. package/src/research-bridge/format.ts +16 -1
  373. package/src/research-bridge/index.ts +1 -0
  374. package/src/research-bridge/invoke.ts +41 -5
  375. package/src/self-dev/decompose.ts +45 -9
  376. package/src/self-dev/draft-residue.ts +221 -2
  377. package/src/self-dev/draft-sweep.ts +29 -16
  378. package/src/self-dev/orchestrate.ts +1 -0
  379. package/src/self-dev/run-supervisor.ts +22 -7
  380. package/src/self-dev/shard-run.ts +260 -3
  381. package/src/self-implement/comment-only-source.ts +54 -0
  382. package/src/self-implement/doctor-visit.ts +25 -0
  383. package/src/self-implement/gate-artifacts.ts +2 -2
  384. package/src/self-implement/gate-baseline.ts +44 -9
  385. package/src/self-implement/gate-cli.ts +151 -86
  386. package/src/self-implement/gate-failure-signature.ts +44 -0
  387. package/src/self-implement/gate-remote.ts +2 -0
  388. package/src/self-implement/goal-author.ts +74 -3
  389. package/src/self-implement/goal-doc/section.ts +22 -0
  390. package/src/self-implement/goal-supervisor-fill.ts +38 -4
  391. package/src/self-implement/knowhow-pack.ts +120 -7
  392. package/src/self-implement/l8-merge-queue.ts +198 -32
  393. package/src/self-implement/off-diff-evidence.ts +23 -0
  394. package/src/self-implement/orchestrator.ts +55 -12
  395. package/src/self-implement/pod-inplace-worktree.ts +82 -0
  396. package/src/self-implement/pod-warm-pool-profile.ts +2 -2
  397. package/src/self-implement/premise-review.ts +7 -5
  398. package/src/self-implement/run-ledger.ts +258 -0
  399. package/src/self-implement/seams.ts +33 -10
  400. package/src/self-implement/self-implement-cli.ts +26 -2
  401. package/src/session/channel-ask-op-envelope.ts +36 -0
  402. package/src/session/channel-op-reply-route.ts +200 -0
  403. package/src/session/chat.ts +3 -0
  404. package/src/session/index.ts +2 -0
  405. package/src/session-runtime/index.ts +9 -22
  406. package/src/skills/runner.ts +7 -3
  407. package/src/skills/tools/dev-harness.ts +2 -2
  408. package/src/skills/tools/schedule-wakeup.ts +1 -6
  409. package/src/status/bar.ts +3 -3
  410. package/src/storage/file-lock.ts +17 -4
  411. package/src/task-agent/adopt-pr.ts +63 -2
  412. package/src/task-agent/evidence-command-normalize.ts +42 -0
  413. package/src/task-agent/export-redactions-provision.ts +43 -0
  414. package/src/task-agent/parent-host.ts +59 -12
  415. package/src/task-agent/repair-cycle.ts +67 -8
  416. package/src/task-agent/repair-worker.ts +37 -1
  417. package/src/task-agent/task-hand.ts +98 -10
  418. package/src/task-agent/tier-split.ts +37 -3
  419. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +31 -4
  420. package/src/task-orchestrator/surfaces/pod-pool.ts +27 -4
  421. package/src/task-orchestrator/surfaces/self-implement-pod.ts +126 -48
  422. package/src/task-orchestrator/surfaces/self-implement.ts +33 -2
  423. package/src/terminal/session-registry.ts +1 -0
  424. package/src/terminal/terminal-modal-router.ts +156 -0
  425. package/src/tool-hints/gate.ts +6 -4
  426. package/src/tool-hints/signals.ts +33 -1
  427. package/src/tui/chat/background-job-status.ts +8 -2
  428. package/src/tui/entry.ts +469 -31
  429. package/src/tui/sidetab/attach.ts +304 -0
  430. package/src/tui/sidetab/list.ts +176 -0
  431. package/src/tui/sidetab/nl-tool.ts +136 -0
  432. package/src/tui/slash/resume-command.ts +23 -0
  433. package/src/tui/slash/resume.ts +3 -23
  434. package/src/tui/slash/term-next.ts +2 -1
  435. package/src/tui-client/local-chat-turn.ts +29 -8
  436. package/src/user-config.ts +11 -2
  437. package/src/ux-sim/fake-llm.ts +11 -5
  438. package/src/ux-sim/next-tui-seat-scenarios.ts +53 -1
  439. package/src/version/packed-revision.json +1 -1
  440. package/apps/pwa/out/_next/static/chunks/1041-5841aae848fb6403.js +0 -1
  441. package/apps/pwa/out/_next/static/chunks/1087.3177e54f672dc526.js +0 -1
  442. package/apps/pwa/out/_next/static/chunks/1105.efe1e13503ac7a33.js +0 -1
  443. package/apps/pwa/out/_next/static/chunks/1417-1768233ac92f8a5d.js +0 -1
  444. package/apps/pwa/out/_next/static/chunks/1612.81bbc19af06af16c.js +0 -1
  445. package/apps/pwa/out/_next/static/chunks/1766.728d42053070bb2d.js +0 -1
  446. package/apps/pwa/out/_next/static/chunks/1773.d38e945481e04125.js +0 -1
  447. package/apps/pwa/out/_next/static/chunks/1974-c12f9600da3d3c26.js +0 -1
  448. package/apps/pwa/out/_next/static/chunks/1ed5e8af.cf424815c7c0efad.js +0 -1
  449. package/apps/pwa/out/_next/static/chunks/2061.7232dc9dc058417f.js +0 -1
  450. package/apps/pwa/out/_next/static/chunks/210.90b2cbd64926cf34.js +0 -32
  451. package/apps/pwa/out/_next/static/chunks/2318-450e75d07605b8fe.js +0 -1
  452. package/apps/pwa/out/_next/static/chunks/244-a810d282a20f1783.js +0 -1
  453. package/apps/pwa/out/_next/static/chunks/2514.03b60f266531a015.js +0 -148
  454. package/apps/pwa/out/_next/static/chunks/2526-a657538869888604.js +0 -1
  455. package/apps/pwa/out/_next/static/chunks/2533-6f0341ff075809e9.js +0 -1
  456. package/apps/pwa/out/_next/static/chunks/2676.e7994c695ef0f30d.js +0 -1
  457. package/apps/pwa/out/_next/static/chunks/2734.3ffdf54254fb6f99.js +0 -27
  458. package/apps/pwa/out/_next/static/chunks/2770-23252bf3a37880b9.js +0 -1
  459. package/apps/pwa/out/_next/static/chunks/2969.66b8b545139d1544.js +0 -1
  460. package/apps/pwa/out/_next/static/chunks/3128.a9210d08800ea6a8.js +0 -1
  461. package/apps/pwa/out/_next/static/chunks/3212.12179fd3eee8cef1.js +0 -1
  462. package/apps/pwa/out/_next/static/chunks/3578.4e9f9948a9a6f2f4.js +0 -2
  463. package/apps/pwa/out/_next/static/chunks/3760-2909ddb9279281cd.js +0 -1
  464. package/apps/pwa/out/_next/static/chunks/3825.1921b5901bf33dce.js +0 -36
  465. package/apps/pwa/out/_next/static/chunks/3994-63eb884ed0547011.js +0 -1
  466. package/apps/pwa/out/_next/static/chunks/4115.5b438b99f0ae729f.js +0 -226
  467. package/apps/pwa/out/_next/static/chunks/4269.d8f9f34bb0fc6c43.js +0 -287
  468. package/apps/pwa/out/_next/static/chunks/431-4eed5bd2c5fe1719.js +0 -1
  469. package/apps/pwa/out/_next/static/chunks/4313.76f232c304a15bde.js +0 -1
  470. package/apps/pwa/out/_next/static/chunks/4324.c99ba3e83eb1b691.js +0 -1
  471. package/apps/pwa/out/_next/static/chunks/4599-96eff3f2a9c242c1.js +0 -1
  472. package/apps/pwa/out/_next/static/chunks/4725-34bed25720cc326b.js +0 -1
  473. package/apps/pwa/out/_next/static/chunks/4825-a9d9d4e9f5892309.js +0 -1
  474. package/apps/pwa/out/_next/static/chunks/4833-7934c592c5569f00.js +0 -1
  475. package/apps/pwa/out/_next/static/chunks/4879-68518f547a207aff.js +0 -1
  476. package/apps/pwa/out/_next/static/chunks/4896.f9b70285363b27b2.js +0 -24
  477. package/apps/pwa/out/_next/static/chunks/49aa54ab.80f39398e0ed0f30.js +0 -1
  478. package/apps/pwa/out/_next/static/chunks/5056.482687319b82638e.js +0 -104
  479. package/apps/pwa/out/_next/static/chunks/506.16b83d56a437a9ee.js +0 -1
  480. package/apps/pwa/out/_next/static/chunks/5260-20593b0a25d0afbc.js +0 -1
  481. package/apps/pwa/out/_next/static/chunks/5404.cc4e9fec26fd5925.js +0 -1
  482. package/apps/pwa/out/_next/static/chunks/576.485e83e3f3dec19a.js +0 -1
  483. package/apps/pwa/out/_next/static/chunks/5832-a6be14e663f65726.js +0 -1
  484. package/apps/pwa/out/_next/static/chunks/5854-56648a9fe646b9a0.js +0 -1
  485. package/apps/pwa/out/_next/static/chunks/5925.3b1f8b135710249b.js +0 -93
  486. package/apps/pwa/out/_next/static/chunks/6168.17749aa3905fa073.js +0 -74
  487. package/apps/pwa/out/_next/static/chunks/6416.5d50b8ec9986ca98.js +0 -1
  488. package/apps/pwa/out/_next/static/chunks/6758.e7fbf532953c0783.js +0 -1
  489. package/apps/pwa/out/_next/static/chunks/6795.0cb8a59730d99703.js +0 -149
  490. package/apps/pwa/out/_next/static/chunks/6971-fb5f9dc0a2cb5d2d.js +0 -1
  491. package/apps/pwa/out/_next/static/chunks/717.522b08685979b814.js +0 -1
  492. package/apps/pwa/out/_next/static/chunks/7191.e41eb8f455c7f089.js +0 -41
  493. package/apps/pwa/out/_next/static/chunks/7233.8e744b7513119409.js +0 -1
  494. package/apps/pwa/out/_next/static/chunks/7238-97c2226f80504bd8.js +0 -1
  495. package/apps/pwa/out/_next/static/chunks/7285.307963afcf7e7660.js +0 -24
  496. package/apps/pwa/out/_next/static/chunks/7299-fbe4b8c47a87614a.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/7314.df0d95930b0c3f50.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/7406.c0589fdf2ee66629.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/752-90e3e7daad3d8e39.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/7524-bcd3d9928c6842d9.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/7549.9a4de3bf231cfc52.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/765.76ae970ceb597b5b.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/7893-e444a1b0809cd1e4.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/7956-33748cabb3a05562.js +0 -1
  505. package/apps/pwa/out/_next/static/chunks/7c5ed21f-f286708e57cf88f7.js +0 -1
  506. package/apps/pwa/out/_next/static/chunks/8030-aa66b478b53dbfca.js +0 -1
  507. package/apps/pwa/out/_next/static/chunks/8051.c88c07dacbbc5f21.js +0 -8
  508. package/apps/pwa/out/_next/static/chunks/8080-40d781e7704d3671.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/8149.80e402d4345e8dae.js +0 -77
  510. package/apps/pwa/out/_next/static/chunks/8205.50a5268f4e12ba0b.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/8231.a128feaa6f8b6079.js +0 -1
  512. package/apps/pwa/out/_next/static/chunks/85.4ddb1dda2ea3ffa2.js +0 -286
  513. package/apps/pwa/out/_next/static/chunks/8537.26517a09e8247beb.js +0 -1
  514. package/apps/pwa/out/_next/static/chunks/8595-e9b7081ab773a61f.js +0 -1
  515. package/apps/pwa/out/_next/static/chunks/8821-30cc65a61a60951f.js +0 -1
  516. package/apps/pwa/out/_next/static/chunks/8905.b3385aadb7b133e6.js +0 -79
  517. package/apps/pwa/out/_next/static/chunks/8983.246bb3a1ac38f50f.js +0 -1
  518. package/apps/pwa/out/_next/static/chunks/9072.18342f5941a1f3ec.js +0 -1
  519. package/apps/pwa/out/_next/static/chunks/9123.9df04866b2d6e00f.js +0 -1
  520. package/apps/pwa/out/_next/static/chunks/9175-7d383df15a82e2be.js +0 -1
  521. package/apps/pwa/out/_next/static/chunks/9181-bf89c07ce5b8f599.js +0 -1
  522. package/apps/pwa/out/_next/static/chunks/920-429bbc4504b45f71.js +0 -1
  523. package/apps/pwa/out/_next/static/chunks/9207.625edf45d656151c.js +0 -1
  524. package/apps/pwa/out/_next/static/chunks/9378.d2575c6ffd6dd756.js +0 -94
  525. package/apps/pwa/out/_next/static/chunks/957.db5b5c4d32a35240.js +0 -1
  526. package/apps/pwa/out/_next/static/chunks/958.53a8dcf4729e6997.js +0 -64
  527. package/apps/pwa/out/_next/static/chunks/9647.8f0cd0898c592a29.js +0 -1
  528. package/apps/pwa/out/_next/static/chunks/9665-26abb02665704c2c.js +0 -1
  529. package/apps/pwa/out/_next/static/chunks/9703.e7d81b6faef1050a.js +0 -1
  530. package/apps/pwa/out/_next/static/chunks/9882-7a9164b3c2dacbe5.js +0 -1
  531. package/apps/pwa/out/_next/static/chunks/9998.b74e033efadecd10.js +0 -1
  532. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c604fb980a634d88.js +0 -1
  533. package/apps/pwa/out/_next/static/chunks/app/approvals/page-ec2d8a4979ca9075.js +0 -1
  534. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-cb6add08ad3c7660.js +0 -1
  535. package/apps/pwa/out/_next/static/chunks/app/board/page-e2eaa321ec223fe6.js +0 -1
  536. package/apps/pwa/out/_next/static/chunks/app/botlab/page-5b8a09c475b5f844.js +0 -1
  537. package/apps/pwa/out/_next/static/chunks/app/bots/page-91a77daedfbac191.js +0 -1
  538. package/apps/pwa/out/_next/static/chunks/app/ceo/page-f37fc5e974bda39b.js +0 -1
  539. package/apps/pwa/out/_next/static/chunks/app/chat/page-91429a0b78415a94.js +0 -1
  540. package/apps/pwa/out/_next/static/chunks/app/consult/page-b95798b58263b910.js +0 -1
  541. package/apps/pwa/out/_next/static/chunks/app/control/page-763d53e175e17aa1.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/app/decisions/page-d55023204055b12f.js +0 -1
  543. package/apps/pwa/out/_next/static/chunks/app/design-check/page-89d2140d78abc810.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/app/editor/page-82dc069abfe27f55.js +0 -1
  545. package/apps/pwa/out/_next/static/chunks/app/exec/page-25d9eee7beb552c0.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/app/field/page-770b4594c713ee97.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/app/healthz/route-c604fb980a634d88.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/app/inside/page-ba8e0dcae4f83985.js +0 -1
  549. package/apps/pwa/out/_next/static/chunks/app/layout-09788e1aa9a87db6.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/app/live/page-3337c6a1328c2674.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/app/live-run/page-89acd5d68c0bc560.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/app/loops/page-5c13e2c76371d797.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/app/market/page-653a9f18642825c6.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-fe986fe79b107546.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/app/morning/page-1e5da297557d2354.js +0 -1
  556. package/apps/pwa/out/_next/static/chunks/app/not-found-91ea9f5869a40943.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/app/observatory/page-7824e21b999962e2.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-53d00a60cc75a922.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4e931248b3f9574b.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/app/outputs/page-443364c9e4a23b31.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/app/reflection/page-25ab9c614c14129b.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-dbcdcfac0a395a8a.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/app/sessions/page-bf1de6645089ec58.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c4201761a5e839c9.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/app/settings/page-c304f469ea5d70e4.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8e5a238cf5e50c9c.js +0 -1
  567. package/apps/pwa/out/_next/static/chunks/app/setup/layout-c604fb980a634d88.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/app/setup/page-32b526069ee50b3c.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/app/showroom/page-84220c776608cedc.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/app/tasks/page-e475618cbfd5ca2b.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/app/term/page-ac2c1e6169cfce57.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/app/today/page-fccd3020af2f8806.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/app/trace/page-9c0d7198ff7b5a91.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/app/vault/page-d54b1ec9b9c6430d.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-8c4bef1791d732ca.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/app/workflows/page-46fcc0b0bbc6fbfe.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/app/workspace/page-c1c80e345fa9d6f5.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-7dfda00afd172f5b.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/f21fd540.63ac75328926ed30.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/framework-77fdcc873519a6cd.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/main-app-d0d620d2ce235713.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/main-e0ef79c79337ba90.js +0 -1
  583. package/apps/pwa/out/_next/static/chunks/pages/_app-84b1bc71e3f9ce6a.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/pages/_error-2acb61f1363128cb.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/webpack-462ff5727ceb7824.js +0 -1
  586. package/scripts/migrate-monad-to-elanous.ts +0 -195
  587. package/scripts/rebrand-elanous.ts +0 -242
  588. /package/apps/pwa/out/_next/static/{8kITumG9GAZ1LwT9POrAR → RrjXTwz-urRAbChP1bX02}/_ssgManifest.js +0 -0
@@ -3,23 +3,32 @@
3
3
  // every later node reads the repaired commit without anyone hand-editing the run state.
4
4
  import { spawnSync } from 'node:child_process';
5
5
  import { createHash } from 'node:crypto';
6
- import { accessSync, closeSync, constants, copyFileSync, existsSync, fstatSync, openSync, readFileSync, readdirSync, renameSync, statSync, writeFileSync } from 'node:fs';
7
- import { join, resolve } from 'node:path';
6
+ import { accessSync, closeSync, constants, copyFileSync, existsSync, fstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, realpathSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
7
+ import { tmpdir } from 'node:os';
8
+ import { dirname, join, resolve } from 'node:path';
8
9
  import { debug } from '../../src/debug/log.js';
9
10
  import { effectiveInstanceRoot, releaseLedgerRoot } from '../../src/instance/resolve.js';
10
11
  import { lastJsonObject, runGraph, verifyReleaseCommandRoot, verifyReleaseRecipeSnapshot, type GraphRunState } from '../../src/graph-runner/runner.js';
11
12
  import { withOpSeatFlock } from '../../src/steward/resync-tick.js';
12
13
  import { releaseRunUniverse } from './release-universe.js';
13
14
  import { isReleaseVersion } from './release-version.js';
14
- import { changedFilesBetween, clearPartialPlan, planPartialRegate, planStalledShardRecheck, writePartialPlan, writeRecheckPlan } from './gate-partial.js';
15
+ import { changedFilesBetween, clearPartialPlan, collectExecutedPodShardImages, planPartialRegate, planStalledShardRecheck, writePartialPlan, writeRecheckPlan } from './gate-partial.js';
15
16
  import { gitClosureTree, testsReachingChanged, TRACEABLE_RELEASE_JSON, type ClosureTree } from './gate-closure.js';
16
17
  import { releaseResumeAcceptedRegressions, releaseResumeGateExecutor, releaseResumeGatePodPool, releaseResumeWaiver, type AcceptedRegression } from './resume.js';
17
18
  import { reserveReleaseResumeAttempt } from './resume-cap.js';
19
+ import { cutReleaseBranch } from './cut-branch.js';
18
20
  import { releaseLoopConfig } from './unattended-release.js';
19
21
  import { trafficOptionsFromConfig } from './traffic-transition.js';
20
22
 
21
23
  const GRAPH = join(import.meta.dir, '../../graphs/release/release-loop.yaml');
22
24
 
25
+ /** Compare existing artifact paths by filesystem identity so macOS /var and /private/var aliases agree. */
26
+ function sameRealPath(left: unknown, right: unknown): boolean {
27
+ if (typeof left !== 'string' || typeof right !== 'string') return false;
28
+ try { return realpathSync(left) === realpathSync(right); }
29
+ catch { return false; }
30
+ }
31
+
23
32
  export interface ResumeReleaseDeps {
24
33
  root?: string;
25
34
  repo?: string;
@@ -227,7 +236,8 @@ function verifyResumePreflight(run: GraphRunState, opts: ResumeReleaseOptions, d
227
236
  let owner: { runId?: unknown; commit?: unknown } | undefined;
228
237
  let ownerReadable = !existsSync(ownerPath);
229
238
  try { owner = JSON.parse(readFileSync(ownerPath, 'utf8')) as typeof owner; ownerReadable = true; } catch { /* old artifacts can be owned by the run ledger */ }
230
- const ledgerOwns = output?.out === prepared && output.commit === currentTip && output.outcome === 'ok';
239
+ const ledgerOwns = output !== undefined && sameRealPath(output.out, prepared)
240
+ && output.commit === currentTip && output.outcome === 'ok';
231
241
  const receiptOwns = owner ? owner.runId === opts.runId && owner.commit === currentTip : ownerReadable && ledgerOwns;
232
242
  if (!ledgerOwns || manifest?.version !== version || manifest.sourceCommit !== currentTip || !receiptOwns) {
233
243
  throw new Error(`prepared artifact is outside the current release run ledger: ${prepared} — remedy: preserve it, compare release.json and .release-node-owner.json with run ${opts.runId}, then resolve ownership before resume`);
@@ -265,8 +275,8 @@ function verifyResumePreflight(run: GraphRunState, opts: ResumeReleaseOptions, d
265
275
  const outputOwner = docsRecord?.artifactOwner as { runId?: unknown; attempt?: unknown } | undefined;
266
276
  const receiptMatches = receipt?.branch === docsBranch && receipt.owner?.runId === opts.runId
267
277
  && receipt.owner.attempt === outputOwner?.attempt && outputOwner?.runId === opts.runId
268
- && typeof receipt.worktree === 'string' && worktree === receipt.worktree && docsWorktrees.includes(receipt.worktree)
269
- && existsSync(receipt.worktree);
278
+ && typeof receipt.worktree === 'string' && sameRealPath(worktree, receipt.worktree)
279
+ && docsWorktrees.some((listedWorktree) => sameRealPath(listedWorktree, receipt.worktree));
270
280
  if (hasDocsArtifact && !receiptMatches) {
271
281
  throw new Error(`docs branch/worktree is outside the current release run ledger: ${docsBranch} — remedy: compare ${receiptPath} with the saved docs node output and resolve ownership before resume`);
272
282
  }
@@ -290,7 +300,7 @@ export type PartialOutcome = { requested: false }
290
300
 
291
301
  /** Build (or clear) the partial re-gate plan for the new tip. Fail-closed: any doubt leaves a full gate.
292
302
  * GATE-REUSE-BY-DIFF: `--from gate` on a moved tip plans by default (`partial: undefined`); `partial: false` opts out. */
293
- function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean }, deps: ResumeReleaseDeps, dryRun = false): PartialOutcome {
303
+ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean; gateExecutor?: string }, deps: ResumeReleaseDeps, dryRun = false): PartialOutcome {
294
304
  const root = deps.root ?? effectiveInstanceRoot();
295
305
  // Any resume starts from no plan: one without a partial plan (from whatever node) never inherits an older plan.
296
306
  if (!dryRun) clearPartialPlan(root, tip.version);
@@ -328,7 +338,7 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
328
338
  for (const item of stalled) {
329
339
  if (!item || typeof item !== 'object' || !Array.isArray((item as { files?: unknown }).files)
330
340
  || !(item as { files: unknown[] }).files.every((file) => typeof file === 'string')
331
- || !['incomplete', 'no-output', 'job-failed', 'exit-code', 'unattributed', 'timeout'].includes(String((item as { reason?: unknown }).reason)))
341
+ || !['incomplete', 'no-output', 'job-failed', 'exit-code', 'unattributed', 'timeout', 'admission-timeout', 'admission-error'].includes(String((item as { reason?: unknown }).reason)))
332
342
  return fallback('stalled-shard record is malformed');
333
343
  stalledFiles.push(...(item as { files: string[] }).files);
334
344
  }
@@ -387,9 +397,16 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
387
397
  const jsonClosure = testsReachingChanged(tree, [TRACEABLE_RELEASE_JSON], tests, { cliEntries: CLI_ENTRIES });
388
398
  if (!jsonClosure.ok || jsonClosure.reached === 0) return fallback('known-issues JSON reaches no verified test — full gate');
389
399
  }
400
+ const priorCutLogs = [...new Set([deps.ledgerRoot ?? releaseLedgerRoot(), root])]
401
+ .map((location) => join(location, 'release', tip.version, 'gate-logs', 'cut'))
402
+ .find((dir) => existsSync(dir));
403
+ const savedInput = saved.input as { gateExecutor?: unknown; gatePodPool?: unknown };
404
+ const podGate = (opts.gateExecutor ?? savedInput.gateExecutor ?? (savedInput.gatePodPool ? 'pod' : undefined)) === 'pod';
405
+ const priorShards = priorCutLogs ? collectExecutedPodShardImages(priorCutLogs, tip.from) : undefined;
390
406
  const planned = planPartialRegate({ version: tip.version, priorCommit: tip.from, forCommit: tip.to, changedFiles: changed,
391
407
  priorFailures: prior.failures as string[], priorErrors: (prior.errors ?? []) as string[],
392
408
  existsAtTip: (file) => tracked.has(file),
409
+ ...(podGate ? { priorShards: priorShards ?? [] } : {}),
393
410
  closure: (changedCode) => testsReachingChanged(tree, changedCode, tests, { cliEntries: CLI_ENTRIES }) });
394
411
  if (!planned.ok) return fallback(planned.reason);
395
412
  if (planned.plan.files.length >= tests.length) return fallback(`every test is re-run (${tests.length}) — full gate`);
@@ -539,3 +556,275 @@ async function resumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: ResumeRe
539
556
  deps: { root: deps.root, ...(deps.runBash ? { runBash: deps.runBash } : {}) } });
540
557
  return { tip, partial, state };
541
558
  }
559
+
560
+ // RELEASE-RECOVER-ONE-CMD (0.2.36): a failed release run is repaired by three commands that used to be chained by
561
+ // hand (append dry-run → append → resume --from export-check). `release recover` runs them in that order, stops the
562
+ // moment one fails, and leaves every stage's result in the resume-attempts ledger.
563
+ export type RecoverStageName = 'append-dry-run' | 'append' | 'resume';
564
+ const RECOVER_STAGES: readonly RecoverStageName[] = ['append-dry-run', 'append', 'resume'];
565
+ /** The first node that consumes the cut — a moved tip must restart no later than here, so it is the default. */
566
+ const RECOVER_FIRST_NODE = 'export-check';
567
+
568
+ export interface RecoverReleaseOptions {
569
+ runId: string;
570
+ /** main commits that repair the cut — appended to release/<v> as a fast-forward only. */
571
+ pick: string[];
572
+ /** Node the resume restarts at — default `export-check`. */
573
+ from?: string;
574
+ commandRoot?: string;
575
+ partial?: boolean;
576
+ /** The checkout the append runs in (its `origin` remote); default the process cwd. */
577
+ repoRoot?: string;
578
+ }
579
+
580
+ export interface RecoverStageRecord {
581
+ stage: RecoverStageName; outcome: 'ok' | 'failed'; at: string; branch?: string; commit?: string | null;
582
+ from?: string; gatePlan?: 'not-run' | 'partial' | 'full'; status?: string; reason?: string;
583
+ /** The `--pick` list this recovery appended — a re-entry skips the append only for the same list. */
584
+ pick?: string[];
585
+ }
586
+
587
+ export interface RecoverReleaseResult {
588
+ runId: string; version: string; branch: string; fromNode: string; stages: RecoverStageRecord[];
589
+ resume?: { status: string; tip: TipRefresh; partial: PartialOutcome };
590
+ }
591
+
592
+ export interface RecoverReleaseDeps extends ResumeReleaseDeps {
593
+ /** `release cut-branch --append` — tests record the plan instead of pushing the branch. */
594
+ cutBranch?: typeof cutReleaseBranch;
595
+ }
596
+
597
+ /** A recovery that stopped at a stage: the stages after it never ran, and a human decides the next move. */
598
+ export class ReleaseRecoverStopped extends Error {
599
+ constructor(readonly stage: RecoverStageName, readonly stages: readonly RecoverStageRecord[],
600
+ readonly notRun: readonly RecoverStageName[], reason: string) {
601
+ super(`release recover ${stage} 단계에서 멈췄다: ${reason} — ${notRun.length
602
+ ? `${notRun.join(' · ')} 단계는 실행하지 않았다` : '뒤에 실행할 단계가 없다'} · 사람이 판단한다(단계 원장: release/<v>/resume-attempts/<runId>.json)`);
603
+ this.name = 'ReleaseRecoverStopped';
604
+ }
605
+ }
606
+
607
+ /** Append one stage result to the resume-attempts ledger. `attempts[]` keeps its shape — the stage results are added. */
608
+ function recordRecoverStage(entry: { root: string; runId: string; version: string; priorCountKnown: boolean; stage: RecoverStageRecord }): void {
609
+ const path = join(entry.root, 'release', entry.version, 'resume-attempts', `${entry.runId}.json`);
610
+ const saved = existsSync(path)
611
+ ? JSON.parse(readFileSync(path, 'utf8')) as { attempts?: unknown; recoveries?: unknown; priorCountKnown?: unknown }
612
+ : undefined;
613
+ if (saved !== undefined && (!saved || typeof saved !== 'object' || Array.isArray(saved) || !Array.isArray(saved.attempts)
614
+ || (saved.recoveries !== undefined && !Array.isArray(saved.recoveries)))) throw new Error('release resume attempts ledger invalid');
615
+ const next = { ...(saved ?? {}), version: entry.version, runId: entry.runId,
616
+ priorCountKnown: saved?.priorCountKnown ?? entry.priorCountKnown,
617
+ attempts: (saved?.attempts ?? []) as string[],
618
+ recoveries: [...((saved?.recoveries ?? []) as RecoverStageRecord[]), entry.stage] };
619
+ mkdirSync(dirname(path), { recursive: true });
620
+ const temporary = `${path}.${process.pid}.recover.tmp`;
621
+ writeFileSync(temporary, `${JSON.stringify(next)}\n`, { mode: 0o600 });
622
+ renameSync(temporary, path);
623
+ }
624
+
625
+ /**
626
+ * Repair a failed release run in one command: append dry-run (plan only) → append (fast-forward release/<v> onto the
627
+ * picked repairs) → resume --from export-check (preflight plan, then the graph). A failing stage stops the command:
628
+ * the later stages never run, and the stage results are already in the ledger for the human to read.
629
+ */
630
+ export async function recoverReleaseRun(opts: RecoverReleaseOptions, deps: RecoverReleaseDeps = {}): Promise<RecoverReleaseResult> {
631
+ if (!Array.isArray(opts.pick) || !opts.pick.length) throw new Error('release recover requires at least one --pick commit');
632
+ const root = releaseRunUniverse({ root: deps.root });
633
+ const statePath = join(root, 'graph-runs', 'release-loop', `${safeRunId(opts.runId)}.json`);
634
+ const savedRun = JSON.parse(readFileSync(statePath, 'utf8')) as GraphRunState;
635
+ if (savedRun.graphId !== 'release-loop' || savedRun.runId !== opts.runId) throw new Error(`run identity mismatch: ${statePath}`);
636
+ // The saved version-release record names what to repair: the release branch and the commit the run cut.
637
+ const versionRecord = savedRun.nodes.find((node) => node.nodeId === 'version-release');
638
+ const recorded = lastJsonObject(versionRecord?.output);
639
+ const version = typeof recorded?.version === 'string' ? recorded.version : '';
640
+ // RELEASE-RECOVER-BASE: a first recover that reached the resume left refreshed-tip lines in the
641
+ // version-release output, and `lastJsonObject` names the CURRENT branch tip. The append builds on the
642
+ // commit the run CUT — an ancestor of the picked repairs on origin/main — so walk to the FIRST JSON
643
+ // object of the output for that original cut. Without this, a second recover with a different --pick
644
+ // fails append-dry-run with «base … is not an ancestor of origin/main» and strands the new repair
645
+ // unpushed while the run looks recovered.
646
+ const firstCutObject = (text: unknown): { version?: unknown; commit?: unknown } | undefined => {
647
+ if (typeof text !== 'string') return undefined;
648
+ for (const line of text.split('\n')) {
649
+ const trimmed = line.trim();
650
+ if (!trimmed.startsWith('{')) continue;
651
+ try {
652
+ const parsed: unknown = JSON.parse(trimmed);
653
+ if (parsed && typeof parsed === 'object' && typeof (parsed as { version?: unknown }).version === 'string') {
654
+ return parsed as { version?: unknown; commit?: unknown };
655
+ }
656
+ } catch { /* not a JSON line — keep walking */ }
657
+ }
658
+ return undefined;
659
+ };
660
+ const originalCut = firstCutObject(versionRecord?.output) ?? recorded;
661
+ const base = typeof originalCut?.commit === 'string' ? originalCut.commit : '';
662
+ if (!versionRecord?.ok || !isReleaseVersion(version) || !/^[0-9a-f]{40}$/i.test(base)) {
663
+ throw new Error(`release run ${opts.runId} has no successful version-release record — nothing to repair`);
664
+ }
665
+ const branch = `release/${version}`;
666
+ const fromNode = opts.from ?? RECOVER_FIRST_NODE;
667
+ // RELEASE-RECOVER-PREFLIGHT (MS-02 review): the append pushes release/<v> on origin, and the resume refuses a
668
+ // bad run only after that push. Every read-only resume rejection is checked here FIRST — a run the resume
669
+ // would refuse never moves the branch. command-root mismatch is gone by construction: recover prepares the
670
+ // tip checkout itself below, and a human-provided --command-root is verified by the plan before the graph.
671
+ if (savedRun.status !== 'failed') {
672
+ throw new Error(`release run ${opts.runId} is ${savedRun.status} — only a failed run is recovered; ${branch} was not moved`);
673
+ }
674
+ if (fromNode === 'version-release') {
675
+ throw new Error(`--from version-release would cut again — recover at a later node; ${branch} was not moved`);
676
+ }
677
+ const restartAt = savedRun.path.indexOf(fromNode);
678
+ if (restartAt < 0 || savedRun.path.lastIndexOf(fromNode) !== restartAt) {
679
+ throw new Error(`--from ${fromNode} is not a node on the saved path of ${opts.runId}; ${branch} was not moved`);
680
+ }
681
+ const consumers = ['export-check', 'gate'].map((node) => savedRun.path.indexOf(node)).filter((index) => index >= 0);
682
+ const firstConsumer = consumers.length ? Math.min(...consumers) : -1;
683
+ if (firstConsumer >= 0 && restartAt > firstConsumer) {
684
+ throw new Error(`--from ${fromNode} is later than ${savedRun.path[firstConsumer]!} — later nodes were built from the old tip; ${branch} was not moved`);
685
+ }
686
+ const savedInput = (savedRun.input ?? {}) as { branchCut?: unknown; version?: unknown };
687
+ if (savedInput.branchCut !== true || savedInput.version !== version || recorded?.branch !== branch) {
688
+ throw new Error(`release run ${opts.runId} was not cut as ${branch} (input.branchCut/version or version-release branch differ); ${branch} was not moved`);
689
+ }
690
+ // The attempt ledger is the resume's own read-only gate — a refused recover consumes no attempt.
691
+ reserveReleaseResumeAttempt(savedRun, root, { now: deps.now, checkOnly: true });
692
+ const now = (): string => (deps.now?.() ?? new Date()).toISOString();
693
+ const stages: RecoverStageRecord[] = [];
694
+ const record = (stage: RecoverStageRecord): void => {
695
+ stages.push(stage);
696
+ recordRecoverStage({ root, runId: opts.runId, version, priorCountKnown: !savedRun.resume, stage });
697
+ };
698
+ const stop = (stage: RecoverStageName, error: unknown): never => {
699
+ throw new ReleaseRecoverStopped(stage, stages, RECOVER_STAGES.slice(RECOVER_STAGES.indexOf(stage) + 1),
700
+ error instanceof Error ? error.message : String(error));
701
+ };
702
+ const reason = (error: unknown): string => error instanceof Error ? error.message : String(error);
703
+ const cutBranch = deps.cutBranch ?? cutReleaseBranch;
704
+ const cut = { version, base, pick: opts.pick, append: true, repoRoot: opts.repoRoot, log: deps.onSource };
705
+ const repoRoot = resolve(opts.repoRoot ?? deps.repo ?? process.cwd());
706
+ // RELEASE-RECOVER-REENTRY (MS-02 review): once the append pushed and the resume did not finish, re-running
707
+ // recover with the same --pick is refused by cut-branch with «pick … is already in release/<v>» and dies at
708
+ // stage 1 with no guidance. The ledger recoveries decide: when their append-ok commit is still inside the
709
+ // advertised branch tip AND its recorded pick list is this --pick (an entry with no pick record is «기록 없음»,
710
+ // not an empty list — it falls back to the safe side and appends again), the repair already landed and this
711
+ // recover continues from the resume — the append stages never re-run.
712
+ /** Same pick list, order-insensitive, full 40-hex object ids only — anything else is «no record». */
713
+ const samePickList = (recorded: unknown): boolean => {
714
+ if (!Array.isArray(recorded) || recorded.length !== opts.pick.length) return false;
715
+ const left = recorded.map((sha) => (typeof sha === 'string' ? sha.toLowerCase() : '')).sort();
716
+ const right = [...opts.pick].map((sha) => sha.toLowerCase()).sort();
717
+ return left.every((sha, index) => /^[0-9a-f]{40}$/.test(sha) && sha === right[index]);
718
+ };
719
+ const ledgerPath = join(root, 'release', version, 'resume-attempts', `${opts.runId}.json`);
720
+ const recoveries: RecoverStageRecord[] = existsSync(ledgerPath)
721
+ ? ((JSON.parse(readFileSync(ledgerPath, 'utf8')) as { recoveries?: unknown[] }).recoveries ?? []) as RecoverStageRecord[]
722
+ : [];
723
+ let appendedCommit: string | undefined;
724
+ for (let index = recoveries.length - 1; index >= 0; index -= 1) {
725
+ const entry = recoveries[index]!;
726
+ if (entry.stage === 'append' && entry.outcome === 'ok' && typeof entry.commit === 'string' && samePickList(entry.pick)) {
727
+ appendedCommit = entry.commit;
728
+ break;
729
+ }
730
+ }
731
+ let advertised = '';
732
+ try {
733
+ advertised = (deps.git ?? gitRunner(repoRoot))(['ls-remote', '--heads', 'origin', `refs/heads/${branch}`]).split(/\s+/)[0] ?? '';
734
+ } catch {
735
+ advertised = '';
736
+ }
737
+ const isAncestor = deps.isAncestor ?? ancestorRunner(repoRoot);
738
+ const humanResume = `release resume --run ${opts.runId} --from ${fromNode}`;
739
+ const alreadyInGuidance = (cause: string): string =>
740
+ `${cause} — ${branch}가 이미 고른 수선을 담고 있다: 이 recover는 append하지 않았다. 사람이 직접 이어 간다: ${humanResume}`;
741
+ let tip = '';
742
+ if (appendedCommit !== undefined && /^[0-9a-f]{40}$/i.test(advertised) && isAncestor(appendedCommit, advertised)) {
743
+ tip = advertised;
744
+ deps.onSource?.(`· recover 재진입 · 원장의 append-ok(${appendedCommit.slice(0, 12)})이 가지 끝에 그대로 있다 — append를 다시 하지 않고 ${fromNode} resume부터 이어 간다`);
745
+ } else {
746
+ if (/^[0-9a-f]{40}$/i.test(advertised) && opts.pick.every((sha) => isAncestor(sha, advertised))) {
747
+ // The repair is on the branch already (a hand-run append) and the ledger has no append-ok for it: there is
748
+ // nothing to append — the stop error and the ledger record both name the human's next step.
749
+ const guidance = alreadyInGuidance(`pick ${opts.pick.join(' + ')} is already in ${branch}`);
750
+ record({ stage: 'append-dry-run', outcome: 'failed', at: now(), branch, reason: guidance });
751
+ return stop('append-dry-run', new Error(guidance));
752
+ }
753
+ // 1/3 append dry-run — the fast-forward plan only: no worktree, no push, no run-state write.
754
+ try {
755
+ const planned = cutBranch({ ...cut, dryRun: true });
756
+ record({ stage: 'append-dry-run', outcome: 'ok', at: now(), branch: planned.branch, commit: planned.commit });
757
+ deps.onSource?.(`· recover 1/3 append dry-run · ${planned.branch} ← ${opts.pick.join(' + ')} · 가지·push·런 상태 변경 없음`);
758
+ } catch (error) {
759
+ const raw = reason(error);
760
+ if (!/is already in /.test(raw)) {
761
+ record({ stage: 'append-dry-run', outcome: 'failed', at: now(), branch, reason: raw });
762
+ return stop('append-dry-run', error);
763
+ }
764
+ const guided = alreadyInGuidance(raw);
765
+ record({ stage: 'append-dry-run', outcome: 'failed', at: now(), branch, reason: guided });
766
+ return stop('append-dry-run', new Error(guided));
767
+ }
768
+ // 2/3 append — cherry-pick the repairs onto release/<v> and push the fast-forward (never a force push).
769
+ try {
770
+ const appended = cutBranch(cut);
771
+ if (!appended.commit) throw new Error(`${appended.branch} append produced no commit`);
772
+ tip = appended.commit;
773
+ record({ stage: 'append', outcome: 'ok', at: now(), branch: appended.branch, commit: tip, pick: opts.pick });
774
+ deps.onSource?.(`↻ recover 2/3 append · ${appended.branch} ${base.slice(0, 12)} → ${tip.slice(0, 12)} (fast-forward)`);
775
+ } catch (error) {
776
+ const raw = reason(error);
777
+ if (!/is already in /.test(raw)) {
778
+ record({ stage: 'append', outcome: 'failed', at: now(), branch, reason: raw });
779
+ return stop('append', error);
780
+ }
781
+ const guided = alreadyInGuidance(raw);
782
+ record({ stage: 'append', outcome: 'failed', at: now(), branch, reason: guided });
783
+ return stop('append', new Error(guided));
784
+ }
785
+ }
786
+
787
+ // 3/3 resume --from export-check — the read-only preflight first (its plan is the record), then the graph run.
788
+ // The append moved only the origin branch tip: the operator checkout is still wherever it was, and the resume
789
+ // preflight (verifyReleaseCommandRoot) demands a checkout whose HEAD is the new tip. So recover prepares that
790
+ // checkout itself — a temporary worktree at the tip. A human-provided --command-root stays a judgment
791
+ // boundary: the plan verifies it against the tip and stops there on a mismatch.
792
+ const resumeOptions: ResumeReleaseOptions = { runId: opts.runId, from: fromNode,
793
+ ...(opts.partial === undefined ? {} : { partial: opts.partial }) };
794
+ let preparedRoot: { dir: string; git: (args: string[]) => string } | undefined;
795
+ if (opts.commandRoot !== undefined) resumeOptions.commandRoot = opts.commandRoot;
796
+ else {
797
+ const temp = mkdtempSync(join(tmpdir(), 'release-recover-'));
798
+ const dir = join(temp, 'command-root');
799
+ const worktreeGit = gitRunner(repoRoot);
800
+ try {
801
+ worktreeGit(['worktree', 'add', '--detach', dir, tip]);
802
+ } catch (error) {
803
+ rmSync(temp, { recursive: true, force: true });
804
+ throw error;
805
+ }
806
+ preparedRoot = { dir, git: worktreeGit };
807
+ resumeOptions.commandRoot = dir;
808
+ }
809
+ try {
810
+ const plan = await planResumeReleaseRun(resumeOptions, deps);
811
+ const resumed = await resumeReleaseRun(resumeOptions, deps);
812
+ record({ stage: 'resume', outcome: 'ok', at: now(), from: fromNode, branch: resumed.tip.branch,
813
+ commit: resumed.tip.to, gatePlan: plan.gatePlan.mode, status: resumed.state.status });
814
+ deps.onSource?.(`▶ recover 3/3 resume ${fromNode}부터 · tip ${resumed.tip.to.slice(0, 12)} · gate ${plan.gatePlan.mode} → ${resumed.state.status}`);
815
+ return { runId: opts.runId, version, branch: resumed.tip.branch, fromNode, stages,
816
+ resume: { status: resumed.state.status, tip: resumed.tip, partial: resumed.partial } };
817
+ } catch (error) {
818
+ record({ stage: 'resume', outcome: 'failed', at: now(), from: fromNode, branch, reason: reason(error) });
819
+ return stop('resume', error);
820
+ } finally {
821
+ if (preparedRoot) {
822
+ try {
823
+ preparedRoot.git(['worktree', 'remove', '--force', preparedRoot.dir]);
824
+ } catch {
825
+ try { preparedRoot.git(['worktree', 'prune']); } catch { /* the temp dir below is still removed */ }
826
+ }
827
+ rmSync(dirname(preparedRoot.dir), { recursive: true, force: true });
828
+ }
829
+ }
830
+ }
@@ -53,12 +53,20 @@ function printedVerdict(output: unknown): boolean {
53
53
  });
54
54
  }
55
55
 
56
- /** The only post-gate detour allowed before the terminal failure is the executed, release-only triage shadow. */
57
- function failedAfterGate(state: GraphRunState, index: number): boolean {
56
+ /** A failed release may execute its warning-only traffic restore before the terminal failure. */
57
+ function failedAfterTrafficRestore(state: GraphRunState, index: number): boolean {
58
58
  if (state.nodes.length !== state.path.length || state.path.at(-1) !== 'failed' || state.nodes.at(-1)?.nodeId !== 'failed') return false;
59
59
  if (index === state.path.length - 2) return true;
60
- return index === state.path.length - 3 && state.path[index + 1] === 'gate-triage'
61
- && state.nodes[index + 1]?.nodeId === 'gate-triage' && state.nodes[index + 1]?.executed === true;
60
+ return index === state.path.length - 3 && state.path[index + 1] === 'traffic-failed-restore'
61
+ && state.nodes[index + 1]?.nodeId === 'traffic-failed-restore' && state.nodes[index + 1]?.executed === true;
62
+ }
63
+
64
+ /** Gate may also pass through its executed triage shadow before the traffic restore. */
65
+ function failedAfterGate(state: GraphRunState, index: number): boolean {
66
+ if (failedAfterTrafficRestore(state, index)) return true;
67
+ return state.path[index + 1] === 'gate-triage'
68
+ && state.nodes[index + 1]?.nodeId === 'gate-triage' && state.nodes[index + 1]?.executed === true
69
+ && failedAfterTrafficRestore(state, index + 1);
62
70
  }
63
71
 
64
72
  export function gateUnmeasuredDetail(state: GraphRunState, index: number): string | undefined {
@@ -113,7 +121,7 @@ export function releaseResumeWaiver(
113
121
  }
114
122
  if (!unique || state.path.lastIndexOf(opts.waive) !== index || state.nodes[index]?.nodeId !== opts.waive || state.nodes[index]?.ok !== false ||
115
123
  state.nodes[index]?.executed !== true || (opts.waive === 'gate' ? !failedAfterGate(state, index)
116
- : state.path.at(-2) !== opts.waive || state.path.at(-1) !== 'failed' || state.nodes.at(-1)?.nodeId !== 'failed')) {
124
+ : !failedAfterTrafficRestore(state, index))) {
117
125
  throw new Error(`--waive ${opts.waive} requires the failed executable node on the saved path`);
118
126
  }
119
127
  // Only user-visible, non-publication checks can become accepted regressions; build, safety and approval gates stay blocking.
@@ -7,8 +7,11 @@ import { errorResult, finishNode, readGraphContext } from './node-verdict.js';
7
7
  import { applyReleaseTrafficEvent, trafficOptionsFromConfig, type ReleaseTrafficEvent, type TrafficTransitionOptions } from './traffic-transition.js';
8
8
 
9
9
  const EVENTS: Record<string, ReleaseTrafficEvent> = {
10
+ 'traffic-cut-start': 'cut-start',
10
11
  'traffic-gate-passed': 'gate-passed',
12
+ 'traffic-npm-published': 'npm-published',
11
13
  'traffic-verify-complete': 'verify-complete',
14
+ 'traffic-failed-restore': 'failed',
12
15
  };
13
16
 
14
17
  export interface TrafficNodeDeps {
@@ -40,12 +43,17 @@ export async function runTrafficNode(node = process.argv[2], env: NodeJS.Process
40
43
  }
41
44
  const result = await applyReleaseTrafficEvent(context, event, options);
42
45
  if (!result.applied) {
43
- const consequence = event === 'verify-complete'
44
- ? '릴리스 검증은 성공했으나 트래픽 복귀가 적용되지 않았습니다.'
45
- : 'gate는 성공했으나 트래픽 감속이 적용되지 않았습니다.';
46
+ const consequence = event === 'failed' && result.reason === 'published-keeps-full'
47
+ ? 'npm 발행은 완료됐으므로 전체 트래픽을 유지합니다. 후속 발행 단계 실패를 확인하세요.'
48
+ : event === 'verify-complete' || event === 'npm-published'
49
+ ? '발행은 성공했으나 전체 트래픽 전환이 적용되지 않았습니다.'
50
+ : event === 'failed' ? '컷 전 트래픽 복귀가 적용되지 않았습니다.'
51
+ : event === 'cut-start' ? '컷은 시작했으나 gate 모드 전환이 적용되지 않았습니다.'
52
+ : 'gate는 성공했으나 트래픽 감속이 적용되지 않았습니다.';
46
53
  (deps.report ?? reportFailure)(`RELEASE-POSTPUB-TRAFFIC ${version} ${runId || 'unknown'} ${node} 무시 — ${result.reason ?? 'traffic state unchanged'}. ${consequence}`);
47
54
  }
48
- return finishNode(node!, version, { outcome: 'ok', verdict: 'pass', summary: `${event} ${result.applied ? 'applied' : result.reason ?? 'ignored'}` });
55
+ return finishNode(node!, version, { outcome: result.applied ? 'ok' : 'fail', verdict: result.applied ? 'pass' : 'fail',
56
+ summary: `${event} ${result.applied ? 'applied' : result.reason ?? 'ignored'}` });
49
57
  } catch (error) {
50
58
  const detail = errorResult(error).summary;
51
59
  const isLockTimeout = detail.startsWith('traffic lock timeout:');
@@ -4,12 +4,17 @@ import { debug } from '../../src/debug/log.js';
4
4
  import type { GraphContext } from './node-verdict.js';
5
5
  import type { ReleaseLoopConfig } from './unattended-release.js';
6
6
 
7
- export type ReleaseTrafficEvent = 'gate-passed' | 'verify-complete';
7
+ export type ReleaseTrafficEvent = 'cut-start' | 'gate-passed' | 'npm-published' | 'verify-complete' | 'failed';
8
+
9
+ export interface TrafficBeforeCut {
10
+ global?: string;
11
+ perCycle: Record<string, unknown>;
12
+ }
8
13
 
9
14
  export interface TrafficFile {
10
15
  global?: string;
11
16
  seats: Record<string, Record<string, unknown>>;
12
- releaseLifecycle?: { runId: string; version: string; stage: 1 | 2; event: ReleaseTrafficEvent; startedAt?: string; updatedAt: string };
17
+ releaseLifecycle?: { runId: string; version: string; stage: 0 | 1 | 2 | 3; event: ReleaseTrafficEvent; startedAt?: string; updatedAt: string; beforeCut?: TrafficBeforeCut };
13
18
  [key: string]: unknown;
14
19
  }
15
20
 
@@ -18,7 +23,7 @@ type ReleaseTrafficConfig = ReleaseLoopConfig & {
18
23
  };
19
24
 
20
25
  const SEATS = ['OP', 'TC', 'MK', 'UX'] as const;
21
- const STAGE: Record<ReleaseTrafficEvent, 1 | 2> = { 'gate-passed': 1, 'verify-complete': 2 };
26
+ const STAGE: Record<ReleaseTrafficEvent, 0 | 1 | 2 | 3> = { 'cut-start': 0, 'gate-passed': 1, 'npm-published': 2, 'verify-complete': 2, failed: 3 };
22
27
  const TRAFFIC_PER_CYCLE = { half: 3, full: 6 } as const;
23
28
 
24
29
  export interface TrafficTransitionOptions {
@@ -112,32 +117,65 @@ export async function applyReleaseTrafficEvent(
112
117
  const log = options.log ?? ((name, fields) => debug.log('release-loop.traffic', name, fields));
113
118
  const stage = STAGE[event];
114
119
  const stamp = (options.now ?? (() => new Date()))().toISOString();
115
- const rate = event === 'gate-passed' ? TRAFFIC_PER_CYCLE.half : TRAFFIC_PER_CYCLE.full;
116
- const releaseLifecycle = { runId, version, stage, event, ...(context.startedAt ? { startedAt: context.startedAt } : {}), updatedAt: stamp };
120
+ const rate = event === 'gate-passed' ? TRAFFIC_PER_CYCLE.half
121
+ : event === 'npm-published' || event === 'verify-complete' ? TRAFFIC_PER_CYCLE.full : undefined;
117
122
  const release = () => {
118
123
  const unlock = acquireTrafficLock(path, options.lockTimeoutMs);
119
124
  try {
120
125
  const before = readTrafficFile(path);
121
126
  const prior = before.releaseLifecycle;
127
+ if ((event === 'npm-published' || event === 'verify-complete') && context.outputs['npm-publish']?.outcome !== 'ok') {
128
+ return { file: before, applied: false, idempotent: false, reason: 'npm-publish-unconfirmed' };
129
+ }
122
130
  const sameRun = prior?.runId === runId;
123
131
  const newerVersion = !prior?.runId || compareReleaseVersions(version, prior.version) > 0;
124
132
  const sameVersionLaterRun = prior?.version === version && isLaterRun(context.startedAt, prior.startedAt);
125
- if (sameRun && prior.stage >= stage) return { file: before, applied: true, idempotent: true };
126
- const permitted = sameRun
127
- ? prior.stage === stage - 1
128
- : newerVersion || (event === 'gate-passed' && prior?.version === version && (prior.stage ?? 0) < 2 && sameVersionLaterRun)
129
- || (event === 'verify-complete' && prior?.version === version && sameVersionLaterRun);
133
+ const resumedTrafficNode = sameRun && prior.stage === 3
134
+ && (event === 'cut-start' && context.nodeId === 'traffic-cut-start'
135
+ || event === 'gate-passed' && context.nodeId === 'traffic-gate-passed'
136
+ || event === 'npm-published' && context.nodeId === 'traffic-npm-published'
137
+ || event === 'verify-complete' && context.nodeId === 'traffic-verify-complete' && context.outputs['npm-publish']?.outcome === 'ok');
138
+ if (sameRun && prior.stage === 3 && event !== 'failed' && !resumedTrafficNode) {
139
+ return { file: before, applied: false, idempotent: false, reason: 'run-already-failed' };
140
+ }
141
+ if (sameRun && prior.stage >= stage && event !== 'failed' && !resumedTrafficNode) return { file: before, applied: true, idempotent: true };
142
+ if (event === 'failed' && (!sameRun || prior?.stage === 2 || prior?.stage === 3 || !prior?.beforeCut)) {
143
+ return { file: before, applied: false, idempotent: false,
144
+ reason: !sameRun ? prior?.runId ? 'different-run-is-current' : 'before-cut-unmeasured'
145
+ : prior?.stage === 2 ? 'published-keeps-full' : prior?.stage === 3 ? 'already-restored' : 'before-cut-unmeasured' };
146
+ }
147
+ const permitted = event === 'failed' ? sameRun
148
+ : sameRun ? resumedTrafficNode || (stage === 0 ? prior.stage === 0 : stage > prior.stage)
149
+ : newerVersion || (prior?.version === version && sameVersionLaterRun && (prior.stage < 2 || prior.stage === 3 || event === 'verify-complete'));
130
150
  if (!permitted) {
131
151
  return { file: before, applied: false, idempotent: false, reason: prior?.runId && prior.runId !== runId ? 'different-run-is-current' : 'event-out-of-order' };
132
152
  }
153
+ if (event === 'cut-start' && prior && prior.stage < 2 && !prior.beforeCut) {
154
+ return { file: before, applied: false, idempotent: false, reason: 'before-cut-unmeasured' };
155
+ }
156
+ const beforeCut = event === 'cut-start' ? {
157
+ ...(prior?.stage !== undefined && prior.stage < 2 && prior.beforeCut
158
+ ? prior.beforeCut
159
+ : { ...(before.global === undefined ? {} : { global: before.global }),
160
+ perCycle: Object.fromEntries(SEATS.map((seat) => [seat, before.seats[seat]?.perCycle])) }),
161
+ } : prior?.beforeCut;
162
+ const releaseLifecycle = { runId, version, stage, event, ...(context.startedAt ? { startedAt: context.startedAt } : {}),
163
+ updatedAt: stamp, ...(beforeCut ? { beforeCut } : {}) };
133
164
  const after: TrafficFile = {
134
165
  ...before,
135
- ...(event === 'verify-complete' ? { global: 'green' } : {}),
166
+ ...(event === 'cut-start' ? { global: 'gate' }
167
+ : event === 'gate-passed' ? { global: 'yellow' }
168
+ : event === 'npm-published' || event === 'verify-complete' ? { global: 'green' }
169
+ : beforeCut?.global === undefined ? {} : { global: beforeCut.global }),
136
170
  seats: Object.fromEntries(Object.entries(before.seats).map(([seat, value]) => [
137
- seat, SEATS.includes(seat as typeof SEATS[number]) ? { ...value, perCycle: rate } : value,
171
+ seat, SEATS.includes(seat as typeof SEATS[number])
172
+ ? { ...value, ...(event === 'failed' ? { perCycle: beforeCut?.perCycle[seat] } : rate === undefined ? {} : { perCycle: rate }) }
173
+ : value,
138
174
  ])),
139
175
  releaseLifecycle,
140
176
  };
177
+ if (event === 'failed' && beforeCut?.global === undefined) delete after.global;
178
+ if (event === 'failed') for (const seat of SEATS) if (beforeCut?.perCycle[seat] === undefined) delete after.seats[seat]?.perCycle;
141
179
  atomicWriteTrafficFile(path, after);
142
180
  return { file: after, applied: true, idempotent: false };
143
181
  } finally { unlock(); }
@@ -145,7 +183,9 @@ export async function applyReleaseTrafficEvent(
145
183
  const result = release();
146
184
  const reason = result.applied && result.idempotent ? 'already-applied' : result.reason;
147
185
  log(result.idempotent ? 'already-applied' : result.applied ? 'applied' : 'ignored', {
148
- runId, version, event, stage, reason, perCycle: rate, ...(event === 'verify-complete' ? { global: 'green' } : {}),
186
+ runId, version, event, stage, reason, ...(rate === undefined ? {} : { perCycle: rate }),
187
+ ...(event === 'cut-start' ? { global: 'gate' } : event === 'gate-passed' ? { global: 'yellow' }
188
+ : event === 'npm-published' || event === 'verify-complete' ? { global: 'green' } : {}),
149
189
  });
150
190
  return { applied: result.applied, stage: result.file.releaseLifecycle?.stage ?? 0, ...(reason ? { reason } : {}) };
151
191
  }
@@ -8,6 +8,7 @@ import { debug } from '../../src/debug/log.js';
8
8
  import { effectiveInstanceRoot, prodInstanceRoot } from '../../src/instance/resolve.js';
9
9
  import { landingFreezeMessage, readLandingFreeze, type LandingFreeze } from '../../src/release-loop/landing-freeze.js';
10
10
  import { baseVersion, isReleaseVersion, isStableVersion, prereleaseKind } from './release-version.js';
11
+ import { cutReleaseBranch } from './cut-branch.js';
11
12
 
12
13
  type Kind = 'release' | 'dev-bump';
13
14
  type Output = { outcome: 'ok' | 'error'; kind: Kind | null; version: string | null; commit: string | null; pr: number | null; files?: string[]; worktree?: string; error?: string;
@@ -441,19 +442,30 @@ export function main(args: string[] = process.argv.slice(2), repo = process.cwd(
441
442
  let branchCut = false;
442
443
  let runId: string | undefined;
443
444
  try {
444
- if (args[0] !== 'release' && args[0] !== 'dev-bump') throw new Error('usage: version-node.ts <release|dev-bump> [--version <v>] [--cut <sha>] [--cut-commit <sha>] --json');
445
- kind = args[0];
445
+ if (args[0] !== 'release' && args[0] !== 'dev-bump' && args[0] !== 'cut-branch')
446
+ throw new Error('usage: version-node.ts <release|dev-bump|cut-branch> [--version <v>] [--cut <sha>] [--cut-commit <sha>] [--pick <sha>] [--base <sha>] [--append] [--dry-run] --json');
447
+ kind = args[0] === 'cut-branch' ? 'release' : args[0];
448
+ const picks: string[] = [];
449
+ let base: string | undefined;
450
+ let append = false;
451
+ let dryRun = false;
452
+ let versionRelease: { commit?: unknown; base?: unknown; branch?: unknown } | undefined;
446
453
  for (let i = 1; i < args.length; i++) {
447
454
  if (args[i] === '--json') continue;
448
455
  if (args[i] === '--version' && args[i + 1]) { version = args[++i]!; continue; }
449
456
  if (args[i] === '--cut' && args[i + 1]) { cut = args[++i]!; continue; }
450
457
  if (args[i] === '--cut-commit' && args[i + 1]) { cutCommit = args[++i]!; continue; }
458
+ if (args[i] === '--pick' && args[i + 1]) { picks.push(args[++i]!); continue; }
459
+ if (args[i] === '--base' && args[i + 1]) { base = args[++i]!; continue; }
460
+ if (args[i] === '--append') { append = true; continue; }
461
+ if (args[i] === '--dry-run') { dryRun = true; continue; }
451
462
  throw new Error(`unknown or incomplete argument: ${args[i]}`);
452
463
  }
453
464
  if (process.env.ELANOUS_GRAPH_CONTEXT) {
454
465
  const location = process.env.ELANOUS_GRAPH_CONTEXT;
455
466
  const context = JSON.parse(location.trimStart().startsWith('{') ? location : readFileSync(location, 'utf8')) as {
456
- runId?: unknown; input?: { version?: unknown; cutCommit?: unknown; forceFreeze?: unknown; branchCut?: unknown }; outputs?: Record<string, { commit?: unknown } | undefined> };
467
+ runId?: unknown; input?: { version?: unknown; cutCommit?: unknown; forceFreeze?: unknown; branchCut?: unknown };
468
+ outputs?: Record<string, { commit?: unknown; base?: unknown; branch?: unknown } | undefined> };
457
469
  if (context.runId !== undefined) {
458
470
  if (typeof context.runId !== 'string') throw new Error('runId must be a string');
459
471
  runId = context.runId;
@@ -466,11 +478,24 @@ export function main(args: string[] = process.argv.slice(2), repo = process.cwd(
466
478
  cutCommit = context.input.cutCommit;
467
479
  }
468
480
  // The cut = the commit version-release landed (what cutoff and the gate measured).
481
+ // RELEASE-APPEND-BASE-AUTO: cut-branch consumes that same record as its ledger seam
482
+ // (ledger: () => versionRelease), so it must not also see it injected as --cut.
469
483
  const releasedCommit = context.outputs?.['version-release']?.commit;
470
- if (!cut && typeof releasedCommit === 'string' && releasedCommit) cut = releasedCommit;
484
+ if (args[0] !== 'cut-branch' && !cut && typeof releasedCommit === 'string' && releasedCommit) cut = releasedCommit;
485
+ // RELEASE-APPEND-BASE-AUTO: the version-release node's record is the ledger seam cut-branch consults.
486
+ versionRelease = context.outputs?.['version-release'];
471
487
  }
472
488
  if (!version) throw new Error('version required (--version or ELANOUS_GRAPH_CONTEXT.input.version)');
473
489
  nextDevVersion(version);
490
+ if (args[0] === 'cut-branch') {
491
+ if (cut !== undefined || cutCommit !== undefined) throw new Error('--cut/--cut-commit are not valid for cut-branch');
492
+ if (!picks.length) throw new Error('at least one --pick commit is required');
493
+ const result = cutReleaseBranch({
494
+ version, pick: picks, ...(base !== undefined ? { base } : {}), append, dryRun,
495
+ repoRoot: resolve(repo), ledger: () => versionRelease,
496
+ });
497
+ return { outcome: 'ok', kind: 'release', version, commit: result.commit, pr: null, branch: result.branch, ...(dryRun ? { skipped: 'dry-run' } : {}) };
498
+ }
474
499
  if (kind === 'dev-bump' && cutCommit !== undefined) throw new Error('--cut-commit is only valid for release');
475
500
  return landing(resolve(repo), kind, version, cut, cutCommit, forceFreeze, branchCut, undefined, runId);
476
501
  } catch (error) {