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
@@ -17,9 +17,13 @@ import { gateHistory } from './gate-history.js';
17
17
  import { baselineFromCutLogs } from './baseline-from-logs';
18
18
  import { mergePartialFailures, readPartialPlanResult, testFileOfId, verifyDocsOnlyCarry } from './gate-partial';
19
19
  import { planGateRadiusShadow, shadowActual, writeGateRadiusShadow, type GateRadiusShadowPlan } from './gate-radius-shadow';
20
- import { planHardRehearsalRadius, partialGateImagesMatch, preflightGateImageDigest } from './gate-radius-live';
21
- import { actualGatePodImageDigest, gateBaselineReceipt, gateEnvFingerprint, gateGraphPins, gateSourcePins,
20
+ import { planHardRehearsalRadius, partialGateImagesMatch, preflightGateExecutionIdentity, preflightGateImageDigest } from './gate-radius-live';
21
+ import { actualGatePodImageDigest, gateBaselineReceipt, gateEnvFingerprint, gateGraphPins, gateSourcePins, parseGatePodRuntimeIdentity,
22
22
  POD_SWEEP_INTEGRATION_ONLY } from './gate-baseline-receipt';
23
+ import { gateImagePinPath, readGateImagePin, resolveGateImagePin, writeGateImagePin } from './gate-image-pin.js';
24
+ import { gateReceiptRegistryPath, planGateTrainReceiptCarry } from './gate-train-receipts.js';
25
+ import { gateEnvFingerprint as gateReceiptEnvFingerprint, gateLockfileHash } from '../../src/self-implement/gate-receipt-registry.js';
26
+ import { SPAWN_SYNC_MAX_BUFFER } from '../../src/util/spawn-sync-output.js';
23
27
  export { POD_SWEEP_INTEGRATION_ONLY } from './gate-baseline-receipt';
24
28
  import { logPlanWalltime, planDurations, readFileMemory } from './shard-plan';
25
29
  import { defaultGateHostMirrorReaders, resolveGateHostMirror, type GateHostMirror, type GateHostMirrorReaders } from './gate-host-mirror';
@@ -30,7 +34,7 @@ import { readJunitFileSeconds } from './gate-method';
30
34
  import { deriveCdpTestPatterns } from '../test-deterministic';
31
35
  import { emitNodeResult, readGraphContext } from './node-verdict.js';
32
36
  import { isGateExecutor, type GateExecutor } from './gate-executor.js';
33
- import { runPodCommand, parsePodCpu, podCpuMillis, resolvePodCpu, type RunPodCommandOptions, type PodCommandResult, type PodCpu } from '../../src/task-orchestrator/surfaces/pod-command-job.js';
37
+ import { runPodCommand, parsePodCpu, podCpuMillis, resolvePodCpu, podCommandImageFor, type RunPodCommandOptions, type PodCommandResult, type PodCpu } from '../../src/task-orchestrator/surfaces/pod-command-job.js';
34
38
  import { POD_BUN_CACHE_HOST_PATH, parseInstallSeconds, podBunCacheVolume } from '../../src/task-orchestrator/surfaces/pod-bun-cache.js';
35
39
  import { PodPoolScheduler, parsePodPool, checkPodPool, defaultRemoteRun, snapshotWarmPoolMembers, type PodPoolMember } from '../../src/task-orchestrator/surfaces/pod-pool.js';
36
40
  import { POD_INSTALL_SLOTS_DEFAULT, POD_INSTALL_SLOTS_HOST_PATH, installSlotScript } from '../../src/task-orchestrator/surfaces/pod-install-slots.js';
@@ -45,7 +49,7 @@ import { getUserConfig } from '../../src/user-config.js';
45
49
  import { beginGateShardAttempt, finalizeTerminalGateShards, gateShardsPath, recordGateShardsVerdict, writeGateShards, type GateJobTerminal, type GateShard } from '../../src/release-loop/gate-shards.js';
46
50
  import { appendGateShardLedger, gateShardLedgerPath, type GateShardLedgerRow } from '../../src/release-loop/gate-shard-ledger.js';
47
51
  import { markGateShardUnmeasured, nextGateShardAttempt, readGateShardManifest, readGateShardResult, saveGateShardResult } from './gate-shard-receipts.js';
48
- import { planGateShardDispatch, type GateShardReceipt } from './gate-shard-dispatch.js';
52
+ import { planGateShardDispatch, summarizeGateProgress, type GateProgressGroup, type GateShardReceipt } from './gate-shard-dispatch.js';
49
53
  import { defaultKubectl } from '../../src/task-orchestrator/surfaces/self-implement-pod.js';
50
54
  import { executeTuiNodeCheck, type ExecutedTuiCheck } from './tui-node-checks.js';
51
55
  import { runWithReleaseGateTraffic } from './gate-traffic-mode.js';
@@ -113,12 +117,28 @@ export interface GateTools {
113
117
  podCommand?: (options: RunPodCommandOptions) => Promise<PodCommandResult>;
114
118
  /** Kubernetes client (pool readiness · Pod lifecycle · Job log tail · failure evidence); absent = `defaultKubectl`. */
115
119
  kubectl?: typeof defaultKubectl;
120
+ /** Mock-only imageID observation seam; production reads the completed Job's child imageID from Kubernetes. */
121
+ podImageDigest?: (input: { job: PodCommandResult; host: string }) => string | null;
116
122
  /** SSH transport behind {@link GateRunner.command} on a remote host; absent = a real `ssh` child. */
117
123
  ssh?: (host: string, script: string, limitMs?: number) => { rc: number; output: string; timedOut?: boolean };
118
124
  /** SSH reachability probe of a multi-host roster; absent = `defaultRemoteRun`. */
119
125
  remoteRun?: typeof defaultRemoteRun;
120
126
  /** Pool admission a shard waits on (leases ⊕ a cluster reading); absent = the gate's own pool admission. */
121
127
  admission?: GateAdmission;
128
+ /** The live progress heartbeat scheduler; tests inject a deterministic tick, production uses an unref'd interval. */
129
+ progressScheduler?: GateProgressScheduler;
130
+ }
131
+
132
+ export type GateProgressScheduler = (tick: () => void, intervalMs: number) => () => void;
133
+ const scheduleGateProgress: GateProgressScheduler = (tick, intervalMs) => {
134
+ const timer = setInterval(tick, intervalMs);
135
+ timer.unref();
136
+ return () => clearInterval(timer);
137
+ };
138
+
139
+ export interface GateSweepProgress {
140
+ executor: 'host' | 'pod';
141
+ groups: readonly GateProgressGroup[];
122
142
  }
123
143
  export interface GateRunner {
124
144
  command(cmd: string, args: string[], cwd: string, limitMs?: number): Promise<CommandResult>;
@@ -128,7 +148,7 @@ export interface GateRunner {
128
148
  /** One checkout/install for a small independent file group; each file retains its own rc and log. */
129
149
  recheckPodBatch?(files: readonly string[], commit: string, pod: NonNullable<GateOptions['pod']>, timeoutMs: number): Promise<Map<string, CommandResult>>;
130
150
  /** `only` (GATE-PARTIAL) restricts the sweep to these test files; absent = every test file. */
131
- sweep(tree: string, logDir?: string, pod?: GateOptions['pod'], only?: readonly string[]): Promise<CommandResult>;
151
+ sweep(tree: string, logDir?: string, pod?: GateOptions['pod'], only?: readonly string[], onProgress?: (progress: GateSweepProgress) => void): Promise<CommandResult>;
132
152
  add(tree: string, commit: string): Promise<void>;
133
153
  remove(tree: string): Promise<void>;
134
154
  snapshot(tree: string, commit: string): Promise<void>;
@@ -137,6 +157,7 @@ export interface GateRunner {
137
157
  remoteMirror?: string;
138
158
  /** GATE-FLOW-SIM — the clock this runner measures with (the injected fake clock in a scenario); absent = `Date.now`. */
139
159
  now?(): number;
160
+ progressScheduler?: GateProgressScheduler;
140
161
  }
141
162
 
142
163
  /** One watchdog tick. Batch Kubernetes reads per host and trust only the current attempt's result marker. */
@@ -211,6 +232,8 @@ export interface GateOptions {
211
232
  /** Only a direct hard rehearsal may pin graph bytes from its measured Git commit. */
212
233
  rehearsalBaseline?: boolean;
213
234
  forceFreeze?: boolean;
235
+ /** Force all release tests to run, bypassing every reusable partial plan. */
236
+ full?: boolean;
214
237
  version: string;
215
238
  baselineVersion?: string;
216
239
  baselineCommit?: string;
@@ -227,6 +250,9 @@ export interface GateOptions {
227
250
  recheckFileLimit?: number;
228
251
  /** freshShards — GATE-SHARD-RESUME off: every shard starts a Pod even when this logDir already holds its result. */
229
252
  pod?: { pool: string; shards?: number; shardTimeoutSeconds?: number; durationSource?: string; bunCache?: string; freshShards?: boolean;
253
+ /** One immutable container image identity for this gate graph run and all its resumes. */
254
+ pinnedImageDigest?: string;
255
+ pinnedRunId?: string;
230
256
  /** Remeasure one named shard, or all incomplete shards with auto. Complete siblings keep their receipts. */
231
257
  rerouteShard?: string | 'auto';
232
258
  /** Pool context for the rerouted shard; the planner otherwise distributes missing shards over pool contexts. */
@@ -346,7 +372,7 @@ export interface GateResult {
346
372
  /** GATE-WARM-BOOT-SCAN — observation only: the cut sweep's test selection time (pod sweep · absent otherwise). */
347
373
  selectionMs?: number;
348
374
  error?: string;
349
- stalledShards?: Array<{ shard: number; files: string[]; reason: 'incomplete' | 'no-output' | 'job-failed' | 'exit-code' | 'unattributed' | 'timeout' | 'admission-timeout' | 'admission-error'; lastFile?: string; summaryFailures?: number; namedFailures?: number; detail?: string;
375
+ stalledShards?: Array<{ shard: number; files: string[]; reason: 'incomplete' | 'no-output' | 'job-failed' | 'exit-code' | 'unattributed' | 'timeout' | 'admission-timeout' | 'admission-error' | 'image-pin-mismatch'; lastFile?: string; summaryFailures?: number; namedFailures?: number; detail?: string;
350
376
  /** Pool member that ran the final unmeasured attempt; enables a bounded cross-host tail remeasure. */
351
377
  lastHost?: string;
352
378
  /** Failure of the optional cross-host tail attempt; original stall fields remain authoritative. */
@@ -360,6 +386,7 @@ export interface GateResult {
360
386
  partialFailures?: SweepFailures;
361
387
  /** GATE-PARTIAL — this verdict re-ran only these files and carried the prior gate's results for the rest. */
362
388
  partial?: { priorCommit: string; files: number };
389
+ trainReceiptReuse?: { carried: number; total: number; receipts: number; reasons: string[] };
363
390
  /** A failure that passed an evidence-checked isolated re-run: warning (flaky under load), not an introduced regression. */
364
391
  loadFlaky?: string[];
365
392
  /** File-level unhandled errors that reproduced or could not be measured in isolation. They keep the gate red. */
@@ -807,6 +834,7 @@ export function gateShardShell(o: { parts: readonly (readonly string[])[]; cdpPa
807
834
  ...(o.beatSeconds ? gateBeatScript(o.beatSeconds, o.installSlots > 0) : []),
808
835
  ...(o.cachePrefix ? [o.cachePrefix] : []),
809
836
  gateEnvBaselineLine(),
837
+ `bun -e ${quote("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}));")} > "$O/runtime-identity.json" 2> "$O/runtime-identity.err" || rm -f "$O/runtime-identity.json"`,
810
838
  ...(o.rulesBaseSha ? [`export ELANOUS_GATE_BASE_SHA=${quote(o.rulesBaseSha)}`] : []),
811
839
  // Seconds since this container's PID 1 started (the clone ran before this shell) — 0 when /proc cannot tell.
812
840
  `pod_age=$(awk -v t="$(cut -d' ' -f22 /proc/1/stat 2>/dev/null)" -v hz="$(getconf CLK_TCK 2>/dev/null)" '{ if (t > 0 && hz > 0) print int($1 - t / hz); else print 0 }' /proc/uptime 2>/dev/null)`,
@@ -1166,6 +1194,7 @@ export interface ShardArtifacts {
1166
1194
  finished: string[];
1167
1195
  /** Raw logs of the parts that did not finish (diagnostics · last started file). */
1168
1196
  unfinishedLog?: string;
1197
+ runtimeIdentity?: ReturnType<typeof parseGatePodRuntimeIdentity>;
1169
1198
  /** Original failure ids whose one same-Pod standalone retry completed cleanly. */
1170
1199
  loadFlaky?: string[];
1171
1200
  /** Files for which a same-Pod retry completed, even if it remained failing. */
@@ -1174,16 +1203,20 @@ export interface ShardArtifacts {
1174
1203
  /** Reads a shard Pod's outbox: the part layout ({@link gateShardShell}) or the older single-run one (shard.log · shard.rc · junit.xml). */
1175
1204
  export function readShardArtifacts(dir: string, parts: readonly (readonly string[])[]): ShardArtifacts {
1176
1205
  const read = (name: string) => { const path = join(dir, name); return existsSync(path) ? readFileSync(path, 'utf8') : undefined; };
1206
+ const identityText = read('runtime-identity.json');
1207
+ const runtimeIdentity = identityText === undefined ? undefined : parseGatePodRuntimeIdentity(identityText) ?? undefined;
1177
1208
  const rcOf = (text: string | undefined) => { const value = text?.trim() ?? ''; return /^\d+$/.test(value) && Number.isSafeInteger(Number(value)) ? Number(value) : undefined; };
1178
1209
  const installRc = rcOf(read('install.rc'));
1179
1210
  const install = read('install.log');
1180
1211
  if (installRc === undefined && install === undefined) {
1181
1212
  const junit = read('junit.xml');
1182
1213
  const rc = rcOf(read('shard.rc'));
1183
- return { output: read('shard.log'), ...(junit !== undefined ? { junit } : {}), ...(rc !== undefined ? { rc } : {}), timedOut: false, finished: [] };
1214
+ return { output: read('shard.log'), ...(junit !== undefined ? { junit } : {}), ...(rc !== undefined ? { rc } : {}),
1215
+ ...(runtimeIdentity ? { runtimeIdentity } : {}), timedOut: false, finished: [] };
1184
1216
  }
1185
1217
  const timedOut = read('shard.timeout') !== undefined;
1186
- if (installRc !== 0) return { output: install, ...(installRc !== undefined ? { rc: installRc } : {}), timedOut, finished: [] };
1218
+ if (installRc !== 0) return { output: install, ...(installRc !== undefined ? { rc: installRc } : {}),
1219
+ ...(runtimeIdentity ? { runtimeIdentity } : {}), timedOut, finished: [] };
1187
1220
  const good: Array<{ log: string; junit?: string; rc: 0 | 1; files: readonly string[]; loadFlaky?: string[]; loadFlakyAttemptedFiles?: string[] }> = [];
1188
1221
  const bad: string[] = [];
1189
1222
  const exitAnomaly: string[] = [];
@@ -1261,6 +1294,7 @@ export function readShardArtifacts(dir: string, parts: readonly (readonly string
1261
1294
  ...(attemptedFiles.size ? { loadFlakyAttemptedFiles: [...attemptedFiles].sort() } : {}),
1262
1295
  ...(bad.length ? { unfinishedLog: bad.join('\n') } : {}),
1263
1296
  ...(exitAnomaly.length && !otherBad ? { exitCodeOnly: exitAnomaly } : {}),
1297
+ ...(runtimeIdentity ? { runtimeIdentity } : {}),
1264
1298
  };
1265
1299
  }
1266
1300
 
@@ -1447,6 +1481,7 @@ class StalledPodShards extends Error {
1447
1481
  const exitCode = stalledShards.filter((item) => item.reason === 'exit-code');
1448
1482
  super(`pod sweep incomplete: stalled shards ${stalledShards.map(({ shard }) => shard).join(', ')}`
1449
1483
  + (unmeasured.length ? ` (unmeasured: no test output within ${unmeasured[0]!.stall!.seconds}s of checkout — shards ${[...new Set(unmeasured.map(({ shard }) => shard))].join(', ')})` : '')
1484
+ + (stalledShards.some((item) => item.reason === 'image-pin-mismatch') ? ` (image pin mismatch: ${stalledShards.filter((item) => item.reason === 'image-pin-mismatch').map((item) => item.detail ?? `shard ${item.shard}`).join('; ')})` : '')
1450
1485
  + (exitCode.length ? ` (exit-code anomaly: 0 fail but exit ≠ 0/1 — ${[...new Set(exitCode.flatMap(({ files }) => files))].join(', ')})` : '')
1451
1486
  + (abortedShards ? ` (중단 조각 ${abortedShards}: mid-run-stall)` : ''));
1452
1487
  }
@@ -1676,7 +1711,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
1676
1711
  if (!/^[0-9a-f]{40}$/.test(base)) throw new Error(`rules gate base invalid for ${commit.slice(0, 12)}: ${base}`);
1677
1712
  return base;
1678
1713
  };
1679
- const podSweep = async (tree: string, logDir: string | undefined, pod: NonNullable<GateOptions['pod']>, only?: readonly string[]): Promise<CommandResult> => {
1714
+ const podSweep = async (tree: string, logDir: string | undefined, pod: NonNullable<GateOptions['pod']>, only?: readonly string[], onProgress?: (progress: GateSweepProgress) => void): Promise<CommandResult> => {
1680
1715
  const sweepId = randomUUID();
1681
1716
  const shardCount = pod.shards ?? 24;
1682
1717
  const isolationConcurrency = pod.isolationConcurrency ?? 4;
@@ -1936,6 +1971,13 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
1936
1971
  // GATE-LIVE-OBS — root rows stay stable; each recursive split/retry and tail gets its own row.
1937
1972
  // Otherwise a finished root Job can hide a still-running child (or tail) behind 80/80 done.
1938
1973
  const board: GateShard[] = shards.map((item, index) => ({ id: `pod-${index}`, state: 'pending', plannedMin: Math.round(item.plannedSeconds / 6) / 10 }));
1974
+ const reportProgress = () => onProgress?.({ executor: 'pod', groups: board.map((row) => ({
1975
+ id: row.id, state: row.state === 'retry' ? 'running' : row.state,
1976
+ ...(row.host ? { host: row.host } : {}),
1977
+ ...(row.startedAt ? { startedAtMs: Date.parse(row.startedAt) } : {}),
1978
+ ...(row.endedAt ? { endedAtMs: Date.parse(row.endedAt) } : {}),
1979
+ })) });
1980
+ reportProgress();
1939
1981
  const filesById = new Map(shards.map((item, index) => [`pod-${index}`, [...item.files]]));
1940
1982
  const detailsById = new Map<string, Partial<GateShardLedgerRow>>();
1941
1983
  const observeShard = (row: GateShard) => {
@@ -1972,6 +2014,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
1972
2014
  for (const key of clear) delete next[key];
1973
2015
  if (JSON.stringify(next) === JSON.stringify(row)) return;
1974
2016
  board[index] = next;
2017
+ reportProgress();
1975
2018
  observeShard(next);
1976
2019
  flushBoard();
1977
2020
  };
@@ -2075,6 +2118,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
2075
2118
  let podStages: Record<string, number> | undefined;
2076
2119
  let podLifecycle: Record<string, string> | undefined;
2077
2120
  let imageDigest: string | null = null;
2121
+ let runtimeIdentity: ReturnType<typeof parseGatePodRuntimeIdentity> | undefined;
2078
2122
  let podJobName: string | undefined;
2079
2123
  let batchArtifactsComplete = true;
2080
2124
  let junit: string | undefined;
@@ -2086,6 +2130,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
2086
2130
  let lastFile: string | undefined;
2087
2131
  let jobFailed = false;
2088
2132
  let jobError = '';
2133
+ let imagePinMismatch = false;
2089
2134
  let failureCapture: ReturnType<NonNullable<NonNullable<GateOptions['pod']>['failureEvidence']>> | undefined;
2090
2135
  let jobNameForCapture: string | undefined;
2091
2136
  let jobContextForCapture: string | undefined;
@@ -2198,7 +2243,9 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
2198
2243
  // poolOverride is the injected scheduler used by isolated tests; it has no authenticated kubectl
2199
2244
  // context. Those sweeps intentionally cannot mint a valid baseline receipt (missing observed digest).
2200
2245
  // Production pool sweeps always use this Kubernetes lookup; lookup failure is receipt-invalid only.
2201
- if (!poolOverride && placedOn) {
2246
+ if (tools.podImageDigest) {
2247
+ imageDigest = tools.podImageDigest({ job, host: placedOn ?? 'unobserved' });
2248
+ } else if (!poolOverride && placedOn) {
2202
2249
  try {
2203
2250
  const got = kubectl(['--context', placedOn, '-n', 'elanous-test', 'get', 'pods', '-l', `job-name=${job.job}`, '-o', 'json']);
2204
2251
  if (got.status === 0) {
@@ -2207,10 +2254,18 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
2207
2254
  }
2208
2255
  } catch { /* timings never change the verdict */ }
2209
2256
  }
2257
+ if (pod.pinnedImageDigest && imageDigest !== pod.pinnedImageDigest) {
2258
+ imagePinMismatch = true;
2259
+ debug.log('release-loop.gate', 'image-pin-mismatch', { runId: pod.pinnedRunId ?? null,
2260
+ shard, branch, host: placedOn ?? null, pinnedDigest: pod.pinnedImageDigest, actualDigest: imageDigest,
2261
+ decision: 'reject-job' }, { level: 'error' });
2262
+ throw new Error(`gate image pin mismatch: expected ${pod.pinnedImageDigest}, got ${imageDigest ?? 'unmeasured'}`);
2263
+ }
2210
2264
  jobExitCode = job.exitCode;
2211
2265
  if (job.exitCode !== 0) captureFailure();
2212
2266
  batchArtifactDir = job.artifactsDir;
2213
2267
  const read = readShardArtifacts(job.artifactsDir, parts);
2268
+ runtimeIdentity = read.runtimeIdentity;
2214
2269
  if (logDir) {
2215
2270
  mkdirSync(logDir, { recursive: true });
2216
2271
  let retryArtifactIndex = 0;
@@ -2319,7 +2374,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
2319
2374
  ...(failureCapture.logTail !== undefined ? { logTail: redactedGateLogTail(failureCapture.logTail) } : {}),
2320
2375
  ...(failureCapture.error ? { error: redactSecretText(failureCapture.error) } : {}),
2321
2376
  } } : {}),
2322
- ...(imageDigest ? { imageDigest } : {}),
2377
+ ...(imageDigest ? { imageDigest } : {}), ...(runtimeIdentity ? { runtimeIdentity } : {}),
2323
2378
  ...(fileSeconds !== undefined ? { fileSeconds, testSeconds } : {}), ...(testWallSeconds !== undefined ? { testWallSeconds } : {}),
2324
2379
  ...(podOverheadSeconds !== undefined ? { podOverheadSeconds } : {}),
2325
2380
  ...(unfinishedLogBytes !== undefined ? { unfinishedLogBytes } : {}),
@@ -2353,6 +2408,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
2353
2408
  let namedFailures: number | undefined;
2354
2409
  let unattributedDetail: string | undefined;
2355
2410
  if (descent === 'split') reason = 'incomplete';
2411
+ else if (imagePinMismatch) reason = 'image-pin-mismatch';
2356
2412
  // GATE-POD-STALL-FAILFAST: stopped with no test output — the existing «no output» reason (one fresh retry).
2357
2413
  else if (stall) reason = 'no-output';
2358
2414
  else if (timedOut) reason = 'timeout';
@@ -2471,6 +2527,8 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
2471
2527
  }
2472
2528
  }
2473
2529
  if (reason) {
2530
+ if (reason === 'image-pin-mismatch') return { runs: [], stalled: [{ shard, files: paths, reason,
2531
+ detail: jobError.slice(0, 300), ...(placedOn ? { lastHost: placedOn } : {}) }] };
2474
2532
  // GATE-SKIP-KNOWN-RETRY — a shard whose summary is complete but whose failures could not be read back (unattributed)
2475
2533
  // and whose every named failure is one the previous release already had is not re-run: re-running only re-measures
2476
2534
  // known failures (0.2.18 cut: 109 retry Pods · 1,840 shard-minutes, 4x the first pass). Those failures go to the
@@ -2623,7 +2681,10 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
2623
2681
  // has no base/environment/image binding and cannot authorize this cross-host measurement.
2624
2682
  // The sixth runShardImpl argument is `fresh`: a missing receipt must launch a new Job.
2625
2683
  const freshForReceipt = !!receiptRoot || pod.freshShards === true;
2626
- const done = await withRootSlot(() => runShard(item.files, shard, 0, '', false, freshForReceipt));
2684
+ const done = await withRootSlot(() => {
2685
+ markShard(shard, { state: 'running', startedAt: new Date().toISOString() }, ['waitReason']);
2686
+ return runShard(item.files, shard, 0, '', false, freshForReceipt);
2687
+ });
2627
2688
  shardDurations[shard] = clock() - shardStartedAt;
2628
2689
  const state = !done.stalled.length ? 'done' as const : done.stalled.some((stall) => stall.reason === 'timeout') ? 'timeout' as const : 'failed' as const;
2629
2690
  markShard(shard, { state, rc: done.stalled.length || done.runs.some((run) => run.fail || run.errors) ? 1 : 0, endedAt: new Date().toISOString() }, ['waitReason']);
@@ -2815,6 +2876,33 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
2815
2876
  await quarantine('gate-job-failed');
2816
2877
  throw error;
2817
2878
  }
2879
+ if (pod.pinnedImageDigest) {
2880
+ let actualDigest: string | null = null;
2881
+ if (tools.podImageDigest) {
2882
+ actualDigest = tools.podImageDigest({ job, host: producer?.context ?? 'unobserved' });
2883
+ } else if (!poolOverride) {
2884
+ // Rechecks launch through the pool scheduler too; find the Job in its configured
2885
+ // contexts so a recheck cannot silently escape the run-level image pin.
2886
+ const contexts = [...new Set(parsePodPool(pod.pool).map((member) => member.context))];
2887
+ const observed: string[] = [];
2888
+ for (const context of contexts) {
2889
+ try {
2890
+ const got = kubectl(['--context', context, '-n', 'elanous-test', 'get', 'pods', '-l', `job-name=${job.job}`, '-o', 'json']);
2891
+ if (got.status !== 0) continue;
2892
+ const digest = actualGatePodImageDigest(got.stdout);
2893
+ if (digest) observed.push(digest);
2894
+ } catch { /* inability to observe is rejected below */ }
2895
+ }
2896
+ if (new Set(observed).size === 1) actualDigest = observed[0]!;
2897
+ }
2898
+ if (actualDigest !== pod.pinnedImageDigest) {
2899
+ debug.log('release-loop.gate', 'image-pin-mismatch', { runId: pod.pinnedRunId ?? null,
2900
+ job: job.job, phase: 'recheck', host: producer?.context ?? null,
2901
+ pinnedDigest: pod.pinnedImageDigest, actualDigest, decision: 'reject-job' }, { level: 'error' });
2902
+ await quarantine('gate-image-pin-mismatch');
2903
+ throw new Error(`gate image pin mismatch: expected ${pod.pinnedImageDigest}, got ${actualDigest ?? 'unmeasured'} (recheck)`);
2904
+ }
2905
+ }
2818
2906
  try {
2819
2907
  const artifact = (name: string) => {
2820
2908
  const path = join(job.artifactsDir, name);
@@ -2959,8 +3047,9 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
2959
3047
  recheckPodBatch: (files, commit, pod, timeoutMs) => runPodRecheckBatch(files, commit, pod, timeoutMs),
2960
3048
  get remoteMirror() { return remoteMirror; },
2961
3049
  set remoteMirror(path) { remoteMirror = path; },
2962
- async sweep(tree, logDir, pod, only) {
2963
- if (pod) return podSweep(tree, logDir, pod, only);
3050
+ progressScheduler: tools.progressScheduler ?? scheduleGateProgress,
3051
+ async sweep(tree, logDir, pod, only, onProgress) {
3052
+ if (pod) return podSweep(tree, logDir, pod, only, onProgress);
2964
3053
  const listed = await command('git', ['ls-files', '*.test.*'], tree);
2965
3054
  check(listed, 'git ls-files tests');
2966
3055
  const allFiles = listed.output.split(/\r?\n/).filter((file) => /\.test\.(?:tsx?|jsx?|mts|cts)$/.test(file));
@@ -2982,13 +3071,26 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
2982
3071
  } else cdpPatterns = deriveCdpTestPatterns({ cwd: tree });
2983
3072
  const outputs: string[] = [];
2984
3073
  let total = { pass: 0, fail: 0, errors: 0, ran: 0, files: 0 };
2985
- for (const group of groups) {
2986
- if (!group.paths.length || !files.some((f) => group.paths.some((p) => f === p || f.startsWith(`${p}/`)))) continue;
3074
+ const selectedGroups = groups.filter((group) => group.paths.length && files.some((f) => group.paths.some((p) => f === p || f.startsWith(`${p}/`))));
3075
+ const progressGroups: GateProgressGroup[] = selectedGroups.map((group) => ({ id: group.name, state: 'pending', ...(remote ? { host: remote } : {}) }));
3076
+ onProgress?.({ executor: 'host', groups: progressGroups });
3077
+ for (const group of selectedGroups) {
3078
+ const progress = progressGroups.find((item) => item.id === group.name)!;
3079
+ progress.state = 'running'; progress.startedAtMs = clock();
3080
+ onProgress?.({ executor: 'host', groups: progressGroups });
2987
3081
  const start = Date.now();
2988
3082
  const groupIgnores = [...new Set(cdpPatterns)]
2989
3083
  .filter((pattern) => files.includes(pattern) && group.paths.some((p) => pattern === p || pattern.startsWith(`${p}/`)))
2990
3084
  .flatMap((pattern) => ['--path-ignore-patterns', pattern]);
2991
- const run = await command('bun', ['run', 'test:deterministic', ...groupIgnores, ...group.paths.map(asPath)], tree);
3085
+ let run: CommandResult;
3086
+ try { run = await command('bun', ['run', 'test:deterministic', ...groupIgnores, ...group.paths.map(asPath)], tree); }
3087
+ catch (error) {
3088
+ progress.state = 'failed'; progress.endedAtMs = clock();
3089
+ onProgress?.({ executor: 'host', groups: progressGroups });
3090
+ throw error;
3091
+ }
3092
+ progress.state = run.timedOut ? 'timeout' : run.rc === 0 ? 'done' : 'failed'; progress.endedAtMs = clock();
3093
+ onProgress?.({ executor: 'host', groups: progressGroups });
2992
3094
  if (logDir) {
2993
3095
  mkdirSync(logDir, { recursive: true });
2994
3096
  writeFileSync(join(logDir, `${group.name}.log`), run.output, { mode: 0o600 });
@@ -3120,7 +3222,8 @@ export function saveBaselineFileCache(ledger: string, commit: string, rows: Base
3120
3222
  }
3121
3223
 
3122
3224
  export async function judgeGate(opts: GateOptions, runner: GateRunner = createGateRunner(opts.repo ?? process.cwd(), opts.remote),
3123
- imageProbe: typeof preflightGateImageDigest = preflightGateImageDigest): Promise<GateResult> {
3225
+ imageProbe: typeof preflightGateImageDigest = preflightGateImageDigest,
3226
+ identityProbe: typeof preflightGateExecutionIdentity = preflightGateExecutionIdentity): Promise<GateResult> {
3124
3227
  if (opts.pod?.warmReplay) opts = { ...opts, pod: { ...opts.pod,
3125
3228
  warmShadowScope: { requested: false, attempts: 0 } } };
3126
3229
  const explicitConfig = getElanousConfigDirOverride();
@@ -3167,18 +3270,88 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
3167
3270
  if (!sha.test(opts.commit) || !versionPattern.test(opts.version)
3168
3271
  || (opts.baselineVersion && !versionPattern.test(opts.baselineVersion))
3169
3272
  || (opts.baselineCommit && !sha.test(opts.baselineCommit))) throw new Error('invalid commit or version');
3170
- if (!opts.baselineVersion) throw new Error('previous release version required (--baseline-version)');
3273
+ const baselineVersion = opts.baselineVersion;
3274
+ if (!baselineVersion) throw new Error('previous release version required (--baseline-version)');
3171
3275
  if (opts.rehearsalBaseline && (opts.graphRunId || !opts.pod || !/-rc\.\d+$/.test(opts.version)))
3172
3276
  throw new Error('rehearsal baseline requires a direct Pod gate with an isolated rc version');
3173
3277
  // Observation only. Failure to plan cannot change the gate selector or verdict.
3174
3278
  try { radiusShadow = planGateRadiusShadow({ repo, ledgerRoot: ledger, version: opts.version,
3175
- baselineVersion: opts.baselineVersion, forCommit: opts.commit, instanceRoot: root,
3279
+ baselineVersion, forCommit: opts.commit, instanceRoot: root,
3176
3280
  expectedBaselineCommit: opts.baselineCommit }); }
3177
3281
  catch (error) { debug.log('release-loop.gate', 'radius-shadow-unavailable', { reason: String(error) }); }
3178
3282
  if (!Number.isSafeInteger(opts.recheckFileLimit ?? GATE_HOST_RECHECK_FILE_LIMIT) || (opts.recheckFileLimit ?? GATE_HOST_RECHECK_FILE_LIMIT) < 1)
3179
3283
  throw new Error('invalid gate recheck file limit');
3180
3284
  if (opts.pod && (!opts.pod.pool || !Number.isSafeInteger(opts.pod.shards ?? 24) || (opts.pod.shards ?? 24) < 1
3181
3285
  || !Number.isSafeInteger(opts.pod.shardTimeoutSeconds ?? 1200) || (opts.pod.shardTimeoutSeconds ?? 1200) < 1)) throw new Error('invalid pod sweep options');
3286
+ if (opts.pod && opts.graphRunId) {
3287
+ const components = opts.pod.pool.split(',').map((part) => part.trim()).filter(Boolean);
3288
+ const members = parsePodPool(opts.pod.pool);
3289
+ const memberSpecs = members.map((member) => {
3290
+ const spec = components.find((part) => part.split(/[@:#]/, 1)[0] === member.context);
3291
+ if (!spec) throw new Error(`gate image pin cannot map pool member ${member.context}`);
3292
+ return { context: member.context, spec };
3293
+ });
3294
+ const poolImages = new Map<string, readonly string[]>();
3295
+ const currentImages: Record<string, string | null> = {};
3296
+ for (const member of memberSpecs) {
3297
+ const digest = imageProbe(member.spec, opts.commit);
3298
+ currentImages[member.context] = digest;
3299
+ poolImages.set(member.context, digest ? [digest] : []);
3300
+ }
3301
+ const pinRunId = opts.graphRunId;
3302
+ const pinPath = gateImagePinPath(root, opts.version, pinRunId);
3303
+ const gateImageTag = `${podCommandImageFor([], true).split(':')[0]}:${opts.commit.slice(0, 12)}`;
3304
+ let savedPin: ReturnType<typeof readGateImagePin>;
3305
+ try { savedPin = existsSync(pinPath) ? readGateImagePin(pinPath, pinRunId, gateImageTag, opts.commit) : null; }
3306
+ catch (error) {
3307
+ debug.log('release-loop.gate', 'image-pin', { runId: pinRunId, currentImages, decision: 'fail-closed',
3308
+ reason: 'saved-image-pin-invalid', error: String(error).slice(0, 300) }, { level: 'error' });
3309
+ throw new Error('gate resume refused: saved image digest pin is invalid');
3310
+ }
3311
+ const savedDigest = savedPin?.digest ?? null;
3312
+ let previouslyExecuted = false;
3313
+ if (opts.graphRunId) {
3314
+ // A gate's immutable pin is written before any Pod work. The graph state is only a legacy-run detector:
3315
+ // if it already records an executed gate but this pin does not exist, resume cannot safely invent one.
3316
+ const runPath = join(root, 'graph-runs', 'release-loop', `${opts.graphRunId}.json`);
3317
+ if (existsSync(runPath)) {
3318
+ try {
3319
+ const prior = JSON.parse(readFileSync(runPath, 'utf8')) as { nodes?: Array<{ nodeId?: unknown; executed?: unknown }> };
3320
+ previouslyExecuted = prior.nodes?.some((node) => node.nodeId === 'gate' && node.executed === true) ?? false;
3321
+ } catch { previouslyExecuted = true; }
3322
+ }
3323
+ }
3324
+ if (!savedDigest && previouslyExecuted) {
3325
+ debug.log('release-loop.gate', 'image-pin', { runId: opts.graphRunId, savedDigest: null, currentImages,
3326
+ decision: 'fail-closed', reason: 'resume-pin-missing' }, { level: 'error' });
3327
+ throw new Error('gate resume refused: saved image digest pin is missing');
3328
+ }
3329
+ const resolution = resolveGateImagePin({ ...(savedDigest ? { savedDigest } : {}),
3330
+ ...(!savedDigest ? { initialDigest: [...poolImages.values()].flat().find(Boolean) ?? null } : {}), poolImages });
3331
+ if (!resolution.ok) {
3332
+ debug.log('release-loop.gate', 'image-pin', { runId: opts.graphRunId ?? null, savedDigest, currentImages,
3333
+ decision: 'fail-closed', reason: resolution.reason }, { level: 'error' });
3334
+ throw new Error(`gate image pin refused: ${resolution.reason}`);
3335
+ }
3336
+ const selected = memberSpecs.filter((member) => resolution.eligiblePools.includes(member.context));
3337
+ if (!selected.length) throw new Error('gate image pin refused: no eligible pool');
3338
+ if (!savedDigest) {
3339
+ try { writeGateImagePin(pinPath, pinRunId, resolution.digest, gateImageTag, opts.commit); }
3340
+ catch (error) {
3341
+ debug.log('release-loop.gate', 'image-pin', { runId: pinRunId, pinnedDigest: resolution.digest,
3342
+ currentImages, decision: 'fail-closed', reason: 'pin-write-failed', error: String(error).slice(0, 300) }, { level: 'error' });
3343
+ throw error;
3344
+ }
3345
+ }
3346
+ opts = { ...opts, pod: { ...opts.pod, pool: selected.map((member) => member.spec).join(','),
3347
+ pinnedImageDigest: resolution.digest, pinnedRunId: pinRunId } };
3348
+ debug.log('release-loop.gate', 'image-pin', { runId: opts.graphRunId ?? null, pinnedDigest: resolution.digest,
3349
+ currentImages, eligiblePools: resolution.eligiblePools, excludedPools: members.map((member) => member.context)
3350
+ .filter((context) => !resolution.eligiblePools.includes(context)), decision: savedDigest ? 'resume-pinned' : 'pin-created' });
3351
+ } else if (opts.pod) {
3352
+ debug.log('release-loop.gate', 'image-pin-unscoped', { runId: null, decision: 'not-applicable',
3353
+ reason: 'direct-pod-gate-without-graph-run-id' });
3354
+ }
3182
3355
  if (opts.remote && !/^(?:[\w.-]+@)?[\w.-]+$/.test(opts.remote)) throw new Error('invalid ssh host');
3183
3356
  if (opts.remote) {
3184
3357
  runner.remoteMirror = undefined;
@@ -3189,8 +3362,8 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
3189
3362
  const homePath = home?.output.split(/\r?\n/).find((line) => line.startsWith('/'));
3190
3363
  if (home && (!homePath || !/^\/[^\r\n]*$/.test(homePath))) throw new Error('invalid remote home');
3191
3364
  const mirror = configured.startsWith('~/') ? join(homePath!, configured.slice(2)) : configured;
3192
- const baseSha = opts.baselineCommit ?? baselineReleaseCommit(opts.baselineVersion);
3193
- cachedBaseline(opts.baselineVersion, baseSha);
3365
+ const baseSha = opts.baselineCommit ?? baselineReleaseCommit(baselineVersion);
3366
+ cachedBaseline(baselineVersion, baseSha);
3194
3367
  const present = await runner.command('test', ['-d', mirror], '/tmp');
3195
3368
  if (present.rc === 1) {
3196
3369
  check(await runner.command('mkdir', ['-p', dirname(mirror)], '/tmp'), 'remote mirror parent');
@@ -3219,26 +3392,57 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
3219
3392
  // GATE-PARTIAL: a plan made by `release resume --from gate --partial` for exactly this commit re-runs only its files.
3220
3393
  // Its logs go to cut-partial/ — the full cut/ logs stay whole (the next release reads them as a baseline source).
3221
3394
  const partialRead = readPartialPlanResult(roots, opts.version, opts.commit);
3222
- const explicitPartial = partialRead.plan;
3395
+ const explicitPartial = opts.full ? null : partialRead.plan;
3223
3396
  const rehearsalStampPresent = ['hard-5h', 'hard-mid'].some((step) =>
3224
3397
  existsSync(join(root, 'release', opts.version, 'rehearsal', `${step}.json`)));
3225
- const currentImage = !explicitPartial && opts.graphRunId && opts.pod && rehearsalStampPresent
3226
- ? imageProbe(opts.pod.pool) : null;
3398
+ const currentIdentity = !opts.full && !explicitPartial && opts.graphRunId && opts.pod && rehearsalStampPresent
3399
+ ? identityProbe(opts.pod.pool, process.env, opts.commit) : null;
3400
+ const currentImage = currentIdentity?.imageDigest ?? (!opts.full && !explicitPartial
3401
+ && opts.graphRunId && opts.pod && rehearsalStampPresent ? imageProbe(opts.pod.pool, opts.commit) : null);
3227
3402
  const receiptImage = currentImage ?? (opts.graphRunId && opts.pod && process.env.ELANOUS_GRAPH_CONTEXT
3228
- ? imageProbe(opts.pod.pool) : null);
3229
- const receiptBase = opts.baselineCommit ?? (opts.baselineVersion && roots.some((location) =>
3230
- existsSync(join(location, 'release', opts.baselineVersion!, 'release.json')))
3231
- ? baselineReleaseCommit(opts.baselineVersion) : undefined);
3232
- const radius = !explicitPartial && opts.graphRunId && opts.pod && rehearsalStampPresent
3403
+ ? imageProbe(opts.pod.pool, opts.commit) : null);
3404
+ const receiptBase = opts.baselineCommit ?? (roots.some((location) =>
3405
+ existsSync(join(location, 'release', baselineVersion, 'release.json')))
3406
+ ? baselineReleaseCommit(baselineVersion) : undefined);
3407
+ const radius = !opts.full && !explicitPartial && opts.graphRunId && opts.pod && rehearsalStampPresent
3233
3408
  ? planHardRehearsalRadius({ root, repo, version: opts.version, forCommit: opts.commit,
3234
- graphPins: gateGraphPins(root, opts.graphRunId), envFingerprint: gateEnvFingerprint(process.env), imageDigest: currentImage })
3409
+ graphPins: gateGraphPins(root, opts.graphRunId), envFingerprint: gateEnvFingerprint(process.env), imageDigest: currentImage,
3410
+ executionIdentity: currentIdentity })
3235
3411
  : null;
3236
3412
  if (radius) debug.log('release-loop.gate', 'radius-live-plan', { version: opts.version, reason: radius.reason,
3237
3413
  files: radius.plan?.files.length ?? null, rehearsalRunId: radius.rehearsalRunId ?? null });
3238
- let partial = explicitPartial ?? radius?.plan ?? null;
3414
+ let partial = opts.full ? null : explicitPartial ?? radius?.plan ?? null;
3415
+ let trainReceiptPlan: ReturnType<typeof planGateTrainReceiptCarry> = null;
3416
+ if (!opts.full && !partial && opts.graphRunId && opts.pod) {
3417
+ const receiptPath = gateReceiptRegistryPath(repo);
3418
+ const bun = spawnSync('bun', ['--version'], { cwd: repo, encoding: 'utf8', maxBuffer: SPAWN_SYNC_MAX_BUFFER });
3419
+ if (receiptPath && bun.status === 0 && !bun.error) {
3420
+ try {
3421
+ trainReceiptPlan = planGateTrainReceiptCarry({ repo, commit: opts.commit, receiptPath,
3422
+ bunVersion: bun.stdout.trim(), lockfileHash: gateLockfileHash(repo), envFingerprint: gateReceiptEnvFingerprint(process.env) });
3423
+ if (trainReceiptPlan?.carriedFiles.length) partial = { version: opts.version, priorCommit: trainReceiptPlan.priorCommit,
3424
+ forCommit: opts.commit, files: trainReceiptPlan.files, priorFailures: [], priorErrors: [] };
3425
+ } catch (error) {
3426
+ debug.log('release-loop.gate', 'train-receipt-plan-failed', { version: opts.version, reason: String(error).slice(0, 240) });
3427
+ }
3428
+ }
3429
+ if (trainReceiptPlan) {
3430
+ result.trainReceiptReuse = { carried: trainReceiptPlan.carriedFiles.length, total: trainReceiptPlan.totalFiles,
3431
+ receipts: trainReceiptPlan.receipts.length, reasons: trainReceiptPlan.reasons };
3432
+ debug.log('release-loop.gate', 'train-receipt-plan', { version: opts.version, commit: opts.commit,
3433
+ carried: trainReceiptPlan.carriedFiles.length, total: trainReceiptPlan.totalFiles,
3434
+ ratio: trainReceiptPlan.carriedFiles.length / trainReceiptPlan.totalFiles,
3435
+ receipts: trainReceiptPlan.receipts, selectedFiles: trainReceiptPlan.files.length,
3436
+ reasons: trainReceiptPlan.reasons, action: trainReceiptPlan.carriedFiles.length ? 'run-remaining-tests' : 'full-sweep' });
3437
+ } else if (!opts.full && !explicitPartial && !radius?.plan) {
3438
+ debug.log('release-loop.gate', 'train-receipt-plan', { version: opts.version, commit: opts.commit,
3439
+ carried: 0, total: null, ratio: 0, receipts: [], selectedFiles: null,
3440
+ reasons: ['no safe receipt coverage'], action: 'full-sweep' });
3441
+ }
3442
+ }
3239
3443
  const explicitPodPlan = !!explicitPartial && explicitPartial.kind !== 'recheck' && !!opts.pod;
3240
3444
  const explicitPodDigest = explicitPodPlan ? explicitPartial!.shardImageDigests?.[0] : undefined;
3241
- const probedPodDigest = explicitPodDigest ? imageProbe(opts.pod!.pool) : null;
3445
+ const probedPodDigest = explicitPodDigest ? imageProbe(opts.pod!.pool, opts.commit) : null;
3242
3446
  if (explicitPodPlan && (!explicitPodDigest || probedPodDigest !== explicitPodDigest)) {
3243
3447
  debug.log('release-loop.gate', 'partial-image-preflight-fallback', { version: opts.version,
3244
3448
  reason: !explicitPodDigest || !probedPodDigest ? 'executed-pod-image-digest-missing' : 'executed-pod-image-digest-mismatch' }, { level: 'warn' });
@@ -3274,15 +3478,33 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
3274
3478
  let knownFailures: string[] = [];
3275
3479
  if (opts.pod) {
3276
3480
  try {
3277
- const knownSha = opts.baselineCommit ?? baselineReleaseCommit(opts.baselineVersion);
3278
- const saved = cachedBaseline(opts.baselineVersion, knownSha)?.saved;
3279
- const recorded = saved ? undefined : baselineFromCutLogs(join(ledger, 'release', opts.baselineVersion, 'gate-logs', 'cut'), knownSha);
3481
+ const knownSha = opts.baselineCommit ?? baselineReleaseCommit(baselineVersion);
3482
+ const saved = cachedBaseline(baselineVersion, knownSha)?.saved;
3483
+ const recorded = saved ? undefined : baselineFromCutLogs(join(ledger, 'release', baselineVersion, 'gate-logs', 'cut'), knownSha);
3280
3484
  const trustedKnown = saved ?? (recorded?.complete ? recorded : undefined);
3281
3485
  if (trustedKnown) knownFailures = [...trustedKnown.failures, ...(trustedKnown.errors ?? []), ...loadEnvKnownFailures(repo)];
3282
3486
  } catch (error) { debug.log('release-loop.gate', 'known-failures-unavailable', { error: String(error) }); }
3283
3487
  }
3284
- const durationSource = join(ledger, 'release', opts.baselineVersion, 'gate-logs', 'cut');
3285
- const runCut = (selected: typeof partial) => selected?.kind === 'carry' ? undefined : runner.sweep(cutTree,
3488
+ const durationSource = join(ledger, 'release', baselineVersion, 'gate-logs', 'cut');
3489
+ const runCut = async (selected: typeof partial) => {
3490
+ if (selected?.kind === 'carry') return undefined;
3491
+ const progressFile = opts.graphRunId && /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(opts.graphRunId)
3492
+ ? join(ledger, 'graph-runs', 'release-loop', `${opts.graphRunId}.json.contexts`, 'gate-progress.jsonl') : undefined;
3493
+ let latest: GateSweepProgress | undefined;
3494
+ const emitProgress = () => {
3495
+ if (!latest) return;
3496
+ const nowMs = runner.now?.() ?? Date.now();
3497
+ const event = { version: opts.version, runId: opts.graphRunId ?? null, at: new Date(nowMs).toISOString(),
3498
+ ...summarizeGateProgress({ executor: latest.executor, groups: latest.groups, nowMs }) };
3499
+ debug.log('release-loop.gate', 'progress', event);
3500
+ if (progressFile) {
3501
+ try { mkdirSync(dirname(progressFile), { recursive: true }); appendFileSync(progressFile, `${JSON.stringify(event)}\n`, { mode: 0o600 }); }
3502
+ catch (error) { debug.log('release-loop.gate', 'progress-log-write-failed', { path: progressFile, error: String(error) }, { level: 'warn' }); }
3503
+ }
3504
+ };
3505
+ const cancel = (runner.progressScheduler ?? scheduleGateProgress)(emitProgress, 60_000);
3506
+ try {
3507
+ const sweepResult = await runner.sweep(cutTree,
3286
3508
  join(root, 'release', opts.version, 'gate-logs', selected ? 'cut-partial' : 'cut'),
3287
3509
  opts.pod ? { ...opts.pod, durationSource, ...(knownFailures.length ? { knownFailures } : {}), onSelectionTimed: ({ ms }) => { result.selectionMs = ms; },
3288
3510
  // GATE-RISK-LANE — the watch list is read from this gate's own ledgers (the instance root ⊕ the machine ledger);
@@ -3295,7 +3517,11 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
3295
3517
  receiptRoot: join(ledger, 'release', opts.version, 'gate-logs', 'cut', 'receipts'),
3296
3518
  base40: receiptBase,
3297
3519
  expectedImageDigest: receiptImage } } : undefined,
3298
- selected?.files);
3520
+ selected?.files, (progress) => { latest = progress; emitProgress(); });
3521
+ emitProgress();
3522
+ return sweepResult;
3523
+ } finally { cancel?.(); }
3524
+ };
3299
3525
  let cutRun = await runCut(partial);
3300
3526
  const radiusSelectionMatched = partial?.files && cutRun?.podSweep?.files
3301
3527
  && JSON.stringify([...partial.files].sort()) === JSON.stringify([...cutRun.podSweep.files].sort());
@@ -3376,7 +3602,7 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
3376
3602
  if (!work) throw new Error('gate workspace missing before baseline attribution recheck');
3377
3603
  const recheckTree = join(work, 'baseline-attribution-recheck');
3378
3604
  trees.push(recheckTree);
3379
- const baseShaForRecheck = opts.baselineCommit ?? baselineReleaseCommit(opts.baselineVersion);
3605
+ const baseShaForRecheck = opts.baselineCommit ?? baselineReleaseCommit(baselineVersion);
3380
3606
  await runner.add(recheckTree, baseShaForRecheck);
3381
3607
  for (const dir of [recheckTree, join(recheckTree, 'apps/pwa')])
3382
3608
  check(await runner.command('bun', ['install'], dir), `bun install ${dir}`);
@@ -3455,13 +3681,13 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
3455
3681
  }
3456
3682
  if (partial) result.partial = { priorCommit: partial.priorCommit, files: partial.files.length };
3457
3683
  cutFailures = cut;
3458
- const baseSha = opts.baselineCommit ?? baselineReleaseCommit(opts.baselineVersion);
3684
+ const baseSha = opts.baselineCommit ?? baselineReleaseCommit(baselineVersion);
3459
3685
  baselineCommitForVerdict = baseSha;
3460
- const cached = cachedBaseline(opts.baselineVersion, baseSha);
3461
- const recorded = cached ? undefined : baselineFromCutLogs(join(ledger, 'release', opts.baselineVersion, 'gate-logs', 'cut'), baseSha);
3686
+ const cached = cachedBaseline(baselineVersion, baseSha);
3687
+ const recorded = cached ? undefined : baselineFromCutLogs(join(ledger, 'release', baselineVersion, 'gate-logs', 'cut'), baseSha);
3462
3688
  const trusted = cached?.saved ?? (recorded?.complete ? recorded : undefined);
3463
3689
  result.baselineSource = cached ? cached.source : recorded?.complete ? 'cut-logs' : 'swept';
3464
- debug.log('release-loop.gate', 'baseline', { version: opts.baselineVersion, source: result.baselineSource, commit: baseSha });
3690
+ debug.log('release-loop.gate', 'baseline', { version: baselineVersion, source: result.baselineSource, commit: baseSha });
3465
3691
  let baseTree: string | undefined;
3466
3692
  const getBaseTree = async () => {
3467
3693
  if (!baseTree) {
@@ -3492,7 +3718,7 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
3492
3718
  present = cutFiles.filter((file) => known.has(file));
3493
3719
  }
3494
3720
  result.baselineDeferred = { cutFiles: cutFiles.length, swept: present.length };
3495
- debug.log('release-loop.gate', 'baseline-deferred', { version: opts.baselineVersion, commit: baseSha, cutFiles: cutFiles.length, swept: present.length });
3721
+ debug.log('release-loop.gate', 'baseline-deferred', { version: baselineVersion, commit: baseSha, cutFiles: cutFiles.length, swept: present.length });
3496
3722
  if (present.length) {
3497
3723
  // K9b — the baseline is swept on the same Pod pool as the cut, not on this host (0.2.6: an hour of local baseline
3498
3724
  // after a 23-minute Pod cut).
@@ -3994,6 +4220,7 @@ export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOption
3994
4220
  const arg = args[i];
3995
4221
  if (arg === '--help') return 'help';
3996
4222
  if (arg === '--json') continue;
4223
+ if (arg === '--full') { opts.full = true; continue; }
3997
4224
  if (arg === '--rehearsal-baseline') { opts.rehearsalBaseline = true; continue; }
3998
4225
  // Applied after the loop and only to a Pod gate — on its own it must not turn a local gate into a Pod one (review r5).
3999
4226
  if (arg === '--fresh-shards') { freshShards = true; continue; }
@@ -4077,7 +4304,7 @@ export async function runGateNode(argv: string[], env: NodeJS.ProcessEnv, runner
4077
4304
  debug.log('release-loop.gate', 'executor', { version: opts.version, ...placement, requested: opts.executor ?? null });
4078
4305
  }
4079
4306
  if (opts === 'help') {
4080
- console.log('Usage: bun scripts/release-loop/gate-node.ts --commit <sha> --version <v> [--baseline-version <prev>] [--baseline-commit <sha>] [--remote <ssh-host>] [--remote-mirror <path>] [--pod-pool <pool>] [--pod-shards <n>] [--pod-shard-timeout-seconds <n>] [--recheck-file-limit <n>] [--fresh-shards] [--reroute-shard <pod-N|auto>] [--shard-host <pool-context>] [--executor <pod|host>] [--rehearsal-baseline] [--json]\nWithout flags, input.commit, input.version and input.previousVersion come from the JSON file at ELANOUS_GRAPH_CONTEXT.\n--executor host (input.gateExecutor) ignores every pod option and sweeps on --remote (input.gateRemote, required).\n--rehearsal-baseline pins source graph bytes only for a direct Pod gate on an isolated rc version.');
4307
+ console.log('Usage: bun scripts/release-loop/gate-node.ts --commit <sha> --version <v> [--baseline-version <prev>] [--baseline-commit <sha>] [--remote <ssh-host>] [--remote-mirror <path>] [--pod-pool <pool>] [--pod-shards <n>] [--pod-shard-timeout-seconds <n>] [--recheck-file-limit <n>] [--fresh-shards] [--reroute-shard <pod-N|auto>] [--shard-host <pool-context>] [--executor <pod|host>] [--rehearsal-baseline] [--full] [--json]\nWithout flags, input.commit, input.version and input.previousVersion come from the JSON file at ELANOUS_GRAPH_CONTEXT.\n--executor host (input.gateExecutor) ignores every pod option and sweeps on --remote (input.gateRemote, required).\n--rehearsal-baseline pins source graph bytes only for a direct Pod gate on an isolated rc version.\n--full disables receipt and partial reuse and runs the complete test inventory.');
4081
4308
  return 0;
4082
4309
  }
4083
4310
  version = opts.version;
@@ -4113,6 +4340,18 @@ export async function runGateNode(argv: string[], env: NodeJS.ProcessEnv, runner
4113
4340
  return decision;
4114
4341
  });
4115
4342
  result.autoheal = result.autoheals.find((decision) => decision.action === 'hitl') ?? result.autoheals[0];
4343
+ } else if (versionPattern.test(version)) {
4344
+ // Some failures (for example an unreachable gate host or a measured regression) have no
4345
+ // stalled shard. Keep them in the same durable policy ledger instead of silently skipping autoheal.
4346
+ const cause = gateAutohealCause(result);
4347
+ if (cause) {
4348
+ const signature = createHash('sha256').update(JSON.stringify({ cause,
4349
+ error: result.error ?? null, introduced: [...(result.introduced ?? [])].sort() })).digest('hex');
4350
+ result.autoheal = decideAndRecordGateAutoheal({ cause, signature, version, runId: graphRunId ?? 'direct',
4351
+ shardKey: 'gate', shardKeys: ['gate'], shardElapsedMs: result.durationMs, overallElapsedMs: result.durationMs,
4352
+ storeRoot: autohealStoreRoot || releaseLedgerRoot() });
4353
+ debug.log('release-loop.gate-autoheal', 'decision', { runId: graphRunId, version, ...result.autoheal });
4354
+ }
4116
4355
  }
4117
4356
  if (result.error) console.error(result.error);
4118
4357
  debug.log('release-loop.gate', 'result', { version, outcome: result.outcome, ...(placement ?? {}) });