elanous 0.2.42 → 0.2.44

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 (764) 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/{X1ucLw10Cds_h614NoEnh → HMXsxdy5MIIcHPMHv1wD4}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/08334415.f0e80896acbef722.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1032-78ba4f6391258cab.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1082-53f86a26e03d309d.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1109-7d0e264f6f2edbc7.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1124-7c26330402481143.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/{2160.a097e7b138ae1c55.js → 118.2a89982fe4276880.js} +1 -1
  10. package/apps/pwa/out/_next/static/chunks/1244.6c435dbf1bf21d8a.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/1252.9e665904d3aa3b1a.js +32 -0
  12. package/apps/pwa/out/_next/static/chunks/1266.a46cc33cb6a72d75.js +104 -0
  13. package/apps/pwa/out/_next/static/chunks/1306.967240ede9a0f757.js +24 -0
  14. package/apps/pwa/out/_next/static/chunks/1344.3b95f3bd1b77a3fe.js +94 -0
  15. package/apps/pwa/out/_next/static/chunks/{4043.6b91203436c9c86b.js → 1401.22dfe92640a2b1ac.js} +4 -4
  16. package/apps/pwa/out/_next/static/chunks/1431-f7c78bd5be21e011.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/1451-cdd35a8a0b31834b.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/{6632.0eac1c554f8f2ef3.js → 1694.18debfd77562e56d.js} +2 -2
  19. package/apps/pwa/out/_next/static/chunks/1708-f03d587b7a7545c8.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/1775-fa7152bdeff6c95f.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/178-4da509cd7266672a.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/1850-522de4ee34957210.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/1876-c6f6e0ddcc76c6a1.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/1976.097066b072432242.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/{3507.97e1977bc4d5fb0a.js → 1978.eeb60a606dc5deac.js} +2 -2
  26. package/apps/pwa/out/_next/static/chunks/2043.e233e3b5a3e790f5.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/2150.71b94e3348107c83.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2158.ebc6fc5a10280ab7.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/2216.802962c04f40a26e.js +64 -0
  30. package/apps/pwa/out/_next/static/chunks/2294.7890edcfa046c97d.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/235.c89aaae4bcd6233a.js +74 -0
  32. package/apps/pwa/out/_next/static/chunks/{5885.0a3e52c8faccec98.js → 239.de075467b5523469.js} +2 -2
  33. package/apps/pwa/out/_next/static/chunks/249.ba7051590d603c8f.js +226 -0
  34. package/apps/pwa/out/_next/static/chunks/2571.a6ac4e26c8618403.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/2610.e149cd564a08913e.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/2705.9816f3c244f8b85f.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/2801.afeb8a4dae22f85f.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/2954.bbefc95d6b20e0d8.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/2e0f720a.a0697c08e3806a60.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/3042.96ec235fe41c8d4e.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/3097-8e358f158acef46b.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/3205-93b1abe28b4fb16a.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/3273.b4434f0dcae7eb29.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/3311.9cbc43ea0c90d60c.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/{1487.2f67b79444062da6.js → 3349.b5f09ee474d9533e.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/3503.a47a9755fb4d97b0.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/3506-5f429b3b7e1392b0.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/3843.bad404d008893291.js +77 -0
  49. package/apps/pwa/out/_next/static/chunks/3877-4b40e530b47e3bf9.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/435-ce2958253cf7de61.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/4393.28fa5228d0eec217.js +149 -0
  52. package/apps/pwa/out/_next/static/chunks/4404.7281c847c6e10dfc.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/{8673-bab689a55f7e13b9.js → 4415-b9d6a90960920a4a.js} +3 -3
  54. package/apps/pwa/out/_next/static/chunks/447-5b304de1fe098f43.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/4484-8ebea83d431e6fae.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/{8262.ddc03e06b733f184.js → 4502.bdcc877abd437a05.js} +1 -1
  57. package/apps/pwa/out/_next/static/chunks/4599-f95790fc5c65b8df.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/{1262.e804feb8fd1acb8c.js → 46.57c783fa4884714f.js} +2 -2
  59. package/apps/pwa/out/_next/static/chunks/463-b9114e411f33728e.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/4659.a57d1f0c5d754cd2.js +36 -0
  61. package/apps/pwa/out/_next/static/chunks/4722-a847e7a28115035f.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/{1063.2fb8a9dc55c731f2.js → 4757.67c6d7473a038e78.js} +1 -1
  63. package/apps/pwa/out/_next/static/chunks/4874-c86f878ec4502ad3.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/{6377.bc7df239edbff244.js → 4891.3f80ba517a3b2373.js} +1 -1
  65. package/apps/pwa/out/_next/static/chunks/4998.810ca8833084caa9.js +62 -0
  66. package/apps/pwa/out/_next/static/chunks/4f98febc.ea5841d4d616bfb2.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/{3482.a9ae9d2ad6a5bd97.js → 5052.7dbb12a55d4f18f5.js} +1 -1
  68. package/apps/pwa/out/_next/static/chunks/5166-ca73b3e361085a6d.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/5180-039f4a18b07ad387.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/5232.5a2ce4daf7a9e11d.js +115 -0
  71. package/apps/pwa/out/_next/static/chunks/5439.3fec6dbce29ecb36.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/5612-074d9a5e00dfac6a.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/5675-01989d524a84c505.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/5751.af641a1adf5ec697.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/5758-c6cb98714d18ab0c.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/578eded8.b582637306e16964.js +122 -0
  77. package/apps/pwa/out/_next/static/chunks/5790.5eb06a653ebd8571.js +34 -0
  78. package/apps/pwa/out/_next/static/chunks/5853-4033e5bd5afbe06b.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/5904.97d70bb7f46b2db7.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/{3347.f846266b5e5f3ede.js → 5989.7ecd362c5c44b747.js} +3 -3
  81. package/apps/pwa/out/_next/static/chunks/{73d2eb0e.4966ad55293c801a.js → 5a34fb2f.52f1b4e1baebb48a.js} +3 -3
  82. package/apps/pwa/out/_next/static/chunks/{7a98bc80.fa935989e829f9c2.js → 5a5078f8.2099200e5ea95e16.js} +1 -1
  83. package/apps/pwa/out/_next/static/chunks/6036-c302c34ff0b3d410.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/606.ef81c313381f4264.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/616.1af54a928b1ba704.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/6201.bc936f71f613a8e9.js +41 -0
  87. package/apps/pwa/out/_next/static/chunks/6204-fdc86e0e64c4f92d.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/{7411.9b4b8b747dc7b802.js → 6245.1182e007e9d230d9.js} +1 -1
  89. package/apps/pwa/out/_next/static/chunks/{b450776b.3db34d8d965f82d3.js → 63699a35.17262e162bc514b2.js} +1 -1
  90. package/apps/pwa/out/_next/static/chunks/6420.494801a80030160c.js +27 -0
  91. package/apps/pwa/out/_next/static/chunks/{e8543907.5b040a5f22a0b0e5.js → 6549dad0.2740b25e51bf8f05.js} +1 -1
  92. package/apps/pwa/out/_next/static/chunks/{9427.96aa912ddb5801e0.js → 6881.9e7955847640c7fe.js} +17 -17
  93. package/apps/pwa/out/_next/static/chunks/6931.fcdfb94141e67b3d.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/6991-8e081c6ce38c0ba1.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/7010.1bbd17a7f2b3e376.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/7029.d1466acf55fae2ed.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/7152-a123aa8e129c131f.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/7455.196168fffac3b9c4.js +24 -0
  99. package/apps/pwa/out/_next/static/chunks/7638.6ca4770dd1129138.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/7649-f37d76260e5e2a5a.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/7679.32c40cdd14f20b4a.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/7751.04bfbd16b4c6cd0a.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/7864.b1ff31d4030b8994.js +148 -0
  104. package/apps/pwa/out/_next/static/chunks/7869-c47bd2c075c1fefa.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/{8138.6c10b2a356d52fca.js → 8442.2f09ca969d631615.js} +1 -1
  106. package/apps/pwa/out/_next/static/chunks/8445.2738dbd59a78c78c.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/8447.5510c439f9938c6d.js +286 -0
  108. package/apps/pwa/out/_next/static/chunks/{9415.69e4a21c60c7d2db.js → 8551.f762945c2cda2e95.js} +1 -1
  109. package/apps/pwa/out/_next/static/chunks/8635-57a9d8d9db70f4f1.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/86961958-4b0689a5048f756b.js +1 -0
  111. package/apps/pwa/out/_next/static/chunks/8709.3c6019bb18f8554f.js +8 -0
  112. package/apps/pwa/out/_next/static/chunks/8759.c999528a0a907bf6.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/{9a852a62-6192a6ea9346ced3.js → 87f498e5-0881de09f2e44707.js} +1 -1
  114. package/apps/pwa/out/_next/static/chunks/8843.c3197f904c952b81.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/{3d0154af.5331fec737ffe8c3.js → 892fd990.456232f326cc39c2.js} +1 -1
  116. package/apps/pwa/out/_next/static/chunks/8965-135a2c9eaa2464cb.js +1 -0
  117. package/apps/pwa/out/_next/static/chunks/8971-8d3b9c96f8a6c958.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/9059.c8598d96f8e5e9cd.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/9115.90447ae198b84eae.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/9147.d9b0a02e0babc9b4.js +93 -0
  121. package/apps/pwa/out/_next/static/chunks/9204-83075f4b70c61cdc.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/9244-c32ef14b5dc72bdf.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/930.2a9a30f964350c66.js +90 -0
  124. package/apps/pwa/out/_next/static/chunks/9398.1f56e3f17795f43f.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/9527.3e5ad31c891c5fcb.js +79 -0
  126. package/apps/pwa/out/_next/static/chunks/9571-4ac933e774d1225c.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-9fc644de2d8edae3.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/approvals/page-0c9dace2db62bee8.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f5e65c3dc9d89f2a.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/board/page-2a9c19b9a140b7b1.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/botlab/page-61533ad92141adfb.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/bots/page-112004ced6fd0329.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/ceo/page-f733318c237a49d8.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/chat/page-51ea677d2d33bbc0.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/consult/page-428b41f2f9566e3c.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/control/page-1b29396ea90a61e5.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/decisions/page-cf83ec613ac904bf.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/design-check/page-3f4c0ecbc284bb5f.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/editor/page-b63c322f54f70cc6.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/exec/page-fe328c4ee9aac6b4.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/field/page-bfb855c584f0fb98.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/healthz/route-9fc644de2d8edae3.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/inside/page-47b1710c110eabd7.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/intake/page-42895214458bc6d2.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/layout-d03de0ebd8366e23.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/live/page-3ed6c47e4b1c61c2.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/live-run/page-ae653e9bf261c738.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/loops/page-23f3f0a0209c0ac4.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/market/page-88c691273aae66f9.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e187ac52a95538b3.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/morning/page-6e45828daed1295e.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/not-found-edb5d7287bb46186.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/observatory/page-4a7eaacd93c3f6a5.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-3ca073d43d74cc84.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-1e9fee67b1199436.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-df1283ea963c6816.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/outputs/page-19fcf8d8301ccd18.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/page-5ab699e0303bbb40.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/reflection/page-7cde0d29756984c5.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-9d4c598e8ee22f7a.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/sessions/page-cc1609a71f44ec62.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-7659a9a1345bfaa9.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/settings/page-0afef60ecc0c4af5.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-a3553949f8f34f5f.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/setup/layout-9fc644de2d8edae3.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/setup/page-057a69702f3c67c7.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/share/page-c0f0e570f3f74c73.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/showroom/page-1825071ae34e3b89.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/tasks/page-6244e3e135d8ccfc.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/term/page-da787e8a10b8a82e.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/today/page-1316d32498e2be42.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/trace/page-4df94572e343de2e.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/app/vault/page-708969f7475e88c1.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-c58add08c919a1dc.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/app/workflows/page-a24847f45e5d5bf9.js +1 -0
  176. package/apps/pwa/out/_next/static/chunks/app/workspace/page-acfec65a3a60e22e.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-3dfc598e6fbde245.js +1 -0
  178. package/apps/pwa/out/_next/static/chunks/framework-c658a626118411b7.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-4c73685fd891445b.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-app-6310f7e5de7dd01d.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_app-51c326bf48610be1.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_error-d0beadf754da59b0.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/webpack-acd243bfbf3f6732.js +1 -0
  184. package/apps/pwa/out/_next/static/css/d1f8bb401740a549.css +1 -0
  185. package/apps/pwa/out/approvals/index.html +1 -1
  186. package/apps/pwa/out/approvals/index.txt +23 -23
  187. package/apps/pwa/out/autopilot/index.html +1 -1
  188. package/apps/pwa/out/autopilot/index.txt +23 -23
  189. package/apps/pwa/out/board/index.html +1 -1
  190. package/apps/pwa/out/board/index.txt +23 -23
  191. package/apps/pwa/out/botlab/index.html +1 -1
  192. package/apps/pwa/out/botlab/index.txt +22 -22
  193. package/apps/pwa/out/bots/index.html +1 -1
  194. package/apps/pwa/out/bots/index.txt +22 -22
  195. package/apps/pwa/out/ceo/index.html +1 -1
  196. package/apps/pwa/out/ceo/index.txt +23 -23
  197. package/apps/pwa/out/chat/index.html +1 -1
  198. package/apps/pwa/out/chat/index.txt +22 -22
  199. package/apps/pwa/out/consult/index.html +1 -1
  200. package/apps/pwa/out/consult/index.txt +23 -23
  201. package/apps/pwa/out/control/index.html +1 -1
  202. package/apps/pwa/out/control/index.txt +23 -23
  203. package/apps/pwa/out/decisions/index.html +1 -1
  204. package/apps/pwa/out/decisions/index.txt +22 -22
  205. package/apps/pwa/out/design-check/index.html +1 -1
  206. package/apps/pwa/out/design-check/index.txt +23 -23
  207. package/apps/pwa/out/editor/index.html +1 -1
  208. package/apps/pwa/out/editor/index.txt +23 -23
  209. package/apps/pwa/out/exec/index.html +1 -1
  210. package/apps/pwa/out/exec/index.txt +23 -23
  211. package/apps/pwa/out/field/index.html +1 -1
  212. package/apps/pwa/out/field/index.txt +23 -23
  213. package/apps/pwa/out/healthz +1 -1
  214. package/apps/pwa/out/index.html +1 -1
  215. package/apps/pwa/out/index.txt +22 -22
  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 +23 -23
  220. package/apps/pwa/out/live/index.html +1 -1
  221. package/apps/pwa/out/live/index.txt +23 -23
  222. package/apps/pwa/out/live-run/index.html +1 -1
  223. package/apps/pwa/out/live-run/index.txt +23 -23
  224. package/apps/pwa/out/loops/index.html +1 -1
  225. package/apps/pwa/out/loops/index.txt +22 -22
  226. package/apps/pwa/out/market/index.html +1 -1
  227. package/apps/pwa/out/market/index.txt +22 -22
  228. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  229. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  230. package/apps/pwa/out/morning/index.html +1 -1
  231. package/apps/pwa/out/morning/index.txt +23 -23
  232. package/apps/pwa/out/observatory/index.html +1 -1
  233. package/apps/pwa/out/observatory/index.txt +23 -23
  234. package/apps/pwa/out/ops/checklist/index.html +1 -1
  235. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  236. package/apps/pwa/out/ops/release/index.html +1 -1
  237. package/apps/pwa/out/ops/release/index.txt +22 -22
  238. package/apps/pwa/out/ops/seats/index.html +1 -1
  239. package/apps/pwa/out/ops/seats/index.txt +22 -22
  240. package/apps/pwa/out/outputs/index.html +1 -1
  241. package/apps/pwa/out/outputs/index.txt +22 -22
  242. package/apps/pwa/out/reflection/index.html +1 -1
  243. package/apps/pwa/out/reflection/index.txt +23 -23
  244. package/apps/pwa/out/scheduler/index.html +1 -1
  245. package/apps/pwa/out/scheduler/index.txt +23 -23
  246. package/apps/pwa/out/sessions/index.html +1 -1
  247. package/apps/pwa/out/sessions/index.txt +22 -22
  248. package/apps/pwa/out/settings/devices/index.html +1 -1
  249. package/apps/pwa/out/settings/devices/index.txt +23 -23
  250. package/apps/pwa/out/settings/index.html +1 -1
  251. package/apps/pwa/out/settings/index.txt +23 -23
  252. package/apps/pwa/out/setup/done/index.html +1 -1
  253. package/apps/pwa/out/setup/done/index.txt +23 -23
  254. package/apps/pwa/out/setup/index.html +1 -1
  255. package/apps/pwa/out/setup/index.txt +23 -23
  256. package/apps/pwa/out/share/index.html +1 -1
  257. package/apps/pwa/out/share/index.txt +23 -23
  258. package/apps/pwa/out/showroom/index.html +1 -1
  259. package/apps/pwa/out/showroom/index.txt +22 -22
  260. package/apps/pwa/out/tasks/index.html +1 -1
  261. package/apps/pwa/out/tasks/index.txt +23 -23
  262. package/apps/pwa/out/term/index.html +1 -1
  263. package/apps/pwa/out/term/index.txt +23 -23
  264. package/apps/pwa/out/today/index.html +1 -1
  265. package/apps/pwa/out/today/index.txt +23 -23
  266. package/apps/pwa/out/trace/index.html +1 -1
  267. package/apps/pwa/out/trace/index.txt +23 -23
  268. package/apps/pwa/out/vault/index.html +1 -1
  269. package/apps/pwa/out/vault/index.txt +23 -23
  270. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  271. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  272. package/apps/pwa/out/workflows/index.html +1 -1
  273. package/apps/pwa/out/workflows/index.txt +23 -23
  274. package/apps/pwa/out/workspace/index.html +1 -1
  275. package/apps/pwa/out/workspace/index.txt +23 -23
  276. package/apps/pwa/out/worktrees/index.html +1 -1
  277. package/apps/pwa/out/worktrees/index.txt +23 -23
  278. package/catalog/external-commands.yaml +5 -3
  279. package/catalog/meta/catalogVersion.yaml +2 -1
  280. package/catalog/signing/catalog-signing-public.pem +3 -0
  281. package/graphs/intake/intake-daily.yaml +16 -0
  282. package/graphs/intake/news-sources.yaml +1 -0
  283. package/graphs/intake/recipes.yaml +7 -0
  284. package/graphs/overlays/README.md +6 -0
  285. package/graphs/overlays/examples/research-runtime-empty-terminal.yaml +28 -0
  286. package/graphs/profiles/local.yaml +5 -0
  287. package/graphs/profiles/pod.yaml +4 -0
  288. package/graphs/release/release-loop.yaml +33 -17
  289. package/graphs/units/verbs/make-doc.yaml +1 -0
  290. package/package.json +6 -2
  291. package/plugins/demo-absorption-briefing/plugin.json +18 -0
  292. package/plugins/demo-absorption-briefing/skills/demo-absorption-briefing/SKILL.md +8 -0
  293. package/plugins/demo-browser-hand/plugin.json +13 -0
  294. package/plugins/demo-browser-hand/skills/demo-browser-hand/SKILL.md +8 -0
  295. package/plugins/demo-linear-secretary/plugin.json +15 -0
  296. package/plugins/demo-linear-secretary/skills/demo-linear-secretary/SKILL.md +8 -0
  297. package/plugins/demo-plugin-wizard/plugin.json +11 -0
  298. package/plugins/demo-plugin-wizard/skills/demo-plugin-wizard/SKILL.md +8 -0
  299. package/plugins/demo-research/plugin.json +16 -0
  300. package/plugins/demo-research/skills/demo-research/SKILL.md +8 -0
  301. package/plugins/demo-video-briefing/plugin.json +15 -0
  302. package/plugins/demo-video-briefing/skills/demo-video-briefing/SKILL.md +8 -0
  303. package/scripts/bench-metering-plan.ts +229 -0
  304. package/scripts/build-signed-catalog.ts +48 -0
  305. package/scripts/ci-typecheck-main-delta.sh +88 -0
  306. package/scripts/codex-fallback-live-probe.ts +1 -1
  307. package/scripts/demo/demo1104-parallel-host.ts +56 -0
  308. package/scripts/demo/demo1104-parallel-scenes.ts +301 -0
  309. package/scripts/demo/demo1104-parallel.ts +72 -0
  310. package/scripts/demo/demo1104-stage.config.ts +21 -0
  311. package/scripts/demo/demo1104-stage.ts +316 -0
  312. package/scripts/goal-method-audit.ts +17 -1
  313. package/scripts/graph-units/land-enqueue.ts +90 -0
  314. package/scripts/hooks/codex-notify.sh +32 -0
  315. package/scripts/install.sh +10 -38
  316. package/scripts/landing-heal/land-plan.ts +57 -0
  317. package/scripts/public-leak-baseline.txt +0 -1
  318. package/scripts/release-loop/auto-approve-node.ts +1 -1
  319. package/scripts/release-loop/docs-node.ts +4 -2
  320. package/scripts/release-loop/gate-autoheal-sim-evidence.ts +60 -0
  321. package/scripts/release-loop/gate-image-pin.ts +116 -2
  322. package/scripts/release-loop/gate-method.ts +139 -0
  323. package/scripts/release-loop/gate-node.ts +395 -43
  324. package/scripts/release-loop/gate-progress-sim.ts +73 -0
  325. package/scripts/release-loop/gate-traffic-mode.ts +151 -3
  326. package/scripts/release-loop/node-verdict.ts +38 -25
  327. package/scripts/release-loop/notes-repair.ts +18 -1
  328. package/scripts/release-loop/preflight.ts +544 -10
  329. package/scripts/release-loop/render-warm-control-manifest.ts +22 -0
  330. package/scripts/release-loop/resume-release.ts +9 -7
  331. package/scripts/release-loop/resume.ts +13 -4
  332. package/scripts/release-loop/unattended-release.ts +20 -9
  333. package/scripts/release-loop/warm-control-forward.ts +178 -0
  334. package/scripts/retro-cycle.ts +30 -8
  335. package/scripts/rhythm/daily.ts +39 -4
  336. package/scripts/rhythm/linear-progress-sync.ts +311 -0
  337. package/scripts/ux-sim.ts +7 -1
  338. package/scripts/video-pipeline.ts +9 -1
  339. package/scripts/warm-pool-provider-config.ts +35 -7
  340. package/src/acp/agent-manager.ts +1 -0
  341. package/src/acp/codex-app-server-agent.ts +33 -0
  342. package/src/acp/codex-app-server-client.ts +153 -16
  343. package/src/acp/core-turn-bridge.ts +17 -12
  344. package/src/acp/event-router.ts +20 -1
  345. package/src/acp/server.ts +6 -0
  346. package/src/agent-mission/browser-roundtrip-coordinator.ts +85 -0
  347. package/src/agent-mission/claude-headless.ts +4 -1
  348. package/src/agent-mission/codex-exec.ts +105 -0
  349. package/src/agent-mission/driver.ts +192 -11
  350. package/src/agent-mission/grok-auth-status.ts +107 -0
  351. package/src/agent-mission/hand-hooks.ts +101 -0
  352. package/src/agent-mission/mission-cli.ts +56 -21
  353. package/src/agent-mission/mission-hand-reply.ts +118 -0
  354. package/src/agent-mission/mission-line.ts +180 -0
  355. package/src/agent-mission/mission-out.ts +256 -18
  356. package/src/agent-mission/tool-install.ts +122 -11
  357. package/src/agent-substrate/pr-reviewer.ts +147 -11
  358. package/src/agent-substrate/self-review-cli.ts +10 -2
  359. package/src/agents/general-purpose.md +1 -1
  360. package/src/auto-research/research-fanout.ts +221 -0
  361. package/src/auto-research/research-turn.ts +362 -31
  362. package/src/autopilot/mission-codebase-gate.ts +7 -3
  363. package/src/autopilot/pr-manager.ts +31 -3
  364. package/src/boot/daemon-tools/self-implement.ts +3 -1
  365. package/src/boot/daemon-tools/web-search.ts +1 -2
  366. package/src/briefing/brief-items.ts +55 -11
  367. package/src/cli/agent-cli.ts +2 -1
  368. package/src/cli/catalog-cli.ts +31 -0
  369. package/src/cli/connector-cli.ts +16 -0
  370. package/src/cli/control-cli.ts +15 -6
  371. package/src/cli/doctor-fix.ts +2 -2
  372. package/src/cli/intake-source-cli.ts +44 -8
  373. package/src/cli/log-event-names.ts +8 -0
  374. package/src/cli/msg-cli.ts +32 -3
  375. package/src/cli/pr-cli.ts +21 -1
  376. package/src/cli/pr-land-heavy-remote.ts +44 -6
  377. package/src/cli/pr-land-overlap-evidence.ts +13 -1
  378. package/src/cli/pr-land-pod-shadow-job.ts +143 -5
  379. package/src/cli/pr-land-pod-shadow.ts +9 -2
  380. package/src/cli/release-checklist-read-cli.ts +44 -16
  381. package/src/cli/release-cli.ts +23 -3
  382. package/src/cli/research-cli.ts +9 -0
  383. package/src/cli/setup-cli.ts +166 -6
  384. package/src/cli/setup-keys.ts +199 -0
  385. package/src/cli/task-plugin-wizard-host.ts +114 -0
  386. package/src/cli/tasks-cli.ts +54 -9
  387. package/src/cli/tui-test-cli.ts +190 -0
  388. package/src/config.ts +4 -2
  389. package/src/connectors/linear-daily-progress.ts +228 -0
  390. package/src/connectors/linear-projector.ts +34 -12
  391. package/src/connectors/linear.ts +151 -7
  392. package/src/control-plane/ledger.ts +42 -4
  393. package/src/control-plane/member-tokens.ts +25 -2
  394. package/src/control-plane/ports.ts +8 -3
  395. package/src/control-plane/server.ts +26 -5
  396. package/src/core-turn/deep-turn-bloom.ts +67 -0
  397. package/src/core-turn/research-policy.ts +43 -0
  398. package/src/core-turn/run-core-turn.ts +488 -51
  399. package/src/core-turn/types.ts +4 -0
  400. package/src/dashboard/chat/acp-chat.ts +10 -0
  401. package/src/dashboard/classic-sidetab-mission-job.ts +192 -0
  402. package/src/dashboard/classic-sidetab-mission-pair.ts +82 -0
  403. package/src/dashboard/classic-sidetab-mission-trio.ts +242 -0
  404. package/src/dashboard/classic-sidetab-mission.ts +169 -5
  405. package/src/dashboard/index.ts +387 -14
  406. package/src/dashboard/input/chat-main-acp-dispatch.ts +23 -0
  407. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +8 -0
  408. package/src/dashboard/mission-cli-consumer.ts +20 -8
  409. package/src/dashboard/plugin-wizard-runtime.ts +50 -0
  410. package/src/dashboard/plugin-wizard-tool.ts +55 -0
  411. package/src/dashboard/sidetab-runtime.ts +3 -1
  412. package/src/dashboard/terminal-popup-runtime.ts +6 -3
  413. package/src/dashboard/visible-mission-diagnostics.ts +16 -0
  414. package/src/decisions/linear-progress-summary.ts +78 -0
  415. package/src/decisions/linear-progress-sync.ts +152 -0
  416. package/src/domains/outbound-alert.ts +49 -11
  417. package/src/domains/retro-cycle-observation.ts +43 -0
  418. package/src/domains/retro-cycle.ts +13 -2
  419. package/src/domains/retro-proposals.ts +92 -0
  420. package/src/domains/retro-report.ts +6 -1
  421. package/src/domains/telegram-kind-route.ts +16 -9
  422. package/src/graph-runner/arg-allowance.ts +13 -0
  423. package/src/graph-runner/graph-cli.ts +24 -1
  424. package/src/graph-runner/graph-grow-llm.ts +3 -1
  425. package/src/graph-runner/graph-run-api.ts +3 -1
  426. package/src/graph-runner/runner.ts +310 -46
  427. package/src/graph-wizard/mission-compose.ts +407 -12
  428. package/src/harness/dispatch-backpressure.ts +54 -1
  429. package/src/harness/dispatch-shadow-compare.ts +74 -0
  430. package/src/harness/harness-cli-command.ts +15 -3
  431. package/src/harness/harness-pod-dispatch.ts +3 -0
  432. package/src/harness/skill-exec.ts +21 -9
  433. package/src/harness/skill-select.ts +1 -1
  434. package/src/hooks/error-report-cloud.ts +101 -0
  435. package/src/hooks/receiver.ts +29 -2
  436. package/src/index.ts +129 -12
  437. package/src/intake-plane/actions.ts +354 -0
  438. package/src/intake-plane/adapters/telegram-ambient.ts +34 -18
  439. package/src/intake-plane/intake-sources.ts +78 -8
  440. package/src/intake-plane/items.ts +14 -4
  441. package/src/intake-plane/linear-mission-sync.ts +80 -0
  442. package/src/intake-plane/linear-secretary-runtime.ts +178 -0
  443. package/src/intake-plane/linear-wish.ts +227 -1
  444. package/src/life/life-cli.ts +168 -0
  445. package/src/life/life-kinds.ts +160 -0
  446. package/src/live-supply/budget-consumer.ts +78 -0
  447. package/src/live-supply/read.ts +3 -0
  448. package/src/live-supply/snapshot.ts +45 -0
  449. package/src/llm/child-first-llm-observation.ts +112 -0
  450. package/src/llm/provider-credentials.ts +68 -6
  451. package/src/llm/provider-failure-evidence.ts +44 -0
  452. package/src/llm.ts +172 -12
  453. package/src/maturity/feature-maturity.ts +3 -3
  454. package/src/mcp/client.ts +7 -0
  455. package/src/mcp/proxy-runtime.ts +6 -1
  456. package/src/mcp/server.ts +54 -0
  457. package/src/msg/msg-sse-server.ts +3 -3
  458. package/src/msg/msg-store.ts +177 -19
  459. package/src/native-tool-catalog.ts +36 -2
  460. package/src/nexus/api/consult-requests.ts +18 -6
  461. package/src/nexus/api/discord-trigger-bot.ts +6 -6
  462. package/src/nexus/api/graphs-api.ts +4 -2
  463. package/src/nexus/api/grokbot-trigger-bot.ts +206 -0
  464. package/src/nexus/api/http-server.ts +37 -1
  465. package/src/nexus/api/mcp-public-allowlist.ts +2 -0
  466. package/src/nexus/api/telegram-trigger-bot.ts +6 -6
  467. package/src/nexus/boot/register-mcp-clients.ts +114 -8
  468. package/src/nexus/index.ts +22 -2
  469. package/src/nexus/outbound/format.ts +9 -0
  470. package/src/nexus/outbound/grokbot-adapter.ts +71 -0
  471. package/src/nexus/outbound/router.ts +121 -16
  472. package/src/nexus/setup-status.ts +9 -0
  473. package/src/nexus/state/state.ts +3 -0
  474. package/src/oauth/codex-account-store.ts +20 -7
  475. package/src/oauth/codex-owner-mirror.ts +81 -0
  476. package/src/oauth/codex.ts +17 -2
  477. package/src/oauth/fallback-chain.ts +47 -17
  478. package/src/onboarding/first-run-doctor.ts +8 -1
  479. package/src/onboarding/first-run.ts +126 -29
  480. package/src/onboarding/key-setup.ts +103 -0
  481. package/src/onboarding/menu-answer.ts +13 -0
  482. package/src/onboarding/secret-prompt.ts +37 -0
  483. package/src/plugins/install/plugin-install.ts +59 -0
  484. package/src/pr-tool/census.ts +130 -0
  485. package/src/preview/terminal.ts +22 -7
  486. package/src/prompt-enhance/enhance.ts +10 -0
  487. package/src/pty-shell/pty-control-ipc.ts +4 -1
  488. package/src/pty-shell/pty-write-provenance.ts +21 -3
  489. package/src/pty-shell/registry.ts +9 -2
  490. package/src/registry/catalog-server.ts +232 -0
  491. package/src/registry/loader.ts +45 -1
  492. package/src/registry/types.ts +1 -2
  493. package/src/release-loop/proposal-auto-place.ts +83 -0
  494. package/src/research/axis-fanout.ts +79 -29
  495. package/src/research/lenses/second-order.ts +312 -0
  496. package/src/self-dev/ask-launch-flow.ts +72 -1
  497. package/src/self-dev/dev-cli.ts +7 -2
  498. package/src/self-dev/dev-pipeline.ts +5 -0
  499. package/src/self-dev/draft-residue.ts +64 -5
  500. package/src/self-dev/draft-sweep.ts +31 -10
  501. package/src/self-dev/draft-triage-rules.ts +60 -3
  502. package/src/self-dev/graph-front-nodes.ts +2 -1
  503. package/src/self-dev/graph-journey-route.ts +76 -21
  504. package/src/self-dev/launch-preflight.ts +58 -0
  505. package/src/self-dev/orchestrate.ts +1 -1
  506. package/src/self-dev/run-supervisor.ts +152 -2
  507. package/src/self-dev/shard-aggregate.ts +4 -0
  508. package/src/self-dev/shard-run.ts +36 -15
  509. package/src/self-implement/abandoned-classification.ts +12 -3
  510. package/src/self-implement/budget-gate.ts +7 -0
  511. package/src/self-implement/child-llm-preference.ts +10 -1
  512. package/src/self-implement/doctor-visit.ts +30 -8
  513. package/src/self-implement/elanous-tui-spawn.ts +3 -0
  514. package/src/self-implement/fixtures/tdd-green-missing-passing-child-transcript.txt +2 -0
  515. package/src/self-implement/gate-artifacts.ts +2 -1
  516. package/src/self-implement/goal-author-cli.ts +4 -2
  517. package/src/self-implement/goal-author.ts +549 -10
  518. package/src/self-implement/graph-overlay-yaml.ts +181 -4
  519. package/src/self-implement/graph-overlay.ts +3 -1
  520. package/src/self-implement/graph-profile-yaml.ts +107 -0
  521. package/src/self-implement/graph-yaml.ts +94 -5
  522. package/src/self-implement/off-diff-evidence.ts +249 -5
  523. package/src/self-implement/orchestrator.ts +298 -26
  524. package/src/self-implement/run-chart.ts +6 -3
  525. package/src/self-implement/run-ledger.ts +51 -1
  526. package/src/self-implement/runtime-overlay-run-steps.ts +95 -0
  527. package/src/self-implement/tdd-evidence-tags.ts +9 -0
  528. package/src/self-implement/test-scenario-request.ts +37 -6
  529. package/src/self-implement/train-gate-receipt.ts +166 -0
  530. package/src/self-implement/train-gate-remote.ts +62 -18
  531. package/src/session/chat.ts +6 -4
  532. package/src/session/session-fanout.ts +2 -0
  533. package/src/session/streaming/acp-stream-sink.ts +2 -1
  534. package/src/session/streaming/chunk-producer.ts +6 -0
  535. package/src/session/streaming/stream-compositor.ts +3 -2
  536. package/src/session-runtime/index.ts +74 -6
  537. package/src/session-runtime/retry-policy.ts +1 -1
  538. package/src/setup/llm-env-detect.ts +3 -0
  539. package/src/setup/service-key-catalog.ts +46 -0
  540. package/src/setup/service-keys.ts +241 -0
  541. package/src/sidetab-core/model.ts +205 -10
  542. package/src/sidetab-core/visible-agent-mission-pair.ts +51 -0
  543. package/src/sidetab-core/visible-agent-mission.ts +182 -48
  544. package/src/skills/tools/agent.ts +23 -4
  545. package/src/skills/tools/omni-search.ts +127 -27
  546. package/src/task-agent/parent-host.ts +29 -1
  547. package/src/task-agent/recall-inject.ts +98 -0
  548. package/src/task-agent/repair-outcomes.ts +65 -0
  549. package/src/task-agent/task-hand.ts +46 -14
  550. package/src/task-orchestrator/dispatcher.ts +14 -1
  551. package/src/task-orchestrator/linear-capture-events.ts +55 -0
  552. package/src/task-orchestrator/store.ts +16 -5
  553. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +23 -1
  554. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +107 -1
  555. package/src/task-orchestrator/surfaces/pod-ledger-events.ts +113 -14
  556. package/src/task-orchestrator/surfaces/pod-si-warm-host.ts +21 -9
  557. package/src/task-orchestrator/surfaces/pod-source-receive.ts +23 -11
  558. package/src/task-orchestrator/surfaces/pod-warm-switch.ts +2 -0
  559. package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +1 -1
  560. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +467 -77
  561. package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +45 -3
  562. package/src/task-orchestrator/surfaces/self-implement-pod.ts +210 -14
  563. package/src/task-orchestrator/surfaces/self-implement.ts +27 -3
  564. package/src/task-orchestrator/surfaces/warm-reuse-evidence.ts +169 -0
  565. package/src/task-orchestrator/types.ts +14 -0
  566. package/src/telegram.ts +14 -16
  567. package/src/terminal/coding-agent.ts +14 -5
  568. package/src/terminal/session-registry.ts +1 -1
  569. package/src/tool-hints/gate.ts +46 -14
  570. package/src/tool-hints/signals.ts +1 -1
  571. package/src/tool-hints/types.ts +2 -0
  572. package/src/tool-runtime/agent-runtime.ts +1 -0
  573. package/src/tool-runtime/browser-hand-runtime.ts +391 -0
  574. package/src/tool-runtime/browser-runtime.ts +33 -19
  575. package/src/tool-runtime/elanous-linear-issue-runtime.ts +76 -0
  576. package/src/tool-runtime/elanous-linear-secretary-runtime.ts +173 -0
  577. package/src/tool-runtime/graph-mcp-runtimes.ts +240 -0
  578. package/src/tool-runtime/index.ts +6 -0
  579. package/src/tool-runtime/types.ts +5 -0
  580. package/src/tui/entry.ts +51 -8
  581. package/src/tui/sidetab/attach.ts +252 -11
  582. package/src/tui/sidetab/herdr-portal.ts +225 -0
  583. package/src/tui/sidetab/list.ts +3 -2
  584. package/src/tui-client/next-dashboard-runtime.ts +52 -0
  585. package/src/user-config.ts +34 -9
  586. package/src/user-intent/types.ts +4 -3
  587. package/src/ux-sim/browser-hand.ts +165 -0
  588. package/src/ux-sim/draft-inventory-builder.ts +199 -0
  589. package/src/ux-sim/fake-browser-page.ts +81 -0
  590. package/src/ux-sim/fake-linear-device.ts +31 -0
  591. package/src/ux-sim/graph-engine-sim.ts +47 -0
  592. package/src/ux-sim/linear-secretary.ts +208 -0
  593. package/src/ux-sim/mock-scenario-catalog.json +81 -26
  594. package/src/ux-sim/mock-scenario-catalog.ts +175 -5
  595. package/src/ux-sim/onboarding-first-run-device.ts +31 -21
  596. package/src/ux-sim/release-sim-rig.ts +62 -18
  597. package/src/ux-sim/scenario.ts +70 -26
  598. package/src/ux-sim/service-key-setup.ts +113 -0
  599. package/src/ux-sim/term-slash-device.ts +74 -0
  600. package/src/ux-sim/turn-throw-sim.ts +31 -0
  601. package/src/version/packed-revision.json +1 -1
  602. package/src/web-search/exa.ts +77 -0
  603. package/src/web-search/index.ts +30 -9
  604. package/src/web-search/provider.ts +4 -1
  605. package/src/web-search/x-intent.ts +5 -0
  606. package/apps/pwa/out/_next/static/chunks/0244e5b5.6fc7670f8223d82d.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/1121.13ba7a8aff19df81.js +0 -36
  608. package/apps/pwa/out/_next/static/chunks/1147-5196d19c222051b3.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/1319.b9b815ae0f181e06.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/1345-31427b833438f521.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/1455-530ca16c7e71baff.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/1574.878c18ecc97b1bd4.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/1586.fafc33a9a5835cb7.js +0 -32
  614. package/apps/pwa/out/_next/static/chunks/1744.855fd60ae4c4a8c8.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/193.21a47a3e07ef6860.js +0 -1
  616. package/apps/pwa/out/_next/static/chunks/1945.7b54990d578c119b.js +0 -1
  617. package/apps/pwa/out/_next/static/chunks/2004.e839fda7ea5a738a.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/2044-dd681a5c08779ff8.js +0 -1
  619. package/apps/pwa/out/_next/static/chunks/2101.2248decb38829b03.js +0 -286
  620. package/apps/pwa/out/_next/static/chunks/2300.41ea70ac1bef1446.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/2357-07b5eeb5fb12628d.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/2364.28652020695a6db3.js +0 -34
  623. package/apps/pwa/out/_next/static/chunks/2456.b6de0e1c05f28843.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/2526.c3e50ac1fec064bd.js +0 -1
  625. package/apps/pwa/out/_next/static/chunks/2549-cda50b716ff4250c.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/2550-5f1e88f615568045.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/261-319078c954e40d66.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/2690.0521c738b7749931.js +0 -115
  629. package/apps/pwa/out/_next/static/chunks/2974.595d1e29d040cb6a.js +0 -64
  630. package/apps/pwa/out/_next/static/chunks/3207.a756c95e9ec8c3c8.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/3285.89ee2f298682f5e5.js +0 -93
  632. package/apps/pwa/out/_next/static/chunks/3447.06edc7b092021417.js +0 -41
  633. package/apps/pwa/out/_next/static/chunks/3510-9ca392698bfe4391.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/3541.772e5ce425aaa611.js +0 -24
  635. package/apps/pwa/out/_next/static/chunks/3741.9cf76acaa2ee5298.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/3837.a210d7d0db8d8abd.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/3898-955c896f394217a6.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/3947-ef57acea76eaa8d2.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/4037-3b82ebc7d78afdc7.js +0 -1
  640. package/apps/pwa/out/_next/static/chunks/405.9022d4a478202b67.js +0 -77
  641. package/apps/pwa/out/_next/static/chunks/417-fb2cc9a9910e5968.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/4276.336d76d858fcdf1b.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/4302.6ced9aa4535de947.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/4320.583cbab64db6be02.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/4768-8eac183fd291ca53.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/4770.769c9cd9c7034eba.js +0 -148
  647. package/apps/pwa/out/_next/static/chunks/4895.65605ec3e6504e89.js +0 -74
  648. package/apps/pwa/out/_next/static/chunks/50061469.0326382241ef3b2d.js +0 -122
  649. package/apps/pwa/out/_next/static/chunks/5112.4b9f300124fab7b3.js +0 -149
  650. package/apps/pwa/out/_next/static/chunks/5335.375ee922bcd5bc83.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/5342.023a2e729689b320.js +0 -27
  652. package/apps/pwa/out/_next/static/chunks/53c73f84.373b6160adfa89a4.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/5431-72a1dbde59400691.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/5440-4236de26c5ea5856.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/5486-b318c165f645d8ba.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/5506-c7d2ec25c62a7c94.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/5570-c66ecbea26b4e3b1.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/5696.ffca5aa259d8451e.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/5793-8c584aea2aa87560.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/5848-6de6b8c173351a90.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/5999.94e3c83040200f31.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/5a7901d6-2d8d9b5587cf9042.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/6131.348b7d6bdcd267ce.js +0 -226
  664. package/apps/pwa/out/_next/static/chunks/6164.86133a0172611184.js +0 -90
  665. package/apps/pwa/out/_next/static/chunks/6515.e07e8b2b8a707a87.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/6522-c005514f87d18982.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/6642.648adb1767f39ecf.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/6848.138805a09d001d09.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/7049-c09e2b7a253a24f3.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/7072.1e00447d9fa39972.js +0 -104
  671. package/apps/pwa/out/_next/static/chunks/7106.64201c539c5e220c.js +0 -94
  672. package/apps/pwa/out/_next/static/chunks/7151-1f742b455986530e.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/7199.18d6b25e001154ce.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/7279-c199ea83f34338fa.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/728b938d.ffaa7ad583d60e58.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/7309.171c3de54e0322f1.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/733.7a1ab3342e03306f.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/7504.d5cf1e7cd89b1b08.js +0 -24
  679. package/apps/pwa/out/_next/static/chunks/7573-0fd4af62d65d4598.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/759.6f8267df4689c401.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/7825-668b046f72f14483.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/7852-1538768b6eb19388.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/7868.cff810430bb63c91.js +0 -1
  684. package/apps/pwa/out/_next/static/chunks/8084-dfcc796874ab4e07.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/8266.6bf9b131b2584678.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/8403.7b81d5e64074a725.js +0 -8
  687. package/apps/pwa/out/_next/static/chunks/8470.1998cbd0866a7eac.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/8505.fd803c6c33b54cc3.js +0 -79
  689. package/apps/pwa/out/_next/static/chunks/8546-6f03f8ea34dd61d9.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/8687-246fda8433d3477c.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/898-280022fb082936bc.js +0 -1
  692. package/apps/pwa/out/_next/static/chunks/9098-50eefc1844212e3d.js +0 -1
  693. package/apps/pwa/out/_next/static/chunks/9211-330b6736a63d4cb3.js +0 -1
  694. package/apps/pwa/out/_next/static/chunks/9249.976f1e69a9d80754.js +0 -1
  695. package/apps/pwa/out/_next/static/chunks/9255-cf88fceda5666645.js +0 -1
  696. package/apps/pwa/out/_next/static/chunks/9306-e523ba58bb8adc42.js +0 -1
  697. package/apps/pwa/out/_next/static/chunks/9324.a9866d717ba62cb6.js +0 -1
  698. package/apps/pwa/out/_next/static/chunks/9325.a8510e8aa87c8143.js +0 -1
  699. package/apps/pwa/out/_next/static/chunks/9353-12bfff0c130104cf.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/9479-154a697d4b6e4e98.js +0 -1
  701. package/apps/pwa/out/_next/static/chunks/9540-65aa6ccc31b3fd78.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/9633.ce5bddc56784da72.js +0 -1
  703. package/apps/pwa/out/_next/static/chunks/9656-374232558fd528d0.js +0 -1
  704. package/apps/pwa/out/_next/static/chunks/9832.f4a98c878ecf2c0a.js +0 -62
  705. package/apps/pwa/out/_next/static/chunks/9865.f094440645f02d98.js +0 -1
  706. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-e772b4710df5d936.js +0 -1
  707. package/apps/pwa/out/_next/static/chunks/app/approvals/page-4361368e6a76b967.js +0 -1
  708. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-802f349fdd65248d.js +0 -1
  709. package/apps/pwa/out/_next/static/chunks/app/board/page-7f35513988a2a495.js +0 -1
  710. package/apps/pwa/out/_next/static/chunks/app/botlab/page-798121656540e33e.js +0 -1
  711. package/apps/pwa/out/_next/static/chunks/app/bots/page-20a089490affe98c.js +0 -1
  712. package/apps/pwa/out/_next/static/chunks/app/ceo/page-e5e4ec786d70e604.js +0 -1
  713. package/apps/pwa/out/_next/static/chunks/app/chat/page-d12fe5d0801430e4.js +0 -1
  714. package/apps/pwa/out/_next/static/chunks/app/consult/page-d8cf21022fecc026.js +0 -1
  715. package/apps/pwa/out/_next/static/chunks/app/control/page-21ce565c9c57ee97.js +0 -1
  716. package/apps/pwa/out/_next/static/chunks/app/decisions/page-b56563025f619c9b.js +0 -1
  717. package/apps/pwa/out/_next/static/chunks/app/design-check/page-52f14dd133da8348.js +0 -1
  718. package/apps/pwa/out/_next/static/chunks/app/editor/page-c0ed7fdcdba76633.js +0 -1
  719. package/apps/pwa/out/_next/static/chunks/app/exec/page-af8c3020cb520f15.js +0 -1
  720. package/apps/pwa/out/_next/static/chunks/app/field/page-72eda67af9f5e35c.js +0 -1
  721. package/apps/pwa/out/_next/static/chunks/app/healthz/route-e772b4710df5d936.js +0 -1
  722. package/apps/pwa/out/_next/static/chunks/app/inside/page-67a6620d31f0a46f.js +0 -1
  723. package/apps/pwa/out/_next/static/chunks/app/intake/page-f059363aa0880ec2.js +0 -1
  724. package/apps/pwa/out/_next/static/chunks/app/layout-f9f917d6a57f69fb.js +0 -1
  725. package/apps/pwa/out/_next/static/chunks/app/live/page-d3ae55eee2f0826c.js +0 -1
  726. package/apps/pwa/out/_next/static/chunks/app/live-run/page-0e3d7b9421b79dac.js +0 -1
  727. package/apps/pwa/out/_next/static/chunks/app/loops/page-ba738f7e233eb211.js +0 -1
  728. package/apps/pwa/out/_next/static/chunks/app/market/page-33438b98c74fd599.js +0 -1
  729. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-0b34d5fd990d793a.js +0 -1
  730. package/apps/pwa/out/_next/static/chunks/app/morning/page-0f291d3c8fd55a3a.js +0 -1
  731. package/apps/pwa/out/_next/static/chunks/app/not-found-3fced192be5279c1.js +0 -1
  732. package/apps/pwa/out/_next/static/chunks/app/observatory/page-04cb3cf182a7c1ea.js +0 -1
  733. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-6ccbd2ab23a21542.js +0 -1
  734. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-aa0518c48a5f4e56.js +0 -1
  735. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4b32226d341ad7d7.js +0 -1
  736. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3224fd8f6383b61f.js +0 -1
  737. package/apps/pwa/out/_next/static/chunks/app/page-efbc97459f2914e2.js +0 -1
  738. package/apps/pwa/out/_next/static/chunks/app/reflection/page-2fe487d88cd5d97c.js +0 -1
  739. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-48861dd1a858bab7.js +0 -1
  740. package/apps/pwa/out/_next/static/chunks/app/sessions/page-197d5d4d21d228ea.js +0 -1
  741. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a763ba89fbdd9c27.js +0 -1
  742. package/apps/pwa/out/_next/static/chunks/app/settings/page-7edb253510b1c136.js +0 -1
  743. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0c875702c61965ea.js +0 -1
  744. package/apps/pwa/out/_next/static/chunks/app/setup/layout-e772b4710df5d936.js +0 -1
  745. package/apps/pwa/out/_next/static/chunks/app/setup/page-e425c951151095f7.js +0 -1
  746. package/apps/pwa/out/_next/static/chunks/app/share/page-9c90e0aec4e27f77.js +0 -1
  747. package/apps/pwa/out/_next/static/chunks/app/showroom/page-733e09a02e727d42.js +0 -1
  748. package/apps/pwa/out/_next/static/chunks/app/tasks/page-4f899e14c34d77a1.js +0 -1
  749. package/apps/pwa/out/_next/static/chunks/app/term/page-bf067887b3307423.js +0 -1
  750. package/apps/pwa/out/_next/static/chunks/app/today/page-7af10868ff87d04e.js +0 -1
  751. package/apps/pwa/out/_next/static/chunks/app/trace/page-efe5304057aa11c7.js +0 -1
  752. package/apps/pwa/out/_next/static/chunks/app/vault/page-76bcd29aaa8a26c3.js +0 -1
  753. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-4cfd88e2273ce50f.js +0 -1
  754. package/apps/pwa/out/_next/static/chunks/app/workflows/page-0a1ddb47efba0a32.js +0 -1
  755. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2a176c707152c892.js +0 -1
  756. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-efe03031b8797fbf.js +0 -1
  757. package/apps/pwa/out/_next/static/chunks/framework-d0d2d673bfd8a8a9.js +0 -1
  758. package/apps/pwa/out/_next/static/chunks/main-4c9fceb2baa09cb7.js +0 -1
  759. package/apps/pwa/out/_next/static/chunks/main-app-b0ab8d8fc9a97556.js +0 -1
  760. package/apps/pwa/out/_next/static/chunks/pages/_app-7e2cc62a2e5cec96.js +0 -1
  761. package/apps/pwa/out/_next/static/chunks/pages/_error-22324935f753ec5e.js +0 -1
  762. package/apps/pwa/out/_next/static/chunks/webpack-9dc1bfa25334998a.js +0 -1
  763. package/apps/pwa/out/_next/static/css/be9019f696217521.css +0 -1
  764. /package/apps/pwa/out/_next/static/{X1ucLw10Cds_h614NoEnh → HMXsxdy5MIIcHPMHv1wD4}/_ssgManifest.js +0 -0
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env bun
2
2
  import { spawn, spawnSync } from 'node:child_process';
3
- import { appendFileSync, chmodSync, copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { appendFileSync, chmodSync, copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
4
4
  import { hostname, tmpdir } from 'node:os';
5
5
  import { createHash, randomUUID } from 'node:crypto';
6
- import { basename, dirname, join, resolve } from 'node:path';
6
+ import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
7
7
  import { debug, redactSecretText } from '../../src/debug/log.js';
8
8
  import { registerStandaloneLogSink } from '../../src/domains/standalone-log-sink.js';
9
9
  import { landingFreezeMessage, readLandingFreeze } from '../../src/release-loop/landing-freeze.js';
@@ -20,7 +20,8 @@ import { planGateRadiusShadow, shadowActual, writeGateRadiusShadow, type GateRad
20
20
  import { planHardRehearsalRadius, partialGateImagesMatch, preflightGateExecutionIdentity, preflightGateImageDigest } from './gate-radius-live';
21
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';
23
+ import { gateImagePinPath, normalizeGateImageDigest, pinnedGateImageSync as buildPinnedGateImageSync, readGateImagePin,
24
+ readGateImagePinForRun, replaceGateImagePin, resolveGateImagePin, writeGateImagePin } from './gate-image-pin.js';
24
25
  import { gateReceiptRegistryPath, planGateTrainReceiptCarry } from './gate-train-receipts.js';
25
26
  import { gateEnvFingerprint as gateReceiptEnvFingerprint, gateLockfileHash } from '../../src/self-implement/gate-receipt-registry.js';
26
27
  import { SPAWN_SYNC_MAX_BUFFER } from '../../src/util/spawn-sync-output.js';
@@ -36,13 +37,15 @@ import { emitNodeResult, readGraphContext } from './node-verdict.js';
36
37
  import { isGateExecutor, type GateExecutor } from './gate-executor.js';
37
38
  import { runPodCommand, parsePodCpu, podCpuMillis, resolvePodCpu, podCommandImageFor, type RunPodCommandOptions, type PodCommandResult, type PodCpu } from '../../src/task-orchestrator/surfaces/pod-command-job.js';
38
39
  import { POD_BUN_CACHE_HOST_PATH, parseInstallSeconds, podBunCacheVolume } from '../../src/task-orchestrator/surfaces/pod-bun-cache.js';
39
- import { PodPoolScheduler, parsePodPool, checkPodPool, defaultRemoteRun, snapshotWarmPoolMembers, type PodPoolMember } from '../../src/task-orchestrator/surfaces/pod-pool.js';
40
+ import { NODE_REGISTRY, PodPoolScheduler, parsePodPool, checkPodPool, defaultRemoteRun, snapshotWarmPoolMembers, syncPoolImages, type PodPoolMember } from '../../src/task-orchestrator/surfaces/pod-pool.js';
40
41
  import { POD_INSTALL_SLOTS_DEFAULT, POD_INSTALL_SLOTS_HOST_PATH, installSlotScript } from '../../src/task-orchestrator/surfaces/pod-install-slots.js';
41
42
  import { gateDepsInstallScript, parseGateDeps } from '../../src/task-orchestrator/surfaces/pod-gate-deps.js';
42
43
  import { warmReferenceFromJobArtifacts, writeWarmJobManifest } from './gate-warm-artifacts.js';
43
44
  import { assessWarmWorkerShadow, parseWarmWorkerReply, type WarmWorkerCommand, type WarmWorkerFileResult, type WarmWorkerKey } from '../../src/task-orchestrator/surfaces/pod-warm-worker-protocol.js';
44
45
  import type { WarmPodTransportResult } from '../../src/task-orchestrator/surfaces/pod-warm-worker-pod.js';
45
- import { requestWarmPodReplay, finalizeWarmPodReplay, quarantineWarmPodReplay, type WarmReplayResult } from '../../src/task-orchestrator/surfaces/pod-warm-worker-provider.js';
46
+ import { configureWarmPodProvider, loadWarmPodProvider, requestWarmPodReplay, finalizeWarmPodReplay, quarantineWarmPodReplay, type WarmReplayInput, type WarmReplayResult } from '../../src/task-orchestrator/surfaces/pod-warm-worker-provider.js';
47
+ import { DEFAULT_CONTROL_PORT } from '../../src/control-plane/server.js';
48
+ import { createRunScopedWarmControlForward, startWarmControlPortForward } from './warm-control-forward.js';
46
49
  import { resolveWarmPoolProfile } from '../../src/self-implement/pod-warm-pool-profile.js';
47
50
  import { decideWarmReuse, type WarmReuseSwitchConfig, type WarmReuseEvidence } from '../../src/task-orchestrator/surfaces/pod-warm-switch.js';
48
51
  import { getUserConfig } from '../../src/user-config.js';
@@ -52,7 +55,7 @@ import { markGateShardUnmeasured, nextGateShardAttempt, readGateShardManifest, r
52
55
  import { planGateShardDispatch, summarizeGateProgress, type GateProgressGroup, type GateShardReceipt } from './gate-shard-dispatch.js';
53
56
  import { defaultKubectl } from '../../src/task-orchestrator/surfaces/self-implement-pod.js';
54
57
  import { executeTuiNodeCheck, type ExecutedTuiCheck } from './tui-node-checks.js';
55
- import { runWithReleaseGateTraffic } from './gate-traffic-mode.js';
58
+ import { runWithReleaseGateTraffic, runWithReleasePwaTraffic } from './gate-traffic-mode.js';
56
59
  import { emitPodLifecycle, gatePodLifecycleEvent, gatePodOutcome } from '../../src/task-orchestrator/surfaces/pod-lifecycle-stages.js';
57
60
  import { gateHostReservationRoot, reserveReleaseGateHosts } from '../../src/release-loop/gate-host-reservation.js';
58
61
  import { normRoot } from '../../src/instance/resolve.js';
@@ -111,6 +114,8 @@ function redactedGateLogTail(input: string): string {
111
114
  * Every field falls back to the real tool, so an absent seam is today's behaviour byte for byte.
112
115
  */
113
116
  export interface GateTools {
117
+ /** Graph run identity passed to pool admission for shadow observations. */
118
+ graphRunId?: string | null;
114
119
  /** Wall clock the gate measures with; absent = `Date.now`. */
115
120
  now?: () => number;
116
121
  /** The Pod Job runner (the fake Pod stage of a scenario); absent = the positional `podCommand`, else `runPodCommand`. */
@@ -227,6 +232,10 @@ export function reconcileStaleGateShardBoard(input: {
227
232
  }
228
233
  export interface GateOptions {
229
234
  commit: string;
235
+ /** Explicit pre-cut warm↔fresh comparison, persisted before the ordinary gate reads warmHistory. */
236
+ warmPrecutCanary?: { file: string };
237
+ /** RELEASE-POLICY-PACK — the gate Pod image is tied to this resolved cut commit. */
238
+ imagePin?: 'cut-commit';
230
239
  /** The graph run that launched this gate; direct hard rehearsal instead pins source bytes explicitly. */
231
240
  graphRunId?: string;
232
241
  /** Only a direct hard rehearsal may pin graph bytes from its measured Git commit. */
@@ -250,6 +259,7 @@ export interface GateOptions {
250
259
  recheckFileLimit?: number;
251
260
  /** freshShards — GATE-SHARD-RESUME off: every shard starts a Pod even when this logDir already holds its result. */
252
261
  pod?: { pool: string; shards?: number; shardTimeoutSeconds?: number; durationSource?: string; bunCache?: string; freshShards?: boolean;
262
+ imagePin?: 'cut-commit';
253
263
  /** One immutable container image identity for this gate graph run and all its resumes. */
254
264
  pinnedImageDigest?: string;
255
265
  pinnedRunId?: string;
@@ -335,14 +345,64 @@ export interface GateOptions {
335
345
  }
336
346
 
337
347
  /** Gate-profile opt-in wires the producer only for the process entry, never a test runner seam. */
338
- export function withWarmGateHooks(opts: GateOptions, config: unknown, injectedRunner = false): GateOptions {
348
+ export function withWarmGateHooks(opts: GateOptions, config: unknown, injectedRunner = false,
349
+ onWarmCleanup?: (cleanup: () => Promise<void>) => void): GateOptions {
339
350
  if (injectedRunner || !opts.pod || resolveWarmPoolProfile(config) !== 'gate') return opts;
340
351
  const warmReuseConfig = config && typeof config === 'object' && 'pod' in config
341
352
  && config.pod && typeof config.pod === 'object' && 'warmReuse' in config.pod
342
353
  ? config.pod.warmReuse as WarmReuseSwitchConfig : undefined;
343
- return { ...opts, pod: { ...opts.pod, warmReplay: requestWarmPodReplay,
354
+ const lifecycle = createRunScopedWarmControlForward(async () => {
355
+ const configPath = process.env.ELANOUS_WARM_POD_PROVIDER_CONFIG;
356
+ if (!configPath) throw new Error('warm provider config unavailable');
357
+ const target = warmProviderTarget(configPath);
358
+ const forward = await startWarmControlPortForward({ context: target.context, namespace: target.namespace,
359
+ service: 'elanous-control', remotePort: DEFAULT_CONTROL_PORT });
360
+ try {
361
+ const provider = loadWarmPodProvider(configPath, { runtimeOverrides: { controlUrl: forward.url } });
362
+ configureWarmPodProvider(provider);
363
+ return forward;
364
+ } catch (error) {
365
+ await forward.stop();
366
+ throw error;
367
+ }
368
+ });
369
+ const replay: NonNullable<GateOptions['pod']>['warmReplay'] = async (input) => {
370
+ try { await lifecycle.ensure(); }
371
+ catch (error) {
372
+ const reason = error instanceof Error ? error.message : String(error);
373
+ debug.log('pod.warm.control-forward', 'unavailable', { reason, fallback: 'fresh-job' }, { level: 'error' });
374
+ return { command: null, transport: { status: 'fallback', reason: 'control-unavailable', reply: null } };
375
+ }
376
+ return requestWarmPodReplay(input);
377
+ };
378
+ const cleanup = async () => {
379
+ try { await lifecycle.stop(); }
380
+ finally { configureWarmPodProvider(null); }
381
+ };
382
+ onWarmCleanup?.(cleanup);
383
+ return { ...opts, pod: { ...opts.pod, warmReplay: replay,
344
384
  warmFinalize: finalizeWarmPodReplay, warmQuarantine: quarantineWarmPodReplay, warmProducerEnabled: true, warmReuseConfig } };
345
385
  }
386
+
387
+ function warmProviderTarget(path: string): { context: string; namespace: string } {
388
+ if (!isAbsolute(path) || (statSync(path).mode & 0o077) !== 0) throw new Error('warm provider config must be private');
389
+ const parsed: unknown = JSON.parse(readFileSync(path, 'utf8'));
390
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error('invalid warm provider config');
391
+ const data = parsed as { candidates?: Array<{ spec?: { kubectlContext?: string; namespace?: string } }>;
392
+ poolTemplates?: Array<{ spec?: { kubectlContext?: string; namespace?: string } }> };
393
+ const rows = [...(data.candidates ?? []), ...(data.poolTemplates ?? [])];
394
+ const targets = new Map<string, { context: string; namespace: string }>();
395
+ for (const row of rows) {
396
+ const context = row.spec?.kubectlContext;
397
+ const namespace = row.spec?.namespace;
398
+ if (typeof context !== 'string' || !context || typeof namespace !== 'string' || !namespace)
399
+ throw new Error('warm provider target is incomplete');
400
+ targets.set(`${context}/${namespace}`, { context, namespace });
401
+ }
402
+ if (targets.size !== 1) throw new Error('warm control forward requires one cluster and namespace per gate run');
403
+ return [...targets.values()][0]!;
404
+ }
405
+
346
406
  export interface GateResult {
347
407
  outcome: 'ok' | 'regression' | 'error';
348
408
  commit: string;
@@ -409,29 +469,151 @@ const WARM_REPLAY_BUDGET = 3;
409
469
  interface WarmVerdictSample { version: string; commit: string; baselineCommit: string; warmShadow: WarmShadowObservation[] }
410
470
  const recordValue = (value: unknown): Record<string, unknown> | null =>
411
471
  value !== null && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : null;
472
+ function validWarmShadowObservation(value: unknown): value is WarmShadowObservation {
473
+ const row = recordValue(value);
474
+ if (!row || !Array.isArray(row.files) || row.files.length !== 1 || typeof row.files[0] !== 'string'
475
+ || !/^(?:[\w.-]+\/)*[\w.-]+\.test\.[cm]?[jt]sx?$/.test(row.files[0])
476
+ || row.files[0].split('/').some((part) => part === '.' || part === '..')
477
+ || typeof row.attemptId !== 'string' || !row.attemptId || typeof row.reason !== 'string'
478
+ || !['observed', 'fallback', 'unmeasured'].includes(String(row.status))
479
+ || !['yes', 'no', 'failed', 'unavailable'].includes(String(row.quarantined))) return false;
480
+ if (row.finalized !== undefined && typeof row.finalized !== 'boolean') return false;
481
+ if (row.cutSha !== undefined && typeof row.cutSha !== 'string') return false;
482
+ if (row.baselineSha !== undefined && typeof row.baselineSha !== 'string') return false;
483
+ if (row.workerKey !== undefined) {
484
+ const key = recordValue(row.workerKey);
485
+ if (!key || typeof key.context !== 'string' || !key.context || typeof key.imageDigest !== 'string' || !key.imageDigest
486
+ || typeof key.lockHash !== 'string' || !key.lockHash || typeof key.architecture !== 'string' || !key.architecture) return false;
487
+ }
488
+ return true;
489
+ }
490
+ function validWarmVerdictSample(value: unknown): value is WarmVerdictSample {
491
+ const row = recordValue(value);
492
+ return !!row && typeof row.version === 'string' && typeof row.commit === 'string'
493
+ && typeof row.baselineCommit === 'string' && Array.isArray(row.warmShadow)
494
+ && row.warmShadow.every(validWarmShadowObservation);
495
+ }
412
496
  /** The verdict is atomically replaced; carry a bounded prior-verdict window in that same SSOT. */
413
497
  function readWarmVerdictHistory(path: string): WarmVerdictSample[] | undefined {
414
498
  if (!existsSync(path)) return [];
415
499
  try {
416
500
  const current = recordValue(JSON.parse(readFileSync(path, 'utf8')));
417
501
  if (!current || !Array.isArray(current.warmHistory)) return undefined;
418
- const history = [...current.warmHistory, { version: current.version, commit: current.commit,
419
- baselineCommit: current.baselineCommit, warmShadow: current.warmShadow }].slice(-WARM_VERDICT_HISTORY_LIMIT);
420
- if (history.some((entry) => { const row = recordValue(entry);
421
- return !row || typeof row.version !== 'string' || typeof row.commit !== 'string'
422
- || typeof row.baselineCommit !== 'string' || !Array.isArray(row.warmShadow); })) return undefined;
423
- return history as WarmVerdictSample[];
502
+ if (current.warmHistory.some((entry) => !validWarmVerdictSample(entry))) return undefined;
503
+ const hasCurrentSample = ['version', 'commit', 'baselineCommit', 'warmShadow'].some((key) => key in current);
504
+ const history = [...current.warmHistory];
505
+ if (current.warmHistoryOnly !== true || hasCurrentSample) {
506
+ const currentSample = { version: current.version, commit: current.commit,
507
+ baselineCommit: current.baselineCommit, warmShadow: current.warmShadow };
508
+ if (!validWarmVerdictSample(currentSample)) return undefined;
509
+ if (current.warmHistoryOnly !== true) history.push(currentSample);
510
+ }
511
+ return history.slice(-WARM_VERDICT_HISTORY_LIMIT) as WarmVerdictSample[];
424
512
  } catch { return undefined; }
425
513
  }
514
+
515
+ /** Append one actual pre-cut replay observation to the gate verdict's bounded history, preserving its gate result. */
516
+ function recordWarmPrecutCanarySample(path: string, sample: WarmVerdictSample): void {
517
+ if (!sample.version || !/^[0-9a-f]{7,40}$/i.test(sample.commit) || !/^[0-9a-f]{7,40}$/i.test(sample.baselineCommit)
518
+ || !Array.isArray(sample.warmShadow) || sample.warmShadow.length !== 1 || !sample.warmShadow.every(validWarmShadowObservation))
519
+ throw new Error('invalid pre-cut canary sample');
520
+ let current: Record<string, unknown> = {};
521
+ if (existsSync(path)) {
522
+ let decoded: unknown;
523
+ try { decoded = JSON.parse(readFileSync(path, 'utf8')); }
524
+ catch (error) { throw new Error(`gate verdict JSON is malformed: ${String(error)}`); }
525
+ const parsed = recordValue(decoded);
526
+ if (!parsed) throw new Error('gate verdict is not an object');
527
+ current = parsed;
528
+ if (current.warmHistory !== undefined && !Array.isArray(current.warmHistory)) throw new Error('gate verdict warmHistory is invalid');
529
+ }
530
+ const storedHistory = Array.isArray(current.warmHistory) ? [...current.warmHistory] : [];
531
+ if (storedHistory.some((entry) => !validWarmVerdictSample(entry))) throw new Error('gate verdict warmHistory contains an invalid row');
532
+ const history = storedHistory;
533
+ const hasCurrentSample = ['version', 'commit', 'baselineCommit', 'warmShadow'].some((key) => key in current);
534
+ if (current.warmHistoryOnly === true && hasCurrentSample) {
535
+ const previous = { version: current.version, commit: current.commit, baselineCommit: current.baselineCommit, warmShadow: current.warmShadow };
536
+ if (!validWarmVerdictSample(previous)) throw new Error('gate verdict current warm sample is invalid');
537
+ } else if (current.warmHistoryOnly !== true) {
538
+ const previous = { version: current.version, commit: current.commit, baselineCommit: current.baselineCommit, warmShadow: current.warmShadow };
539
+ if (!validWarmVerdictSample(previous)) throw new Error('gate verdict current warm sample is invalid');
540
+ history.push(previous);
541
+ }
542
+ history.push(sample);
543
+ const body = JSON.stringify({ ...current, warmHistory: history.slice(-WARM_VERDICT_HISTORY_LIMIT), warmHistoryOnly: true }, null, 2) + '\n';
544
+ mkdirSync(dirname(path), { recursive: true });
545
+ const temp = join(dirname(path), `.gate-verdict-${process.pid}-${randomUUID()}.tmp`);
546
+ try { writeFileSync(temp, body, { mode: 0o600, flag: 'wx' }); chmodSync(temp, 0o600); renameSync(temp, path); }
547
+ finally { if (existsSync(temp)) rmSync(temp); }
548
+ }
549
+
550
+ /** Run a bounded set of real fresh-Job ↔ warm-worker comparisons before the release gate. */
551
+ export async function runWarmPrecutCanary(input: { opts: GateOptions; runner: GateRunner; file: string;
552
+ config: unknown; historyPath: string; timeoutMs?: number }): Promise<{ samples: WarmShadowObservation[]; decision: ReturnType<typeof decideWarmReuse> }> {
553
+ const { opts, runner } = input;
554
+ if (!opts.pod || !runner.recheckPod || !opts.baselineCommit || !opts.pod.warmReplay || !opts.pod.warmFinalize || !opts.pod.warmQuarantine)
555
+ throw new Error('pre-cut canary requires a Pod pool, baseline commit, and warm worker hooks');
556
+ if (!/^(?:[\w.-]+\/)*[\w.-]+\.test\.[cm]?[jt]sx?$/.test(input.file)
557
+ || input.file.split('/').some((part) => part === '.' || part === '..')) throw new Error('pre-cut canary requires one repository test file');
558
+ if (readWarmVerdictHistory(input.historyPath) === undefined)
559
+ throw new Error('gate verdict contains malformed warm history or current sample; refusing canary without writing');
560
+ const samples = 2;
561
+ if (samples > WARM_REPLAY_BUDGET) throw new Error(`pre-cut canary sample count exceeds replay budget (${WARM_REPLAY_BUDGET})`);
562
+ const lock = await runner.localCommand('git', ['show', `${opts.commit}:bun.lock`], opts.repo ?? process.cwd());
563
+ if (lock.rc !== 0 || !lock.output) throw new Error('pre-cut canary cut lock is unmeasured');
564
+ if (!/^[0-9a-f]{40}$/i.test(opts.baselineCommit)) throw new Error('pre-cut canary baseline commit is invalid');
565
+ const baseline = await runner.localCommand('git', ['cat-file', '-e', `${opts.baselineCommit}^{commit}`], opts.repo ?? process.cwd());
566
+ if (baseline.rc !== 0) throw new Error(`pre-cut canary baseline commit is absent: ${opts.baselineCommit.slice(0, 12)}`);
567
+ const trackedFile = await runner.localCommand('git', ['cat-file', '-e', `${opts.commit}:${input.file}`], opts.repo ?? process.cwd());
568
+ if (trackedFile.rc !== 0) throw new Error(`pre-cut canary test file is absent from cut ${opts.commit.slice(0, 12)}`);
569
+ const lockHash = createHash('sha256').update(lock.output).digest('hex');
570
+ const pod = { ...opts.pod, warmProducerEnabled: false, warmCutSha: opts.commit, warmBaselineSha: opts.baselineCommit,
571
+ warmEligible: true, warmReplayOwner: true, warmShadowScope: { requested: false, attempts: 0 } };
572
+ const observations: WarmShadowObservation[] = [];
573
+ const attemptIds = new Set<string>();
574
+ for (let index = 0; index < samples; index++) {
575
+ let observation: WarmShadowObservation;
576
+ let failure: Error | undefined;
577
+ try {
578
+ const result = await runner.recheckPod(input.file, opts.commit, pod, input.timeoutMs ?? GATE_ISOLATED_TIMEOUT_MS);
579
+ observation = result.warmShadow ?? { files: [input.file], attemptId: `precut-unmeasured-${index + 1}-${randomUUID()}`,
580
+ status: 'unmeasured', reason: result.rc === 0 ? 'warm-shadow-missing' : `fresh-job-rc-${result.rc}`, quarantined: 'no' };
581
+ if (result.rc !== 0) { observation = { ...observation, status: 'unmeasured', reason: `fresh-job-rc-${result.rc}` };
582
+ failure = new Error(`pre-cut fresh Job failed on attempt ${index + 1}: rc=${result.rc}`); }
583
+ else if (observation.status !== 'observed' || !observation.workerKey
584
+ || observation.files.length !== 1 || observation.files[0] !== input.file
585
+ || observation.cutSha !== opts.commit || observation.baselineSha !== opts.baselineCommit
586
+ || observation.workerKey.lockHash !== lockHash || !['match', 'result'].includes(observation.reason)
587
+ || attemptIds.has(observation.attemptId)
588
+ || (observation.reason === 'match' && (observation.finalized !== true || observation.quarantined !== 'no'))) {
589
+ failure = new Error(`pre-cut warm comparison was not measured on attempt ${index + 1}: ${observation.reason}`);
590
+ }
591
+ } catch (error) {
592
+ observation = { files: [input.file], attemptId: `precut-error-${index + 1}-${randomUUID()}`,
593
+ status: 'unmeasured', reason: String(error), quarantined: 'unavailable' };
594
+ failure = error instanceof Error ? error : new Error(String(error));
595
+ }
596
+ attemptIds.add(observation.attemptId);
597
+ observations.push(observation);
598
+ recordWarmPrecutCanarySample(input.historyPath, { version: opts.version, commit: opts.commit,
599
+ baselineCommit: opts.baselineCommit, warmShadow: [observation] });
600
+ if (failure) throw failure;
601
+ }
602
+ const history = readWarmVerdictHistory(input.historyPath);
603
+ const evidence = warmEvidenceFromVerdicts(history, opts.version, opts.commit, opts.baselineCommit, lockHash);
604
+ const warmReuseConfig = (input.config && typeof input.config === 'object' && 'pod' in input.config
605
+ && recordValue((input.config as Record<string, unknown>).pod)?.warmReuse) as WarmReuseSwitchConfig | undefined;
606
+ return { samples: observations, decision: decideWarmReuse('gate-recheck', warmReuseConfig,
607
+ evidence ? { 'gate-recheck': evidence } : undefined) };
608
+ }
426
609
  /** Every counted match has a measured, finalized worker with this exact cut, baseline and lock. */
427
610
  export function warmEvidenceFromVerdicts(history: readonly WarmVerdictSample[] | undefined,
428
611
  version: string, head: string, baseline: string, lock: string): WarmReuseEvidence | undefined {
429
612
  if (!history?.length || !lock) return undefined;
430
- const rows = history.flatMap((verdict) => {
431
- if (verdict.version !== version || verdict.commit !== head || verdict.baselineCommit !== baseline
432
- || !Array.isArray(verdict.warmShadow)) return [null];
433
- return verdict.warmShadow.length ? verdict.warmShadow : [null];
434
- });
613
+ const matchingHistory = history.filter((verdict) => verdict.version === version && verdict.commit === head
614
+ && verdict.baselineCommit === baseline);
615
+ const rows = matchingHistory.flatMap((verdict) => verdict.warmShadow.length ? verdict.warmShadow : [null]);
616
+ if (!rows.length) return undefined;
435
617
  const attempts = new Set<string>();
436
618
  const key = rows.at(-1)?.workerKey;
437
619
  if (rows.some((entry) => {
@@ -1575,7 +1757,45 @@ export interface GateAdmission {
1575
1757
  acquire(signal: AbortSignal): Promise<() => void>;
1576
1758
  /** The pool-wide reading admission waits on (recommended · limitedBy · memorySlots); null before any reading. */
1577
1759
  waitReason(): string | null;
1760
+ /** Optional deterministic-test observation override; production uses poolGateAdmission's unknown-only provider. */
1761
+ priorityShadowObservation?(gateWaiting: boolean): GatePriorityShadowObservation;
1762
+ }
1763
+
1764
+ export type GatePriorityImageState = 'verified' | 'mismatch' | 'unknown';
1765
+ export interface GatePriorityShadowObservation {
1766
+ gateRunId: string | null;
1767
+ host: string | null;
1768
+ competingRunId: string | null;
1769
+ competingHost: string | null;
1770
+ gateImageState: GatePriorityImageState;
1771
+ competingImageState: GatePriorityImageState;
1772
+ gateWaiting: boolean;
1773
+ lowerPriorityWaiting: boolean | null;
1774
+ }
1775
+ export interface GatePriorityShadowDecision extends GatePriorityShadowObservation {
1776
+ event: 'gate-priority-shadow-decision';
1777
+ decision: 'gate-first' | 'no-contention' | 'insufficient-evidence';
1778
+ selectionRationale: string;
1578
1779
  }
1780
+
1781
+ /** A shadow-only policy decision; it has no pool or Pod handle and cannot change runtime state. */
1782
+ export function gatePriorityShadowDecision(input: GatePriorityShadowObservation): GatePriorityShadowDecision {
1783
+ const base = { event: 'gate-priority-shadow-decision' as const, ...input };
1784
+ if (!input.gateWaiting || input.lowerPriorityWaiting === false) {
1785
+ return { ...base, decision: 'no-contention', selectionRationale: !input.gateWaiting ? 'gate-not-waiting' : 'no-lower-priority-waiter-observed' };
1786
+ }
1787
+ if (input.lowerPriorityWaiting !== true) {
1788
+ return { ...base, decision: 'insufficient-evidence', selectionRationale: 'lower-priority-wait-state-unknown' };
1789
+ }
1790
+ if (!input.gateRunId || !input.competingRunId || !input.host || input.host !== input.competingHost) {
1791
+ return { ...base, decision: 'insufficient-evidence', selectionRationale: 'same-host-run-identities-unavailable' };
1792
+ }
1793
+ if (input.gateImageState !== 'verified' || input.competingImageState !== 'verified') {
1794
+ return { ...base, decision: 'insufficient-evidence', selectionRationale: 'image-state-not-verified' };
1795
+ }
1796
+ return { ...base, decision: 'gate-first', selectionRationale: 'same-host-contention-and-verified-gate-image; gate-policy-priority' };
1797
+ }
1798
+
1579
1799
  export const GATE_ADMISSION_WAIT_SECONDS_DEFAULT = 1800;
1580
1800
  /** Queue pressure scales the default wait but never creates an unbounded admission timer. */
1581
1801
  export function gateAdmissionWaitSeconds(shards: number, slots: number): number {
@@ -1625,17 +1845,38 @@ export function defaultGateMemberProbe(member: PodPoolMember, tools: GateTools =
1625
1845
  return !member.sshHost || (tools.remoteRun ?? defaultRemoteRun)(member.sshHost, 'true').status === 0;
1626
1846
  }
1627
1847
 
1628
- export function poolGateAdmission(pool: PodPoolScheduler): GateAdmission {
1629
- return { acquire: (signal) => pool.acquireAdmission(signal, undefined, { gate: true }), waitReason: () => pool.waitReason() };
1848
+ export function poolGateAdmission(pool: PodPoolScheduler, gateRunId: string | null = null,
1849
+ priorityShadowObservation?: (gateWaiting: boolean) => GatePriorityShadowObservation): GateAdmission {
1850
+ const singleHost = pool.members.length === 1 ? (pool.members[0]!.sshHost ?? hostname()) : null;
1851
+ // The optional provider is a deterministic test seam only. Production never fabricates a competitor or image fact.
1852
+ return {
1853
+ acquire: (signal) => pool.acquireAdmission(signal, undefined, { gate: true }),
1854
+ waitReason: () => pool.waitReason(),
1855
+ // At admission time a multi-host pool has not selected a host yet. Existing measurements do not expose
1856
+ // competing run ids or image states, so keep those unknown rather than deriving them from Job names.
1857
+ priorityShadowObservation: priorityShadowObservation ?? ((gateWaiting) => ({ gateRunId, host: singleHost, competingRunId: null, competingHost: null,
1858
+ gateImageState: 'unknown', competingImageState: 'unknown', gateWaiting, lowerPriorityWaiting: null })),
1859
+ };
1630
1860
  }
1631
- export async function admitGateShard(admission: GateAdmission | undefined, o: { waitMs: number; onWait: (reason: string) => void; reasonPollMs?: number; firstReasonMs?: number }): Promise<GateAdmissionResult> {
1861
+ export async function admitGateShard(admission: GateAdmission | undefined, o: { waitMs: number; onWait: (reason: string) => void; reasonPollMs?: number; firstReasonMs?: number;
1862
+ onPriorityShadow?: (decision: GatePriorityShadowDecision) => void }): Promise<GateAdmissionResult> {
1632
1863
  const noop = () => {};
1633
1864
  if (!admission || !(o.waitMs > 0)) return { outcome: 'off', waitedMs: 0, release: noop };
1634
1865
  const started = Date.now();
1635
1866
  const controller = new AbortController();
1636
1867
  let timedOut = false;
1637
1868
  let lastReason: string | undefined;
1638
- const tell = () => { lastReason = gateAdmissionWaitText(admission.waitReason()); o.onWait(lastReason); };
1869
+ let priorityShadowLogged = false;
1870
+ const tell = () => {
1871
+ lastReason = gateAdmissionWaitText(admission.waitReason());
1872
+ if (!priorityShadowLogged && admission.priorityShadowObservation) {
1873
+ priorityShadowLogged = true;
1874
+ const decision = gatePriorityShadowDecision(admission.priorityShadowObservation(true));
1875
+ if (o.onPriorityShadow) o.onPriorityShadow(decision);
1876
+ else debug.log('release-loop.gate', 'gate-priority-shadow-decision', decision);
1877
+ }
1878
+ o.onWait(lastReason);
1879
+ };
1639
1880
  const deadline = setTimeout(() => { timedOut = true; controller.abort(); }, o.waitMs);
1640
1881
  // A grant inside the first moment never touches the shard row (no flapping in shards.json).
1641
1882
  let ticker: ReturnType<typeof setInterval> | undefined;
@@ -1678,6 +1919,27 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
1678
1919
  const ssh = tools.ssh ?? defaultSshCommand;
1679
1920
  const remoteRun = tools.remoteRun ?? defaultRemoteRun;
1680
1921
  const runPod = tools.podCommand ?? podCommand;
1922
+ const pinnedGateImageSync = (pod: NonNullable<GateOptions['pod']>, commit: string): RunPodCommandOptions['syncImages'] | undefined => {
1923
+ const hasDigest = Boolean(pod.pinnedImageDigest?.trim());
1924
+ const hasRunId = Boolean(pod.pinnedRunId?.trim());
1925
+ if (hasDigest !== hasRunId) throw new Error('gate pinned image: both pinnedImageDigest and pinnedRunId are required');
1926
+ if (!hasDigest) return undefined;
1927
+ return async (members, image, localCommit) => {
1928
+ if (localCommit !== commit) throw new Error('gate pinned image: requested source commit changed');
1929
+ const probe = preflightGateImageDigest;
1930
+ const poolSpecFor = (member: PodPoolMember) => `${member.context}${member.sshHost ? `@${member.sshHost}` : ''}:${member.capacity}#${member.registry ?? NODE_REGISTRY}`;
1931
+ const result = buildPinnedGateImageSync({ runId: pod.pinnedRunId!, commit, digest: pod.pinnedImageDigest!, image,
1932
+ members: members.map((member) => ({ context: member.context, registry: member.registry ?? NODE_REGISTRY })),
1933
+ probeTagDigest: (member) => {
1934
+ const candidate = members.find((row) => row.context === member.context && (row.registry ?? NODE_REGISTRY) === member.registry);
1935
+ return candidate ? probe(poolSpecFor(candidate), commit) : null;
1936
+ } });
1937
+ for (const drift of result.tagDrifts) debug.log('release-loop.gate', 'image-tag-drift', {
1938
+ ...drift, action: 'skip-push-use-pinned-digest',
1939
+ }, { level: 'warn' });
1940
+ return result.images;
1941
+ };
1942
+ };
1681
1943
  // GATE-HOSTMIRROR-EXPLICIT: resolved once per runner and handed to every podCommand (shard · retry · recheck).
1682
1944
  let gateMirror: GateHostMirror | undefined;
1683
1945
  const gateHostMirror = (pod: NonNullable<GateOptions['pod']>): { hostMirror?: string } => {
@@ -1720,6 +1982,9 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
1720
1982
  if (!pod.pool || !Number.isSafeInteger(shardCount) || shardCount < 1
1721
1983
  || !Number.isSafeInteger(deadlineSeconds) || deadlineSeconds < 1
1722
1984
  || !Number.isSafeInteger(isolationConcurrency) || isolationConcurrency < 1) throw new Error('invalid pod sweep options');
1985
+ const hasPinnedDigest = Boolean(pod.pinnedImageDigest?.trim());
1986
+ const hasPinnedRunId = Boolean(pod.pinnedRunId?.trim());
1987
+ if (hasPinnedDigest !== hasPinnedRunId) throw new Error('gate pinned image: both pinnedImageDigest and pinnedRunId are required');
1723
1988
  const head = await command('git', ['rev-parse', 'HEAD'], tree);
1724
1989
  check(head, 'cut tree HEAD');
1725
1990
  const commit = head.output.trim();
@@ -1848,7 +2113,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
1848
2113
  const admissionWaitSeconds = pod.admissionWaitSeconds ?? gateAdmissionWaitSeconds(shards.length, rootSlots);
1849
2114
  if (!Number.isSafeInteger(admissionWaitSeconds) || admissionWaitSeconds < 0) throw new Error('invalid pod sweep options');
1850
2115
  // An injected pool (tests) runs without admission unless `pod.admission` is passed; the gate's own pool asks it.
1851
- const gateAdmission = pod.admission ?? tools.admission ?? (poolOverride ? undefined : poolGateAdmission(poolScheduler));
2116
+ const gateAdmission = pod.admission ?? tools.admission ?? (poolOverride ? undefined : poolGateAdmission(poolScheduler, tools.graphRunId ?? null));
1852
2117
  debug.log('release-loop.gate', 'pod-admission', { waitSeconds: admissionWaitSeconds, source: pod.admissionWaitSeconds !== undefined ? 'config' : 'default', enabled: !!gateAdmission && admissionWaitSeconds > 0,
1853
2118
  rootSlots, shards: shards.length });
1854
2119
  // GATE-POD-STALL-FAILFAST: a shard with no test output within the stall window is stopped and recorded unmeasured.
@@ -2227,7 +2492,9 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
2227
2492
  pool: pod.pool, poolScheduler: trackedPool(shard, branch, jobName, resultFile, attemptId, (member) => {
2228
2493
  stamp('poolPlaced'); jobContext = member.context; jobContextForCapture = member.context; placedOn = member.context; placed();
2229
2494
  if (branch === '-tail') tailPlacedHost = member.context;
2230
- }, skipMembers, depth === 0 && branch === '' && hostRetries === 0 && !retriedNoOutput && !batchIsolation && skipMembers.size === 0), clone: true, source: { kind: 'commit', sha: commit }, deadlineSeconds: shardDeadline,
2495
+ }, skipMembers, depth === 0 && branch === '' && hostRetries === 0 && !retriedNoOutput && !batchIsolation && skipMembers.size === 0), clone: true, source: { kind: 'commit', sha: commit },
2496
+ ...(pod.imagePin === 'cut-commit' || pod.pinnedImageDigest || pod.pinnedRunId ? { imageCommit: commit } : {}),
2497
+ ...(pod.pinnedImageDigest || pod.pinnedRunId ? { syncImages: pinnedGateImageSync(pod, commit)! } : {}), deadlineSeconds: shardDeadline,
2231
2498
  ...gateHostMirror(pod),
2232
2499
  ...(multiHost ? { statusUnreadableMs: memberUnreadableSeconds * 1000 } : {}),
2233
2500
  ...(bunCache ? { bunCache } : {}),
@@ -2854,7 +3121,9 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
2854
3121
  let producer: RunPodCommandOptions['warmProducer'];
2855
3122
  if (leased && replay?.transport.status === 'observed' && replay.transport.reason === 'complete') {
2856
3123
  if (warmCommandMatchesRequest(leased, warmRequest!) && leased.podName && leased.podUid
2857
- && leased.podContext && leased.image?.endsWith(`@${leased.key.imageDigest}`)) {
3124
+ && leased.podContext && leased.image?.endsWith(`@${leased.key.imageDigest}`)
3125
+ && (!pod.pinnedImageDigest || (normalizeGateImageDigest(pod.pinnedImageDigest) !== null
3126
+ && normalizeGateImageDigest(leased.key.imageDigest) === normalizeGateImageDigest(pod.pinnedImageDigest)))) {
2858
3127
  producer = { pod: leased.podName, podUid: leased.podUid, context: leased.podContext, image: leased.image,
2859
3128
  commit, files: ['package.json', 'bun.lock'] };
2860
3129
  } else await quarantine('gate-producer-identity');
@@ -2868,7 +3137,9 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
2868
3137
  run: process.env.ELANOUS_RUN_ID || attemptId, phase: 'release-gate', requestedAt: new Date().toISOString(),
2869
3138
  graphNode: 'gate-triage', commit, reason: `release gate recheck ${files.length} files`, pool: pod.pool,
2870
3139
  memReq: POD_ISOLATION_MEMORY_LIMIT, warmReused: !!producer },
2871
- source: { kind: 'commit', sha: commit }, deadlineSeconds, ...(producer ? { warmProducer: producer, image: producer.image, imageCommit: commit } : {}),
3140
+ source: { kind: 'commit', sha: commit }, deadlineSeconds, ...(producer ? { warmProducer: producer, image: producer.image } : {}),
3141
+ ...(producer || pod.pinnedImageDigest || pod.pinnedRunId ? { imageCommit: commit } : {}),
3142
+ ...(pod.pinnedImageDigest || pod.pinnedRunId ? { syncImages: pinnedGateImageSync(pod, commit)! } : {}),
2872
3143
  ...(bunCache ? { bunCache } : {}), ...gateHostMirror(pod),
2873
3144
  ...(installSlots > 0 ? { installSlots: POD_INSTALL_SLOTS_HOST_PATH } : {}), ...(pod.cpu ? { cpu: pod.cpu } : {}),
2874
3145
  memoryLimit: POD_ISOLATION_MEMORY_LIMIT, name: `gate-recheck-${randomUUID()}`, command: ['bash', '-lc', shell] });
@@ -3221,9 +3492,10 @@ export function saveBaselineFileCache(ledger: string, commit: string, rows: Base
3221
3492
  } finally { if (existsSync(temp)) rmSync(temp); }
3222
3493
  }
3223
3494
 
3224
- export async function judgeGate(opts: GateOptions, runner: GateRunner = createGateRunner(opts.repo ?? process.cwd(), opts.remote),
3495
+ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGateRunner(opts.repo ?? process.cwd(), opts.remote, undefined, undefined, undefined, undefined, { graphRunId: opts.graphRunId ?? null }),
3225
3496
  imageProbe: typeof preflightGateImageDigest = preflightGateImageDigest,
3226
- identityProbe: typeof preflightGateExecutionIdentity = preflightGateExecutionIdentity): Promise<GateResult> {
3497
+ identityProbe: typeof preflightGateExecutionIdentity = preflightGateExecutionIdentity,
3498
+ syncImages: typeof syncPoolImages = syncPoolImages): Promise<GateResult> {
3227
3499
  if (opts.pod?.warmReplay) opts = { ...opts, pod: { ...opts.pod,
3228
3500
  warmShadowScope: { requested: false, attempts: 0 } } };
3229
3501
  const explicitConfig = getElanousConfigDirOverride();
@@ -3266,11 +3538,11 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
3266
3538
  let cutSweepRc: number | null = null;
3267
3539
  let radiusShadow: GateRadiusShadowPlan | undefined;
3268
3540
  const trees: string[] = [];
3541
+ const baselineVersion = opts.baselineVersion;
3269
3542
  try {
3270
3543
  if (!sha.test(opts.commit) || !versionPattern.test(opts.version)
3271
3544
  || (opts.baselineVersion && !versionPattern.test(opts.baselineVersion))
3272
3545
  || (opts.baselineCommit && !sha.test(opts.baselineCommit))) throw new Error('invalid commit or version');
3273
- const baselineVersion = opts.baselineVersion;
3274
3546
  if (!baselineVersion) throw new Error('previous release version required (--baseline-version)');
3275
3547
  if (opts.rehearsalBaseline && (opts.graphRunId || !opts.pod || !/-rc\.\d+$/.test(opts.version)))
3276
3548
  throw new Error('rehearsal baseline requires a direct Pod gate with an isolated rc version');
@@ -3302,11 +3574,22 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
3302
3574
  const pinPath = gateImagePinPath(root, opts.version, pinRunId);
3303
3575
  const gateImageTag = `${podCommandImageFor([], true).split(':')[0]}:${opts.commit.slice(0, 12)}`;
3304
3576
  let savedPin: ReturnType<typeof readGateImagePin>;
3577
+ let supersededPin: ReturnType<typeof readGateImagePinForRun> = null;
3578
+ let stalePinNeedsSuperseding = false;
3305
3579
  try { savedPin = existsSync(pinPath) ? readGateImagePin(pinPath, pinRunId, gateImageTag, opts.commit) : null; }
3306
3580
  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');
3581
+ let prior: ReturnType<typeof readGateImagePinForRun> = null;
3582
+ try { prior = readGateImagePinForRun(pinPath, pinRunId, gateImageTag); } catch { /* Keep invalid or corrupt pins fail-closed. */ }
3583
+ if (!prior || prior.commit === opts.commit.toLowerCase()) {
3584
+ debug.log('release-loop.gate', 'image-pin', { runId: pinRunId, currentImages, decision: 'fail-closed',
3585
+ reason: 'saved-image-pin-invalid', error: String(error).slice(0, 300) }, { level: 'error' });
3586
+ throw new Error('gate resume refused: saved image digest pin is invalid');
3587
+ }
3588
+ supersededPin = prior;
3589
+ stalePinNeedsSuperseding = true;
3590
+ savedPin = null;
3591
+ debug.log('release-loop.gate', 'image-pin', { runId: pinRunId, currentImages, decision: 'stale-tip-detected',
3592
+ priorCommit: supersededPin.commit, nextCommit: opts.commit.toLowerCase() });
3310
3593
  }
3311
3594
  const savedDigest = savedPin?.digest ?? null;
3312
3595
  let previouslyExecuted = false;
@@ -3321,11 +3604,34 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
3321
3604
  } catch { previouslyExecuted = true; }
3322
3605
  }
3323
3606
  }
3324
- if (!savedDigest && previouslyExecuted) {
3607
+ if (!savedDigest && previouslyExecuted && !supersededPin) {
3325
3608
  debug.log('release-loop.gate', 'image-pin', { runId: opts.graphRunId, savedDigest: null, currentImages,
3326
3609
  decision: 'fail-closed', reason: 'resume-pin-missing' }, { level: 'error' });
3327
3610
  throw new Error('gate resume refused: saved image digest pin is missing');
3328
3611
  }
3612
+ const hasPoolImage = [...poolImages.values()].some((images) => images.some((image) => normalizeGateImageDigest(image) !== null));
3613
+ if (!savedDigest && !hasPoolImage) {
3614
+ let syncResults: Awaited<ReturnType<typeof syncPoolImages>>;
3615
+ try { syncResults = await syncImages(members, podCommandImageFor([], true), opts.commit); }
3616
+ catch (error) {
3617
+ debug.log('release-loop.gate', 'image-pin', { runId: pinRunId, decision: 'fail-closed',
3618
+ reason: 'initial-image-sync-failed', error: String(error).slice(0, 300) }, { level: 'error' });
3619
+ throw new Error('gate image pin refused: initial image sync failed');
3620
+ }
3621
+ const failedSyncs = memberSpecs.filter((member) => !syncResults.get(member.context)?.ok);
3622
+ if (failedSyncs.length) {
3623
+ debug.log('release-loop.gate', 'image-pin', { runId: pinRunId, decision: 'fail-closed', reason: 'initial-image-sync-failed',
3624
+ failed: failedSyncs.map((member) => ({ context: member.context, result: syncResults.get(member.context) ?? null })) }, { level: 'error' });
3625
+ throw new Error('gate image pin refused: initial image sync failed');
3626
+ }
3627
+ for (const member of memberSpecs) {
3628
+ const digest = imageProbe(member.spec, opts.commit);
3629
+ currentImages[member.context] = digest;
3630
+ poolImages.set(member.context, digest ? [digest] : []);
3631
+ }
3632
+ debug.log('release-loop.gate', 'image-pin', { runId: pinRunId, decision: 'initial-image-sync',
3633
+ outcomes: [...syncResults].map(([context, result]) => ({ context, ok: result.ok, action: result.action })), currentImages });
3634
+ }
3329
3635
  const resolution = resolveGateImagePin({ ...(savedDigest ? { savedDigest } : {}),
3330
3636
  ...(!savedDigest ? { initialDigest: [...poolImages.values()].flat().find(Boolean) ?? null } : {}), poolImages });
3331
3637
  if (!resolution.ok) {
@@ -3336,7 +3642,11 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
3336
3642
  const selected = memberSpecs.filter((member) => resolution.eligiblePools.includes(member.context));
3337
3643
  if (!selected.length) throw new Error('gate image pin refused: no eligible pool');
3338
3644
  if (!savedDigest) {
3339
- try { writeGateImagePin(pinPath, pinRunId, resolution.digest, gateImageTag, opts.commit); }
3645
+ try {
3646
+ if (stalePinNeedsSuperseding) {
3647
+ replaceGateImagePin(pinPath, pinRunId, resolution.digest, gateImageTag, opts.commit, gateImageTag, supersededPin!.commit);
3648
+ } else writeGateImagePin(pinPath, pinRunId, resolution.digest, gateImageTag, opts.commit);
3649
+ }
3340
3650
  catch (error) {
3341
3651
  debug.log('release-loop.gate', 'image-pin', { runId: pinRunId, pinnedDigest: resolution.digest,
3342
3652
  currentImages, decision: 'fail-closed', reason: 'pin-write-failed', error: String(error).slice(0, 300) }, { level: 'error' });
@@ -3506,7 +3816,7 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
3506
3816
  try {
3507
3817
  const sweepResult = await runner.sweep(cutTree,
3508
3818
  join(root, 'release', opts.version, 'gate-logs', selected ? 'cut-partial' : 'cut'),
3509
- opts.pod ? { ...opts.pod, durationSource, ...(knownFailures.length ? { knownFailures } : {}), onSelectionTimed: ({ ms }) => { result.selectionMs = ms; },
3819
+ opts.pod ? { ...opts.pod, ...(opts.imagePin ? { imagePin: opts.imagePin } : {}), durationSource, ...(knownFailures.length ? { knownFailures } : {}), onSelectionTimed: ({ ms }) => { result.selectionMs = ms; },
3510
3820
  // GATE-RISK-LANE — the watch list is read from this gate's own ledgers (the instance root ⊕ the machine ledger);
3511
3821
  // the cut sweep puts those files first, the baseline sweep keeps its plan.
3512
3822
  riskWatch: { instanceRoots: roots, ledgerRoots: roots },
@@ -3608,7 +3918,7 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
3608
3918
  check(await runner.command('bun', ['install'], dir), `bun install ${dir}`);
3609
3919
  const rechecked = await runner.sweep(recheckTree,
3610
3920
  join(root, 'release', opts.version, 'gate-logs', 'baseline-attribution-recheck'),
3611
- opts.pod ? { ...opts.pod, durationSource } : undefined,
3921
+ opts.pod ? { ...opts.pod, ...(opts.imagePin ? { imagePin: opts.imagePin } : {}), durationSource } : undefined,
3612
3922
  targetFiles);
3613
3923
  if (rechecked.timedOut || rechecked.abortedShards || rechecked.stalledEnv?.length) {
3614
3924
  attribution.baselineResult = 'timeout';
@@ -3723,7 +4033,7 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
3723
4033
  // 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
3724
4034
  // after a 23-minute Pod cut).
3725
4035
  const baseRun = await runner.sweep(baseTree!, join(root, 'release', opts.version, 'gate-logs', 'baseline'),
3726
- opts.pod ? { ...opts.pod, durationSource } : undefined,
4036
+ opts.pod ? { ...opts.pod, ...(opts.imagePin ? { imagePin: opts.imagePin } : {}), durationSource } : undefined,
3727
4037
  present);
3728
4038
  baseline = await localStalled(baseRun, baseTree!, baseSha, 'baseline');
3729
4039
  } else baseline = { failures: [], errors: [] };
@@ -4174,6 +4484,14 @@ export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOption
4174
4484
  const fromGraph: Record<string, unknown> = context?.input ?? {};
4175
4485
  const graphCommit = context?.outputs['version-release']?.commit;
4176
4486
  if (typeof graphCommit === 'string') fromGraph.commit = graphCommit;
4487
+ if (fromGraph.gateImagePin !== undefined && fromGraph.gateImagePin !== 'cut-commit') {
4488
+ throw new Error(`unsupported gate image pin: ${String(fromGraph.gateImagePin)}`);
4489
+ }
4490
+ // Policy-selected Pod gates must pin their container to the resolved cut commit. Host sweeps do
4491
+ // not consume a container image pin, and the legacy no-executor path retains its old behavior.
4492
+ if (fromGraph.gateExecutor === 'pod' && fromGraph.gateImagePin !== 'cut-commit') {
4493
+ throw new Error('gateImagePin cut-commit is required for a policy-selected release gate');
4494
+ }
4177
4495
  const baselineCommit = context?.outputs.cutoff?.baseline as { sha?: unknown } | undefined;
4178
4496
  if (typeof baselineCommit?.sha === 'string') fromGraph.previousCommit = baselineCommit.sha;
4179
4497
  if (fromGraph.gateExecutor !== undefined && !isGateExecutor(fromGraph.gateExecutor)) throw new Error(`gateExecutor must be pod or host: ${String(fromGraph.gateExecutor)}`);
@@ -4186,6 +4504,7 @@ export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOption
4186
4504
  || !['darwin', 'linux'].includes((value as { os?: string }).os ?? ''))))
4187
4505
  throw new Error('gateShardCapabilities must map pod-N to {os: darwin|linux}');
4188
4506
  const opts: GateOptions = {
4507
+ ...(fromGraph.gateImagePin === 'cut-commit' ? { imagePin: 'cut-commit' as const } : {}),
4189
4508
  rehearsalTui: fromGraph.rehearsalTui === true,
4190
4509
  recheckFileLimit: fromGraph.gateRecheckFileLimit !== undefined ? Number(fromGraph.gateRecheckFileLimit) : undefined,
4191
4510
  commit: typeof fromGraph.commit === 'string' ? fromGraph.commit : '',
@@ -4214,12 +4533,19 @@ export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOption
4214
4533
  };
4215
4534
  if (isGateExecutor(fromGraph.gateExecutor)) opts.executor = fromGraph.gateExecutor;
4216
4535
  let freshShards = false;
4536
+ let warmPrecutCanaryFile: string | undefined;
4217
4537
  let rerouteShard: string | undefined;
4218
4538
  let shardHost: string | undefined;
4219
4539
  for (let i = 0; i < args.length; i++) {
4220
4540
  const arg = args[i];
4221
4541
  if (arg === '--help') return 'help';
4222
4542
  if (arg === '--json') continue;
4543
+ if (arg === '--precut-canary-file') {
4544
+ const value = args[++i];
4545
+ if (!value || value.startsWith('--')) throw new Error('--precut-canary-file requires one test file');
4546
+ warmPrecutCanaryFile = value;
4547
+ continue;
4548
+ }
4223
4549
  if (arg === '--full') { opts.full = true; continue; }
4224
4550
  if (arg === '--rehearsal-baseline') { opts.rehearsalBaseline = true; continue; }
4225
4551
  // 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).
@@ -4264,6 +4590,7 @@ export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOption
4264
4590
  else if (key === 'remoteMirror') opts.remoteMirror = value;
4265
4591
  }
4266
4592
  if (freshShards && opts.pod) opts.pod = { ...opts.pod, freshShards: true };
4593
+ if (warmPrecutCanaryFile) opts.warmPrecutCanary = { file: warmPrecutCanaryFile };
4267
4594
  if ((rerouteShard || shardHost) && !opts.pod?.pool) throw new Error('shard reroute requires a Pod pool');
4268
4595
  if (shardHost && !rerouteShard) throw new Error('--shard-host requires --reroute-shard');
4269
4596
  if (opts.pod && rerouteShard) opts.pod = { ...opts.pod, rerouteShard, ...(shardHost ? { shardHost } : {}) };
@@ -4286,11 +4613,12 @@ export async function runGateNode(argv: string[], env: NodeJS.ProcessEnv, runner
4286
4613
  judge: (opts: GateOptions, runner?: GateRunner, imageProbe?: typeof preflightGateImageDigest) => Promise<GateResult> = judgeGate,
4287
4614
  imageProbe: typeof preflightGateImageDigest = preflightGateImageDigest): Promise<number> {
4288
4615
  const start = Date.now();
4289
- let result: GateResult;
4616
+ let result: GateResult | undefined;
4290
4617
  let version = '';
4291
4618
  let graphRunId: string | null = null;
4292
4619
  let autohealStoreRoot = '';
4293
4620
  let placement: { executor: string | null; host: string | null } | undefined;
4621
+ let warmControlCleanup: (() => Promise<void>) | undefined;
4294
4622
  try {
4295
4623
  const parsed = parseOptions(argv, env);
4296
4624
  let config: unknown = null;
@@ -4298,13 +4626,28 @@ export async function runGateNode(argv: string[], env: NodeJS.ProcessEnv, runner
4298
4626
  try { config = getUserConfig(); }
4299
4627
  catch (error) { debug.log('release-loop.gate', 'warm-profile-unavailable', { error: String(error) }); }
4300
4628
  }
4301
- const opts = parsed === 'help' ? parsed : withWarmGateHooks(parsed, config, !!runner);
4629
+ const opts = parsed === 'help' ? parsed : withWarmGateHooks(parsed, config, !!runner,
4630
+ cleanup => { warmControlCleanup = cleanup; });
4631
+ if (opts !== 'help' && opts.warmPrecutCanary) {
4632
+ const canaryRunner = runner ?? createGateRunner(opts.repo ?? process.cwd(), opts.remote, undefined, undefined, undefined, undefined, { graphRunId: opts.graphRunId ?? null });
4633
+ const warmPrecutTestSeam = (runner as (GateRunner & { warmPrecutCanaryTestSeam?: {
4634
+ config: unknown; hooks: Pick<NonNullable<GateOptions['pod']>, 'warmReplay' | 'warmFinalize' | 'warmQuarantine' | 'warmReuseConfig'>;
4635
+ } }) | undefined)?.warmPrecutCanaryTestSeam;
4636
+ const canaryOpts = warmPrecutTestSeam && opts.pod ? { ...opts, pod: { ...opts.pod, ...warmPrecutTestSeam.hooks } } : opts;
4637
+ const canary = await runWarmPrecutCanary({ opts: canaryOpts, runner: canaryRunner, file: opts.warmPrecutCanary.file,
4638
+ config: warmPrecutTestSeam?.config ?? config,
4639
+ historyPath: verdictFile(opts.ledgerRoot ?? releaseLedgerRoot(), opts.version) });
4640
+ const output = { version: opts.version, commit: opts.commit, samples: canary.samples, decision: canary.decision };
4641
+ console.log(JSON.stringify(output));
4642
+ return 0;
4643
+ }
4302
4644
  if (opts !== 'help') {
4303
4645
  placement = gateExecutorOf(opts);
4304
- debug.log('release-loop.gate', 'executor', { version: opts.version, ...placement, requested: opts.executor ?? null });
4646
+ debug.log('release-loop.gate', 'executor', { version: opts.version, ...placement, requested: opts.executor ?? null,
4647
+ imagePin: opts.imagePin ?? null, imageCommit: opts.imagePin === 'cut-commit' ? opts.commit : null });
4305
4648
  }
4306
4649
  if (opts === 'help') {
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.');
4650
+ 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>] [--precut-canary-file <test-file>] [--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]\n--precut-canary-file runs exactly two one-file warm↔fresh comparisons and stores them in this version\'s gate-verdict warmHistory before the standard gate consumes it; decideWarmReuse applies minMatchSamples.\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.');
4308
4651
  return 0;
4309
4652
  }
4310
4653
  version = opts.version;
@@ -4324,7 +4667,15 @@ export async function runGateNode(argv: string[], env: NodeJS.ProcessEnv, runner
4324
4667
  } catch (error) {
4325
4668
  result = { outcome: 'error', commit: '', introduced: [], preexisting: 0, fixed: 0, knownEnv: 0, knownEnvCleared: [], stalledEnv: [], rcNonzeroNoFailures: [], durationMs: Date.now() - start, error: String(error) };
4326
4669
  placement ??= requestedGateExecutor(argv, env);
4670
+ } finally {
4671
+ try { await warmControlCleanup?.(); }
4672
+ catch (error) {
4673
+ const reason = error instanceof Error ? error.message : String(error);
4674
+ debug.log('pod.warm.control-forward', 'cleanup-failed', { version, reason }, { level: 'error' });
4675
+ if (typeof result !== 'undefined') result = { ...result, outcome: 'error', error: [result.error, reason].filter(Boolean).join('; ') };
4676
+ }
4327
4677
  }
4678
+ if (!result) throw new Error('release gate returned without a result');
4328
4679
  if (versionPattern.test(version) && result.stalledShards?.length) {
4329
4680
  result.autoheals = result.stalledShards.map((shard, index) => {
4330
4681
  const shardKey = `pod-${shard.shard}`;
@@ -4363,5 +4714,6 @@ if (import.meta.main) {
4363
4714
  // The graph spawns this file as its own process, which inherits no daemon sink — without this every
4364
4715
  // `release-loop.gate`/`release.gate`/`pod.pool` event stayed in the file trail and `elanous logs` read 0 rows.
4365
4716
  await registerStandaloneLogSink('release-loop');
4366
- process.exitCode = await runWithReleaseGateTraffic(process.env, () => runGateNode(process.argv.slice(2), process.env));
4717
+ process.exitCode = await runWithReleaseGateTraffic(process.env,
4718
+ () => runWithReleasePwaTraffic(process.env, () => runGateNode(process.argv.slice(2), process.env)));
4367
4719
  }