elanous 0.2.40 → 0.2.42

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 (685) hide show
  1. package/README.md +7 -7
  2. package/apps/pwa/out/404/index.html +1 -1
  3. package/apps/pwa/out/404.html +1 -1
  4. package/apps/pwa/out/_next/static/{HjeK7zONHfR7op1hKXp4k → X1ucLw10Cds_h614NoEnh}/_buildManifest.js +1 -1
  5. package/apps/pwa/out/_next/static/chunks/0244e5b5.6fc7670f8223d82d.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/{4769.9fd582071301f382.js → 1063.2fb8a9dc55c731f2.js} +1 -1
  7. package/apps/pwa/out/_next/static/chunks/1121.13ba7a8aff19df81.js +36 -0
  8. package/apps/pwa/out/_next/static/chunks/1147-5196d19c222051b3.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/{470.51a72a2348306455.js → 1262.e804feb8fd1acb8c.js} +2 -2
  10. package/apps/pwa/out/_next/static/chunks/1319.b9b815ae0f181e06.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/1345-31427b833438f521.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/{7219-15bdd9a80661af8e.js → 1455-530ca16c7e71baff.js} +1 -1
  13. package/apps/pwa/out/_next/static/chunks/{1281.d81877e35b9068f4.js → 1487.2f67b79444062da6.js} +1 -1
  14. package/apps/pwa/out/_next/static/chunks/1574.878c18ecc97b1bd4.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/1586.fafc33a9a5835cb7.js +32 -0
  16. package/apps/pwa/out/_next/static/chunks/1744.855fd60ae4c4a8c8.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/193.21a47a3e07ef6860.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/1945.7b54990d578c119b.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/2004.e839fda7ea5a738a.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/2044-dd681a5c08779ff8.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/2101.2248decb38829b03.js +286 -0
  22. package/apps/pwa/out/_next/static/chunks/2160.a097e7b138ae1c55.js +23 -0
  23. package/apps/pwa/out/_next/static/chunks/2300.41ea70ac1bef1446.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/2357-07b5eeb5fb12628d.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/2364.28652020695a6db3.js +34 -0
  26. package/apps/pwa/out/_next/static/chunks/{1350.59f40e39e02d91b1.js → 2456.b6de0e1c05f28843.js} +1 -1
  27. package/apps/pwa/out/_next/static/chunks/2526.c3e50ac1fec064bd.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2549-cda50b716ff4250c.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/2550-5f1e88f615568045.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/261-319078c954e40d66.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/2690.0521c738b7749931.js +115 -0
  32. package/apps/pwa/out/_next/static/chunks/{5436.fdf6fa8ae3e9588d.js → 2974.595d1e29d040cb6a.js} +1 -1
  33. package/apps/pwa/out/_next/static/chunks/{5383.bdfa5d886e39a826.js → 3207.a756c95e9ec8c3c8.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/3285.89ee2f298682f5e5.js +93 -0
  35. package/apps/pwa/out/_next/static/chunks/{2177.df8547f9d7215c4e.js → 3347.f846266b5e5f3ede.js} +3 -3
  36. package/apps/pwa/out/_next/static/chunks/3447.06edc7b092021417.js +41 -0
  37. package/apps/pwa/out/_next/static/chunks/{9184.45afbf44fec87574.js → 3482.a9ae9d2ad6a5bd97.js} +1 -1
  38. package/apps/pwa/out/_next/static/chunks/3507.97e1977bc4d5fb0a.js +2 -0
  39. package/apps/pwa/out/_next/static/chunks/3510-9ca392698bfe4391.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/3541.772e5ce425aaa611.js +24 -0
  41. package/apps/pwa/out/_next/static/chunks/{8099.1cccf31c1baa7e1c.js → 3741.9cf76acaa2ee5298.js} +1 -1
  42. package/apps/pwa/out/_next/static/chunks/3837.a210d7d0db8d8abd.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/3898-955c896f394217a6.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/3947-ef57acea76eaa8d2.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/{4864a2ca.1c1c280a7772c5e5.js → 3d0154af.5331fec737ffe8c3.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/4037-3b82ebc7d78afdc7.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/4043.6b91203436c9c86b.js +309 -0
  48. package/apps/pwa/out/_next/static/chunks/405.9022d4a478202b67.js +77 -0
  49. package/apps/pwa/out/_next/static/chunks/417-fb2cc9a9910e5968.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/{3330.7443183b7e6fdbdf.js → 4276.336d76d858fcdf1b.js} +1 -1
  51. package/apps/pwa/out/_next/static/chunks/4302.6ced9aa4535de947.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/4320.583cbab64db6be02.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/4768-8eac183fd291ca53.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/4770.769c9cd9c7034eba.js +148 -0
  55. package/apps/pwa/out/_next/static/chunks/4895.65605ec3e6504e89.js +74 -0
  56. package/apps/pwa/out/_next/static/chunks/{3b355b3d.6f73c98bf6f9c749.js → 50061469.0326382241ef3b2d.js} +1 -1
  57. package/apps/pwa/out/_next/static/chunks/5112.4b9f300124fab7b3.js +149 -0
  58. package/apps/pwa/out/_next/static/chunks/5335.375ee922bcd5bc83.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/5342.023a2e729689b320.js +27 -0
  60. package/apps/pwa/out/_next/static/chunks/{8d173e38.18a2f7b4e1848bdb.js → 53c73f84.373b6160adfa89a4.js} +1 -1
  61. package/apps/pwa/out/_next/static/chunks/{1537-6bb97b5214928ea9.js → 5431-72a1dbde59400691.js} +1 -1
  62. package/apps/pwa/out/_next/static/chunks/5440-4236de26c5ea5856.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/5486-b318c165f645d8ba.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/5506-c7d2ec25c62a7c94.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/5570-c66ecbea26b4e3b1.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/5696.ffca5aa259d8451e.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/5793-8c584aea2aa87560.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/5848-6de6b8c173351a90.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/5885.0a3e52c8faccec98.js +287 -0
  70. package/apps/pwa/out/_next/static/chunks/5999.94e3c83040200f31.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/5a7901d6-2d8d9b5587cf9042.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/{261.0d471e8aded799f3.js → 6131.348b7d6bdcd267ce.js} +3 -3
  73. package/apps/pwa/out/_next/static/chunks/6164.86133a0172611184.js +90 -0
  74. package/apps/pwa/out/_next/static/chunks/{9175.2de5209a5ce6f68a.js → 6377.bc7df239edbff244.js} +1 -1
  75. package/apps/pwa/out/_next/static/chunks/6515.e07e8b2b8a707a87.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/6522-c005514f87d18982.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/{2226.2cf61ec781827219.js → 6632.0eac1c554f8f2ef3.js} +2 -2
  78. package/apps/pwa/out/_next/static/chunks/6642.648adb1767f39ecf.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/6848.138805a09d001d09.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/7049-c09e2b7a253a24f3.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/7072.1e00447d9fa39972.js +104 -0
  82. package/apps/pwa/out/_next/static/chunks/7106.64201c539c5e220c.js +94 -0
  83. package/apps/pwa/out/_next/static/chunks/7151-1f742b455986530e.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/7199.18d6b25e001154ce.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/7279-c199ea83f34338fa.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/728b938d.ffaa7ad583d60e58.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/7309.171c3de54e0322f1.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/733.7a1ab3342e03306f.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/{b84cad00.594027ebe0531fe2.js → 73d2eb0e.4966ad55293c801a.js} +3 -3
  90. package/apps/pwa/out/_next/static/chunks/{6257.c0037b8949239f5e.js → 7411.9b4b8b747dc7b802.js} +2 -2
  91. package/apps/pwa/out/_next/static/chunks/7504.d5cf1e7cd89b1b08.js +24 -0
  92. package/apps/pwa/out/_next/static/chunks/7573-0fd4af62d65d4598.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/759.6f8267df4689c401.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/7825-668b046f72f14483.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/7852-1538768b6eb19388.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/7868.cff810430bb63c91.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/{9189505a.980427ac048c8c60.js → 7a98bc80.fa935989e829f9c2.js} +1 -1
  98. package/apps/pwa/out/_next/static/chunks/8084-dfcc796874ab4e07.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/8138.6c10b2a356d52fca.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/{2566.beaffc3a5e1fcd2e.js → 8262.ddc03e06b733f184.js} +1 -1
  101. package/apps/pwa/out/_next/static/chunks/8266.6bf9b131b2584678.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/8403.7b81d5e64074a725.js +8 -0
  103. package/apps/pwa/out/_next/static/chunks/8470.1998cbd0866a7eac.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8505.fd803c6c33b54cc3.js +79 -0
  105. package/apps/pwa/out/_next/static/chunks/{8272-025653884f9645d2.js → 8546-6f03f8ea34dd61d9.js} +1 -1
  106. package/apps/pwa/out/_next/static/chunks/{6051-0ae955eedb10adb9.js → 8673-bab689a55f7e13b9.js} +3 -3
  107. package/apps/pwa/out/_next/static/chunks/8687-246fda8433d3477c.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/898-280022fb082936bc.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/9098-50eefc1844212e3d.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/9211-330b6736a63d4cb3.js +1 -0
  111. package/apps/pwa/out/_next/static/chunks/{4351.0f37a6db2b34d50b.js → 9249.976f1e69a9d80754.js} +1 -1
  112. package/apps/pwa/out/_next/static/chunks/9255-cf88fceda5666645.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/9306-e523ba58bb8adc42.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/9324.a9866d717ba62cb6.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/9325.a8510e8aa87c8143.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/9353-12bfff0c130104cf.js +1 -0
  117. package/apps/pwa/out/_next/static/chunks/9415.69e4a21c60c7d2db.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/{6749.0744ce6eb08d40e9.js → 9427.96aa912ddb5801e0.js} +16 -16
  119. package/apps/pwa/out/_next/static/chunks/9479-154a697d4b6e4e98.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/9540-65aa6ccc31b3fd78.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/{7611.85f045bf232053ac.js → 9633.ce5bddc56784da72.js} +1 -1
  122. package/apps/pwa/out/_next/static/chunks/{3746-f80a78f220bf37b5.js → 9656-374232558fd528d0.js} +1 -1
  123. package/apps/pwa/out/_next/static/chunks/9832.f4a98c878ecf2c0a.js +62 -0
  124. package/apps/pwa/out/_next/static/chunks/9865.f094440645f02d98.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/9a852a62-6192a6ea9346ced3.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-e772b4710df5d936.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/approvals/page-4361368e6a76b967.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-802f349fdd65248d.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/board/page-7f35513988a2a495.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/botlab/page-798121656540e33e.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/bots/page-20a089490affe98c.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/ceo/page-e5e4ec786d70e604.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/chat/page-d12fe5d0801430e4.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/consult/page-d8cf21022fecc026.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/control/page-21ce565c9c57ee97.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/decisions/page-b56563025f619c9b.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/design-check/page-52f14dd133da8348.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/editor/page-c0ed7fdcdba76633.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/exec/page-af8c3020cb520f15.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/field/page-72eda67af9f5e35c.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/healthz/route-e772b4710df5d936.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/inside/page-67a6620d31f0a46f.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/intake/page-f059363aa0880ec2.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/layout-f9f917d6a57f69fb.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/live/page-d3ae55eee2f0826c.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/live-run/{page-83d27757e2b8060a.js → page-0e3d7b9421b79dac.js} +1 -1
  147. package/apps/pwa/out/_next/static/chunks/app/loops/page-ba738f7e233eb211.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/market/page-33438b98c74fd599.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-0b34d5fd990d793a.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/morning/page-0f291d3c8fd55a3a.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/not-found-3fced192be5279c1.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/observatory/page-04cb3cf182a7c1ea.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-6ccbd2ab23a21542.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-aa0518c48a5f4e56.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4b32226d341ad7d7.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3224fd8f6383b61f.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/page-efbc97459f2914e2.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/reflection/page-2fe487d88cd5d97c.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-48861dd1a858bab7.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/sessions/page-197d5d4d21d228ea.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a763ba89fbdd9c27.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/settings/page-7edb253510b1c136.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0c875702c61965ea.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/setup/layout-e772b4710df5d936.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/setup/page-e425c951151095f7.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/share/page-9c90e0aec4e27f77.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/showroom/page-733e09a02e727d42.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/tasks/page-4f899e14c34d77a1.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/term/{page-139d722de1dcd6c1.js → page-bf067887b3307423.js} +1 -1
  170. package/apps/pwa/out/_next/static/chunks/app/today/page-7af10868ff87d04e.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/trace/page-efe5304057aa11c7.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/vault/page-76bcd29aaa8a26c3.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-4cfd88e2273ce50f.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/app/workflows/page-0a1ddb47efba0a32.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2a176c707152c892.js +1 -0
  176. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-efe03031b8797fbf.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/{409c787b.e48c5bec8ec10456.js → b450776b.3db34d8d965f82d3.js} +1 -1
  178. package/apps/pwa/out/_next/static/chunks/{ca3702f8.2913182e73889fa2.js → e8543907.5b040a5f22a0b0e5.js} +7 -7
  179. package/apps/pwa/out/_next/static/chunks/framework-d0d2d673bfd8a8a9.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-4c9fceb2baa09cb7.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/main-app-b0ab8d8fc9a97556.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_app-7e2cc62a2e5cec96.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/pages/_error-22324935f753ec5e.js +1 -0
  184. package/apps/pwa/out/_next/static/chunks/webpack-9dc1bfa25334998a.js +1 -0
  185. package/apps/pwa/out/_next/static/css/{b9d8151e48998417.css → 62c858ad53854f47.css} +1 -1
  186. package/apps/pwa/out/approvals/index.html +1 -1
  187. package/apps/pwa/out/approvals/index.txt +22 -22
  188. package/apps/pwa/out/autopilot/index.html +1 -1
  189. package/apps/pwa/out/autopilot/index.txt +22 -22
  190. package/apps/pwa/out/board/index.html +1 -1
  191. package/apps/pwa/out/board/index.txt +22 -22
  192. package/apps/pwa/out/botlab/index.html +1 -1
  193. package/apps/pwa/out/botlab/index.txt +21 -21
  194. package/apps/pwa/out/bots/index.html +1 -1
  195. package/apps/pwa/out/bots/index.txt +21 -21
  196. package/apps/pwa/out/ceo/index.html +1 -1
  197. package/apps/pwa/out/ceo/index.txt +22 -22
  198. package/apps/pwa/out/chat/index.html +1 -1
  199. package/apps/pwa/out/chat/index.txt +21 -21
  200. package/apps/pwa/out/consult/index.html +1 -1
  201. package/apps/pwa/out/consult/index.txt +22 -22
  202. package/apps/pwa/out/control/index.html +1 -1
  203. package/apps/pwa/out/control/index.txt +22 -22
  204. package/apps/pwa/out/decisions/index.html +1 -1
  205. package/apps/pwa/out/decisions/index.txt +21 -21
  206. package/apps/pwa/out/design-check/index.html +1 -1
  207. package/apps/pwa/out/design-check/index.txt +22 -22
  208. package/apps/pwa/out/editor/index.html +1 -1
  209. package/apps/pwa/out/editor/index.txt +23 -24
  210. package/apps/pwa/out/exec/index.html +1 -1
  211. package/apps/pwa/out/exec/index.txt +22 -22
  212. package/apps/pwa/out/field/index.html +1 -1
  213. package/apps/pwa/out/field/index.txt +22 -22
  214. package/apps/pwa/out/healthz +1 -1
  215. package/apps/pwa/out/index.html +1 -1
  216. package/apps/pwa/out/index.txt +21 -21
  217. package/apps/pwa/out/inside/index.html +1 -1
  218. package/apps/pwa/out/inside/index.txt +22 -22
  219. package/apps/pwa/out/intake/index.html +1 -1
  220. package/apps/pwa/out/intake/index.txt +22 -22
  221. package/apps/pwa/out/live/index.html +1 -1
  222. package/apps/pwa/out/live/index.txt +23 -24
  223. package/apps/pwa/out/live-run/index.html +1 -1
  224. package/apps/pwa/out/live-run/index.txt +23 -24
  225. package/apps/pwa/out/loops/index.html +1 -1
  226. package/apps/pwa/out/loops/index.txt +21 -21
  227. package/apps/pwa/out/market/index.html +1 -1
  228. package/apps/pwa/out/market/index.txt +21 -21
  229. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  230. package/apps/pwa/out/missions/placeholder/index.txt +21 -21
  231. package/apps/pwa/out/morning/index.html +1 -1
  232. package/apps/pwa/out/morning/index.txt +22 -22
  233. package/apps/pwa/out/observatory/index.html +1 -1
  234. package/apps/pwa/out/observatory/index.txt +22 -22
  235. package/apps/pwa/out/ops/checklist/index.html +1 -1
  236. package/apps/pwa/out/ops/checklist/index.txt +21 -21
  237. package/apps/pwa/out/ops/release/index.html +1 -1
  238. package/apps/pwa/out/ops/release/index.txt +21 -21
  239. package/apps/pwa/out/ops/seats/index.html +1 -1
  240. package/apps/pwa/out/ops/seats/index.txt +21 -21
  241. package/apps/pwa/out/outputs/index.html +1 -1
  242. package/apps/pwa/out/outputs/index.txt +21 -21
  243. package/apps/pwa/out/reflection/index.html +1 -1
  244. package/apps/pwa/out/reflection/index.txt +22 -22
  245. package/apps/pwa/out/scheduler/index.html +1 -1
  246. package/apps/pwa/out/scheduler/index.txt +22 -22
  247. package/apps/pwa/out/sessions/index.html +1 -1
  248. package/apps/pwa/out/sessions/index.txt +21 -21
  249. package/apps/pwa/out/settings/devices/index.html +1 -1
  250. package/apps/pwa/out/settings/devices/index.txt +22 -22
  251. package/apps/pwa/out/settings/index.html +1 -1
  252. package/apps/pwa/out/settings/index.txt +22 -22
  253. package/apps/pwa/out/setup/done/index.html +1 -1
  254. package/apps/pwa/out/setup/done/index.txt +22 -22
  255. package/apps/pwa/out/setup/index.html +1 -1
  256. package/apps/pwa/out/setup/index.txt +22 -22
  257. package/apps/pwa/out/share/index.html +1 -1
  258. package/apps/pwa/out/share/index.txt +22 -22
  259. package/apps/pwa/out/showroom/index.html +1 -1
  260. package/apps/pwa/out/showroom/index.txt +21 -21
  261. package/apps/pwa/out/tasks/index.html +1 -1
  262. package/apps/pwa/out/tasks/index.txt +22 -22
  263. package/apps/pwa/out/term/index.html +1 -1
  264. package/apps/pwa/out/term/index.txt +22 -22
  265. package/apps/pwa/out/today/index.html +1 -1
  266. package/apps/pwa/out/today/index.txt +22 -22
  267. package/apps/pwa/out/trace/index.html +1 -1
  268. package/apps/pwa/out/trace/index.txt +22 -22
  269. package/apps/pwa/out/vault/index.html +1 -1
  270. package/apps/pwa/out/vault/index.txt +22 -22
  271. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  272. package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
  273. package/apps/pwa/out/workflows/index.html +1 -1
  274. package/apps/pwa/out/workflows/index.txt +22 -22
  275. package/apps/pwa/out/workspace/index.html +1 -1
  276. package/apps/pwa/out/workspace/index.txt +22 -22
  277. package/apps/pwa/out/worktrees/index.html +1 -1
  278. package/apps/pwa/out/worktrees/index.txt +22 -22
  279. package/bin/elanous.mjs +6 -1
  280. package/catalog/models/openai/gpt-6.1-sol.yaml +16 -0
  281. package/catalog/models/openai/gpt-6.1-terra.yaml +16 -0
  282. package/docs/goal-context/04-mock-scenarios.md +13 -0
  283. package/graphs/catalog/catalog.yaml +1 -1
  284. package/graphs/demo/persona-teacher/lesson-pack.yaml +16 -0
  285. package/graphs/demo/persona-teacher/recipes.yaml +17 -0
  286. package/graphs/demo/persona-teacher/weekly-lesson.yaml +36 -0
  287. package/graphs/heal/heal-loop.yaml +1 -0
  288. package/graphs/overlays/stop-heal-ladder.yaml +136 -2
  289. package/graphs/release/release-loop.yaml +3 -1
  290. package/graphs/units/gate/gate.yaml +20 -0
  291. package/graphs/units/git/git.yaml +26 -0
  292. package/graphs/units/implement/implement.yaml +19 -0
  293. package/graphs/units/landing/landing.yaml +18 -0
  294. package/graphs/units/review/review.yaml +20 -0
  295. package/graphs/units/verbs/recipes.yaml +13 -10
  296. package/graphs/units/verbs/research.yaml +20 -0
  297. package/graphs/work/research-report.yaml +21 -3
  298. package/package.json +1 -1
  299. package/scripts/disk-steward.ts +46 -0
  300. package/scripts/fleet/node-setup.sh +50 -0
  301. package/scripts/graph-units/verbs/research.ts +55 -0
  302. package/scripts/heal/run-role.ts +99 -5
  303. package/scripts/land-global-checks.ts +6 -1
  304. package/scripts/lib/decompose-ab.ts +1 -0
  305. package/scripts/lib/fixtures/tui-regress/help-insufficient-commands.txt +15 -0
  306. package/scripts/lib/fixtures/tui-regress/help-live.txt +42 -0
  307. package/scripts/lib/fixtures/tui-regress/help-missing-keys.txt +21 -0
  308. package/scripts/lib/fixtures/tui-regress/help-original.txt +51 -0
  309. package/scripts/lib/fixtures/tui-regress/help-shifted-input.txt +26 -0
  310. package/scripts/lib/tui-regress-checks.ts +33 -13
  311. package/scripts/llm-cost-ledger.ts +179 -8
  312. package/scripts/measure-nl-routing-corpus.ts +10 -2
  313. package/scripts/persona-teacher/weekly-lesson-node.ts +172 -0
  314. package/scripts/public-copy-lint.ts +163 -19
  315. package/scripts/ratchets/train-side-invariants.sh +0 -1
  316. package/scripts/release-loop/gate-autoheal.ts +117 -0
  317. package/scripts/release-loop/gate-baseline-receipt.ts +55 -10
  318. package/scripts/release-loop/gate-diff.ts +22 -3
  319. package/scripts/release-loop/gate-image-pin.ts +81 -0
  320. package/scripts/release-loop/gate-node-sim.ts +207 -0
  321. package/scripts/release-loop/gate-node.ts +489 -62
  322. package/scripts/release-loop/gate-pod-image-sticky-plan.ts +99 -0
  323. package/scripts/release-loop/gate-radius-live.ts +97 -13
  324. package/scripts/release-loop/gate-shard-dispatch.ts +43 -0
  325. package/scripts/release-loop/gate-shard-load-flaky.ts +256 -0
  326. package/scripts/release-loop/gate-train-receipts.ts +122 -0
  327. package/scripts/release-loop/gate-triage-node.ts +70 -10
  328. package/scripts/release-loop/resume-release.ts +330 -23
  329. package/scripts/release-loop/traffic-node.ts +6 -3
  330. package/scripts/release-loop/unattended-release.ts +4 -2
  331. package/scripts/shadow-authoring-probe.ts +20 -0
  332. package/scripts/ux-sim.ts +37 -3
  333. package/scripts/warm-pool-provider-config.ts +263 -0
  334. package/skills/omni-crawl/SKILL.md +5 -2
  335. package/skills/omni-crawl/scripts/main.ts +84 -23
  336. package/src/acp/core-turn-bridge.ts +99 -4
  337. package/src/agent/registry.ts +2 -0
  338. package/src/agent/task-notification.ts +25 -5
  339. package/src/agent/types.ts +6 -0
  340. package/src/agent-mission/mission-cli.ts +24 -0
  341. package/src/agent-mission/mission-out.ts +595 -0
  342. package/src/agent-mission/tool-install.ts +88 -2
  343. package/src/agent-substrate/context-strategy.ts +4 -4
  344. package/src/agent-substrate/self-review-cli.ts +267 -40
  345. package/src/auto-research/research-turn.ts +133 -51
  346. package/src/autopilot/discovery/wiki-claim-proposer.ts +22 -1
  347. package/src/autopilot/pr-manager.ts +38 -7
  348. package/src/autopilot/pty-control-loop.ts +2 -0
  349. package/src/boot/daemon-tools/self-implement.ts +46 -9
  350. package/src/budget/shadow-arms.ts +52 -0
  351. package/src/capture/tui-self-report.ts +22 -6
  352. package/src/chat/index.ts +1 -1
  353. package/src/chat/pickers/state.ts +7 -1
  354. package/src/cli/fixtures/review-loop-breaker/opwaiver-32646-1694ccc7a170.json +1 -0
  355. package/src/cli/fixtures/review-loop-breaker/opwaiver-32853-bd8f73595c83.json +1 -0
  356. package/src/cli/fixtures/review-loop-breaker/opwaiver-33097-1cc85900413c.json +1 -0
  357. package/src/cli/fixtures/review-loop-breaker/opwaiver-33106-972f31e4a3ca.json +1 -0
  358. package/src/cli/fixtures/review-loop-breaker/opwaiver-33113-3ab51f05968f.json +1 -0
  359. package/src/cli/fixtures/review-loop-breaker/pr-33097-1cc85900413c.json +1 -0
  360. package/src/cli/log-event-names.ts +5 -0
  361. package/src/cli/mcp-login.ts +8 -18
  362. package/src/cli/msg-cli.ts +57 -1
  363. package/src/cli/pr-land-heavy-remote.ts +47 -9
  364. package/src/cli/pr-land-overlap-evidence.ts +100 -1
  365. package/src/cli/pr-land-pod-shadow-job.ts +60 -0
  366. package/src/cli/release-checklist-read-cli.ts +20 -10
  367. package/src/cli/release-cli.ts +27 -1
  368. package/src/cli/research-cli.ts +4 -5
  369. package/src/cli/sim-cli.ts +7 -1
  370. package/src/cli/tasks-cli.ts +12 -4
  371. package/src/codex/models.ts +30 -15
  372. package/src/compact/auto.ts +8 -1
  373. package/src/control-plane/ledger.ts +52 -2
  374. package/src/control-plane/server.ts +16 -2
  375. package/src/core-turn/run-core-turn.ts +313 -89
  376. package/src/core-turn/run-goal-loop.ts +106 -2
  377. package/src/core-turn/types.ts +2 -0
  378. package/src/dashboard/acp-boot.ts +72 -3
  379. package/src/dashboard/classic-sidetab-mission-job.ts +58 -0
  380. package/src/dashboard/classic-sidetab-mission.ts +185 -0
  381. package/src/dashboard/index.ts +155 -15
  382. package/src/dashboard/input/agent-wake.ts +23 -1
  383. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +9 -0
  384. package/src/dashboard/mission-cli-consumer.ts +153 -0
  385. package/src/dashboard/seat-boot-preamble.ts +171 -1
  386. package/src/dashboard/side-basic-runtime.ts +54 -1
  387. package/src/dashboard/sidetab-runtime.ts +90 -1
  388. package/src/dashboard/skill-catalog-preamble.ts +101 -0
  389. package/src/dashboard/skill-detection-runtime.ts +55 -15
  390. package/src/dashboard/slash-runtime/dashboard-handlers.ts +15 -0
  391. package/src/dashboard/turn-preamble.ts +65 -16
  392. package/src/dashboard/turn-prompt-runtime.ts +4 -2
  393. package/src/disk-steward/judge.ts +73 -0
  394. package/src/disk-steward/ledger.ts +122 -0
  395. package/src/disk-steward/steward.ts +373 -0
  396. package/src/domains/guardian-tick.ts +191 -2
  397. package/src/graph-runner/graph-cli.ts +44 -7
  398. package/src/graph-runner/graph-tick.ts +10 -2
  399. package/src/graph-runner/in-process-registry.ts +37 -0
  400. package/src/graph-runner/runner.ts +34 -4
  401. package/src/grok/agent-search.ts +14 -4
  402. package/src/grok/credential.ts +28 -11
  403. package/src/grounding/ref-index.ts +19 -0
  404. package/src/grounding/sources-cli.ts +9 -1
  405. package/src/harness/harness-cli-command.ts +267 -14
  406. package/src/harness/harness-incidents.ts +312 -7
  407. package/src/harness/harness-pod-dispatch.ts +81 -15
  408. package/src/harness/harness-space.ts +15 -1
  409. package/src/harness/harness-stop.ts +155 -11
  410. package/src/harness/progress-worker-entry.ts +59 -0
  411. package/src/harness/progress-worker-runtime.ts +11 -0
  412. package/src/index.ts +52 -7
  413. package/src/instance/dev-project.ts +25 -0
  414. package/src/instance/spawn-elanous.ts +6 -4
  415. package/src/intelligence-map/model-alias.ts +6 -5
  416. package/src/intelligence-map/model-catalog.ts +59 -2
  417. package/src/llm/model-defaults.ts +4 -4
  418. package/src/llm.ts +208 -19
  419. package/src/loops/orchestrator/coordinator-shadow.ts +226 -0
  420. package/src/loops/orchestrator/tick.ts +61 -0
  421. package/src/maturity/feature-maturity.ts +1 -1
  422. package/src/mcp/mcp-oauth.ts +3 -1
  423. package/src/model-tier/llm-tier-map.ts +9 -9
  424. package/src/model-tier/turn-tier.ts +1 -1
  425. package/src/msg/msg-mirror-github.ts +80 -0
  426. package/src/msg/msg-mirror-policy.ts +61 -0
  427. package/src/msg/msg-mirror-runner.ts +219 -0
  428. package/src/msg/msg-mirror-state.ts +33 -0
  429. package/src/msg/msg-store.ts +11 -0
  430. package/src/native-tool-catalog.ts +2 -2
  431. package/src/nexus/api/harness-traversal.ts +11 -0
  432. package/src/output-truncation.ts +41 -12
  433. package/src/pr-tool/census.ts +3 -2
  434. package/src/project/project-store.ts +48 -1
  435. package/src/pty-shell/tui-run-state.ts +188 -2
  436. package/src/release-loop/checklist-target-path-hint.ts +13 -0
  437. package/src/release-loop/checklist.ts +9 -1
  438. package/src/release-loop/gate-host-reservation.ts +144 -0
  439. package/src/release-loop/release-schedule.ts +90 -1
  440. package/src/repl/index.ts +43 -2
  441. package/src/research/axis-fanout.ts +121 -16
  442. package/src/research/bloom-exemplars.ts +137 -0
  443. package/src/research/gap-followup.ts +13 -1
  444. package/src/research/plugin-discovery.ts +36 -14
  445. package/src/research-bridge/invoke.ts +22 -5
  446. package/src/research-bridge/types.ts +2 -0
  447. package/src/self-dev/ask-launch-flow.ts +39 -25
  448. package/src/self-dev/decompose.ts +119 -5
  449. package/src/self-dev/dev-cli.ts +17 -17
  450. package/src/self-dev/graph-front-nodes.ts +23 -0
  451. package/src/self-dev/launch-preflight-text.ts +5 -6
  452. package/src/self-dev/launch-preflight.ts +9 -4
  453. package/src/self-dev/orchestrate.ts +27 -2
  454. package/src/self-dev/review-provider-fallback.ts +9 -2
  455. package/src/self-dev/run-supervisor.ts +226 -6
  456. package/src/self-dev/run-types.ts +7 -0
  457. package/src/self-implement/doctor-visit.ts +88 -0
  458. package/src/self-implement/gate-artifacts.ts +13 -0
  459. package/src/self-implement/gate-cli.ts +54 -24
  460. package/src/self-implement/gate-remote.ts +23 -5
  461. package/src/self-implement/goal-author-clarification.ts +31 -12
  462. package/src/self-implement/goal-author.ts +403 -8
  463. package/src/self-implement/graph-overlay-yaml.ts +7 -0
  464. package/src/self-implement/graph-stage-units.ts +172 -0
  465. package/src/self-implement/graph-templates.ts +162 -1
  466. package/src/self-implement/graph-variant.ts +37 -4
  467. package/src/self-implement/headless-elanous-driver.ts +31 -2
  468. package/src/self-implement/heal-intake.ts +112 -4
  469. package/src/self-implement/heal-triage.ts +20 -0
  470. package/src/self-implement/host-regate.ts +31 -8
  471. package/src/self-implement/knowhow-pack.ts +38 -0
  472. package/src/self-implement/merge-train.ts +5 -0
  473. package/src/self-implement/orchestrator.ts +359 -30
  474. package/src/self-implement/regate-detail.ts +38 -1
  475. package/src/self-implement/rework-child-provider.ts +25 -0
  476. package/src/self-implement/run-chart.ts +119 -0
  477. package/src/self-implement/run-ledger.ts +176 -17
  478. package/src/self-implement/run-stop.ts +208 -5
  479. package/src/self-implement/running-runs.ts +35 -12
  480. package/src/self-implement/self-implement-cli.ts +36 -4
  481. package/src/self-implement/stop-autoheal-operational.ts +43 -0
  482. package/src/self-implement/stop-autoheal.ts +28 -6
  483. package/src/session/chat.ts +69 -58
  484. package/src/session/request-size-guard.ts +123 -0
  485. package/src/session-runtime/index.ts +98 -4
  486. package/src/session-runtime/tier-flip.ts +3 -3
  487. package/src/sidetab-core/model.ts +150 -0
  488. package/src/sidetab-core/visible-agent-mission.ts +186 -0
  489. package/src/skills/name-match.ts +20 -0
  490. package/src/skills/tools/agent.ts +30 -2
  491. package/src/skills/tools/omni-search.ts +67 -5
  492. package/src/task-agent/parent-host.ts +14 -1
  493. package/src/task-agent/pod-distributor-entry.ts +3 -0
  494. package/src/task-agent/task-hand.ts +31 -1
  495. package/src/task-orchestrator/surfaces/pod-distributor.ts +48 -0
  496. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +43 -0
  497. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +38 -1
  498. package/src/task-orchestrator/surfaces/pod-ledger-events.ts +29 -4
  499. package/src/task-orchestrator/surfaces/pod-si-warm-entry.ts +33 -6
  500. package/src/task-orchestrator/surfaces/pod-source-bundle.ts +45 -9
  501. package/src/task-orchestrator/surfaces/pod-source-receive.ts +163 -13
  502. package/src/task-orchestrator/surfaces/pod-source-spec.ts +3 -3
  503. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +209 -39
  504. package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +80 -3
  505. package/src/task-orchestrator/surfaces/self-implement-pod.ts +280 -60
  506. package/src/task-orchestrator/surfaces/self-implement.ts +18 -0
  507. package/src/tokens.ts +35 -2
  508. package/src/tool-runtime/agent-runtime.ts +1 -0
  509. package/src/tool-runtime/elanous-skills-list-runtime.ts +5 -1
  510. package/src/tool-runtime/skill-exec-runtime.ts +33 -13
  511. package/src/tool-runtime/types.ts +1 -0
  512. package/src/tui/entry.ts +24 -7
  513. package/src/tui/sidetab/attach.ts +47 -0
  514. package/src/tui/sidetab/list.ts +45 -0
  515. package/src/tui/sidetab/nl-tool.ts +2 -136
  516. package/src/tui/sidetab/peek.ts +52 -0
  517. package/src/tui-client/dashboard-session.ts +3 -0
  518. package/src/tui-client/local-chat-turn.ts +26 -13
  519. package/src/tui.ts +22 -0
  520. package/src/user-config.ts +11 -5
  521. package/src/ux-sim/fake-kubectl.ts +37 -3
  522. package/src/ux-sim/fault-needs-human-reason-repair.ts +107 -0
  523. package/src/ux-sim/fault-pod-child-result.ts +42 -0
  524. package/src/ux-sim/fault-pool-cap.ts +11 -6
  525. package/src/ux-sim/mock-scenario-catalog.json +41 -0
  526. package/src/ux-sim/mock-scenario-catalog.ts +156 -0
  527. package/src/ux-sim/release-sim-rig.ts +262 -0
  528. package/src/ux-sim/scenario.ts +116 -23
  529. package/src/ux-sim/seminar-rehearsal.ts +109 -0
  530. package/src/ux-sim/subagent-tools-device.ts +2 -2
  531. package/src/version/packed-revision.json +1 -1
  532. package/src/voice/voice-tts-singleton.ts +18 -3
  533. package/src/web-search/grok.ts +5 -9
  534. package/apps/pwa/out/_next/static/chunks/1060.014f1f01a17aec26.js +0 -115
  535. package/apps/pwa/out/_next/static/chunks/1415.79821ec7ae99acdb.js +0 -1
  536. package/apps/pwa/out/_next/static/chunks/1452.162bb2d9d699e82a.js +0 -1
  537. package/apps/pwa/out/_next/static/chunks/147.f92f63bbc5e85fa5.js +0 -286
  538. package/apps/pwa/out/_next/static/chunks/1661.fb425a1d8a065b72.js +0 -149
  539. package/apps/pwa/out/_next/static/chunks/1671-1c22b40b50b208f2.js +0 -1
  540. package/apps/pwa/out/_next/static/chunks/1758.0204d3f48783c49d.js +0 -90
  541. package/apps/pwa/out/_next/static/chunks/1806.027bf78b6a94d340.js +0 -104
  542. package/apps/pwa/out/_next/static/chunks/2047.8f23e210995d4548.js +0 -2
  543. package/apps/pwa/out/_next/static/chunks/2061-7f2bbc53ae96c88b.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/2066.a77257c8c350770a.js +0 -23
  545. package/apps/pwa/out/_next/static/chunks/2096-a938496bc996dcbd.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/2200-9a453b8f99687d49.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/2281-b8cede695c6c650e.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/2499.02fbfd18126c10c0.js +0 -1
  549. package/apps/pwa/out/_next/static/chunks/2522.f2766b76d22c8bdf.js +0 -34
  550. package/apps/pwa/out/_next/static/chunks/2615.3c9738153de3ef3f.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/2727.e703d99ac414916d.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/2752.7904b8df0254d0ae.js +0 -27
  553. package/apps/pwa/out/_next/static/chunks/2762-5051a862dfa40e99.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/2767-31784bfedf8e245d.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/2774-719820e3f5b2a3eb.js +0 -1
  556. package/apps/pwa/out/_next/static/chunks/2812-f016aa0dda149287.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/2829-734fe8bf2eee7347.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/311-32dcac79970985e8.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/3175-f93f7495cd28253c.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/3184.8da993f5502d2140.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/3231-d7c981db0ff8bdcc.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/3250.151309c5176d6b4b.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/3517.a5aa1797a9dbf2ee.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/3567-45f676877ed9e8a5.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/3819.8a1de79b2e8c7298.js +0 -24
  566. package/apps/pwa/out/_next/static/chunks/39.9b37b0be4c128a83.js +0 -77
  567. package/apps/pwa/out/_next/static/chunks/4023.a19045970a9ce16a.js +0 -36
  568. package/apps/pwa/out/_next/static/chunks/413-24c8284bda08241d.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/4135.e45d6ebc29e20647.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/4283.65502f5d0e6ff6c0.js +0 -287
  571. package/apps/pwa/out/_next/static/chunks/4305-4e28fa92a7b34a5b.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/4335-50efa61fd7613142.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/4720.ecbd1693fa151068.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/483.725e4a61be922788.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/4969-4b209d7307410c00.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/5068-c7f53862f83c0917.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/5315.bc96711c24dfefe2.js +0 -74
  578. package/apps/pwa/out/_next/static/chunks/5542.efbf14f16baed4dd.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/5622.94893f5aea7185d0.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/5644.8f401a25601d05fc.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/5658.35f70eb9871a54bf.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/5721.7bdc0e904c76ab51.js +0 -8
  583. package/apps/pwa/out/_next/static/chunks/5790-eef0ca20884a6bdf.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/5802.ef0d8598ca68fe84.js +0 -62
  585. package/apps/pwa/out/_next/static/chunks/5890.fb974729788dcb71.js +0 -1
  586. package/apps/pwa/out/_next/static/chunks/5924.5ef25cddd36d5dbf.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/5979-e59c099ebf8d1f35.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/6020.714cfeada2315e2e.js +0 -94
  589. package/apps/pwa/out/_next/static/chunks/6027-dfe0349d8c50f698.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/6178.bc4730c86cf74536.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/6273.d808b0dcb3c2b155.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/6292.6ef1df64fa6a4c8a.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/647-af8dd920ba46533c.js +0 -1
  594. package/apps/pwa/out/_next/static/chunks/6541-170dc5c59e135c0c.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/6624.981ef7caeb680560.js +0 -32
  596. package/apps/pwa/out/_next/static/chunks/6742-a1ad4a94ba7bc37a.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/6836-493dc55c39187949.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/6856-53ac421c06039a18.js +0 -1
  599. package/apps/pwa/out/_next/static/chunks/6908.06008c67f6380ea9.js +0 -148
  600. package/apps/pwa/out/_next/static/chunks/6915.096931864206f522.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/6925.5e181fb7dcf90193.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/6cc1d1d6.9a98dd4622f1afc6.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/7203-800e11037adc41eb.js +0 -1
  604. package/apps/pwa/out/_next/static/chunks/7279.6170f591105936bb.js +0 -93
  605. package/apps/pwa/out/_next/static/chunks/7349-373165c7fbdcc3df.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/7514-a533ba0ca1cae4f4.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/7515.1ad6efe157fa4448.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/7521.d919cc1fab80c1d9.js +0 -24
  609. package/apps/pwa/out/_next/static/chunks/7538.90e97bbdf9178cff.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/7825-39203196a12579b2.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/7869.5e1a9a12fd70d6c7.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/8086.1ab9103c41be884f.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/8179-e61fffbdb70f1152.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/8265.bdeb991b34db6a06.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/8357.1aaa713b052a78b1.js +0 -309
  616. package/apps/pwa/out/_next/static/chunks/8638-e42615f2166165b5.js +0 -1
  617. package/apps/pwa/out/_next/static/chunks/8639-92c11d069792429e.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/8691.54e22831d5e8ccf8.js +0 -1
  619. package/apps/pwa/out/_next/static/chunks/9069.657590a84afb8fac.js +0 -41
  620. package/apps/pwa/out/_next/static/chunks/9125-ae588d88a0293b93.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/9311.f9f641a46a508611.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/9371-a6fee8378ebfa3ee.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/9421-3aa9ff4fa7f63d8a.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/9992.07ce73c10bd5e6a1.js +0 -79
  625. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-76394dc618d322c8.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/app/approvals/page-1b948637c37b3abd.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-c63400f1bcf3e47c.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/app/board/page-95d0957267819586.js +0 -1
  629. package/apps/pwa/out/_next/static/chunks/app/botlab/page-ba957ff875a026a0.js +0 -1
  630. package/apps/pwa/out/_next/static/chunks/app/bots/page-bb6fd0714879cb87.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/app/ceo/page-8f9038520d2acbef.js +0 -1
  632. package/apps/pwa/out/_next/static/chunks/app/chat/page-d4b48397e05fce2c.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/app/consult/page-72d5744245dc0c27.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/app/control/page-37625dc8300fa43a.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/app/decisions/page-fbef9d160272dbc1.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/app/design-check/page-58424168df84928e.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/app/editor/page-72c12ba430ac57c0.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/app/exec/page-d53f3e9ed806d7e0.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/app/field/page-4d2fbd45feee0829.js +0 -1
  640. package/apps/pwa/out/_next/static/chunks/app/healthz/route-76394dc618d322c8.js +0 -1
  641. package/apps/pwa/out/_next/static/chunks/app/inside/page-8783dc0ed7590a2c.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/app/intake/page-3706cb390606e930.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/app/layout-9b0a86598e0111fd.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/app/live/page-4edb8df90f4537ce.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/app/loops/page-47398b9ba1cc5e1b.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/app/market/page-fb86f55cfe2e681d.js +0 -1
  647. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e22345fc8fd47b1b.js +0 -1
  648. package/apps/pwa/out/_next/static/chunks/app/morning/page-fca2e59a81744d9e.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/app/not-found-6e96bda63100b31e.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/app/observatory/page-4d17ad8e0db8cea4.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-121f92766c33beaa.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-43f14212c545f92d.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ccec2023e7a974ec.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/app/outputs/page-b04f33f548b89a94.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/app/page-c8792823c0917021.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/app/reflection/page-8c0e32782ceef4cb.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-94bd3922eb230ce8.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/app/sessions/page-bca565887bafa03c.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-ac1dffafe649fc6c.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/app/settings/page-52374edb5e23e455.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ad4ebca120d43ca7.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/app/setup/layout-76394dc618d322c8.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/app/setup/page-718b89ce4b4ae4e4.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/app/share/page-204eca33eeeaddec.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/app/showroom/page-e58ba9bcdb9ac0f9.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/app/tasks/page-98f6dd50bafd7734.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/app/today/page-5a4bdd72bff27057.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/app/trace/page-6f6ab17d44f984ce.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/app/vault/page-aabb7bd15fb72da9.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-e4322319164a4436.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/app/workflows/page-e9efc0c8f32e2371.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/app/workspace/page-ccde99f07aaf71f7.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-80464aced9614390.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/c696986b.5dc8bb1143af16d1.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/d5d3cefc-373c4eb722ed848b.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/d8de0de2-dd0f4ac8e5ab1832.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/framework-002b472a66935d31.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/main-49b88d47292c0476.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/main-app-fff2549cb10a133f.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/pages/_app-32abbbba3f3b76b7.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/pages/_error-9d9cfef60c60dd7a.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/webpack-cc7f483fa294034a.js +0 -1
  683. package/apps/pwa/out/_next/static/css/d0a48dc099ed86cf.css +0 -1
  684. package/scripts/ratchets/next-tui-entry-budget.ts +0 -124
  685. /package/apps/pwa/out/_next/static/{HjeK7zONHfR7op1hKXp4k → X1ucLw10Cds_h614NoEnh}/_ssgManifest.js +0 -0
@@ -8,17 +8,21 @@ import { tmpdir } from 'node:os';
8
8
  import { dirname, join, resolve } from 'node:path';
9
9
  import { debug } from '../../src/debug/log.js';
10
10
  import { effectiveInstanceRoot, releaseLedgerRoot } from '../../src/instance/resolve.js';
11
+ import { acquireLockAsync } from '../../src/storage/file-lock.js';
11
12
  import { lastJsonObject, runGraph, verifyReleaseCommandRoot, verifyReleaseRecipeSnapshot, type GraphRunState } from '../../src/graph-runner/runner.js';
12
13
  import { withOpSeatFlock } from '../../src/steward/resync-tick.js';
13
14
  import { releaseRunUniverse } from './release-universe.js';
14
15
  import { isReleaseVersion } from './release-version.js';
15
- import { changedFilesBetween, clearPartialPlan, collectExecutedPodShardImages, planPartialRegate, planStalledShardRecheck, writePartialPlan, writeRecheckPlan } from './gate-partial.js';
16
+ import { changedFilesBetween, clearPartialPlan, collectExecutedPodShardImages, planPartialRegate, planSameCommitRecheck, planStalledShardRecheck, testFileOfId, writePartialPlan, writeRecheckPlan } from './gate-partial.js';
17
+ import { gateAssignableTestsAtCommit, gateEnvFingerprint } from './gate-baseline-receipt.js';
18
+ import { readGateShardManifest, readGateShardResult } from './gate-shard-receipts.js';
16
19
  import { gitClosureTree, testsReachingChanged, TRACEABLE_RELEASE_JSON, type ClosureTree } from './gate-closure.js';
17
20
  import { assertReleaseResumeApprovalBoundary, releaseResumeAcceptedRegressions, releaseResumeGateExecutor, releaseResumeGatePodPool, releaseResumeGateShardReroute, releaseResumeWaiver, type AcceptedRegression } from './resume.js';
18
21
  import { reserveReleaseResumeAttempt } from './resume-cap.js';
19
22
  import { cutReleaseBranch } from './cut-branch.js';
20
23
  import { releaseLoopConfig } from './unattended-release.js';
21
24
  import { trafficOptionsFromConfig } from './traffic-transition.js';
25
+ import type { GateAutohealDecision } from './gate-autoheal.js';
22
26
 
23
27
  const GRAPH = join(import.meta.dir, '../../graphs/release/release-loop.yaml');
24
28
 
@@ -46,6 +50,8 @@ export interface ResumeReleaseDeps {
46
50
  ledgerRoot?: string;
47
51
  /** GATE-REUSE-BY-DIFF: the tip tree for the import closure — default read from git objects of the tip commit. */
48
52
  closureTree?: (commit: string) => ClosureTree;
53
+ /** Test seam for the pinned Pod gate selection; production reads the selected test universe at the cut commit. */
54
+ gateTestsAtCommit?: (commit: string) => string[] | null;
49
55
  /** Source observation before the first resumed node. */
50
56
  onSource?: (line: string) => void;
51
57
  /** Test seam; production resolves this from ELANOUS_RELEASE_TRAFFIC_FILE / release.loop.trafficFile. */
@@ -62,9 +68,36 @@ export interface ResumeReleaseOptions {
62
68
  export interface ResumeReleasePlan {
63
69
  runId: string; version: string; fromNode: string; commandRoot: string; tip: TipRefresh;
64
70
  partial: PartialOutcome; gatePlan: { mode: 'not-run' | 'partial' | 'full'; reason?: string };
71
+ /** Most recent failed gate's autoheal recommendation, carried into the resume plan for the caller. */
72
+ autoheal?: GateAutohealDecision;
73
+ autohealReroute?: string;
65
74
  nextAttempt: number; priorCountKnown: boolean; preflight: 'passed';
66
75
  }
67
76
 
77
+ function priorGateAutoheal(run: GraphRunState): GateAutohealDecision | undefined {
78
+ const gate = lastJsonObject(run.nodes.find((node) => node.nodeId === 'gate')?.output);
79
+ const value = gate?.autoheal;
80
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
81
+ const candidate = value as Partial<GateAutohealDecision>;
82
+ if (!['reroute-shard', 'retry-shard', 'partial-resume', 'host-switch', 'hitl'].includes(String(candidate.action))) return undefined;
83
+ if (!['shard-pod-death', 'timeout', 'host-down', 'interference', 'true-regression', 'environment'].includes(String(candidate.cause))) return undefined;
84
+ if (typeof candidate.signature !== 'string' || typeof candidate.reason !== 'string'
85
+ || typeof candidate.shardElapsedMs !== 'number' || typeof candidate.overallElapsedMs !== 'number') return undefined;
86
+ return candidate as GateAutohealDecision;
87
+ }
88
+
89
+ function automaticReroute(run: GraphRunState, autoheal: GateAutohealDecision | undefined, tipChanged: boolean, from: string): string | undefined {
90
+ if (!autoheal || tipChanged || from !== 'gate' || typeof (run.input as Record<string, unknown> | undefined)?.gatePodPool !== 'string'
91
+ || (autoheal.action !== 'retry-shard' && autoheal.action !== 'reroute-shard' && autoheal.action !== 'host-switch')) return undefined;
92
+ const key = autoheal.shardKey;
93
+ if (autoheal?.action === 'host-switch') return 'auto';
94
+ if (typeof key === 'string' && /^pod-\d+$/.test(key)) return key;
95
+ if (typeof key === 'string' && /^\d+$/.test(key)) return `pod-${key}`;
96
+ const gate = lastJsonObject(run.nodes.find((node) => node.nodeId === 'gate')?.output);
97
+ const stalled = gate?.stalledShards;
98
+ return Array.isArray(stalled) && stalled.length > 0 ? 'auto' : undefined;
99
+ }
100
+
68
101
  /** CLI entry files: a production file that only names one (help text, a goal's command line) does not run it. */
69
102
  const CLI_ENTRIES = ['bin/elanous.mjs', 'src/index.ts'] as const;
70
103
  /** The gate's own test-file set (gate-node sweep: `git ls-files '*.test.*'` filtered by this). */
@@ -293,14 +326,111 @@ function verifyResumePreflight(run: GraphRunState, opts: ResumeReleaseOptions, d
293
326
  }
294
327
  }
295
328
 
296
- /** GATE-PARTIAL outcome of a resume — applied (plan written) or the reason it fell back to a full gate. `auto` marks the
297
- * default plan of `--from gate` (GATE-REUSE-BY-DIFF); `closure` is set when code changes were followed. */
329
+ /** GATE-PARTIAL outcome of a resume — applied (with its exact remeasure selection) or the reason it fell back to a full gate.
330
+ * `auto` marks the default plan of `--from gate` (GATE-REUSE-BY-DIFF); `closure` is set when code changes were followed. */
298
331
  export type PartialOutcome = { requested: false }
299
- | { requested: true; applied: true; files: number; priorCommit: string; auto?: true; closure?: { reached: number; opaque: number } }
332
+ | { requested: true; applied: true; files: number; plannedFiles: string[]; priorCommit: string; auto?: true; closure?: { reached: number; opaque: number } }
300
333
  | { requested: true; applied: false; reason: string; auto?: true };
301
334
 
302
335
  /** Build (or clear) the partial re-gate plan for the new tip. Fail-closed: any doubt leaves a full gate.
303
336
  * GATE-REUSE-BY-DIFF: `--from gate` on a moved tip plans by default (`partial: undefined`); `partial: false` opts out. */
337
+ type StalledGateEvidence = {
338
+ stalledShards: Array<{ shard: number; files: string[]; reason: string }>;
339
+ partialSummary: { pass: number; fail: number; errors: number; ran: number; files: number };
340
+ partialFailures: { failures: string[]; errors: string[] };
341
+ preservedShardCount: number;
342
+ };
343
+
344
+ /** Recover an interrupted Pod sweep from its atomic shard receipts. No receipt or identity doubt authorizes a partial gate. */
345
+ function stalledGateEvidence(input: { version: string; commit: string; repo: string; roots: string[];
346
+ gateOutput?: Record<string, unknown>; expectedBase40?: string; expectedTests?: (commit: string) => string[] | null }):
347
+ { ok: true; evidence: StalledGateEvidence } | { ok: false; reason: string } {
348
+ const fail = (reason: string) => ({ ok: false as const, reason });
349
+ if (input.gateOutput && (input.gateOutput.outcome !== 'error' || input.gateOutput.commit !== input.commit))
350
+ return fail('saved gate result is not an error for the recorded cut');
351
+ if (!/^[a-f0-9]{40}$/i.test(input.expectedBase40 ?? ''))
352
+ return fail('saved cutoff baseline is absent or invalid');
353
+ const fingerprint = gateEnvFingerprint(process.env);
354
+ let receiptDir: string | undefined;
355
+ for (const root of input.roots) {
356
+ const candidate = join(root, 'release', input.version, 'gate-logs', 'cut', 'receipts');
357
+ if (existsSync(candidate)) { receiptDir = candidate; break; }
358
+ }
359
+ if (!receiptDir) return fail('stalled gate receipts are absent');
360
+ const names = readdirSync(receiptDir).filter((name) => /^pod-\d+\.receipt\.json$/.test(name)).sort((a, b) =>
361
+ Number(/^pod-(\d+)/.exec(a)![1]) - Number(/^pod-(\d+)/.exec(b)![1]));
362
+ if (!names.length || names.some((name, index) => Number(/^pod-(\d+)/.exec(name)![1]) !== index))
363
+ return fail('stalled gate receipt shard set is incomplete');
364
+ const allFiles: string[] = [];
365
+ const failures: string[] = [];
366
+ const errors: string[] = [];
367
+ const stalledShards: StalledGateEvidence['stalledShards'] = [];
368
+ const imageDigests = new Set<string>();
369
+ let pass = 0, failCount = 0, errorCount = 0, ran = 0, files = 0, preservedShardCount = 0;
370
+ let base40: string | undefined;
371
+ for (const name of names) {
372
+ const id = name.slice(0, -'.receipt.json'.length);
373
+ const receipt = readGateShardManifest(receiptDir, id);
374
+ if (!receipt || receipt.head40 !== input.commit || !/^[a-f0-9]{40}$/i.test(receipt.base40)
375
+ || !receipt.files.length
376
+ || receipt.files.some((file) => typeof file !== 'string'
377
+ || !/^(?:[\w.-]+\/)*[\w.-]+\.test\.(?:tsx?|jsx?|mts|cts)$/.test(file) || file.split('/').includes('..'))
378
+ || typeof receipt.envKey !== 'string' || typeof receipt.host !== 'string')
379
+ return fail(`stalled gate receipt identity or file list is invalid: ${id}`);
380
+ if (receipt.base40 !== input.expectedBase40)
381
+ return fail(`stalled gate receipt baseline differs from saved cutoff baseline: ${id}`);
382
+ if (base40 && receipt.base40 !== base40) return fail(`stalled gate receipts disagree on baseline: ${id}`);
383
+ base40 = receipt.base40;
384
+ allFiles.push(...receipt.files);
385
+ if (receipt.status === 'unmeasured') {
386
+ stalledShards.push({ shard: Number(id.slice(4)), files: [...receipt.files], reason: 'incomplete' });
387
+ continue;
388
+ }
389
+ if (!receipt.envKey.startsWith(`${fingerprint}:`)) return fail(`stalled gate receipt environment mismatch: ${id}`);
390
+ const imageDigest = receipt.envKey.slice(fingerprint.length + 1);
391
+ if (!/^sha256:[a-f0-9]{64}$/i.test(imageDigest)) return fail(`stalled gate receipt image identity is missing: ${id}`);
392
+ imageDigests.add(imageDigest.toLowerCase());
393
+ const result = readGateShardResult(receiptDir, { shard: { id, files: receipt.files }, head40: input.commit,
394
+ base40: receipt.base40, envKey: receipt.envKey });
395
+ if (!result) return fail(`stalled gate receipt payload is incomplete or corrupt: ${id}`);
396
+ const totals = result.runs.reduce((sum, run) => ({ pass: sum.pass + run.pass, fail: sum.fail + run.fail,
397
+ errors: sum.errors + run.errors, ran: sum.ran + run.ran, files: sum.files + run.files }),
398
+ { pass: 0, fail: 0, errors: 0, ran: 0, files: 0 });
399
+ if (!Object.values(totals).every(Number.isSafeInteger) || receipt.failureIds.length !== totals.fail
400
+ || receipt.errorIds.length !== totals.errors)
401
+ return fail(`stalled gate receipt counts do not reconcile: ${id}`);
402
+ pass += totals.pass; failCount += totals.fail; errorCount += totals.errors; ran += totals.ran; files += totals.files;
403
+ failures.push(...receipt.failureIds); errors.push(...receipt.errorIds); preservedShardCount++;
404
+ }
405
+ if (!stalledShards.length) return fail('no incomplete shard receipt identifies the interrupted work');
406
+ if (new Set(allFiles).size !== allFiles.length) return fail('stalled gate receipt file assignments overlap');
407
+ if (imageDigests.size !== 1) return fail('stalled gate completed shards do not share one executed image');
408
+ const expected = (input.expectedTests ?? ((commit) => gateAssignableTestsAtCommit(input.repo, commit)))(input.commit);
409
+ if (!expected || JSON.stringify([...expected].sort()) !== JSON.stringify([...allFiles].sort()))
410
+ return fail('stalled gate receipts do not cover the pinned test selection');
411
+ const partialSummary = { pass, fail: failCount, errors: errorCount, ran, files };
412
+ const partialFailures = { failures: [...new Set(failures)].sort(), errors: [...new Set(errors)].sort() };
413
+ const gate = input.gateOutput;
414
+ if (gate) {
415
+ const savedSummary = gate.partialSummary as Partial<StalledGateEvidence['partialSummary']> | undefined;
416
+ const savedFailures = gate.partialFailures as Partial<StalledGateEvidence['partialFailures']> | undefined;
417
+ if (savedSummary && JSON.stringify(savedSummary) !== JSON.stringify(partialSummary))
418
+ return fail('saved gate summary disagrees with shard receipts');
419
+ if (savedFailures && (JSON.stringify([...((savedFailures.failures as string[] | undefined) ?? [])].sort()) !== JSON.stringify(partialFailures.failures)
420
+ || JSON.stringify([...((savedFailures.errors as string[] | undefined) ?? [])].sort()) !== JSON.stringify(partialFailures.errors)))
421
+ return fail('saved gate failures disagree with shard receipts');
422
+ if (gate.preservedShardCount !== undefined && gate.preservedShardCount !== preservedShardCount)
423
+ return fail('saved preserved shard count disagrees with shard receipts');
424
+ if (Array.isArray(gate.stalledShards)) {
425
+ const savedStalled = gate.stalledShards.flatMap((item) => item && typeof item === 'object'
426
+ && Array.isArray((item as { files?: unknown }).files) ? (item as { files: string[] }).files : []).sort();
427
+ if (JSON.stringify(savedStalled) !== JSON.stringify(stalledShards.flatMap((item) => item.files).sort()))
428
+ return fail('saved stalled shard list disagrees with shard receipts');
429
+ }
430
+ }
431
+ return { ok: true, evidence: { stalledShards, partialSummary, partialFailures, preservedShardCount } };
432
+ }
433
+
304
434
  function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean; gateExecutor?: string }, deps: ResumeReleaseDeps, dryRun = false): PartialOutcome {
305
435
  const root = deps.root ?? effectiveInstanceRoot();
306
436
  // Any resume starts from no plan: one without a partial plan (from whatever node) never inherits an older plan.
@@ -326,6 +456,42 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
326
456
  const saved = JSON.parse(readFileSync(tip.statePath, 'utf8')) as GraphRunState;
327
457
  const gateRecord = saved.nodes.find((node) => node.nodeId === 'gate');
328
458
  const previousGate = gateRecord ? lastJsonObject(gateRecord.output) as Record<string, unknown> | undefined : undefined;
459
+ // `loadFlaky` is also a partial-resume cause, but it is emitted separately from
460
+ // shard-only interference. Carry both sets into the same affected-file recheck.
461
+ const interference = [...new Set([
462
+ ...(Array.isArray(previousGate?.interferenceErrors) ? previousGate.interferenceErrors : []),
463
+ ...(Array.isArray(previousGate?.loadFlaky) ? previousGate.loadFlaky : []),
464
+ ])];
465
+ if (previousGate?.outcome === 'error' && previousGate.commit === tip.to && previousGate.autoheal
466
+ && typeof previousGate.autoheal === 'object' && (previousGate.autoheal as { action?: unknown }).action === 'partial-resume') {
467
+ if (!Array.isArray(interference) || !interference.length || !interference.every((id) => typeof id === 'string'))
468
+ return fallback('partial-resume has no complete interference error record');
469
+ let closure: ClosureTree;
470
+ try { closure = (deps.closureTree ?? ((commit: string) => gitClosureTree(deps.repo ?? process.cwd(), commit)))(tip.to); }
471
+ catch (error) { return fallback(`tip tree unreadable: ${String(error instanceof Error ? error.message : error).slice(0, 200)}`); }
472
+ const tracked = new Set(closure.files);
473
+ const planned = planSameCommitRecheck({ version: tip.version, measuredCommit: tip.to, forCommit: tip.to,
474
+ failures: [], errors: interference as string[], existsAtCommit: (file) => tracked.has(file) });
475
+ if (!planned.ok) return fallback(planned.reason);
476
+ // The gate consumer only executes same-cut partial work for the stalled-only recheck shape.
477
+ // Interference files are likewise unmeasured, so mark exactly this set as the recheck boundary.
478
+ const partialFailures = previousGate.partialFailures as { failures?: unknown; errors?: unknown } | undefined;
479
+ if (!Array.isArray(partialFailures?.failures) || !partialFailures.failures.every((id) => typeof id === 'string')
480
+ || !Array.isArray(partialFailures.errors) || !partialFailures.errors.every((id) => typeof id === 'string'))
481
+ return fallback('partial-resume has no complete prior failure record');
482
+ let priorFailures: string[];
483
+ let priorErrors: string[];
484
+ try {
485
+ const affected = new Set(planned.plan.files);
486
+ priorFailures = (partialFailures.failures as string[]).filter((id) => !affected.has(testFileOfId(id)));
487
+ priorErrors = (partialFailures.errors as string[]).filter((id) => !affected.has(testFileOfId(id)));
488
+ } catch { return fallback('partial-resume prior failure record is malformed'); }
489
+ const recheckPlan = { ...planned.plan, priorFailures, priorErrors, stalledFiles: planned.plan.files };
490
+ const path = dryRun ? undefined : writeRecheckPlan(root, recheckPlan);
491
+ debug.log('release-loop.resume', 'interference-recheck-planned', { version: tip.version, commit: tip.to,
492
+ remeasureFileCount: recheckPlan.files.length, fallbackReason: null, ...(path ? { path } : {}), dryRun });
493
+ return { requested: true, applied: true, files: recheckPlan.files.length, priorCommit: tip.to, ...(auto ? { auto: true as const } : {}) };
494
+ }
329
495
  const stalled = previousGate?.stalledShards;
330
496
  const summary = previousGate?.partialSummary as { pass?: unknown; fail?: unknown; errors?: unknown; ran?: unknown; files?: unknown } | undefined;
331
497
  const measured = previousGate?.partialFailures as { failures?: unknown; errors?: unknown } | undefined;
@@ -356,9 +522,11 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
356
522
  debug.log('release-loop.resume', 'stalled-recheck-planned', { version: tip.version, commit: tip.to,
357
523
  planKind: planned.plan.kind, remeasureFileCount: planned.plan.files.length,
358
524
  preservedShardCount: planned.plan.preservedShardCount ?? 0, fallbackReason: null, path });
359
- return { requested: true, applied: true, files: planned.plan.files.length, priorCommit: tip.to, ...(auto ? { auto: true as const } : {}) };
525
+ return { requested: true, applied: true, files: planned.plan.files.length, plannedFiles: [...planned.plan.files], priorCommit: tip.to,
526
+ ...(auto ? { auto: true as const } : {}) };
360
527
  }
361
528
  let prior: { commit?: unknown; failures?: unknown; errors?: unknown } | undefined;
529
+ let movedStalledEvidence: StalledGateEvidence | undefined;
362
530
  for (const location of [...new Set([deps.ledgerRoot ?? releaseLedgerRoot(), root])]) {
363
531
  const path = join(location, 'release', tip.version, 'gate-failures.json');
364
532
  if (!existsSync(path)) continue;
@@ -368,7 +536,19 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
368
536
  if (record?.commit === tip.from) { prior = record; break; }
369
537
  }
370
538
  if (!prior || !Array.isArray(prior.failures) || !(prior.errors === undefined || Array.isArray(prior.errors))) {
371
- return fallback(`no complete gate record for ${tip.from.slice(0, 12)} (a stalled or errored gate keeps none)`);
539
+ const gateRecord = saved.nodes.find((node) => node.nodeId === 'gate');
540
+ const gateOutput = gateRecord ? lastJsonObject(gateRecord.output) as Record<string, unknown> | undefined : undefined;
541
+ const cutoffRecord = saved.nodes.find((node) => node.nodeId === 'cutoff');
542
+ const cutoffOutput = cutoffRecord ? lastJsonObject(cutoffRecord.output) as Record<string, unknown> | undefined : undefined;
543
+ const expectedBase40 = (cutoffOutput?.baseline as { sha?: unknown } | undefined)?.sha;
544
+ const evidence = stalledGateEvidence({ version: tip.version, commit: tip.from, repo: deps.repo ?? process.cwd(),
545
+ roots: [...new Set([deps.ledgerRoot ?? releaseLedgerRoot(), root])], gateOutput,
546
+ ...(typeof expectedBase40 === 'string' ? { expectedBase40 } : {}),
547
+ expectedTests: deps.gateTestsAtCommit });
548
+ if (!evidence.ok) return fallback(`no complete gate record for ${tip.from.slice(0, 12)}; ${evidence.reason}`);
549
+ movedStalledEvidence = evidence.evidence;
550
+ prior = { commit: tip.from, failures: movedStalledEvidence.partialFailures.failures,
551
+ errors: movedStalledEvidence.partialFailures.errors };
372
552
  }
373
553
  // Both sides of a rename count (`--no-renames`): a source moved to a test/Markdown name is still a source change.
374
554
  let changed: string[];
@@ -404,10 +584,11 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
404
584
  const savedInput = saved.input as { gateExecutor?: unknown; gatePodPool?: unknown };
405
585
  const podGate = (opts.gateExecutor ?? savedInput.gateExecutor ?? (savedInput.gatePodPool ? 'pod' : undefined)) === 'pod';
406
586
  const priorShards = priorCutLogs ? collectExecutedPodShardImages(priorCutLogs, tip.from) : undefined;
407
- const planned = planPartialRegate({ version: tip.version, priorCommit: tip.from, forCommit: tip.to, changedFiles: changed,
587
+ const stalledFiles = movedStalledEvidence?.stalledShards.flatMap((shard) => shard.files) ?? [];
588
+ const planned = planPartialRegate({ version: tip.version, priorCommit: tip.from, forCommit: tip.to, changedFiles: [...changed, ...stalledFiles],
408
589
  priorFailures: prior.failures as string[], priorErrors: (prior.errors ?? []) as string[],
409
590
  existsAtTip: (file) => tracked.has(file),
410
- ...(podGate ? { priorShards: priorShards ?? [] } : {}),
591
+ ...((podGate || movedStalledEvidence) ? { priorShards: priorShards ?? [] } : {}),
411
592
  closure: (changedCode) => testsReachingChanged(tree, changedCode, tests, { cliEntries: CLI_ENTRIES }) });
412
593
  if (!planned.ok) return fallback(planned.reason);
413
594
  if (planned.plan.files.length >= tests.length) return fallback(`every test is re-run (${tests.length}) — full gate`);
@@ -427,7 +608,7 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
427
608
  const path = dryRun ? undefined : writePartialPlan(root, planned.plan);
428
609
  debug.log('release-loop.resume', 'partial-planned', { version: tip.version, priorCommit: tip.from, commit: tip.to, files: planned.plan.files.length,
429
610
  tests: tests.length, auto, ...(path ? { path } : {}), dryRun, ...(planned.closure ? { reached: planned.closure.reached, opaque: planned.closure.opaque } : {}) });
430
- return { requested: true, applied: true, files: planned.plan.files.length, priorCommit: tip.from,
611
+ return { requested: true, applied: true, files: planned.plan.files.length, plannedFiles: [...planned.plan.files], priorCommit: tip.from,
431
612
  ...(auto ? { auto: true as const } : {}), ...(planned.closure ? { closure: planned.closure } : {}) };
432
613
  }
433
614
 
@@ -483,19 +664,25 @@ async function planResumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: Resu
483
664
  waiverCommit = recorded;
484
665
  }
485
666
  const tip = refreshReleaseBranchTipInternal(opts.runId, deps, opts.from, waiverCommit, true);
667
+ const fromIndex = savedRun.path.indexOf(opts.from);
668
+ const gateIndex = savedRun.path.indexOf('gate');
669
+ const gateWillRun = fromIndex >= 0 && gateIndex >= fromIndex;
486
670
  if (opts.rerouteShard !== undefined && tip.changed) throw new Error('shard reroute requires the original cut head; moved tip needs a full re-gate');
487
671
  verifyResumeRecipePin(opts.commandRoot ?? process.cwd(), savedRun.graphSnapshot, tip.version);
488
672
  const commandRoot = verifyReleaseCommandRoot(opts.commandRoot ?? process.cwd(), tip.to, deps.git);
489
673
  verifyResumePreflight(savedRun, opts, deps, tip.version, tip.from, tip.to, commandRoot);
490
- const partial = preparePartial(tip, opts.rerouteShard !== undefined ? { ...opts, partial: false } : opts, deps, true);
491
- const fromIndex = savedRun.path.indexOf(opts.from);
492
- const gateIndex = savedRun.path.indexOf('gate');
493
- const gateWillRun = fromIndex >= 0 && gateIndex >= fromIndex;
674
+ const autoheal = gateWillRun ? priorGateAutoheal(savedRun) : undefined;
675
+ const autoReroute = opts.rerouteShard === undefined ? automaticReroute(savedRun, autoheal, tip.changed, opts.from) : undefined;
676
+ const automaticShardReroute = autoReroute ? releaseResumeGateShardReroute(savedRun, { from: opts.from, shard: autoReroute }) : undefined;
677
+ const effectiveOpts = automaticShardReroute ? { ...opts, rerouteShard: automaticShardReroute.set.gateRerouteShard } : opts;
678
+ const partial = preparePartial(tip, effectiveOpts.rerouteShard !== undefined ? { ...effectiveOpts, partial: false } : effectiveOpts, deps, true);
494
679
  const gatePlan = !gateWillRun ? { mode: 'not-run' as const }
495
680
  : partial.requested && partial.applied ? { mode: 'partial' as const }
496
681
  : { mode: 'full' as const, reason: partial.requested ? partial.reason : opts.partial === false ? 'partial recheck disabled' : 'no partial plan applies' };
682
+ if (autoheal) debug.log('release-loop.resume', 'gate-autoheal-plan', { runId: opts.runId, version: tip.version, appliedReroute: automaticShardReroute?.set.gateRerouteShard ?? null, ...autoheal });
497
683
  return { runId: opts.runId, version: tip.version, fromNode: opts.from, commandRoot, tip, partial,
498
- gatePlan, nextAttempt: attempt.attempt, priorCountKnown: attempt.priorCountKnown, preflight: 'passed' };
684
+ gatePlan, ...(autoheal ? { autoheal } : {}), ...(automaticShardReroute ? { autohealReroute: automaticShardReroute.set.gateRerouteShard } : {}),
685
+ nextAttempt: attempt.attempt, priorCountKnown: attempt.priorCountKnown, preflight: 'passed' };
499
686
  }
500
687
 
501
688
  async function resumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: ResumeReleaseDeps): Promise<{ tip: TipRefresh; partial: PartialOutcome; state: GraphRunState }> {
@@ -550,7 +737,12 @@ async function resumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: ResumeRe
550
737
  const commandRoot = verifyReleaseCommandRoot(opts.commandRoot ?? process.cwd(), previewTip.to, deps.git);
551
738
  verifyResumePreflight(savedRun, opts, deps, previewTip.version, previewTip.from, previewTip.to, commandRoot);
552
739
  const tip = refreshReleaseBranchTipInternal(opts.runId, deps, opts.from, waiverCommit, false, previewTip.to);
553
- const partial = preparePartial(tip, shardReroute ? { ...opts, partial: false } : opts, deps);
740
+ const autoheal = priorGateAutoheal(savedRun);
741
+ const autoReroute = !shardReroute ? automaticReroute(savedRun, autoheal, tip.changed, opts.from) : undefined;
742
+ const automaticShardReroute = autoReroute ? releaseResumeGateShardReroute(savedRun, { from: opts.from, shard: autoReroute }) : undefined;
743
+ const partial = preparePartial(tip, shardReroute || automaticShardReroute
744
+ ? { ...opts, rerouteShard: (shardReroute ?? automaticShardReroute)!.set.gateRerouteShard, partial: false } : opts, deps);
745
+ if (autoheal) debug.log('release-loop.resume', 'gate-autoheal-plan', { runId: opts.runId, version: tip.version, appliedReroute: automaticShardReroute?.set.gateRerouteShard ?? null, ...autoheal });
554
746
  // A failed preflight does not consume an observed release resume attempt.
555
747
  const attempt = reserveReleaseResumeAttempt(savedRun, deps.root!, { now: deps.now });
556
748
  debug.log('release-loop.resume', 'attempt', { runId: opts.runId, attempt: attempt.attempt,
@@ -565,8 +757,8 @@ async function resumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: ResumeRe
565
757
  ...(acceptedRegressions ? { releaseAcceptedRegressions: { requested: opts.acceptedRegressions!, acceptedRegressions } } : {}),
566
758
  ...(gateExecutor ? { releaseGateExecutor: { executor: gateExecutor.set.gateExecutor, ...(opts.gateHost !== undefined ? { host: opts.gateHost } : {}) } } : {}),
567
759
  ...(gatePodPool ? { releaseGatePodPool: gatePodPool.set.gatePodPool } : {}),
568
- ...(shardReroute ? { releaseGateShardReroute: { shard: shardReroute.set.gateRerouteShard,
569
- ...(shardReroute.set.gateShardHost ? { host: shardReroute.set.gateShardHost } : {}),
760
+ ...(shardReroute || automaticShardReroute ? { releaseGateShardReroute: { shard: (shardReroute ?? automaticShardReroute)!.set.gateRerouteShard,
761
+ ...((shardReroute ?? automaticShardReroute)!.set.gateShardHost ? { host: (shardReroute ?? automaticShardReroute)!.set.gateShardHost } : {}),
570
762
  ...(opts.shardOs ? { os: opts.shardOs } : {}) } } : {}),
571
763
  deps: { root: deps.root, ...(deps.runBash ? { runBash: deps.runBash } : {}) } });
572
764
  return { tip, partial, state };
@@ -604,6 +796,23 @@ export interface RecoverReleaseResult {
604
796
  resume?: { status: string; tip: TipRefresh; partial: PartialOutcome };
605
797
  }
606
798
 
799
+ export interface RecoverReleaseLandingOptions {
800
+ runId: string;
801
+ /** The landed repair commit on main; this is the idempotency key for a landing notification. */
802
+ mergeSha: string;
803
+ from?: string;
804
+ commandRoot?: string;
805
+ partial?: boolean;
806
+ repoRoot?: string;
807
+ }
808
+
809
+ export type RecoverReleaseLandingResult = RecoverReleaseResult & { duplicate: boolean };
810
+
811
+ /** Commander represents `--no-partial` as false and its default (flag omitted) as true. */
812
+ export function recoverLandingPartialOption(optionValue?: boolean): boolean | undefined {
813
+ return optionValue === false ? false : undefined;
814
+ }
815
+
607
816
  export interface RecoverReleaseDeps extends ResumeReleaseDeps {
608
817
  /** `release cut-branch --append` — tests record the plan instead of pushing the branch. */
609
818
  cutBranch?: typeof cutReleaseBranch;
@@ -619,11 +828,19 @@ export class ReleaseRecoverStopped extends Error {
619
828
  }
620
829
  }
621
830
 
831
+ function readRecoverJson(path: string, label: string): unknown {
832
+ try { return JSON.parse(readFileSync(path, 'utf8')) as unknown; }
833
+ catch (error) {
834
+ const detail = error instanceof Error ? error.message : String(error);
835
+ throw new Error(`release ${label} JSON을 읽을 수 없다: ${path} (${detail}) — 파일을 보존하고 원장을 올바른 JSON으로 복구한 뒤 재시도하라.`);
836
+ }
837
+ }
838
+
622
839
  /** Append one stage result to the resume-attempts ledger. `attempts[]` keeps its shape — the stage results are added. */
623
840
  function recordRecoverStage(entry: { root: string; runId: string; version: string; priorCountKnown: boolean; stage: RecoverStageRecord }): void {
624
841
  const path = join(entry.root, 'release', entry.version, 'resume-attempts', `${entry.runId}.json`);
625
842
  const saved = existsSync(path)
626
- ? JSON.parse(readFileSync(path, 'utf8')) as { attempts?: unknown; recoveries?: unknown; priorCountKnown?: unknown }
843
+ ? readRecoverJson(path, 'resume attempts 원장') as { attempts?: unknown; recoveries?: unknown; priorCountKnown?: unknown }
627
844
  : undefined;
628
845
  if (saved !== undefined && (!saved || typeof saved !== 'object' || Array.isArray(saved) || !Array.isArray(saved.attempts)
629
846
  || (saved.recoveries !== undefined && !Array.isArray(saved.recoveries)))) throw new Error('release resume attempts ledger invalid');
@@ -637,16 +854,39 @@ function recordRecoverStage(entry: { root: string; runId: string; version: strin
637
854
  renameSync(temporary, path);
638
855
  }
639
856
 
857
+ async function acquireReleaseRecoveryLock(root: string, runId: string) {
858
+ const lockDir = join(root, 'release', 'recovery-locks');
859
+ mkdirSync(lockDir, { recursive: true });
860
+ // A full-gate subprocess can block this process's event loop, delaying the async lock heartbeat.
861
+ // The observed release gate reached 2h20m; leave a finite four-hour stale/wait window for one recovery.
862
+ const lock = await acquireLockAsync(join(lockDir, `${runId}.lock`), {
863
+ staleMs: 4 * 60 * 60_000, retryBusyMs: 1_000, maxTries: 14_400,
864
+ });
865
+ if (!lock.stillHeld()) {
866
+ lock.release();
867
+ throw new Error(`release recovery lock was lost for ${runId} — retry after inspecting the recovery ledger`);
868
+ }
869
+ return lock;
870
+ }
871
+
640
872
  /**
641
873
  * Repair a failed release run in one command: append dry-run (plan only) → append (fast-forward release/<v> onto the
642
874
  * picked repairs) → resume --from export-check (preflight plan, then the graph). A failing stage stops the command:
643
875
  * the later stages never run, and the stage results are already in the ledger for the human to read.
644
876
  */
645
877
  export async function recoverReleaseRun(opts: RecoverReleaseOptions, deps: RecoverReleaseDeps = {}): Promise<RecoverReleaseResult> {
646
- if (!Array.isArray(opts.pick) || !opts.pick.length) throw new Error('release recover requires at least one --pick commit');
647
878
  const root = releaseRunUniverse({ root: deps.root });
879
+ const runId = safeRunId(opts.runId);
880
+ const lock = await acquireReleaseRecoveryLock(root, runId);
881
+ try {
882
+ return await recoverReleaseRunLocked(opts, deps, root);
883
+ } finally { lock.release(); }
884
+ }
885
+
886
+ async function recoverReleaseRunLocked(opts: RecoverReleaseOptions, deps: RecoverReleaseDeps, root: string): Promise<RecoverReleaseResult> {
887
+ if (!Array.isArray(opts.pick) || !opts.pick.length) throw new Error('release recover requires at least one --pick commit');
648
888
  const statePath = join(root, 'graph-runs', 'release-loop', `${safeRunId(opts.runId)}.json`);
649
- const savedRun = JSON.parse(readFileSync(statePath, 'utf8')) as GraphRunState;
889
+ const savedRun = readRecoverJson(statePath, 'release run 상태') as GraphRunState;
650
890
  if (savedRun.graphId !== 'release-loop' || savedRun.runId !== opts.runId) throw new Error(`run identity mismatch: ${statePath}`);
651
891
  // The saved version-release record names what to repair: the release branch and the commit the run cut.
652
892
  const versionRecord = savedRun.nodes.find((node) => node.nodeId === 'version-release');
@@ -707,8 +947,9 @@ export async function recoverReleaseRun(opts: RecoverReleaseOptions, deps: Recov
707
947
  const now = (): string => (deps.now?.() ?? new Date()).toISOString();
708
948
  const stages: RecoverStageRecord[] = [];
709
949
  const record = (stage: RecoverStageRecord): void => {
710
- stages.push(stage);
711
- recordRecoverStage({ root, runId: opts.runId, version, priorCountKnown: !savedRun.resume, stage });
950
+ const withPick = { ...stage, pick: stage.pick ?? [...opts.pick] };
951
+ stages.push(withPick);
952
+ recordRecoverStage({ root, runId: opts.runId, version, priorCountKnown: !savedRun.resume, stage: withPick });
712
953
  };
713
954
  const stop = (stage: RecoverStageName, error: unknown): never => {
714
955
  throw new ReleaseRecoverStopped(stage, stages, RECOVER_STAGES.slice(RECOVER_STAGES.indexOf(stage) + 1),
@@ -733,7 +974,7 @@ export async function recoverReleaseRun(opts: RecoverReleaseOptions, deps: Recov
733
974
  };
734
975
  const ledgerPath = join(root, 'release', version, 'resume-attempts', `${opts.runId}.json`);
735
976
  const recoveries: RecoverStageRecord[] = existsSync(ledgerPath)
736
- ? ((JSON.parse(readFileSync(ledgerPath, 'utf8')) as { recoveries?: unknown[] }).recoveries ?? []) as RecoverStageRecord[]
977
+ ? ((readRecoverJson(ledgerPath, 'resume attempts 원장') as { recoveries?: unknown[] }).recoveries ?? []) as RecoverStageRecord[]
737
978
  : [];
738
979
  let appendedCommit: string | undefined;
739
980
  for (let index = recoveries.length - 1; index >= 0; index -= 1) {
@@ -843,3 +1084,69 @@ export async function recoverReleaseRun(opts: RecoverReleaseOptions, deps: Recov
843
1084
  }
844
1085
  }
845
1086
  }
1087
+
1088
+ /**
1089
+ * Manual landing-event entrypoint. The event's merge SHA is the dedupe key; once that repair has completed its
1090
+ * resume stage, a redelivered event returns the recorded result without appending or running the graph again.
1091
+ * The event producer can call this after its own hook lands; this function does not subscribe to PR-land events.
1092
+ */
1093
+ export async function recoverReleaseRunFromLanding(opts: RecoverReleaseLandingOptions,
1094
+ deps: RecoverReleaseDeps = {}): Promise<RecoverReleaseLandingResult> {
1095
+ if (!/^[0-9a-f]{40}$/i.test(opts.mergeSha)) throw new Error('release recover-landing requires a full 40-hex merge SHA');
1096
+ const root = releaseRunUniverse({ root: deps.root });
1097
+ const runId = safeRunId(opts.runId);
1098
+ // Share one run-scoped lock with manual `release recover`: the dedupe read, append, and resume must not race.
1099
+ const landingLock = await acquireReleaseRecoveryLock(root, runId);
1100
+ try {
1101
+ return await recoverReleaseRunFromLandingLocked(opts, deps, root, runId);
1102
+ } finally { landingLock.release(); }
1103
+ }
1104
+
1105
+ async function recoverReleaseRunFromLandingLocked(opts: RecoverReleaseLandingOptions, deps: RecoverReleaseDeps,
1106
+ root: string, runId: string): Promise<RecoverReleaseLandingResult> {
1107
+ const pick = [opts.mergeSha.toLowerCase()];
1108
+ const statePath = join(root, 'graph-runs', 'release-loop', `${runId}.json`);
1109
+ if (existsSync(statePath)) {
1110
+ const savedRun = readRecoverJson(statePath, 'release run 상태') as GraphRunState;
1111
+ const versionNode = savedRun.nodes.find((node) => node.nodeId === 'version-release');
1112
+ const recorded = lastJsonObject(versionNode?.output);
1113
+ const version = typeof recorded?.version === 'string' ? recorded.version : '';
1114
+ if (savedRun.graphId === 'release-loop' && savedRun.runId === runId && isReleaseVersion(version)) {
1115
+ const ledgerPath = join(root, 'release', version, 'resume-attempts', `${runId}.json`);
1116
+ if (existsSync(ledgerPath)) {
1117
+ const ledger = readRecoverJson(ledgerPath, 'resume attempts 원장') as { recoveries?: unknown };
1118
+ if (ledger.recoveries !== undefined && !Array.isArray(ledger.recoveries)) throw new Error(`release resume attempts ledger invalid: ${ledgerPath}`);
1119
+ const records = (ledger.recoveries ?? []) as RecoverStageRecord[];
1120
+ const matchesPick = (entry: RecoverStageRecord): boolean => Array.isArray(entry.pick)
1121
+ && entry.pick.some((sha) => typeof sha === 'string' && sha.toLowerCase() === pick[0]);
1122
+ let completedIndex = -1;
1123
+ for (let index = records.length - 1; index >= 0; index -= 1) {
1124
+ const entry = records[index]!;
1125
+ if (matchesPick(entry) && entry.stage === 'resume' && entry.outcome === 'ok') {
1126
+ completedIndex = index;
1127
+ break;
1128
+ }
1129
+ }
1130
+ if (completedIndex >= 0) {
1131
+ let cycleStart = completedIndex;
1132
+ for (let index = completedIndex; index >= 0; index -= 1) {
1133
+ const entry = records[index]!;
1134
+ if (matchesPick(entry) && entry.stage === 'append-dry-run') {
1135
+ cycleStart = index;
1136
+ break;
1137
+ }
1138
+ }
1139
+ const completed = records[completedIndex]!;
1140
+ return { runId, version, branch: `release/${version}`, fromNode: completed.from ?? opts.from ?? RECOVER_FIRST_NODE,
1141
+ stages: records.slice(cycleStart, completedIndex + 1).filter(matchesPick), duplicate: true };
1142
+ }
1143
+ }
1144
+ }
1145
+ }
1146
+ const result = await recoverReleaseRunLocked({ runId, pick,
1147
+ ...(opts.from === undefined ? {} : { from: opts.from }),
1148
+ ...(opts.commandRoot === undefined ? {} : { commandRoot: opts.commandRoot }),
1149
+ ...(opts.partial === undefined ? {} : { partial: opts.partial }),
1150
+ ...(opts.repoRoot === undefined ? {} : { repoRoot: opts.repoRoot }) }, deps, root);
1151
+ return { ...result, duplicate: false };
1152
+ }
@@ -42,7 +42,10 @@ export async function runTrafficNode(node = process.argv[2], env: NodeJS.Process
42
42
  return finishNode(node!, version, { outcome: 'fail', verdict: 'fail', summary });
43
43
  }
44
44
  const result = await applyReleaseTrafficEvent(context, event, options);
45
- if (!result.applied) {
45
+ // Replaying the terminal restore after this run already restored its pre-cut traffic is a successful no-op.
46
+ const alreadyRestored = event === 'failed' && result.reason === 'already-restored';
47
+ const applied = result.applied || alreadyRestored;
48
+ if (!applied) {
46
49
  const consequence = event === 'failed' && result.reason === 'published-keeps-full'
47
50
  ? 'npm 발행은 완료됐으므로 전체 트래픽을 유지합니다. 후속 발행 단계 실패를 확인하세요.'
48
51
  : event === 'verify-complete' || event === 'npm-published'
@@ -52,8 +55,8 @@ export async function runTrafficNode(node = process.argv[2], env: NodeJS.Process
52
55
  : 'gate는 성공했으나 트래픽 감속이 적용되지 않았습니다.';
53
56
  (deps.report ?? reportFailure)(`RELEASE-POSTPUB-TRAFFIC ${version} ${runId || 'unknown'} ${node} 무시 — ${result.reason ?? 'traffic state unchanged'}. ${consequence}`);
54
57
  }
55
- return finishNode(node!, version, { outcome: result.applied ? 'ok' : 'fail', verdict: result.applied ? 'pass' : 'fail',
56
- summary: `${event} ${result.applied ? 'applied' : result.reason ?? 'ignored'}` });
58
+ return finishNode(node!, version, { outcome: applied ? 'ok' : 'fail', verdict: applied ? 'pass' : 'fail',
59
+ summary: `${event} ${alreadyRestored ? 'already-restored' : applied ? 'applied' : result.reason ?? 'ignored'}` });
57
60
  } catch (error) {
58
61
  const detail = errorResult(error).summary;
59
62
  const isLockTimeout = detail.startsWith('traffic lock timeout:');
@@ -49,6 +49,7 @@ export interface ReleaseLoopConfig {
49
49
  export interface ReleaseRunInput extends ReleaseLoopConfig {
50
50
  version: string;
51
51
  previousVersion: string;
52
+ triggerEvent?: { id: string; type: 'release.cut.due'; version: string; cutAt: string; at: string };
52
53
  cutCommit?: string;
53
54
  /** Required for the pod executor (the default); a host-executor run may have none. */
54
55
  gatePodPool?: string;
@@ -143,7 +144,7 @@ export function buildReleaseRunInput(version: string, deps: UnattendedReleaseDep
143
144
 
144
145
  /** Fail closed at the entry boundary, before any graph node can change a release. */
145
146
  export async function runUnattendedRelease(
146
- opts: { version: string; dryRun?: boolean; cutCommit?: string; forceFreeze?: boolean; forceNew?: boolean; mainCut?: boolean; prerelease?: PrereleaseKind; publishAt?: string; gateExecutor?: string; gateHost?: string },
147
+ opts: { version: string; dryRun?: boolean; cutCommit?: string; forceFreeze?: boolean; forceNew?: boolean; mainCut?: boolean; prerelease?: PrereleaseKind; publishAt?: string; gateExecutor?: string; gateHost?: string; triggerEvent?: ReleaseRunInput['triggerEvent'] },
147
148
  deps: UnattendedReleaseDeps = {},
148
149
  ): Promise<{ input: ReleaseRunInput; dryRun: boolean; state?: GraphRunState }> {
149
150
  // A dry run only builds the input and mutates nothing, so it neither creates nor waits on the OP-seat lock.
@@ -154,7 +155,7 @@ export async function runUnattendedRelease(
154
155
  }
155
156
 
156
157
  async function runUnattendedReleaseLocked(
157
- opts: { version: string; dryRun?: boolean; cutCommit?: string; forceFreeze?: boolean; forceNew?: boolean; mainCut?: boolean; prerelease?: PrereleaseKind; publishAt?: string; gateExecutor?: string; gateHost?: string },
158
+ opts: { version: string; dryRun?: boolean; cutCommit?: string; forceFreeze?: boolean; forceNew?: boolean; mainCut?: boolean; prerelease?: PrereleaseKind; publishAt?: string; gateExecutor?: string; gateHost?: string; triggerEvent?: ReleaseRunInput['triggerEvent'] },
158
159
  deps: UnattendedReleaseDeps,
159
160
  ): Promise<{ input: ReleaseRunInput; dryRun: boolean; state?: GraphRunState }> {
160
161
  // RELEASE-BRANCH (10-06): the default cut is a release branch, so main keeps landing and the run no longer needs a
@@ -171,6 +172,7 @@ async function runUnattendedReleaseLocked(
171
172
  }
172
173
  const version = resolveRunVersion(opts.version, opts.prerelease, deps);
173
174
  const input = buildReleaseRunInput(version, deps, opts.cutCommit, { executor: opts.gateExecutor, host: opts.gateHost });
175
+ if (opts.triggerEvent) input.triggerEvent = opts.triggerEvent;
174
176
  if (opts.publishAt !== undefined) {
175
177
  const invalid = publishAtError(opts.publishAt);
176
178
  if (invalid) throw new Error(invalid);
@@ -0,0 +1,20 @@
1
+ /**
2
+ * SHADOW-MODEL-ARMS(0.2.44 칸) · 저작 단계 그림자 «1팔» probe — 명시 실행 전용 스크립트.
3
+ *
4
+ * 저작(goal-author) 단계 그림자 1팔을 `planShadowArms` 일일 상한 안에서 «실제 호출»해
5
+ * 난이도·모델·예상/실제 비용·결과를 한 줄 원장 형식으로 stdout 에 출력한다.
6
+ * ⛔ 관측만 한다 — 라이브 LLM(Claude 구독) 호출 0 · 골/PR/착지 파일 쓰기 0.
7
+ * ⛔ 자동 런 훅과 다른 단계(implement·review·judge) 팔은 후속 조각이다.
8
+ *
9
+ * 사용(저장소 루트에서): bun scripts/shadow-authoring-probe.ts
10
+ */
11
+ import { probeAuthoringShadowArm } from '../src/self-implement/goal-author.js';
12
+
13
+ /** 명시 실행 probe — 생산 저작 probe 경로(probeAuthoringShadowArm ⊕ probeShadowArm ⊕ planShadowArms)를 그대로 지나 한 줄을 낸다. */
14
+ export function runShadowAuthoringProbe(authorRunId = `shadow-authoring-probe-${new Date().toISOString().slice(0, 10)}`): string {
15
+ return probeAuthoringShadowArm(authorRunId).line;
16
+ }
17
+
18
+ if (import.meta.main) {
19
+ console.log(runShadowAuthoringProbe());
20
+ }