elanous 0.2.32 → 0.2.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (633) hide show
  1. package/apps/pwa/out/404/index.html +1 -1
  2. package/apps/pwa/out/404.html +1 -1
  3. package/apps/pwa/out/_next/static/{IVKr3AlRUVLKsw2fh7eXF → L04y73dlwKeTW-GetaTF3}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/1162.c69abda138c7ce43.js +79 -0
  5. package/apps/pwa/out/_next/static/chunks/1449-1fede62af84c3e43.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1598-13fc845935e816df.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1639.62c5e16fa20d242f.js +94 -0
  8. package/apps/pwa/out/_next/static/chunks/{4174.92ed04922709ac9e.js → 1644.35dd25240ae24e4b.js} +2 -2
  9. package/apps/pwa/out/_next/static/chunks/1661-adb8b2babc9aaf8c.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/{2940-619bfd8ef337c611.js → 1895-60ed0e341c658bd4.js} +1 -1
  11. package/apps/pwa/out/_next/static/chunks/{5525.86debf231e181813.js → 2124.ed5bf480cea7f85b.js} +1 -1
  12. package/apps/pwa/out/_next/static/chunks/2147.107c0fc6146fb15a.js +23 -0
  13. package/apps/pwa/out/_next/static/chunks/2202-ef1c9b000c3074fc.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/2247.1e326638ee43e672.js +115 -0
  15. package/apps/pwa/out/_next/static/chunks/2268.56e1e3d58180abae.js +8 -0
  16. package/apps/pwa/out/_next/static/chunks/2469-606819c812414021.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/{6092.a7f99e68b188ca66.js → 2547.dd27303624cb4da8.js} +1 -1
  18. package/apps/pwa/out/_next/static/chunks/{2213.2ccbada92c33ea0f.js → 2554.3b5e200f5f5cfcd6.js} +16 -16
  19. package/apps/pwa/out/_next/static/chunks/2593.e53c1620c0bb3599.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/2690.e96671114222676a.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/2898-79ee1a5cc0cd9d13.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/298.26099a9f65e5c8ff.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/{5173-233188df2f42c626.js → 2987-30cf9c2eb7b8bdd5.js} +1 -1
  24. package/apps/pwa/out/_next/static/chunks/2992.51fc2a7295bd8139.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/{3541-ce3a5424ed5ad7f1.js → 3001-5a52788c813b6113.js} +1 -1
  26. package/apps/pwa/out/_next/static/chunks/3014-291245fba9df7b5b.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/3068.ee78201f0251a18d.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/3083.cd399a17e9365f78.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/3103.84470c8445962f04.js +34 -0
  30. package/apps/pwa/out/_next/static/chunks/{6433.fc573af22bbc9ab7.js → 3110.0a7065f25c841818.js} +17 -17
  31. package/apps/pwa/out/_next/static/chunks/3117-e1c474683fd1f824.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/3121.779fafe3e44612c5.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/3251-2c17bd5a91f8de2e.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/326.3fb32322bb2ddf6d.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/3411-f87314c6c66ebc73.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/{6157.0c9af021de4fb31b.js → 3414.172ad4ca2d9b08e8.js} +2 -2
  37. package/apps/pwa/out/_next/static/chunks/3421-7ff7fbaa156985a7.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/{4745.67d6f99c245af3f7.js → 3498.cf2a15a8a9e5b926.js} +3 -3
  39. package/apps/pwa/out/_next/static/chunks/381.f55dc63ea87735e6.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/3819.163d4777f0a597f6.js +32 -0
  41. package/apps/pwa/out/_next/static/chunks/{5543.a438b8ab5a13ea47.js → 3837.722e053fb9f321ad.js} +1 -1
  42. package/apps/pwa/out/_next/static/chunks/4129-ab0a6189f1778633.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/4361.93d14ba594b3a035.js +104 -0
  44. package/apps/pwa/out/_next/static/chunks/4364.7a7eb19cceb67d2f.js +286 -0
  45. package/apps/pwa/out/_next/static/chunks/4406.d71f506f7c3ee9d3.js +149 -0
  46. package/apps/pwa/out/_next/static/chunks/4461.2ad1514180ae7e29.js +148 -0
  47. package/apps/pwa/out/_next/static/chunks/4479.bd309cbe0e3842ee.js +64 -0
  48. package/apps/pwa/out/_next/static/chunks/4522-05966f777d66d13b.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/4543-9a3e9812e845de65.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/4557.f8f1d84a0d90d590.js +74 -0
  51. package/apps/pwa/out/_next/static/chunks/4589-79b11542179ef58c.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/4633-9f8f9f88ffbb8f5c.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/4831.23af4d10c0697b5e.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/4983.5951b85724f61f2a.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/{b313b242.cb1265eea908db61.js → 4fa33ebd.21c1bd5b294496e6.js} +1 -1
  56. package/apps/pwa/out/_next/static/chunks/{997ce8a5-2ad501927003b41b.js → 5014f970-22bef0b5210f7d60.js} +1 -1
  57. package/apps/pwa/out/_next/static/chunks/{2112-e4efc21a6505d5bc.js → 5028-f0722e6114c313e7.js} +1 -1
  58. package/apps/pwa/out/_next/static/chunks/{2898.fd781bc7cf01a7b7.js → 5249.96a848656b582a93.js} +1 -1
  59. package/apps/pwa/out/_next/static/chunks/5311.16e0ffbfa5468b80.js +62 -0
  60. package/apps/pwa/out/_next/static/chunks/{9407.e49124dedf0f5e72.js → 5446.edf141cff9ef647d.js} +1 -1
  61. package/apps/pwa/out/_next/static/chunks/{9002.e5658224e3dc1b6f.js → 5489.ec2dd1b5272a19dc.js} +1 -1
  62. package/apps/pwa/out/_next/static/chunks/{1335-6731f392c35216eb.js → 5576-82d3703636d5c4b9.js} +3 -3
  63. package/apps/pwa/out/_next/static/chunks/559.df87695df637920c.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/5618-5c88f2f70178c4ad.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/5764.05487bf0b73ddb6b.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/5930.e678826c3f831aab.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/5dfb57f7.f2e3305326202474.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/6053-d893974de92e6198.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/{2985.39ccdecd36848b12.js → 6086.913a69e6fa3e4a8b.js} +4 -4
  70. package/apps/pwa/out/_next/static/chunks/6200-cefd6f33f59d765e.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/6314-edeba650ae686ac9.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/6316-bc9a9b74d7842d79.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/6476-13b9779b73528008.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/666-04db3ac4c8e3c66f.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/670.682e55106ef915a6.js +77 -0
  76. package/apps/pwa/out/_next/static/chunks/{7862.30a8d6482bb238eb.js → 6740.43de73fc427c9fe2.js} +1 -1
  77. package/apps/pwa/out/_next/static/chunks/6816.b7ba9d0a1a4a46a7.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/690-fbbcc777f189af37.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/6989.0d3d4020e5cb3df6.js +90 -0
  80. package/apps/pwa/out/_next/static/chunks/{2871.0bca6137343b3f76.js → 7005.1623d2aea78eb85f.js} +1 -1
  81. package/apps/pwa/out/_next/static/chunks/7193-cf355cca5ba6c5d5.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/7217-d17393fe7af78fa6.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/7232.24edbd938a69c8d0.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/7295cd1e.b4b557908888eeee.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/7323.adb02de21991883c.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/7386.5d8e7e42e818dfb6.js +287 -0
  87. package/apps/pwa/out/_next/static/chunks/{7502.ccd5b8065545edd0.js → 7431.3051e9b8a4372d2d.js} +2 -2
  88. package/apps/pwa/out/_next/static/chunks/{3583.a05e88cc8d594628.js → 7448.478958ffbbba0ac0.js} +1 -1
  89. package/apps/pwa/out/_next/static/chunks/7537.ba2208c5c656b8a3.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/7562.948255cbc3e79d33.js +41 -0
  91. package/apps/pwa/out/_next/static/chunks/7596-962ae6db6cfc9c83.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/7671.cff51beac212a2ba.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/7883.3916734ee16d3e58.js +27 -0
  94. package/apps/pwa/out/_next/static/chunks/{9989.3694300e5f000b14.js → 7952.b192c31b6a0e00cc.js} +1 -1
  95. package/apps/pwa/out/_next/static/chunks/801.5b24e81eded22d1b.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/8176.8c9a8613f5a01ac1.js +36 -0
  97. package/apps/pwa/out/_next/static/chunks/819e78a6.fa3dd7f7da19bf61.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/8220-78978450c3183d8d.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/8221-eb60b5bc692a3361.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/826.e9a26064f474949e.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/{5307.60f2209e809bd627.js → 8368.014caa8de2f5af34.js} +1 -1
  102. package/apps/pwa/out/_next/static/chunks/8459-a351487a4480f584.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/8482-75823d513a4ce151.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8488.8da515248cda3f3a.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/{9221.7d22eaf353a2d4e7.js → 8548.036c344973968847.js} +1 -1
  106. package/apps/pwa/out/_next/static/chunks/8584.388a1779d9b203af.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/8637.a55e098e61ffefb8.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/8817.61bb7ffbba502ecb.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/{9398-6aed28f0a96d0d6f.js → 9131-fce7256c4180c4a5.js} +1 -1
  110. package/apps/pwa/out/_next/static/chunks/9207-8e6a12db30373f32.js +1 -0
  111. package/apps/pwa/out/_next/static/chunks/9263-00ae932c62696a86.js +1 -0
  112. package/apps/pwa/out/_next/static/chunks/9318.caafc3a130e8769c.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/932.da3fb55951882562.js +93 -0
  114. package/apps/pwa/out/_next/static/chunks/9433.002f8426ac75b6b7.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/9464.dd631a5a14b1d660.js +24 -0
  116. package/apps/pwa/out/_next/static/chunks/{3278.2e5d15b53a4091c8.js → 9550.d5f76828f972058c.js} +2 -2
  117. package/apps/pwa/out/_next/static/chunks/{2534.e85ae21ad0dac829.js → 9739.8d5f36ac67a3b236.js} +1 -1
  118. package/apps/pwa/out/_next/static/chunks/9743.beeabb9d5f86abe4.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/9843-a23d3f32dc43f715.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/994-28211077c7c587d5.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/{7a1887c4.f36b88fba31da8fd.js → 9a8cb466.cc9fc9e6e46038aa.js} +1 -1
  122. package/apps/pwa/out/_next/static/chunks/{0bb0ff36.b8d418980951e5ba.js → 9b48c871.204d5d13e7809bd0.js} +7 -7
  123. package/apps/pwa/out/_next/static/chunks/{409f1e57.d0649fc47691ebfe.js → abb4b98b.163e0f56e84ae35d.js} +1 -1
  124. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-3b4c947f174e33d8.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/approvals/page-369d5ce99483f859.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-63c0ed0eb26e5fce.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/board/page-62802c6003bc85cc.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/botlab/page-ecb41560d4e77be9.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/bots/page-7b65ad8130c93e95.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/ceo/page-8755477c15a9060c.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/chat/page-5725447dd972c471.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/consult/page-b979de0591a69375.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/control/page-6d0796bf4633751a.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/decisions/page-59c31dc0394e67b4.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/design-check/page-12dfc95f6cc5c718.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/editor/page-25788244e66aa4dd.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/exec/page-a2555dc705466322.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/field/page-4fb31eb476dcbad3.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/healthz/route-3b4c947f174e33d8.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/inside/page-578729adbeaa40cb.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/intake/page-a1d20fffbec35205.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/layout-4a558f94516b8004.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/live/page-9e3834705b86145e.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/live-run/page-d7bff0f0905a662c.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/loops/page-cb1c5fe746ea4014.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/market/page-9ee320507356582a.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66fd183b74835886.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/morning/page-3272a064a01a9927.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/not-found-f281b7a7f6ef2529.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/observatory/page-d45a65f8b7124fcd.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-e9c1fe684dd19a66.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-b8322932fc6e34de.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-82c66d9af2290575.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/outputs/page-63c6752009c97b05.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/page-bb33d42ebc358aec.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/reflection/page-7e4c9379197d7214.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-38bf1e9a7001732b.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/sessions/page-2e5b752012389d56.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-8c66a3b24a745630.js → page-398c0faa173203f7.js} +1 -1
  160. package/apps/pwa/out/_next/static/chunks/app/settings/page-002f53a965b6445c.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-7f75997f61a26269.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/setup/layout-3b4c947f174e33d8.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/setup/page-05631753294f23fb.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/share/page-edd700fd2cc3d7f8.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/showroom/page-105f83ecb4594365.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/tasks/page-5823e87d2017f19c.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/term/{page-04d00ae9b0890349.js → page-df3445b207ceceab.js} +1 -1
  168. package/apps/pwa/out/_next/static/chunks/app/today/page-b51ca09566c03de7.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/trace/page-8aacd5ded5dbac60.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/vault/page-bae3365a0b3c6a8e.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-9ed52b48ff1aa7fd.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/workflows/page-b6f8cad35fe00946.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/app/workspace/page-6d25ceda500d2457.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-b6401a5f2c962fe8.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/ebf6cf5e.0d2f887b040e80fb.js +122 -0
  176. package/apps/pwa/out/_next/static/chunks/f09ec7d7-d29abe23edcbb193.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/{ebc7d829.264a0bcb6c750b50.js → ffdc3a04.b9c23c1b28dd4a96.js} +1 -1
  178. package/apps/pwa/out/_next/static/chunks/framework-9322cb8a56ac081c.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-2fbab294e49a1561.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-app-f36729f56621a8f3.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_app-2a62e450b47c6114.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_error-5e17f7b445f34d43.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/webpack-b4263e2190ea7adf.js +1 -0
  184. package/apps/pwa/out/approvals/index.html +1 -1
  185. package/apps/pwa/out/approvals/index.txt +22 -22
  186. package/apps/pwa/out/autopilot/index.html +1 -1
  187. package/apps/pwa/out/autopilot/index.txt +22 -22
  188. package/apps/pwa/out/board/index.html +1 -1
  189. package/apps/pwa/out/board/index.txt +22 -22
  190. package/apps/pwa/out/botlab/index.html +1 -1
  191. package/apps/pwa/out/botlab/index.txt +21 -21
  192. package/apps/pwa/out/bots/index.html +1 -1
  193. package/apps/pwa/out/bots/index.txt +21 -21
  194. package/apps/pwa/out/ceo/index.html +1 -1
  195. package/apps/pwa/out/ceo/index.txt +22 -22
  196. package/apps/pwa/out/chat/index.html +1 -1
  197. package/apps/pwa/out/chat/index.txt +21 -21
  198. package/apps/pwa/out/consult/index.html +1 -1
  199. package/apps/pwa/out/consult/index.txt +22 -22
  200. package/apps/pwa/out/control/index.html +1 -1
  201. package/apps/pwa/out/control/index.txt +22 -22
  202. package/apps/pwa/out/decisions/index.html +1 -1
  203. package/apps/pwa/out/decisions/index.txt +21 -21
  204. package/apps/pwa/out/design-check/index.html +1 -1
  205. package/apps/pwa/out/design-check/index.txt +22 -22
  206. package/apps/pwa/out/editor/index.html +1 -1
  207. package/apps/pwa/out/editor/index.txt +22 -22
  208. package/apps/pwa/out/exec/index.html +1 -1
  209. package/apps/pwa/out/exec/index.txt +22 -22
  210. package/apps/pwa/out/field/index.html +1 -1
  211. package/apps/pwa/out/field/index.txt +22 -22
  212. package/apps/pwa/out/healthz +1 -1
  213. package/apps/pwa/out/index.html +1 -1
  214. package/apps/pwa/out/index.txt +21 -21
  215. package/apps/pwa/out/inside/index.html +1 -1
  216. package/apps/pwa/out/inside/index.txt +21 -21
  217. package/apps/pwa/out/intake/index.html +1 -1
  218. package/apps/pwa/out/intake/index.txt +22 -22
  219. package/apps/pwa/out/live/index.html +1 -1
  220. package/apps/pwa/out/live/index.txt +22 -22
  221. package/apps/pwa/out/live-run/index.html +1 -1
  222. package/apps/pwa/out/live-run/index.txt +22 -22
  223. package/apps/pwa/out/loops/index.html +1 -1
  224. package/apps/pwa/out/loops/index.txt +21 -21
  225. package/apps/pwa/out/market/index.html +1 -1
  226. package/apps/pwa/out/market/index.txt +21 -21
  227. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  228. package/apps/pwa/out/missions/placeholder/index.txt +21 -21
  229. package/apps/pwa/out/morning/index.html +1 -1
  230. package/apps/pwa/out/morning/index.txt +22 -22
  231. package/apps/pwa/out/observatory/index.html +1 -1
  232. package/apps/pwa/out/observatory/index.txt +22 -22
  233. package/apps/pwa/out/ops/checklist/index.html +1 -1
  234. package/apps/pwa/out/ops/checklist/index.txt +21 -21
  235. package/apps/pwa/out/ops/release/index.html +1 -1
  236. package/apps/pwa/out/ops/release/index.txt +21 -21
  237. package/apps/pwa/out/ops/seats/index.html +1 -1
  238. package/apps/pwa/out/ops/seats/index.txt +21 -21
  239. package/apps/pwa/out/outputs/index.html +1 -1
  240. package/apps/pwa/out/outputs/index.txt +21 -21
  241. package/apps/pwa/out/reflection/index.html +1 -1
  242. package/apps/pwa/out/reflection/index.txt +22 -22
  243. package/apps/pwa/out/scheduler/index.html +1 -1
  244. package/apps/pwa/out/scheduler/index.txt +22 -22
  245. package/apps/pwa/out/sessions/index.html +1 -1
  246. package/apps/pwa/out/sessions/index.txt +21 -21
  247. package/apps/pwa/out/settings/devices/index.html +1 -1
  248. package/apps/pwa/out/settings/devices/index.txt +22 -22
  249. package/apps/pwa/out/settings/index.html +1 -1
  250. package/apps/pwa/out/settings/index.txt +22 -22
  251. package/apps/pwa/out/setup/done/index.html +1 -1
  252. package/apps/pwa/out/setup/done/index.txt +22 -22
  253. package/apps/pwa/out/setup/index.html +1 -1
  254. package/apps/pwa/out/setup/index.txt +22 -22
  255. package/apps/pwa/out/share/index.html +1 -1
  256. package/apps/pwa/out/share/index.txt +22 -22
  257. package/apps/pwa/out/showroom/index.html +1 -1
  258. package/apps/pwa/out/showroom/index.txt +21 -21
  259. package/apps/pwa/out/tasks/index.html +1 -1
  260. package/apps/pwa/out/tasks/index.txt +22 -22
  261. package/apps/pwa/out/term/index.html +1 -1
  262. package/apps/pwa/out/term/index.txt +22 -22
  263. package/apps/pwa/out/today/index.html +1 -1
  264. package/apps/pwa/out/today/index.txt +22 -22
  265. package/apps/pwa/out/trace/index.html +1 -1
  266. package/apps/pwa/out/trace/index.txt +22 -22
  267. package/apps/pwa/out/vault/index.html +1 -1
  268. package/apps/pwa/out/vault/index.txt +22 -22
  269. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  270. package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
  271. package/apps/pwa/out/workflows/index.html +1 -1
  272. package/apps/pwa/out/workflows/index.txt +22 -22
  273. package/apps/pwa/out/workspace/index.html +1 -1
  274. package/apps/pwa/out/workspace/index.txt +22 -22
  275. package/apps/pwa/out/worktrees/index.html +1 -1
  276. package/apps/pwa/out/worktrees/index.txt +22 -22
  277. package/bin/elanous.mjs +9 -2
  278. package/docs/goal-context/README.md +1 -0
  279. package/graphs/default-loop.yaml +20 -3
  280. package/graphs/document-loop.yaml +21 -2
  281. package/graphs/implement-loop.yaml +6 -0
  282. package/graphs/journey/SCENARIOS.md +33 -0
  283. package/graphs/journey/recipes.yaml +1 -0
  284. package/graphs/journey/ta-journey.yaml +84 -0
  285. package/graphs/journey/units/author/author.yaml +20 -0
  286. package/graphs/journey/units/author/recipes.yaml +2 -0
  287. package/graphs/journey/units/green/green.yaml +20 -0
  288. package/graphs/journey/units/green/recipes.yaml +2 -0
  289. package/graphs/journey/units/pr/pr.yaml +20 -0
  290. package/graphs/journey/units/pr/recipes.yaml +2 -0
  291. package/graphs/journey/units/remember/recipes.yaml +2 -0
  292. package/graphs/journey/units/remember/remember.yaml +20 -0
  293. package/graphs/operate-loop.yaml +21 -2
  294. package/graphs/overlays/stop-heal-ladder.yaml +1 -1
  295. package/graphs/release-rehearsal/recipes.yaml +1 -1
  296. package/graphs/release-rehearsal/release-rehearsal.yaml +2 -2
  297. package/graphs/research-loop.yaml +23 -2
  298. package/graphs/units/dispatch-pod/dispatch-pod.yaml +50 -0
  299. package/graphs/units/dispatch-pod/recipes.yaml +14 -0
  300. package/graphs/units/entry/entry.yaml +50 -0
  301. package/graphs/units/entry/recipes.yaml +14 -0
  302. package/graphs/units/harvest/harvest.yaml +25 -0
  303. package/graphs/units/harvest/recipes.yaml +4 -0
  304. package/graphs/units/land/land.yaml +45 -0
  305. package/graphs/units/land/recipes.yaml +3 -0
  306. package/graphs/units/recipes.yaml +7 -0
  307. package/graphs/units/recover.yaml +39 -0
  308. package/graphs/units/reflect/recipes.yaml +3 -0
  309. package/graphs/units/reflect/reflect.yaml +15 -0
  310. package/graphs/units/task-agent-recover-review.yaml +17 -0
  311. package/graphs/units/task-agent-recover-wait.yaml +17 -0
  312. package/graphs/units/verbs/make-doc.yaml +34 -0
  313. package/graphs/units/verbs/recipes.yaml +33 -0
  314. package/graphs/units/verbs/verify.yaml +55 -0
  315. package/graphs/work/recipes.yaml +2 -0
  316. package/graphs/work/research-report.yaml +28 -0
  317. package/package.json +1 -1
  318. package/scripts/ci-typecheck-changed.ts +69 -16
  319. package/scripts/docs/generate-graph-anatomy.ts +19 -2
  320. package/scripts/graph-harvest-unit.ts +22 -0
  321. package/scripts/graph-units/journey-stub.ts +6 -0
  322. package/scripts/graph-units/land-enqueue.ts +78 -0
  323. package/scripts/graph-units/recover-plan.ts +15 -0
  324. package/scripts/graph-units/reflect-node.ts +88 -0
  325. package/scripts/graph-units/ta-journey-observe.ts +109 -0
  326. package/scripts/graph-units/verbs/headless-goal-loop.ts +152 -0
  327. package/scripts/graph-units/verbs/verify-stage.ts +75 -0
  328. package/scripts/graph-units/verbs-envelope.ts +139 -0
  329. package/scripts/hq/standby-snapshot.ts +25 -0
  330. package/scripts/public-export.ts +43 -1
  331. package/scripts/ratchets/train-side-invariants.sh +45 -0
  332. package/scripts/release-loop/auto-approve-node.ts +30 -16
  333. package/scripts/release-loop/gate-closure.ts +5 -1
  334. package/scripts/release-loop/gate-diff.ts +1 -1
  335. package/scripts/release-loop/gate-node.ts +498 -31
  336. package/scripts/release-loop/gate-partial.ts +3 -3
  337. package/scripts/release-loop/gate-radius-shadow.ts +138 -0
  338. package/scripts/release-loop/gate-traffic-mode.ts +128 -0
  339. package/scripts/release-loop/gate-triage-node.ts +5 -2
  340. package/scripts/release-loop/known-issues-0.2.33-approved.json +194 -0
  341. package/scripts/release-loop/known-issues-node.ts +77 -8
  342. package/scripts/release-loop/preflight.ts +93 -7
  343. package/scripts/release-loop/publish-node.ts +60 -9
  344. package/scripts/release-loop/resume-cap.ts +56 -0
  345. package/scripts/release-loop/resume-release.ts +114 -15
  346. package/scripts/release-loop/resume.ts +24 -1
  347. package/scripts/release-loop/tui-sim-node.ts +8 -2
  348. package/scripts/release-loop/unattended-release.ts +8 -2
  349. package/src/acp/core-turn-bridge.ts +20 -5
  350. package/src/acp/reasoning-thought-buffer.ts +45 -0
  351. package/src/agent-substrate/acp-reviewer.ts +52 -12
  352. package/src/agent-substrate/review-finding-key.ts +53 -0
  353. package/src/agent-substrate/self-review-cli.ts +126 -5
  354. package/src/ask-user-question/pending-questions.ts +1 -1
  355. package/src/autopilot/pr-manager.ts +192 -43
  356. package/src/chat/tool-render/dispatch.ts +62 -10
  357. package/src/cli/control-cli.ts +24 -7
  358. package/src/cli/guardian-cli.ts +10 -0
  359. package/src/cli/light-entry-route.ts +35 -0
  360. package/src/cli/llm-detect-cli.ts +23 -1
  361. package/src/cli/pr-cli.ts +76 -12
  362. package/src/cli/pr-land-base-read.ts +2 -0
  363. package/src/cli/pr-land-overlap-evidence.ts +154 -16
  364. package/src/cli/pr-land-recovery.ts +64 -0
  365. package/src/cli/pty-takeover-cli.ts +6 -5
  366. package/src/cli/release-cli.ts +120 -33
  367. package/src/cli/release-run-if-ready.ts +29 -28
  368. package/src/cli/release-single-flight.ts +178 -0
  369. package/src/cli/seat-cli.ts +37 -0
  370. package/src/cli/setup-cli.ts +40 -1
  371. package/src/cli/tasks-cli.ts +149 -5
  372. package/src/control/control-status.ts +8 -3
  373. package/src/control-plane/ledger.ts +5 -3
  374. package/src/control-plane/server.ts +5 -3
  375. package/src/core-turn/short-question.ts +5 -1
  376. package/src/dashboard/index.ts +36 -67
  377. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +3 -0
  378. package/src/dashboard/input/dashboard-plain-turn-stream.ts +4 -0
  379. package/src/dashboard/input/seat-autowake.ts +56 -3
  380. package/src/dashboard/seat-boot-preamble.ts +1 -1
  381. package/src/dashboard/slash-runtime/ask-clarification.ts +48 -0
  382. package/src/dashboard/slash-runtime/dashboard-delegated-slashes.ts +68 -0
  383. package/src/dashboard/slash-runtime/dashboard-handlers.ts +62 -322
  384. package/src/dashboard/slash-runtime/dashboard-log-slash.ts +208 -0
  385. package/src/dashboard/turn-preamble.ts +47 -1
  386. package/src/dashboard/turn-stream-formatter.ts +34 -1
  387. package/src/dashboard/turn-stream-runtime.ts +2 -0
  388. package/src/dashboard/turn-typeahead-prompt-zone.ts +67 -0
  389. package/src/domains/guardian-tick.ts +54 -2
  390. package/src/domains/outbound-alert.ts +3 -2
  391. package/src/extractors.ts +3 -1
  392. package/src/git-fs/retry.ts +4 -0
  393. package/src/git-fs/runner.ts +2 -0
  394. package/src/graph-runner/runner.ts +114 -44
  395. package/src/grok/credential.ts +5 -0
  396. package/src/harness/harness-cli-command.ts +39 -10
  397. package/src/harness/harness-incidents.ts +4 -0
  398. package/src/harness/harness-pod-dispatch.ts +38 -1
  399. package/src/harness/harness-salvage-cli.ts +14 -2
  400. package/src/index.ts +192 -8
  401. package/src/intake-plane/already-built-hint.ts +89 -0
  402. package/src/knowhow/pack.ts +199 -0
  403. package/src/light-cli.ts +95 -0
  404. package/src/llm.ts +55 -9
  405. package/src/loops/budget.ts +95 -0
  406. package/src/loops/msg-wake.ts +8 -2
  407. package/src/loops/orchestrator/traffic.ts +132 -1
  408. package/src/mss/logging/log-store.ts +4 -5
  409. package/src/nexus/api/graphs-api.ts +22 -3
  410. package/src/nexus/api/http-server.ts +1 -1
  411. package/src/nexus/api/ops-api.ts +12 -0
  412. package/src/oauth/claude-pty-maturity.ts +66 -0
  413. package/src/oauth/codex-access-mirror.ts +55 -0
  414. package/src/oauth/fallback-chain.ts +1 -1
  415. package/src/onboarding/first-run.ts +5 -3
  416. package/src/onboarding/non-interactive.ts +64 -13
  417. package/src/onboarding.ts +10 -2
  418. package/src/ops/overview-collect.ts +36 -1
  419. package/src/ops/overview.ts +11 -0
  420. package/src/pr-tool/census.ts +23 -5
  421. package/src/pr-tool/inflight-registry.ts +160 -7
  422. package/src/prompt-library/gpt6-family-addendum.ts +14 -1
  423. package/src/pty-shell/herdr-agent-report.ts +442 -0
  424. package/src/pty-shell/pty-control-ipc.ts +5 -5
  425. package/src/pty-shell/pty-write-outcome.ts +24 -6
  426. package/src/pty-shell/tui-run-state.ts +117 -3
  427. package/src/release-loop/checklist.ts +79 -5
  428. package/src/release-loop/feature-store.ts +19 -2
  429. package/src/release-loop/placement.ts +10 -3
  430. package/src/release-loop/rehearsal.ts +205 -25
  431. package/src/release-loop/rubric.ts +16 -2
  432. package/src/self-dev/draft-sweep.ts +52 -3
  433. package/src/self-dev/graph-journey-nodes.ts +10 -6
  434. package/src/self-dev/graph-journey-route.ts +60 -6
  435. package/src/self-dev/orchestrate.ts +79 -3
  436. package/src/self-dev/run-supervisor.ts +107 -12
  437. package/src/self-dev/run-types.ts +5 -1
  438. package/src/self-implement/gate-baseline.ts +49 -5
  439. package/src/self-implement/gate-remote.ts +9 -5
  440. package/src/self-implement/goal-author-identity.ts +66 -0
  441. package/src/self-implement/goal-author-preservation-obligation.ts +35 -0
  442. package/src/self-implement/goal-author.ts +4 -120
  443. package/src/self-implement/graph-overlay-yaml.ts +17 -2
  444. package/src/self-implement/graph-unit-flatten.ts +103 -0
  445. package/src/self-implement/graph-unit-resolver.ts +107 -0
  446. package/src/self-implement/graph-yaml.ts +151 -1
  447. package/src/self-implement/headless-elanous-driver.ts +1 -0
  448. package/src/self-implement/knowhow-pack.ts +103 -0
  449. package/src/self-implement/orchestrator.ts +8 -0
  450. package/src/self-implement/pod-warm-pool-profile.ts +45 -0
  451. package/src/self-implement/review-mustfix-class.ts +203 -0
  452. package/src/self-implement/run-stop.ts +8 -2
  453. package/src/self-implement/seams.ts +32 -1
  454. package/src/self-implement/stop-autoheal.ts +25 -1
  455. package/src/session/chat.ts +27 -0
  456. package/src/session-runtime/retry-policy.ts +2 -1
  457. package/src/skills/tools/schedule-wakeup.ts +66 -17
  458. package/src/steward/release-day.ts +7 -3
  459. package/src/steward/triage.ts +37 -4
  460. package/src/task-agent/harvest-unit.ts +141 -0
  461. package/src/task-agent/live-moves.ts +98 -12
  462. package/src/task-agent/parent-host.ts +93 -3
  463. package/src/task-agent/postland-card.ts +80 -1
  464. package/src/task-agent/rejudge-on-head.ts +112 -26
  465. package/src/task-agent/repair-branch-cas.ts +4 -1
  466. package/src/task-agent/repair-cycle.ts +16 -3
  467. package/src/task-agent/run-identity.ts +4 -3
  468. package/src/task-agent/shadow.ts +12 -2
  469. package/src/task-agent/task-hand.ts +54 -7
  470. package/src/task-agent/work-order-build.ts +22 -0
  471. package/src/task-orchestrator/surfaces/pod-account-broker.ts +39 -3
  472. package/src/task-orchestrator/surfaces/pod-command-job.ts +2 -1
  473. package/src/task-orchestrator/surfaces/pod-lifecycle-stages.ts +86 -8
  474. package/src/task-orchestrator/surfaces/pod-pool.ts +73 -17
  475. package/src/task-orchestrator/surfaces/pod-source-receive.ts +54 -4
  476. package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +26 -3
  477. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +389 -0
  478. package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +2 -1
  479. package/src/task-orchestrator/surfaces/self-implement-pod.ts +223 -38
  480. package/src/task-orchestrator/surfaces/self-implement.ts +83 -2
  481. package/src/tool-runtime/truncation-store.ts +4 -0
  482. package/src/tui-client/dashboard-session.ts +10 -1
  483. package/src/user-config/coercion.ts +36 -0
  484. package/src/user-config.ts +18 -41
  485. package/src/ux-sim/scenario.ts +72 -13
  486. package/src/version/packed-revision.json +1 -1
  487. package/apps/pwa/out/_next/static/chunks/1034-8b639cee57b8e4a8.js +0 -1
  488. package/apps/pwa/out/_next/static/chunks/1085-6c2d35526f11f840.js +0 -1
  489. package/apps/pwa/out/_next/static/chunks/1220.1ff7d5cdc34b1ae5.js +0 -27
  490. package/apps/pwa/out/_next/static/chunks/1247.5884ec81b39fe2e7.js +0 -287
  491. package/apps/pwa/out/_next/static/chunks/1302.87681521a7a4661e.js +0 -62
  492. package/apps/pwa/out/_next/static/chunks/1324-59dae3002413623b.js +0 -1
  493. package/apps/pwa/out/_next/static/chunks/1343-f40b5870ac1eda5a.js +0 -1
  494. package/apps/pwa/out/_next/static/chunks/146-b6cd56be11dda017.js +0 -1
  495. package/apps/pwa/out/_next/static/chunks/1654.32de4b324981a188.js +0 -23
  496. package/apps/pwa/out/_next/static/chunks/1834-98b64172bc1efe77.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/1849.1ce38efa6c527d9a.js +0 -149
  498. package/apps/pwa/out/_next/static/chunks/1913.7cc6fb6055b1ed3f.js +0 -41
  499. package/apps/pwa/out/_next/static/chunks/2001.6c7e0fd02190886c.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/2030.41fae98c7e907304.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/2099-08b2f2dfba742c9b.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/2134.ecee03a550ee7250.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/2236.66061832181cb745.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/2422.b0bd272e7020d7e6.js +0 -1
  505. package/apps/pwa/out/_next/static/chunks/3040.92ff9ffc9f971cb9.js +0 -115
  506. package/apps/pwa/out/_next/static/chunks/3144.6861096c7b4edef7.js +0 -64
  507. package/apps/pwa/out/_next/static/chunks/3267c0e6.d34854e7b3984a6e.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/3771.e928edf2867d28c0.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/3902.e1e71aaac850a045.js +0 -34
  510. package/apps/pwa/out/_next/static/chunks/4063-03284b5de59965f3.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/4111.2bfedbb233ad206b.js +0 -1
  512. package/apps/pwa/out/_next/static/chunks/4300-c7ec4eeabbb5f3f7.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/4483.646d5065199e1b55.js +0 -36
  514. package/apps/pwa/out/_next/static/chunks/4519.28199ff26b2fd80d.js +0 -1
  515. package/apps/pwa/out/_next/static/chunks/4581.ef2443578054544f.js +0 -1
  516. package/apps/pwa/out/_next/static/chunks/4608-8d894e9cbe4f08bf.js +0 -1
  517. package/apps/pwa/out/_next/static/chunks/4639.ae48a01d1bb50a93.js +0 -286
  518. package/apps/pwa/out/_next/static/chunks/4739.08742ac668aada67.js +0 -77
  519. package/apps/pwa/out/_next/static/chunks/4882-16cef71d6a1a32be.js +0 -1
  520. package/apps/pwa/out/_next/static/chunks/4999.c064a4000c2a9e94.js +0 -79
  521. package/apps/pwa/out/_next/static/chunks/5260-69f35419bca263e7.js +0 -1
  522. package/apps/pwa/out/_next/static/chunks/5272.b92d1b72a15c25fe.js +0 -1
  523. package/apps/pwa/out/_next/static/chunks/5304.04d24756f242a094.js +0 -148
  524. package/apps/pwa/out/_next/static/chunks/5415-997980f828667736.js +0 -1
  525. package/apps/pwa/out/_next/static/chunks/5458-16cd7a2b51b74c8c.js +0 -1
  526. package/apps/pwa/out/_next/static/chunks/5572-42c9084f27ca761e.js +0 -1
  527. package/apps/pwa/out/_next/static/chunks/5661-80470aac963d9f36.js +0 -1
  528. package/apps/pwa/out/_next/static/chunks/5723.421338408b5c30c4.js +0 -74
  529. package/apps/pwa/out/_next/static/chunks/5809.64c3664267c50bce.js +0 -1
  530. package/apps/pwa/out/_next/static/chunks/6000.64bc21ac8627f2f5.js +0 -94
  531. package/apps/pwa/out/_next/static/chunks/6013.39459b9c68eabf5f.js +0 -1
  532. package/apps/pwa/out/_next/static/chunks/607-06466dff012b03ef.js +0 -1
  533. package/apps/pwa/out/_next/static/chunks/60fb96c9.360b01d8d4927b7b.js +0 -1
  534. package/apps/pwa/out/_next/static/chunks/6192.a190b989b81e9743.js +0 -1
  535. package/apps/pwa/out/_next/static/chunks/6323.8672b4817a306928.js +0 -1
  536. package/apps/pwa/out/_next/static/chunks/6444.7d1fdcb9b09ae71c.js +0 -1
  537. package/apps/pwa/out/_next/static/chunks/6517.4147878ac06d584d.js +0 -90
  538. package/apps/pwa/out/_next/static/chunks/6532.e34302010b2f0b68.js +0 -32
  539. package/apps/pwa/out/_next/static/chunks/6544cb48.a87fe20ddf137be2.js +0 -1
  540. package/apps/pwa/out/_next/static/chunks/6546.74bc4ed82f19016d.js +0 -104
  541. package/apps/pwa/out/_next/static/chunks/6684-e7523ce7c2fad075.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/6726.ebdc82feb161431e.js +0 -1
  543. package/apps/pwa/out/_next/static/chunks/6739.00fb6f892e5e392b.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/6745.934c4921bf15513a.js +0 -1
  545. package/apps/pwa/out/_next/static/chunks/6767.ea607cf0c20a25af.js +0 -24
  546. package/apps/pwa/out/_next/static/chunks/6843.67bfd020da0926c5.js +0 -93
  547. package/apps/pwa/out/_next/static/chunks/69-0a2c8209c6d4a158.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/6948.1062d4594652b5bf.js +0 -1
  549. package/apps/pwa/out/_next/static/chunks/7138-eaf841fc1dc03463.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/7174-cda29ee13d7790e7.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/7304-3c07583f4e405f4f.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/7371.80c05c653b08e907.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/7391.ebae168025c91b6a.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/7557.e325503a090a4310.js +0 -8
  555. package/apps/pwa/out/_next/static/chunks/7569-9ce9432c7f10989f.js +0 -1
  556. package/apps/pwa/out/_next/static/chunks/7886-55854585fadeded1.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/7947-d0759430f8042240.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/8018.a32663f04a6fc5f9.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/8097-9530c03bcc550f02.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/8215-1661812b6e9ad71f.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/8260-19c921a51ca8ec3e.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/8440.3a5cf5c85aa76db8.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/8519-4599be2249161e68.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/8547.b028b90c5c01ace6.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/8587-cbe02a953a7f4990.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/877-2b977259c4b15655.js +0 -1
  567. package/apps/pwa/out/_next/static/chunks/9019-0a4b40c50c7314fb.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/9138.b6e80031afb1d973.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/9188-d36e4fc869ddc166.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/9230.44f5039e796e4ab4.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/9279-d28c5b7754d010ae.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/939.336acce73e98482c.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/9610.c07cce4bfcc14ed7.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/9667-ce78c2ce91050ff9.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-339fabb3499da0e1.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/app/approvals/page-504204b58c82c253.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-a9717a67c2eecbaf.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/app/board/page-5c9ec30aee62dfbf.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/app/botlab/page-584191533721fa16.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/app/bots/page-73c4b4bb3ac7ab5f.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/app/ceo/page-c0f1ca371f710b06.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/app/chat/page-46430d45a9e04eac.js +0 -1
  583. package/apps/pwa/out/_next/static/chunks/app/consult/page-3b2ebf1dc64ff0be.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/app/control/page-a27b1e5a0d1b308f.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/app/decisions/page-25654b82094f7d22.js +0 -1
  586. package/apps/pwa/out/_next/static/chunks/app/design-check/page-8ea49a4c6dd2c6e7.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/app/editor/page-ce5113aefea0a8d1.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/app/exec/page-0e5a41fac1c75396.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/app/field/page-31aaf1ed37c6df9a.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/app/healthz/route-339fabb3499da0e1.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/app/inside/page-3dcc1fa411e1f4e0.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/app/intake/page-c69247e7134d329d.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/app/layout-66dc5cd3a0999abe.js +0 -1
  594. package/apps/pwa/out/_next/static/chunks/app/live/page-26ff7ff651a7217d.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/app/live-run/page-617b3976981efa96.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/app/loops/page-d256f5d5d9552823.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/app/market/page-ce8f1907b8a6101c.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-33e182a316263116.js +0 -1
  599. package/apps/pwa/out/_next/static/chunks/app/morning/page-32866485913d6a04.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/app/not-found-7607cca9b84950c6.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/app/observatory/page-eeab86176b03dc3b.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-5b93dbae3432b996.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-77a6cb90a9cf2b10.js +0 -1
  604. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f47e768b5b242b5a.js +0 -1
  605. package/apps/pwa/out/_next/static/chunks/app/outputs/page-eb41aa3367fc2505.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/app/page-4d13ab0b8a267e3f.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/app/reflection/page-84dad32ff0fc0831.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-95306f892aa2d0ee.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/app/sessions/page-940fb6c217f6b470.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/app/settings/page-7b675dfd35c36fc9.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8bd0d3cf7665bcfc.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/app/setup/layout-339fabb3499da0e1.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/app/setup/page-c219f2ed171131be.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/app/share/page-40f8fa21bdae955b.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/app/showroom/page-468f8313e4f1fbd9.js +0 -1
  616. package/apps/pwa/out/_next/static/chunks/app/tasks/page-7bbb8fa353a246c4.js +0 -1
  617. package/apps/pwa/out/_next/static/chunks/app/today/page-4214b855d9233ea8.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/app/trace/page-936752c7aa58762e.js +0 -1
  619. package/apps/pwa/out/_next/static/chunks/app/vault/page-ec052ad359df1797.js +0 -1
  620. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-8305570551091338.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/app/workflows/page-fad650e4793adb65.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/app/workspace/page-d90e64743826e1d8.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-a16bcfd49af86a32.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/e5012ffe.f727e23c231f4342.js +0 -122
  625. package/apps/pwa/out/_next/static/chunks/ea695e3c-1f1d70d740cf3031.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/framework-906af9bd029e166c.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/main-a24847f9f684d469.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/main-app-7bdcd8aa7c8397d3.js +0 -1
  629. package/apps/pwa/out/_next/static/chunks/pages/_app-6ba46a5066fc226d.js +0 -1
  630. package/apps/pwa/out/_next/static/chunks/pages/_error-a8dc6d07591f2a25.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/webpack-f13df28c15c55eee.js +0 -1
  632. package/src/cli/g6a-registration-probe.ts +0 -16
  633. /package/apps/pwa/out/_next/static/{IVKr3AlRUVLKsw2fh7eXF → L04y73dlwKeTW-GetaTF3}/_ssgManifest.js +0 -0
@@ -5,11 +5,11 @@
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>] [--publish-at <iso>] [--gate-executor <pod|host> [--gate-host <ssh-host>]] [--dry-run] [--json] [--if-ready]
9
- // elanous release resume --run <runId> --from <node> [--waive <node> --reason <text>] [--accepted-regressions <json>] [--gate-executor <pod|host> [--gate-host <ssh-host>]] [--json]
8
+ // elanous release run --version <x.y.z> [--prerelease rc] [--main-cut | --cut-commit <sha>] [--publish-at <iso>] [--gate-executor <pod|host> [--gate-host <ssh-host>]] [--dry-run] [--json] [--if-ready] [--force-new]
9
+ // elanous release resume --run <runId> --from <node> [--waive <node> --reason <text>] [--accepted-regressions <json>] [--gate-executor <pod|host> [--gate-host <ssh-host>]] [--gate-pod-pool <pool>] [--json]
10
10
  // elanous release auto-start [--window-minutes <n>] [--apply] [--json]
11
11
  // elanous release rehearsal tick [--apply] [--checkout <dir>] [--shards <n>] [--remote <host>] [--json]
12
- // elanous release preflight --version <x.y.z> [--publish-at <iso>] [--json] (컷 30분 전 사전 점검 · 읽기 전용 · ⛔ 있으면 exit 1)
12
+ // elanous release preflight --version <x.y.z> [--publish-at <iso>] [--json] (컷 − 60분 main export 포함 사전 점검 · 읽기 전용 · ⛔ 있으면 exit 1)
13
13
  // elanous release cut-branch --version <x.y.z> --base <sha> --pick <sha>... [--dry-run]
14
14
  //
15
15
  // 🩸 계기(2026-09-25 v0.1.0 첫 공개): 손으로 밟은 절차에서 둘을 빠뜨릴 뻔했다 — 공개본 git 커밋(판 커밋이 공개본 커밋이 된다) ·
@@ -28,13 +28,13 @@ import { effectiveInstanceRoot, releaseLedgerRoot } from '../instance/resolve.js
28
28
  import { getElanousConfigDirOverride } from '../elanous-config-dir.js';
29
29
  import { envLiteral } from '../platform/env-literal.js';
30
30
  import { getUserConfig, userConfigPath } from '../user-config.js';
31
- import { addItem, cellsReferencingDoc, claimItem, decodeClaimHistoryEntry, devVersion, inspectChecklistTargetPaths, lintDocRefs, listChecklist, normalizeRefs, ownerMatches, parseOwner, parityGap, refRoots, removeItem, renderRefsStatus, seedFromRoadmap, setItem, summarizeChecklist, type ChecklistStatus, type ChecklistDisposition, type ChecklistKind, type ChecklistDispatchKind } from '../release-loop/checklist.js';
31
+ import { addItem, cellsReferencingDoc, claimItem, decodeClaimHistoryEntry, devVersion, inspectChecklistTargetPaths, lintDocRefs, listChecklist, normalizeRefs, noteDispatchSkip, ownerMatches, parseOwner, parityGap, refRoots, removeItem, renderRefsStatus, seedFromRoadmap, setItem, summarizeChecklist, type ChecklistStatus, type ChecklistDisposition, type ChecklistKind, type ChecklistDispatchKind } from '../release-loop/checklist.js';
32
32
  import * as features from '../release-loop/feature-store.js';
33
33
  import { evidencePlan, landedButYellow, type MergedChecklistPr } from '../release-loop/landed-but-yellow.js';
34
34
  import { formatSchedule, getSchedule, listSchedules, setSchedule } from '../release-loop/release-schedule.js';
35
35
  import { mergedPrsLast24h, placeCell, planPullForward, pullForwardPlacementConfig, rebalance, seatMove, type PlacementPriority } from '../release-loop/placement.js';
36
36
  import { readTrafficSignal } from '../seat-loop/traffic-signal.js';
37
- import { parseRubric, readRubricItems, rubricGrade, rubricScore } from '../release-loop/rubric.js';
37
+ import { parseRubric, parseRubricS, readRubricItems, rubricGrade, rubricScore } from '../release-loop/rubric.js';
38
38
  import { writeStdoutJson } from './stdout-json.js';
39
39
  import { CliUserError } from './cli-user-error.js';
40
40
  import { hqCliWriteAllowed, type HqDeps } from '../hq/hq.js';
@@ -43,11 +43,12 @@ import { isIsolatedLedgerWriteRoot } from '../hq/ledger-write-target.js';
43
43
  import { runUnattendedRelease, type UnattendedReleaseDeps } from '../../scripts/release-loop/unattended-release.js';
44
44
  import { publishAtError } from '../../scripts/release-loop/publish-at.js';
45
45
  import { cutReleaseBranch } from '../../scripts/release-loop/cut-branch.js';
46
- import { resumeReleaseRun } from '../../scripts/release-loop/resume-release.js';
46
+ import { planResumeReleaseRun, resumeReleaseRun } from '../../scripts/release-loop/resume-release.js';
47
47
  import type { AcceptedRegression } from '../../scripts/release-loop/resume.js';
48
48
  import type { PrereleaseKind } from '../../scripts/release-loop/release-version.js';
49
49
  import { releaseReadiness } from '../../scripts/release-loop/release-readiness.js';
50
50
  import { runReleaseIfReady } from './release-run-if-ready.js';
51
+ import { ReleaseRunClaimConflict, findActiveReleaseRun, readActiveForceReleaseRun, readReleaseRunLock, withDirectReleaseRunLock, withForceReleaseRunMarker } from './release-single-flight.js';
51
52
  import { formatPreflight, releasePreflight } from '../../scripts/release-loop/preflight.js';
52
53
  import { defaultReleaseGraphRunsRoot, formatPublishProposal, measurePublishWindow, proposePublishAt, publishWindowWarning } from '../../scripts/release-loop/publish-window.js';
53
54
  import { autoStartScheduledRelease, type AutoStartDeferral } from '../release-loop/auto-start.js';
@@ -491,13 +492,33 @@ async function jsonAction<T>(json: boolean | undefined, body: (log: (l: string)
491
492
  }
492
493
  }
493
494
 
494
- function warnChecklistTargetPaths(input: { title: string; evidence?: string; refs?: readonly string[]; dispatchKind?: ChecklistDispatchKind }): void {
495
+ function warnChecklistTargetPaths(input: { title: string; evidence?: string; refs?: readonly string[]; dispatchKind?: ChecklistDispatchKind }, strict = false): void {
495
496
  const cwd = process.cwd();
496
- const existsAtMain = (path: string): boolean => {
497
- if (path.startsWith('/') || path.split('/').includes('..')) return false;
498
- const result = spawnSync('git', ['cat-file', '-e', `origin/main:${path}`], { cwd, timeout: 5_000, stdio: 'ignore' });
499
- return result.status === 0;
497
+ const objectTypeAtMain = (path: string): 'blob' | 'tree' | 'commit' | 'blocked-blob' | 'blocked-gitlink' | 'unavailable' | null => {
498
+ if (path.startsWith('/') || path.split('/').includes('..')) return null;
499
+ const segments = path.split('/');
500
+ for (let length = 1; length < segments.length; length++) {
501
+ const ancestor = segments.slice(0, length).join('/');
502
+ const lookup = spawnSync('git', ['cat-file', '-t', `origin/main:${ancestor}`], { cwd, timeout: 5_000, encoding: 'utf8' });
503
+ if (lookup.status === 0) {
504
+ if (lookup.stdout.trim() === 'blob') return 'blocked-blob';
505
+ if (lookup.stdout.trim() === 'commit') return 'blocked-gitlink';
506
+ if (lookup.stdout.trim() !== 'tree') return 'unavailable';
507
+ continue;
508
+ }
509
+ const missing = spawnSync('git', ['ls-tree', 'origin/main', '--', ancestor], { cwd, timeout: 5_000, encoding: 'utf8' });
510
+ return missing.status === 0 && missing.stdout.trim() === '' ? null : 'unavailable';
511
+ }
512
+ const result = spawnSync('git', ['cat-file', '-t', `origin/main:${path}`], { cwd, timeout: 5_000, encoding: 'utf8' });
513
+ const type = result.status === 0 ? result.stdout.trim() : '';
514
+ if (type === 'blob' || type === 'tree' || type === 'commit') return type;
515
+ if (result.status === 0) return 'unavailable';
516
+ // cat-file also fails for a genuinely new path. ls-tree distinguishes that
517
+ // from a missing ref or another Git read failure before --strict accepts 신설:.
518
+ const probe = spawnSync('git', ['ls-tree', 'origin/main', '--', path], { cwd, timeout: 5_000, encoding: 'utf8' });
519
+ return probe.status === 0 && probe.stdout.trim() === '' ? null : 'unavailable';
500
520
  };
521
+ const existsAtMain = (path: string): boolean => objectTypeAtMain(path) === 'blob';
501
522
  const mergedPrFiles = (number: number): string[] => {
502
523
  const result = spawnSync('gh', ['pr', 'view', String(number), '--json', 'state,files'], { cwd, encoding: 'utf8', timeout: 10_000 });
503
524
  if (result.status !== 0) return [];
@@ -506,7 +527,8 @@ function warnChecklistTargetPaths(input: { title: string; evidence?: string; ref
506
527
  return pr.state === 'MERGED' ? (pr.files ?? []).map((file) => file.path).filter((path): path is string => typeof path === 'string') : [];
507
528
  } catch { return []; }
508
529
  };
509
- const report = inspectChecklistTargetPaths(input, { existsAtMain, mergedPrFiles });
530
+ const report = inspectChecklistTargetPaths(input, { existsAtMain, objectTypeAtMain, mergedPrFiles });
531
+ if (strict && report.warnings.length) throw new CliUserError(`칸 대상 경로/완료 경계 검사: ${report.warnings.join(' · ')}`, '문면을 고치거나 --strict 없이 경고로 저장한다');
510
532
  for (const warning of report.warnings) {
511
533
  console.error(`⚠ 대상 경로: ${warning}${report.candidates.length ? ` · 후보: ${report.candidates.join(' · ')}` : ''}`);
512
534
  debug.log('release.checklist', 'target-path-warning', { warning, candidates: report.candidates });
@@ -713,9 +735,9 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
713
735
  for (const item of readRubricItems(version, releaseLedgerRoot())) {
714
736
  if (item.priority !== 'P0' && item.priority !== 'P1') continue;
715
737
  const group = byPriority[item.priority];
716
- const rubric = parseRubric(`${item.title}\n${item.evidence ?? ''}`);
717
- if (rubric === null) group.missing++;
718
- else { group.s[rubric.S as 0 | 1 | 2 | 3]++; group.total += rubric.S; }
738
+ const s = parseRubricS(item.evidence ?? '') ?? parseRubric(item.title)?.S ?? parseRubricS(item.title);
739
+ if (s === null) group.missing++;
740
+ else { group.s[s as 0 | 1 | 2 | 3]++; group.total += s; }
719
741
  }
720
742
  const result = { version, byPriority, total: byPriority.P0.total + byPriority.P1.total, missing: byPriority.P0.missing + byPriority.P1.missing };
721
743
  if (opts.json) await writeStdoutJson(`${JSON.stringify(result)}\n`);
@@ -858,7 +880,7 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
858
880
  if (opts.applyEvidence && !opts.dryRun && !mayWrite(cmd)) return;
859
881
  const snapshots = opts.open ? openSnapshots : [(landedDeps.checklist ?? listChecklist)(context(cmd).version)];
860
882
  const now = (landedDeps.now ?? (() => new Date()))();
861
- // --version 도 --open 과 같은 판 전체 최좌 수정일로 병합 PR 을 찾는다 — 선택 판 칸의 updatedAt 에 묶이면 그보다 오래 전에 병합된 PR 을 놓친다(LBY-VERSION-OPEN-MISMATCH · OPT #4059).
883
+ // --version 도 --open 과 같은 판 전체 최초 수정일로 병합 PR 을 찾는다 — 선택 판 칸의 updatedAt 에 묶이면 그보다 오래 전에 병합된 PR 을 놓친다(LBY-VERSION-OPEN-MISMATCH · OPT #4059).
862
884
  const sinceSnapshots = opts.open ? snapshots : [...openSnapshots, ...snapshots];
863
885
  const updated = sinceSnapshots.flatMap(({ items }) => items
864
886
  .filter((item) => (item.status === 'yellow' || item.status === 'red') && (opts.owner === undefined || ownerMatches(item.owner, opts.owner)))
@@ -908,13 +930,14 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
908
930
  .option('--ceo-minutes <minutes>', '대표 손 분량(분)').option('--ceo-date <date>', '대표 손 날짜 YYYY-MM-DD (없으면 판 착지일 KST)')
909
931
  .option('--accelerator', '가속 등급 — 다른 칸의 처리량을 올리는 칸(자율성·효율성·동시성). 생략하면 등급 없음')
910
932
  .option('--allow-duplicate-id', '다른 판에 같은 id 가 있어도 경고 후 추가')
933
+ .option('--strict', '대상 경로·완료 경계 경고가 있으면 저장 전 거부')
911
934
  .option('--ref <path...>', '근거 문서 — 저장소 상대 경로[#절] (반복 가능)')
912
- .action((id: string, title: string, opts: { ref?: string[]; owner?: string; kind?: string; priority?: string; deadlineVersion?: string; predecessor?: string[]; ceoMinutes?: string; ceoDate?: string; accelerator?: boolean; allowDuplicateId?: boolean }, cmd: Command) => {
935
+ .action((id: string, title: string, opts: { ref?: string[]; owner?: string; kind?: string; priority?: string; deadlineVersion?: string; predecessor?: string[]; ceoMinutes?: string; ceoDate?: string; accelerator?: boolean; allowDuplicateId?: boolean; strict?: boolean }, cmd: Command) => {
913
936
  const { version, json } = context(cmd);
914
937
  if (!mayWrite(cmd)) return;
915
938
  const dispatchKind = opts.kind && !['screen', 'postpub'].includes(opts.kind) ? opts.kind as ChecklistDispatchKind : undefined;
939
+ warnChecklistTargetPaths({ title, refs: opts.ref, dispatchKind }, opts.strict);
916
940
  const data = addItem(version, { id, title, ...(opts.ref !== undefined ? { refs: opts.ref } : {}), ...(opts.owner !== undefined ? { owner: opts.owner } : {}), ...(opts.kind !== undefined && !dispatchKind ? { kind: opts.kind as ChecklistKind } : {}), ...(dispatchKind ? { dispatchKind } : {}), ...(opts.priority !== undefined ? { priority: opts.priority as PlacementPriority } : {}), ...(opts.deadlineVersion !== undefined ? { deadlineVersion: opts.deadlineVersion } : {}), ...(opts.predecessor !== undefined ? { predecessors: opts.predecessor } : {}), ...(opts.ceoMinutes !== undefined ? { ceoMinutes: ceoMinutes(opts.ceoMinutes) } : {}), ...(opts.ceoDate !== undefined ? { ceoDate: opts.ceoDate } : {}), ...(opts.accelerator ? { accelerator: true } : {}) }, { allowDuplicateId: opts.allowDuplicateId });
917
- warnChecklistTargetPaths({ title, refs: opts.ref, dispatchKind });
918
941
  if (json) console.log(JSON.stringify(data)); else console.log(`✅ ${id} 추가`);
919
942
  });
920
943
  withContext(checklist.command('set <id>').description('칸 상태·근거·담당·처분 갱신'))
@@ -977,15 +1000,26 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
977
1000
  else features.move(id, from, to, actor(), undefined, undefined, opts.reason);
978
1001
  if (json) console.log(JSON.stringify(listChecklist(to))); else console.log(`✅ ${id} ${from} → ${to}`);
979
1002
  });
980
- withContext(checklist.command('retitle <id> <title>').description('칸 제목 수정')).action((id: string, title: string, _opts: unknown, cmd: Command) => {
1003
+ withContext(checklist.command('retitle <id> <title>').description('칸 제목 수정')).option('--strict', '대상 경로·완료 경계 경고가 있으면 저장 전 거부').action((id: string, title: string, opts: { strict?: boolean }, cmd: Command) => {
981
1004
  const { version, json } = context(cmd);
982
1005
  if (!mayWrite(cmd)) return;
983
1006
  const snapshot = listChecklist(version);
984
- features.retitle(id, title, actor(), snapshot.released, snapshot.dev);
985
1007
  const item = snapshot.items.find((entry) => entry.id === id);
986
- warnChecklistTargetPaths({ title, evidence: item?.evidence, refs: item?.refs, dispatchKind: item?.dispatchKind });
1008
+ if (!opts.strict) warnChecklistTargetPaths({ title, evidence: item?.evidence, refs: item?.refs, dispatchKind: item?.dispatchKind });
1009
+ features.retitle(id, title, actor(), snapshot.released, snapshot.dev, opts.strict
1010
+ ? (affected) => warnChecklistTargetPaths({ title, evidence: affected.evidence, refs: affected.refs, dispatchKind: affected.dispatchKind }, true)
1011
+ : undefined);
987
1012
  if (json) console.log(JSON.stringify(listChecklist(version))); else console.log(`✅ ${id} 제목 수정`);
988
1013
  });
1014
+ withContext(checklist.command('skip-note <id> <reason>').description('디스패처 건너뜀 사유를 칸 근거에 중복 없이 기록'))
1015
+ .requiredOption('--expected-title <title>', '검사한 칸 제목; 바뀐 제목이면 기록하지 않는다')
1016
+ .requiredOption('--by <actor>', '실제 기록 주체(디스패처 자리)')
1017
+ .action((id: string, reason: string, opts: { expectedTitle: string; by: string }, cmd: Command) => {
1018
+ const { version, json } = context(cmd);
1019
+ if (!mayWrite(cmd)) return;
1020
+ const data = noteDispatchSkip(version, id, reason, opts.expectedTitle, opts.by);
1021
+ if (json) console.log(JSON.stringify(data)); else console.log(`✅ ${id} 건너뜀 근거 확인`);
1022
+ });
989
1023
  const evidence = checklist.command('evidence').description('칸의 PR·커밋 근거');
990
1024
  withContext(evidence.command('add <id> <ref>').description('근거 참조 추가')).action((id: string, ref: string, _opts: unknown, cmd: Command) => {
991
1025
  const { version, json } = context(cmd);
@@ -1082,7 +1116,7 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
1082
1116
  .action(async (o: { version: string; base: string; pick: string[]; append?: boolean; dryRun?: boolean; json?: boolean }) => {
1083
1117
  await jsonAction(o.json, async (log) => cutReleaseBranch({ ...o, log }), () => true);
1084
1118
  });
1085
- const runAction = async (o: { version: string; cutCommit?: string; mainCut?: boolean; prerelease?: string; dryRun?: boolean; ifReady?: boolean; publishAt?: string; forceFreeze?: boolean; gateExecutor?: string; gateHost?: string; json?: boolean }, logRun = console.log): Promise<boolean | AutoStartDeferral | undefined> => {
1119
+ const runAction = async (o: { version: string; cutCommit?: string; mainCut?: boolean; prerelease?: string; dryRun?: boolean; ifReady?: boolean; publishAt?: string; forceFreeze?: boolean; forceNew?: boolean; gateExecutor?: string; gateHost?: string; json?: boolean }, logRun = console.log): Promise<boolean | AutoStartDeferral | undefined> => {
1086
1120
  // Only the rc rehearsal channel is operated for now (alpha/beta stay valid version shapes, not run options).
1087
1121
  if (o.prerelease !== undefined && o.prerelease !== 'rc') {
1088
1122
  const message = `--prerelease supports rc only: ${o.prerelease}`;
@@ -1099,6 +1133,18 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
1099
1133
  process.exitCode = 1;
1100
1134
  return false;
1101
1135
  }
1136
+ const ledgerRoot = releaseRunDeps.ledgerRoot ?? releaseLedgerRoot();
1137
+ if (!o.dryRun && !o.forceNew) {
1138
+ const active = findActiveReleaseRun(o.version, ledgerRoot) ?? readActiveForceReleaseRun(o.version, ledgerRoot);
1139
+ if (active) {
1140
+ const error = `release run already running: ${o.version} runId=${active.runId} pid=${active.pid} startedAt=${active.startedAt} (--force-new to bypass)`;
1141
+ debug.log('release.run', 'already-running', { version: o.version, ...active });
1142
+ if (o.json) await writeStdoutJson(`${JSON.stringify({ ok: false, reason: 'already-running', error, ...active })}\n`);
1143
+ else console.error(`⛔ ${error}`);
1144
+ process.exitCode = 1;
1145
+ return false;
1146
+ }
1147
+ }
1102
1148
  const prerelease = o.prerelease as PrereleaseKind | undefined;
1103
1149
  if (!o.dryRun) {
1104
1150
  try {
@@ -1130,10 +1176,20 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
1130
1176
  try {
1131
1177
  const outcome = await runReleaseIfReady(o.version, {
1132
1178
  ledgerRoot: releaseRunDeps.ledgerRoot,
1179
+ forceNew: o.forceNew,
1133
1180
  readiness: (version, options) => releaseReadiness(version, { ledgerRoot: releaseRunDeps.ledgerRoot, checklist: releaseRunDeps.checklist, ...options }),
1134
- run: () => runUnattendedRelease({ version: o.version, cutCommit: o.cutCommit, mainCut: o.mainCut, prerelease, forceFreeze: o.forceFreeze, publishAt: o.publishAt, gateExecutor: o.gateExecutor, gateHost: o.gateHost }, releaseRunDeps),
1181
+ run: () => runUnattendedRelease({ version: o.version, cutCommit: o.cutCommit, mainCut: o.mainCut, prerelease, forceFreeze: o.forceFreeze, forceNew: o.forceNew, publishAt: o.publishAt, gateExecutor: o.gateExecutor, gateHost: o.gateHost }, releaseRunDeps),
1135
1182
  });
1136
1183
  if (outcome.skipped) {
1184
+ if (outcome.reason === 'already-running') {
1185
+ const active = findActiveReleaseRun(o.version, ledgerRoot) ?? readActiveForceReleaseRun(o.version, ledgerRoot);
1186
+ const lock = readReleaseRunLock(o.version, ledgerRoot);
1187
+ const error = `release run already running: ${o.version} runId=${active?.runId ?? 'pending'} pid=${active?.pid ?? lock?.pid ?? 'unknown'} startedAt=${active?.startedAt ?? lock?.startedAt ?? 'unknown'} (--force-new to bypass)`;
1188
+ if (o.json) await writeStdoutJson(`${JSON.stringify({ ok: false, reason: 'already-running', error, ...(active ?? {}) })}\n`);
1189
+ else console.error(`⛔ ${error}`);
1190
+ process.exitCode = 1;
1191
+ return false;
1192
+ }
1137
1193
  if (o.json) await writeStdoutJson(`${JSON.stringify(outcome)}\n`);
1138
1194
  else logRun(`· 준비 안 됨 ${o.version} · ${outcome.reason} · ${outcome.detail}`);
1139
1195
  return { deferred: true, reason: outcome.reason };
@@ -1159,7 +1215,20 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
1159
1215
  }
1160
1216
  }
1161
1217
  await jsonAction(o.json, async (log) => {
1162
- const result = await runUnattendedRelease({ version: o.version, dryRun: o.dryRun, cutCommit: o.cutCommit, mainCut: o.mainCut, prerelease, forceFreeze: o.forceFreeze, publishAt: o.publishAt, gateExecutor: o.gateExecutor, gateHost: o.gateHost }, releaseRunDeps);
1218
+ const run = () => runUnattendedRelease({ version: o.version, dryRun: o.dryRun, cutCommit: o.cutCommit, mainCut: o.mainCut, prerelease, forceFreeze: o.forceFreeze, forceNew: o.forceNew, publishAt: o.publishAt, gateExecutor: o.gateExecutor, gateHost: o.gateHost }, releaseRunDeps);
1219
+ const forceRun = () => {
1220
+ const atStart = releaseReadiness(o.version, { ledgerRoot, checklist: releaseRunDeps.checklist, isPidAlive: () => false });
1221
+ if (!atStart.ready) throw new Error(`release run --force-new not ready after claim: ${atStart.reason}`);
1222
+ return run();
1223
+ };
1224
+ let result: Awaited<ReturnType<typeof runUnattendedRelease>>;
1225
+ try { result = o.dryRun ? await run() : o.forceNew ? await withForceReleaseRunMarker(o.version, ledgerRoot, forceRun) : await withDirectReleaseRunLock(o.version, ledgerRoot, run); }
1226
+ catch (error) {
1227
+ if (!(error instanceof ReleaseRunClaimConflict)) throw error;
1228
+ const active = findActiveReleaseRun(o.version, ledgerRoot) ?? readActiveForceReleaseRun(o.version, ledgerRoot);
1229
+ const lock = readReleaseRunLock(o.version, ledgerRoot);
1230
+ throw new Error(`release run already running: ${o.version} runId=${active?.runId ?? 'pending'} pid=${active?.pid ?? lock?.pid ?? 'unknown'} startedAt=${active?.startedAt ?? lock?.startedAt ?? 'unknown'} (--force-new to bypass)`);
1231
+ }
1163
1232
  log(`${result.dryRun ? '· 드라이런' : '▶ 릴리스 루프'} ${result.input.version} · 입력 ${JSON.stringify(result.input)}`);
1164
1233
  return result;
1165
1234
  }, (r) => r.dryRun || r.state?.status === 'done' || r.state?.status === 'awaiting-approval');
@@ -1174,12 +1243,13 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
1174
1243
  .option('--if-ready', '준비되지 않았으면 사유를 출력하고 성공으로 건너뛴다')
1175
1244
  .option('--publish-at <iso>', '승인·선행 노드 통과 뒤 지정 시각까지 발행 대기(오프셋 포함 ISO 시각)')
1176
1245
  .option('--force-freeze', '동결 중 게이트·발행 강행(관측 기록) — 가지 컷은 동결이 필요 없다 · 동결은 비상 스위치로 남는다')
1246
+ .option('--force-new', '같은 판의 살아 있는 릴리스 런을 확인한 뒤에도 새 런을 명시적으로 시작')
1177
1247
  .option('--gate-executor <pod|host>', 'gate 노드 실행기(기본 pod) — host 는 Pod 대신 SSH 호스트의 깨끗한 클론에서 같은 판정(면제 아님)')
1178
1248
  .option('--gate-host <ssh-host>', '--gate-executor host 의 SSH 호스트(기본 release.loop.gateRemote)')
1179
1249
  .option('--json', '결과 한 줄 JSON(stdout) · 사람 줄은 stderr')
1180
- .action(async (o: { version: string; cutCommit?: string; mainCut?: boolean; prerelease?: string; dryRun?: boolean; ifReady?: boolean; publishAt?: string; forceFreeze?: boolean; gateExecutor?: string; gateHost?: string; json?: boolean }) => { await runAction(o); });
1250
+ .action(async (o: { version: string; cutCommit?: string; mainCut?: boolean; prerelease?: string; dryRun?: boolean; ifReady?: boolean; publishAt?: string; forceFreeze?: boolean; forceNew?: boolean; gateExecutor?: string; gateHost?: string; json?: boolean }) => { await runAction(o); });
1181
1251
  release.command('preflight')
1182
- .description('컷 30분 전 사전 점검(읽기 전용) — 체크리스트 게이트 모의 · 발행 뒤 칸 · run --dry-run 입력 · 동결 · 일정/게이트 실측. ⛔ 있으면 exit 1')
1252
+ .description('컷 전 사전 점검(읽기 전용) — 체크리스트·런 입력·동결·일정 + 컷 − 60분 origin/main export(자동 예약 없음). ⛔ 있으면 exit 1')
1183
1253
  .requiredOption('--version <v>', '점검할 판(x.y.z)')
1184
1254
  .option('--publish-at <iso>', '계획 발행 시각(기본: 컷 뒤 첫 07·18시 KST)')
1185
1255
  .option('--json', '결과 한 줄 JSON(stdout)')
@@ -1229,8 +1299,11 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
1229
1299
  .option('--accepted-regressions <json>', '알려진 문제 추가 입력 JSON 배열 [{"id":"…","note":"…"}]')
1230
1300
  .option('--gate-executor <pod|host>', '다시 도는 gate 의 실행기 전환(--from gate 이전만) — 런 입력에 기록 · 면제 아님')
1231
1301
  .option('--gate-host <ssh-host>', '--gate-executor host 의 SSH 호스트(기본 저장된 런 입력 gateRemote)')
1302
+ .option('--gate-pod-pool <pool>', '다시 도는 gate 의 Pod 풀을 전환(--from gate 이전만) — 런 입력에 기록')
1303
+ .option('--command-root <path>', '노드 명령을 실행할 컷 tip checkout(생략 시 현재 폴더)')
1304
+ .option('--dry-run', '재개 preflight만 수행하고 런·상태 원장·시도 횟수는 변경하지 않는다')
1232
1305
  .option('--json', '결과 한 줄 JSON(stdout)')
1233
- .action(async (o: { run: string; from: string; partial?: boolean; waive?: string; reason?: string; acceptedRegressions?: string; gateExecutor?: string; gateHost?: string; json?: boolean }) => {
1306
+ .action(async (o: { run: string; from: string; partial?: boolean; waive?: string; reason?: string; acceptedRegressions?: string; gateExecutor?: string; gateHost?: string; gatePodPool?: string; commandRoot?: string; dryRun?: boolean; json?: boolean }) => {
1234
1307
  await jsonAction(o.json, async (log) => {
1235
1308
  let acceptedRegressions: AcceptedRegression[] | undefined;
1236
1309
  if (o.acceptedRegressions !== undefined) {
@@ -1241,21 +1314,34 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
1241
1314
  throw new Error('acceptedRegressions must be an array of {id, note}');
1242
1315
  }
1243
1316
  }
1244
- const result = await resumeReleaseRun({ runId: o.run, from: o.from, partial: o.partial,
1317
+ const resumeOptions = { runId: o.run, from: o.from, partial: o.partial, commandRoot: o.commandRoot,
1245
1318
  ...(o.waive === undefined ? {} : { waive: o.waive }),
1246
1319
  ...(o.reason === undefined ? {} : { reason: o.reason }),
1247
1320
  ...(o.acceptedRegressions === undefined ? {} : { acceptedRegressions }),
1248
1321
  ...(o.gateExecutor === undefined ? {} : { gateExecutor: o.gateExecutor }),
1249
- ...(o.gateHost === undefined ? {} : { gateHost: o.gateHost }) });
1250
- const override = result.state.inputOverrides?.at(-1);
1251
- if (o.gateExecutor !== undefined && override) log(`⇄ gate 실행기 ${override.set.gateExecutor}${override.set.gateRemote ? ` @ ${override.set.gateRemote}` : ''} (런 입력에 기록 · 면제 아님)`);
1322
+ ...(o.gateHost === undefined ? {} : { gateHost: o.gateHost }),
1323
+ ...(o.gatePodPool === undefined ? {} : { gatePodPool: o.gatePodPool }) };
1324
+ if (o.dryRun) {
1325
+ const plan = await planResumeReleaseRun(resumeOptions, { onSource: log });
1326
+ const gate = plan.gatePlan.mode === 'not-run' ? 'gate 실행 없음'
1327
+ : plan.gatePlan.mode === 'partial' ? `부분 gate 계획 ${plan.partial.requested && plan.partial.applied ? `${plan.partial.files}파일` : '미상'}`
1328
+ : `전체 gate${plan.gatePlan.reason ? ` (${plan.gatePlan.reason})` : ''}`;
1329
+ log(`· 재개 preflight 통과 · ${plan.version} · ${plan.fromNode}부터 · tip ${plan.tip.to.slice(0, 12)}${plan.tip.changed ? ` (기록 tip ${plan.tip.from.slice(0, 12)}에서 이동)` : ''} · ${gate} · 시도 ${plan.nextAttempt}/${plan.attemptLimit} · 실행·원장 변경 없음`);
1330
+ return plan;
1331
+ }
1332
+ const result = await resumeReleaseRun(resumeOptions, { onSource: log });
1333
+ const executorOverride = [...(result.state.inputOverrides ?? [])].reverse().find((item) => item.kind === 'gate-executor');
1334
+ const poolOverride = [...(result.state.inputOverrides ?? [])].reverse().find((item) => item.kind === 'gate-pod-pool');
1335
+ if (o.gateExecutor !== undefined && executorOverride?.kind === 'gate-executor') log(`⇄ gate 실행기 ${executorOverride.set.gateExecutor}${executorOverride.set.gateRemote ? ` @ ${executorOverride.set.gateRemote}` : ''} (런 입력에 기록 · 면제 아님)`);
1336
+ if (o.gatePodPool !== undefined && poolOverride?.kind === 'gate-pod-pool') log(`⇄ gate Pod 풀 ${poolOverride.set.gatePodPool} (런 입력에 기록)`);
1252
1337
  if (result.partial.requested) log(result.partial.applied
1253
1338
  ? `◐ 부분 재검${result.partial.auto ? '(기본)' : ''}: ${result.partial.files}파일${result.partial.closure ? ` (코드 닿음 ${result.partial.closure.reached} · 닫힘 불투명 ${result.partial.closure.opaque})` : ''} · 나머지는 앞 게이트(${result.partial.priorCommit.slice(0, 12)}) 결과를 잇는다`
1254
1339
  : `● 전체 게이트로: ${result.partial.reason}`);
1255
1340
  log(`${result.tip.changed ? `↻ ${result.tip.branch} ${result.tip.from.slice(0, 12)} → ${result.tip.to.slice(0, 12)} (백업 ${result.tip.backup})` : `· ${result.tip.branch} 끝 그대로 ${result.tip.to.slice(0, 12)}`} · ${o.from} 부터 → ${result.state.status}`);
1256
1341
  return { tip: result.tip, partial: result.partial, status: result.state.status, runId: result.state.runId,
1257
- ...(o.gateExecutor !== undefined && result.state.inputOverrides?.length ? { gateExecutor: result.state.inputOverrides.at(-1) } : {}) };
1258
- }, (r) => r.status === 'done' || r.status === 'awaiting-approval');
1342
+ ...(o.gateExecutor !== undefined && executorOverride?.kind === 'gate-executor' ? { gateExecutor: executorOverride } : {}),
1343
+ ...(o.gatePodPool !== undefined && poolOverride?.kind === 'gate-pod-pool' ? { gatePodPool: poolOverride } : {}) };
1344
+ }, (r) => 'preflight' in r || r.status === 'done' || r.status === 'awaiting-approval');
1259
1345
  });
1260
1346
  release.command('gate-history')
1261
1347
  .description('GATE-FAIL-HISTORY (0.2.29) — 판을 넘는 게이트 실패 표 — 모든 release-loop·light-rc 런 원장 ⊕ gate-verdict 에서 시험 파일별 introduced·preexisting·env·flaky 횟수 · 마지막 실패 판 · 원인 PR(수리 PR 본문 «원인 #N»)')
@@ -1308,6 +1394,7 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
1308
1394
  await jsonAction(o.json, async (log) => {
1309
1395
  const ledgerRoot = scheduleLedgerRoot();
1310
1396
  if (o.apply && !mayWriteLedger('release rehearsal tick', false, ledgerRoot)) throw new Error('release rehearsal tick: ledger write denied');
1397
+ // The tick reports a preflight shard shortage as an unmeasured attempt; its released stage can be claimed on the next CLI tick.
1311
1398
  const result = await runRehearsalTick({ apply: o.apply, checkout: o.checkout, shards: o.shards === undefined ? undefined : Number(o.shards), remote: o.remote, ledgerRoot }, rehearsalDeps);
1312
1399
  log(`· 리허설 ${result.status}${'version' in result ? ` ${result.version}` : ''}${result.status === 'due' ? ` · 실행 ${result.run ?? '-'} · 건너뜀 ${result.supersede.join(', ') || '-'}` : ''}`);
1313
1400
  return result;
@@ -1,10 +1,9 @@
1
- import { closeSync, mkdirSync, openSync, rmSync, writeFileSync } from 'node:fs';
2
- import { join } from 'node:path';
3
1
  import { debug } from '../debug/log.js';
4
2
  import { releaseLedgerRoot } from '../instance/resolve.js';
5
3
  import { adviseLaunch, compareLaunchAdvice } from '../harness/launch-advice.js';
6
4
  import type { QueuePool } from '../harness/harness-queue.js';
7
5
  import type { ReleaseReadiness } from '../../scripts/release-loop/release-readiness.js';
6
+ import { ReleaseRunClaimConflict, releaseRunLockOwnerAlive, withDirectReleaseRunLock, withForceReleaseRunMarker } from './release-single-flight.js';
8
7
 
9
8
  export type ReleaseIfReadySkip = { skipped: true; reason: Exclude<ReleaseReadiness, { ready: true }>['reason']; detail: string };
10
9
 
@@ -16,6 +15,8 @@ export async function runReleaseIfReady<T>(version: string, deps: {
16
15
  now?: () => Date;
17
16
  advice?: typeof adviseLaunch;
18
17
  pool?: () => QueuePool | undefined;
18
+ /** Explicit operator override: keep readiness checks, bypass only the same-version lock. */
19
+ forceNew?: boolean;
19
20
  }): Promise<ReleaseIfReadySkip | { skipped: false; result: T }> {
20
21
  const skip = (readiness: Exclude<ReleaseReadiness, { ready: true }>): ReleaseIfReadySkip => {
21
22
  const detail = readiness.reason === 'checklist-blocked'
@@ -24,36 +25,36 @@ export async function runReleaseIfReady<T>(version: string, deps: {
24
25
  debug.log('release-loop.readiness', readiness.reason, { version, detail });
25
26
  return { skipped: true, reason: readiness.reason, detail };
26
27
  };
27
- const readiness = await deps.readiness(version);
28
+ const ledgerRoot = deps.ledgerRoot ?? releaseLedgerRoot();
29
+ const readiness = await deps.readiness(version, { isPidAlive: deps.forceNew ? () => false
30
+ : (pid) => releaseRunLockOwnerAlive(version, ledgerRoot, pid) });
28
31
  if (!readiness.ready) return skip(readiness);
29
32
 
30
- const dir = join(deps.ledgerRoot ?? releaseLedgerRoot(), 'release', version);
31
- mkdirSync(dir, { recursive: true });
32
- const lock = join(dir, 'run.lock');
33
- let fd: number;
34
- try { fd = openSync(lock, 'wx', 0o600); }
35
- catch (error) {
36
- if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
33
+ if (deps.forceNew) return withForceReleaseRunMarker(version, ledgerRoot, async () => {
34
+ const atStart = await deps.readiness(version, { isPidAlive: () => false });
35
+ if (!atStart.ready) return skip(atStart);
36
+ return { skipped: false, result: await deps.run() };
37
+ });
38
+
39
+ try {
40
+ return await withDirectReleaseRunLock(version, ledgerRoot, async () => {
41
+ // This call still checks publication and checklist, but must not treat our own lock as another runner.
42
+ const insideLock = await deps.readiness(version, { isPidAlive: () => false });
43
+ if (!insideLock.ready) return skip(insideLock);
44
+ debug.log('release-loop.readiness', 'ready', { version });
45
+ try {
46
+ const advice = (deps.advice ?? adviseLaunch)({ pool: deps.pool?.(), caller: 'release-run' });
47
+ const { kind } = compareLaunchAdvice(advice, 'launched');
48
+ debug.log('resource.advice', 'launch-advice', { caller: 'release-run', verdict: advice.verdict, why: advice.why, actual: 'launched', kind, version });
49
+ } catch {
50
+ // Shadow only: a throwing advice seam must never affect the launch decision or result.
51
+ }
52
+ return { skipped: false, result: await deps.run() };
53
+ }, { pid: deps.pid, now: deps.now });
54
+ } catch (error) {
55
+ if (!(error instanceof ReleaseRunClaimConflict)) throw error;
37
56
  const detail = 'run.lock already exists';
38
57
  debug.log('release-loop.readiness', 'already-running', { version, detail });
39
58
  return { skipped: true, reason: 'already-running', detail };
40
59
  }
41
- try {
42
- writeFileSync(fd, `${JSON.stringify({ pid: deps.pid ?? process.pid, startedAt: (deps.now ?? (() => new Date()))().toISOString() })}\n`);
43
- // This call still checks publication and checklist, but must not treat our own lock as another runner.
44
- const insideLock = await deps.readiness(version, { isPidAlive: () => false });
45
- if (!insideLock.ready) return skip(insideLock);
46
- debug.log('release-loop.readiness', 'ready', { version });
47
- try {
48
- const advice = (deps.advice ?? adviseLaunch)({ pool: deps.pool?.(), caller: 'release-run' });
49
- const { kind } = compareLaunchAdvice(advice, 'launched');
50
- debug.log('resource.advice', 'launch-advice', { caller: 'release-run', verdict: advice.verdict, why: advice.why, actual: 'launched', kind, version });
51
- } catch {
52
- // Shadow only: a throwing advice seam must never affect the launch decision or result.
53
- }
54
- return { skipped: false, result: await deps.run() };
55
- } finally {
56
- closeSync(fd);
57
- rmSync(lock);
58
- }
59
60
  }
@@ -0,0 +1,178 @@
1
+ import { closeSync, mkdirSync, openSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { join } from 'node:path';
4
+ import { Database } from 'bun:sqlite';
5
+ import { graphRunAlive, type GraphRunState } from '../graph-runner/runner.js';
6
+ import { psProcessStartMs, START_TOLERANCE_MS } from '../harness/harness-stop.js';
7
+
8
+ export interface ActiveReleaseRun {
9
+ runId: string;
10
+ pid: number;
11
+ startedAt: string;
12
+ }
13
+
14
+ export class ReleaseRunClaimConflict extends Error {
15
+ readonly code = 'EEXIST';
16
+ constructor() { super('release run claim already exists'); }
17
+ }
18
+
19
+ function isPidAlive(pid: number): boolean {
20
+ try { process.kill(pid, 0); return true; }
21
+ catch (error) {
22
+ if ((error as NodeJS.ErrnoException).code === 'ESRCH') return false;
23
+ if ((error as NodeJS.ErrnoException).code === 'EPERM') return true;
24
+ throw error;
25
+ }
26
+ }
27
+
28
+ /** The saved graph run is the authority for an active release, not a process-name match. */
29
+ export function findActiveReleaseRun(version: string, ledgerRoot: string): ActiveReleaseRun | undefined {
30
+ const dir = join(ledgerRoot, 'graph-runs', 'release-loop');
31
+ let names: string[];
32
+ try { names = readdirSync(dir).filter((name) => name.endsWith('.json')); }
33
+ catch (error) {
34
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;
35
+ throw error;
36
+ }
37
+ const runs: ActiveReleaseRun[] = [];
38
+ for (const name of names) {
39
+ try {
40
+ const state = JSON.parse(readFileSync(join(dir, name), 'utf8')) as {
41
+ graphId?: unknown; runId?: unknown; status?: unknown; pid?: unknown; pidStartedAt?: unknown; startedAt?: unknown;
42
+ input?: { version?: unknown };
43
+ };
44
+ if (state.graphId !== 'release-loop' || state.status !== 'running' || state.input?.version !== version
45
+ || typeof state.runId !== 'string' || typeof state.startedAt !== 'string'
46
+ || typeof state.pid !== 'number' || !Number.isSafeInteger(state.pid) || state.pid <= 0
47
+ || graphRunAlive(state as GraphRunState) !== true) continue;
48
+ runs.push({ runId: state.runId, pid: state.pid, startedAt: state.startedAt });
49
+ } catch (error) {
50
+ if (error instanceof SyntaxError || (error as NodeJS.ErrnoException).code === 'ENOENT') continue;
51
+ throw error;
52
+ }
53
+ }
54
+ return runs.sort((a, b) => b.startedAt.localeCompare(a.startedAt))[0];
55
+ }
56
+
57
+ export function readReleaseRunLock(version: string, ledgerRoot: string): { pid?: number; startedAt?: string; pidStartedAt?: string } | undefined {
58
+ try {
59
+ const value = JSON.parse(readFileSync(join(ledgerRoot, 'release', version, 'run.lock'), 'utf8')) as { pid?: unknown; startedAt?: unknown; pidStartedAt?: unknown };
60
+ return { pid: typeof value.pid === 'number' ? value.pid : undefined,
61
+ startedAt: typeof value.startedAt === 'string' ? value.startedAt : undefined,
62
+ pidStartedAt: typeof value.pidStartedAt === 'string' ? value.pidStartedAt : undefined };
63
+ } catch (error) {
64
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;
65
+ if (error instanceof SyntaxError) return {};
66
+ throw error;
67
+ }
68
+ }
69
+
70
+ /** A live PID belongs to this lock only if its process birth still matches. */
71
+ export function releaseRunLockOwnerAlive(version: string, ledgerRoot: string, pid: number): boolean {
72
+ const owner = readReleaseRunLock(version, ledgerRoot);
73
+ if (!owner || owner.pid !== pid || !isPidAlive(pid)) return false;
74
+ const actual = psProcessStartMs(pid);
75
+ // A failed birth probe must not let a second launch reclaim a live PID's lock.
76
+ if (actual === null) return true;
77
+ const recorded = Date.parse(owner.pidStartedAt ?? '');
78
+ if (Number.isFinite(recorded)) return Math.abs(actual - recorded) <= START_TOLERANCE_MS;
79
+ // Older locks have only their claim time. A process born after that claim reused the PID.
80
+ const claimedAt = Date.parse(owner.startedAt ?? '');
81
+ return Number.isFinite(claimedAt) && actual <= claimedAt + START_TOLERANCE_MS;
82
+ }
83
+
84
+ function claimDb(ledgerRoot: string): Database {
85
+ mkdirSync(join(ledgerRoot, 'release'), { recursive: true });
86
+ const db = new Database(join(ledgerRoot, 'release', 'run-lock-claim.sqlite'), { create: true });
87
+ db.exec('PRAGMA busy_timeout = 5000');
88
+ db.exec('CREATE TABLE IF NOT EXISTS force_starts (token TEXT PRIMARY KEY, version TEXT NOT NULL, pid INTEGER NOT NULL, pid_started_at TEXT NOT NULL, started_at TEXT NOT NULL)');
89
+ return db;
90
+ }
91
+
92
+ type ForceStart = { token: string; version: string; pid: number; pid_started_at: string; started_at: string };
93
+ function forceStartAlive(row: ForceStart): boolean {
94
+ if (!isPidAlive(row.pid)) return false;
95
+ const actual = psProcessStartMs(row.pid);
96
+ return actual === null || Math.abs(actual - Date.parse(row.pid_started_at)) <= START_TOLERANCE_MS;
97
+ }
98
+
99
+ function liveForceStart(db: Database, version: string): ForceStart | undefined {
100
+ const rows = db.query('SELECT token, version, pid, pid_started_at, started_at FROM force_starts WHERE version = ?').all(version) as ForceStart[];
101
+ for (const row of rows) {
102
+ if (forceStartAlive(row)) return row;
103
+ db.query('DELETE FROM force_starts WHERE token = ?').run(row.token);
104
+ }
105
+ return undefined;
106
+ }
107
+
108
+ export function readActiveForceReleaseRun(version: string, ledgerRoot: string): ActiveReleaseRun | undefined {
109
+ const db = claimDb(ledgerRoot);
110
+ try {
111
+ const row = liveForceStart(db, version);
112
+ return row && { runId: 'pending', pid: row.pid, startedAt: row.started_at };
113
+ } finally { db.close(); }
114
+ }
115
+
116
+ /** Force runs bypass refusal but publish a claim before launching so later normal runs refuse. */
117
+ export async function withForceReleaseRunMarker<T>(version: string, ledgerRoot: string, run: () => Promise<T>): Promise<T> {
118
+ const db = claimDb(ledgerRoot);
119
+ const token = randomUUID();
120
+ try {
121
+ db.exec('BEGIN IMMEDIATE');
122
+ try {
123
+ const pidStartedAt = psProcessStartMs(process.pid) ?? Date.now() - process.uptime() * 1000;
124
+ db.query('INSERT INTO force_starts (token, version, pid, pid_started_at, started_at) VALUES (?, ?, ?, ?, ?)')
125
+ .run(token, version, process.pid, new Date(pidStartedAt).toISOString(), new Date().toISOString());
126
+ db.exec('COMMIT');
127
+ } catch (error) { db.exec('ROLLBACK'); throw error; }
128
+ return await run();
129
+ } finally {
130
+ db.query('DELETE FROM force_starts WHERE token = ?').run(token);
131
+ db.close();
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Atomic claim closes the gap before the graph's first saved state. SQLite serializes
137
+ * stale-lock reclamation among direct entrants; a scheduled entrant only uses O_EXCL,
138
+ * so it either wins after removal or sees the direct entrant's new file.
139
+ */
140
+ export async function withDirectReleaseRunLock<T>(version: string, ledgerRoot: string, run: () => Promise<T>,
141
+ owner: { pid?: number; now?: () => Date } = {}): Promise<T> {
142
+ const dir = join(ledgerRoot, 'release', version);
143
+ mkdirSync(dir, { recursive: true });
144
+ const lock = join(dir, 'run.lock');
145
+ const db = claimDb(ledgerRoot);
146
+ const token = randomUUID();
147
+ let fd: number | undefined;
148
+ try {
149
+ db.exec('BEGIN IMMEDIATE');
150
+ try {
151
+ if (liveForceStart(db, version)) {
152
+ throw new ReleaseRunClaimConflict();
153
+ }
154
+ const existing = readReleaseRunLock(version, ledgerRoot);
155
+ if (existing?.pid && !releaseRunLockOwnerAlive(version, ledgerRoot, existing.pid)) rmSync(lock, { force: true });
156
+ fd = openSync(lock, 'wx', 0o600);
157
+ const pid = owner.pid ?? process.pid;
158
+ const pidStartedAt = psProcessStartMs(pid) ?? Date.now() - process.uptime() * 1000;
159
+ writeFileSync(fd, `${JSON.stringify({ pid, pidStartedAt: new Date(pidStartedAt).toISOString(), startedAt: (owner.now ?? (() => new Date()))().toISOString(), token })}\n`);
160
+ db.exec('COMMIT');
161
+ } catch (error) {
162
+ if (fd !== undefined) { closeSync(fd); rmSync(lock, { force: true }); fd = undefined; }
163
+ db.exec('ROLLBACK');
164
+ if ((error as NodeJS.ErrnoException).code === 'EEXIST') throw new ReleaseRunClaimConflict();
165
+ throw error;
166
+ }
167
+ } finally { db.close(); }
168
+ try { return await run(); }
169
+ finally {
170
+ if (fd !== undefined) closeSync(fd);
171
+ try {
172
+ const current = JSON.parse(readFileSync(lock, 'utf8')) as { token?: unknown };
173
+ if (current.token === token) rmSync(lock);
174
+ } catch (error) {
175
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
176
+ }
177
+ }
178
+ }