elanous 0.2.40 → 0.2.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (685) hide show
  1. package/README.md +7 -7
  2. package/apps/pwa/out/404/index.html +1 -1
  3. package/apps/pwa/out/404.html +1 -1
  4. package/apps/pwa/out/_next/static/{HjeK7zONHfR7op1hKXp4k → X1ucLw10Cds_h614NoEnh}/_buildManifest.js +1 -1
  5. package/apps/pwa/out/_next/static/chunks/0244e5b5.6fc7670f8223d82d.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/{4769.9fd582071301f382.js → 1063.2fb8a9dc55c731f2.js} +1 -1
  7. package/apps/pwa/out/_next/static/chunks/1121.13ba7a8aff19df81.js +36 -0
  8. package/apps/pwa/out/_next/static/chunks/1147-5196d19c222051b3.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/{470.51a72a2348306455.js → 1262.e804feb8fd1acb8c.js} +2 -2
  10. package/apps/pwa/out/_next/static/chunks/1319.b9b815ae0f181e06.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/1345-31427b833438f521.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/{7219-15bdd9a80661af8e.js → 1455-530ca16c7e71baff.js} +1 -1
  13. package/apps/pwa/out/_next/static/chunks/{1281.d81877e35b9068f4.js → 1487.2f67b79444062da6.js} +1 -1
  14. package/apps/pwa/out/_next/static/chunks/1574.878c18ecc97b1bd4.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/1586.fafc33a9a5835cb7.js +32 -0
  16. package/apps/pwa/out/_next/static/chunks/1744.855fd60ae4c4a8c8.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/193.21a47a3e07ef6860.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/1945.7b54990d578c119b.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/2004.e839fda7ea5a738a.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/2044-dd681a5c08779ff8.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/2101.2248decb38829b03.js +286 -0
  22. package/apps/pwa/out/_next/static/chunks/2160.a097e7b138ae1c55.js +23 -0
  23. package/apps/pwa/out/_next/static/chunks/2300.41ea70ac1bef1446.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/2357-07b5eeb5fb12628d.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/2364.28652020695a6db3.js +34 -0
  26. package/apps/pwa/out/_next/static/chunks/{1350.59f40e39e02d91b1.js → 2456.b6de0e1c05f28843.js} +1 -1
  27. package/apps/pwa/out/_next/static/chunks/2526.c3e50ac1fec064bd.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2549-cda50b716ff4250c.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/2550-5f1e88f615568045.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/261-319078c954e40d66.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/2690.0521c738b7749931.js +115 -0
  32. package/apps/pwa/out/_next/static/chunks/{5436.fdf6fa8ae3e9588d.js → 2974.595d1e29d040cb6a.js} +1 -1
  33. package/apps/pwa/out/_next/static/chunks/{5383.bdfa5d886e39a826.js → 3207.a756c95e9ec8c3c8.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/3285.89ee2f298682f5e5.js +93 -0
  35. package/apps/pwa/out/_next/static/chunks/{2177.df8547f9d7215c4e.js → 3347.f846266b5e5f3ede.js} +3 -3
  36. package/apps/pwa/out/_next/static/chunks/3447.06edc7b092021417.js +41 -0
  37. package/apps/pwa/out/_next/static/chunks/{9184.45afbf44fec87574.js → 3482.a9ae9d2ad6a5bd97.js} +1 -1
  38. package/apps/pwa/out/_next/static/chunks/3507.97e1977bc4d5fb0a.js +2 -0
  39. package/apps/pwa/out/_next/static/chunks/3510-9ca392698bfe4391.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/3541.772e5ce425aaa611.js +24 -0
  41. package/apps/pwa/out/_next/static/chunks/{8099.1cccf31c1baa7e1c.js → 3741.9cf76acaa2ee5298.js} +1 -1
  42. package/apps/pwa/out/_next/static/chunks/3837.a210d7d0db8d8abd.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/3898-955c896f394217a6.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/3947-ef57acea76eaa8d2.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/{4864a2ca.1c1c280a7772c5e5.js → 3d0154af.5331fec737ffe8c3.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/4037-3b82ebc7d78afdc7.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/4043.6b91203436c9c86b.js +309 -0
  48. package/apps/pwa/out/_next/static/chunks/405.9022d4a478202b67.js +77 -0
  49. package/apps/pwa/out/_next/static/chunks/417-fb2cc9a9910e5968.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/{3330.7443183b7e6fdbdf.js → 4276.336d76d858fcdf1b.js} +1 -1
  51. package/apps/pwa/out/_next/static/chunks/4302.6ced9aa4535de947.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/4320.583cbab64db6be02.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/4768-8eac183fd291ca53.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/4770.769c9cd9c7034eba.js +148 -0
  55. package/apps/pwa/out/_next/static/chunks/4895.65605ec3e6504e89.js +74 -0
  56. package/apps/pwa/out/_next/static/chunks/{3b355b3d.6f73c98bf6f9c749.js → 50061469.0326382241ef3b2d.js} +1 -1
  57. package/apps/pwa/out/_next/static/chunks/5112.4b9f300124fab7b3.js +149 -0
  58. package/apps/pwa/out/_next/static/chunks/5335.375ee922bcd5bc83.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/5342.023a2e729689b320.js +27 -0
  60. package/apps/pwa/out/_next/static/chunks/{8d173e38.18a2f7b4e1848bdb.js → 53c73f84.373b6160adfa89a4.js} +1 -1
  61. package/apps/pwa/out/_next/static/chunks/{1537-6bb97b5214928ea9.js → 5431-72a1dbde59400691.js} +1 -1
  62. package/apps/pwa/out/_next/static/chunks/5440-4236de26c5ea5856.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/5486-b318c165f645d8ba.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/5506-c7d2ec25c62a7c94.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/5570-c66ecbea26b4e3b1.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/5696.ffca5aa259d8451e.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/5793-8c584aea2aa87560.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/5848-6de6b8c173351a90.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/5885.0a3e52c8faccec98.js +287 -0
  70. package/apps/pwa/out/_next/static/chunks/5999.94e3c83040200f31.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/5a7901d6-2d8d9b5587cf9042.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/{261.0d471e8aded799f3.js → 6131.348b7d6bdcd267ce.js} +3 -3
  73. package/apps/pwa/out/_next/static/chunks/6164.86133a0172611184.js +90 -0
  74. package/apps/pwa/out/_next/static/chunks/{9175.2de5209a5ce6f68a.js → 6377.bc7df239edbff244.js} +1 -1
  75. package/apps/pwa/out/_next/static/chunks/6515.e07e8b2b8a707a87.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/6522-c005514f87d18982.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/{2226.2cf61ec781827219.js → 6632.0eac1c554f8f2ef3.js} +2 -2
  78. package/apps/pwa/out/_next/static/chunks/6642.648adb1767f39ecf.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/6848.138805a09d001d09.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/7049-c09e2b7a253a24f3.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/7072.1e00447d9fa39972.js +104 -0
  82. package/apps/pwa/out/_next/static/chunks/7106.64201c539c5e220c.js +94 -0
  83. package/apps/pwa/out/_next/static/chunks/7151-1f742b455986530e.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/7199.18d6b25e001154ce.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/7279-c199ea83f34338fa.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/728b938d.ffaa7ad583d60e58.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/7309.171c3de54e0322f1.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/733.7a1ab3342e03306f.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/{b84cad00.594027ebe0531fe2.js → 73d2eb0e.4966ad55293c801a.js} +3 -3
  90. package/apps/pwa/out/_next/static/chunks/{6257.c0037b8949239f5e.js → 7411.9b4b8b747dc7b802.js} +2 -2
  91. package/apps/pwa/out/_next/static/chunks/7504.d5cf1e7cd89b1b08.js +24 -0
  92. package/apps/pwa/out/_next/static/chunks/7573-0fd4af62d65d4598.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/759.6f8267df4689c401.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/7825-668b046f72f14483.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/7852-1538768b6eb19388.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/7868.cff810430bb63c91.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/{9189505a.980427ac048c8c60.js → 7a98bc80.fa935989e829f9c2.js} +1 -1
  98. package/apps/pwa/out/_next/static/chunks/8084-dfcc796874ab4e07.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/8138.6c10b2a356d52fca.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/{2566.beaffc3a5e1fcd2e.js → 8262.ddc03e06b733f184.js} +1 -1
  101. package/apps/pwa/out/_next/static/chunks/8266.6bf9b131b2584678.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/8403.7b81d5e64074a725.js +8 -0
  103. package/apps/pwa/out/_next/static/chunks/8470.1998cbd0866a7eac.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8505.fd803c6c33b54cc3.js +79 -0
  105. package/apps/pwa/out/_next/static/chunks/{8272-025653884f9645d2.js → 8546-6f03f8ea34dd61d9.js} +1 -1
  106. package/apps/pwa/out/_next/static/chunks/{6051-0ae955eedb10adb9.js → 8673-bab689a55f7e13b9.js} +3 -3
  107. package/apps/pwa/out/_next/static/chunks/8687-246fda8433d3477c.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/898-280022fb082936bc.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/9098-50eefc1844212e3d.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/9211-330b6736a63d4cb3.js +1 -0
  111. package/apps/pwa/out/_next/static/chunks/{4351.0f37a6db2b34d50b.js → 9249.976f1e69a9d80754.js} +1 -1
  112. package/apps/pwa/out/_next/static/chunks/9255-cf88fceda5666645.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/9306-e523ba58bb8adc42.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/9324.a9866d717ba62cb6.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/9325.a8510e8aa87c8143.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/9353-12bfff0c130104cf.js +1 -0
  117. package/apps/pwa/out/_next/static/chunks/9415.69e4a21c60c7d2db.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/{6749.0744ce6eb08d40e9.js → 9427.96aa912ddb5801e0.js} +16 -16
  119. package/apps/pwa/out/_next/static/chunks/9479-154a697d4b6e4e98.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/9540-65aa6ccc31b3fd78.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/{7611.85f045bf232053ac.js → 9633.ce5bddc56784da72.js} +1 -1
  122. package/apps/pwa/out/_next/static/chunks/{3746-f80a78f220bf37b5.js → 9656-374232558fd528d0.js} +1 -1
  123. package/apps/pwa/out/_next/static/chunks/9832.f4a98c878ecf2c0a.js +62 -0
  124. package/apps/pwa/out/_next/static/chunks/9865.f094440645f02d98.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/9a852a62-6192a6ea9346ced3.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-e772b4710df5d936.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/approvals/page-4361368e6a76b967.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-802f349fdd65248d.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/board/page-7f35513988a2a495.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/botlab/page-798121656540e33e.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/bots/page-20a089490affe98c.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/ceo/page-e5e4ec786d70e604.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/chat/page-d12fe5d0801430e4.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/consult/page-d8cf21022fecc026.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/control/page-21ce565c9c57ee97.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/decisions/page-b56563025f619c9b.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/design-check/page-52f14dd133da8348.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/editor/page-c0ed7fdcdba76633.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/exec/page-af8c3020cb520f15.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/field/page-72eda67af9f5e35c.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/healthz/route-e772b4710df5d936.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/inside/page-67a6620d31f0a46f.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/intake/page-f059363aa0880ec2.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/layout-f9f917d6a57f69fb.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/live/page-d3ae55eee2f0826c.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/live-run/{page-83d27757e2b8060a.js → page-0e3d7b9421b79dac.js} +1 -1
  147. package/apps/pwa/out/_next/static/chunks/app/loops/page-ba738f7e233eb211.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/market/page-33438b98c74fd599.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-0b34d5fd990d793a.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/morning/page-0f291d3c8fd55a3a.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/not-found-3fced192be5279c1.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/observatory/page-04cb3cf182a7c1ea.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-6ccbd2ab23a21542.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-aa0518c48a5f4e56.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4b32226d341ad7d7.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3224fd8f6383b61f.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/page-efbc97459f2914e2.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/reflection/page-2fe487d88cd5d97c.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-48861dd1a858bab7.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/sessions/page-197d5d4d21d228ea.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a763ba89fbdd9c27.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/settings/page-7edb253510b1c136.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0c875702c61965ea.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/setup/layout-e772b4710df5d936.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/setup/page-e425c951151095f7.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/share/page-9c90e0aec4e27f77.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/showroom/page-733e09a02e727d42.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/tasks/page-4f899e14c34d77a1.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/term/{page-139d722de1dcd6c1.js → page-bf067887b3307423.js} +1 -1
  170. package/apps/pwa/out/_next/static/chunks/app/today/page-7af10868ff87d04e.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/trace/page-efe5304057aa11c7.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/vault/page-76bcd29aaa8a26c3.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-4cfd88e2273ce50f.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/app/workflows/page-0a1ddb47efba0a32.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2a176c707152c892.js +1 -0
  176. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-efe03031b8797fbf.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/{409c787b.e48c5bec8ec10456.js → b450776b.3db34d8d965f82d3.js} +1 -1
  178. package/apps/pwa/out/_next/static/chunks/{ca3702f8.2913182e73889fa2.js → e8543907.5b040a5f22a0b0e5.js} +7 -7
  179. package/apps/pwa/out/_next/static/chunks/framework-d0d2d673bfd8a8a9.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-4c9fceb2baa09cb7.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/main-app-b0ab8d8fc9a97556.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_app-7e2cc62a2e5cec96.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/pages/_error-22324935f753ec5e.js +1 -0
  184. package/apps/pwa/out/_next/static/chunks/webpack-9dc1bfa25334998a.js +1 -0
  185. package/apps/pwa/out/_next/static/css/{b9d8151e48998417.css → 62c858ad53854f47.css} +1 -1
  186. package/apps/pwa/out/approvals/index.html +1 -1
  187. package/apps/pwa/out/approvals/index.txt +22 -22
  188. package/apps/pwa/out/autopilot/index.html +1 -1
  189. package/apps/pwa/out/autopilot/index.txt +22 -22
  190. package/apps/pwa/out/board/index.html +1 -1
  191. package/apps/pwa/out/board/index.txt +22 -22
  192. package/apps/pwa/out/botlab/index.html +1 -1
  193. package/apps/pwa/out/botlab/index.txt +21 -21
  194. package/apps/pwa/out/bots/index.html +1 -1
  195. package/apps/pwa/out/bots/index.txt +21 -21
  196. package/apps/pwa/out/ceo/index.html +1 -1
  197. package/apps/pwa/out/ceo/index.txt +22 -22
  198. package/apps/pwa/out/chat/index.html +1 -1
  199. package/apps/pwa/out/chat/index.txt +21 -21
  200. package/apps/pwa/out/consult/index.html +1 -1
  201. package/apps/pwa/out/consult/index.txt +22 -22
  202. package/apps/pwa/out/control/index.html +1 -1
  203. package/apps/pwa/out/control/index.txt +22 -22
  204. package/apps/pwa/out/decisions/index.html +1 -1
  205. package/apps/pwa/out/decisions/index.txt +21 -21
  206. package/apps/pwa/out/design-check/index.html +1 -1
  207. package/apps/pwa/out/design-check/index.txt +22 -22
  208. package/apps/pwa/out/editor/index.html +1 -1
  209. package/apps/pwa/out/editor/index.txt +23 -24
  210. package/apps/pwa/out/exec/index.html +1 -1
  211. package/apps/pwa/out/exec/index.txt +22 -22
  212. package/apps/pwa/out/field/index.html +1 -1
  213. package/apps/pwa/out/field/index.txt +22 -22
  214. package/apps/pwa/out/healthz +1 -1
  215. package/apps/pwa/out/index.html +1 -1
  216. package/apps/pwa/out/index.txt +21 -21
  217. package/apps/pwa/out/inside/index.html +1 -1
  218. package/apps/pwa/out/inside/index.txt +22 -22
  219. package/apps/pwa/out/intake/index.html +1 -1
  220. package/apps/pwa/out/intake/index.txt +22 -22
  221. package/apps/pwa/out/live/index.html +1 -1
  222. package/apps/pwa/out/live/index.txt +23 -24
  223. package/apps/pwa/out/live-run/index.html +1 -1
  224. package/apps/pwa/out/live-run/index.txt +23 -24
  225. package/apps/pwa/out/loops/index.html +1 -1
  226. package/apps/pwa/out/loops/index.txt +21 -21
  227. package/apps/pwa/out/market/index.html +1 -1
  228. package/apps/pwa/out/market/index.txt +21 -21
  229. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  230. package/apps/pwa/out/missions/placeholder/index.txt +21 -21
  231. package/apps/pwa/out/morning/index.html +1 -1
  232. package/apps/pwa/out/morning/index.txt +22 -22
  233. package/apps/pwa/out/observatory/index.html +1 -1
  234. package/apps/pwa/out/observatory/index.txt +22 -22
  235. package/apps/pwa/out/ops/checklist/index.html +1 -1
  236. package/apps/pwa/out/ops/checklist/index.txt +21 -21
  237. package/apps/pwa/out/ops/release/index.html +1 -1
  238. package/apps/pwa/out/ops/release/index.txt +21 -21
  239. package/apps/pwa/out/ops/seats/index.html +1 -1
  240. package/apps/pwa/out/ops/seats/index.txt +21 -21
  241. package/apps/pwa/out/outputs/index.html +1 -1
  242. package/apps/pwa/out/outputs/index.txt +21 -21
  243. package/apps/pwa/out/reflection/index.html +1 -1
  244. package/apps/pwa/out/reflection/index.txt +22 -22
  245. package/apps/pwa/out/scheduler/index.html +1 -1
  246. package/apps/pwa/out/scheduler/index.txt +22 -22
  247. package/apps/pwa/out/sessions/index.html +1 -1
  248. package/apps/pwa/out/sessions/index.txt +21 -21
  249. package/apps/pwa/out/settings/devices/index.html +1 -1
  250. package/apps/pwa/out/settings/devices/index.txt +22 -22
  251. package/apps/pwa/out/settings/index.html +1 -1
  252. package/apps/pwa/out/settings/index.txt +22 -22
  253. package/apps/pwa/out/setup/done/index.html +1 -1
  254. package/apps/pwa/out/setup/done/index.txt +22 -22
  255. package/apps/pwa/out/setup/index.html +1 -1
  256. package/apps/pwa/out/setup/index.txt +22 -22
  257. package/apps/pwa/out/share/index.html +1 -1
  258. package/apps/pwa/out/share/index.txt +22 -22
  259. package/apps/pwa/out/showroom/index.html +1 -1
  260. package/apps/pwa/out/showroom/index.txt +21 -21
  261. package/apps/pwa/out/tasks/index.html +1 -1
  262. package/apps/pwa/out/tasks/index.txt +22 -22
  263. package/apps/pwa/out/term/index.html +1 -1
  264. package/apps/pwa/out/term/index.txt +22 -22
  265. package/apps/pwa/out/today/index.html +1 -1
  266. package/apps/pwa/out/today/index.txt +22 -22
  267. package/apps/pwa/out/trace/index.html +1 -1
  268. package/apps/pwa/out/trace/index.txt +22 -22
  269. package/apps/pwa/out/vault/index.html +1 -1
  270. package/apps/pwa/out/vault/index.txt +22 -22
  271. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  272. package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
  273. package/apps/pwa/out/workflows/index.html +1 -1
  274. package/apps/pwa/out/workflows/index.txt +22 -22
  275. package/apps/pwa/out/workspace/index.html +1 -1
  276. package/apps/pwa/out/workspace/index.txt +22 -22
  277. package/apps/pwa/out/worktrees/index.html +1 -1
  278. package/apps/pwa/out/worktrees/index.txt +22 -22
  279. package/bin/elanous.mjs +6 -1
  280. package/catalog/models/openai/gpt-6.1-sol.yaml +16 -0
  281. package/catalog/models/openai/gpt-6.1-terra.yaml +16 -0
  282. package/docs/goal-context/04-mock-scenarios.md +13 -0
  283. package/graphs/catalog/catalog.yaml +1 -1
  284. package/graphs/demo/persona-teacher/lesson-pack.yaml +16 -0
  285. package/graphs/demo/persona-teacher/recipes.yaml +17 -0
  286. package/graphs/demo/persona-teacher/weekly-lesson.yaml +36 -0
  287. package/graphs/heal/heal-loop.yaml +1 -0
  288. package/graphs/overlays/stop-heal-ladder.yaml +136 -2
  289. package/graphs/release/release-loop.yaml +3 -1
  290. package/graphs/units/gate/gate.yaml +20 -0
  291. package/graphs/units/git/git.yaml +26 -0
  292. package/graphs/units/implement/implement.yaml +19 -0
  293. package/graphs/units/landing/landing.yaml +18 -0
  294. package/graphs/units/review/review.yaml +20 -0
  295. package/graphs/units/verbs/recipes.yaml +13 -10
  296. package/graphs/units/verbs/research.yaml +20 -0
  297. package/graphs/work/research-report.yaml +21 -3
  298. package/package.json +1 -1
  299. package/scripts/disk-steward.ts +46 -0
  300. package/scripts/fleet/node-setup.sh +50 -0
  301. package/scripts/graph-units/verbs/research.ts +55 -0
  302. package/scripts/heal/run-role.ts +99 -5
  303. package/scripts/land-global-checks.ts +6 -1
  304. package/scripts/lib/decompose-ab.ts +1 -0
  305. package/scripts/lib/fixtures/tui-regress/help-insufficient-commands.txt +15 -0
  306. package/scripts/lib/fixtures/tui-regress/help-live.txt +42 -0
  307. package/scripts/lib/fixtures/tui-regress/help-missing-keys.txt +21 -0
  308. package/scripts/lib/fixtures/tui-regress/help-original.txt +51 -0
  309. package/scripts/lib/fixtures/tui-regress/help-shifted-input.txt +26 -0
  310. package/scripts/lib/tui-regress-checks.ts +33 -13
  311. package/scripts/llm-cost-ledger.ts +179 -8
  312. package/scripts/measure-nl-routing-corpus.ts +10 -2
  313. package/scripts/persona-teacher/weekly-lesson-node.ts +172 -0
  314. package/scripts/public-copy-lint.ts +163 -19
  315. package/scripts/ratchets/train-side-invariants.sh +0 -1
  316. package/scripts/release-loop/gate-autoheal.ts +117 -0
  317. package/scripts/release-loop/gate-baseline-receipt.ts +55 -10
  318. package/scripts/release-loop/gate-diff.ts +22 -3
  319. package/scripts/release-loop/gate-image-pin.ts +81 -0
  320. package/scripts/release-loop/gate-node-sim.ts +207 -0
  321. package/scripts/release-loop/gate-node.ts +489 -62
  322. package/scripts/release-loop/gate-pod-image-sticky-plan.ts +99 -0
  323. package/scripts/release-loop/gate-radius-live.ts +97 -13
  324. package/scripts/release-loop/gate-shard-dispatch.ts +43 -0
  325. package/scripts/release-loop/gate-shard-load-flaky.ts +256 -0
  326. package/scripts/release-loop/gate-train-receipts.ts +122 -0
  327. package/scripts/release-loop/gate-triage-node.ts +70 -10
  328. package/scripts/release-loop/resume-release.ts +330 -23
  329. package/scripts/release-loop/traffic-node.ts +6 -3
  330. package/scripts/release-loop/unattended-release.ts +4 -2
  331. package/scripts/shadow-authoring-probe.ts +20 -0
  332. package/scripts/ux-sim.ts +37 -3
  333. package/scripts/warm-pool-provider-config.ts +263 -0
  334. package/skills/omni-crawl/SKILL.md +5 -2
  335. package/skills/omni-crawl/scripts/main.ts +84 -23
  336. package/src/acp/core-turn-bridge.ts +99 -4
  337. package/src/agent/registry.ts +2 -0
  338. package/src/agent/task-notification.ts +25 -5
  339. package/src/agent/types.ts +6 -0
  340. package/src/agent-mission/mission-cli.ts +24 -0
  341. package/src/agent-mission/mission-out.ts +595 -0
  342. package/src/agent-mission/tool-install.ts +88 -2
  343. package/src/agent-substrate/context-strategy.ts +4 -4
  344. package/src/agent-substrate/self-review-cli.ts +267 -40
  345. package/src/auto-research/research-turn.ts +133 -51
  346. package/src/autopilot/discovery/wiki-claim-proposer.ts +22 -1
  347. package/src/autopilot/pr-manager.ts +38 -7
  348. package/src/autopilot/pty-control-loop.ts +2 -0
  349. package/src/boot/daemon-tools/self-implement.ts +46 -9
  350. package/src/budget/shadow-arms.ts +52 -0
  351. package/src/capture/tui-self-report.ts +22 -6
  352. package/src/chat/index.ts +1 -1
  353. package/src/chat/pickers/state.ts +7 -1
  354. package/src/cli/fixtures/review-loop-breaker/opwaiver-32646-1694ccc7a170.json +1 -0
  355. package/src/cli/fixtures/review-loop-breaker/opwaiver-32853-bd8f73595c83.json +1 -0
  356. package/src/cli/fixtures/review-loop-breaker/opwaiver-33097-1cc85900413c.json +1 -0
  357. package/src/cli/fixtures/review-loop-breaker/opwaiver-33106-972f31e4a3ca.json +1 -0
  358. package/src/cli/fixtures/review-loop-breaker/opwaiver-33113-3ab51f05968f.json +1 -0
  359. package/src/cli/fixtures/review-loop-breaker/pr-33097-1cc85900413c.json +1 -0
  360. package/src/cli/log-event-names.ts +5 -0
  361. package/src/cli/mcp-login.ts +8 -18
  362. package/src/cli/msg-cli.ts +57 -1
  363. package/src/cli/pr-land-heavy-remote.ts +47 -9
  364. package/src/cli/pr-land-overlap-evidence.ts +100 -1
  365. package/src/cli/pr-land-pod-shadow-job.ts +60 -0
  366. package/src/cli/release-checklist-read-cli.ts +20 -10
  367. package/src/cli/release-cli.ts +27 -1
  368. package/src/cli/research-cli.ts +4 -5
  369. package/src/cli/sim-cli.ts +7 -1
  370. package/src/cli/tasks-cli.ts +12 -4
  371. package/src/codex/models.ts +30 -15
  372. package/src/compact/auto.ts +8 -1
  373. package/src/control-plane/ledger.ts +52 -2
  374. package/src/control-plane/server.ts +16 -2
  375. package/src/core-turn/run-core-turn.ts +313 -89
  376. package/src/core-turn/run-goal-loop.ts +106 -2
  377. package/src/core-turn/types.ts +2 -0
  378. package/src/dashboard/acp-boot.ts +72 -3
  379. package/src/dashboard/classic-sidetab-mission-job.ts +58 -0
  380. package/src/dashboard/classic-sidetab-mission.ts +185 -0
  381. package/src/dashboard/index.ts +155 -15
  382. package/src/dashboard/input/agent-wake.ts +23 -1
  383. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +9 -0
  384. package/src/dashboard/mission-cli-consumer.ts +153 -0
  385. package/src/dashboard/seat-boot-preamble.ts +171 -1
  386. package/src/dashboard/side-basic-runtime.ts +54 -1
  387. package/src/dashboard/sidetab-runtime.ts +90 -1
  388. package/src/dashboard/skill-catalog-preamble.ts +101 -0
  389. package/src/dashboard/skill-detection-runtime.ts +55 -15
  390. package/src/dashboard/slash-runtime/dashboard-handlers.ts +15 -0
  391. package/src/dashboard/turn-preamble.ts +65 -16
  392. package/src/dashboard/turn-prompt-runtime.ts +4 -2
  393. package/src/disk-steward/judge.ts +73 -0
  394. package/src/disk-steward/ledger.ts +122 -0
  395. package/src/disk-steward/steward.ts +373 -0
  396. package/src/domains/guardian-tick.ts +191 -2
  397. package/src/graph-runner/graph-cli.ts +44 -7
  398. package/src/graph-runner/graph-tick.ts +10 -2
  399. package/src/graph-runner/in-process-registry.ts +37 -0
  400. package/src/graph-runner/runner.ts +34 -4
  401. package/src/grok/agent-search.ts +14 -4
  402. package/src/grok/credential.ts +28 -11
  403. package/src/grounding/ref-index.ts +19 -0
  404. package/src/grounding/sources-cli.ts +9 -1
  405. package/src/harness/harness-cli-command.ts +267 -14
  406. package/src/harness/harness-incidents.ts +312 -7
  407. package/src/harness/harness-pod-dispatch.ts +81 -15
  408. package/src/harness/harness-space.ts +15 -1
  409. package/src/harness/harness-stop.ts +155 -11
  410. package/src/harness/progress-worker-entry.ts +59 -0
  411. package/src/harness/progress-worker-runtime.ts +11 -0
  412. package/src/index.ts +52 -7
  413. package/src/instance/dev-project.ts +25 -0
  414. package/src/instance/spawn-elanous.ts +6 -4
  415. package/src/intelligence-map/model-alias.ts +6 -5
  416. package/src/intelligence-map/model-catalog.ts +59 -2
  417. package/src/llm/model-defaults.ts +4 -4
  418. package/src/llm.ts +208 -19
  419. package/src/loops/orchestrator/coordinator-shadow.ts +226 -0
  420. package/src/loops/orchestrator/tick.ts +61 -0
  421. package/src/maturity/feature-maturity.ts +1 -1
  422. package/src/mcp/mcp-oauth.ts +3 -1
  423. package/src/model-tier/llm-tier-map.ts +9 -9
  424. package/src/model-tier/turn-tier.ts +1 -1
  425. package/src/msg/msg-mirror-github.ts +80 -0
  426. package/src/msg/msg-mirror-policy.ts +61 -0
  427. package/src/msg/msg-mirror-runner.ts +219 -0
  428. package/src/msg/msg-mirror-state.ts +33 -0
  429. package/src/msg/msg-store.ts +11 -0
  430. package/src/native-tool-catalog.ts +2 -2
  431. package/src/nexus/api/harness-traversal.ts +11 -0
  432. package/src/output-truncation.ts +41 -12
  433. package/src/pr-tool/census.ts +3 -2
  434. package/src/project/project-store.ts +48 -1
  435. package/src/pty-shell/tui-run-state.ts +188 -2
  436. package/src/release-loop/checklist-target-path-hint.ts +13 -0
  437. package/src/release-loop/checklist.ts +9 -1
  438. package/src/release-loop/gate-host-reservation.ts +144 -0
  439. package/src/release-loop/release-schedule.ts +90 -1
  440. package/src/repl/index.ts +43 -2
  441. package/src/research/axis-fanout.ts +121 -16
  442. package/src/research/bloom-exemplars.ts +137 -0
  443. package/src/research/gap-followup.ts +13 -1
  444. package/src/research/plugin-discovery.ts +36 -14
  445. package/src/research-bridge/invoke.ts +22 -5
  446. package/src/research-bridge/types.ts +2 -0
  447. package/src/self-dev/ask-launch-flow.ts +39 -25
  448. package/src/self-dev/decompose.ts +119 -5
  449. package/src/self-dev/dev-cli.ts +17 -17
  450. package/src/self-dev/graph-front-nodes.ts +23 -0
  451. package/src/self-dev/launch-preflight-text.ts +5 -6
  452. package/src/self-dev/launch-preflight.ts +9 -4
  453. package/src/self-dev/orchestrate.ts +27 -2
  454. package/src/self-dev/review-provider-fallback.ts +9 -2
  455. package/src/self-dev/run-supervisor.ts +226 -6
  456. package/src/self-dev/run-types.ts +7 -0
  457. package/src/self-implement/doctor-visit.ts +88 -0
  458. package/src/self-implement/gate-artifacts.ts +13 -0
  459. package/src/self-implement/gate-cli.ts +54 -24
  460. package/src/self-implement/gate-remote.ts +23 -5
  461. package/src/self-implement/goal-author-clarification.ts +31 -12
  462. package/src/self-implement/goal-author.ts +403 -8
  463. package/src/self-implement/graph-overlay-yaml.ts +7 -0
  464. package/src/self-implement/graph-stage-units.ts +172 -0
  465. package/src/self-implement/graph-templates.ts +162 -1
  466. package/src/self-implement/graph-variant.ts +37 -4
  467. package/src/self-implement/headless-elanous-driver.ts +31 -2
  468. package/src/self-implement/heal-intake.ts +112 -4
  469. package/src/self-implement/heal-triage.ts +20 -0
  470. package/src/self-implement/host-regate.ts +31 -8
  471. package/src/self-implement/knowhow-pack.ts +38 -0
  472. package/src/self-implement/merge-train.ts +5 -0
  473. package/src/self-implement/orchestrator.ts +359 -30
  474. package/src/self-implement/regate-detail.ts +38 -1
  475. package/src/self-implement/rework-child-provider.ts +25 -0
  476. package/src/self-implement/run-chart.ts +119 -0
  477. package/src/self-implement/run-ledger.ts +176 -17
  478. package/src/self-implement/run-stop.ts +208 -5
  479. package/src/self-implement/running-runs.ts +35 -12
  480. package/src/self-implement/self-implement-cli.ts +36 -4
  481. package/src/self-implement/stop-autoheal-operational.ts +43 -0
  482. package/src/self-implement/stop-autoheal.ts +28 -6
  483. package/src/session/chat.ts +69 -58
  484. package/src/session/request-size-guard.ts +123 -0
  485. package/src/session-runtime/index.ts +98 -4
  486. package/src/session-runtime/tier-flip.ts +3 -3
  487. package/src/sidetab-core/model.ts +150 -0
  488. package/src/sidetab-core/visible-agent-mission.ts +186 -0
  489. package/src/skills/name-match.ts +20 -0
  490. package/src/skills/tools/agent.ts +30 -2
  491. package/src/skills/tools/omni-search.ts +67 -5
  492. package/src/task-agent/parent-host.ts +14 -1
  493. package/src/task-agent/pod-distributor-entry.ts +3 -0
  494. package/src/task-agent/task-hand.ts +31 -1
  495. package/src/task-orchestrator/surfaces/pod-distributor.ts +48 -0
  496. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +43 -0
  497. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +38 -1
  498. package/src/task-orchestrator/surfaces/pod-ledger-events.ts +29 -4
  499. package/src/task-orchestrator/surfaces/pod-si-warm-entry.ts +33 -6
  500. package/src/task-orchestrator/surfaces/pod-source-bundle.ts +45 -9
  501. package/src/task-orchestrator/surfaces/pod-source-receive.ts +163 -13
  502. package/src/task-orchestrator/surfaces/pod-source-spec.ts +3 -3
  503. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +209 -39
  504. package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +80 -3
  505. package/src/task-orchestrator/surfaces/self-implement-pod.ts +280 -60
  506. package/src/task-orchestrator/surfaces/self-implement.ts +18 -0
  507. package/src/tokens.ts +35 -2
  508. package/src/tool-runtime/agent-runtime.ts +1 -0
  509. package/src/tool-runtime/elanous-skills-list-runtime.ts +5 -1
  510. package/src/tool-runtime/skill-exec-runtime.ts +33 -13
  511. package/src/tool-runtime/types.ts +1 -0
  512. package/src/tui/entry.ts +24 -7
  513. package/src/tui/sidetab/attach.ts +47 -0
  514. package/src/tui/sidetab/list.ts +45 -0
  515. package/src/tui/sidetab/nl-tool.ts +2 -136
  516. package/src/tui/sidetab/peek.ts +52 -0
  517. package/src/tui-client/dashboard-session.ts +3 -0
  518. package/src/tui-client/local-chat-turn.ts +26 -13
  519. package/src/tui.ts +22 -0
  520. package/src/user-config.ts +11 -5
  521. package/src/ux-sim/fake-kubectl.ts +37 -3
  522. package/src/ux-sim/fault-needs-human-reason-repair.ts +107 -0
  523. package/src/ux-sim/fault-pod-child-result.ts +42 -0
  524. package/src/ux-sim/fault-pool-cap.ts +11 -6
  525. package/src/ux-sim/mock-scenario-catalog.json +41 -0
  526. package/src/ux-sim/mock-scenario-catalog.ts +156 -0
  527. package/src/ux-sim/release-sim-rig.ts +262 -0
  528. package/src/ux-sim/scenario.ts +116 -23
  529. package/src/ux-sim/seminar-rehearsal.ts +109 -0
  530. package/src/ux-sim/subagent-tools-device.ts +2 -2
  531. package/src/version/packed-revision.json +1 -1
  532. package/src/voice/voice-tts-singleton.ts +18 -3
  533. package/src/web-search/grok.ts +5 -9
  534. package/apps/pwa/out/_next/static/chunks/1060.014f1f01a17aec26.js +0 -115
  535. package/apps/pwa/out/_next/static/chunks/1415.79821ec7ae99acdb.js +0 -1
  536. package/apps/pwa/out/_next/static/chunks/1452.162bb2d9d699e82a.js +0 -1
  537. package/apps/pwa/out/_next/static/chunks/147.f92f63bbc5e85fa5.js +0 -286
  538. package/apps/pwa/out/_next/static/chunks/1661.fb425a1d8a065b72.js +0 -149
  539. package/apps/pwa/out/_next/static/chunks/1671-1c22b40b50b208f2.js +0 -1
  540. package/apps/pwa/out/_next/static/chunks/1758.0204d3f48783c49d.js +0 -90
  541. package/apps/pwa/out/_next/static/chunks/1806.027bf78b6a94d340.js +0 -104
  542. package/apps/pwa/out/_next/static/chunks/2047.8f23e210995d4548.js +0 -2
  543. package/apps/pwa/out/_next/static/chunks/2061-7f2bbc53ae96c88b.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/2066.a77257c8c350770a.js +0 -23
  545. package/apps/pwa/out/_next/static/chunks/2096-a938496bc996dcbd.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/2200-9a453b8f99687d49.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/2281-b8cede695c6c650e.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/2499.02fbfd18126c10c0.js +0 -1
  549. package/apps/pwa/out/_next/static/chunks/2522.f2766b76d22c8bdf.js +0 -34
  550. package/apps/pwa/out/_next/static/chunks/2615.3c9738153de3ef3f.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/2727.e703d99ac414916d.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/2752.7904b8df0254d0ae.js +0 -27
  553. package/apps/pwa/out/_next/static/chunks/2762-5051a862dfa40e99.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/2767-31784bfedf8e245d.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/2774-719820e3f5b2a3eb.js +0 -1
  556. package/apps/pwa/out/_next/static/chunks/2812-f016aa0dda149287.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/2829-734fe8bf2eee7347.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/311-32dcac79970985e8.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/3175-f93f7495cd28253c.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/3184.8da993f5502d2140.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/3231-d7c981db0ff8bdcc.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/3250.151309c5176d6b4b.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/3517.a5aa1797a9dbf2ee.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/3567-45f676877ed9e8a5.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/3819.8a1de79b2e8c7298.js +0 -24
  566. package/apps/pwa/out/_next/static/chunks/39.9b37b0be4c128a83.js +0 -77
  567. package/apps/pwa/out/_next/static/chunks/4023.a19045970a9ce16a.js +0 -36
  568. package/apps/pwa/out/_next/static/chunks/413-24c8284bda08241d.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/4135.e45d6ebc29e20647.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/4283.65502f5d0e6ff6c0.js +0 -287
  571. package/apps/pwa/out/_next/static/chunks/4305-4e28fa92a7b34a5b.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/4335-50efa61fd7613142.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/4720.ecbd1693fa151068.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/483.725e4a61be922788.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/4969-4b209d7307410c00.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/5068-c7f53862f83c0917.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/5315.bc96711c24dfefe2.js +0 -74
  578. package/apps/pwa/out/_next/static/chunks/5542.efbf14f16baed4dd.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/5622.94893f5aea7185d0.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/5644.8f401a25601d05fc.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/5658.35f70eb9871a54bf.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/5721.7bdc0e904c76ab51.js +0 -8
  583. package/apps/pwa/out/_next/static/chunks/5790-eef0ca20884a6bdf.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/5802.ef0d8598ca68fe84.js +0 -62
  585. package/apps/pwa/out/_next/static/chunks/5890.fb974729788dcb71.js +0 -1
  586. package/apps/pwa/out/_next/static/chunks/5924.5ef25cddd36d5dbf.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/5979-e59c099ebf8d1f35.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/6020.714cfeada2315e2e.js +0 -94
  589. package/apps/pwa/out/_next/static/chunks/6027-dfe0349d8c50f698.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/6178.bc4730c86cf74536.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/6273.d808b0dcb3c2b155.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/6292.6ef1df64fa6a4c8a.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/647-af8dd920ba46533c.js +0 -1
  594. package/apps/pwa/out/_next/static/chunks/6541-170dc5c59e135c0c.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/6624.981ef7caeb680560.js +0 -32
  596. package/apps/pwa/out/_next/static/chunks/6742-a1ad4a94ba7bc37a.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/6836-493dc55c39187949.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/6856-53ac421c06039a18.js +0 -1
  599. package/apps/pwa/out/_next/static/chunks/6908.06008c67f6380ea9.js +0 -148
  600. package/apps/pwa/out/_next/static/chunks/6915.096931864206f522.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/6925.5e181fb7dcf90193.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/6cc1d1d6.9a98dd4622f1afc6.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/7203-800e11037adc41eb.js +0 -1
  604. package/apps/pwa/out/_next/static/chunks/7279.6170f591105936bb.js +0 -93
  605. package/apps/pwa/out/_next/static/chunks/7349-373165c7fbdcc3df.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/7514-a533ba0ca1cae4f4.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/7515.1ad6efe157fa4448.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/7521.d919cc1fab80c1d9.js +0 -24
  609. package/apps/pwa/out/_next/static/chunks/7538.90e97bbdf9178cff.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/7825-39203196a12579b2.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/7869.5e1a9a12fd70d6c7.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/8086.1ab9103c41be884f.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/8179-e61fffbdb70f1152.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/8265.bdeb991b34db6a06.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/8357.1aaa713b052a78b1.js +0 -309
  616. package/apps/pwa/out/_next/static/chunks/8638-e42615f2166165b5.js +0 -1
  617. package/apps/pwa/out/_next/static/chunks/8639-92c11d069792429e.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/8691.54e22831d5e8ccf8.js +0 -1
  619. package/apps/pwa/out/_next/static/chunks/9069.657590a84afb8fac.js +0 -41
  620. package/apps/pwa/out/_next/static/chunks/9125-ae588d88a0293b93.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/9311.f9f641a46a508611.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/9371-a6fee8378ebfa3ee.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/9421-3aa9ff4fa7f63d8a.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/9992.07ce73c10bd5e6a1.js +0 -79
  625. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-76394dc618d322c8.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/app/approvals/page-1b948637c37b3abd.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-c63400f1bcf3e47c.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/app/board/page-95d0957267819586.js +0 -1
  629. package/apps/pwa/out/_next/static/chunks/app/botlab/page-ba957ff875a026a0.js +0 -1
  630. package/apps/pwa/out/_next/static/chunks/app/bots/page-bb6fd0714879cb87.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/app/ceo/page-8f9038520d2acbef.js +0 -1
  632. package/apps/pwa/out/_next/static/chunks/app/chat/page-d4b48397e05fce2c.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/app/consult/page-72d5744245dc0c27.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/app/control/page-37625dc8300fa43a.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/app/decisions/page-fbef9d160272dbc1.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/app/design-check/page-58424168df84928e.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/app/editor/page-72c12ba430ac57c0.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/app/exec/page-d53f3e9ed806d7e0.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/app/field/page-4d2fbd45feee0829.js +0 -1
  640. package/apps/pwa/out/_next/static/chunks/app/healthz/route-76394dc618d322c8.js +0 -1
  641. package/apps/pwa/out/_next/static/chunks/app/inside/page-8783dc0ed7590a2c.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/app/intake/page-3706cb390606e930.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/app/layout-9b0a86598e0111fd.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/app/live/page-4edb8df90f4537ce.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/app/loops/page-47398b9ba1cc5e1b.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/app/market/page-fb86f55cfe2e681d.js +0 -1
  647. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e22345fc8fd47b1b.js +0 -1
  648. package/apps/pwa/out/_next/static/chunks/app/morning/page-fca2e59a81744d9e.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/app/not-found-6e96bda63100b31e.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/app/observatory/page-4d17ad8e0db8cea4.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-121f92766c33beaa.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-43f14212c545f92d.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ccec2023e7a974ec.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/app/outputs/page-b04f33f548b89a94.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/app/page-c8792823c0917021.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/app/reflection/page-8c0e32782ceef4cb.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-94bd3922eb230ce8.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/app/sessions/page-bca565887bafa03c.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-ac1dffafe649fc6c.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/app/settings/page-52374edb5e23e455.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ad4ebca120d43ca7.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/app/setup/layout-76394dc618d322c8.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/app/setup/page-718b89ce4b4ae4e4.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/app/share/page-204eca33eeeaddec.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/app/showroom/page-e58ba9bcdb9ac0f9.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/app/tasks/page-98f6dd50bafd7734.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/app/today/page-5a4bdd72bff27057.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/app/trace/page-6f6ab17d44f984ce.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/app/vault/page-aabb7bd15fb72da9.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-e4322319164a4436.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/app/workflows/page-e9efc0c8f32e2371.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/app/workspace/page-ccde99f07aaf71f7.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-80464aced9614390.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/c696986b.5dc8bb1143af16d1.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/d5d3cefc-373c4eb722ed848b.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/d8de0de2-dd0f4ac8e5ab1832.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/framework-002b472a66935d31.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/main-49b88d47292c0476.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/main-app-fff2549cb10a133f.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/pages/_app-32abbbba3f3b76b7.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/pages/_error-9d9cfef60c60dd7a.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/webpack-cc7f483fa294034a.js +0 -1
  683. package/apps/pwa/out/_next/static/css/d0a48dc099ed86cf.css +0 -1
  684. package/scripts/ratchets/next-tui-entry-budget.ts +0 -124
  685. /package/apps/pwa/out/_next/static/{HjeK7zONHfR7op1hKXp4k → X1ucLw10Cds_h614NoEnh}/_ssgManifest.js +0 -0
@@ -0,0 +1,99 @@
1
+ interface StickyGatePool { id?: string | null; available: boolean; imageDigest: string | null }
2
+ interface StickyGateShard { id?: string | null; files?: string[] | null }
3
+ interface StickyGateReceipt { shardId: string; status: 'complete' | 'stalled'; pool?: string | null; imageDigest: string }
4
+ interface StickyGateShardAction {
5
+ shardId: string; files: string[]; kind: 'reuse' | 'run' | 'unmeasured'; pool?: string; imageDigest?: string;
6
+ }
7
+ interface StickyGatePlan {
8
+ mode: 'full' | 'partial' | 'HOLD'; pinnedDigest: string | null; excludedPools: string[];
9
+ shards: StickyGateShardAction[]; reason?: string;
10
+ }
11
+ interface StickyGatePlanInput {
12
+ phase: 'start' | 'resume'; pools: readonly StickyGatePool[]; shards: readonly StickyGateShard[];
13
+ pinnedDigest?: string | null; receipts?: readonly StickyGateReceipt[];
14
+ }
15
+
16
+ const DIGEST = /^sha256:[a-f0-9]{64}$/i;
17
+ const validDigest = (value: string | null | undefined): value is string => typeof value === 'string' && DIGEST.test(value);
18
+ const normalizeDigest = (value: string | null | undefined): string | null => validDigest(value) ? value.toLowerCase() : null;
19
+ const validPoolIdentity = (value: string | null | undefined): value is string => typeof value === 'string' && value.trim().length > 0;
20
+
21
+ function hold(input: StickyGatePlanInput, pinnedDigest: string | null, reason: string, excludedPools: string[] = []): StickyGatePlan {
22
+ return { mode: 'HOLD', pinnedDigest, excludedPools, reason,
23
+ shards: input.shards.map((shard, index) => ({
24
+ shardId: validPoolIdentity(shard?.id) ? shard.id : `unmeasured-${index}`,
25
+ files: Array.isArray(shard?.files) ? [...shard.files] : [], kind: 'unmeasured',
26
+ })) };
27
+ }
28
+
29
+ function isValidPool(pool: StickyGatePool | null | undefined): pool is StickyGatePool & { id: string } {
30
+ return !!pool && validPoolIdentity(pool.id);
31
+ }
32
+
33
+ function isValidShard(shard: StickyGateShard | null | undefined): shard is StickyGateShard & { id: string; files: string[] } {
34
+ return !!shard && validPoolIdentity(shard.id) && Array.isArray(shard.files) && shard.files.length > 0
35
+ && shard.files.every((file) => typeof file === 'string') && new Set(shard.files).size === shard.files.length;
36
+ }
37
+
38
+ /**
39
+ * Deterministic E4 planning simulation. A production caller must persist the chosen digest at run start
40
+ * and pass it unchanged on resume; this module itself never runs a host command or pulls an image.
41
+ */
42
+ export function planStickyGateImages(input: StickyGatePlanInput): StickyGatePlan {
43
+ if (!input.shards.length || !input.pools.length) return hold(input, null, 'empty pool or shard inventory');
44
+ const pools = input.pools.filter(isValidPool);
45
+ const validShards = input.shards.filter(isValidShard);
46
+ if (pools.length !== input.pools.length || new Set(pools.map((pool) => pool.id)).size !== pools.length
47
+ || validShards.length !== input.shards.length || new Set(validShards.map((shard) => shard.id)).size !== validShards.length)
48
+ return hold(input, null, 'invalid pool or shard identity');
49
+
50
+ let pinnedDigest: string | null = null;
51
+ if (input.phase === 'resume') {
52
+ pinnedDigest = normalizeDigest(input.pinnedDigest);
53
+ if (!pinnedDigest) return hold(input, null, 'run image digest missing');
54
+ } else if (input.pinnedDigest !== undefined && input.pinnedDigest !== null) {
55
+ pinnedDigest = normalizeDigest(input.pinnedDigest);
56
+ if (!pinnedDigest) return hold(input, null, 'invalid pinned image digest');
57
+ } else {
58
+ pinnedDigest = pools.map((pool) => pool.available ? normalizeDigest(pool.imageDigest) : null)
59
+ .find((digest): digest is string => digest !== null) ?? null;
60
+ if (!pinnedDigest) return hold(input, null, 'no available pool has a valid image digest');
61
+ }
62
+
63
+ const excludedPools = pools.filter((pool) => !pool.available || normalizeDigest(pool.imageDigest) !== pinnedDigest)
64
+ .map((pool) => pool.id);
65
+ const eligiblePools = pools.filter((pool) => pool.available && normalizeDigest(pool.imageDigest) === pinnedDigest);
66
+ if (!eligiblePools.length) return hold(input, pinnedDigest, 'no available pool has the pinned digest', excludedPools);
67
+
68
+ const receipts = input.receipts ?? [];
69
+ const shardIds = new Set(validShards.map((shard) => shard.id));
70
+ if (receipts.some((receipt) => !shardIds.has(receipt.shardId))
71
+ || new Set(receipts.map((receipt) => receipt.shardId)).size !== receipts.length)
72
+ return hold(input, pinnedDigest, 'receipt inventory does not match the shard plan', excludedPools);
73
+
74
+ const receiptByShard = new Map(receipts.map((receipt) => [receipt.shardId, receipt]));
75
+ const completedReceipts = input.phase === 'resume' ? receipts.filter((receipt) => receipt.status === 'complete') : [];
76
+ const mismatchedCompleteDigest = completedReceipts.some((receipt) => normalizeDigest(receipt.imageDigest) !== pinnedDigest);
77
+ const missingCompletePool = completedReceipts.some((receipt) => !validPoolIdentity(receipt.pool));
78
+ const mixedCompleteReceipt = mismatchedCompleteDigest || missingCompletePool;
79
+ const fullSweep = input.phase === 'start' || mixedCompleteReceipt
80
+ || !validShards.some((shard) => receiptByShard.get(shard.id)?.status === 'complete');
81
+ const mode: StickyGatePlan['mode'] = fullSweep ? 'full' : 'partial';
82
+ const shards: StickyGateShardAction[] = [];
83
+ let nextPool = 0;
84
+ for (const shard of validShards) {
85
+ const receipt = receiptByShard.get(shard.id);
86
+ const canReuse = input.phase === 'resume' && !fullSweep && receipt?.status === 'complete'
87
+ && normalizeDigest(receipt.imageDigest) === pinnedDigest && validPoolIdentity(receipt.pool);
88
+ if (canReuse) {
89
+ shards.push({ shardId: shard.id, files: [...shard.files], kind: 'reuse', pool: receipt.pool ?? undefined, imageDigest: pinnedDigest });
90
+ continue;
91
+ }
92
+ const pool = eligiblePools[nextPool++ % eligiblePools.length]!;
93
+ shards.push({ shardId: shard.id, files: [...shard.files], kind: 'run', pool: pool.id, imageDigest: pinnedDigest });
94
+ }
95
+
96
+ return { mode, pinnedDigest, excludedPools, shards,
97
+ ...(mixedCompleteReceipt ? { reason: mismatchedCompleteDigest
98
+ ? 'completed receipts contain a different image digest' : 'completed receipt is missing pool identity' } : {}) };
99
+ }
@@ -5,9 +5,10 @@ import { join } from 'node:path';
5
5
  import { changedFilesBetween, planPartialRegate, testFileOfId, type PartialPlan } from './gate-partial.js';
6
6
  import { CODE_FILE, gitClosureTree, testsReachingChanged } from './gate-closure.js';
7
7
  import { versionOnlyBumps } from './gate-radius-shadow.js';
8
- import { gateAssignableTestsAtCommit, gateBaselineReceipt, type GateBaselineReceipt } from './gate-baseline-receipt.js';
9
- import { parsePodPool, remoteImageId } from '../../src/task-orchestrator/surfaces/pod-pool.js';
8
+ import { gateAssignableTestsAtCommit, gateBaselineReceipt, gateEnvFingerprint, type GateBaselineReceipt, type GatePodRuntimeIdentity } from './gate-baseline-receipt.js';
9
+ import { defaultRemoteRun, NODE_REGISTRY, parsePodPool } from '../../src/task-orchestrator/surfaces/pod-pool.js';
10
10
  import { podCommandImageFor } from '../../src/task-orchestrator/surfaces/pod-command-job.js';
11
+ import { gateTestBaseline } from '../lib/gate-test-env.js';
11
12
  import { SPAWN_SYNC_MAX_BUFFER } from '../../src/util/spawn-sync-output.js';
12
13
 
13
14
  const SHA = /^[0-9a-f]{40}$/i;
@@ -27,18 +28,91 @@ export function hardRehearsalGateArgs(version: string, runId: string, commit: st
27
28
 
28
29
  export interface LiveRadiusDecision { plan: PartialPlan | null; reason: string; rehearsalRunId?: string }
29
30
 
30
- /** Read the image ID on every candidate host before deciding. A later Job must still prove its actual image. */
31
- export function preflightGateImageDigest(pool: string): string | null {
31
+ export interface GatePodExecutionIdentity extends GatePodRuntimeIdentity { imageDigest: string }
32
+
33
+ const shellQuote = (value: string) => `'${value.replace(/'/g, `'\\''`)}'`;
34
+
35
+ /** Registry v2 manifest digest is the identity Kubernetes reports in containerStatuses.imageID. */
36
+ export function parseGateRepoDigestResponse(headers: string): string | null {
37
+ const match = /^docker-content-digest:\s*(sha256:[0-9a-f]{64})\s*$/im.exec(headers);
38
+ return match?.[1]?.toLowerCase() ?? null;
39
+ }
40
+
41
+ export interface GateRepoDigestProbeRequest { url: string; acceptHeader: string; command: string }
42
+
43
+ export function gateRepoDigestProbeRequest(registry: string, image: string, commit: string): GateRepoDigestProbeRequest | null {
44
+ const port = /:(\d+)$/.exec(registry)?.[1];
45
+ if (!port || !SHA.test(commit)) return null;
46
+ const imageName = image.startsWith('elanous-harness-lite:') ? 'elanous-harness-lite' : 'elanous-harness';
47
+ const tag = commit.slice(0, 12);
48
+ const url = `http://localhost:${port}/v2/${imageName}/manifests/${tag}`;
49
+ const accept = 'application/vnd.oci.image.index.v1+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.docker.distribution.manifest.v2+json';
50
+ const acceptHeader = `Accept: ${accept}`;
51
+ return { url, acceptHeader, command: `curl -fsS --max-time 8 -D - -o /dev/null -H ${shellQuote(acceptHeader)} ${shellQuote(url)}` };
52
+ }
53
+
54
+ function probeGateRepoDigest(member: ReturnType<typeof parsePodPool>[number], image: string, commit: string): string | null {
55
+ const registry = member.registry ?? NODE_REGISTRY;
56
+ const request = gateRepoDigestProbeRequest(registry, image, commit);
57
+ if (!request) return null;
58
+ const result = member.sshHost
59
+ ? defaultRemoteRun(member.sshHost, request.command)
60
+ : spawnSync('curl', ['-fsS', '--max-time', '8', '-D', '-', '-o', '/dev/null', '-H', request.acceptHeader, request.url],
61
+ { encoding: 'utf8', timeout: 10_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
62
+ return result.status === 0 ? parseGateRepoDigestResponse(result.stdout) : null;
63
+ }
64
+
65
+ function gateRegistryHostImage(member: ReturnType<typeof parsePodPool>[number], image: string, commit: string): string | null {
66
+ const registry = member.registry ?? NODE_REGISTRY;
67
+ const port = /:(\d+)$/.exec(registry)?.[1];
68
+ if (!port) return null;
69
+ const imageName = image.startsWith('elanous-harness-lite:') ? 'elanous-harness-lite' : 'elanous-harness';
70
+ return `localhost:${port}/${imageName}:${commit.slice(0, 12)}`;
71
+ }
72
+
73
+ /** Read the immutable pushed image manifest on every candidate host. Local Docker Image IDs are diagnostic only. */
74
+ export function preflightGateImageDigest(pool: string, commit?: string): string | null {
75
+ try {
76
+ if (!commit || !SHA.test(commit)) return null;
77
+ const image = podCommandImageFor([], true);
78
+ const members = parsePodPool(pool);
79
+ if (!members.length) return null;
80
+ const digests = members.map((member) => probeGateRepoDigest(member, image, commit));
81
+ return digests.every((digest) => digest && DIGEST.test(digest) && digest === digests[0]) ? digests[0]! : null;
82
+ } catch { return null; }
83
+ }
84
+
85
+ /** Probe each candidate's exact gate image/runtime and deterministic environment without a Kubernetes Job. */
86
+ export function preflightGateExecutionIdentity(pool: string, env: NodeJS.ProcessEnv, commit?: string): GatePodExecutionIdentity | null {
32
87
  try {
88
+ if (!commit || !SHA.test(commit)) return null;
33
89
  const image = podCommandImageFor([], true);
34
90
  const members = parsePodPool(pool);
35
91
  if (!members.length) return null;
36
- const ids = members.map((member) => {
37
- if (member.sshHost) return remoteImageId(member, image);
38
- const result = spawnSync('docker', ['image', 'inspect', image, '--format', '{{.Id}}'], { encoding: 'utf8', timeout: 30_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
39
- return result.status === 0 ? result.stdout.trim() : null;
92
+ const baseline = gateTestBaseline(env);
93
+ const expectedEnvFingerprint = gateEnvFingerprint(env);
94
+ const script = "const {createHash}=require('node:crypto');const keys=['ELANOUS_TEST_FAKE_LLM','LANG','LC_ALL','TZ'];const e=Object.fromEntries(keys.sort().map(k=>[k,process.env[k]??'']));const envFingerprint=createHash('sha256').update(JSON.stringify(e)).digest('hex');console.log(JSON.stringify({bunVersion:Bun.version,platform:process.platform,architecture:process.arch,envFingerprint}));";
95
+ const rows = members.map((member): GatePodExecutionIdentity | null => {
96
+ const imageDigest = probeGateRepoDigest(member, image, commit);
97
+ if (!imageDigest || !DIGEST.test(imageDigest)) return null;
98
+ const hostImage = gateRegistryHostImage(member, image, commit);
99
+ if (!hostImage) return null;
100
+ const args = ['run', '--rm', '--pull=never', '--network=none', ...Object.entries(baseline).flatMap(([key, value]) => ['-e', `${key}=${value}`]), '--entrypoint', 'bun', hostImage, '-e', script];
101
+ const result = member.sshHost
102
+ ? defaultRemoteRun(member.sshHost, `docker ${args.map(shellQuote).join(' ')}`)
103
+ : spawnSync('docker', args, { encoding: 'utf8', timeout: 30_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
104
+ if (result.status !== 0) return null;
105
+ try {
106
+ const row = JSON.parse(result.stdout.trim()) as Partial<GatePodRuntimeIdentity>;
107
+ if (typeof row.bunVersion !== 'string' || typeof row.platform !== 'string' || typeof row.architecture !== 'string'
108
+ || row.envFingerprint !== expectedEnvFingerprint) return null;
109
+ return { imageDigest, bunVersion: row.bunVersion, platform: row.platform, architecture: row.architecture,
110
+ envFingerprint: row.envFingerprint };
111
+ } catch { return null; }
40
112
  });
41
- return ids.every((id) => id && DIGEST.test(id) && id === ids[0]) ? ids[0]! : null;
113
+ if (rows.some((row) => row === null)) return null;
114
+ const first = rows[0]!;
115
+ return rows.every((row) => JSON.stringify(row) === JSON.stringify(first)) ? first : null;
42
116
  } catch { return null; }
43
117
  }
44
118
 
@@ -82,10 +156,15 @@ export function partialGateImagesMatch(root: string, version: string, digest: st
82
156
  /** Every missing or mismatched proof returns a full gate. The caller uses this only when no explicit resume plan exists. */
83
157
  export function planHardRehearsalRadius(input: { root: string; repo: string; version: string; forCommit: string;
84
158
  graphPins: { graphSha: string; recipeSha: string } | null; envFingerprint: string;
85
- imageDigest: string | null }): LiveRadiusDecision {
159
+ imageDigest: string | null; executionIdentity: GatePodExecutionIdentity | null }): LiveRadiusDecision {
86
160
  const full = (reason: string): LiveRadiusDecision => ({ plan: null, reason });
87
161
  if (!/^\d+\.\d+\.\d+$/.test(input.version) || !SHA.test(input.forCommit)) return full('invalid release identity');
88
- if (!input.graphPins || !/^[0-9a-f]{64}$/i.test(input.envFingerprint) || !input.imageDigest || !DIGEST.test(input.imageDigest))
162
+ if (!input.graphPins || !/^[0-9a-f]{64}$/i.test(input.envFingerprint) || !input.imageDigest || !DIGEST.test(input.imageDigest)
163
+ || !input.executionIdentity || input.executionIdentity.imageDigest !== input.imageDigest.toLowerCase()
164
+ || input.executionIdentity.envFingerprint !== input.envFingerprint
165
+ || !/^\d+\.\d+\.\d+(?:[-+][\w.-]+)?$/.test(input.executionIdentity.bunVersion)
166
+ || !/^[a-z0-9_-]{1,32}$/.test(input.executionIdentity.platform)
167
+ || !/^[a-z0-9_-]{1,32}$/.test(input.executionIdentity.architecture))
89
168
  return full('current graph, environment or Pod image identity unmeasured');
90
169
  try {
91
170
  const stamps = ['hard-5h', 'hard-mid'].map((step) => {
@@ -118,14 +197,19 @@ export function planHardRehearsalRadius(input: { root: string; repo: string; ver
118
197
  if (verdict.commit !== stamp.sha || verdict.outcome !== 'ok' || failures.commit !== stamp.sha
119
198
  || !Array.isArray(failures.failures) || failures.failures.length !== 0
120
199
  || !Array.isArray(failures.errors) || failures.errors.length !== 0
121
- || !receipt || receipt.schema !== 1 || !receipt.valid || receipt.version !== gateVersion || receipt.commit !== stamp.sha
200
+ || !receipt || receipt.schema !== 2 || !receipt.valid || receipt.version !== gateVersion || receipt.commit !== stamp.sha
122
201
  || receipt.graphSha !== input.graphPins.graphSha || receipt.recipeSha !== input.graphPins.recipeSha
123
202
  || receipt.envFingerprint !== input.envFingerprint || receipt.imageDigest !== input.imageDigest.toLowerCase()
203
+ || !receipt.executionIdentity
204
+ || JSON.stringify(receipt.executionIdentity) !== JSON.stringify({ bunVersion: input.executionIdentity.bunVersion,
205
+ platform: input.executionIdentity.platform, architecture: input.executionIdentity.architecture,
206
+ envFingerprint: input.executionIdentity.envFingerprint })
124
207
  || !Number.isSafeInteger(receipt.files) || receipt.files < 1 || !Number.isSafeInteger(receipt.shards) || receipt.shards < 1)
125
208
  return full('hard rehearsal receipt or current execution identity differs');
126
209
  const recomputed = gateBaselineReceipt({ root: input.root, version: gateVersion, commit: stamp.sha,
127
210
  fullSweep: true, graphPins: input.graphPins, envFingerprint: input.envFingerprint });
128
- if (!recomputed.valid || recomputed.imageDigest !== receipt.imageDigest || recomputed.files !== receipt.files
211
+ if (!recomputed.valid || recomputed.imageDigest !== receipt.imageDigest
212
+ || JSON.stringify(recomputed.executionIdentity) !== JSON.stringify(receipt.executionIdentity) || recomputed.files !== receipt.files
129
213
  || recomputed.shards !== receipt.shards) return full(`hard rehearsal Pod proof differs: ${recomputed.reason}`);
130
214
  const ancestor = spawnSync('git', ['merge-base', '--is-ancestor', stamp.sha, input.forCommit], { cwd: input.repo, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
131
215
  if (ancestor.status !== 0 || ancestor.error) return full('hard rehearsal commit is not an ancestor');
@@ -29,6 +29,49 @@ export interface GateShardHost {
29
29
  reserved: boolean;
30
30
  }
31
31
 
32
+ export type GateProgressState = 'pending' | 'running' | 'done' | 'failed' | 'timeout' | 'unmeasured';
33
+
34
+ /** A host group or planned Pod shard as seen by the release gate's live progress observer. */
35
+ export interface GateProgressGroup {
36
+ id: string;
37
+ state: GateProgressState;
38
+ host?: string;
39
+ startedAtMs?: number;
40
+ endedAtMs?: number;
41
+ }
42
+
43
+ export interface GateProgressSummary {
44
+ executor: 'host' | 'pod';
45
+ host: string | null;
46
+ completed: number;
47
+ total: number;
48
+ failed: number;
49
+ groups: Array<{ id: string; state: GateProgressState; host: string | null; elapsedMs: number | null }>;
50
+ }
51
+
52
+ /** Keep the progress event shape identical for serial host groups and parallel Pod shards. */
53
+ export function summarizeGateProgress(input: {
54
+ executor: 'host' | 'pod';
55
+ groups: readonly GateProgressGroup[];
56
+ nowMs: number;
57
+ }): GateProgressSummary {
58
+ const terminal = new Set<GateProgressState>(['done', 'failed', 'timeout', 'unmeasured']);
59
+ const hosts = [...new Set(input.groups.flatMap((group) => group.host?.trim() ? [group.host.trim()] : []))].sort();
60
+ return {
61
+ executor: input.executor,
62
+ host: hosts.length ? hosts.join(',') : null,
63
+ completed: input.groups.filter((group) => terminal.has(group.state)).length,
64
+ total: input.groups.length,
65
+ failed: input.groups.filter((group) => group.state === 'failed' || group.state === 'timeout').length,
66
+ groups: input.groups.map((group) => {
67
+ const end = group.endedAtMs ?? input.nowMs;
68
+ const elapsedMs = Number.isFinite(group.startedAtMs) && Number.isFinite(end)
69
+ ? Math.max(0, end - group.startedAtMs!) : null;
70
+ return { id: group.id, state: group.state, host: group.host?.trim() || null, elapsedMs };
71
+ }),
72
+ };
73
+ }
74
+
32
75
  export interface GateShardDispatchInput {
33
76
  head40: string;
34
77
  base40: string;
@@ -0,0 +1,256 @@
1
+ #!/usr/bin/env bun
2
+ import { readFileSync, writeFileSync } from 'node:fs';
3
+
4
+ function decodeXml(value: string): string {
5
+ return value.replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&lt;/g, '<')
6
+ .replace(/&gt;/g, '>').replace(/&amp;/g, '&');
7
+ }
8
+
9
+ /** Remove legal comments and reduce CDATA before tag parsing, without accepting markup inside tags. */
10
+ function stripCommentsAndCdata(xml: string): string | null {
11
+ let source = '';
12
+ let inMarkup = false;
13
+ let quote: '"' | "'" | undefined;
14
+ const offset = xml.startsWith('\uFEFF') ? 1 : 0;
15
+ for (let index = offset; index < xml.length;) {
16
+ if (xml.startsWith('<!--', index)) {
17
+ if (inMarkup) return null;
18
+ const end = xml.indexOf('-->', index + 4);
19
+ if (end < 0 || xml.slice(index + 4, end).includes('--')) return null;
20
+ index = end + 3;
21
+ continue;
22
+ }
23
+ if (xml.startsWith('<?', index)) {
24
+ const end = xml.indexOf('?>', index + 2);
25
+ if (inMarkup || index !== offset || end < 0) return null;
26
+ const declaration = xml.slice(index, end + 2);
27
+ if (!/^<\?xml\s+version=["']1\.0["'](?:\s+encoding=["'][A-Za-z][A-Za-z0-9._-]*["'])?(?:\s+standalone=["'](?:yes|no)["'])?\s*\?>$/.test(declaration)) return null;
28
+ index = end + 2;
29
+ continue;
30
+ }
31
+ if (xml.startsWith('<![CDATA[', index)) {
32
+ if (inMarkup) return null;
33
+ const end = xml.indexOf(']]>', index + 9);
34
+ if (end < 0) return null;
35
+ source += xml.slice(index + 9, end).trim() ? '__CDATA_TEXT__' : ' ';
36
+ index = end + 3;
37
+ continue;
38
+ }
39
+ const character = xml[index]!;
40
+ if (inMarkup) {
41
+ if (quote) {
42
+ if (character === quote) quote = undefined;
43
+ else if (character === '<') return null;
44
+ } else if (character === '"' || character === "'") quote = character;
45
+ else if (character === '>') inMarkup = false;
46
+ else if (character === '<') return null;
47
+ } else if (character === '<') inMarkup = true;
48
+ source += character;
49
+ index++;
50
+ }
51
+ return source;
52
+ }
53
+
54
+ type JunitCase = { file?: string; id?: string; failures: number; errors: number; skipped: number };
55
+ type JunitDocument = { cases: JunitCase[]; suiteFailed: boolean; unattributedSuiteFailure: boolean };
56
+ function parseJunit(xml: string): JunitDocument | null {
57
+ if (/<!DOCTYPE/i.test(xml)) return null;
58
+ for (const character of xml) {
59
+ const codepoint = character.codePointAt(0)!;
60
+ const xml10Character = codepoint === 0x9 || codepoint === 0xa || codepoint === 0xd
61
+ || (codepoint >= 0x20 && codepoint <= 0xd7ff) || (codepoint >= 0xe000 && codepoint <= 0xfffd)
62
+ || (codepoint >= 0x10000 && codepoint <= 0x10ffff);
63
+ if (!xml10Character) return null;
64
+ }
65
+ const source = stripCommentsAndCdata(xml);
66
+ if (source === null) return null;
67
+ if (/<\?|\?>/.test(source)) return null;
68
+ if (/<!--|-->|<!\[CDATA\[|\]\]>/.test(source)) return null;
69
+ const stack: Array<{
70
+ name: string;
71
+ file?: string;
72
+ suiteTitle?: string;
73
+ declaredTests?: number;
74
+ declaredFailures?: number;
75
+ declaredErrors?: number;
76
+ testCount: number;
77
+ caseStart: number;
78
+ }> = [];
79
+ const cases: JunitCase[] = [];
80
+ let roots = 0;
81
+ let sawSuite = false;
82
+ let suiteFailed = false;
83
+ let unattributedSuiteFailure = false;
84
+ let rootName: string | undefined;
85
+ let cursor = 0;
86
+ if (/&(?!(?:amp|lt|gt|quot|apos|#\d+|#x[\da-f]+);)/i.test(source)) return null;
87
+ for (const entity of source.matchAll(/&#(x[\da-f]+|\d+);/gi)) {
88
+ const codepoint = entity[1]!.startsWith('x') ? Number.parseInt(entity[1]!.slice(1), 16) : Number.parseInt(entity[1]!, 10);
89
+ const xml10Character = codepoint === 0x9 || codepoint === 0xa || codepoint === 0xd
90
+ || (codepoint >= 0x20 && codepoint <= 0xd7ff) || (codepoint >= 0xe000 && codepoint <= 0xfffd)
91
+ || (codepoint >= 0x10000 && codepoint <= 0x10ffff);
92
+ if (!Number.isInteger(codepoint) || !xml10Character) return null;
93
+ }
94
+ for (const match of source.matchAll(/<([^<>]+)>/g)) {
95
+ const between = source.slice(cursor, match.index);
96
+ if (between.includes('<') || between.includes(']]>') || (!stack.length && between.trim())) return null;
97
+ cursor = match.index! + match[0].length;
98
+ const body = match[1]!.trim();
99
+ // Comments and CDATA have already been stripped above, and the one XML declaration was
100
+ // removed before tokenization. Every other declaration is unsupported and invalid here.
101
+ if (!body || body.startsWith('!')) return null;
102
+ const close = body.startsWith('/');
103
+ const selfClose = body.endsWith('/');
104
+ const tagMatch = /^\/?([A-Za-z_:][\w:.-]*)/.exec(body);
105
+ if (!tagMatch) return null;
106
+ const name = tagMatch[1]!;
107
+ const attrsText = body.slice(tagMatch[0].length, body.length - (selfClose ? 1 : 0));
108
+ const attrs: Record<string, string> = {};
109
+ const attrPattern = /([A-Za-z_:][\w:.-]*)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
110
+ for (const attr of attrsText.matchAll(attrPattern)) {
111
+ if (attrs[attr[1]!] !== undefined) return null;
112
+ attrs[attr[1]!] = decodeXml(attr[2] ?? attr[3] ?? '');
113
+ }
114
+ if (!close && attrsText.replace(attrPattern, '').trim()) return null;
115
+ if (close && attrsText.trim()) return null;
116
+ if (close) {
117
+ if (selfClose) return null;
118
+ const closed = stack.pop();
119
+ if (!closed || closed.name !== name || (closed.declaredTests !== undefined && closed.declaredTests !== closed.testCount)) return null;
120
+ if (closed.declaredFailures !== undefined
121
+ && closed.declaredFailures !== cases.slice(closed.caseStart).reduce((count, testcase) => count + testcase.failures, 0)) return null;
122
+ if (closed.declaredErrors !== undefined
123
+ && closed.declaredErrors !== cases.slice(closed.caseStart).reduce((count, testcase) => count + testcase.errors, 0)) return null;
124
+ continue;
125
+ }
126
+ if (name === 'testcase' && stack.at(-1)?.name !== 'testsuite') return null;
127
+ if ((name === 'failure' || name === 'error') && stack.at(-1)?.name !== 'testcase') return null;
128
+ if (!stack.length) { roots++; rootName = name; }
129
+ let file = attrs.file;
130
+ let declaredTests: number | undefined;
131
+ let declaredFailures: number | undefined;
132
+ let declaredErrors: number | undefined;
133
+ if ((name === 'testsuite' || name === 'testsuites') && attrs.tests !== undefined) {
134
+ declaredTests = Number(attrs.tests);
135
+ if (!Number.isSafeInteger(declaredTests) || declaredTests < 0) return null;
136
+ }
137
+ if (name === 'testsuite' || name === 'testsuites') {
138
+ for (const [attribute, set] of [['failures', (value: number) => { declaredFailures = value; }], ['errors', (value: number) => { declaredErrors = value; }]] as const) {
139
+ const count = attrs[attribute];
140
+ if (count === undefined) continue;
141
+ if (!/^\d+$/.test(count) || !Number.isSafeInteger(Number(count))) return null;
142
+ set(Number(count));
143
+ }
144
+ }
145
+ if (name === 'testsuite') {
146
+ file ??= attrs.name;
147
+ sawSuite = true;
148
+ if ((declaredFailures ?? 0) > 0 || (declaredErrors ?? 0) > 0) suiteFailed = true;
149
+ const inherited = [...stack].reverse().find((entry) => entry.name === 'testsuite')?.file;
150
+ if (!file && inherited) file = inherited;
151
+ } else {
152
+ const inherited = [...stack].reverse().find((entry) => entry.name === 'testsuite')?.file;
153
+ if (!file && inherited) file = inherited;
154
+ if (name === 'testsuites' && ((declaredFailures ?? 0) > 0 || (declaredErrors ?? 0) > 0)) suiteFailed = true;
155
+ }
156
+ if (name === 'testcase') {
157
+ for (const ancestor of stack) if (ancestor.name === 'testsuite' || ancestor.name === 'testsuites') ancestor.testCount++;
158
+ const suiteEntries = stack.filter((entry) => entry.name === 'testsuite');
159
+ // Some JUnit writers repeat the file path as the outer suite name. Keep the
160
+ // remaining hierarchy so sibling suites with the same testcase name stay distinct.
161
+ const identitySuites = suiteEntries[0]?.suiteTitle === file ? suiteEntries.slice(1) : suiteEntries;
162
+ const suiteNames = identitySuites.map((entry) => entry.suiteTitle ?? '').filter(Boolean);
163
+ const testcaseName = attrs.name;
164
+ const idName = [...suiteNames, ...(testcaseName ? [testcaseName] : [])].filter(Boolean).join(' > ');
165
+ cases.push({ file, ...(file && idName ? { id: `${file} > ${idName}` } : {}), failures: 0, errors: 0, skipped: 0 });
166
+ }
167
+ if (name === 'failure' || name === 'error' || name === 'skipped') {
168
+ if (stack.some((entry) => entry.name === 'testcase')) {
169
+ const current = cases.at(-1);
170
+ if (current) current[name === 'failure' ? 'failures' : name === 'error' ? 'errors' : 'skipped']++;
171
+ } else if (stack.some((entry) => entry.name === 'testsuite')) { suiteFailed = true; unattributedSuiteFailure = true; }
172
+ }
173
+ if (!selfClose) stack.push({ name, file, ...(name === 'testsuite' && attrs.name ? { suiteTitle: attrs.name } : {}), ...(declaredTests !== undefined ? { declaredTests } : {}),
174
+ ...(declaredFailures !== undefined ? { declaredFailures } : {}), ...(declaredErrors !== undefined ? { declaredErrors } : {}),
175
+ testCount: 0, caseStart: cases.length });
176
+ else if ((declaredTests !== undefined && declaredTests !== 0) || (declaredFailures !== undefined && declaredFailures !== 0)
177
+ || (declaredErrors !== undefined && declaredErrors !== 0)) return null;
178
+ }
179
+ const trailingText = source.slice(cursor);
180
+ if (trailingText.includes('<') || trailingText.includes(']]>') || trailingText.trim() || stack.length || roots !== 1
181
+ || !['testsuite', 'testsuites'].includes(rootName ?? '') || !sawSuite || cases.length === 0) return null;
182
+ return { cases, suiteFailed, unattributedSuiteFailure };
183
+ }
184
+
185
+ /** A null count means the XML was malformed or incomplete. */
186
+ export function junitTestcaseCount(xml: string): number | null {
187
+ return parseJunit(xml)?.cases.length ?? null;
188
+ }
189
+
190
+ /** Unique file attribution for every testcase; null means malformed, incomplete, or unattributed evidence. */
191
+ export function junitTestcaseFiles(xml: string): string[] | null {
192
+ const parsed = parseJunit(xml);
193
+ if (!parsed || parsed.unattributedSuiteFailure || parsed.cases.some((testcase) => !testcase.file)) return null;
194
+ return [...new Set(parsed.cases.map((testcase) => testcase.file!))].sort();
195
+ }
196
+
197
+ /** Named failing/error cases, preserving duplicate identities so a retry must rerun each occurrence. */
198
+ export function junitFailedCaseIds(xml: string): string[] | null {
199
+ const parsed = parseJunit(xml);
200
+ if (!parsed || parsed.unattributedSuiteFailure) return null;
201
+ const failed = parsed.cases.filter((testcase) => testcase.failures > 0 || testcase.errors > 0);
202
+ if (failed.some((testcase) => !testcase.id)) return null;
203
+ const identityCounts = new Map<string, number>();
204
+ for (const testcase of parsed.cases) if (testcase.id) identityCounts.set(testcase.id, (identityCounts.get(testcase.id) ?? 0) + 1);
205
+ // A duplicate testcase identity cannot show which occurrence failed. Treat it as
206
+ // ambiguous evidence instead of allowing a passing sibling to clear the failure.
207
+ if (failed.some((testcase) => identityCounts.get(testcase.id!)! > 1)) return null;
208
+ return failed.map((testcase) => testcase.id!).sort();
209
+ }
210
+
211
+ /** Named cases that completed without failure, error, or skip. */
212
+ export function junitPassedCaseIds(xml: string): string[] | null {
213
+ const parsed = parseJunit(xml);
214
+ if (!parsed) return null;
215
+ return parsed.cases.flatMap((testcase) => testcase.id && !testcase.failures && !testcase.errors && !testcase.skipped ? [testcase.id] : []).sort();
216
+ }
217
+
218
+ /** Attribute files for testcases that have a failure/error node; malformed XML returns null (fail closed). */
219
+ export function failedJunitFiles(xml: string, allowed: readonly string[]): string[] | null {
220
+ const allow = new Set(allowed);
221
+ const parsed = parseJunit(xml);
222
+ if (!parsed || parsed.unattributedSuiteFailure || parsed.cases.some((testcase) => !testcase.file || !allow.has(testcase.file))) return null;
223
+ return [...new Set(parsed.cases.filter((testcase) => testcase.failures > 0 || testcase.errors > 0).map((testcase) => testcase.file!))].sort();
224
+ }
225
+
226
+ interface RetryVerdict { clean: boolean; tests: number; failures: number; errors: number; }
227
+ export function cleanRetryVerdict(rc: number | undefined, log: string | undefined, junit: string | undefined, file: string, expectedFailureIds?: readonly string[]): RetryVerdict {
228
+ const summary = log?.match(/Ran (\d+) tests? across ([1-9]\d*) files?\.?/);
229
+ const pass = Number(log?.match(/^\s*(\d+)\s+pass\s*$/m)?.[1] ?? NaN);
230
+ const failures = Number(log?.match(/^\s*(\d+)\s+fail\s*$/m)?.[1] ?? NaN);
231
+ const errors = Number(log?.match(/^\s*(\d+)\s+errors?\s*$/m)?.[1] ?? 0);
232
+ const tests = Number(summary?.[1] ?? 0);
233
+ const parsed = junit ? parseJunit(junit) : null;
234
+ const cases = parsed?.cases;
235
+ const exactFile = cases?.every((testcase) => testcase.file === file) ?? false;
236
+ const hasFailure = parsed?.suiteFailed || (cases?.some((testcase) => testcase.failures > 0 || testcase.errors > 0 || testcase.skipped > 0) ?? true);
237
+ const rerunIds = junitPassedCaseIds(junit ?? '') ?? [];
238
+ const rerunCounts = new Map<string, number>();
239
+ for (const id of rerunIds) rerunCounts.set(id, (rerunCounts.get(id) ?? 0) + 1);
240
+ const expectedCounts = new Map<string, number>();
241
+ for (const id of expectedFailureIds ?? []) expectedCounts.set(id, (expectedCounts.get(id) ?? 0) + 1);
242
+ const reranOriginalFailures = expectedFailureIds === undefined
243
+ || (expectedFailureIds.length > 0 && [...expectedCounts].every(([id, count]) => (rerunCounts.get(id) ?? 0) >= count));
244
+ const clean = rc === 0 && tests > 0 && Number(summary?.[2]) === 1 && pass === tests && failures === 0 && errors === 0
245
+ && cases?.length === tests && exactFile && !hasFailure && reranOriginalFailures && !/^# Unhandled error between tests/m.test(log ?? '');
246
+ return { clean, tests, failures: Number.isFinite(failures) ? failures : -1, errors: Number.isFinite(errors) ? errors : -1 };
247
+ }
248
+
249
+ if (import.meta.main) {
250
+ const [xmlPath, outputPath, manifestPath] = process.argv.slice(2);
251
+ if (!xmlPath || !outputPath || !manifestPath) process.exit(2);
252
+ let result: string[] | null = null;
253
+ try { result = failedJunitFiles(readFileSync(xmlPath, 'utf8'), readFileSync(manifestPath, 'utf8').split(/\r?\n/).filter(Boolean)); } catch { /* fail closed */ }
254
+ if (!result) process.exit(1);
255
+ writeFileSync(outputPath, result.length ? `${result.join('\n')}\n` : '');
256
+ }