elanous 0.2.42 → 0.2.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (685) hide show
  1. package/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 → Om5xS9jpjSXJCeTNGYEi4}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{7a98bc80.fa935989e829f9c2.js → 0966f8a1.745fcd44f64a87ea.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/{73d2eb0e.4966ad55293c801a.js → 0ad6802e.ae7afb63fdcb4491.js} +1 -1
  6. package/apps/pwa/out/_next/static/chunks/114.2b90424d1a3ce84c.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1151-5c2bb8f002ca2dd6.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1200.6a37c202f9f17e9a.js +226 -0
  9. package/apps/pwa/out/_next/static/chunks/1261-a616d5728e02e624.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/1313-19f737e0222c4444.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/1357.3bbef3976c37f94a.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/1362-20adddfaf662b0fa.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/{3507.97e1977bc4d5fb0a.js → 1406.a1dc2ab6f8449374.js} +2 -2
  14. package/apps/pwa/out/_next/static/chunks/1737-9491fc6ab29b9ec6.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/1803-4f91a8e7dcaf042c.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/1961.a81b12872c3e93a9.js +23 -0
  17. package/apps/pwa/out/_next/static/chunks/{53c73f84.373b6160adfa89a4.js → 1970cf59.2d4d8ce5d6c81853.js} +1 -1
  18. package/apps/pwa/out/_next/static/chunks/1971-8890f3962ba7ee0a.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/{9211-330b6736a63d4cb3.js → 2073-ba274e00e4f9efb4.js} +1 -1
  20. package/apps/pwa/out/_next/static/chunks/{7411.9b4b8b747dc7b802.js → 2144.25b292396712196d.js} +2 -2
  21. package/apps/pwa/out/_next/static/chunks/2268-53861f339e0b65a2.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/2312.0c8b61e22d3136d7.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/{6632.0eac1c554f8f2ef3.js → 2573.387a707f6f9d720c.js} +2 -2
  24. package/apps/pwa/out/_next/static/chunks/273.f156eee28f11af77.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/2873.d2140e5a82546401.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/{1262.e804feb8fd1acb8c.js → 3072.01338851ca21a6f7.js} +2 -2
  27. package/apps/pwa/out/_next/static/chunks/3091.a5a4143dea33d197.js +90 -0
  28. package/apps/pwa/out/_next/static/chunks/{2456.b6de0e1c05f28843.js → 3095.dc44586a5f83b86d.js} +1 -1
  29. package/apps/pwa/out/_next/static/chunks/{3207.a756c95e9ec8c3c8.js → 3113.d7472bbc27d580c7.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/3228-9650b6447b6fa288.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/3402.44b565b314f2263c.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/3557ac4a.c8931684475018ad.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/{8505.fd803c6c33b54cc3.js → 360.ae9f9f4c33c395e8.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/3638.80412ea63aae8f3f.js +286 -0
  35. package/apps/pwa/out/_next/static/chunks/{b450776b.3db34d8d965f82d3.js → 363bfab3.9623b9f89363541f.js} +1 -1
  36. package/apps/pwa/out/_next/static/chunks/3694.e0099884345f0198.js +64 -0
  37. package/apps/pwa/out/_next/static/chunks/3744.54f87db52acfd1ab.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/3903.3ef4656c67572764.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/3935.16701e33f8982dc9.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/3955.6922286237f2ac2b.js +24 -0
  41. package/apps/pwa/out/_next/static/chunks/3969.272ee7f9fd6c62e5.js +34 -0
  42. package/apps/pwa/out/_next/static/chunks/{8138.6c10b2a356d52fca.js → 4199.ce86b281fd0a60b7.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/4311-6dd3144002cc0c2d.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/4426-2b230fad00c88930.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/{2690.0521c738b7749931.js → 4445.7990c76b3751a918.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/4447-14c5161fa59166c7.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/4856-b0b8075bba8c53f4.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/4870-de370d65e73c0bb5.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/4885-2c989165c4b29fad.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/{1945.7b54990d578c119b.js → 4894.fec2ca9e99caefa2.js} +1 -1
  51. package/apps/pwa/out/_next/static/chunks/5093.8f04b7f8cb899a95.js +94 -0
  52. package/apps/pwa/out/_next/static/chunks/5122-2bb177cebf89e19a.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/5154.5f56dc3902e3d275.js +36 -0
  54. package/apps/pwa/out/_next/static/chunks/529.64ff7257180b2ac9.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/5394.5dabeb0c62293a28.js +93 -0
  56. package/apps/pwa/out/_next/static/chunks/5461.08bf2404ff38ec85.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/5474-22e08a85583209b2.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/{9427.96aa912ddb5801e0.js → 5564.9e72532188f57586.js} +16 -16
  59. package/apps/pwa/out/_next/static/chunks/5567-265df39ed8fe9dcb.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/5570-a94ae19811cb2046.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/5574-b47c9ef2dceff1fd.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/{4043.6b91203436c9c86b.js → 5628.6ddb2d5ffba4ff4c.js} +4 -4
  63. package/apps/pwa/out/_next/static/chunks/{1319.b9b815ae0f181e06.js → 5730.43d7ccc09988e646.js} +1 -1
  64. package/apps/pwa/out/_next/static/chunks/{3482.a9ae9d2ad6a5bd97.js → 5789.2da35c258292e1e5.js} +1 -1
  65. package/apps/pwa/out/_next/static/chunks/5824.da9527b15a3221e5.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/5866.366966af47fdde5d.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/{1487.2f67b79444062da6.js → 5930.8b4df3b46c32b05a.js} +1 -1
  68. package/apps/pwa/out/_next/static/chunks/5963-c4d8469c52a0828b.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/6001.ec4049ceb3c43418.js +27 -0
  70. package/apps/pwa/out/_next/static/chunks/6120-471f03db1e299c0c.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/6175-41479e643aca1878.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/6291.a2de4f9787637075.js +104 -0
  73. package/apps/pwa/out/_next/static/chunks/6400-6699907c3537436a.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/6428.1f4327d6ae5fdf9a.js +287 -0
  75. package/apps/pwa/out/_next/static/chunks/644.02cb026fa44bcd0a.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/6466-6c87d60d3dd174fb.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/{3347.f846266b5e5f3ede.js → 6540.c7bef57ad8f90a87.js} +3 -3
  78. package/apps/pwa/out/_next/static/chunks/6773.d4264665ab31bec0.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/6822.3c789f1a8443e774.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/{8262.ddc03e06b733f184.js → 6944.309f4e3ea54d1537.js} +1 -1
  81. package/apps/pwa/out/_next/static/chunks/7261-8ff69d3b2ee671fd.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/7266.084c6bbc70035140.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/7271.5c0100e7320aa856.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/7429-bf5731358c8f2f68.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/7464.01f7a0ae1215fbe7.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/7479.0fcad5f37eb64f0c.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/{4770.769c9cd9c7034eba.js → 7483.427a2fe2049e388d.js} +2 -2
  88. package/apps/pwa/out/_next/static/chunks/{1147-5196d19c222051b3.js → 7502-be74718178e13e56.js} +1 -1
  89. package/apps/pwa/out/_next/static/chunks/7530.0889722b19572065.js +24 -0
  90. package/apps/pwa/out/_next/static/chunks/7645.5b2ee3ad45c55ea3.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/7788.7d1c79f1a8e18946.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/7900.096f455c7c1efc53.js +77 -0
  93. package/apps/pwa/out/_next/static/chunks/7910-6f139bcafd238b9f.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/7966.f71c8958d1637548.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/{8673-bab689a55f7e13b9.js → 8062-7cd0bcc7f01455ba.js} +3 -3
  96. package/apps/pwa/out/_next/static/chunks/8073.f6cf2f2c71aafbd0.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/8201.190b94088980f430.js +74 -0
  98. package/apps/pwa/out/_next/static/chunks/8205.282ca4bb62d54a0a.js +62 -0
  99. package/apps/pwa/out/_next/static/chunks/8313-dfa1021bb58a46d6.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/837-81336ebaca8198a8.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/8447.7963352ef5f71b76.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/8574-868e8129525ef6d9.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/8588.d1c6a2903727ce87.js +149 -0
  104. package/apps/pwa/out/_next/static/chunks/8637.90f92d5c8db4732c.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/868.6e953be05bcf6dca.js +41 -0
  106. package/apps/pwa/out/_next/static/chunks/8706-e1fe0309343a2273.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/8749-06384cdfc4520add.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/{9656-374232558fd528d0.js → 8861-cc1fa1c7b920fa8e.js} +1 -1
  109. package/apps/pwa/out/_next/static/chunks/{5440-4236de26c5ea5856.js → 8967-7dfebf710c9dec9d.js} +1 -1
  110. package/apps/pwa/out/_next/static/chunks/{1063.2fb8a9dc55c731f2.js → 8986.8e2e4efbfa655c59.js} +1 -1
  111. package/apps/pwa/out/_next/static/chunks/8997-3c564dbb3b58b055.js +1 -0
  112. package/apps/pwa/out/_next/static/chunks/{4320.583cbab64db6be02.js → 923.2034d10ae91e9c20.js} +1 -1
  113. package/apps/pwa/out/_next/static/chunks/9312.bc1b89c5d4368033.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/{1574.878c18ecc97b1bd4.js → 943.39ae5e82f4f1893f.js} +1 -1
  115. package/apps/pwa/out/_next/static/chunks/9469.0500480de55fb205.js +32 -0
  116. package/apps/pwa/out/_next/static/chunks/9770.5f75bc6088b16109.js +1 -0
  117. package/apps/pwa/out/_next/static/chunks/9810-2543267050e58b82.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/9870.8419702475254745.js +8 -0
  119. package/apps/pwa/out/_next/static/chunks/9941-376d1b74d51b01a4.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/9958.385434ab04241758.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/aa57532f.510551854ff239b1.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-deea1450b17cb8dc.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/app/approvals/page-70985b904f8650ef.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-a2e15e33e56a4353.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/board/page-fc72ce2db12d6ca9.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/botlab/page-be03e41f9436ebd9.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/bots/page-35a35999382da3f2.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/ceo/page-3ff5b1e6ca88b848.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/chat/page-a65101300a0ee5c1.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/consult/page-3aca5b6d67dba511.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/control/page-45b8ab0a71e61314.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/decisions/page-53046e282d4cda8e.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/design-check/page-17809e60e8354c17.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/editor/page-fd2442d347bf5898.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/exec/page-e9d720d6fc082e21.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/field/page-6b8b4267bab0a677.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/healthz/route-deea1450b17cb8dc.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/inside/page-8bcf6233ac7c17af.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/intake/page-7cc25591cfc2126f.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/layout-053e871f35c9beba.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/live/page-eff819f0c2d5e084.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/live-run/page-4d7e9d8db5274a87.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/loops/page-2b2d8f5aa5f69ad4.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/market/page-0b8f92c5062e68b1.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1bd9d0fdac2ced3d.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/morning/{page-0f291d3c8fd55a3a.js → page-9ee28f7df07428ea.js} +1 -1
  147. package/apps/pwa/out/_next/static/chunks/app/not-found-44b7a1ff468c6648.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/observatory/page-cbc6523711dc1b65.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-fdad47899c2695be.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-79a8b5fee4c20871.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-03e8ac9606aeaffb.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/outputs/page-4cd55c55ec287e2f.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/page-db17b4305404161e.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/reflection/page-30041d848350fd02.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-c540da022dbf86b0.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/sessions/page-9399efc538da057f.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-ca2d09128c585ca4.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/settings/page-0e2635aecd343038.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ef7f4399b6b7726e.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/setup/layout-deea1450b17cb8dc.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/page-caf530535b875468.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/share/page-eaa20c1543a5fd8e.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/showroom/page-c541eb1b75120f61.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/tasks/page-ab5e213eda9479f4.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/term/page-0a815f34367b6db6.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/today/page-e858ba0ed9393b68.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/trace/page-1638ee9dd6e0582f.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/vault/page-4cf5a399109391f2.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b4396321238798ec.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/workflows/page-cb72bfd21006781a.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workspace/page-6135d0242afbc434.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-a1dccf907eddb856.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/c7945435-b44d4dbe90ba0dcb.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/{3d0154af.5331fec737ffe8c3.js → cb0f8df8.09a4a6be97e3fc5f.js} +1 -1
  175. package/apps/pwa/out/_next/static/chunks/{e8543907.5b040a5f22a0b0e5.js → d96660fb.cbc21a2a88539dbb.js} +7 -7
  176. package/apps/pwa/out/_next/static/chunks/{50061469.0326382241ef3b2d.js → ea514caa.05a24f78d9dffe5f.js} +1 -1
  177. package/apps/pwa/out/_next/static/chunks/fb7d20d5-6892502b6ee39ca6.js +1 -0
  178. package/apps/pwa/out/_next/static/chunks/framework-e8a911e19b1be41f.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-7022f418fabba021.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-app-e5f15df755756a1e.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_app-4525f91e36940c9b.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_error-140f0e519c085b0f.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/webpack-8f22ef63dcb865b1.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/examples/research-runtime-empty-terminal.yaml +28 -0
  285. package/graphs/profiles/local.yaml +5 -0
  286. package/graphs/profiles/pod.yaml +4 -0
  287. package/package.json +6 -2
  288. package/plugins/demo-absorption-briefing/plugin.json +18 -0
  289. package/plugins/demo-absorption-briefing/skills/demo-absorption-briefing/SKILL.md +8 -0
  290. package/plugins/demo-browser-hand/plugin.json +13 -0
  291. package/plugins/demo-browser-hand/skills/demo-browser-hand/SKILL.md +8 -0
  292. package/plugins/demo-linear-secretary/plugin.json +15 -0
  293. package/plugins/demo-linear-secretary/skills/demo-linear-secretary/SKILL.md +8 -0
  294. package/plugins/demo-plugin-wizard/plugin.json +11 -0
  295. package/plugins/demo-plugin-wizard/skills/demo-plugin-wizard/SKILL.md +8 -0
  296. package/plugins/demo-research/plugin.json +16 -0
  297. package/plugins/demo-research/skills/demo-research/SKILL.md +8 -0
  298. package/plugins/demo-video-briefing/plugin.json +15 -0
  299. package/plugins/demo-video-briefing/skills/demo-video-briefing/SKILL.md +8 -0
  300. package/scripts/build-signed-catalog.ts +48 -0
  301. package/scripts/ci-typecheck-main-delta.sh +88 -0
  302. package/scripts/codex-fallback-live-probe.ts +1 -1
  303. package/scripts/demo/demo1104-parallel-scenes.ts +301 -0
  304. package/scripts/demo/demo1104-stage.config.ts +21 -0
  305. package/scripts/demo/demo1104-stage.ts +316 -0
  306. package/scripts/goal-method-audit.ts +17 -1
  307. package/scripts/graph-units/land-enqueue.ts +90 -0
  308. package/scripts/hooks/codex-notify.sh +32 -0
  309. package/scripts/install.sh +10 -38
  310. package/scripts/landing-heal/land-plan.ts +57 -0
  311. package/scripts/release-loop/auto-approve-node.ts +1 -1
  312. package/scripts/release-loop/gate-autoheal-sim-evidence.ts +60 -0
  313. package/scripts/release-loop/gate-method.ts +139 -0
  314. package/scripts/release-loop/gate-node.ts +180 -20
  315. package/scripts/release-loop/gate-traffic-mode.ts +151 -3
  316. package/scripts/release-loop/node-verdict.ts +38 -25
  317. package/scripts/release-loop/preflight.ts +362 -8
  318. package/scripts/release-loop/unattended-release.ts +20 -9
  319. package/scripts/rhythm/daily.ts +36 -4
  320. package/scripts/rhythm/linear-progress-sync.ts +298 -0
  321. package/scripts/video-pipeline.ts +9 -1
  322. package/scripts/warm-pool-provider-config.ts +29 -6
  323. package/src/acp/agent-manager.ts +1 -0
  324. package/src/acp/codex-app-server-agent.ts +33 -0
  325. package/src/acp/codex-app-server-client.ts +153 -16
  326. package/src/acp/core-turn-bridge.ts +17 -12
  327. package/src/acp/event-router.ts +20 -1
  328. package/src/acp/server.ts +6 -0
  329. package/src/agent-mission/browser-roundtrip-coordinator.ts +85 -0
  330. package/src/agent-mission/claude-headless.ts +4 -1
  331. package/src/agent-mission/codex-exec.ts +105 -0
  332. package/src/agent-mission/driver.ts +192 -11
  333. package/src/agent-mission/grok-auth-status.ts +47 -0
  334. package/src/agent-mission/hand-hooks.ts +101 -0
  335. package/src/agent-mission/mission-cli.ts +56 -21
  336. package/src/agent-mission/mission-hand-reply.ts +118 -0
  337. package/src/agent-mission/mission-line.ts +180 -0
  338. package/src/agent-mission/mission-out.ts +256 -18
  339. package/src/agent-mission/tool-install.ts +122 -11
  340. package/src/agents/general-purpose.md +1 -1
  341. package/src/auto-research/research-turn.ts +95 -10
  342. package/src/autopilot/pr-manager.ts +31 -3
  343. package/src/boot/daemon-tools/self-implement.ts +3 -1
  344. package/src/boot/daemon-tools/web-search.ts +1 -2
  345. package/src/briefing/brief-items.ts +55 -11
  346. package/src/cli/agent-cli.ts +2 -1
  347. package/src/cli/catalog-cli.ts +31 -0
  348. package/src/cli/connector-cli.ts +16 -0
  349. package/src/cli/doctor-fix.ts +2 -2
  350. package/src/cli/intake-source-cli.ts +44 -8
  351. package/src/cli/msg-cli.ts +32 -3
  352. package/src/cli/pr-cli.ts +21 -1
  353. package/src/cli/pr-land-heavy-remote.ts +44 -6
  354. package/src/cli/pr-land-overlap-evidence.ts +13 -1
  355. package/src/cli/pr-land-pod-shadow-job.ts +143 -5
  356. package/src/cli/pr-land-pod-shadow.ts +9 -2
  357. package/src/cli/release-checklist-read-cli.ts +44 -16
  358. package/src/cli/release-cli.ts +23 -3
  359. package/src/cli/research-cli.ts +9 -0
  360. package/src/cli/setup-cli.ts +166 -6
  361. package/src/cli/setup-keys.ts +187 -0
  362. package/src/cli/task-plugin-wizard-host.ts +114 -0
  363. package/src/cli/tasks-cli.ts +54 -9
  364. package/src/cli/tui-test-cli.ts +186 -0
  365. package/src/config.ts +4 -2
  366. package/src/connectors/linear-daily-progress.ts +228 -0
  367. package/src/connectors/linear-projector.ts +34 -12
  368. package/src/connectors/linear.ts +151 -7
  369. package/src/core-turn/deep-turn-bloom.ts +67 -0
  370. package/src/core-turn/research-policy.ts +37 -0
  371. package/src/core-turn/run-core-turn.ts +320 -30
  372. package/src/core-turn/types.ts +2 -0
  373. package/src/dashboard/chat/acp-chat.ts +10 -0
  374. package/src/dashboard/classic-sidetab-mission-job.ts +103 -0
  375. package/src/dashboard/classic-sidetab-mission-pair.ts +81 -0
  376. package/src/dashboard/classic-sidetab-mission-trio.ts +219 -0
  377. package/src/dashboard/classic-sidetab-mission.ts +167 -5
  378. package/src/dashboard/index.ts +378 -13
  379. package/src/dashboard/input/chat-main-acp-dispatch.ts +23 -0
  380. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +8 -0
  381. package/src/dashboard/mission-cli-consumer.ts +20 -8
  382. package/src/dashboard/plugin-wizard-runtime.ts +50 -0
  383. package/src/dashboard/plugin-wizard-tool.ts +55 -0
  384. package/src/dashboard/sidetab-runtime.ts +1 -1
  385. package/src/dashboard/terminal-popup-runtime.ts +6 -3
  386. package/src/decisions/linear-progress-summary.ts +78 -0
  387. package/src/decisions/linear-progress-sync.ts +152 -0
  388. package/src/domains/outbound-alert.ts +49 -11
  389. package/src/domains/telegram-kind-route.ts +16 -9
  390. package/src/graph-runner/arg-allowance.ts +13 -0
  391. package/src/graph-runner/graph-run-api.ts +3 -1
  392. package/src/graph-runner/runner.ts +85 -17
  393. package/src/graph-wizard/mission-compose.ts +407 -12
  394. package/src/harness/dispatch-backpressure.ts +54 -1
  395. package/src/harness/dispatch-shadow-compare.ts +74 -0
  396. package/src/harness/harness-cli-command.ts +11 -1
  397. package/src/harness/skill-exec.ts +21 -9
  398. package/src/harness/skill-select.ts +1 -1
  399. package/src/hooks/error-report-cloud.ts +101 -0
  400. package/src/hooks/receiver.ts +29 -2
  401. package/src/index.ts +127 -10
  402. package/src/intake-plane/actions.ts +354 -0
  403. package/src/intake-plane/adapters/telegram-ambient.ts +34 -18
  404. package/src/intake-plane/intake-sources.ts +78 -8
  405. package/src/intake-plane/items.ts +14 -4
  406. package/src/intake-plane/linear-mission-sync.ts +80 -0
  407. package/src/intake-plane/linear-secretary-runtime.ts +178 -0
  408. package/src/intake-plane/linear-wish.ts +227 -1
  409. package/src/life/life-cli.ts +168 -0
  410. package/src/life/life-kinds.ts +160 -0
  411. package/src/live-supply/budget-consumer.ts +78 -0
  412. package/src/live-supply/read.ts +3 -0
  413. package/src/live-supply/snapshot.ts +45 -0
  414. package/src/llm/provider-credentials.ts +8 -2
  415. package/src/llm.ts +2 -1
  416. package/src/maturity/feature-maturity.ts +3 -3
  417. package/src/msg/msg-sse-server.ts +3 -3
  418. package/src/msg/msg-store.ts +177 -19
  419. package/src/native-tool-catalog.ts +36 -2
  420. package/src/nexus/api/http-server.ts +37 -1
  421. package/src/nexus/api/mcp-public-allowlist.ts +2 -0
  422. package/src/nexus/index.ts +7 -2
  423. package/src/nexus/outbound/format.ts +9 -0
  424. package/src/nexus/outbound/router.ts +98 -13
  425. package/src/nexus/setup-status.ts +9 -0
  426. package/src/nexus/state/state.ts +3 -0
  427. package/src/oauth/codex-account-store.ts +20 -7
  428. package/src/oauth/codex-owner-mirror.ts +44 -0
  429. package/src/oauth/codex.ts +17 -2
  430. package/src/oauth/fallback-chain.ts +47 -17
  431. package/src/onboarding/first-run-doctor.ts +8 -1
  432. package/src/onboarding/first-run.ts +85 -26
  433. package/src/onboarding/key-setup.ts +103 -0
  434. package/src/onboarding/menu-answer.ts +13 -0
  435. package/src/onboarding/secret-prompt.ts +37 -0
  436. package/src/plugins/install/plugin-install.ts +59 -0
  437. package/src/pr-tool/census.ts +130 -0
  438. package/src/pty-shell/pty-control-ipc.ts +4 -1
  439. package/src/registry/catalog-server.ts +232 -0
  440. package/src/registry/loader.ts +45 -1
  441. package/src/registry/types.ts +1 -2
  442. package/src/research/axis-fanout.ts +43 -28
  443. package/src/self-dev/ask-launch-flow.ts +72 -1
  444. package/src/self-dev/dev-cli.ts +4 -1
  445. package/src/self-dev/dev-pipeline.ts +5 -0
  446. package/src/self-dev/draft-residue.ts +64 -5
  447. package/src/self-dev/graph-front-nodes.ts +2 -1
  448. package/src/self-dev/graph-journey-route.ts +76 -21
  449. package/src/self-dev/launch-preflight.ts +58 -0
  450. package/src/self-dev/orchestrate.ts +1 -1
  451. package/src/self-dev/shard-aggregate.ts +4 -0
  452. package/src/self-dev/shard-run.ts +36 -15
  453. package/src/self-implement/abandoned-classification.ts +12 -3
  454. package/src/self-implement/budget-gate.ts +7 -0
  455. package/src/self-implement/child-llm-preference.ts +10 -1
  456. package/src/self-implement/doctor-visit.ts +30 -8
  457. package/src/self-implement/elanous-tui-spawn.ts +3 -0
  458. package/src/self-implement/fixtures/tdd-green-missing-passing-child-transcript.txt +2 -0
  459. package/src/self-implement/gate-artifacts.ts +2 -1
  460. package/src/self-implement/goal-author-cli.ts +4 -2
  461. package/src/self-implement/goal-author.ts +358 -4
  462. package/src/self-implement/graph-overlay-yaml.ts +30 -4
  463. package/src/self-implement/graph-overlay.ts +3 -1
  464. package/src/self-implement/graph-profile-yaml.ts +107 -0
  465. package/src/self-implement/graph-yaml.ts +26 -3
  466. package/src/self-implement/off-diff-evidence.ts +249 -5
  467. package/src/self-implement/orchestrator.ts +240 -15
  468. package/src/self-implement/run-chart.ts +6 -3
  469. package/src/self-implement/run-ledger.ts +51 -1
  470. package/src/self-implement/runtime-overlay-run-steps.ts +95 -0
  471. package/src/self-implement/tdd-evidence-tags.ts +9 -0
  472. package/src/self-implement/train-gate-receipt.ts +166 -0
  473. package/src/self-implement/train-gate-remote.ts +62 -18
  474. package/src/session/chat.ts +4 -2
  475. package/src/session/session-fanout.ts +2 -0
  476. package/src/session/streaming/acp-stream-sink.ts +2 -1
  477. package/src/session/streaming/chunk-producer.ts +6 -0
  478. package/src/session/streaming/stream-compositor.ts +3 -2
  479. package/src/session-runtime/index.ts +37 -1
  480. package/src/session-runtime/retry-policy.ts +1 -1
  481. package/src/setup/llm-env-detect.ts +3 -0
  482. package/src/setup/service-key-catalog.ts +46 -0
  483. package/src/setup/service-keys.ts +241 -0
  484. package/src/sidetab-core/visible-agent-mission.ts +98 -22
  485. package/src/skills/tools/agent.ts +23 -4
  486. package/src/skills/tools/omni-search.ts +123 -25
  487. package/src/task-agent/parent-host.ts +10 -0
  488. package/src/task-agent/repair-outcomes.ts +65 -0
  489. package/src/task-agent/task-hand.ts +20 -7
  490. package/src/task-orchestrator/dispatcher.ts +14 -1
  491. package/src/task-orchestrator/linear-capture-events.ts +55 -0
  492. package/src/task-orchestrator/store.ts +16 -5
  493. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +4 -0
  494. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +92 -1
  495. package/src/task-orchestrator/surfaces/pod-ledger-events.ts +113 -14
  496. package/src/task-orchestrator/surfaces/pod-source-receive.ts +23 -11
  497. package/src/task-orchestrator/surfaces/pod-warm-switch.ts +2 -0
  498. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +35 -9
  499. package/src/task-orchestrator/surfaces/self-implement-pod.ts +172 -6
  500. package/src/task-orchestrator/surfaces/self-implement.ts +27 -3
  501. package/src/task-orchestrator/surfaces/warm-reuse-evidence.ts +169 -0
  502. package/src/task-orchestrator/types.ts +14 -0
  503. package/src/telegram.ts +14 -16
  504. package/src/terminal/coding-agent.ts +14 -5
  505. package/src/terminal/session-registry.ts +1 -1
  506. package/src/tool-hints/gate.ts +1 -0
  507. package/src/tool-runtime/agent-runtime.ts +1 -0
  508. package/src/tool-runtime/browser-hand-runtime.ts +381 -0
  509. package/src/tool-runtime/browser-runtime.ts +2 -1
  510. package/src/tool-runtime/elanous-linear-issue-runtime.ts +76 -0
  511. package/src/tool-runtime/elanous-linear-secretary-runtime.ts +57 -0
  512. package/src/tool-runtime/graph-mcp-runtimes.ts +240 -0
  513. package/src/tool-runtime/index.ts +6 -0
  514. package/src/tool-runtime/types.ts +3 -0
  515. package/src/tui/entry.ts +24 -1
  516. package/src/tui/sidetab/attach.ts +140 -11
  517. package/src/tui/sidetab/herdr-portal.ts +225 -0
  518. package/src/user-config.ts +34 -9
  519. package/src/user-intent/types.ts +4 -3
  520. package/src/ux-sim/browser-hand.ts +165 -0
  521. package/src/ux-sim/draft-inventory-builder.ts +198 -0
  522. package/src/ux-sim/fake-browser-page.ts +81 -0
  523. package/src/ux-sim/fake-linear-device.ts +31 -0
  524. package/src/ux-sim/graph-engine-sim.ts +47 -0
  525. package/src/ux-sim/linear-secretary.ts +216 -0
  526. package/src/ux-sim/mock-scenario-catalog.json +75 -23
  527. package/src/ux-sim/mock-scenario-catalog.ts +120 -2
  528. package/src/ux-sim/onboarding-first-run-device.ts +31 -21
  529. package/src/ux-sim/scenario.ts +70 -26
  530. package/src/ux-sim/service-key-setup.ts +50 -0
  531. package/src/ux-sim/term-slash-device.ts +74 -0
  532. package/src/ux-sim/turn-throw-sim.ts +31 -0
  533. package/src/version/packed-revision.json +1 -1
  534. package/src/web-search/exa.ts +77 -0
  535. package/src/web-search/index.ts +30 -9
  536. package/src/web-search/provider.ts +4 -1
  537. package/src/web-search/x-intent.ts +5 -0
  538. package/apps/pwa/out/_next/static/chunks/0244e5b5.6fc7670f8223d82d.js +0 -1
  539. package/apps/pwa/out/_next/static/chunks/1121.13ba7a8aff19df81.js +0 -36
  540. package/apps/pwa/out/_next/static/chunks/1345-31427b833438f521.js +0 -1
  541. package/apps/pwa/out/_next/static/chunks/1455-530ca16c7e71baff.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/1586.fafc33a9a5835cb7.js +0 -32
  543. package/apps/pwa/out/_next/static/chunks/1744.855fd60ae4c4a8c8.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/193.21a47a3e07ef6860.js +0 -1
  545. package/apps/pwa/out/_next/static/chunks/2004.e839fda7ea5a738a.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/2044-dd681a5c08779ff8.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/2101.2248decb38829b03.js +0 -286
  548. package/apps/pwa/out/_next/static/chunks/2160.a097e7b138ae1c55.js +0 -23
  549. package/apps/pwa/out/_next/static/chunks/2300.41ea70ac1bef1446.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/2357-07b5eeb5fb12628d.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/2364.28652020695a6db3.js +0 -34
  552. package/apps/pwa/out/_next/static/chunks/2526.c3e50ac1fec064bd.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/2549-cda50b716ff4250c.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/2550-5f1e88f615568045.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/261-319078c954e40d66.js +0 -1
  556. package/apps/pwa/out/_next/static/chunks/2974.595d1e29d040cb6a.js +0 -64
  557. package/apps/pwa/out/_next/static/chunks/3285.89ee2f298682f5e5.js +0 -93
  558. package/apps/pwa/out/_next/static/chunks/3447.06edc7b092021417.js +0 -41
  559. package/apps/pwa/out/_next/static/chunks/3510-9ca392698bfe4391.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/3541.772e5ce425aaa611.js +0 -24
  561. package/apps/pwa/out/_next/static/chunks/3741.9cf76acaa2ee5298.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/3837.a210d7d0db8d8abd.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/3898-955c896f394217a6.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/3947-ef57acea76eaa8d2.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/4037-3b82ebc7d78afdc7.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/405.9022d4a478202b67.js +0 -77
  567. package/apps/pwa/out/_next/static/chunks/417-fb2cc9a9910e5968.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/4276.336d76d858fcdf1b.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/4302.6ced9aa4535de947.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/4768-8eac183fd291ca53.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/4895.65605ec3e6504e89.js +0 -74
  572. package/apps/pwa/out/_next/static/chunks/5112.4b9f300124fab7b3.js +0 -149
  573. package/apps/pwa/out/_next/static/chunks/5335.375ee922bcd5bc83.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/5342.023a2e729689b320.js +0 -27
  575. package/apps/pwa/out/_next/static/chunks/5431-72a1dbde59400691.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/5486-b318c165f645d8ba.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/5506-c7d2ec25c62a7c94.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/5570-c66ecbea26b4e3b1.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/5696.ffca5aa259d8451e.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/5793-8c584aea2aa87560.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/5848-6de6b8c173351a90.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/5885.0a3e52c8faccec98.js +0 -287
  583. package/apps/pwa/out/_next/static/chunks/5999.94e3c83040200f31.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/5a7901d6-2d8d9b5587cf9042.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/6131.348b7d6bdcd267ce.js +0 -226
  586. package/apps/pwa/out/_next/static/chunks/6164.86133a0172611184.js +0 -90
  587. package/apps/pwa/out/_next/static/chunks/6377.bc7df239edbff244.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/6515.e07e8b2b8a707a87.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/6522-c005514f87d18982.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/6642.648adb1767f39ecf.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/6848.138805a09d001d09.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/7049-c09e2b7a253a24f3.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/7072.1e00447d9fa39972.js +0 -104
  594. package/apps/pwa/out/_next/static/chunks/7106.64201c539c5e220c.js +0 -94
  595. package/apps/pwa/out/_next/static/chunks/7151-1f742b455986530e.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/7199.18d6b25e001154ce.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/7279-c199ea83f34338fa.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/728b938d.ffaa7ad583d60e58.js +0 -1
  599. package/apps/pwa/out/_next/static/chunks/7309.171c3de54e0322f1.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/733.7a1ab3342e03306f.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/7504.d5cf1e7cd89b1b08.js +0 -24
  602. package/apps/pwa/out/_next/static/chunks/7573-0fd4af62d65d4598.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/759.6f8267df4689c401.js +0 -1
  604. package/apps/pwa/out/_next/static/chunks/7825-668b046f72f14483.js +0 -1
  605. package/apps/pwa/out/_next/static/chunks/7852-1538768b6eb19388.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/7868.cff810430bb63c91.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/8084-dfcc796874ab4e07.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/8266.6bf9b131b2584678.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/8403.7b81d5e64074a725.js +0 -8
  610. package/apps/pwa/out/_next/static/chunks/8470.1998cbd0866a7eac.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/8546-6f03f8ea34dd61d9.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/8687-246fda8433d3477c.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/898-280022fb082936bc.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/9098-50eefc1844212e3d.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/9249.976f1e69a9d80754.js +0 -1
  616. package/apps/pwa/out/_next/static/chunks/9255-cf88fceda5666645.js +0 -1
  617. package/apps/pwa/out/_next/static/chunks/9306-e523ba58bb8adc42.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/9324.a9866d717ba62cb6.js +0 -1
  619. package/apps/pwa/out/_next/static/chunks/9325.a8510e8aa87c8143.js +0 -1
  620. package/apps/pwa/out/_next/static/chunks/9353-12bfff0c130104cf.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/9415.69e4a21c60c7d2db.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/9479-154a697d4b6e4e98.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/9540-65aa6ccc31b3fd78.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/9633.ce5bddc56784da72.js +0 -1
  625. package/apps/pwa/out/_next/static/chunks/9832.f4a98c878ecf2c0a.js +0 -62
  626. package/apps/pwa/out/_next/static/chunks/9865.f094440645f02d98.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/9a852a62-6192a6ea9346ced3.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-e772b4710df5d936.js +0 -1
  629. package/apps/pwa/out/_next/static/chunks/app/approvals/page-4361368e6a76b967.js +0 -1
  630. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-802f349fdd65248d.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/app/board/page-7f35513988a2a495.js +0 -1
  632. package/apps/pwa/out/_next/static/chunks/app/botlab/page-798121656540e33e.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/app/bots/page-20a089490affe98c.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/app/ceo/page-e5e4ec786d70e604.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/app/chat/page-d12fe5d0801430e4.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/app/consult/page-d8cf21022fecc026.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/app/control/page-21ce565c9c57ee97.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/app/decisions/page-b56563025f619c9b.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/app/design-check/page-52f14dd133da8348.js +0 -1
  640. package/apps/pwa/out/_next/static/chunks/app/editor/page-c0ed7fdcdba76633.js +0 -1
  641. package/apps/pwa/out/_next/static/chunks/app/exec/page-af8c3020cb520f15.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/app/field/page-72eda67af9f5e35c.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/app/healthz/route-e772b4710df5d936.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/app/inside/page-67a6620d31f0a46f.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/app/intake/page-f059363aa0880ec2.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/app/layout-f9f917d6a57f69fb.js +0 -1
  647. package/apps/pwa/out/_next/static/chunks/app/live/page-d3ae55eee2f0826c.js +0 -1
  648. package/apps/pwa/out/_next/static/chunks/app/live-run/page-0e3d7b9421b79dac.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/app/loops/page-ba738f7e233eb211.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/app/market/page-33438b98c74fd599.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-0b34d5fd990d793a.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/app/not-found-3fced192be5279c1.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/app/observatory/page-04cb3cf182a7c1ea.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-6ccbd2ab23a21542.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-aa0518c48a5f4e56.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4b32226d341ad7d7.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3224fd8f6383b61f.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/app/page-efbc97459f2914e2.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/app/reflection/page-2fe487d88cd5d97c.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-48861dd1a858bab7.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/app/sessions/page-197d5d4d21d228ea.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a763ba89fbdd9c27.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/app/settings/page-7edb253510b1c136.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0c875702c61965ea.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/app/setup/layout-e772b4710df5d936.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/app/setup/page-e425c951151095f7.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/app/share/page-9c90e0aec4e27f77.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/app/showroom/page-733e09a02e727d42.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/app/tasks/page-4f899e14c34d77a1.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/app/term/page-bf067887b3307423.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/app/today/page-7af10868ff87d04e.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/app/trace/page-efe5304057aa11c7.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/app/vault/page-76bcd29aaa8a26c3.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-4cfd88e2273ce50f.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/app/workflows/page-0a1ddb47efba0a32.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2a176c707152c892.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-efe03031b8797fbf.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/framework-d0d2d673bfd8a8a9.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/main-4c9fceb2baa09cb7.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/main-app-b0ab8d8fc9a97556.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/pages/_app-7e2cc62a2e5cec96.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/pages/_error-22324935f753ec5e.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/webpack-9dc1bfa25334998a.js +0 -1
  684. package/apps/pwa/out/_next/static/css/be9019f696217521.css +0 -1
  685. /package/apps/pwa/out/_next/static/{X1ucLw10Cds_h614NoEnh → Om5xS9jpjSXJCeTNGYEi4}/_ssgManifest.js +0 -0
@@ -52,7 +52,7 @@ import { markGateShardUnmeasured, nextGateShardAttempt, readGateShardManifest, r
52
52
  import { planGateShardDispatch, summarizeGateProgress, type GateProgressGroup, type GateShardReceipt } from './gate-shard-dispatch.js';
53
53
  import { defaultKubectl } from '../../src/task-orchestrator/surfaces/self-implement-pod.js';
54
54
  import { executeTuiNodeCheck, type ExecutedTuiCheck } from './tui-node-checks.js';
55
- import { runWithReleaseGateTraffic } from './gate-traffic-mode.js';
55
+ import { runWithReleaseGateTraffic, runWithReleasePwaTraffic } from './gate-traffic-mode.js';
56
56
  import { emitPodLifecycle, gatePodLifecycleEvent, gatePodOutcome } from '../../src/task-orchestrator/surfaces/pod-lifecycle-stages.js';
57
57
  import { gateHostReservationRoot, reserveReleaseGateHosts } from '../../src/release-loop/gate-host-reservation.js';
58
58
  import { normRoot } from '../../src/instance/resolve.js';
@@ -227,6 +227,10 @@ export function reconcileStaleGateShardBoard(input: {
227
227
  }
228
228
  export interface GateOptions {
229
229
  commit: string;
230
+ /** Explicit pre-cut warm↔fresh comparison, persisted before the ordinary gate reads warmHistory. */
231
+ warmPrecutCanary?: { file: string };
232
+ /** RELEASE-POLICY-PACK — the gate Pod image is tied to this resolved cut commit. */
233
+ imagePin?: 'cut-commit';
230
234
  /** The graph run that launched this gate; direct hard rehearsal instead pins source bytes explicitly. */
231
235
  graphRunId?: string;
232
236
  /** Only a direct hard rehearsal may pin graph bytes from its measured Git commit. */
@@ -250,6 +254,7 @@ export interface GateOptions {
250
254
  recheckFileLimit?: number;
251
255
  /** freshShards — GATE-SHARD-RESUME off: every shard starts a Pod even when this logDir already holds its result. */
252
256
  pod?: { pool: string; shards?: number; shardTimeoutSeconds?: number; durationSource?: string; bunCache?: string; freshShards?: boolean;
257
+ imagePin?: 'cut-commit';
253
258
  /** One immutable container image identity for this gate graph run and all its resumes. */
254
259
  pinnedImageDigest?: string;
255
260
  pinnedRunId?: string;
@@ -409,29 +414,151 @@ const WARM_REPLAY_BUDGET = 3;
409
414
  interface WarmVerdictSample { version: string; commit: string; baselineCommit: string; warmShadow: WarmShadowObservation[] }
410
415
  const recordValue = (value: unknown): Record<string, unknown> | null =>
411
416
  value !== null && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : null;
417
+ function validWarmShadowObservation(value: unknown): value is WarmShadowObservation {
418
+ const row = recordValue(value);
419
+ if (!row || !Array.isArray(row.files) || row.files.length !== 1 || typeof row.files[0] !== 'string'
420
+ || !/^(?:[\w.-]+\/)*[\w.-]+\.test\.[cm]?[jt]sx?$/.test(row.files[0])
421
+ || row.files[0].split('/').some((part) => part === '.' || part === '..')
422
+ || typeof row.attemptId !== 'string' || !row.attemptId || typeof row.reason !== 'string'
423
+ || !['observed', 'fallback', 'unmeasured'].includes(String(row.status))
424
+ || !['yes', 'no', 'failed', 'unavailable'].includes(String(row.quarantined))) return false;
425
+ if (row.finalized !== undefined && typeof row.finalized !== 'boolean') return false;
426
+ if (row.cutSha !== undefined && typeof row.cutSha !== 'string') return false;
427
+ if (row.baselineSha !== undefined && typeof row.baselineSha !== 'string') return false;
428
+ if (row.workerKey !== undefined) {
429
+ const key = recordValue(row.workerKey);
430
+ if (!key || typeof key.context !== 'string' || !key.context || typeof key.imageDigest !== 'string' || !key.imageDigest
431
+ || typeof key.lockHash !== 'string' || !key.lockHash || typeof key.architecture !== 'string' || !key.architecture) return false;
432
+ }
433
+ return true;
434
+ }
435
+ function validWarmVerdictSample(value: unknown): value is WarmVerdictSample {
436
+ const row = recordValue(value);
437
+ return !!row && typeof row.version === 'string' && typeof row.commit === 'string'
438
+ && typeof row.baselineCommit === 'string' && Array.isArray(row.warmShadow)
439
+ && row.warmShadow.every(validWarmShadowObservation);
440
+ }
412
441
  /** The verdict is atomically replaced; carry a bounded prior-verdict window in that same SSOT. */
413
442
  function readWarmVerdictHistory(path: string): WarmVerdictSample[] | undefined {
414
443
  if (!existsSync(path)) return [];
415
444
  try {
416
445
  const current = recordValue(JSON.parse(readFileSync(path, 'utf8')));
417
446
  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[];
447
+ if (current.warmHistory.some((entry) => !validWarmVerdictSample(entry))) return undefined;
448
+ const hasCurrentSample = ['version', 'commit', 'baselineCommit', 'warmShadow'].some((key) => key in current);
449
+ const history = [...current.warmHistory];
450
+ if (current.warmHistoryOnly !== true || hasCurrentSample) {
451
+ const currentSample = { version: current.version, commit: current.commit,
452
+ baselineCommit: current.baselineCommit, warmShadow: current.warmShadow };
453
+ if (!validWarmVerdictSample(currentSample)) return undefined;
454
+ if (current.warmHistoryOnly !== true) history.push(currentSample);
455
+ }
456
+ return history.slice(-WARM_VERDICT_HISTORY_LIMIT) as WarmVerdictSample[];
424
457
  } catch { return undefined; }
425
458
  }
459
+
460
+ /** Append one actual pre-cut replay observation to the gate verdict's bounded history, preserving its gate result. */
461
+ function recordWarmPrecutCanarySample(path: string, sample: WarmVerdictSample): void {
462
+ if (!sample.version || !/^[0-9a-f]{7,40}$/i.test(sample.commit) || !/^[0-9a-f]{7,40}$/i.test(sample.baselineCommit)
463
+ || !Array.isArray(sample.warmShadow) || sample.warmShadow.length !== 1 || !sample.warmShadow.every(validWarmShadowObservation))
464
+ throw new Error('invalid pre-cut canary sample');
465
+ let current: Record<string, unknown> = {};
466
+ if (existsSync(path)) {
467
+ let decoded: unknown;
468
+ try { decoded = JSON.parse(readFileSync(path, 'utf8')); }
469
+ catch (error) { throw new Error(`gate verdict JSON is malformed: ${String(error)}`); }
470
+ const parsed = recordValue(decoded);
471
+ if (!parsed) throw new Error('gate verdict is not an object');
472
+ current = parsed;
473
+ if (current.warmHistory !== undefined && !Array.isArray(current.warmHistory)) throw new Error('gate verdict warmHistory is invalid');
474
+ }
475
+ const storedHistory = Array.isArray(current.warmHistory) ? [...current.warmHistory] : [];
476
+ if (storedHistory.some((entry) => !validWarmVerdictSample(entry))) throw new Error('gate verdict warmHistory contains an invalid row');
477
+ const history = storedHistory;
478
+ const hasCurrentSample = ['version', 'commit', 'baselineCommit', 'warmShadow'].some((key) => key in current);
479
+ if (current.warmHistoryOnly === true && hasCurrentSample) {
480
+ const previous = { version: current.version, commit: current.commit, baselineCommit: current.baselineCommit, warmShadow: current.warmShadow };
481
+ if (!validWarmVerdictSample(previous)) throw new Error('gate verdict current warm sample is invalid');
482
+ } else if (current.warmHistoryOnly !== true) {
483
+ const previous = { version: current.version, commit: current.commit, baselineCommit: current.baselineCommit, warmShadow: current.warmShadow };
484
+ if (!validWarmVerdictSample(previous)) throw new Error('gate verdict current warm sample is invalid');
485
+ history.push(previous);
486
+ }
487
+ history.push(sample);
488
+ const body = JSON.stringify({ ...current, warmHistory: history.slice(-WARM_VERDICT_HISTORY_LIMIT), warmHistoryOnly: true }, null, 2) + '\n';
489
+ mkdirSync(dirname(path), { recursive: true });
490
+ const temp = join(dirname(path), `.gate-verdict-${process.pid}-${randomUUID()}.tmp`);
491
+ try { writeFileSync(temp, body, { mode: 0o600, flag: 'wx' }); chmodSync(temp, 0o600); renameSync(temp, path); }
492
+ finally { if (existsSync(temp)) rmSync(temp); }
493
+ }
494
+
495
+ /** Run a bounded set of real fresh-Job ↔ warm-worker comparisons before the release gate. */
496
+ export async function runWarmPrecutCanary(input: { opts: GateOptions; runner: GateRunner; file: string;
497
+ config: unknown; historyPath: string; timeoutMs?: number }): Promise<{ samples: WarmShadowObservation[]; decision: ReturnType<typeof decideWarmReuse> }> {
498
+ const { opts, runner } = input;
499
+ if (!opts.pod || !runner.recheckPod || !opts.baselineCommit || !opts.pod.warmReplay || !opts.pod.warmFinalize || !opts.pod.warmQuarantine)
500
+ throw new Error('pre-cut canary requires a Pod pool, baseline commit, and warm worker hooks');
501
+ if (!/^(?:[\w.-]+\/)*[\w.-]+\.test\.[cm]?[jt]sx?$/.test(input.file)
502
+ || input.file.split('/').some((part) => part === '.' || part === '..')) throw new Error('pre-cut canary requires one repository test file');
503
+ if (readWarmVerdictHistory(input.historyPath) === undefined)
504
+ throw new Error('gate verdict contains malformed warm history or current sample; refusing canary without writing');
505
+ const samples = 2;
506
+ if (samples > WARM_REPLAY_BUDGET) throw new Error(`pre-cut canary sample count exceeds replay budget (${WARM_REPLAY_BUDGET})`);
507
+ const lock = await runner.localCommand('git', ['show', `${opts.commit}:bun.lock`], opts.repo ?? process.cwd());
508
+ if (lock.rc !== 0 || !lock.output) throw new Error('pre-cut canary cut lock is unmeasured');
509
+ if (!/^[0-9a-f]{40}$/i.test(opts.baselineCommit)) throw new Error('pre-cut canary baseline commit is invalid');
510
+ const baseline = await runner.localCommand('git', ['cat-file', '-e', `${opts.baselineCommit}^{commit}`], opts.repo ?? process.cwd());
511
+ if (baseline.rc !== 0) throw new Error(`pre-cut canary baseline commit is absent: ${opts.baselineCommit.slice(0, 12)}`);
512
+ const trackedFile = await runner.localCommand('git', ['cat-file', '-e', `${opts.commit}:${input.file}`], opts.repo ?? process.cwd());
513
+ if (trackedFile.rc !== 0) throw new Error(`pre-cut canary test file is absent from cut ${opts.commit.slice(0, 12)}`);
514
+ const lockHash = createHash('sha256').update(lock.output).digest('hex');
515
+ const pod = { ...opts.pod, warmProducerEnabled: false, warmCutSha: opts.commit, warmBaselineSha: opts.baselineCommit,
516
+ warmEligible: true, warmReplayOwner: true, warmShadowScope: { requested: false, attempts: 0 } };
517
+ const observations: WarmShadowObservation[] = [];
518
+ const attemptIds = new Set<string>();
519
+ for (let index = 0; index < samples; index++) {
520
+ let observation: WarmShadowObservation;
521
+ let failure: Error | undefined;
522
+ try {
523
+ const result = await runner.recheckPod(input.file, opts.commit, pod, input.timeoutMs ?? GATE_ISOLATED_TIMEOUT_MS);
524
+ observation = result.warmShadow ?? { files: [input.file], attemptId: `precut-unmeasured-${index + 1}-${randomUUID()}`,
525
+ status: 'unmeasured', reason: result.rc === 0 ? 'warm-shadow-missing' : `fresh-job-rc-${result.rc}`, quarantined: 'no' };
526
+ if (result.rc !== 0) { observation = { ...observation, status: 'unmeasured', reason: `fresh-job-rc-${result.rc}` };
527
+ failure = new Error(`pre-cut fresh Job failed on attempt ${index + 1}: rc=${result.rc}`); }
528
+ else if (observation.status !== 'observed' || !observation.workerKey
529
+ || observation.files.length !== 1 || observation.files[0] !== input.file
530
+ || observation.cutSha !== opts.commit || observation.baselineSha !== opts.baselineCommit
531
+ || observation.workerKey.lockHash !== lockHash || !['match', 'result'].includes(observation.reason)
532
+ || attemptIds.has(observation.attemptId)
533
+ || (observation.reason === 'match' && (observation.finalized !== true || observation.quarantined !== 'no'))) {
534
+ failure = new Error(`pre-cut warm comparison was not measured on attempt ${index + 1}: ${observation.reason}`);
535
+ }
536
+ } catch (error) {
537
+ observation = { files: [input.file], attemptId: `precut-error-${index + 1}-${randomUUID()}`,
538
+ status: 'unmeasured', reason: String(error), quarantined: 'unavailable' };
539
+ failure = error instanceof Error ? error : new Error(String(error));
540
+ }
541
+ attemptIds.add(observation.attemptId);
542
+ observations.push(observation);
543
+ recordWarmPrecutCanarySample(input.historyPath, { version: opts.version, commit: opts.commit,
544
+ baselineCommit: opts.baselineCommit, warmShadow: [observation] });
545
+ if (failure) throw failure;
546
+ }
547
+ const history = readWarmVerdictHistory(input.historyPath);
548
+ const evidence = warmEvidenceFromVerdicts(history, opts.version, opts.commit, opts.baselineCommit, lockHash);
549
+ const warmReuseConfig = (input.config && typeof input.config === 'object' && 'pod' in input.config
550
+ && recordValue((input.config as Record<string, unknown>).pod)?.warmReuse) as WarmReuseSwitchConfig | undefined;
551
+ return { samples: observations, decision: decideWarmReuse('gate-recheck', warmReuseConfig,
552
+ evidence ? { 'gate-recheck': evidence } : undefined) };
553
+ }
426
554
  /** Every counted match has a measured, finalized worker with this exact cut, baseline and lock. */
427
555
  export function warmEvidenceFromVerdicts(history: readonly WarmVerdictSample[] | undefined,
428
556
  version: string, head: string, baseline: string, lock: string): WarmReuseEvidence | undefined {
429
557
  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
- });
558
+ const matchingHistory = history.filter((verdict) => verdict.version === version && verdict.commit === head
559
+ && verdict.baselineCommit === baseline);
560
+ const rows = matchingHistory.flatMap((verdict) => verdict.warmShadow.length ? verdict.warmShadow : [null]);
561
+ if (!rows.length) return undefined;
435
562
  const attempts = new Set<string>();
436
563
  const key = rows.at(-1)?.workerKey;
437
564
  if (rows.some((entry) => {
@@ -2227,7 +2354,8 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
2227
2354
  pool: pod.pool, poolScheduler: trackedPool(shard, branch, jobName, resultFile, attemptId, (member) => {
2228
2355
  stamp('poolPlaced'); jobContext = member.context; jobContextForCapture = member.context; placedOn = member.context; placed();
2229
2356
  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,
2357
+ }, skipMembers, depth === 0 && branch === '' && hostRetries === 0 && !retriedNoOutput && !batchIsolation && skipMembers.size === 0), clone: true, source: { kind: 'commit', sha: commit },
2358
+ ...(pod.imagePin === 'cut-commit' ? { imageCommit: commit } : {}), deadlineSeconds: shardDeadline,
2231
2359
  ...gateHostMirror(pod),
2232
2360
  ...(multiHost ? { statusUnreadableMs: memberUnreadableSeconds * 1000 } : {}),
2233
2361
  ...(bunCache ? { bunCache } : {}),
@@ -3266,11 +3394,11 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
3266
3394
  let cutSweepRc: number | null = null;
3267
3395
  let radiusShadow: GateRadiusShadowPlan | undefined;
3268
3396
  const trees: string[] = [];
3397
+ const baselineVersion = opts.baselineVersion;
3269
3398
  try {
3270
3399
  if (!sha.test(opts.commit) || !versionPattern.test(opts.version)
3271
3400
  || (opts.baselineVersion && !versionPattern.test(opts.baselineVersion))
3272
3401
  || (opts.baselineCommit && !sha.test(opts.baselineCommit))) throw new Error('invalid commit or version');
3273
- const baselineVersion = opts.baselineVersion;
3274
3402
  if (!baselineVersion) throw new Error('previous release version required (--baseline-version)');
3275
3403
  if (opts.rehearsalBaseline && (opts.graphRunId || !opts.pod || !/-rc\.\d+$/.test(opts.version)))
3276
3404
  throw new Error('rehearsal baseline requires a direct Pod gate with an isolated rc version');
@@ -3506,7 +3634,7 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
3506
3634
  try {
3507
3635
  const sweepResult = await runner.sweep(cutTree,
3508
3636
  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; },
3637
+ opts.pod ? { ...opts.pod, ...(opts.imagePin ? { imagePin: opts.imagePin } : {}), durationSource, ...(knownFailures.length ? { knownFailures } : {}), onSelectionTimed: ({ ms }) => { result.selectionMs = ms; },
3510
3638
  // GATE-RISK-LANE — the watch list is read from this gate's own ledgers (the instance root ⊕ the machine ledger);
3511
3639
  // the cut sweep puts those files first, the baseline sweep keeps its plan.
3512
3640
  riskWatch: { instanceRoots: roots, ledgerRoots: roots },
@@ -3608,7 +3736,7 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
3608
3736
  check(await runner.command('bun', ['install'], dir), `bun install ${dir}`);
3609
3737
  const rechecked = await runner.sweep(recheckTree,
3610
3738
  join(root, 'release', opts.version, 'gate-logs', 'baseline-attribution-recheck'),
3611
- opts.pod ? { ...opts.pod, durationSource } : undefined,
3739
+ opts.pod ? { ...opts.pod, ...(opts.imagePin ? { imagePin: opts.imagePin } : {}), durationSource } : undefined,
3612
3740
  targetFiles);
3613
3741
  if (rechecked.timedOut || rechecked.abortedShards || rechecked.stalledEnv?.length) {
3614
3742
  attribution.baselineResult = 'timeout';
@@ -3723,7 +3851,7 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
3723
3851
  // 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
3852
  // after a 23-minute Pod cut).
3725
3853
  const baseRun = await runner.sweep(baseTree!, join(root, 'release', opts.version, 'gate-logs', 'baseline'),
3726
- opts.pod ? { ...opts.pod, durationSource } : undefined,
3854
+ opts.pod ? { ...opts.pod, ...(opts.imagePin ? { imagePin: opts.imagePin } : {}), durationSource } : undefined,
3727
3855
  present);
3728
3856
  baseline = await localStalled(baseRun, baseTree!, baseSha, 'baseline');
3729
3857
  } else baseline = { failures: [], errors: [] };
@@ -4174,6 +4302,14 @@ export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOption
4174
4302
  const fromGraph: Record<string, unknown> = context?.input ?? {};
4175
4303
  const graphCommit = context?.outputs['version-release']?.commit;
4176
4304
  if (typeof graphCommit === 'string') fromGraph.commit = graphCommit;
4305
+ if (fromGraph.gateImagePin !== undefined && fromGraph.gateImagePin !== 'cut-commit') {
4306
+ throw new Error(`unsupported gate image pin: ${String(fromGraph.gateImagePin)}`);
4307
+ }
4308
+ // Policy-selected Pod gates must pin their container to the resolved cut commit. Host sweeps do
4309
+ // not consume a container image pin, and the legacy no-executor path retains its old behavior.
4310
+ if (fromGraph.gateExecutor === 'pod' && fromGraph.gateImagePin !== 'cut-commit') {
4311
+ throw new Error('gateImagePin cut-commit is required for a policy-selected release gate');
4312
+ }
4177
4313
  const baselineCommit = context?.outputs.cutoff?.baseline as { sha?: unknown } | undefined;
4178
4314
  if (typeof baselineCommit?.sha === 'string') fromGraph.previousCommit = baselineCommit.sha;
4179
4315
  if (fromGraph.gateExecutor !== undefined && !isGateExecutor(fromGraph.gateExecutor)) throw new Error(`gateExecutor must be pod or host: ${String(fromGraph.gateExecutor)}`);
@@ -4186,6 +4322,7 @@ export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOption
4186
4322
  || !['darwin', 'linux'].includes((value as { os?: string }).os ?? ''))))
4187
4323
  throw new Error('gateShardCapabilities must map pod-N to {os: darwin|linux}');
4188
4324
  const opts: GateOptions = {
4325
+ ...(fromGraph.gateImagePin === 'cut-commit' ? { imagePin: 'cut-commit' as const } : {}),
4189
4326
  rehearsalTui: fromGraph.rehearsalTui === true,
4190
4327
  recheckFileLimit: fromGraph.gateRecheckFileLimit !== undefined ? Number(fromGraph.gateRecheckFileLimit) : undefined,
4191
4328
  commit: typeof fromGraph.commit === 'string' ? fromGraph.commit : '',
@@ -4214,12 +4351,19 @@ export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOption
4214
4351
  };
4215
4352
  if (isGateExecutor(fromGraph.gateExecutor)) opts.executor = fromGraph.gateExecutor;
4216
4353
  let freshShards = false;
4354
+ let warmPrecutCanaryFile: string | undefined;
4217
4355
  let rerouteShard: string | undefined;
4218
4356
  let shardHost: string | undefined;
4219
4357
  for (let i = 0; i < args.length; i++) {
4220
4358
  const arg = args[i];
4221
4359
  if (arg === '--help') return 'help';
4222
4360
  if (arg === '--json') continue;
4361
+ if (arg === '--precut-canary-file') {
4362
+ const value = args[++i];
4363
+ if (!value || value.startsWith('--')) throw new Error('--precut-canary-file requires one test file');
4364
+ warmPrecutCanaryFile = value;
4365
+ continue;
4366
+ }
4223
4367
  if (arg === '--full') { opts.full = true; continue; }
4224
4368
  if (arg === '--rehearsal-baseline') { opts.rehearsalBaseline = true; continue; }
4225
4369
  // 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 +4408,7 @@ export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOption
4264
4408
  else if (key === 'remoteMirror') opts.remoteMirror = value;
4265
4409
  }
4266
4410
  if (freshShards && opts.pod) opts.pod = { ...opts.pod, freshShards: true };
4411
+ if (warmPrecutCanaryFile) opts.warmPrecutCanary = { file: warmPrecutCanaryFile };
4267
4412
  if ((rerouteShard || shardHost) && !opts.pod?.pool) throw new Error('shard reroute requires a Pod pool');
4268
4413
  if (shardHost && !rerouteShard) throw new Error('--shard-host requires --reroute-shard');
4269
4414
  if (opts.pod && rerouteShard) opts.pod = { ...opts.pod, rerouteShard, ...(shardHost ? { shardHost } : {}) };
@@ -4299,12 +4444,26 @@ export async function runGateNode(argv: string[], env: NodeJS.ProcessEnv, runner
4299
4444
  catch (error) { debug.log('release-loop.gate', 'warm-profile-unavailable', { error: String(error) }); }
4300
4445
  }
4301
4446
  const opts = parsed === 'help' ? parsed : withWarmGateHooks(parsed, config, !!runner);
4447
+ if (opts !== 'help' && opts.warmPrecutCanary) {
4448
+ const canaryRunner = runner ?? createGateRunner(opts.repo ?? process.cwd(), opts.remote);
4449
+ const warmPrecutTestSeam = (runner as (GateRunner & { warmPrecutCanaryTestSeam?: {
4450
+ config: unknown; hooks: Pick<NonNullable<GateOptions['pod']>, 'warmReplay' | 'warmFinalize' | 'warmQuarantine' | 'warmReuseConfig'>;
4451
+ } }) | undefined)?.warmPrecutCanaryTestSeam;
4452
+ const canaryOpts = warmPrecutTestSeam && opts.pod ? { ...opts, pod: { ...opts.pod, ...warmPrecutTestSeam.hooks } } : opts;
4453
+ const canary = await runWarmPrecutCanary({ opts: canaryOpts, runner: canaryRunner, file: opts.warmPrecutCanary.file,
4454
+ config: warmPrecutTestSeam?.config ?? config,
4455
+ historyPath: verdictFile(opts.ledgerRoot ?? releaseLedgerRoot(), opts.version) });
4456
+ const output = { version: opts.version, commit: opts.commit, samples: canary.samples, decision: canary.decision };
4457
+ console.log(JSON.stringify(output));
4458
+ return 0;
4459
+ }
4302
4460
  if (opts !== 'help') {
4303
4461
  placement = gateExecutorOf(opts);
4304
- debug.log('release-loop.gate', 'executor', { version: opts.version, ...placement, requested: opts.executor ?? null });
4462
+ debug.log('release-loop.gate', 'executor', { version: opts.version, ...placement, requested: opts.executor ?? null,
4463
+ imagePin: opts.imagePin ?? null, imageCommit: opts.imagePin === 'cut-commit' ? opts.commit : null });
4305
4464
  }
4306
4465
  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.');
4466
+ 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
4467
  return 0;
4309
4468
  }
4310
4469
  version = opts.version;
@@ -4363,5 +4522,6 @@ if (import.meta.main) {
4363
4522
  // The graph spawns this file as its own process, which inherits no daemon sink — without this every
4364
4523
  // `release-loop.gate`/`release.gate`/`pod.pool` event stayed in the file trail and `elanous logs` read 0 rows.
4365
4524
  await registerStandaloneLogSink('release-loop');
4366
- process.exitCode = await runWithReleaseGateTraffic(process.env, () => runGateNode(process.argv.slice(2), process.env));
4525
+ process.exitCode = await runWithReleaseGateTraffic(process.env,
4526
+ () => runWithReleasePwaTraffic(process.env, () => runGateNode(process.argv.slice(2), process.env)));
4367
4527
  }
@@ -1,11 +1,18 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, rmdirSync, statSync, writeFileSync } from 'node:fs';
2
+ import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, rmdirSync, statSync, writeFileSync } from 'node:fs';
3
3
  import { homedir } from 'node:os';
4
- import { join } from 'node:path';
4
+ import { basename, dirname, join } from 'node:path';
5
5
  import { debug } from '../../src/debug/log.js';
6
6
 
7
- type TrafficBoard = Record<string, unknown> & { global: string };
7
+ type TrafficBoard = Record<string, unknown> & { global: string; seats?: Record<string, Record<string, unknown>> };
8
8
  type PatchResult = { changed: true; previous: string; warning?: string } | { changed: false; current: string };
9
+ type PwaSnapshot = { reason: unknown; hadReason: boolean; updatedAt: unknown; hadUpdatedAt: boolean; activeUpdatedAt: string;
10
+ heavyLocalBan: unknown; hadHeavyLocalBan: boolean;
11
+ perCycle: Record<string, { value: unknown; hadValue: boolean }> };
12
+ type PwaPatchResult = { changed: true; previous: PwaSnapshot; warning?: string };
13
+ const RELEASE_SEATS = ['OP', 'TC', 'MK', 'UX'] as const;
14
+ const PWA_PER_CYCLE = 3;
15
+ const MAX_PWA_TRAFFIC_BACKUPS_PER_KIND = 10;
9
16
 
10
17
  const defaultTrafficPath = () => join(homedir(), 'elanous-hq', 'seat-state', 'OP', 'traffic.json');
11
18
  const trafficPath = (env: NodeJS.ProcessEnv) => env.ELANOUS_TRAFFIC_SIGNAL || defaultTrafficPath();
@@ -21,6 +28,17 @@ function releaseGateContext(env: NodeJS.ProcessEnv): boolean {
21
28
  }
22
29
  }
23
30
 
31
+ function releasePwaContext(env: NodeJS.ProcessEnv): boolean {
32
+ const source = env.ELANOUS_GRAPH_CONTEXT;
33
+ if (!source) return false;
34
+ try {
35
+ const raw = JSON.parse(source.trimStart().startsWith('{') ? source : readFileSync(source, 'utf8')) as Record<string, unknown>;
36
+ return raw.graphId === 'release-loop' && raw.nodeId === 'pwa';
37
+ } catch {
38
+ return false;
39
+ }
40
+ }
41
+
24
42
  function withTrafficLock<T>(path: string, action: () => T): T {
25
43
  const lock = `${path}.lock.d`;
26
44
  const deadline = Date.now() + 10_000;
@@ -86,6 +104,136 @@ function patchGlobal(path: string, next: string, reason: string, onlyIf?: string
86
104
  });
87
105
  }
88
106
 
107
+ function writeTrafficBoard(path: string, after: TrafficBoard, suffix: string): void {
108
+ const at = new Date();
109
+ const stamp = [at.getMonth() + 1, at.getDate(), at.getHours(), at.getMinutes(), at.getSeconds()].map(value => String(value).padStart(2, '0')).join('');
110
+ writeFileSync(`${path}.bak-${stamp}-${randomUUID().slice(0, 8)}-${suffix}`, readFileSync(path));
111
+ const temp = `${path}.tmp.${process.pid}.${randomUUID()}`;
112
+ try {
113
+ writeFileSync(temp, `${JSON.stringify(after, null, 2)}\n`, { mode: statSync(path).mode & 0o777 });
114
+ renameSync(temp, path);
115
+ } finally { rmSync(temp, { force: true }); }
116
+ // Re-read after the atomic rename so a malformed write is visible to the caller.
117
+ JSON.parse(readFileSync(path, 'utf8')) as TrafficBoard;
118
+ if (suffix.startsWith('release-pwa-')) {
119
+ try { pruneTrafficBackups(path, suffix); }
120
+ catch (error) { debug.log('release-loop.pwa', 'traffic backup pruning failed', { path, suffix, error: String(error) }, { level: 'warn' }); }
121
+ }
122
+ }
123
+
124
+ function pruneTrafficBackups(path: string, suffix: string): void {
125
+ const directory = dirname(path);
126
+ const prefix = `${basename(path)}.bak-`;
127
+ const backups = readdirSync(directory)
128
+ .filter(name => name.startsWith(prefix) && name.endsWith(`-${suffix}`))
129
+ .map(name => ({ path: join(directory, name), modifiedAt: statSync(join(directory, name)).mtimeMs }))
130
+ .sort((a, b) => a.modifiedAt - b.modifiedAt);
131
+ for (const backup of backups.slice(0, Math.max(0, backups.length - MAX_PWA_TRAFFIC_BACKUPS_PER_KIND))) {
132
+ rmSync(backup.path, { force: true });
133
+ }
134
+ }
135
+
136
+ function patchPwaTraffic(path: string): PwaPatchResult {
137
+ return withTrafficLock(path, () => {
138
+ if (!existsSync(path)) throw new Error(`traffic board missing: ${path}`);
139
+ const board = JSON.parse(readFileSync(path, 'utf8')) as TrafficBoard;
140
+ if (!board || typeof board !== 'object' || Array.isArray(board) || typeof board.global !== 'string'
141
+ || !board.seats || typeof board.seats !== 'object' || Array.isArray(board.seats)
142
+ || !RELEASE_SEATS.every(seat => board.seats![seat] && typeof board.seats![seat] === 'object' && !Array.isArray(board.seats![seat]))) {
143
+ throw new Error(`invalid traffic board for PWA policy: ${path}`);
144
+ }
145
+ const activeUpdatedAt = new Date().toISOString();
146
+ const previous: PwaSnapshot = {
147
+ hadReason: Object.hasOwn(board, 'reason'), reason: board.reason,
148
+ hadUpdatedAt: Object.hasOwn(board, 'updatedAt'), updatedAt: board.updatedAt, activeUpdatedAt,
149
+ hadHeavyLocalBan: Object.hasOwn(board, 'heavyLocalBan'), heavyLocalBan: board.heavyLocalBan,
150
+ perCycle: Object.fromEntries(RELEASE_SEATS.map(seat => [seat, {
151
+ hadValue: Object.hasOwn(board.seats![seat]!, 'perCycle'), value: board.seats![seat]!.perCycle,
152
+ }])),
153
+ };
154
+ const updated: TrafficBoard = {
155
+ ...board,
156
+ ...(board.heavyLocalBan === true ? {} : { reason: 'release PWA node running (heavy-local banned)' }),
157
+ heavyLocalBan: true,
158
+ seats: Object.fromEntries(Object.entries(board.seats).map(([seat, value]) => [seat,
159
+ RELEASE_SEATS.includes(seat as typeof RELEASE_SEATS[number]) ? { ...value, perCycle: PWA_PER_CYCLE } : value])),
160
+ updatedAt: activeUpdatedAt,
161
+ };
162
+ writeTrafficBoard(path, updated, 'release-pwa-policy');
163
+ return { changed: true, previous };
164
+ });
165
+ }
166
+
167
+ function restorePwaTraffic(path: string, previous: PwaSnapshot): { restored: boolean; reason?: string } {
168
+ return withTrafficLock(path, () => {
169
+ const board = JSON.parse(readFileSync(path, 'utf8')) as TrafficBoard;
170
+ if (!board || typeof board !== 'object' || Array.isArray(board) || !board.seats || typeof board.seats !== 'object') {
171
+ throw new Error(`invalid traffic board while restoring PWA policy: ${path}`);
172
+ }
173
+ // If another writer removed/replaced our marker, that writer owns the current policy.
174
+ if (board.heavyLocalBan !== true) return { restored: false, reason: 'heavyLocalBan-changed' };
175
+ const seats = { ...board.seats };
176
+ for (const seat of RELEASE_SEATS) {
177
+ const current = seats[seat];
178
+ const snapshot = previous.perCycle[seat];
179
+ if (!current || current.perCycle !== PWA_PER_CYCLE) continue;
180
+ const restored = { ...current };
181
+ if (snapshot?.hadValue) restored.perCycle = snapshot.value;
182
+ else delete restored.perCycle;
183
+ seats[seat] = restored;
184
+ }
185
+ const updated: TrafficBoard = { ...board, seats };
186
+ if (previous.hadHeavyLocalBan) updated.heavyLocalBan = previous.heavyLocalBan;
187
+ else delete updated.heavyLocalBan;
188
+ if (updated.reason === 'release PWA node running (heavy-local banned)') {
189
+ if (previous.hadReason) updated.reason = previous.reason;
190
+ else delete updated.reason;
191
+ }
192
+ if (board.updatedAt === previous.activeUpdatedAt) {
193
+ if (previous.hadUpdatedAt) updated.updatedAt = previous.updatedAt;
194
+ else delete updated.updatedAt;
195
+ }
196
+ writeTrafficBoard(path, updated, 'release-pwa-restore');
197
+ return { restored: true };
198
+ });
199
+ }
200
+
201
+ /** Apply the release PWA node's temporary half-traffic and heavy-local marker, restoring on every exit. */
202
+ export async function runWithReleasePwaTraffic<T>(env: NodeJS.ProcessEnv, run: () => Promise<T>): Promise<T> {
203
+ if (!releasePwaContext(env)) return run();
204
+ const path = trafficPath(env);
205
+ let started: PwaPatchResult;
206
+ try { started = patchPwaTraffic(path); }
207
+ catch (error) {
208
+ debug.log('release-loop.pwa', 'traffic policy set failed', { path, error: String(error) }, { level: 'warn' });
209
+ throw new Error(`release PWA traffic policy could not be applied: ${String(error)}`, { cause: error });
210
+ }
211
+ if (started.warning) debug.log('release-loop.pwa', 'traffic policy verification failed', { path, error: started.warning }, { level: 'warn' });
212
+ let restoreResult: Promise<void> | undefined;
213
+ const restoreOnce = () => restoreResult ??= Promise.resolve().then(() => {
214
+ try {
215
+ const restored = restorePwaTraffic(path, started.previous);
216
+ if (!restored.restored) debug.log('release-loop.pwa', 'traffic policy restore skipped', { path, reason: restored.reason }, { level: 'warn' });
217
+ } catch (error) { debug.log('release-loop.pwa', 'traffic policy restore failed', { path, error: String(error) }, { level: 'warn' }); }
218
+ });
219
+ const interrupt = (signal: NodeJS.Signals) => {
220
+ void restoreOnce().finally(() => {
221
+ process.removeListener(signal, signal === 'SIGINT' ? onSigint : onSigterm);
222
+ process.kill(process.pid, signal);
223
+ });
224
+ };
225
+ const onSigint = () => interrupt('SIGINT');
226
+ const onSigterm = () => interrupt('SIGTERM');
227
+ process.once('SIGINT', onSigint);
228
+ process.once('SIGTERM', onSigterm);
229
+ try { return await run(); }
230
+ finally {
231
+ process.removeListener('SIGINT', onSigint);
232
+ process.removeListener('SIGTERM', onSigterm);
233
+ await restoreOnce();
234
+ }
235
+ }
236
+
89
237
  /** Hold dispatcher traffic at gate only for the release-loop's gate node, then restore conditionally. */
90
238
  export async function runWithReleaseGateTraffic<T>(env: NodeJS.ProcessEnv, run: () => Promise<T>): Promise<T> {
91
239
  if (!releaseGateContext(env)) return run();
@@ -3,6 +3,7 @@ import { spawnSync } from 'node:child_process';
3
3
  import { isReleaseVersion } from './release-version.js';
4
4
  import { readFileSync } from 'node:fs';
5
5
  import { debug } from '../../src/debug/log.js';
6
+ import { runWithReleasePwaTraffic } from './gate-traffic-mode.js';
6
7
 
7
8
  export type NodeOutcome = 'ok' | 'fail' | 'error';
8
9
  export type NodeVerdict = 'pass' | 'flaky' | 'fail';
@@ -86,30 +87,42 @@ export function pwaNodeArgs(commit: string, input: Record<string, unknown>): str
86
87
  }
87
88
 
88
89
  // Existing cutoff/PWA/TUI programs have different output conventions; normalize them at the graph boundary.
89
- if (import.meta.main) {
90
- const node = process.argv[2];
90
+ export async function runNodeVerdict(argv: string[], env: NodeJS.ProcessEnv = process.env,
91
+ runner: CommandRunner = runCommand): Promise<number> {
92
+ const node = argv[0];
91
93
  let version = '';
92
- let result: Record<string, unknown> & { outcome: NodeOutcome; verdict: NodeVerdict; summary: string };
93
- try {
94
- const context = readGraphContext();
95
- version = context.input.version;
96
- const commit = nodeOutput(context, 'version-release', 'commit');
97
- const predecessor = node === 'pwa' ? 'gate' : node === 'tui' ? 'upgrade' : 'version-release';
98
- if (context.outputs[predecessor]?.outcome !== 'ok') throw new Error(`${predecessor} did not pass`);
99
- const args = node === 'cutoff' ? ['scripts/release-loop/cutoff.ts', '--version', version, '--cutoff', commit, '--baseline', `v${context.input.previousVersion}`, '--json']
100
- : node === 'pwa' ? pwaNodeArgs(commit, context.input)
101
- : node === 'tui' ? ['scripts/release-loop/tui-sim-node.ts', '--commit', commit, '--json'] : (() => { throw new Error(`unknown node: ${node}`); })();
102
- const run = runCommand('bun', args);
103
- if (run.stderr) process.stderr.write(run.stderr);
104
- const data = lastResult(run);
105
- const verdict = data?.verdict;
106
- const baseline = data?.baseline as { sha?: string } | undefined;
107
- const outcome: NodeOutcome = node === 'cutoff' ? run.status === 0 && data?.version === version && (data?.cutoff as { sha?: string } | undefined)?.sha === commit && typeof baseline?.sha === 'string' ? 'ok' : 'error'
108
- : run.status === 0 && (verdict === 'pass' || verdict === 'flaky') ? 'ok' : run.status === 1 ? 'fail' : 'error';
109
- if (node === 'cutoff' && outcome === 'ok' && cutoffNotesEmpty(data)) {
110
- result = { ...data, outcome: 'fail', verdict: 'fail', summary: 'cutoff notes-empty — 사용자에게 보일 변경 줄이 0이다(release/next.md 와 PR 노트 조각을 확인하라)' };
111
- } else result = { ...data, outcome, verdict: outcome === 'ok' ? verdict === 'flaky' ? 'flaky' : 'pass' : 'fail',
112
- summary: outcome === 'ok' ? `${node} ${verdict === 'flaky' ? 'flaky' : 'pass'}` : `${node} ${data?.error || run.stderr.trim() || data?.verdict || 'failed'} (rc=${run.status})` };
113
- } catch (error) { result = errorResult(error); }
114
- process.exitCode = finishNode(node ?? 'unknown', version, result);
94
+ const execute = async (): Promise<number> => {
95
+ let result: Record<string, unknown> & { outcome: NodeOutcome; verdict: NodeVerdict; summary: string };
96
+ try {
97
+ const context = readGraphContext(env);
98
+ version = context.input.version;
99
+ const commit = nodeOutput(context, 'version-release', 'commit');
100
+ const predecessor = node === 'pwa' ? 'gate' : node === 'tui' ? 'upgrade' : 'version-release';
101
+ if (context.outputs[predecessor]?.outcome !== 'ok') throw new Error(`${predecessor} did not pass`);
102
+ const args = node === 'cutoff' ? ['scripts/release-loop/cutoff.ts', '--version', version, '--cutoff', commit, '--baseline', `v${context.input.previousVersion}`, '--json']
103
+ : node === 'pwa' ? pwaNodeArgs(commit, context.input)
104
+ : node === 'tui' ? ['scripts/release-loop/tui-sim-node.ts', '--commit', commit, '--json'] : (() => { throw new Error(`unknown node: ${node}`); })();
105
+ const run = runner('bun', args);
106
+ if (run.stderr) process.stderr.write(run.stderr);
107
+ const data = lastResult(run);
108
+ const verdict = data?.verdict;
109
+ const baseline = data?.baseline as { sha?: string } | undefined;
110
+ const outcome: NodeOutcome = node === 'cutoff' ? run.status === 0 && data?.version === version && (data?.cutoff as { sha?: string } | undefined)?.sha === commit && typeof baseline?.sha === 'string' ? 'ok' : 'error'
111
+ : run.status === 0 && (verdict === 'pass' || verdict === 'flaky') ? 'ok' : run.status === 1 ? 'fail' : 'error';
112
+ if (node === 'cutoff' && outcome === 'ok' && cutoffNotesEmpty(data)) {
113
+ result = { ...data, outcome: 'fail', verdict: 'fail', summary: 'cutoff notes-empty — 사용자에게 보일 변경 줄이 0이다(release/next.md 와 PR 노트 조각을 확인하라)' };
114
+ } else result = { ...data, outcome, verdict: outcome === 'ok' ? verdict === 'flaky' ? 'flaky' : 'pass' : 'fail',
115
+ summary: outcome === 'ok' ? `${node} ${verdict === 'flaky' ? 'flaky' : 'pass'}` : `${node} ${data?.error || run.stderr.trim() || data?.verdict || 'failed'} (rc=${run.status})` };
116
+ } catch (error) { result = errorResult(error); }
117
+ return finishNode(node ?? 'unknown', version, result);
118
+ };
119
+
120
+ if (node !== 'pwa') return execute();
121
+ try { return await runWithReleasePwaTraffic(env, execute); }
122
+ catch (error) {
123
+ try { version = readGraphContext(env).input.version; } catch { /* preserve empty version when context itself is invalid */ }
124
+ return finishNode(node, version, errorResult(error));
125
+ }
115
126
  }
127
+
128
+ if (import.meta.main) process.exitCode = await runNodeVerdict(process.argv.slice(2), process.env);