elanous 0.2.41 → 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 (589) hide show
  1. package/apps/pwa/out/404/index.html +1 -1
  2. package/apps/pwa/out/404.html +1 -1
  3. package/apps/pwa/out/_next/static/{GiOLWmt4MqDZePubBjA4Q → X1ucLw10Cds_h614NoEnh}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/0244e5b5.6fc7670f8223d82d.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/{5159.115f1c2ca471386c.js → 1063.2fb8a9dc55c731f2.js} +1 -1
  6. package/apps/pwa/out/_next/static/chunks/1121.13ba7a8aff19df81.js +36 -0
  7. package/apps/pwa/out/_next/static/chunks/1147-5196d19c222051b3.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/{9048.212083de42d25be3.js → 1262.e804feb8fd1acb8c.js} +2 -2
  9. package/apps/pwa/out/_next/static/chunks/1319.b9b815ae0f181e06.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/1345-31427b833438f521.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/1455-530ca16c7e71baff.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/{4527.a69f2894d1bc9f4a.js → 1487.2f67b79444062da6.js} +1 -1
  13. package/apps/pwa/out/_next/static/chunks/1574.878c18ecc97b1bd4.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/1586.fafc33a9a5835cb7.js +32 -0
  15. package/apps/pwa/out/_next/static/chunks/1744.855fd60ae4c4a8c8.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/193.21a47a3e07ef6860.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/1945.7b54990d578c119b.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/2004.e839fda7ea5a738a.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/2044-dd681a5c08779ff8.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/2101.2248decb38829b03.js +286 -0
  21. package/apps/pwa/out/_next/static/chunks/2160.a097e7b138ae1c55.js +23 -0
  22. package/apps/pwa/out/_next/static/chunks/2300.41ea70ac1bef1446.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/2357-07b5eeb5fb12628d.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/2364.28652020695a6db3.js +34 -0
  25. package/apps/pwa/out/_next/static/chunks/2456.b6de0e1c05f28843.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/2526.c3e50ac1fec064bd.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/2549-cda50b716ff4250c.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2550-5f1e88f615568045.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/261-319078c954e40d66.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/{530.d26346e18089640a.js → 2690.0521c738b7749931.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/{4702.4a4adc1f16d7c93b.js → 2974.595d1e29d040cb6a.js} +1 -1
  32. package/apps/pwa/out/_next/static/chunks/{2263.e09b6edea33bad2d.js → 3207.a756c95e9ec8c3c8.js} +1 -1
  33. package/apps/pwa/out/_next/static/chunks/{9541.eb8e2f8480c7feca.js → 3285.89ee2f298682f5e5.js} +2 -2
  34. package/apps/pwa/out/_next/static/chunks/{9507.7026657a2f47eb64.js → 3347.f846266b5e5f3ede.js} +16 -16
  35. package/apps/pwa/out/_next/static/chunks/3447.06edc7b092021417.js +41 -0
  36. package/apps/pwa/out/_next/static/chunks/{1178.1aa9cad0e7695427.js → 3482.a9ae9d2ad6a5bd97.js} +1 -1
  37. package/apps/pwa/out/_next/static/chunks/3507.97e1977bc4d5fb0a.js +2 -0
  38. package/apps/pwa/out/_next/static/chunks/3510-9ca392698bfe4391.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/3541.772e5ce425aaa611.js +24 -0
  40. package/apps/pwa/out/_next/static/chunks/3741.9cf76acaa2ee5298.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/{1661.0dd330f3d89f73f3.js → 3837.a210d7d0db8d8abd.js} +1 -1
  42. package/apps/pwa/out/_next/static/chunks/3898-955c896f394217a6.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/3947-ef57acea76eaa8d2.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/{2fdf2931.b431eee4fb468bc3.js → 3d0154af.5331fec737ffe8c3.js} +1 -1
  45. package/apps/pwa/out/_next/static/chunks/4037-3b82ebc7d78afdc7.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/4043.6b91203436c9c86b.js +309 -0
  47. package/apps/pwa/out/_next/static/chunks/405.9022d4a478202b67.js +77 -0
  48. package/apps/pwa/out/_next/static/chunks/417-fb2cc9a9910e5968.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/4276.336d76d858fcdf1b.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/4302.6ced9aa4535de947.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/4320.583cbab64db6be02.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/4768-8eac183fd291ca53.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/4770.769c9cd9c7034eba.js +148 -0
  54. package/apps/pwa/out/_next/static/chunks/4895.65605ec3e6504e89.js +74 -0
  55. package/apps/pwa/out/_next/static/chunks/50061469.0326382241ef3b2d.js +122 -0
  56. package/apps/pwa/out/_next/static/chunks/{1115.434ae2ecb0ad986d.js → 5112.4b9f300124fab7b3.js} +3 -3
  57. package/apps/pwa/out/_next/static/chunks/5335.375ee922bcd5bc83.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/5342.023a2e729689b320.js +27 -0
  59. package/apps/pwa/out/_next/static/chunks/53c73f84.373b6160adfa89a4.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/5431-72a1dbde59400691.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/5440-4236de26c5ea5856.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/5486-b318c165f645d8ba.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/5506-c7d2ec25c62a7c94.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/5570-c66ecbea26b4e3b1.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/5696.ffca5aa259d8451e.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/5793-8c584aea2aa87560.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/5848-6de6b8c173351a90.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/5885.0a3e52c8faccec98.js +287 -0
  69. package/apps/pwa/out/_next/static/chunks/5999.94e3c83040200f31.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/5a7901d6-2d8d9b5587cf9042.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/6131.348b7d6bdcd267ce.js +226 -0
  72. package/apps/pwa/out/_next/static/chunks/6164.86133a0172611184.js +90 -0
  73. package/apps/pwa/out/_next/static/chunks/{2073.689dcb2680113d76.js → 6377.bc7df239edbff244.js} +1 -1
  74. package/apps/pwa/out/_next/static/chunks/6515.e07e8b2b8a707a87.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/6522-c005514f87d18982.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/{1800.f7ff51dc4978a958.js → 6632.0eac1c554f8f2ef3.js} +1 -1
  77. package/apps/pwa/out/_next/static/chunks/6642.648adb1767f39ecf.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/6848.138805a09d001d09.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/7049-c09e2b7a253a24f3.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/7072.1e00447d9fa39972.js +104 -0
  81. package/apps/pwa/out/_next/static/chunks/7106.64201c539c5e220c.js +94 -0
  82. package/apps/pwa/out/_next/static/chunks/7151-1f742b455986530e.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/7199.18d6b25e001154ce.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/{2527-5c636a43c7cc5dde.js → 7279-c199ea83f34338fa.js} +1 -1
  85. package/apps/pwa/out/_next/static/chunks/728b938d.ffaa7ad583d60e58.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/7309.171c3de54e0322f1.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/733.7a1ab3342e03306f.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/{385e4574.e9886ab32759eda3.js → 73d2eb0e.4966ad55293c801a.js} +1 -1
  89. package/apps/pwa/out/_next/static/chunks/{5471.1e611302ed3a7c53.js → 7411.9b4b8b747dc7b802.js} +2 -2
  90. package/apps/pwa/out/_next/static/chunks/7504.d5cf1e7cd89b1b08.js +24 -0
  91. package/apps/pwa/out/_next/static/chunks/7573-0fd4af62d65d4598.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/759.6f8267df4689c401.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/7825-668b046f72f14483.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/7852-1538768b6eb19388.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/7868.cff810430bb63c91.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/{2fe2c302.41b517f88d6fe502.js → 7a98bc80.fa935989e829f9c2.js} +1 -1
  97. package/apps/pwa/out/_next/static/chunks/8084-dfcc796874ab4e07.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/8138.6c10b2a356d52fca.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/{5574.f35accf4f132b29d.js → 8262.ddc03e06b733f184.js} +1 -1
  100. package/apps/pwa/out/_next/static/chunks/8266.6bf9b131b2584678.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/8403.7b81d5e64074a725.js +8 -0
  102. package/apps/pwa/out/_next/static/chunks/8470.1998cbd0866a7eac.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/8505.fd803c6c33b54cc3.js +79 -0
  104. package/apps/pwa/out/_next/static/chunks/{8050-9b364bdef8f71927.js → 8546-6f03f8ea34dd61d9.js} +1 -1
  105. package/apps/pwa/out/_next/static/chunks/{5947-135e26537d80a6b9.js → 8673-bab689a55f7e13b9.js} +3 -3
  106. package/apps/pwa/out/_next/static/chunks/8687-246fda8433d3477c.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/898-280022fb082936bc.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/9098-50eefc1844212e3d.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/9211-330b6736a63d4cb3.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/{5937.c2df9e3447e1f667.js → 9249.976f1e69a9d80754.js} +1 -1
  111. package/apps/pwa/out/_next/static/chunks/9255-cf88fceda5666645.js +1 -0
  112. package/apps/pwa/out/_next/static/chunks/9306-e523ba58bb8adc42.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/9324.a9866d717ba62cb6.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/9325.a8510e8aa87c8143.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/{1971-7efb628c471dc5a0.js → 9353-12bfff0c130104cf.js} +1 -1
  116. package/apps/pwa/out/_next/static/chunks/{7191.fdc955f7d96064a4.js → 9415.69e4a21c60c7d2db.js} +1 -1
  117. package/apps/pwa/out/_next/static/chunks/{1219.7e17964e0e7cd8e5.js → 9427.96aa912ddb5801e0.js} +16 -16
  118. package/apps/pwa/out/_next/static/chunks/9479-154a697d4b6e4e98.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/9540-65aa6ccc31b3fd78.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/9633.ce5bddc56784da72.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/{5964-7e21164a8f2cf0ce.js → 9656-374232558fd528d0.js} +1 -1
  122. package/apps/pwa/out/_next/static/chunks/{6552.a3840aa7da35ad68.js → 9832.f4a98c878ecf2c0a.js} +1 -1
  123. package/apps/pwa/out/_next/static/chunks/9865.f094440645f02d98.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/9a852a62-6192a6ea9346ced3.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-e772b4710df5d936.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/approvals/page-4361368e6a76b967.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-802f349fdd65248d.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/board/page-7f35513988a2a495.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/botlab/page-798121656540e33e.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/bots/page-20a089490affe98c.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/ceo/page-e5e4ec786d70e604.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/chat/page-d12fe5d0801430e4.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/consult/page-d8cf21022fecc026.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/control/page-21ce565c9c57ee97.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/decisions/page-b56563025f619c9b.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/design-check/page-52f14dd133da8348.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/editor/page-c0ed7fdcdba76633.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/exec/page-af8c3020cb520f15.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/field/page-72eda67af9f5e35c.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/healthz/route-e772b4710df5d936.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/inside/page-67a6620d31f0a46f.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/intake/page-f059363aa0880ec2.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/layout-f9f917d6a57f69fb.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/live/page-d3ae55eee2f0826c.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/live-run/page-0e3d7b9421b79dac.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/loops/page-ba738f7e233eb211.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/market/page-33438b98c74fd599.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-0b34d5fd990d793a.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/morning/page-0f291d3c8fd55a3a.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/not-found-3fced192be5279c1.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/observatory/page-04cb3cf182a7c1ea.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-6ccbd2ab23a21542.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-aa0518c48a5f4e56.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4b32226d341ad7d7.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3224fd8f6383b61f.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/page-efbc97459f2914e2.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/reflection/page-2fe487d88cd5d97c.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-48861dd1a858bab7.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/sessions/page-197d5d4d21d228ea.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a763ba89fbdd9c27.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/settings/page-7edb253510b1c136.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0c875702c61965ea.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/setup/layout-e772b4710df5d936.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/setup/page-e425c951151095f7.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/share/page-9c90e0aec4e27f77.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/showroom/page-733e09a02e727d42.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/tasks/page-4f899e14c34d77a1.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/term/page-bf067887b3307423.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/today/page-7af10868ff87d04e.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/trace/page-efe5304057aa11c7.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/vault/page-76bcd29aaa8a26c3.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-4cfd88e2273ce50f.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/app/workflows/page-0a1ddb47efba0a32.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2a176c707152c892.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-efe03031b8797fbf.js +1 -0
  176. package/apps/pwa/out/_next/static/chunks/{a0a01613.6d1cca322238d7fe.js → b450776b.3db34d8d965f82d3.js} +1 -1
  177. package/apps/pwa/out/_next/static/chunks/{c0c1a699.0ba79f97005965d4.js → e8543907.5b040a5f22a0b0e5.js} +7 -7
  178. package/apps/pwa/out/_next/static/chunks/framework-d0d2d673bfd8a8a9.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-4c9fceb2baa09cb7.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-app-b0ab8d8fc9a97556.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_app-7e2cc62a2e5cec96.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_error-22324935f753ec5e.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/webpack-9dc1bfa25334998a.js +1 -0
  184. package/apps/pwa/out/_next/static/css/{b9d8151e48998417.css → 62c858ad53854f47.css} +1 -1
  185. package/apps/pwa/out/approvals/index.html +1 -1
  186. package/apps/pwa/out/approvals/index.txt +22 -22
  187. package/apps/pwa/out/autopilot/index.html +1 -1
  188. package/apps/pwa/out/autopilot/index.txt +22 -22
  189. package/apps/pwa/out/board/index.html +1 -1
  190. package/apps/pwa/out/board/index.txt +22 -22
  191. package/apps/pwa/out/botlab/index.html +1 -1
  192. package/apps/pwa/out/botlab/index.txt +21 -21
  193. package/apps/pwa/out/bots/index.html +1 -1
  194. package/apps/pwa/out/bots/index.txt +21 -21
  195. package/apps/pwa/out/ceo/index.html +1 -1
  196. package/apps/pwa/out/ceo/index.txt +22 -22
  197. package/apps/pwa/out/chat/index.html +1 -1
  198. package/apps/pwa/out/chat/index.txt +21 -21
  199. package/apps/pwa/out/consult/index.html +1 -1
  200. package/apps/pwa/out/consult/index.txt +22 -22
  201. package/apps/pwa/out/control/index.html +1 -1
  202. package/apps/pwa/out/control/index.txt +22 -22
  203. package/apps/pwa/out/decisions/index.html +1 -1
  204. package/apps/pwa/out/decisions/index.txt +21 -21
  205. package/apps/pwa/out/design-check/index.html +1 -1
  206. package/apps/pwa/out/design-check/index.txt +22 -22
  207. package/apps/pwa/out/editor/index.html +1 -1
  208. package/apps/pwa/out/editor/index.txt +23 -24
  209. package/apps/pwa/out/exec/index.html +1 -1
  210. package/apps/pwa/out/exec/index.txt +22 -22
  211. package/apps/pwa/out/field/index.html +1 -1
  212. package/apps/pwa/out/field/index.txt +22 -22
  213. package/apps/pwa/out/healthz +1 -1
  214. package/apps/pwa/out/index.html +1 -1
  215. package/apps/pwa/out/index.txt +21 -21
  216. package/apps/pwa/out/inside/index.html +1 -1
  217. package/apps/pwa/out/inside/index.txt +22 -22
  218. package/apps/pwa/out/intake/index.html +1 -1
  219. package/apps/pwa/out/intake/index.txt +22 -22
  220. package/apps/pwa/out/live/index.html +1 -1
  221. package/apps/pwa/out/live/index.txt +23 -24
  222. package/apps/pwa/out/live-run/index.html +1 -1
  223. package/apps/pwa/out/live-run/index.txt +23 -24
  224. package/apps/pwa/out/loops/index.html +1 -1
  225. package/apps/pwa/out/loops/index.txt +21 -21
  226. package/apps/pwa/out/market/index.html +1 -1
  227. package/apps/pwa/out/market/index.txt +21 -21
  228. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  229. package/apps/pwa/out/missions/placeholder/index.txt +21 -21
  230. package/apps/pwa/out/morning/index.html +1 -1
  231. package/apps/pwa/out/morning/index.txt +22 -22
  232. package/apps/pwa/out/observatory/index.html +1 -1
  233. package/apps/pwa/out/observatory/index.txt +22 -22
  234. package/apps/pwa/out/ops/checklist/index.html +1 -1
  235. package/apps/pwa/out/ops/checklist/index.txt +21 -21
  236. package/apps/pwa/out/ops/release/index.html +1 -1
  237. package/apps/pwa/out/ops/release/index.txt +21 -21
  238. package/apps/pwa/out/ops/seats/index.html +1 -1
  239. package/apps/pwa/out/ops/seats/index.txt +21 -21
  240. package/apps/pwa/out/outputs/index.html +1 -1
  241. package/apps/pwa/out/outputs/index.txt +21 -21
  242. package/apps/pwa/out/reflection/index.html +1 -1
  243. package/apps/pwa/out/reflection/index.txt +22 -22
  244. package/apps/pwa/out/scheduler/index.html +1 -1
  245. package/apps/pwa/out/scheduler/index.txt +22 -22
  246. package/apps/pwa/out/sessions/index.html +1 -1
  247. package/apps/pwa/out/sessions/index.txt +21 -21
  248. package/apps/pwa/out/settings/devices/index.html +1 -1
  249. package/apps/pwa/out/settings/devices/index.txt +22 -22
  250. package/apps/pwa/out/settings/index.html +1 -1
  251. package/apps/pwa/out/settings/index.txt +22 -22
  252. package/apps/pwa/out/setup/done/index.html +1 -1
  253. package/apps/pwa/out/setup/done/index.txt +22 -22
  254. package/apps/pwa/out/setup/index.html +1 -1
  255. package/apps/pwa/out/setup/index.txt +22 -22
  256. package/apps/pwa/out/share/index.html +1 -1
  257. package/apps/pwa/out/share/index.txt +22 -22
  258. package/apps/pwa/out/showroom/index.html +1 -1
  259. package/apps/pwa/out/showroom/index.txt +21 -21
  260. package/apps/pwa/out/tasks/index.html +1 -1
  261. package/apps/pwa/out/tasks/index.txt +22 -22
  262. package/apps/pwa/out/term/index.html +1 -1
  263. package/apps/pwa/out/term/index.txt +22 -22
  264. package/apps/pwa/out/today/index.html +1 -1
  265. package/apps/pwa/out/today/index.txt +22 -22
  266. package/apps/pwa/out/trace/index.html +1 -1
  267. package/apps/pwa/out/trace/index.txt +22 -22
  268. package/apps/pwa/out/vault/index.html +1 -1
  269. package/apps/pwa/out/vault/index.txt +22 -22
  270. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  271. package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
  272. package/apps/pwa/out/workflows/index.html +1 -1
  273. package/apps/pwa/out/workflows/index.txt +22 -22
  274. package/apps/pwa/out/workspace/index.html +1 -1
  275. package/apps/pwa/out/workspace/index.txt +22 -22
  276. package/apps/pwa/out/worktrees/index.html +1 -1
  277. package/apps/pwa/out/worktrees/index.txt +22 -22
  278. package/docs/goal-context/04-mock-scenarios.md +13 -0
  279. package/graphs/catalog/catalog.yaml +1 -1
  280. package/graphs/heal/heal-loop.yaml +1 -0
  281. package/graphs/overlays/stop-heal-ladder.yaml +113 -0
  282. package/graphs/units/verbs/recipes.yaml +11 -11
  283. package/graphs/work/research-report.yaml +10 -2
  284. package/package.json +1 -1
  285. package/scripts/heal/run-role.ts +99 -5
  286. package/scripts/land-global-checks.ts +6 -1
  287. package/scripts/lib/decompose-ab.ts +1 -0
  288. package/scripts/lib/fixtures/tui-regress/help-insufficient-commands.txt +15 -0
  289. package/scripts/lib/fixtures/tui-regress/help-live.txt +42 -0
  290. package/scripts/lib/fixtures/tui-regress/help-missing-keys.txt +21 -0
  291. package/scripts/lib/fixtures/tui-regress/help-original.txt +51 -0
  292. package/scripts/lib/fixtures/tui-regress/help-shifted-input.txt +26 -0
  293. package/scripts/lib/tui-regress-checks.ts +33 -13
  294. package/scripts/release-loop/gate-baseline-receipt.ts +47 -9
  295. package/scripts/release-loop/gate-image-pin.ts +81 -0
  296. package/scripts/release-loop/gate-node-sim.ts +207 -0
  297. package/scripts/release-loop/gate-node.ts +285 -46
  298. package/scripts/release-loop/gate-pod-image-sticky-plan.ts +99 -0
  299. package/scripts/release-loop/gate-radius-live.ts +97 -13
  300. package/scripts/release-loop/gate-shard-dispatch.ts +43 -0
  301. package/scripts/release-loop/gate-train-receipts.ts +122 -0
  302. package/scripts/release-loop/resume-release.ts +83 -8
  303. package/scripts/ux-sim.ts +18 -1
  304. package/scripts/warm-pool-provider-config.ts +263 -0
  305. package/skills/omni-crawl/SKILL.md +5 -2
  306. package/skills/omni-crawl/scripts/main.ts +84 -23
  307. package/src/acp/core-turn-bridge.ts +13 -0
  308. package/src/agent/registry.ts +2 -0
  309. package/src/agent/task-notification.ts +25 -5
  310. package/src/agent/types.ts +6 -0
  311. package/src/agent-mission/mission-cli.ts +24 -0
  312. package/src/agent-mission/mission-out.ts +595 -0
  313. package/src/agent-mission/tool-install.ts +88 -2
  314. package/src/agent-substrate/context-strategy.ts +4 -4
  315. package/src/agent-substrate/self-review-cli.ts +178 -13
  316. package/src/auto-research/research-turn.ts +123 -50
  317. package/src/autopilot/pr-manager.ts +38 -7
  318. package/src/autopilot/pty-control-loop.ts +2 -0
  319. package/src/capture/tui-self-report.ts +22 -6
  320. package/src/chat/index.ts +1 -1
  321. package/src/cli/fixtures/review-loop-breaker/opwaiver-32646-1694ccc7a170.json +1 -0
  322. package/src/cli/fixtures/review-loop-breaker/opwaiver-32853-bd8f73595c83.json +1 -0
  323. package/src/cli/fixtures/review-loop-breaker/opwaiver-33097-1cc85900413c.json +1 -0
  324. package/src/cli/fixtures/review-loop-breaker/opwaiver-33106-972f31e4a3ca.json +1 -0
  325. package/src/cli/fixtures/review-loop-breaker/opwaiver-33113-3ab51f05968f.json +1 -0
  326. package/src/cli/fixtures/review-loop-breaker/pr-33097-1cc85900413c.json +1 -0
  327. package/src/cli/pr-land-heavy-remote.ts +32 -0
  328. package/src/cli/pr-land-overlap-evidence.ts +100 -1
  329. package/src/cli/pr-land-pod-shadow-job.ts +60 -0
  330. package/src/cli/release-checklist-read-cli.ts +20 -10
  331. package/src/cli/research-cli.ts +4 -5
  332. package/src/cli/tasks-cli.ts +7 -0
  333. package/src/compact/auto.ts +8 -1
  334. package/src/control-plane/ledger.ts +52 -2
  335. package/src/control-plane/server.ts +16 -2
  336. package/src/core-turn/run-core-turn.ts +311 -90
  337. package/src/core-turn/run-goal-loop.ts +18 -0
  338. package/src/core-turn/types.ts +2 -0
  339. package/src/dashboard/classic-sidetab-mission-job.ts +58 -0
  340. package/src/dashboard/classic-sidetab-mission.ts +185 -0
  341. package/src/dashboard/index.ts +149 -15
  342. package/src/dashboard/input/agent-wake.ts +23 -1
  343. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +9 -0
  344. package/src/dashboard/mission-cli-consumer.ts +153 -0
  345. package/src/dashboard/seat-boot-preamble.ts +171 -1
  346. package/src/dashboard/side-basic-runtime.ts +54 -1
  347. package/src/dashboard/sidetab-runtime.ts +90 -1
  348. package/src/dashboard/skill-catalog-preamble.ts +101 -0
  349. package/src/dashboard/skill-detection-runtime.ts +55 -15
  350. package/src/dashboard/slash-runtime/dashboard-handlers.ts +15 -0
  351. package/src/dashboard/turn-preamble.ts +53 -15
  352. package/src/dashboard/turn-prompt-runtime.ts +4 -2
  353. package/src/graph-runner/graph-cli.ts +23 -7
  354. package/src/graph-runner/graph-tick.ts +10 -2
  355. package/src/graph-runner/runner.ts +1 -1
  356. package/src/grok/agent-search.ts +14 -4
  357. package/src/grok/credential.ts +28 -11
  358. package/src/harness/harness-pod-dispatch.ts +81 -15
  359. package/src/harness/harness-stop.ts +155 -11
  360. package/src/index.ts +34 -5
  361. package/src/instance/spawn-elanous.ts +6 -4
  362. package/src/llm.ts +166 -8
  363. package/src/loops/orchestrator/coordinator-shadow.ts +226 -0
  364. package/src/loops/orchestrator/tick.ts +13 -0
  365. package/src/maturity/feature-maturity.ts +1 -1
  366. package/src/native-tool-catalog.ts +2 -2
  367. package/src/nexus/api/harness-traversal.ts +11 -0
  368. package/src/pr-tool/census.ts +3 -2
  369. package/src/pty-shell/tui-run-state.ts +188 -2
  370. package/src/release-loop/checklist-target-path-hint.ts +13 -0
  371. package/src/release-loop/checklist.ts +9 -1
  372. package/src/repl/index.ts +43 -2
  373. package/src/research/axis-fanout.ts +121 -16
  374. package/src/research/bloom-exemplars.ts +3 -1
  375. package/src/research/gap-followup.ts +13 -1
  376. package/src/research/plugin-discovery.ts +36 -14
  377. package/src/research-bridge/invoke.ts +22 -5
  378. package/src/research-bridge/types.ts +2 -0
  379. package/src/self-dev/decompose.ts +119 -5
  380. package/src/self-dev/launch-preflight-text.ts +5 -6
  381. package/src/self-dev/orchestrate.ts +12 -2
  382. package/src/self-dev/review-provider-fallback.ts +9 -2
  383. package/src/self-dev/run-supervisor.ts +42 -4
  384. package/src/self-dev/run-types.ts +4 -0
  385. package/src/self-implement/doctor-visit.ts +88 -0
  386. package/src/self-implement/goal-author.ts +326 -3
  387. package/src/self-implement/graph-overlay-yaml.ts +7 -0
  388. package/src/self-implement/headless-elanous-driver.ts +31 -2
  389. package/src/self-implement/heal-intake.ts +112 -4
  390. package/src/self-implement/heal-triage.ts +20 -0
  391. package/src/self-implement/host-regate.ts +27 -4
  392. package/src/self-implement/merge-train.ts +5 -0
  393. package/src/self-implement/orchestrator.ts +126 -11
  394. package/src/self-implement/regate-detail.ts +38 -1
  395. package/src/self-implement/rework-child-provider.ts +25 -0
  396. package/src/self-implement/run-chart.ts +119 -0
  397. package/src/self-implement/run-ledger.ts +112 -11
  398. package/src/self-implement/run-stop.ts +188 -4
  399. package/src/self-implement/stop-autoheal.ts +13 -0
  400. package/src/session/chat.ts +38 -4
  401. package/src/session-runtime/index.ts +98 -4
  402. package/src/session-runtime/tier-flip.ts +3 -3
  403. package/src/sidetab-core/model.ts +19 -5
  404. package/src/sidetab-core/visible-agent-mission.ts +186 -0
  405. package/src/skills/name-match.ts +20 -0
  406. package/src/skills/tools/agent.ts +30 -2
  407. package/src/skills/tools/omni-search.ts +67 -5
  408. package/src/task-agent/pod-distributor-entry.ts +3 -0
  409. package/src/task-agent/task-hand.ts +27 -0
  410. package/src/task-orchestrator/surfaces/pod-distributor.ts +48 -0
  411. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +38 -1
  412. package/src/task-orchestrator/surfaces/pod-si-warm-entry.ts +33 -6
  413. package/src/task-orchestrator/surfaces/pod-source-bundle.ts +45 -9
  414. package/src/task-orchestrator/surfaces/pod-source-receive.ts +163 -13
  415. package/src/task-orchestrator/surfaces/pod-source-spec.ts +3 -3
  416. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +209 -39
  417. package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +80 -3
  418. package/src/task-orchestrator/surfaces/self-implement-pod.ts +165 -45
  419. package/src/tokens.ts +35 -2
  420. package/src/tool-runtime/agent-runtime.ts +1 -0
  421. package/src/tool-runtime/elanous-skills-list-runtime.ts +5 -1
  422. package/src/tool-runtime/skill-exec-runtime.ts +33 -13
  423. package/src/tool-runtime/types.ts +1 -0
  424. package/src/tui/entry.ts +14 -4
  425. package/src/tui/sidetab/attach.ts +47 -0
  426. package/src/tui-client/local-chat-turn.ts +26 -13
  427. package/src/tui.ts +22 -0
  428. package/src/ux-sim/fake-kubectl.ts +37 -3
  429. package/src/ux-sim/fault-pool-cap.ts +11 -6
  430. package/src/ux-sim/mock-scenario-catalog.json +41 -0
  431. package/src/ux-sim/mock-scenario-catalog.ts +156 -0
  432. package/src/ux-sim/scenario.ts +116 -23
  433. package/src/version/packed-revision.json +1 -1
  434. package/src/voice/voice-tts-singleton.ts +18 -3
  435. package/src/web-search/grok.ts +5 -9
  436. package/apps/pwa/out/_next/static/chunks/1121.667cec0bcdbb3fdb.js +0 -36
  437. package/apps/pwa/out/_next/static/chunks/1197-54da59d250a6e476.js +0 -1
  438. package/apps/pwa/out/_next/static/chunks/1245.1cfd13b26dbd71d6.js +0 -287
  439. package/apps/pwa/out/_next/static/chunks/1494-f93046ff5c5a124d.js +0 -1
  440. package/apps/pwa/out/_next/static/chunks/1609.2b312797599b8e45.js +0 -1
  441. package/apps/pwa/out/_next/static/chunks/167-0c121e4b47e0f9da.js +0 -1
  442. package/apps/pwa/out/_next/static/chunks/1700.cdc2bf8cc685e221.js +0 -90
  443. package/apps/pwa/out/_next/static/chunks/1703-78e46acad29f3cfc.js +0 -1
  444. package/apps/pwa/out/_next/static/chunks/1765.679b29625c8e88d4.js +0 -77
  445. package/apps/pwa/out/_next/static/chunks/1999-8de7b7bb36801913.js +0 -1
  446. package/apps/pwa/out/_next/static/chunks/2198-557d49184104d165.js +0 -1
  447. package/apps/pwa/out/_next/static/chunks/221-440ce67fa9f5c839.js +0 -1
  448. package/apps/pwa/out/_next/static/chunks/2427-8ed8e98624cbd664.js +0 -1
  449. package/apps/pwa/out/_next/static/chunks/2457-725c5827bc003795.js +0 -1
  450. package/apps/pwa/out/_next/static/chunks/2491-2718b15a5ccb75a5.js +0 -1
  451. package/apps/pwa/out/_next/static/chunks/2692-1027cb16fe4ccf49.js +0 -1
  452. package/apps/pwa/out/_next/static/chunks/2743.5d3f76948be32b34.js +0 -41
  453. package/apps/pwa/out/_next/static/chunks/2773.0e80b9d325fb5635.js +0 -32
  454. package/apps/pwa/out/_next/static/chunks/2873-0f3cb56c8e3528ea.js +0 -1
  455. package/apps/pwa/out/_next/static/chunks/3097.864e92bdaa2f4d15.js +0 -79
  456. package/apps/pwa/out/_next/static/chunks/3136-d6870ba63300a93e.js +0 -1
  457. package/apps/pwa/out/_next/static/chunks/3173-0289113d434e83b7.js +0 -1
  458. package/apps/pwa/out/_next/static/chunks/3230-49298c3b73ef2268.js +0 -1
  459. package/apps/pwa/out/_next/static/chunks/325-dd9208a1a0ab7ce4.js +0 -1
  460. package/apps/pwa/out/_next/static/chunks/3283.528f430dd043fb4e.js +0 -74
  461. package/apps/pwa/out/_next/static/chunks/3295.cc8cb13b8f0dae1a.js +0 -1
  462. package/apps/pwa/out/_next/static/chunks/3688-86e3b88abf18cb3b.js +0 -1
  463. package/apps/pwa/out/_next/static/chunks/3804-7220467a5473a8df.js +0 -1
  464. package/apps/pwa/out/_next/static/chunks/3808.3e1e247ce7bdf889.js +0 -1
  465. package/apps/pwa/out/_next/static/chunks/3927.b5473ad109da44c5.js +0 -1
  466. package/apps/pwa/out/_next/static/chunks/3934-98ea94832e194796.js +0 -1
  467. package/apps/pwa/out/_next/static/chunks/4133.2056bb4d78f621af.js +0 -1
  468. package/apps/pwa/out/_next/static/chunks/4156.ba4de4a3664f04b4.js +0 -1
  469. package/apps/pwa/out/_next/static/chunks/4297-239ca82f293faab0.js +0 -1
  470. package/apps/pwa/out/_next/static/chunks/4320-dcc5a7734126f4c9.js +0 -1
  471. package/apps/pwa/out/_next/static/chunks/4434.94b4c552a2d91e69.js +0 -148
  472. package/apps/pwa/out/_next/static/chunks/4579.03e4092f73950965.js +0 -226
  473. package/apps/pwa/out/_next/static/chunks/4667.97e31aa38c95c475.js +0 -309
  474. package/apps/pwa/out/_next/static/chunks/4815.481501c57b3568d9.js +0 -1
  475. package/apps/pwa/out/_next/static/chunks/487-d16e698ef14662d9.js +0 -1
  476. package/apps/pwa/out/_next/static/chunks/4888-7a38bf522c9cc6fe.js +0 -1
  477. package/apps/pwa/out/_next/static/chunks/5034-30e429d6bb086881.js +0 -1
  478. package/apps/pwa/out/_next/static/chunks/5040-35c494e60dadd97d.js +0 -1
  479. package/apps/pwa/out/_next/static/chunks/50c6ab1e.81a96e419350909d.js +0 -1
  480. package/apps/pwa/out/_next/static/chunks/5154.4b6a836179356f30.js +0 -94
  481. package/apps/pwa/out/_next/static/chunks/5228-a8200df486278f2d.js +0 -1
  482. package/apps/pwa/out/_next/static/chunks/5379.97af106d09566f69.js +0 -8
  483. package/apps/pwa/out/_next/static/chunks/53aeb4ee-51c92f8d683f5220.js +0 -1
  484. package/apps/pwa/out/_next/static/chunks/5479-b6372e762719f92f.js +0 -1
  485. package/apps/pwa/out/_next/static/chunks/5534-699c5cbcbc96c5d7.js +0 -1
  486. package/apps/pwa/out/_next/static/chunks/5701.9c4e8f250312eeee.js +0 -24
  487. package/apps/pwa/out/_next/static/chunks/5744.b9415e18ad623100.js +0 -23
  488. package/apps/pwa/out/_next/static/chunks/5892.2526d962339662d8.js +0 -1
  489. package/apps/pwa/out/_next/static/chunks/5941.8210cc93c9202c0a.js +0 -286
  490. package/apps/pwa/out/_next/static/chunks/5975.4004f820ae3cecd5.js +0 -1
  491. package/apps/pwa/out/_next/static/chunks/6142-7ccd48795d7abd55.js +0 -1
  492. package/apps/pwa/out/_next/static/chunks/6160.bf9177b8c1bfb243.js +0 -1
  493. package/apps/pwa/out/_next/static/chunks/6398.71ba0690c8027555.js +0 -27
  494. package/apps/pwa/out/_next/static/chunks/6492.2bd5c10edc1a064e.js +0 -1
  495. package/apps/pwa/out/_next/static/chunks/6736.1e36d3ac5fa5e41d.js +0 -24
  496. package/apps/pwa/out/_next/static/chunks/6874.d5cf7eb8edb2cabe.js +0 -2
  497. package/apps/pwa/out/_next/static/chunks/6900.3ccbe069bdd4556a.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/71327eb4.add50b365f94bf3a.js +0 -122
  499. package/apps/pwa/out/_next/static/chunks/7136.deca3e3a2ddaa374.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/7139-ee9f8ed2677a40b3.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/7423-e0aa5d9e2962120e.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/7453.afc1ea7e05d56a8c.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/7503-5e6a3d7fe67e4a0c.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/7761-476fd7bf768abd34.js +0 -1
  505. package/apps/pwa/out/_next/static/chunks/7975.eafb12097a0a9e62.js +0 -1
  506. package/apps/pwa/out/_next/static/chunks/8152.64e253c35cccfaae.js +0 -1
  507. package/apps/pwa/out/_next/static/chunks/8190.620f0231b3924bcb.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/83184dcd-0ee6247ff41eb3f2.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/8448.45b6b1f1a99eeb35.js +0 -104
  510. package/apps/pwa/out/_next/static/chunks/8464-e24f10451b658aac.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/8477.e4c944bb3aa5d2f3.js +0 -1
  512. package/apps/pwa/out/_next/static/chunks/8710.ca385257d5ca422e.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/8830.627ccf294ec2465c.js +0 -1
  514. package/apps/pwa/out/_next/static/chunks/8880.64c445592f04acaf.js +0 -1
  515. package/apps/pwa/out/_next/static/chunks/8981.c706964c8ba27aff.js +0 -1
  516. package/apps/pwa/out/_next/static/chunks/9053.bd0fffaff8d3f836.js +0 -1
  517. package/apps/pwa/out/_next/static/chunks/9161-c66e723d7ec9d682.js +0 -1
  518. package/apps/pwa/out/_next/static/chunks/9185.1ecdf9b65c0bb684.js +0 -1
  519. package/apps/pwa/out/_next/static/chunks/9356.fccd5f58086b4ff5.js +0 -1
  520. package/apps/pwa/out/_next/static/chunks/9446.dc0406109371d329.js +0 -1
  521. package/apps/pwa/out/_next/static/chunks/9506.dcf25d467a42627e.js +0 -1
  522. package/apps/pwa/out/_next/static/chunks/9577.ed7582186efa5482.js +0 -1
  523. package/apps/pwa/out/_next/static/chunks/9579.4a8169600fff2058.js +0 -1
  524. package/apps/pwa/out/_next/static/chunks/9642-ff2e039eb62e3b92.js +0 -1
  525. package/apps/pwa/out/_next/static/chunks/9659-8f2323348113ad80.js +0 -1
  526. package/apps/pwa/out/_next/static/chunks/9724.4c5623af2db43153.js +0 -34
  527. package/apps/pwa/out/_next/static/chunks/9834.63ab2bbc3153172e.js +0 -1
  528. package/apps/pwa/out/_next/static/chunks/9869.81a1e24ca682d5a0.js +0 -1
  529. package/apps/pwa/out/_next/static/chunks/addc2b32.8c4ff8f0aefe987d.js +0 -1
  530. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-8d9d76ea322d855c.js +0 -1
  531. package/apps/pwa/out/_next/static/chunks/app/approvals/page-ed2c4e59f9f1fe21.js +0 -1
  532. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-2dc99e03c5d76ca0.js +0 -1
  533. package/apps/pwa/out/_next/static/chunks/app/board/page-b5a3a788330e920f.js +0 -1
  534. package/apps/pwa/out/_next/static/chunks/app/botlab/page-14949db884bfa672.js +0 -1
  535. package/apps/pwa/out/_next/static/chunks/app/bots/page-ea76c85b36388a97.js +0 -1
  536. package/apps/pwa/out/_next/static/chunks/app/ceo/page-01716d20af62d325.js +0 -1
  537. package/apps/pwa/out/_next/static/chunks/app/chat/page-62baf6881a64b1e9.js +0 -1
  538. package/apps/pwa/out/_next/static/chunks/app/consult/page-f89ba590c2af8afa.js +0 -1
  539. package/apps/pwa/out/_next/static/chunks/app/control/page-22f8b0f78d9637e5.js +0 -1
  540. package/apps/pwa/out/_next/static/chunks/app/decisions/page-817f9d7e6820be80.js +0 -1
  541. package/apps/pwa/out/_next/static/chunks/app/design-check/page-ffb9bc0d2650249a.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/app/editor/page-f6d0e64873c0f629.js +0 -1
  543. package/apps/pwa/out/_next/static/chunks/app/exec/page-8bfa0d4ba23a209d.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/app/field/page-f5060462249e32c7.js +0 -1
  545. package/apps/pwa/out/_next/static/chunks/app/healthz/route-8d9d76ea322d855c.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/app/inside/page-b2049f8b1ef79117.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/app/intake/page-1446c5fa93907f11.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/app/layout-b48a7a07c5204156.js +0 -1
  549. package/apps/pwa/out/_next/static/chunks/app/live/page-e974b8c4ca47614c.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/app/live-run/page-8a919b869bd28594.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/app/loops/page-d31b8de6bfcd72ed.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/app/market/page-64765f817909db29.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-d128c8569a36ee9b.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/app/morning/page-b2679d357e9b9270.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/app/not-found-6fabec98a9e7b269.js +0 -1
  556. package/apps/pwa/out/_next/static/chunks/app/observatory/page-048f30149dcf1349.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-9113181d6b5a2725.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-40f9e19149024298.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-1e7a755a13227611.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3c8f8b67a203cf07.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/app/page-18032f5437215062.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/app/reflection/page-da3b739192fd0ee3.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d332545571bd6fd1.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/app/sessions/page-5237aa5b3f3b8ec5.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a142dfb0331a05f5.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/app/settings/page-d16e56c3989c147e.js +0 -1
  567. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-6b6593600869e2cd.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/app/setup/layout-8d9d76ea322d855c.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/app/setup/page-29e8b8a8396b55cb.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/app/share/page-b3414d856a1a7e2f.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/app/showroom/page-c3affa52b7ce961b.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f21dddf9f95b6fab.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/app/term/page-f5a30a24745bf088.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/app/today/page-8c8569a09b4d4e34.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/app/trace/page-2ce32722c0243483.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/app/vault/page-4767b0d9c0a98c6c.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-116ee1339c387a75.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/app/workflows/page-35e8c50ca4ef7ffc.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/app/workspace/page-52e17113356bb891.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-7458ae05ed2ac1a0.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/b2271e54.c1de3347976e90b0.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/framework-ad6c9dd524ad6ef8.js +0 -1
  583. package/apps/pwa/out/_next/static/chunks/main-app-2ddd1f2b44d6aa59.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/main-e3d82bdc358d20c5.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/pages/_app-71d5aa3896e1655f.js +0 -1
  586. package/apps/pwa/out/_next/static/chunks/pages/_error-03ffc54e00438546.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/webpack-56bb89af5a611d0b.js +0 -1
  588. package/apps/pwa/out/_next/static/css/d0a48dc099ed86cf.css +0 -1
  589. /package/apps/pwa/out/_next/static/{GiOLWmt4MqDZePubBjA4Q → X1ucLw10Cds_h614NoEnh}/_ssgManifest.js +0 -0
@@ -0,0 +1,99 @@
1
+ interface StickyGatePool { id?: string | null; available: boolean; imageDigest: string | null }
2
+ interface StickyGateShard { id?: string | null; files?: string[] | null }
3
+ interface StickyGateReceipt { shardId: string; status: 'complete' | 'stalled'; pool?: string | null; imageDigest: string }
4
+ interface StickyGateShardAction {
5
+ shardId: string; files: string[]; kind: 'reuse' | 'run' | 'unmeasured'; pool?: string; imageDigest?: string;
6
+ }
7
+ interface StickyGatePlan {
8
+ mode: 'full' | 'partial' | 'HOLD'; pinnedDigest: string | null; excludedPools: string[];
9
+ shards: StickyGateShardAction[]; reason?: string;
10
+ }
11
+ interface StickyGatePlanInput {
12
+ phase: 'start' | 'resume'; pools: readonly StickyGatePool[]; shards: readonly StickyGateShard[];
13
+ pinnedDigest?: string | null; receipts?: readonly StickyGateReceipt[];
14
+ }
15
+
16
+ const DIGEST = /^sha256:[a-f0-9]{64}$/i;
17
+ const validDigest = (value: string | null | undefined): value is string => typeof value === 'string' && DIGEST.test(value);
18
+ const normalizeDigest = (value: string | null | undefined): string | null => validDigest(value) ? value.toLowerCase() : null;
19
+ const validPoolIdentity = (value: string | null | undefined): value is string => typeof value === 'string' && value.trim().length > 0;
20
+
21
+ function hold(input: StickyGatePlanInput, pinnedDigest: string | null, reason: string, excludedPools: string[] = []): StickyGatePlan {
22
+ return { mode: 'HOLD', pinnedDigest, excludedPools, reason,
23
+ shards: input.shards.map((shard, index) => ({
24
+ shardId: validPoolIdentity(shard?.id) ? shard.id : `unmeasured-${index}`,
25
+ files: Array.isArray(shard?.files) ? [...shard.files] : [], kind: 'unmeasured',
26
+ })) };
27
+ }
28
+
29
+ function isValidPool(pool: StickyGatePool | null | undefined): pool is StickyGatePool & { id: string } {
30
+ return !!pool && validPoolIdentity(pool.id);
31
+ }
32
+
33
+ function isValidShard(shard: StickyGateShard | null | undefined): shard is StickyGateShard & { id: string; files: string[] } {
34
+ return !!shard && validPoolIdentity(shard.id) && Array.isArray(shard.files) && shard.files.length > 0
35
+ && shard.files.every((file) => typeof file === 'string') && new Set(shard.files).size === shard.files.length;
36
+ }
37
+
38
+ /**
39
+ * Deterministic E4 planning simulation. A production caller must persist the chosen digest at run start
40
+ * and pass it unchanged on resume; this module itself never runs a host command or pulls an image.
41
+ */
42
+ export function planStickyGateImages(input: StickyGatePlanInput): StickyGatePlan {
43
+ if (!input.shards.length || !input.pools.length) return hold(input, null, 'empty pool or shard inventory');
44
+ const pools = input.pools.filter(isValidPool);
45
+ const validShards = input.shards.filter(isValidShard);
46
+ if (pools.length !== input.pools.length || new Set(pools.map((pool) => pool.id)).size !== pools.length
47
+ || validShards.length !== input.shards.length || new Set(validShards.map((shard) => shard.id)).size !== validShards.length)
48
+ return hold(input, null, 'invalid pool or shard identity');
49
+
50
+ let pinnedDigest: string | null = null;
51
+ if (input.phase === 'resume') {
52
+ pinnedDigest = normalizeDigest(input.pinnedDigest);
53
+ if (!pinnedDigest) return hold(input, null, 'run image digest missing');
54
+ } else if (input.pinnedDigest !== undefined && input.pinnedDigest !== null) {
55
+ pinnedDigest = normalizeDigest(input.pinnedDigest);
56
+ if (!pinnedDigest) return hold(input, null, 'invalid pinned image digest');
57
+ } else {
58
+ pinnedDigest = pools.map((pool) => pool.available ? normalizeDigest(pool.imageDigest) : null)
59
+ .find((digest): digest is string => digest !== null) ?? null;
60
+ if (!pinnedDigest) return hold(input, null, 'no available pool has a valid image digest');
61
+ }
62
+
63
+ const excludedPools = pools.filter((pool) => !pool.available || normalizeDigest(pool.imageDigest) !== pinnedDigest)
64
+ .map((pool) => pool.id);
65
+ const eligiblePools = pools.filter((pool) => pool.available && normalizeDigest(pool.imageDigest) === pinnedDigest);
66
+ if (!eligiblePools.length) return hold(input, pinnedDigest, 'no available pool has the pinned digest', excludedPools);
67
+
68
+ const receipts = input.receipts ?? [];
69
+ const shardIds = new Set(validShards.map((shard) => shard.id));
70
+ if (receipts.some((receipt) => !shardIds.has(receipt.shardId))
71
+ || new Set(receipts.map((receipt) => receipt.shardId)).size !== receipts.length)
72
+ return hold(input, pinnedDigest, 'receipt inventory does not match the shard plan', excludedPools);
73
+
74
+ const receiptByShard = new Map(receipts.map((receipt) => [receipt.shardId, receipt]));
75
+ const completedReceipts = input.phase === 'resume' ? receipts.filter((receipt) => receipt.status === 'complete') : [];
76
+ const mismatchedCompleteDigest = completedReceipts.some((receipt) => normalizeDigest(receipt.imageDigest) !== pinnedDigest);
77
+ const missingCompletePool = completedReceipts.some((receipt) => !validPoolIdentity(receipt.pool));
78
+ const mixedCompleteReceipt = mismatchedCompleteDigest || missingCompletePool;
79
+ const fullSweep = input.phase === 'start' || mixedCompleteReceipt
80
+ || !validShards.some((shard) => receiptByShard.get(shard.id)?.status === 'complete');
81
+ const mode: StickyGatePlan['mode'] = fullSweep ? 'full' : 'partial';
82
+ const shards: StickyGateShardAction[] = [];
83
+ let nextPool = 0;
84
+ for (const shard of validShards) {
85
+ const receipt = receiptByShard.get(shard.id);
86
+ const canReuse = input.phase === 'resume' && !fullSweep && receipt?.status === 'complete'
87
+ && normalizeDigest(receipt.imageDigest) === pinnedDigest && validPoolIdentity(receipt.pool);
88
+ if (canReuse) {
89
+ shards.push({ shardId: shard.id, files: [...shard.files], kind: 'reuse', pool: receipt.pool ?? undefined, imageDigest: pinnedDigest });
90
+ continue;
91
+ }
92
+ const pool = eligiblePools[nextPool++ % eligiblePools.length]!;
93
+ shards.push({ shardId: shard.id, files: [...shard.files], kind: 'run', pool: pool.id, imageDigest: pinnedDigest });
94
+ }
95
+
96
+ return { mode, pinnedDigest, excludedPools, shards,
97
+ ...(mixedCompleteReceipt ? { reason: mismatchedCompleteDigest
98
+ ? 'completed receipts contain a different image digest' : 'completed receipt is missing pool identity' } : {}) };
99
+ }
@@ -5,9 +5,10 @@ import { join } from 'node:path';
5
5
  import { changedFilesBetween, planPartialRegate, testFileOfId, type PartialPlan } from './gate-partial.js';
6
6
  import { CODE_FILE, gitClosureTree, testsReachingChanged } from './gate-closure.js';
7
7
  import { versionOnlyBumps } from './gate-radius-shadow.js';
8
- import { gateAssignableTestsAtCommit, gateBaselineReceipt, type GateBaselineReceipt } from './gate-baseline-receipt.js';
9
- import { parsePodPool, remoteImageId } from '../../src/task-orchestrator/surfaces/pod-pool.js';
8
+ import { gateAssignableTestsAtCommit, gateBaselineReceipt, gateEnvFingerprint, type GateBaselineReceipt, type GatePodRuntimeIdentity } from './gate-baseline-receipt.js';
9
+ import { defaultRemoteRun, NODE_REGISTRY, parsePodPool } from '../../src/task-orchestrator/surfaces/pod-pool.js';
10
10
  import { podCommandImageFor } from '../../src/task-orchestrator/surfaces/pod-command-job.js';
11
+ import { gateTestBaseline } from '../lib/gate-test-env.js';
11
12
  import { SPAWN_SYNC_MAX_BUFFER } from '../../src/util/spawn-sync-output.js';
12
13
 
13
14
  const SHA = /^[0-9a-f]{40}$/i;
@@ -27,18 +28,91 @@ export function hardRehearsalGateArgs(version: string, runId: string, commit: st
27
28
 
28
29
  export interface LiveRadiusDecision { plan: PartialPlan | null; reason: string; rehearsalRunId?: string }
29
30
 
30
- /** Read the image ID on every candidate host before deciding. A later Job must still prove its actual image. */
31
- export function preflightGateImageDigest(pool: string): string | null {
31
+ export interface GatePodExecutionIdentity extends GatePodRuntimeIdentity { imageDigest: string }
32
+
33
+ const shellQuote = (value: string) => `'${value.replace(/'/g, `'\\''`)}'`;
34
+
35
+ /** Registry v2 manifest digest is the identity Kubernetes reports in containerStatuses.imageID. */
36
+ export function parseGateRepoDigestResponse(headers: string): string | null {
37
+ const match = /^docker-content-digest:\s*(sha256:[0-9a-f]{64})\s*$/im.exec(headers);
38
+ return match?.[1]?.toLowerCase() ?? null;
39
+ }
40
+
41
+ export interface GateRepoDigestProbeRequest { url: string; acceptHeader: string; command: string }
42
+
43
+ export function gateRepoDigestProbeRequest(registry: string, image: string, commit: string): GateRepoDigestProbeRequest | null {
44
+ const port = /:(\d+)$/.exec(registry)?.[1];
45
+ if (!port || !SHA.test(commit)) return null;
46
+ const imageName = image.startsWith('elanous-harness-lite:') ? 'elanous-harness-lite' : 'elanous-harness';
47
+ const tag = commit.slice(0, 12);
48
+ const url = `http://localhost:${port}/v2/${imageName}/manifests/${tag}`;
49
+ const accept = 'application/vnd.oci.image.index.v1+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.docker.distribution.manifest.v2+json';
50
+ const acceptHeader = `Accept: ${accept}`;
51
+ return { url, acceptHeader, command: `curl -fsS --max-time 8 -D - -o /dev/null -H ${shellQuote(acceptHeader)} ${shellQuote(url)}` };
52
+ }
53
+
54
+ function probeGateRepoDigest(member: ReturnType<typeof parsePodPool>[number], image: string, commit: string): string | null {
55
+ const registry = member.registry ?? NODE_REGISTRY;
56
+ const request = gateRepoDigestProbeRequest(registry, image, commit);
57
+ if (!request) return null;
58
+ const result = member.sshHost
59
+ ? defaultRemoteRun(member.sshHost, request.command)
60
+ : spawnSync('curl', ['-fsS', '--max-time', '8', '-D', '-', '-o', '/dev/null', '-H', request.acceptHeader, request.url],
61
+ { encoding: 'utf8', timeout: 10_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
62
+ return result.status === 0 ? parseGateRepoDigestResponse(result.stdout) : null;
63
+ }
64
+
65
+ function gateRegistryHostImage(member: ReturnType<typeof parsePodPool>[number], image: string, commit: string): string | null {
66
+ const registry = member.registry ?? NODE_REGISTRY;
67
+ const port = /:(\d+)$/.exec(registry)?.[1];
68
+ if (!port) return null;
69
+ const imageName = image.startsWith('elanous-harness-lite:') ? 'elanous-harness-lite' : 'elanous-harness';
70
+ return `localhost:${port}/${imageName}:${commit.slice(0, 12)}`;
71
+ }
72
+
73
+ /** Read the immutable pushed image manifest on every candidate host. Local Docker Image IDs are diagnostic only. */
74
+ export function preflightGateImageDigest(pool: string, commit?: string): string | null {
75
+ try {
76
+ if (!commit || !SHA.test(commit)) return null;
77
+ const image = podCommandImageFor([], true);
78
+ const members = parsePodPool(pool);
79
+ if (!members.length) return null;
80
+ const digests = members.map((member) => probeGateRepoDigest(member, image, commit));
81
+ return digests.every((digest) => digest && DIGEST.test(digest) && digest === digests[0]) ? digests[0]! : null;
82
+ } catch { return null; }
83
+ }
84
+
85
+ /** Probe each candidate's exact gate image/runtime and deterministic environment without a Kubernetes Job. */
86
+ export function preflightGateExecutionIdentity(pool: string, env: NodeJS.ProcessEnv, commit?: string): GatePodExecutionIdentity | null {
32
87
  try {
88
+ if (!commit || !SHA.test(commit)) return null;
33
89
  const image = podCommandImageFor([], true);
34
90
  const members = parsePodPool(pool);
35
91
  if (!members.length) return null;
36
- const ids = members.map((member) => {
37
- if (member.sshHost) return remoteImageId(member, image);
38
- const result = spawnSync('docker', ['image', 'inspect', image, '--format', '{{.Id}}'], { encoding: 'utf8', timeout: 30_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
39
- return result.status === 0 ? result.stdout.trim() : null;
92
+ const baseline = gateTestBaseline(env);
93
+ const expectedEnvFingerprint = gateEnvFingerprint(env);
94
+ const script = "const {createHash}=require('node:crypto');const keys=['ELANOUS_TEST_FAKE_LLM','LANG','LC_ALL','TZ'];const e=Object.fromEntries(keys.sort().map(k=>[k,process.env[k]??'']));const envFingerprint=createHash('sha256').update(JSON.stringify(e)).digest('hex');console.log(JSON.stringify({bunVersion:Bun.version,platform:process.platform,architecture:process.arch,envFingerprint}));";
95
+ const rows = members.map((member): GatePodExecutionIdentity | null => {
96
+ const imageDigest = probeGateRepoDigest(member, image, commit);
97
+ if (!imageDigest || !DIGEST.test(imageDigest)) return null;
98
+ const hostImage = gateRegistryHostImage(member, image, commit);
99
+ if (!hostImage) return null;
100
+ const args = ['run', '--rm', '--pull=never', '--network=none', ...Object.entries(baseline).flatMap(([key, value]) => ['-e', `${key}=${value}`]), '--entrypoint', 'bun', hostImage, '-e', script];
101
+ const result = member.sshHost
102
+ ? defaultRemoteRun(member.sshHost, `docker ${args.map(shellQuote).join(' ')}`)
103
+ : spawnSync('docker', args, { encoding: 'utf8', timeout: 30_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
104
+ if (result.status !== 0) return null;
105
+ try {
106
+ const row = JSON.parse(result.stdout.trim()) as Partial<GatePodRuntimeIdentity>;
107
+ if (typeof row.bunVersion !== 'string' || typeof row.platform !== 'string' || typeof row.architecture !== 'string'
108
+ || row.envFingerprint !== expectedEnvFingerprint) return null;
109
+ return { imageDigest, bunVersion: row.bunVersion, platform: row.platform, architecture: row.architecture,
110
+ envFingerprint: row.envFingerprint };
111
+ } catch { return null; }
40
112
  });
41
- return ids.every((id) => id && DIGEST.test(id) && id === ids[0]) ? ids[0]! : null;
113
+ if (rows.some((row) => row === null)) return null;
114
+ const first = rows[0]!;
115
+ return rows.every((row) => JSON.stringify(row) === JSON.stringify(first)) ? first : null;
42
116
  } catch { return null; }
43
117
  }
44
118
 
@@ -82,10 +156,15 @@ export function partialGateImagesMatch(root: string, version: string, digest: st
82
156
  /** Every missing or mismatched proof returns a full gate. The caller uses this only when no explicit resume plan exists. */
83
157
  export function planHardRehearsalRadius(input: { root: string; repo: string; version: string; forCommit: string;
84
158
  graphPins: { graphSha: string; recipeSha: string } | null; envFingerprint: string;
85
- imageDigest: string | null }): LiveRadiusDecision {
159
+ imageDigest: string | null; executionIdentity: GatePodExecutionIdentity | null }): LiveRadiusDecision {
86
160
  const full = (reason: string): LiveRadiusDecision => ({ plan: null, reason });
87
161
  if (!/^\d+\.\d+\.\d+$/.test(input.version) || !SHA.test(input.forCommit)) return full('invalid release identity');
88
- if (!input.graphPins || !/^[0-9a-f]{64}$/i.test(input.envFingerprint) || !input.imageDigest || !DIGEST.test(input.imageDigest))
162
+ if (!input.graphPins || !/^[0-9a-f]{64}$/i.test(input.envFingerprint) || !input.imageDigest || !DIGEST.test(input.imageDigest)
163
+ || !input.executionIdentity || input.executionIdentity.imageDigest !== input.imageDigest.toLowerCase()
164
+ || input.executionIdentity.envFingerprint !== input.envFingerprint
165
+ || !/^\d+\.\d+\.\d+(?:[-+][\w.-]+)?$/.test(input.executionIdentity.bunVersion)
166
+ || !/^[a-z0-9_-]{1,32}$/.test(input.executionIdentity.platform)
167
+ || !/^[a-z0-9_-]{1,32}$/.test(input.executionIdentity.architecture))
89
168
  return full('current graph, environment or Pod image identity unmeasured');
90
169
  try {
91
170
  const stamps = ['hard-5h', 'hard-mid'].map((step) => {
@@ -118,14 +197,19 @@ export function planHardRehearsalRadius(input: { root: string; repo: string; ver
118
197
  if (verdict.commit !== stamp.sha || verdict.outcome !== 'ok' || failures.commit !== stamp.sha
119
198
  || !Array.isArray(failures.failures) || failures.failures.length !== 0
120
199
  || !Array.isArray(failures.errors) || failures.errors.length !== 0
121
- || !receipt || receipt.schema !== 1 || !receipt.valid || receipt.version !== gateVersion || receipt.commit !== stamp.sha
200
+ || !receipt || receipt.schema !== 2 || !receipt.valid || receipt.version !== gateVersion || receipt.commit !== stamp.sha
122
201
  || receipt.graphSha !== input.graphPins.graphSha || receipt.recipeSha !== input.graphPins.recipeSha
123
202
  || receipt.envFingerprint !== input.envFingerprint || receipt.imageDigest !== input.imageDigest.toLowerCase()
203
+ || !receipt.executionIdentity
204
+ || JSON.stringify(receipt.executionIdentity) !== JSON.stringify({ bunVersion: input.executionIdentity.bunVersion,
205
+ platform: input.executionIdentity.platform, architecture: input.executionIdentity.architecture,
206
+ envFingerprint: input.executionIdentity.envFingerprint })
124
207
  || !Number.isSafeInteger(receipt.files) || receipt.files < 1 || !Number.isSafeInteger(receipt.shards) || receipt.shards < 1)
125
208
  return full('hard rehearsal receipt or current execution identity differs');
126
209
  const recomputed = gateBaselineReceipt({ root: input.root, version: gateVersion, commit: stamp.sha,
127
210
  fullSweep: true, graphPins: input.graphPins, envFingerprint: input.envFingerprint });
128
- if (!recomputed.valid || recomputed.imageDigest !== receipt.imageDigest || recomputed.files !== receipt.files
211
+ if (!recomputed.valid || recomputed.imageDigest !== receipt.imageDigest
212
+ || JSON.stringify(recomputed.executionIdentity) !== JSON.stringify(receipt.executionIdentity) || recomputed.files !== receipt.files
129
213
  || recomputed.shards !== receipt.shards) return full(`hard rehearsal Pod proof differs: ${recomputed.reason}`);
130
214
  const ancestor = spawnSync('git', ['merge-base', '--is-ancestor', stamp.sha, input.forCommit], { cwd: input.repo, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
131
215
  if (ancestor.status !== 0 || ancestor.error) return full('hard rehearsal commit is not an ancestor');
@@ -29,6 +29,49 @@ export interface GateShardHost {
29
29
  reserved: boolean;
30
30
  }
31
31
 
32
+ export type GateProgressState = 'pending' | 'running' | 'done' | 'failed' | 'timeout' | 'unmeasured';
33
+
34
+ /** A host group or planned Pod shard as seen by the release gate's live progress observer. */
35
+ export interface GateProgressGroup {
36
+ id: string;
37
+ state: GateProgressState;
38
+ host?: string;
39
+ startedAtMs?: number;
40
+ endedAtMs?: number;
41
+ }
42
+
43
+ export interface GateProgressSummary {
44
+ executor: 'host' | 'pod';
45
+ host: string | null;
46
+ completed: number;
47
+ total: number;
48
+ failed: number;
49
+ groups: Array<{ id: string; state: GateProgressState; host: string | null; elapsedMs: number | null }>;
50
+ }
51
+
52
+ /** Keep the progress event shape identical for serial host groups and parallel Pod shards. */
53
+ export function summarizeGateProgress(input: {
54
+ executor: 'host' | 'pod';
55
+ groups: readonly GateProgressGroup[];
56
+ nowMs: number;
57
+ }): GateProgressSummary {
58
+ const terminal = new Set<GateProgressState>(['done', 'failed', 'timeout', 'unmeasured']);
59
+ const hosts = [...new Set(input.groups.flatMap((group) => group.host?.trim() ? [group.host.trim()] : []))].sort();
60
+ return {
61
+ executor: input.executor,
62
+ host: hosts.length ? hosts.join(',') : null,
63
+ completed: input.groups.filter((group) => terminal.has(group.state)).length,
64
+ total: input.groups.length,
65
+ failed: input.groups.filter((group) => group.state === 'failed' || group.state === 'timeout').length,
66
+ groups: input.groups.map((group) => {
67
+ const end = group.endedAtMs ?? input.nowMs;
68
+ const elapsedMs = Number.isFinite(group.startedAtMs) && Number.isFinite(end)
69
+ ? Math.max(0, end - group.startedAtMs!) : null;
70
+ return { id: group.id, state: group.state, host: group.host?.trim() || null, elapsedMs };
71
+ }),
72
+ };
73
+ }
74
+
32
75
  export interface GateShardDispatchInput {
33
76
  head40: string;
34
77
  base40: string;
@@ -0,0 +1,122 @@
1
+ /** Safe test carry from passed host-regate receipts into a release cut. */
2
+ import { spawnSync } from 'node:child_process';
3
+ import { join } from 'node:path';
4
+ import { gateAssignableTestsAtCommit } from './gate-baseline-receipt.js';
5
+ import { gitClosureTree, testsReachingChanged } from './gate-closure.js';
6
+ import { changedFilesBetween } from './gate-partial.js';
7
+ import { gateReceiptSelfKey, loadGateReceipts, type GateReceipt } from '../../src/self-implement/gate-receipt-registry.js';
8
+ import { SPAWN_SYNC_MAX_BUFFER } from '../../src/util/spawn-sync-output.js';
9
+
10
+ const SHA = /^[0-9a-f]{40}$/i;
11
+ const TEST = /^(?:[\w.-]+\/)*[\w.-]+\.test\.(?:tsx?|jsx?|mts|cts)$/;
12
+ const INERT = /\.(?:md|mdx|txt)$/i;
13
+
14
+ function git(repo: string, args: string[]): string | null {
15
+ const result = spawnSync('git', args, { cwd: repo, encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 });
16
+ return result.status === 0 && !result.error ? result.stdout.trim() : null;
17
+ }
18
+
19
+ function ancestor(repo: string, base: string, head: string): boolean {
20
+ const result = spawnSync('git', ['merge-base', '--is-ancestor', base, head], { cwd: repo, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
21
+ return result.status === 0 && !result.error;
22
+ }
23
+
24
+ function blob(repo: string, commit: string, file: string): string | null {
25
+ return git(repo, ['rev-parse', `${commit}:${file}`]);
26
+ }
27
+
28
+ function candidateTests(tree: ReturnType<typeof gitClosureTree>, receipt: GateReceipt): string[] {
29
+ // The registry stores the changed-file radius, not the expanded importer/neighbor list that the
30
+ // host gate selected. Only changed test files are certainly in that measurement; never infer
31
+ // pass evidence for siblings or importers that the receipt did not persist explicitly.
32
+ return [...new Set(receipt.radiusFiles.filter((file) => TEST.test(file) && tree.files.includes(file)))].sort();
33
+ }
34
+
35
+ export interface GateTrainReceiptPlan {
36
+ priorCommit: string;
37
+ files: string[];
38
+ carriedFiles: string[];
39
+ totalFiles: number;
40
+ reasons: string[];
41
+ receipts: string[];
42
+ }
43
+
44
+ /** Missing/invalid evidence only reduces carry; it never makes a gate pass. */
45
+ export function planGateTrainReceiptCarry(input: {
46
+ repo: string;
47
+ commit: string;
48
+ receiptPath: string;
49
+ bunVersion: string;
50
+ lockfileHash: string;
51
+ envFingerprint: string;
52
+ }): GateTrainReceiptPlan | null {
53
+ if (!SHA.test(input.commit) || !input.bunVersion || !input.lockfileHash || !/^[0-9a-f]{64}$/i.test(input.envFingerprint)) return null;
54
+ const tests = gateAssignableTestsAtCommit(input.repo, input.commit);
55
+ if (!tests?.length) return null;
56
+ const receipts = Object.values(loadGateReceipts(input.receiptPath));
57
+ const currentTree = gitClosureTree(input.repo, input.commit);
58
+ const carried = new Set<string>();
59
+ const accepted: GateReceipt[] = [];
60
+ const reasons = new Set<string>();
61
+ const changedByBase = new Map<string, string[]>();
62
+
63
+ for (const receipt of receipts) {
64
+ if (receipt.verdict !== 'passed' || gateReceiptSelfKey(receipt) !== receipt.key) { reasons.add('invalid-or-non-passing receipt'); continue; }
65
+ if (!receipt.checks.some((check) => check === 'test-interference:with-neighbours' || check === 'test-interference:changed-only')) {
66
+ reasons.add('receipt lacks passing test-interference check'); continue;
67
+ }
68
+ if (receipt.bunVersion !== input.bunVersion || receipt.lockfileHash !== input.lockfileHash
69
+ || receipt.envFingerprint !== input.envFingerprint) { reasons.add('bun/lockfile/environment differs'); continue; }
70
+ if (!ancestor(input.repo, receipt.baseCommit, input.commit)) { reasons.add('receipt base is not an ancestor of cut'); continue; }
71
+ if (receipt.radiusFiles.some((file) => !currentTree.files.includes(file)
72
+ || blob(input.repo, receipt.headCommit, file) !== blob(input.repo, input.commit, file))) {
73
+ reasons.add('receipt radius changed or missing at cut'); continue;
74
+ }
75
+ let measuredChanges: string[];
76
+ try { measuredChanges = changedFilesBetween(input.repo, receipt.baseCommit, receipt.headCommit); }
77
+ catch { reasons.add('receipt base-to-head diff unavailable'); continue; }
78
+ if (measuredChanges.some((file) => !receipt.radiusFiles.includes(file))) {
79
+ reasons.add('receipt radius does not cover base-to-head changes'); continue;
80
+ }
81
+ const candidates = candidateTests(currentTree, receipt);
82
+ if (!candidates?.length) { reasons.add('receipt test radius is empty or opaque'); continue; }
83
+
84
+ let changed: string[];
85
+ try {
86
+ changed = changedByBase.get(receipt.baseCommit) ?? changedFilesBetween(input.repo, receipt.baseCommit, input.commit);
87
+ changedByBase.set(receipt.baseCommit, changed);
88
+ } catch { reasons.add('cut diff from receipt base unavailable'); continue; }
89
+ const outside = changed.filter((file) => !receipt.radiusFiles.includes(file) && !INERT.test(file));
90
+ const nonCodeOutside = outside.find((file) => !/\.(?:[cm]?[jt]sx?)$/.test(file));
91
+ if (nonCodeOutside) {
92
+ reasons.add(`non-code release input changed outside receipt radius: ${nonCodeOutside}`); continue;
93
+ }
94
+ let safeCandidates = candidates;
95
+ if (outside.length) {
96
+ const affected = testsReachingChanged(currentTree, outside, candidates);
97
+ if (!affected.ok) { reasons.add('cut import closure opaque'); continue; }
98
+ const affectedSet = new Set(affected.tests);
99
+ safeCandidates = candidates.filter((test) => !affectedSet.has(test));
100
+ if (!safeCandidates.length) { reasons.add('merge-skew changes reach every receipt test'); continue; }
101
+ }
102
+ for (const test of safeCandidates) carried.add(test);
103
+ accepted.push(receipt);
104
+ }
105
+
106
+ const noCarry = (reason: string): GateTrainReceiptPlan => ({ priorCommit: accepted[0]?.baseCommit ?? input.commit,
107
+ files: [...tests], carriedFiles: [], totalFiles: tests.length,
108
+ reasons: [...new Set([...reasons, ...(reason === 'no safe receipt coverage' && reasons.size ? [] : [reason])])].sort(),
109
+ receipts: accepted.map((receipt) => receipt.key).sort() });
110
+ if (!carried.size || !accepted.length) return noCarry(receipts.length ? 'no safe receipt coverage' : 'gate receipt registry empty');
111
+ const selected = tests.filter((file) => !carried.has(file));
112
+ // The caller falls back to a full sweep rather than skipping all test Pods.
113
+ if (!selected.length) return noCarry('all tests carried; keep at least one measured sweep');
114
+ const latest = [...accepted].sort((a, b) => b.savedAt.localeCompare(a.savedAt))[0]!;
115
+ return { priorCommit: latest.baseCommit, files: selected, carriedFiles: [...carried].sort(), totalFiles: tests.length,
116
+ reasons: [...reasons].sort(), receipts: accepted.map((receipt) => receipt.key).sort() };
117
+ }
118
+
119
+ export function gateReceiptRegistryPath(repo: string): string | null {
120
+ const common = git(repo, ['rev-parse', '--path-format=absolute', '--git-common-dir']);
121
+ return common ? join(common, 'elanous-host-regate', 'gate-receipts.json') : null;
122
+ }
@@ -22,6 +22,7 @@ import { reserveReleaseResumeAttempt } from './resume-cap.js';
22
22
  import { cutReleaseBranch } from './cut-branch.js';
23
23
  import { releaseLoopConfig } from './unattended-release.js';
24
24
  import { trafficOptionsFromConfig } from './traffic-transition.js';
25
+ import type { GateAutohealDecision } from './gate-autoheal.js';
25
26
 
26
27
  const GRAPH = join(import.meta.dir, '../../graphs/release/release-loop.yaml');
27
28
 
@@ -67,9 +68,36 @@ export interface ResumeReleaseOptions {
67
68
  export interface ResumeReleasePlan {
68
69
  runId: string; version: string; fromNode: string; commandRoot: string; tip: TipRefresh;
69
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;
70
74
  nextAttempt: number; priorCountKnown: boolean; preflight: 'passed';
71
75
  }
72
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
+
73
101
  /** CLI entry files: a production file that only names one (help text, a goal's command line) does not run it. */
74
102
  const CLI_ENTRIES = ['bin/elanous.mjs', 'src/index.ts'] as const;
75
103
  /** The gate's own test-file set (gate-node sweep: `git ls-files '*.test.*'` filtered by this). */
@@ -428,6 +456,42 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
428
456
  const saved = JSON.parse(readFileSync(tip.statePath, 'utf8')) as GraphRunState;
429
457
  const gateRecord = saved.nodes.find((node) => node.nodeId === 'gate');
430
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
+ }
431
495
  const stalled = previousGate?.stalledShards;
432
496
  const summary = previousGate?.partialSummary as { pass?: unknown; fail?: unknown; errors?: unknown; ran?: unknown; files?: unknown } | undefined;
433
497
  const measured = previousGate?.partialFailures as { failures?: unknown; errors?: unknown } | undefined;
@@ -600,19 +664,25 @@ async function planResumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: Resu
600
664
  waiverCommit = recorded;
601
665
  }
602
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;
603
670
  if (opts.rerouteShard !== undefined && tip.changed) throw new Error('shard reroute requires the original cut head; moved tip needs a full re-gate');
604
671
  verifyResumeRecipePin(opts.commandRoot ?? process.cwd(), savedRun.graphSnapshot, tip.version);
605
672
  const commandRoot = verifyReleaseCommandRoot(opts.commandRoot ?? process.cwd(), tip.to, deps.git);
606
673
  verifyResumePreflight(savedRun, opts, deps, tip.version, tip.from, tip.to, commandRoot);
607
- const partial = preparePartial(tip, opts.rerouteShard !== undefined ? { ...opts, partial: false } : opts, deps, true);
608
- const fromIndex = savedRun.path.indexOf(opts.from);
609
- const gateIndex = savedRun.path.indexOf('gate');
610
- 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);
611
679
  const gatePlan = !gateWillRun ? { mode: 'not-run' as const }
612
680
  : partial.requested && partial.applied ? { mode: 'partial' as const }
613
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 });
614
683
  return { runId: opts.runId, version: tip.version, fromNode: opts.from, commandRoot, tip, partial,
615
- 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' };
616
686
  }
617
687
 
618
688
  async function resumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: ResumeReleaseDeps): Promise<{ tip: TipRefresh; partial: PartialOutcome; state: GraphRunState }> {
@@ -667,7 +737,12 @@ async function resumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: ResumeRe
667
737
  const commandRoot = verifyReleaseCommandRoot(opts.commandRoot ?? process.cwd(), previewTip.to, deps.git);
668
738
  verifyResumePreflight(savedRun, opts, deps, previewTip.version, previewTip.from, previewTip.to, commandRoot);
669
739
  const tip = refreshReleaseBranchTipInternal(opts.runId, deps, opts.from, waiverCommit, false, previewTip.to);
670
- 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 });
671
746
  // A failed preflight does not consume an observed release resume attempt.
672
747
  const attempt = reserveReleaseResumeAttempt(savedRun, deps.root!, { now: deps.now });
673
748
  debug.log('release-loop.resume', 'attempt', { runId: opts.runId, attempt: attempt.attempt,
@@ -682,8 +757,8 @@ async function resumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: ResumeRe
682
757
  ...(acceptedRegressions ? { releaseAcceptedRegressions: { requested: opts.acceptedRegressions!, acceptedRegressions } } : {}),
683
758
  ...(gateExecutor ? { releaseGateExecutor: { executor: gateExecutor.set.gateExecutor, ...(opts.gateHost !== undefined ? { host: opts.gateHost } : {}) } } : {}),
684
759
  ...(gatePodPool ? { releaseGatePodPool: gatePodPool.set.gatePodPool } : {}),
685
- ...(shardReroute ? { releaseGateShardReroute: { shard: shardReroute.set.gateRerouteShard,
686
- ...(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 } : {}),
687
762
  ...(opts.shardOs ? { os: opts.shardOs } : {}) } } : {}),
688
763
  deps: { root: deps.root, ...(deps.runBash ? { runBash: deps.runBash } : {}) } });
689
764
  return { tip, partial, state };
package/scripts/ux-sim.ts CHANGED
@@ -20,10 +20,11 @@
20
20
  // bun scripts/ux-sim.ts --fault sqlite-busy # 결함 주입: 실제 SQLite 잠금 → 로그 배치 버퍼·복구
21
21
  // bun scripts/ux-sim.ts emit child-error-long --json # 사건 주입(L0): 격리 우주 원장에 주입 줄 → 소비자 목록
22
22
  //
23
- // ⛔ 이 CLI 는 «실제 위젯 render()» 를 부른다 — 흉내 렌더러가 아니다.
23
+ // catalog list/show 는 재사용 장치의 참조를 보여 준다. 선언된 TUI 장면은 실제 위젯 render() 를 부른다 — 흉내 렌더러가 아니다.
24
24
  import { simRenderLogFrame } from '../src/ux-sim/render-frame.js';
25
25
  import { simTypeaheadState, type TurnPhase, type TurnSimSpec } from '../src/ux-sim/turn-states.js';
26
26
  import { renderTurnTypeaheadQueueRow, renderTurnTypeaheadEcho } from '../src/chat/turn-typeahead.js';
27
+ import { loadMockScenarioCatalog, renderMockScenarioDetails, renderMockScenarioList } from '../src/ux-sim/mock-scenario-catalog.js';
27
28
 
28
29
  const PHASES: TurnPhase[] = [
29
30
  'idle', 'thinking', 'streaming', 'streaming-with-subagents', 'harness-child', 'interrupted',
@@ -31,6 +32,22 @@ const PHASES: TurnPhase[] = [
31
32
 
32
33
  const argv = process.argv.slice(2);
33
34
 
35
+ if (argv[0] === 'list') {
36
+ if (argv.length !== 1) { console.error('usage: bun scripts/ux-sim.ts list'); process.exit(2); }
37
+ console.log(renderMockScenarioList(loadMockScenarioCatalog()).join('\n'));
38
+ process.exit(0);
39
+ }
40
+ if (argv[0] === 'show') {
41
+ if (argv.length !== 2) { console.error('usage: bun scripts/ux-sim.ts show <MS-id>'); process.exit(2); }
42
+ try {
43
+ console.log(renderMockScenarioDetails(loadMockScenarioCatalog(), argv[1]!).join('\n'));
44
+ process.exit(0);
45
+ } catch (error) {
46
+ console.error(error instanceof Error ? error.message : String(error));
47
+ process.exit(2);
48
+ }
49
+ }
50
+
34
51
  // ── 사건 주입 «이벤트 몽키» (SIM-EVENT-MONKEY · L0) — 자기 플래그를 가진 하위 명령 ──
35
52
  // bun scripts/ux-sim.ts emit <kind> [--run <id>] [--variant allowed|refused] [--state-dir <dir>] [--json]
36
53
  // bun scripts/ux-sim.ts emit --list