elanous 0.2.32 → 0.2.35

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 (753) 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/{IVKr3AlRUVLKsw2fh7eXF → T1mBQqmkwKDLGtyisDCOd}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{997ce8a5-2ad501927003b41b.js → 0368f25d-ad84af92fefee68f.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/1165.bad13fc9151a7288.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/13.02a07436dec15f89.js +32 -0
  7. package/apps/pwa/out/_next/static/chunks/{5525.86debf231e181813.js → 1530.0e612369b6fd7ea0.js} +1 -1
  8. package/apps/pwa/out/_next/static/chunks/1540.af38731fa7c4a3e0.js +41 -0
  9. package/apps/pwa/out/_next/static/chunks/{7391.ebae168025c91b6a.js → 1674.2a65fd5668e1cc5f.js} +1 -1
  10. package/apps/pwa/out/_next/static/chunks/1850.f2b470ba5a471da0.js +24 -0
  11. package/apps/pwa/out/_next/static/chunks/193.62da94cd0b88323b.js +74 -0
  12. package/apps/pwa/out/_next/static/chunks/1975.260f6d249fac5f7c.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/1982-67cd8dfe8682325f.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/{7947-d0759430f8042240.js → 1993-c0007b7ad0c0ef41.js} +1 -1
  15. package/apps/pwa/out/_next/static/chunks/1999-bb415351c8a2846c.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/{60fb96c9.360b01d8d4927b7b.js → 1c735de4.f993a30bbd3c57c4.js} +1 -1
  17. package/apps/pwa/out/_next/static/chunks/1dc5a567.9b092bfae3d7099b.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/{5307.60f2209e809bd627.js → 2018.a741297bce759490.js} +1 -1
  19. package/apps/pwa/out/_next/static/chunks/2085.8f950b4bda1cbbc7.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/2159-6179a961376956b9.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/216.b5c2a421aa1d28a5.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/2251.49fbe369f74c372c.js +148 -0
  23. package/apps/pwa/out/_next/static/chunks/2286-2939577360d3dcc0.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/{3902.e1e71aaac850a045.js → 2625.5d870aa999f33db1.js} +1 -1
  25. package/apps/pwa/out/_next/static/chunks/2668.34d21539e62bf111.js +77 -0
  26. package/apps/pwa/out/_next/static/chunks/2833-0fe47af99ed13c29.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/2930.d0edb4f108787ef7.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2973.77318e7d85f59477.js +62 -0
  29. package/apps/pwa/out/_next/static/chunks/3103.0df8da390948bb84.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/3123.2bee9ba15d320bd2.js +90 -0
  31. package/apps/pwa/out/_next/static/chunks/3162-6cf652bf7124d8cf.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/{4174.92ed04922709ac9e.js → 3408.21d1570602f4b6c0.js} +2 -2
  33. package/apps/pwa/out/_next/static/chunks/3577-7340009f7007a795.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/3589.5444a06bcf6e775c.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/359-2250789a2d3499e7.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/3618.f514792d6c59d7fd.js +36 -0
  37. package/apps/pwa/out/_next/static/chunks/3669.343f9119aa276001.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/{9398-6aed28f0a96d0d6f.js → 3677-4215f826f6aa68e5.js} +1 -1
  39. package/apps/pwa/out/_next/static/chunks/3736.c2efad489604cc3b.js +79 -0
  40. package/apps/pwa/out/_next/static/chunks/3817.f9aeab2bb6675d73.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/3f7c4ee5.3dde54f01a816613.js +122 -0
  42. package/apps/pwa/out/_next/static/chunks/4182-0b9b1251c29cc0cf.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/{7371.80c05c653b08e907.js → 4346.8601952e73bf5803.js} +1 -1
  44. package/apps/pwa/out/_next/static/chunks/4441-875b97f5821ed58a.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/4582.bd68683c076649e3.js +286 -0
  46. package/apps/pwa/out/_next/static/chunks/4605-6e623f55aec77422.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/4607.5cc27468628eb4bf.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/4627.f07508b3936fd6c0.js +24 -0
  49. package/apps/pwa/out/_next/static/chunks/{2940-619bfd8ef337c611.js → 4705-194e64e47ca0fbdb.js} +1 -1
  50. package/apps/pwa/out/_next/static/chunks/{1335-6731f392c35216eb.js → 4718-17d2af636796a330.js} +3 -3
  51. package/apps/pwa/out/_next/static/chunks/4747-c22262c093e46807.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/4775-178215aed0daba61.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/4815-6ffb8c3c991cc45d.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/4834-beca8b47ad29a898.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/5023-fb4802b6d2492b02.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/{7502.ccd5b8065545edd0.js → 5101.e32282718f99ed6f.js} +2 -2
  57. package/apps/pwa/out/_next/static/chunks/{2001.6c7e0fd02190886c.js → 5154.a118f3957ed14e36.js} +1 -1
  58. package/apps/pwa/out/_next/static/chunks/5210-a2e0015e3d4d5d9c.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/5356.84ac712cc15928bf.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/5401.22352141b6fd336e.js +23 -0
  61. package/apps/pwa/out/_next/static/chunks/{5173-233188df2f42c626.js → 5483-dd5d7697f6bdb106.js} +1 -1
  62. package/apps/pwa/out/_next/static/chunks/5637.281e5dec4ea6ecf1.js +94 -0
  63. package/apps/pwa/out/_next/static/chunks/5662.02eaa029af05ad46.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/{3267c0e6.d34854e7b3984a6e.js → 56d372f2.681ab4ec4c998fae.js} +1 -1
  65. package/apps/pwa/out/_next/static/chunks/5760.22456f433c6f42c0.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/5852.db1f7449f4f74612.js +287 -0
  67. package/apps/pwa/out/_next/static/chunks/5891-0f11a7d7362a9072.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/5943.264d14a44ca32513.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/604-1b3c39df083c02aa.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/6121-5f97072330738caf.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/6193.07d8ca5af4b09086.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/6203-b3fea3a60d69ff14.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/{6433.fc573af22bbc9ab7.js → 6236.e18cced2d5ba6afb.js} +17 -17
  74. package/apps/pwa/out/_next/static/chunks/6286.d638fb5953e2e757.js +8 -0
  75. package/apps/pwa/out/_next/static/chunks/630.fe5a46428d63b013.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/6301.ae0245f507e4395e.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/6673.fbbe5699f6ea5991.js +27 -0
  78. package/apps/pwa/out/_next/static/chunks/6777-f6d301efb5de828d.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/6786.423f24c88de034c7.js +93 -0
  80. package/apps/pwa/out/_next/static/chunks/6854-cef64a948c5e122d.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/6866-79aa5b4e47cac6aa.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/{6544cb48.a87fe20ddf137be2.js → 6b89c8bd.24df687a820500b3.js} +1 -1
  83. package/apps/pwa/out/_next/static/chunks/7037-fe27f001db30d323.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/7080.2d826860534fd88e.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/7118.8731be28c3ebb14b.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/7196.44241bc1df3e26fe.js +149 -0
  87. package/apps/pwa/out/_next/static/chunks/7235.c7c4e6cb5c693c2d.js +104 -0
  88. package/apps/pwa/out/_next/static/chunks/{2871.0bca6137343b3f76.js → 7385.50a308a477bdfc21.js} +1 -1
  89. package/apps/pwa/out/_next/static/chunks/{2985.39ccdecd36848b12.js → 7644.17e37ba6f1d4226e.js} +4 -4
  90. package/apps/pwa/out/_next/static/chunks/7706-58bfb1546637f217.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/7839.5b91455338c9fd3f.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/7857-31f13ee96f4011aa.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/81.f74cb60312fe4001.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/{8018.a32663f04a6fc5f9.js → 8219.baa715d1deee80e2.js} +1 -1
  95. package/apps/pwa/out/_next/static/chunks/{4111.2bfedbb233ad206b.js → 8256.e3ac8421a71f82f4.js} +1 -1
  96. package/apps/pwa/out/_next/static/chunks/8310-444a6a895c402f12.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/8423.f7dda8aca4fcaae5.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/8437-8d5492094b166cfa.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/{2213.2ccbada92c33ea0f.js → 8444.4c26b379ca2b9596.js} +17 -17
  100. package/apps/pwa/out/_next/static/chunks/8496.75649af8652c74f4.js +226 -0
  101. package/apps/pwa/out/_next/static/chunks/8557-e7898769499cd56f.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/8576.65bb654b524f3614.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/8609.558ce2f8f9aea2be.js +64 -0
  104. package/apps/pwa/out/_next/static/chunks/8614.caec96cfb91f4ae7.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/{6092.a7f99e68b188ca66.js → 8637.47f5b35afcae5e6b.js} +1 -1
  106. package/apps/pwa/out/_next/static/chunks/8714.ff0b7ce3ec3e3ae2.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/8732.367f44a916202353.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/9141-7fd1f73a5400bdc2.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/921.c4267d0d69a65404.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/{3278.2e5d15b53a4091c8.js → 9214.63a510d39178e997.js} +2 -2
  111. package/apps/pwa/out/_next/static/chunks/9261.3962c3321b047691.js +115 -0
  112. package/apps/pwa/out/_next/static/chunks/936-26efd1a0efdd44d3.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/{9221.7d22eaf353a2d4e7.js → 938.66ad1ef0e5abae1c.js} +1 -1
  114. package/apps/pwa/out/_next/static/chunks/{6157.0c9af021de4fb31b.js → 9512.5a76207a730e0491.js} +2 -2
  115. package/apps/pwa/out/_next/static/chunks/9627-4b9efe616b27e2e2.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/{69-0a2c8209c6d4a158.js → 967-2aa39b2d04b4c690.js} +1 -1
  117. package/apps/pwa/out/_next/static/chunks/9734.40eb1e558c29ccea.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/9908.3aa54f7a586a73dc.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/{7862.30a8d6482bb238eb.js → 9952.5daf5f94063a2b3e.js} +1 -1
  120. package/apps/pwa/out/_next/static/chunks/9954-ca8d29752bdbb45a.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/9974-507fa67cb94066dd.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-ce9e00f7fb45506a.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/app/approvals/page-fa59d6d17daa3389.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-53f68bbe7bd4b774.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/board/page-1c500fbb9f4a7a04.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/botlab/page-99d72673aa5c307c.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/bots/page-d2d5adc1e9484c88.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/ceo/page-c607c5aae637f1cc.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/chat/page-fa8c23596d555c0f.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/consult/page-bdab8c5e855c8416.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/control/{page-a27b1e5a0d1b308f.js → page-406b47745b53f269.js} +1 -1
  132. package/apps/pwa/out/_next/static/chunks/app/decisions/page-a4276386b7051f50.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/design-check/page-8a26bab9ad375f25.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/editor/page-775f48aa1dc7a2d5.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/exec/page-1c4dd5f959d0c558.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/field/page-c48938a8377c9f70.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/healthz/route-ce9e00f7fb45506a.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/inside/page-c6e7905859425aa9.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/intake/page-c280f4252452e830.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/layout-cf8c57498f20970a.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/live/page-ca3a17caf409fc83.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/live-run/page-26e6c18a1a750cf6.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/loops/page-3abc872cc6aeee39.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/market/page-f7481b7b2174fe3d.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1ae7ef8077680337.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/morning/{page-32866485913d6a04.js → page-47c5d4db587266bd.js} +1 -1
  147. package/apps/pwa/out/_next/static/chunks/app/not-found-415f1e243c7ccb13.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/observatory/page-73f380f17b76f95c.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-2991073ad66ee2d9.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-1f5a60f70cb4dd0c.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fbad60d1e358fb2c.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/outputs/page-bac7dee94e5eae4a.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/page-398047d6949e8ca5.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/reflection/page-2d33a2231540ca89.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-5bf1e6bf70bcb96c.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/sessions/page-ba59c19376a0917d.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-111f187139add30f.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/settings/page-acaae197d5be5779.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0df20b0f546a0d7d.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/setup/layout-ce9e00f7fb45506a.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/page-27e1202d24b8fe35.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/share/page-bda78c50a78363e5.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/showroom/page-da592691293d9c9f.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f832994317a2e9f0.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/term/{page-04d00ae9b0890349.js → page-dc0a532f5b9196ec.js} +1 -1
  166. package/apps/pwa/out/_next/static/chunks/app/today/page-cfccbbeae0473404.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/trace/page-527e2073af84c81b.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/vault/page-bac86e9112671357.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6d59dff410e9d76a.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/workflows/page-53a61f8be09f02d2.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workspace/page-db8f559e763c17f9.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c02aa4cc66455093.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/{7a1887c4.f36b88fba31da8fd.js → b2f6ba4d.fb56fdbbf798df33.js} +1 -1
  174. package/apps/pwa/out/_next/static/chunks/b6705a4f-b9a9047de9eecfd0.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/{b313b242.cb1265eea908db61.js → cbe38a15.5a032598e39970c6.js} +1 -1
  176. package/apps/pwa/out/_next/static/chunks/{409f1e57.d0649fc47691ebfe.js → d06f9160.a3d9062f95de8ad7.js} +1 -1
  177. package/apps/pwa/out/_next/static/chunks/{0bb0ff36.b8d418980951e5ba.js → da5176c6.6773b6c5994e1d99.js} +7 -7
  178. package/apps/pwa/out/_next/static/chunks/framework-50e6acebc630849b.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-7a1ae342d5ed75e6.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/main-app-7ad7d505ba1f92b4.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_app-02edba75232cb235.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_error-97d6ebae3e8eee4a.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/webpack-e986adaa429163e8.js +1 -0
  184. package/apps/pwa/out/approvals/index.html +1 -1
  185. package/apps/pwa/out/approvals/index.txt +22 -22
  186. package/apps/pwa/out/autopilot/index.html +1 -1
  187. package/apps/pwa/out/autopilot/index.txt +22 -22
  188. package/apps/pwa/out/board/index.html +1 -1
  189. package/apps/pwa/out/board/index.txt +22 -22
  190. package/apps/pwa/out/botlab/index.html +1 -1
  191. package/apps/pwa/out/botlab/index.txt +21 -21
  192. package/apps/pwa/out/bots/index.html +1 -1
  193. package/apps/pwa/out/bots/index.txt +21 -21
  194. package/apps/pwa/out/ceo/index.html +1 -1
  195. package/apps/pwa/out/ceo/index.txt +22 -22
  196. package/apps/pwa/out/chat/index.html +1 -1
  197. package/apps/pwa/out/chat/index.txt +21 -21
  198. package/apps/pwa/out/consult/index.html +1 -1
  199. package/apps/pwa/out/consult/index.txt +22 -22
  200. package/apps/pwa/out/control/index.html +1 -1
  201. package/apps/pwa/out/control/index.txt +22 -22
  202. package/apps/pwa/out/decisions/index.html +1 -1
  203. package/apps/pwa/out/decisions/index.txt +21 -21
  204. package/apps/pwa/out/design-check/index.html +1 -1
  205. package/apps/pwa/out/design-check/index.txt +22 -22
  206. package/apps/pwa/out/editor/index.html +1 -1
  207. package/apps/pwa/out/editor/index.txt +22 -22
  208. package/apps/pwa/out/exec/index.html +1 -1
  209. package/apps/pwa/out/exec/index.txt +22 -22
  210. package/apps/pwa/out/field/index.html +1 -1
  211. package/apps/pwa/out/field/index.txt +22 -22
  212. package/apps/pwa/out/healthz +1 -1
  213. package/apps/pwa/out/index.html +1 -1
  214. package/apps/pwa/out/index.txt +21 -21
  215. package/apps/pwa/out/inside/index.html +1 -1
  216. package/apps/pwa/out/inside/index.txt +21 -21
  217. package/apps/pwa/out/intake/index.html +1 -1
  218. package/apps/pwa/out/intake/index.txt +22 -22
  219. package/apps/pwa/out/live/index.html +1 -1
  220. package/apps/pwa/out/live/index.txt +22 -22
  221. package/apps/pwa/out/live-run/index.html +1 -1
  222. package/apps/pwa/out/live-run/index.txt +22 -22
  223. package/apps/pwa/out/loops/index.html +1 -1
  224. package/apps/pwa/out/loops/index.txt +21 -21
  225. package/apps/pwa/out/market/index.html +1 -1
  226. package/apps/pwa/out/market/index.txt +21 -21
  227. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  228. package/apps/pwa/out/missions/placeholder/index.txt +21 -21
  229. package/apps/pwa/out/morning/index.html +1 -1
  230. package/apps/pwa/out/morning/index.txt +22 -22
  231. package/apps/pwa/out/observatory/index.html +1 -1
  232. package/apps/pwa/out/observatory/index.txt +22 -22
  233. package/apps/pwa/out/ops/checklist/index.html +1 -1
  234. package/apps/pwa/out/ops/checklist/index.txt +21 -21
  235. package/apps/pwa/out/ops/release/index.html +1 -1
  236. package/apps/pwa/out/ops/release/index.txt +21 -21
  237. package/apps/pwa/out/ops/seats/index.html +1 -1
  238. package/apps/pwa/out/ops/seats/index.txt +21 -21
  239. package/apps/pwa/out/outputs/index.html +1 -1
  240. package/apps/pwa/out/outputs/index.txt +21 -21
  241. package/apps/pwa/out/reflection/index.html +1 -1
  242. package/apps/pwa/out/reflection/index.txt +22 -22
  243. package/apps/pwa/out/scheduler/index.html +1 -1
  244. package/apps/pwa/out/scheduler/index.txt +22 -22
  245. package/apps/pwa/out/sessions/index.html +1 -1
  246. package/apps/pwa/out/sessions/index.txt +21 -21
  247. package/apps/pwa/out/settings/devices/index.html +1 -1
  248. package/apps/pwa/out/settings/devices/index.txt +22 -22
  249. package/apps/pwa/out/settings/index.html +1 -1
  250. package/apps/pwa/out/settings/index.txt +22 -22
  251. package/apps/pwa/out/setup/done/index.html +1 -1
  252. package/apps/pwa/out/setup/done/index.txt +22 -22
  253. package/apps/pwa/out/setup/index.html +1 -1
  254. package/apps/pwa/out/setup/index.txt +22 -22
  255. package/apps/pwa/out/share/index.html +1 -1
  256. package/apps/pwa/out/share/index.txt +22 -22
  257. package/apps/pwa/out/showroom/index.html +1 -1
  258. package/apps/pwa/out/showroom/index.txt +21 -21
  259. package/apps/pwa/out/tasks/index.html +1 -1
  260. package/apps/pwa/out/tasks/index.txt +22 -22
  261. package/apps/pwa/out/term/index.html +1 -1
  262. package/apps/pwa/out/term/index.txt +22 -22
  263. package/apps/pwa/out/today/index.html +1 -1
  264. package/apps/pwa/out/today/index.txt +22 -22
  265. package/apps/pwa/out/trace/index.html +1 -1
  266. package/apps/pwa/out/trace/index.txt +22 -22
  267. package/apps/pwa/out/vault/index.html +1 -1
  268. package/apps/pwa/out/vault/index.txt +22 -22
  269. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  270. package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
  271. package/apps/pwa/out/workflows/index.html +1 -1
  272. package/apps/pwa/out/workflows/index.txt +22 -22
  273. package/apps/pwa/out/workspace/index.html +1 -1
  274. package/apps/pwa/out/workspace/index.txt +22 -22
  275. package/apps/pwa/out/worktrees/index.html +1 -1
  276. package/apps/pwa/out/worktrees/index.txt +22 -22
  277. package/bin/elanous.mjs +10 -2
  278. package/catalog/providers/opengateway.yaml +25 -0
  279. package/docs/goal-context/01-goal-authoring-checks.md +8 -0
  280. package/docs/goal-context/03-verification-menu.md +24 -0
  281. package/docs/goal-context/04-mock-scenarios.md +100 -0
  282. package/docs/goal-context/README.md +5 -0
  283. package/graphs/catalog/catalog.yaml +1 -1
  284. package/graphs/default-loop.yaml +76 -5
  285. package/graphs/document-loop.yaml +71 -4
  286. package/graphs/implement-loop.yaml +59 -4
  287. package/graphs/journey/SCENARIOS.md +33 -0
  288. package/graphs/journey/recipes.yaml +1 -0
  289. package/graphs/journey/ta-journey.yaml +94 -0
  290. package/graphs/journey/units/author/author.yaml +20 -0
  291. package/graphs/journey/units/author/recipes.yaml +2 -0
  292. package/graphs/journey/units/author-dialogue/author-dialogue.yaml +42 -0
  293. package/graphs/journey/units/author-dialogue/observe.ts +96 -0
  294. package/graphs/journey/units/author-dialogue/recipes.yaml +10 -0
  295. package/graphs/journey/units/green/green.yaml +20 -0
  296. package/graphs/journey/units/green/recipes.yaml +2 -0
  297. package/graphs/journey/units/pr/pr.yaml +20 -0
  298. package/graphs/journey/units/pr/recipes.yaml +2 -0
  299. package/graphs/journey/units/remember/recipes.yaml +2 -0
  300. package/graphs/journey/units/remember/remember.yaml +20 -0
  301. package/graphs/operate-loop.yaml +71 -4
  302. package/graphs/overlays/stop-heal-ladder.yaml +1 -1
  303. package/graphs/release-rehearsal/recipes.yaml +1 -1
  304. package/graphs/release-rehearsal/release-rehearsal.yaml +2 -2
  305. package/graphs/research-loop.yaml +159 -4
  306. package/graphs/test-diet/test-diet.yaml +3 -0
  307. package/graphs/units/dispatch-pod/dispatch-pod.yaml +50 -0
  308. package/graphs/units/dispatch-pod/recipes.yaml +14 -0
  309. package/graphs/units/entry/entry.yaml +50 -0
  310. package/graphs/units/entry/recipes.yaml +14 -0
  311. package/graphs/units/harvest/harvest.yaml +25 -0
  312. package/graphs/units/harvest/recipes.yaml +4 -0
  313. package/graphs/units/land/land.yaml +45 -0
  314. package/graphs/units/land/recipes.yaml +3 -0
  315. package/graphs/units/recipes.yaml +7 -0
  316. package/graphs/units/recover.yaml +39 -0
  317. package/graphs/units/reflect/recipes.yaml +3 -0
  318. package/graphs/units/reflect/reflect.yaml +15 -0
  319. package/graphs/units/task-agent-recover-review.yaml +17 -0
  320. package/graphs/units/task-agent-recover-wait.yaml +17 -0
  321. package/graphs/units/verbs/make-doc.yaml +34 -0
  322. package/graphs/units/verbs/recipes.yaml +33 -0
  323. package/graphs/units/verbs/verify.yaml +55 -0
  324. package/graphs/work/recipes.yaml +2 -0
  325. package/graphs/work/research-report.yaml +28 -0
  326. package/package.json +1 -1
  327. package/scripts/ci-typecheck-changed.ts +69 -16
  328. package/scripts/coord-role-pack.ts +19 -6
  329. package/scripts/docs/generate-graph-anatomy.ts +19 -2
  330. package/scripts/graph-harvest-unit.ts +22 -0
  331. package/scripts/graph-units/journey-stub.ts +6 -0
  332. package/scripts/graph-units/land-enqueue.ts +78 -0
  333. package/scripts/graph-units/recover-plan.ts +15 -0
  334. package/scripts/graph-units/reflect-node.ts +88 -0
  335. package/scripts/graph-units/ta-journey-observe.ts +109 -0
  336. package/scripts/graph-units/verbs/headless-goal-loop.ts +152 -0
  337. package/scripts/graph-units/verbs/verify-stage.ts +75 -0
  338. package/scripts/graph-units/verbs-envelope.ts +139 -0
  339. package/scripts/hq/standby-snapshot.ts +30 -0
  340. package/scripts/public-export.ts +43 -1
  341. package/scripts/ratchets/train-side-invariants.sh +45 -0
  342. package/scripts/release-loop/auto-approve-node.ts +30 -16
  343. package/scripts/release-loop/docs-land-node.ts +21 -5
  344. package/scripts/release-loop/gate-baseline-receipt.ts +155 -0
  345. package/scripts/release-loop/gate-closure.ts +5 -1
  346. package/scripts/release-loop/gate-diff.ts +1 -1
  347. package/scripts/release-loop/gate-node.ts +675 -49
  348. package/scripts/release-loop/gate-partial.ts +68 -14
  349. package/scripts/release-loop/gate-radius-live.ts +177 -0
  350. package/scripts/release-loop/gate-radius-shadow.ts +138 -0
  351. package/scripts/release-loop/gate-traffic-mode.ts +128 -0
  352. package/scripts/release-loop/gate-triage-node.ts +5 -2
  353. package/scripts/release-loop/known-issues-0.2.33-approved.json +194 -0
  354. package/scripts/release-loop/known-issues-node.ts +77 -8
  355. package/scripts/release-loop/node-verdict.ts +12 -1
  356. package/scripts/release-loop/npm-publish-node.ts +66 -12
  357. package/scripts/release-loop/preflight.ts +93 -7
  358. package/scripts/release-loop/probe-pod-host-dirs-live.ts +5 -0
  359. package/scripts/release-loop/publish-node.ts +60 -9
  360. package/scripts/release-loop/pwa-remote.ts +166 -0
  361. package/scripts/release-loop/pwa-scenarios-node.ts +19 -3
  362. package/scripts/release-loop/resume-cap.ts +56 -0
  363. package/scripts/release-loop/resume-release.ts +155 -19
  364. package/scripts/release-loop/resume.ts +24 -1
  365. package/scripts/release-loop/tui-sim-node.ts +8 -2
  366. package/scripts/release-loop/unattended-release.ts +10 -2
  367. package/scripts/test-diet/lib.ts +6 -1
  368. package/scripts/test-diet/node.ts +194 -13
  369. package/scripts/ux-sim.ts +39 -0
  370. package/src/acp/core-turn-bridge.ts +20 -5
  371. package/src/acp/reasoning-thought-buffer.ts +45 -0
  372. package/src/agent/self-ambient-cache.ts +8 -0
  373. package/src/agent-substrate/acp-reviewer.ts +90 -15
  374. package/src/agent-substrate/pr-reviewer.ts +17 -0
  375. package/src/agent-substrate/review-finding-key.ts +53 -0
  376. package/src/agent-substrate/self-review-cli.ts +188 -18
  377. package/src/ask-user-question/pending-questions.ts +1 -1
  378. package/src/autopilot/pr-manager.ts +393 -65
  379. package/src/budget/run-usage-rollup.ts +1 -0
  380. package/src/chat/index.ts +0 -2
  381. package/src/chat/pickers/modal-runtime.ts +10 -27
  382. package/src/chat/tool-render/dispatch.ts +62 -10
  383. package/src/cli/agent-cli.ts +13 -2
  384. package/src/cli/control-cli.ts +24 -7
  385. package/src/cli/doctor-cli.ts +4 -1
  386. package/src/cli/guardian-cli.ts +10 -0
  387. package/src/cli/light-entry-route.ts +75 -0
  388. package/src/cli/llm-detect-cli.ts +23 -1
  389. package/src/cli/pod-cli.ts +77 -3
  390. package/src/cli/pr-cli.ts +81 -14
  391. package/src/cli/pr-land-base-read.ts +2 -0
  392. package/src/cli/pr-land-overlap-evidence.ts +154 -16
  393. package/src/cli/pr-land-pod-shadow-job.ts +3 -0
  394. package/src/cli/pr-land-recovery.ts +64 -0
  395. package/src/cli/pty-takeover-cli.ts +6 -5
  396. package/src/cli/release-checklist-read-cli.ts +79 -0
  397. package/src/cli/release-cli.ts +189 -132
  398. package/src/cli/release-run-if-ready.ts +29 -28
  399. package/src/cli/release-single-flight.ts +178 -0
  400. package/src/cli/remote-resolve.ts +1 -1
  401. package/src/cli/root-tui-entry.ts +48 -0
  402. package/src/cli/seat-cli.ts +37 -0
  403. package/src/cli/setup-cli.ts +40 -1
  404. package/src/cli/sim-cli.ts +22 -0
  405. package/src/cli/tasks-cli.ts +667 -14
  406. package/src/cli/usage-cli.ts +13 -11
  407. package/src/clipboard/history.ts +179 -13
  408. package/src/config.ts +8 -1
  409. package/src/control/control-status.ts +8 -3
  410. package/src/control-plane/ledger.ts +5 -3
  411. package/src/control-plane/server.ts +5 -3
  412. package/src/core-turn/short-question.ts +5 -1
  413. package/src/dashboard/chat/acp-chat.ts +8 -0
  414. package/src/dashboard/index.ts +114 -77
  415. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +3 -0
  416. package/src/dashboard/input/dashboard-plain-turn-stream.ts +4 -0
  417. package/src/dashboard/input/seat-autowake.ts +56 -3
  418. package/src/dashboard/render/theme-resolver.ts +33 -4
  419. package/src/dashboard/seat-boot-preamble.ts +1 -1
  420. package/src/dashboard/slash-runtime/ask-clarification.ts +48 -0
  421. package/src/dashboard/slash-runtime/dashboard-delegated-slashes.ts +68 -0
  422. package/src/dashboard/slash-runtime/dashboard-handlers.ts +62 -322
  423. package/src/dashboard/slash-runtime/dashboard-log-slash.ts +208 -0
  424. package/src/dashboard/turn-preamble.ts +47 -1
  425. package/src/dashboard/turn-stream-formatter.ts +34 -1
  426. package/src/dashboard/turn-stream-runtime.ts +2 -0
  427. package/src/dashboard/turn-typeahead-prompt-zone.ts +67 -0
  428. package/src/domains/guardian-tick.ts +54 -2
  429. package/src/domains/outbound-alert.ts +3 -2
  430. package/src/domains/schedule-registry.ts +16 -2
  431. package/src/extractors.ts +3 -1
  432. package/src/git-fs/idle-refresh.ts +76 -0
  433. package/src/git-fs/index.ts +18 -7
  434. package/src/git-fs/retry.ts +4 -0
  435. package/src/git-fs/runner.ts +2 -0
  436. package/src/git-fs/worktree.ts +42 -4
  437. package/src/graph-kinds/registry.ts +2 -2
  438. package/src/graph-runner/runner.ts +267 -54
  439. package/src/grok/credential.ts +15 -3
  440. package/src/harness/harness-cli-command.ts +151 -38
  441. package/src/harness/harness-incidents.ts +37 -0
  442. package/src/harness/harness-pod-dispatch.ts +38 -1
  443. package/src/harness/harness-salvage-cli.ts +14 -2
  444. package/src/harness/launch-utterance-ledger.ts +208 -0
  445. package/src/impl-discipline/detector.ts +16 -4
  446. package/src/impl-discipline/system-prompt.ts +11 -0
  447. package/src/impl-discipline/types.ts +2 -2
  448. package/src/index.ts +683 -146
  449. package/src/intake-plane/already-built-hint.ts +91 -0
  450. package/src/intake-plane/requirement-funnel.ts +29 -1
  451. package/src/knowhow/pack.ts +199 -0
  452. package/src/light-cli.ts +111 -0
  453. package/src/llm/opengateway-models.ts +11 -0
  454. package/src/llm/provider-credentials.ts +14 -0
  455. package/src/llm/provider-detect.ts +8 -1
  456. package/src/llm/runtime-model-family.ts +2 -1
  457. package/src/llm.ts +268 -65
  458. package/src/loops/budget.ts +95 -0
  459. package/src/loops/msg-wake.ts +8 -2
  460. package/src/loops/orchestrator/traffic.ts +132 -1
  461. package/src/model-tier/llm-tier-map.ts +15 -1
  462. package/src/mss/logging/log-store.ts +4 -5
  463. package/src/native-tool-catalog.ts +15 -1
  464. package/src/nexus/api/graphs-api.ts +22 -3
  465. package/src/nexus/api/http-server.ts +1 -1
  466. package/src/nexus/api/loop-resources.ts +13 -1
  467. package/src/nexus/api/ops-api.ts +12 -0
  468. package/src/oauth/claude-pty-maturity.ts +66 -0
  469. package/src/oauth/codex-access-mirror.ts +55 -0
  470. package/src/oauth/codex.ts +7 -0
  471. package/src/oauth/fallback-chain.ts +1 -1
  472. package/src/onboarding/first-run.ts +5 -3
  473. package/src/onboarding/non-interactive.ts +64 -13
  474. package/src/onboarding.ts +10 -2
  475. package/src/ops/overview-collect.ts +54 -4
  476. package/src/ops/overview.ts +31 -7
  477. package/src/pr-tool/census.ts +282 -30
  478. package/src/pr-tool/inflight-registry.ts +160 -7
  479. package/src/prompt-library/gpt6-family-addendum.ts +14 -1
  480. package/src/pty-shell/herdr-agent-report.ts +442 -0
  481. package/src/pty-shell/pty-control-ipc.ts +5 -5
  482. package/src/pty-shell/pty-manifest.ts +7 -0
  483. package/src/pty-shell/pty-write-outcome.ts +24 -6
  484. package/src/pty-shell/tui-run-state.ts +117 -3
  485. package/src/release-loop/checklist.ts +103 -5
  486. package/src/release-loop/feature-store.ts +19 -2
  487. package/src/release-loop/landed-but-yellow.ts +129 -5
  488. package/src/release-loop/light-rc.ts +1 -0
  489. package/src/release-loop/placement.ts +10 -3
  490. package/src/release-loop/rehearsal.ts +205 -25
  491. package/src/release-loop/rubric.ts +16 -2
  492. package/src/repl/index.ts +174 -17
  493. package/src/repl/ops-msg-bridge.ts +32 -0
  494. package/src/self-dev/ask-launch-flow.ts +44 -5
  495. package/src/self-dev/cli-help-text.ts +17 -0
  496. package/src/self-dev/dev-cli.ts +11 -9
  497. package/src/self-dev/draft-residue.ts +37 -1
  498. package/src/self-dev/draft-sweep.ts +188 -13
  499. package/src/self-dev/draft-triage-rules.ts +107 -0
  500. package/src/self-dev/fleet-reduce.ts +3 -2
  501. package/src/self-dev/graph-front-nodes.ts +112 -0
  502. package/src/self-dev/graph-journey-nodes.ts +11 -6
  503. package/src/self-dev/graph-journey-route.ts +60 -6
  504. package/src/self-dev/harness-run-cli.ts +3 -15
  505. package/src/self-dev/orchestrate-cli-text.ts +10 -0
  506. package/src/self-dev/orchestrate.ts +79 -3
  507. package/src/self-dev/review-provider-fallback.ts +35 -1
  508. package/src/self-dev/run-supervisor.ts +107 -12
  509. package/src/self-dev/run-types.ts +5 -1
  510. package/src/self-dev/self-orchestrate-runtime.ts +1 -11
  511. package/src/self-dev/shard-aggregate.ts +42 -5
  512. package/src/self-dev/shard-run.ts +8 -1
  513. package/src/self-implement/blocked-draft-policy.ts +20 -0
  514. package/src/self-implement/classify-shadow.ts +3 -7
  515. package/src/self-implement/doctor-visit.ts +18 -1
  516. package/src/self-implement/gate-baseline.ts +95 -12
  517. package/src/self-implement/gate-cli.ts +1 -0
  518. package/src/self-implement/gate-remote.ts +40 -25
  519. package/src/self-implement/goal-author-decompose.ts +275 -47
  520. package/src/self-implement/goal-author-identity.ts +66 -0
  521. package/src/self-implement/goal-author-preservation-obligation.ts +35 -0
  522. package/src/self-implement/goal-author.ts +254 -120
  523. package/src/self-implement/goal-elaborate.ts +295 -0
  524. package/src/self-implement/goal-refine.ts +270 -0
  525. package/src/self-implement/graph-overlay-yaml.ts +28 -2
  526. package/src/self-implement/graph-unit-flatten.ts +103 -0
  527. package/src/self-implement/graph-unit-resolver.ts +107 -0
  528. package/src/self-implement/graph-yaml.ts +151 -1
  529. package/src/self-implement/headless-elanous-driver.ts +1 -0
  530. package/src/self-implement/knowhow-pack.ts +103 -0
  531. package/src/self-implement/orchestrator.ts +160 -45
  532. package/src/self-implement/pod-warm-pool-profile.ts +45 -0
  533. package/src/self-implement/review-mustfix-class.ts +203 -0
  534. package/src/self-implement/rework-budget-judgment.ts +5 -8
  535. package/src/self-implement/run-stop.ts +17 -2
  536. package/src/self-implement/running-runs.ts +29 -1
  537. package/src/self-implement/scope-adjacency.ts +113 -0
  538. package/src/self-implement/seams.ts +138 -4
  539. package/src/self-implement/stop-autoheal.ts +112 -2
  540. package/src/session/chat.ts +27 -0
  541. package/src/session-runtime/index.ts +1 -1
  542. package/src/session-runtime/retry-policy.ts +2 -1
  543. package/src/setup/llm-env-detect.ts +5 -0
  544. package/src/skills/tools/agent.ts +140 -14
  545. package/src/skills/tools/msg-ask.ts +100 -0
  546. package/src/skills/tools/ops-ledger.ts +2 -1
  547. package/src/skills/tools/schedule-wakeup.ts +66 -17
  548. package/src/steward/hold-hygiene.ts +24 -6
  549. package/src/steward/release-day.ts +7 -3
  550. package/src/steward/triage-budget.ts +194 -0
  551. package/src/steward/triage.ts +57 -7
  552. package/src/task-agent/actions.ts +69 -1
  553. package/src/task-agent/green-sweep.ts +4 -0
  554. package/src/task-agent/harvest-unit.ts +141 -0
  555. package/src/task-agent/live-moves.ts +257 -15
  556. package/src/task-agent/parent-host.ts +216 -6
  557. package/src/task-agent/postland-card.ts +80 -1
  558. package/src/task-agent/rejudge-on-head.ts +112 -26
  559. package/src/task-agent/repair-branch-cas.ts +4 -1
  560. package/src/task-agent/repair-cycle.ts +16 -3
  561. package/src/task-agent/run-identity.ts +4 -3
  562. package/src/task-agent/shadow.ts +19 -3
  563. package/src/task-agent/task-hand.ts +96 -36
  564. package/src/task-agent/tier-split.ts +127 -0
  565. package/src/task-agent/work-order-build.ts +24 -0
  566. package/src/task-agent/work-order.ts +3 -2
  567. package/src/task-orchestrator/generator-prompt.ts +60 -11
  568. package/src/task-orchestrator/surfaces/pod-account-broker.ts +72 -3
  569. package/src/task-orchestrator/surfaces/pod-child-providers.ts +1 -1
  570. package/src/task-orchestrator/surfaces/pod-command-job.ts +47 -8
  571. package/src/task-orchestrator/surfaces/pod-image-source.ts +89 -1
  572. package/src/task-orchestrator/surfaces/pod-lease.ts +17 -8
  573. package/src/task-orchestrator/surfaces/pod-lifecycle-stages.ts +86 -8
  574. package/src/task-orchestrator/surfaces/pod-manifest-test-normalize.ts +8 -2
  575. package/src/task-orchestrator/surfaces/pod-pool.ts +73 -17
  576. package/src/task-orchestrator/surfaces/pod-provenance.ts +76 -0
  577. package/src/task-orchestrator/surfaces/pod-source-receive.ts +72 -4
  578. package/src/task-orchestrator/surfaces/pod-warm-provenance.ts +81 -0
  579. package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +32 -5
  580. package/src/task-orchestrator/surfaces/pod-warm-worker-protocol.ts +3 -0
  581. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +421 -0
  582. package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +2 -1
  583. package/src/task-orchestrator/surfaces/self-implement-pod.ts +588 -71
  584. package/src/task-orchestrator/surfaces/self-implement.ts +106 -2
  585. package/src/tool-runtime/ops-ledger-runtimes.ts +10 -2
  586. package/src/tool-runtime/truncation-store.ts +4 -0
  587. package/src/tui/README.md +2 -0
  588. package/src/tui/chat/background-job-status.ts +54 -0
  589. package/src/tui/chat/log-view.ts +80 -0
  590. package/src/tui/entry.ts +406 -0
  591. package/src/tui/input/composer.ts +144 -0
  592. package/src/tui/seat-mail-boot.ts +59 -0
  593. package/src/tui/slash/compact.ts +100 -0
  594. package/src/tui/slash/new-session.ts +35 -0
  595. package/src/tui-client/dashboard-session.ts +10 -1
  596. package/src/tui-client/local-chat-turn.ts +239 -0
  597. package/src/tui-client/local-harness-command.ts +63 -0
  598. package/src/tui-client/local-provider-command.ts +184 -0
  599. package/src/user-config/coercion.ts +36 -0
  600. package/src/user-config.ts +22 -43
  601. package/src/ux-sim/event-monkey.ts +378 -0
  602. package/src/ux-sim/fake-bin.ts +42 -7
  603. package/src/ux-sim/fault-nextmove-dispatch.ts +266 -0
  604. package/src/ux-sim/fault-pod-codex-503.ts +211 -0
  605. package/src/ux-sim/fault-scope-escape-lift.ts +189 -0
  606. package/src/ux-sim/scenario.ts +72 -13
  607. package/src/version/packed-revision.json +1 -1
  608. package/apps/pwa/out/_next/static/chunks/1034-8b639cee57b8e4a8.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/1085-6c2d35526f11f840.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/1220.1ff7d5cdc34b1ae5.js +0 -27
  611. package/apps/pwa/out/_next/static/chunks/1247.5884ec81b39fe2e7.js +0 -287
  612. package/apps/pwa/out/_next/static/chunks/1302.87681521a7a4661e.js +0 -62
  613. package/apps/pwa/out/_next/static/chunks/1324-59dae3002413623b.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/1343-f40b5870ac1eda5a.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/146-b6cd56be11dda017.js +0 -1
  616. package/apps/pwa/out/_next/static/chunks/1654.32de4b324981a188.js +0 -23
  617. package/apps/pwa/out/_next/static/chunks/1834-98b64172bc1efe77.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/1849.1ce38efa6c527d9a.js +0 -149
  619. package/apps/pwa/out/_next/static/chunks/1913.7cc6fb6055b1ed3f.js +0 -41
  620. package/apps/pwa/out/_next/static/chunks/2030.41fae98c7e907304.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/2099-08b2f2dfba742c9b.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/2112-e4efc21a6505d5bc.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/2134.ecee03a550ee7250.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/2236.66061832181cb745.js +0 -1
  625. package/apps/pwa/out/_next/static/chunks/2422.b0bd272e7020d7e6.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/2534.e85ae21ad0dac829.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/2898.fd781bc7cf01a7b7.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/3040.92ff9ffc9f971cb9.js +0 -115
  629. package/apps/pwa/out/_next/static/chunks/3144.6861096c7b4edef7.js +0 -64
  630. package/apps/pwa/out/_next/static/chunks/3541-ce3a5424ed5ad7f1.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/3583.a05e88cc8d594628.js +0 -1
  632. package/apps/pwa/out/_next/static/chunks/3771.e928edf2867d28c0.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/4063-03284b5de59965f3.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/4300-c7ec4eeabbb5f3f7.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/4483.646d5065199e1b55.js +0 -36
  636. package/apps/pwa/out/_next/static/chunks/4519.28199ff26b2fd80d.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/4581.ef2443578054544f.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/4608-8d894e9cbe4f08bf.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/4639.ae48a01d1bb50a93.js +0 -286
  640. package/apps/pwa/out/_next/static/chunks/4739.08742ac668aada67.js +0 -77
  641. package/apps/pwa/out/_next/static/chunks/4745.67d6f99c245af3f7.js +0 -226
  642. package/apps/pwa/out/_next/static/chunks/4882-16cef71d6a1a32be.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/4999.c064a4000c2a9e94.js +0 -79
  644. package/apps/pwa/out/_next/static/chunks/5260-69f35419bca263e7.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/5272.b92d1b72a15c25fe.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/5304.04d24756f242a094.js +0 -148
  647. package/apps/pwa/out/_next/static/chunks/5415-997980f828667736.js +0 -1
  648. package/apps/pwa/out/_next/static/chunks/5458-16cd7a2b51b74c8c.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/5543.a438b8ab5a13ea47.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/5572-42c9084f27ca761e.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/5661-80470aac963d9f36.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/5723.421338408b5c30c4.js +0 -74
  653. package/apps/pwa/out/_next/static/chunks/5809.64c3664267c50bce.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/6000.64bc21ac8627f2f5.js +0 -94
  655. package/apps/pwa/out/_next/static/chunks/6013.39459b9c68eabf5f.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/607-06466dff012b03ef.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/6192.a190b989b81e9743.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/6323.8672b4817a306928.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/6444.7d1fdcb9b09ae71c.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/6517.4147878ac06d584d.js +0 -90
  661. package/apps/pwa/out/_next/static/chunks/6532.e34302010b2f0b68.js +0 -32
  662. package/apps/pwa/out/_next/static/chunks/6546.74bc4ed82f19016d.js +0 -104
  663. package/apps/pwa/out/_next/static/chunks/6684-e7523ce7c2fad075.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/6726.ebdc82feb161431e.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/6739.00fb6f892e5e392b.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/6745.934c4921bf15513a.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/6767.ea607cf0c20a25af.js +0 -24
  668. package/apps/pwa/out/_next/static/chunks/6843.67bfd020da0926c5.js +0 -93
  669. package/apps/pwa/out/_next/static/chunks/6948.1062d4594652b5bf.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/7138-eaf841fc1dc03463.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/7174-cda29ee13d7790e7.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/7304-3c07583f4e405f4f.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/7557.e325503a090a4310.js +0 -8
  674. package/apps/pwa/out/_next/static/chunks/7569-9ce9432c7f10989f.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/7886-55854585fadeded1.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/8097-9530c03bcc550f02.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/8215-1661812b6e9ad71f.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/8260-19c921a51ca8ec3e.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/8440.3a5cf5c85aa76db8.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/8519-4599be2249161e68.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/8547.b028b90c5c01ace6.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/8587-cbe02a953a7f4990.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/877-2b977259c4b15655.js +0 -1
  684. package/apps/pwa/out/_next/static/chunks/9002.e5658224e3dc1b6f.js +0 -24
  685. package/apps/pwa/out/_next/static/chunks/9019-0a4b40c50c7314fb.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/9138.b6e80031afb1d973.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/9188-d36e4fc869ddc166.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/9230.44f5039e796e4ab4.js +0 -1
  689. package/apps/pwa/out/_next/static/chunks/9279-d28c5b7754d010ae.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/939.336acce73e98482c.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/9407.e49124dedf0f5e72.js +0 -1
  692. package/apps/pwa/out/_next/static/chunks/9610.c07cce4bfcc14ed7.js +0 -1
  693. package/apps/pwa/out/_next/static/chunks/9667-ce78c2ce91050ff9.js +0 -1
  694. package/apps/pwa/out/_next/static/chunks/9989.3694300e5f000b14.js +0 -1
  695. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-339fabb3499da0e1.js +0 -1
  696. package/apps/pwa/out/_next/static/chunks/app/approvals/page-504204b58c82c253.js +0 -1
  697. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-a9717a67c2eecbaf.js +0 -1
  698. package/apps/pwa/out/_next/static/chunks/app/board/page-5c9ec30aee62dfbf.js +0 -1
  699. package/apps/pwa/out/_next/static/chunks/app/botlab/page-584191533721fa16.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/app/bots/page-73c4b4bb3ac7ab5f.js +0 -1
  701. package/apps/pwa/out/_next/static/chunks/app/ceo/page-c0f1ca371f710b06.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/app/chat/page-46430d45a9e04eac.js +0 -1
  703. package/apps/pwa/out/_next/static/chunks/app/consult/page-3b2ebf1dc64ff0be.js +0 -1
  704. package/apps/pwa/out/_next/static/chunks/app/decisions/page-25654b82094f7d22.js +0 -1
  705. package/apps/pwa/out/_next/static/chunks/app/design-check/page-8ea49a4c6dd2c6e7.js +0 -1
  706. package/apps/pwa/out/_next/static/chunks/app/editor/page-ce5113aefea0a8d1.js +0 -1
  707. package/apps/pwa/out/_next/static/chunks/app/exec/page-0e5a41fac1c75396.js +0 -1
  708. package/apps/pwa/out/_next/static/chunks/app/field/page-31aaf1ed37c6df9a.js +0 -1
  709. package/apps/pwa/out/_next/static/chunks/app/healthz/route-339fabb3499da0e1.js +0 -1
  710. package/apps/pwa/out/_next/static/chunks/app/inside/page-3dcc1fa411e1f4e0.js +0 -1
  711. package/apps/pwa/out/_next/static/chunks/app/intake/page-c69247e7134d329d.js +0 -1
  712. package/apps/pwa/out/_next/static/chunks/app/layout-66dc5cd3a0999abe.js +0 -1
  713. package/apps/pwa/out/_next/static/chunks/app/live/page-26ff7ff651a7217d.js +0 -1
  714. package/apps/pwa/out/_next/static/chunks/app/live-run/page-617b3976981efa96.js +0 -1
  715. package/apps/pwa/out/_next/static/chunks/app/loops/page-d256f5d5d9552823.js +0 -1
  716. package/apps/pwa/out/_next/static/chunks/app/market/page-ce8f1907b8a6101c.js +0 -1
  717. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-33e182a316263116.js +0 -1
  718. package/apps/pwa/out/_next/static/chunks/app/not-found-7607cca9b84950c6.js +0 -1
  719. package/apps/pwa/out/_next/static/chunks/app/observatory/page-eeab86176b03dc3b.js +0 -1
  720. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-5b93dbae3432b996.js +0 -1
  721. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-77a6cb90a9cf2b10.js +0 -1
  722. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f47e768b5b242b5a.js +0 -1
  723. package/apps/pwa/out/_next/static/chunks/app/outputs/page-eb41aa3367fc2505.js +0 -1
  724. package/apps/pwa/out/_next/static/chunks/app/page-4d13ab0b8a267e3f.js +0 -1
  725. package/apps/pwa/out/_next/static/chunks/app/reflection/page-84dad32ff0fc0831.js +0 -1
  726. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-95306f892aa2d0ee.js +0 -1
  727. package/apps/pwa/out/_next/static/chunks/app/sessions/page-940fb6c217f6b470.js +0 -1
  728. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-8c66a3b24a745630.js +0 -1
  729. package/apps/pwa/out/_next/static/chunks/app/settings/page-7b675dfd35c36fc9.js +0 -1
  730. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8bd0d3cf7665bcfc.js +0 -1
  731. package/apps/pwa/out/_next/static/chunks/app/setup/layout-339fabb3499da0e1.js +0 -1
  732. package/apps/pwa/out/_next/static/chunks/app/setup/page-c219f2ed171131be.js +0 -1
  733. package/apps/pwa/out/_next/static/chunks/app/share/page-40f8fa21bdae955b.js +0 -1
  734. package/apps/pwa/out/_next/static/chunks/app/showroom/page-468f8313e4f1fbd9.js +0 -1
  735. package/apps/pwa/out/_next/static/chunks/app/tasks/page-7bbb8fa353a246c4.js +0 -1
  736. package/apps/pwa/out/_next/static/chunks/app/today/page-4214b855d9233ea8.js +0 -1
  737. package/apps/pwa/out/_next/static/chunks/app/trace/page-936752c7aa58762e.js +0 -1
  738. package/apps/pwa/out/_next/static/chunks/app/vault/page-ec052ad359df1797.js +0 -1
  739. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-8305570551091338.js +0 -1
  740. package/apps/pwa/out/_next/static/chunks/app/workflows/page-fad650e4793adb65.js +0 -1
  741. package/apps/pwa/out/_next/static/chunks/app/workspace/page-d90e64743826e1d8.js +0 -1
  742. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-a16bcfd49af86a32.js +0 -1
  743. package/apps/pwa/out/_next/static/chunks/e5012ffe.f727e23c231f4342.js +0 -122
  744. package/apps/pwa/out/_next/static/chunks/ea695e3c-1f1d70d740cf3031.js +0 -1
  745. package/apps/pwa/out/_next/static/chunks/ebc7d829.264a0bcb6c750b50.js +0 -1
  746. package/apps/pwa/out/_next/static/chunks/framework-906af9bd029e166c.js +0 -1
  747. package/apps/pwa/out/_next/static/chunks/main-a24847f9f684d469.js +0 -1
  748. package/apps/pwa/out/_next/static/chunks/main-app-7bdcd8aa7c8397d3.js +0 -1
  749. package/apps/pwa/out/_next/static/chunks/pages/_app-6ba46a5066fc226d.js +0 -1
  750. package/apps/pwa/out/_next/static/chunks/pages/_error-a8dc6d07591f2a25.js +0 -1
  751. package/apps/pwa/out/_next/static/chunks/webpack-f13df28c15c55eee.js +0 -1
  752. package/src/cli/g6a-registration-probe.ts +0 -16
  753. /package/apps/pwa/out/_next/static/{IVKr3AlRUVLKsw2fh7eXF → T1mBQqmkwKDLGtyisDCOd}/_ssgManifest.js +0 -0
@@ -9,7 +9,7 @@ import { existsSync, linkSync, mkdirSync, readFileSync, rmSync, writeFileSync }
9
9
  import { randomUUID } from 'node:crypto';
10
10
  import { spawnSync } from 'node:child_process';
11
11
  import { dirname, join } from 'node:path';
12
- import { CODE_FILE, type ClosureVerdict } from './gate-closure';
12
+ import { CODE_FILE, TRACEABLE_RELEASE_JSON, type ClosureVerdict } from './gate-closure';
13
13
 
14
14
  export interface PartialPlan {
15
15
  /** A verified docs-only append carries the completed, clean cut without starting a test Pod. */
@@ -27,7 +27,13 @@ export interface PartialPlan {
27
27
 
28
28
  /** G1: a second measurement of only the failed files on the very same release commit.
29
29
  * This is deliberately separate from PartialPlan, which compares two different commits. */
30
- export interface RecheckPlan extends PartialPlan { kind: 'recheck' }
30
+ export interface RecheckPlan extends PartialPlan {
31
+ kind: 'recheck';
32
+ /** Files from shards that could not produce a measurement; completed shard results remain in priorFailures/errors. */
33
+ stalledFiles?: string[];
34
+ /** Count of complete shards whose measurements are preserved in this recheck. */
35
+ preservedShardCount?: number;
36
+ }
31
37
 
32
38
  const FULL_SHA = /^[0-9a-f]{40}$/i;
33
39
  const RELEASE_VERSION = /^\d+\.\d+\.\d+(?:-(?:rc|alpha|beta)\.\d+)?$/;
@@ -92,13 +98,49 @@ function validateRecheckPlan(raw: Partial<RecheckPlan>, version: string, path: s
92
98
  typeof raw.forCommit !== 'string' || !FULL_SHA.test(raw.forCommit) || raw.priorCommit !== raw.forCommit ||
93
99
  !Array.isArray(raw.priorFailures) || !raw.priorFailures.every((id) => typeof id === 'string') ||
94
100
  !Array.isArray(raw.priorErrors) || !raw.priorErrors.every((id) => typeof id === 'string') ||
95
- !Array.isArray(raw.files) || !raw.files.length || !raw.files.every((file) => typeof file === 'string')) invalid();
101
+ !Array.isArray(raw.files) || !raw.files.length || !raw.files.every((file) => typeof file === 'string') ||
102
+ (raw.preservedShardCount !== undefined && (!Number.isSafeInteger(raw.preservedShardCount) || raw.preservedShardCount < 0))) invalid();
96
103
  let expected: string[] = [];
97
- try { expected = [...new Set([...raw.priorFailures!, ...raw.priorErrors!].map(testFileOfId))].sort(); }
104
+ try {
105
+ const measuredFiles = [...raw.priorFailures!, ...raw.priorErrors!].map(testFileOfId);
106
+ const stalledFiles = raw.stalledFiles === undefined ? [] : raw.stalledFiles;
107
+ if (!Array.isArray(stalledFiles) || !stalledFiles.every((file) => typeof file === 'string' && TEST_FILE.test(file) && !file.split('/').includes('..'))) invalid();
108
+ if (raw.stalledFiles !== undefined && stalledFiles.some((file) => measuredFiles.includes(file))) invalid();
109
+ expected = [...new Set(stalledFiles)].sort();
110
+ // Legacy plans recheck every prior failed file. Stalled plans re-run only files from incomplete shards; all
111
+ // completed shard outcomes, including runtime errors, are preserved for the combined verdict.
112
+ if (raw.stalledFiles === undefined) expected = [...new Set(measuredFiles)].sort();
113
+ }
98
114
  catch { invalid(); }
99
115
  if (JSON.stringify(raw.files) !== JSON.stringify(expected)) invalid();
100
116
  }
101
117
 
118
+ /** Plan a same-commit retry after a Pod sweep reports stalled shards. Reuse completed outcomes and rerun stalled files. */
119
+ export function planStalledShardRecheck(input: { version: string; measuredCommit: string; forCommit: string;
120
+ failures: readonly string[]; errors: readonly string[]; stalledFiles: readonly string[]; preservedShardCount?: number;
121
+ existsAtCommit: (file: string) => boolean }):
122
+ { ok: true; plan: RecheckPlan } | { ok: false; reason: string } {
123
+ if (!RELEASE_VERSION.test(input.version)) return { ok: false, reason: 'invalid release version' };
124
+ if (!FULL_SHA.test(input.measuredCommit) || !FULL_SHA.test(input.forCommit)) return { ok: false, reason: 'commit is not a full sha' };
125
+ if (input.measuredCommit !== input.forCommit) return { ok: false, reason: 'recheck requires the same commit' };
126
+ try { [...input.failures, ...input.errors].forEach(testFileOfId); }
127
+ catch (error) { return { ok: false, reason: String(error instanceof Error ? error.message : error) }; }
128
+ const stalledFiles = [...new Set(input.stalledFiles)].sort();
129
+ if (!stalledFiles.length) return { ok: false, reason: 'no stalled test files to recheck' };
130
+ if (stalledFiles.some((file) => !TEST_FILE.test(file) || file.split('/').includes('..'))) return { ok: false, reason: 'unsafe stalled test path' };
131
+ const measuredFiles = [...new Set([...input.failures, ...input.errors].map(testFileOfId))];
132
+ if (stalledFiles.some((file) => measuredFiles.includes(file)))
133
+ return { ok: false, reason: 'stalled file overlaps a completed shard result' };
134
+ const files = stalledFiles;
135
+ try {
136
+ const missing = files.find((file) => !input.existsAtCommit(file));
137
+ if (missing) return { ok: false, reason: `failed test file missing at commit: ${missing}` };
138
+ } catch (error) { return { ok: false, reason: `test file existence unreadable: ${String(error)}` }; }
139
+ return { ok: true, plan: { kind: 'recheck', version: input.version, priorCommit: input.measuredCommit,
140
+ forCommit: input.forCommit, files, stalledFiles, ...(input.preservedShardCount === undefined ? {} : { preservedShardCount: input.preservedShardCount }),
141
+ priorFailures: [...input.failures], priorErrors: [...input.errors] } };
142
+ }
143
+
102
144
  export const partialPlanPath = (root: string, version: string) => join(root, 'release', version, 'gate-partial.json');
103
145
  /** The same test-file set the gate sweep runs (`git ls-files` filtered by this suffix). */
104
146
  const TEST_FILE = /^(?:[\w.-]+\/)*[\w.-]+\.test\.(?:tsx?|jsx?|mts|cts)$/;
@@ -137,13 +179,13 @@ export function planPartialRegate(input: { version: string; priorCommit: string;
137
179
  const source = input.changedFiles.filter((file) => !TEST_FILE.test(file) && !isInert(file));
138
180
  const list = (files: string[]) => `${files.slice(0, 5).join(', ')}${files.length > 5 ? ` (+${files.length - 5})` : ''}`;
139
181
  // Only code is followed through the closure: a changed fixture, JSON, lockfile, config or script is a full gate.
140
- const notCode = source.filter((file) => !CODE_FILE.test(file));
182
+ const notCode = source.filter((file) => !CODE_FILE.test(file) && file !== TRACEABLE_RELEASE_JSON);
141
183
  if (source.length && (!input.closure || notCode.length)) return { ok: false, reason: `non-test change → full gate: ${list(notCode.length ? notCode : source)}` };
142
184
  let reaching: string[] = [];
143
185
  let closure: { reached: number; opaque: number } | undefined;
144
186
  if (source.length) {
145
187
  let verdict: ClosureVerdict;
146
- try { verdict = input.closure!(input.changedFiles.filter((file) => CODE_FILE.test(file))); }
188
+ try { verdict = input.closure!(input.changedFiles.filter((file) => CODE_FILE.test(file) || file === TRACEABLE_RELEASE_JSON)); }
147
189
  catch (error) { return { ok: false, reason: `import closure unreadable → full gate: ${String(error instanceof Error ? error.message : error).slice(0, 200)}` }; }
148
190
  if (!verdict.ok) return { ok: false, reason: verdict.reason };
149
191
  const unsafe = verdict.tests.filter((file) => !TEST_FILE.test(file) || file.split('/').includes('..'));
@@ -182,24 +224,32 @@ export function clearPartialPlan(root: string, version: string): void {
182
224
  }
183
225
 
184
226
  /** The plan for exactly this commit, or null. A malformed plan is an error (never silently a full or partial gate). */
185
- export function readPartialPlan(roots: readonly string[], version: string, commit: string): PartialPlan | null {
227
+ export function readPartialPlanResult(roots: readonly string[], version: string, commit: string):
228
+ { plan: PartialPlan | null; fallbackReason: string | null } {
229
+ let fallbackReason: string | null = 'no-applicable-plan';
186
230
  for (const root of roots) {
187
231
  const path = partialPlanPath(root, version);
188
232
  if (!existsSync(path)) continue;
189
233
  const raw = JSON.parse(readFileSync(path, 'utf8')) as Partial<PartialPlan>;
234
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) throw new Error(`invalid gate partial plan: ${path}`);
190
235
  if ((raw as Partial<RecheckPlan>).kind !== undefined && (raw as Partial<RecheckPlan>).kind !== 'recheck' && raw.kind !== 'carry')
191
236
  throw new Error(`invalid gate partial plan: ${path}`);
192
- // Until the gate consumer connects G1, the opt-in recheck plan is a shadow record.
193
- // A legacy full gate may run, but it must never interpret a same-commit recheck as a changed-tip partial gate.
237
+ // Only a well-formed stalled-shard recheck for this exact release commit enters the same-commit consumer path.
194
238
  if ((raw as Partial<RecheckPlan>).kind === 'recheck') {
195
- validateRecheckPlan(raw as Partial<RecheckPlan>, version, path);
239
+ const recheck = raw as Partial<RecheckPlan>;
240
+ validateRecheckPlan(recheck, version, path);
241
+ if (recheck.forCommit === commit && recheck.stalledFiles !== undefined)
242
+ return { plan: recheck, fallbackReason: null };
243
+ if (recheck.forCommit === commit) { fallbackReason = 'recheck-not-stalled-only'; continue; }
244
+ fallbackReason = 'plan-commit-mismatch';
196
245
  continue;
197
246
  }
198
247
  if (raw.version !== version || typeof raw.priorCommit !== 'string' || !/^[0-9a-f]{40}$/i.test(raw.priorCommit)
199
248
  || typeof raw.forCommit !== 'string' || !/^[0-9a-f]{40}$/i.test(raw.forCommit) || raw.priorCommit === raw.forCommit
200
249
  || !Array.isArray(raw.files) || (raw.files.length === 0) !== (raw.kind === 'carry')
201
250
  || !raw.files.every((file) => typeof file === 'string' && TEST_FILE.test(file) && !file.split('/').includes('..'))
202
- || !Array.isArray(raw.priorFailures) || !Array.isArray(raw.priorErrors)) throw new Error(`invalid gate partial plan: ${path}`);
251
+ || !Array.isArray(raw.priorFailures) || !Array.isArray(raw.priorErrors))
252
+ throw new Error(`invalid gate partial plan: ${path}`);
203
253
  if (raw.kind === 'carry' && (raw.priorFailures.length || raw.priorErrors.length)) throw new Error(`invalid gate partial plan: ${path}`);
204
254
  // Every prior failure's file is re-run (planPartialRegate guarantees it) — a plan that would carry a failure it did
205
255
  // not re-run is malformed, not «partial».
@@ -208,10 +258,14 @@ export function readPartialPlan(roots: readonly string[], version: string, commi
208
258
  try { failureFiles = [...raw.priorFailures, ...raw.priorErrors].map((id) => testFileOfId(String(id))); }
209
259
  catch { throw new Error(`invalid gate partial plan: ${path}`); }
210
260
  if (failureFiles.some((file) => !rerun.has(file))) throw new Error(`invalid gate partial plan (prior failure not re-run): ${path}`);
211
- if (raw.forCommit !== commit) continue;
212
- return raw as PartialPlan;
261
+ if (raw.forCommit !== commit) { fallbackReason = 'plan-commit-mismatch'; continue; }
262
+ return { plan: raw as PartialPlan, fallbackReason: null };
213
263
  }
214
- return null;
264
+ return { plan: null, fallbackReason };
265
+ }
266
+
267
+ export function readPartialPlan(roots: readonly string[], version: string, commit: string): PartialPlan | null {
268
+ return readPartialPlanResult(roots, version, commit).plan;
215
269
  }
216
270
 
217
271
  /** Cut failures after a partial re-run: prior results for files not re-run ⊕ this run's results for the re-run files. */
@@ -0,0 +1,177 @@
1
+ /** A hard rehearsal's immutable gate evidence may select the next release's cut tests, but never authorizes a carry. */
2
+ import { spawnSync } from 'node:child_process';
3
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { changedFilesBetween, planPartialRegate, testFileOfId, type PartialPlan } from './gate-partial.js';
6
+ import { CODE_FILE, gitClosureTree, testsReachingChanged } from './gate-closure.js';
7
+ import { versionOnlyBumps } from './gate-radius-shadow.js';
8
+ import { gateAssignableTestsAtCommit, gateBaselineReceipt, type GateBaselineReceipt } from './gate-baseline-receipt.js';
9
+ import { parsePodPool, remoteImageId } from '../../src/task-orchestrator/surfaces/pod-pool.js';
10
+ import { podCommandImageFor } from '../../src/task-orchestrator/surfaces/pod-command-job.js';
11
+ import { SPAWN_SYNC_MAX_BUFFER } from '../../src/util/spawn-sync-output.js';
12
+
13
+ const SHA = /^[0-9a-f]{40}$/i;
14
+ const DIGEST = /^sha256:[0-9a-f]{64}$/i;
15
+ const TEST = /\.test\.(?:tsx?|jsx?|mts|cts)$/;
16
+
17
+ export function hardRehearsalGateVersion(version: string, runId: string): string {
18
+ const id = /^hard-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i.exec(runId)?.[1];
19
+ if (!/^\d+\.\d+\.\d+$/.test(version) || !id) throw new Error('invalid hard rehearsal gate identity');
20
+ return `${version}-rc.${BigInt(`0x${id.replaceAll('-', '')}`).toString(10)}`;
21
+ }
22
+
23
+ export function hardRehearsalGateArgs(version: string, runId: string, commit: string, previous: string, pool: string): string[] {
24
+ return ['scripts/release-loop/gate-node.ts', '--commit', commit, '--version', hardRehearsalGateVersion(version, runId),
25
+ '--baseline-version', previous, '--executor', 'pod', '--pod-pool', pool, '--pod-shards', '12', '--rehearsal-baseline', '--json'];
26
+ }
27
+
28
+ export interface LiveRadiusDecision { plan: PartialPlan | null; reason: string; rehearsalRunId?: string }
29
+
30
+ /** Read the image ID on every candidate host before deciding. A later Job must still prove its actual image. */
31
+ export function preflightGateImageDigest(pool: string): string | null {
32
+ try {
33
+ const image = podCommandImageFor([], true);
34
+ const members = parsePodPool(pool);
35
+ if (!members.length) return null;
36
+ const ids = members.map((member) => {
37
+ if (member.sshHost) return remoteImageId(member, image);
38
+ const result = spawnSync('docker', ['image', 'inspect', image, '--format', '{{.Id}}'], { encoding: 'utf8', timeout: 30_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
39
+ return result.status === 0 ? result.stdout.trim() : null;
40
+ });
41
+ return ids.every((id) => id && DIGEST.test(id) && id === ids[0]) ? ids[0]! : null;
42
+ } catch { return null; }
43
+ }
44
+
45
+ /** The current sweep ID and exact root inventory prevent stale Pod logs from proving a new partial run's image. */
46
+ export function partialGateImagesMatch(root: string, version: string, digest: string, commit: string,
47
+ sweep: { id: string; shards: number; files: readonly string[] } | undefined): boolean {
48
+ if (!SHA.test(commit) || !sweep || !/^[0-9a-f-]{36}$/i.test(sweep.id)
49
+ || !Number.isSafeInteger(sweep.shards) || sweep.shards < 1) return false;
50
+ try {
51
+ const dir = join(root, 'release', version, 'gate-logs', 'cut-partial');
52
+ const names = readdirSync(dir).filter((name) => /^pod-\d+(?:-[\w-]+)?\.json$/.test(name));
53
+ const allRows = names.map((name) => ({ name, ...JSON.parse(readFileSync(join(dir, name), 'utf8')) as {
54
+ commit?: string; sweepId?: string; shardCount?: number; files?: unknown; imageDigest?: string;
55
+ attemptId?: string; parentAttempt?: string } }));
56
+ const rows = allRows.filter((row) => row.sweepId === sweep.id);
57
+ const roots = rows.filter((row) => /^pod-\d+\.json$/.test(row.name));
58
+ if (roots.length !== sweep.shards || roots.some((row) => row.shardCount !== sweep.shards
59
+ || row.commit !== commit || !Array.isArray(row.files) || !row.attemptId || row.imageDigest !== digest)) return false;
60
+ if (allRows.some((row) => /^pod-\d+\.json$/.test(row.name)
61
+ && row.commit === commit && row.sweepId !== sweep.id)) return false;
62
+ if (allRows.some((row) => row.commit === commit && !row.sweepId)) return false;
63
+ const indices = roots.map((row) => Number(/^pod-(\d+)\.json$/.exec(row.name)![1]));
64
+ if (new Set(indices).size !== sweep.shards || indices.some((index) => index < 0 || index >= sweep.shards)) return false;
65
+ const files = roots.flatMap((row) => row.files as string[]);
66
+ if (files.some((file) => typeof file !== 'string')
67
+ || JSON.stringify(files.sort()) !== JSON.stringify([...sweep.files].sort())) return false;
68
+ const attempts = new Set(roots.map((row) => row.attemptId));
69
+ let pending = allRows.filter((row) => !/^pod-\d+\.json$/.test(row.name));
70
+ for (let count = 0; pending.length && count < allRows.length; count++) {
71
+ const connected = pending.filter((row) => row.parentAttempt && attempts.has(row.parentAttempt));
72
+ if (!connected.length) break;
73
+ if (connected.some((row) => row.sweepId !== sweep.id || row.commit !== commit
74
+ || !row.attemptId || row.imageDigest !== digest)) return false;
75
+ for (const row of connected) attempts.add(row.attemptId);
76
+ pending = pending.filter((row) => !connected.includes(row));
77
+ }
78
+ return !pending.some((row) => row.sweepId === sweep.id || (row.commit === commit && !row.sweepId));
79
+ } catch { return false; }
80
+ }
81
+
82
+ /** Every missing or mismatched proof returns a full gate. The caller uses this only when no explicit resume plan exists. */
83
+ export function planHardRehearsalRadius(input: { root: string; repo: string; version: string; forCommit: string;
84
+ graphPins: { graphSha: string; recipeSha: string } | null; envFingerprint: string;
85
+ imageDigest: string | null }): LiveRadiusDecision {
86
+ const full = (reason: string): LiveRadiusDecision => ({ plan: null, reason });
87
+ if (!/^\d+\.\d+\.\d+$/.test(input.version) || !SHA.test(input.forCommit)) return full('invalid release identity');
88
+ if (!input.graphPins || !/^[0-9a-f]{64}$/i.test(input.envFingerprint) || !input.imageDigest || !DIGEST.test(input.imageDigest))
89
+ return full('current graph, environment or Pod image identity unmeasured');
90
+ try {
91
+ const stamps = ['hard-5h', 'hard-mid'].map((step) => {
92
+ const path = join(input.root, 'release', input.version, 'rehearsal', `${step}.json`);
93
+ if (!existsSync(path)) return null;
94
+ const stamp = JSON.parse(readFileSync(path, 'utf8')) as {
95
+ version?: string; step?: string; outcome?: string; sha?: string; endedAt?: string; hardGateRunId?: string;
96
+ hardGate?: { commit?: string; executor?: string; outcome?: string; introduced?: unknown[] };
97
+ };
98
+ return { step, stamp, ended: Date.parse(stamp.endedAt ?? '') };
99
+ }).filter((row): row is NonNullable<typeof row> => row !== null);
100
+ if (!stamps.length) return full('no clean hard rehearsal receipt');
101
+ if (stamps.some((row) => !Number.isFinite(row.ended))) return full('hard rehearsal completion time unmeasured');
102
+ stamps.sort((a, b) => b.ended - a.ended);
103
+ if (stamps.length > 1 && stamps[0]!.ended === stamps[1]!.ended) return full('hard rehearsal order ambiguous');
104
+ for (const { step, stamp } of stamps.slice(0, 1)) {
105
+ if (stamp.version !== input.version || stamp.step !== step || stamp.outcome !== 'ok' || !stamp.sha || !SHA.test(stamp.sha)
106
+ || stamp.hardGate?.outcome !== 'ok' || stamp.hardGate.commit !== stamp.sha || stamp.hardGate.executor !== 'pod'
107
+ || !Array.isArray(stamp.hardGate.introduced) || stamp.hardGate.introduced.length)
108
+ return full('latest hard rehearsal is not clean');
109
+ let gateVersion: string;
110
+ try { gateVersion = hardRehearsalGateVersion(input.version, stamp.hardGateRunId ?? ''); }
111
+ catch { return full('hard rehearsal run identity invalid'); }
112
+ const dir = join(input.root, 'release', gateVersion);
113
+ const verdict = JSON.parse(readFileSync(join(dir, 'gate-verdict.json'), 'utf8')) as {
114
+ commit?: string; outcome?: string; baselineReceipt?: GateBaselineReceipt };
115
+ const failures = JSON.parse(readFileSync(join(dir, 'gate-failures.json'), 'utf8')) as {
116
+ commit?: string; failures?: string[]; errors?: string[] };
117
+ const receipt = verdict.baselineReceipt;
118
+ if (verdict.commit !== stamp.sha || verdict.outcome !== 'ok' || failures.commit !== stamp.sha
119
+ || !Array.isArray(failures.failures) || failures.failures.length !== 0
120
+ || !Array.isArray(failures.errors) || failures.errors.length !== 0
121
+ || !receipt || receipt.schema !== 1 || !receipt.valid || receipt.version !== gateVersion || receipt.commit !== stamp.sha
122
+ || receipt.graphSha !== input.graphPins.graphSha || receipt.recipeSha !== input.graphPins.recipeSha
123
+ || receipt.envFingerprint !== input.envFingerprint || receipt.imageDigest !== input.imageDigest.toLowerCase()
124
+ || !Number.isSafeInteger(receipt.files) || receipt.files < 1 || !Number.isSafeInteger(receipt.shards) || receipt.shards < 1)
125
+ return full('hard rehearsal receipt or current execution identity differs');
126
+ const recomputed = gateBaselineReceipt({ root: input.root, version: gateVersion, commit: stamp.sha,
127
+ fullSweep: true, graphPins: input.graphPins, envFingerprint: input.envFingerprint });
128
+ if (!recomputed.valid || recomputed.imageDigest !== receipt.imageDigest || recomputed.files !== receipt.files
129
+ || recomputed.shards !== receipt.shards) return full(`hard rehearsal Pod proof differs: ${recomputed.reason}`);
130
+ const ancestor = spawnSync('git', ['merge-base', '--is-ancestor', stamp.sha, input.forCommit], { cwd: input.repo, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
131
+ if (ancestor.status !== 0 || ancestor.error) return full('hard rehearsal commit is not an ancestor');
132
+ const expectedFiles = gateAssignableTestsAtCommit(input.repo, stamp.sha);
133
+ const measuredFiles = readdirSync(join(dir, 'gate-logs', 'cut')).filter((name) => /^pod-\d+\.json$/.test(name))
134
+ .flatMap((name) => {
135
+ const row = JSON.parse(readFileSync(join(dir, 'gate-logs', 'cut', name), 'utf8')) as { files?: unknown };
136
+ return Array.isArray(row.files) ? row.files : [];
137
+ });
138
+ if (!expectedFiles || JSON.stringify(measuredFiles.sort()) !== JSON.stringify(expectedFiles)
139
+ || measuredFiles.length !== receipt.files)
140
+ return full('hard rehearsal file inventory differs from receipt');
141
+ const tree = gitClosureTree(input.repo, input.forCommit);
142
+ const tests = tree.files.filter((file) => TEST.test(file));
143
+ const changed = changedFilesBetween(input.repo, stamp.sha, input.forCommit);
144
+ const bumps = versionOnlyBumps(input.repo, stamp.sha, input.forCommit, input.version, changed);
145
+ const effective = changed.filter((file) => !bumps.includes(file));
146
+ // A version-only JSON/lock bump is still observable to tests that read it. Follow those literal readers and
147
+ // opaque tests before removing the bump from the ordinary code-only partial planner.
148
+ const versionEnvReaders = bumps.includes('package.json')
149
+ ? tree.files.filter((file) => CODE_FILE.test(file) && tree.read(file)?.includes('npm_package_version')) : [];
150
+ const bumpRadius = bumps.length ? testsReachingChanged(tree, [...bumps, ...versionEnvReaders], tests) : null;
151
+ if (bumpRadius && !bumpRadius.ok) return full(bumpRadius.reason);
152
+ const planned = planPartialRegate({ version: input.version, priorCommit: stamp.sha, forCommit: input.forCommit,
153
+ changedFiles: effective, priorFailures: failures.failures, priorErrors: failures.errors ?? [],
154
+ existsAtTip: (file) => tree.files.includes(file),
155
+ closure: (code) => testsReachingChanged(tree, code, tests) });
156
+ if (!planned.ok && effective.length) return full(planned.reason);
157
+ let fileSet: string[];
158
+ let base: PartialPlan;
159
+ if (planned.ok) {
160
+ if (planned.plan.kind === 'carry' && !bumpRadius?.ok) return full('docs-only carry requires full gate across releases');
161
+ base = planned.plan.kind === 'carry' ? { ...planned.plan, kind: undefined } : planned.plan;
162
+ fileSet = planned.plan.files;
163
+ } else {
164
+ let priorFiles: string[];
165
+ try { priorFiles = [...failures.failures, ...(failures.errors ?? [])].map(testFileOfId); }
166
+ catch { return full('hard rehearsal failure ids unreadable'); }
167
+ base = { version: input.version, priorCommit: stamp.sha, forCommit: input.forCommit,
168
+ files: [], priorFailures: failures.failures, priorErrors: failures.errors ?? [] };
169
+ fileSet = priorFiles;
170
+ }
171
+ const files = [...new Set([...fileSet, ...(bumpRadius?.ok ? bumpRadius.tests : [])])].sort();
172
+ if (!files.length || files.some((file) => !tests.includes(file))) return full('empty or missing rehearsal radius requires full gate');
173
+ return { plan: { ...base, files }, reason: 'hard rehearsal receipt and import radius verified', rehearsalRunId: stamp.hardGateRunId };
174
+ }
175
+ return full('no clean hard rehearsal receipt');
176
+ } catch (error) { return full(`hard rehearsal evidence unreadable: ${String(error)}`); }
177
+ }
@@ -0,0 +1,138 @@
1
+ /** Observation only: compare a proposed import radius with the full gate that actually ran. */
2
+ import { randomUUID } from 'node:crypto';
3
+ import { existsSync, linkSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
4
+ import { dirname, join } from 'node:path';
5
+ import { spawnSync } from 'node:child_process';
6
+ import { changedFilesBetween, planPartialRegate, testFileOfId } from './gate-partial';
7
+ import { gitClosureTree, testsReachingChanged } from './gate-closure';
8
+ import { SPAWN_SYNC_MAX_BUFFER } from '../../src/util/spawn-sync-output.js';
9
+
10
+ const SHA = /^[0-9a-f]{40}$/i;
11
+ const VERSION = /^\d+\.\d+\.\d+(?:-(?:rc|alpha|beta)\.\d+)?$/;
12
+ const TEST = /\.test\.(?:tsx?|jsx?|mts|cts)$/;
13
+ const BUMP_FILES = new Set(['package.json', 'server.json', 'integrations/acp-registry/elanous/agent.json', 'bun.lock']);
14
+
15
+ export interface GateRadiusShadowPlan {
16
+ schema: 1;
17
+ version: string;
18
+ priorCommit: string | null;
19
+ forCommit: string;
20
+ proposed: 'partial' | 'full';
21
+ files: string[];
22
+ totalTests: number | null;
23
+ reason: string;
24
+ }
25
+
26
+ export interface GateRadiusShadowRecord {
27
+ plan: GateRadiusShadowPlan;
28
+ actual: { commit: string; outcome: 'ok' | 'regression' | 'error'; cutRc: number | null; introduced: string[];
29
+ cutFailures: string[]; cutErrors: string[]; missedIntroduced: string[]; fullSweep: boolean;
30
+ comparison: 'complete' | 'unmeasured' };
31
+ recordedAt: string;
32
+ }
33
+
34
+ function blob(repo: string, commit: string, file: string): unknown {
35
+ const run = spawnSync('git', ['show', `${commit}:${file}`], { cwd: repo, encoding: 'utf8', maxBuffer: SPAWN_SYNC_MAX_BUFFER });
36
+ if (run.status !== 0 || run.error) throw new Error(`unreadable release blob ${file}`);
37
+ return JSON.parse(run.stdout);
38
+ }
39
+
40
+ /** Remove only the fields version-node itself changes; any other JSON difference leaves a full shadow plan. */
41
+ export function versionOnlyBumps(repo: string, from: string, to: string, version: string, changed: readonly string[]): string[] {
42
+ if (!changed.includes('package.json')) return [];
43
+ const prior = blob(repo, from, 'package.json') as Record<string, unknown>;
44
+ const next = blob(repo, to, 'package.json') as Record<string, unknown>;
45
+ if (typeof prior.version !== 'string' || prior.version === version || next.version !== version) return [];
46
+ const inert: string[] = [];
47
+ for (const file of changed.filter((name) => BUMP_FILES.has(name))) {
48
+ const left = blob(repo, from, file) as Record<string, any>;
49
+ const right = blob(repo, to, file) as Record<string, any>;
50
+ const normalize = (row: Record<string, any>, expected: string): boolean => {
51
+ if (file === 'package.json') { if (row.version !== expected) return false; row.version = '<version>'; }
52
+ else if (file === 'server.json') {
53
+ if (row.version !== expected || row.packages?.[0]?.version !== expected) return false;
54
+ row.version = '<version>'; row.packages[0].version = '<version>';
55
+ } else if (file.endsWith('/agent.json')) {
56
+ if (row.version !== expected || row.distribution?.npx?.package !== `elanous@${expected}`) return false;
57
+ row.version = '<version>'; row.distribution.npx.package = 'elanous@<version>';
58
+ } else {
59
+ if (row.workspaces?.['']?.version !== expected) return false;
60
+ row.workspaces[''].version = '<version>';
61
+ }
62
+ return true;
63
+ };
64
+ if (normalize(left, prior.version) && normalize(right, version) && JSON.stringify(left) === JSON.stringify(right)) inert.push(file);
65
+ }
66
+ return inert;
67
+ }
68
+
69
+ export function planGateRadiusShadow(input: { repo: string; ledgerRoot: string; version: string;
70
+ baselineVersion: string; forCommit: string; instanceRoot?: string; expectedBaselineCommit?: string }): GateRadiusShadowPlan {
71
+ const { repo, ledgerRoot, version, baselineVersion, forCommit } = input;
72
+ const full = (reason: string, priorCommit: string | null = null, totalTests: number | null = null): GateRadiusShadowPlan =>
73
+ ({ schema: 1, version, priorCommit, forCommit, proposed: 'full', files: [], totalTests, reason });
74
+ if (!VERSION.test(version) || !VERSION.test(baselineVersion) || !SHA.test(forCommit)) return full('invalid identity');
75
+ try {
76
+ const priorDir = join(ledgerRoot, 'release', baselineVersion);
77
+ const verdict = JSON.parse(readFileSync(join(priorDir, 'gate-verdict.json'), 'utf8')) as { commit?: string; outcome?: string };
78
+ const failures = JSON.parse(readFileSync(join(priorDir, 'gate-failures.json'), 'utf8')) as
79
+ { commit?: string; failures?: string[]; errors?: string[] };
80
+ const priorCommit = verdict.commit;
81
+ const manifestPaths = [...new Set([ledgerRoot, input.instanceRoot].filter((root): root is string => !!root))]
82
+ .map((root) => join(root, 'release', baselineVersion, 'release.json')).filter(existsSync);
83
+ if (!manifestPaths.length) return full('prior release manifest absent', priorCommit ?? null);
84
+ const manifestCommits = manifestPaths.map((path) =>
85
+ (JSON.parse(readFileSync(path, 'utf8')) as { sourceCommit?: unknown }).sourceCommit);
86
+ if (manifestCommits.some((commit) => typeof commit !== 'string' || !SHA.test(commit) || commit !== priorCommit)
87
+ || (input.expectedBaselineCommit && input.expectedBaselineCommit !== priorCommit))
88
+ return full('prior gate commit differs from release source', priorCommit ?? null);
89
+ if (!priorCommit || !SHA.test(priorCommit) || failures.commit !== priorCommit || verdict.outcome !== 'ok'
90
+ || !Array.isArray(failures.failures) || !Array.isArray(failures.errors ?? []))
91
+ return full('prior gate evidence absent or not clean', priorCommit ?? null);
92
+ const tree = gitClosureTree(repo, forCommit);
93
+ const tests = tree.files.filter((file) => TEST.test(file));
94
+ const changed = changedFilesBetween(repo, priorCommit, forCommit);
95
+ const bump = versionOnlyBumps(repo, priorCommit, forCommit, version, changed);
96
+ const effective = changed.filter((file) => !bump.includes(file));
97
+ if (!effective.length && bump.length) {
98
+ const rerun = [...new Set([...failures.failures, ...(failures.errors ?? [])].map(testFileOfId))].sort();
99
+ if (rerun.some((file) => !tests.includes(file))) return full('prior failure file absent at cut', priorCommit, tests.length);
100
+ return { schema: 1, version, priorCommit, forCommit, proposed: 'partial', files: rerun,
101
+ totalTests: tests.length, reason: 'version-only candidate' };
102
+ }
103
+ const result = planPartialRegate({ version, priorCommit, forCommit, changedFiles: effective,
104
+ priorFailures: failures.failures, priorErrors: failures.errors ?? [],
105
+ existsAtTip: (file) => tree.files.includes(file),
106
+ closure: (code) => testsReachingChanged(tree, code, tests) });
107
+ if (!result.ok) return full(result.reason, priorCommit, tests.length);
108
+ // Shadow only: this proposal is never passed to writePartialPlan/readPartialPlan or the sweep selector.
109
+ return { schema: 1, version, priorCommit, forCommit, proposed: 'partial', files: result.plan.files,
110
+ totalTests: tests.length, reason: result.plan.kind === 'carry' ? 'zero-file candidate' : 'import radius candidate' };
111
+ } catch (error) { return full(`shadow evidence unreadable: ${String(error)}`); }
112
+ }
113
+
114
+ /** Append beside the gate verdict; a shadow write never changes a release decision. */
115
+ export function writeGateRadiusShadow(root: string, record: GateRadiusShadowRecord): string {
116
+ const { plan, actual } = record;
117
+ if (!VERSION.test(plan.version) || !SHA.test(plan.forCommit) || actual.commit !== plan.forCommit
118
+ || !['ok', 'regression', 'error'].includes(actual.outcome)) throw new Error('invalid gate radius shadow');
119
+ const path = join(root, 'release', plan.version, `gate-radius-shadow-${plan.forCommit}-${randomUUID()}.json`);
120
+ mkdirSync(dirname(path), { recursive: true });
121
+ const temp = `${path}.${process.pid}.tmp`;
122
+ try {
123
+ writeFileSync(temp, `${JSON.stringify(record, null, 2)}\n`, { mode: 0o600, flag: 'wx' });
124
+ linkSync(temp, path);
125
+ } finally { rmSync(temp, { force: true }); }
126
+ return path;
127
+ }
128
+
129
+ export function shadowActual(input: { plan: GateRadiusShadowPlan; outcome: 'ok' | 'regression' | 'error';
130
+ introduced: readonly string[]; cutFailures: readonly string[]; cutErrors: readonly string[]; cutRc: number | null;
131
+ fullSweep: boolean }): GateRadiusShadowRecord['actual'] {
132
+ const selected = new Set(input.plan.files);
133
+ const missedIntroduced = input.plan.proposed === 'partial' && input.fullSweep
134
+ ? input.introduced.filter((id) => { try { return !selected.has(testFileOfId(id)); } catch { return true; } }) : [];
135
+ return { commit: input.plan.forCommit, outcome: input.outcome, cutRc: input.cutRc, introduced: [...input.introduced],
136
+ cutFailures: [...input.cutFailures], cutErrors: [...input.cutErrors], missedIntroduced, fullSweep: input.fullSweep,
137
+ comparison: input.fullSweep ? 'complete' : 'unmeasured' };
138
+ }
@@ -0,0 +1,128 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, rmdirSync, statSync, writeFileSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { debug } from '../../src/debug/log.js';
6
+
7
+ type TrafficBoard = Record<string, unknown> & { global: string };
8
+ type PatchResult = { changed: true; previous: string; warning?: string } | { changed: false; current: string };
9
+
10
+ const defaultTrafficPath = () => join(homedir(), 'elanous-hq', 'seat-state', 'OP', 'traffic.json');
11
+ const trafficPath = (env: NodeJS.ProcessEnv) => env.ELANOUS_TRAFFIC_SIGNAL || defaultTrafficPath();
12
+
13
+ function releaseGateContext(env: NodeJS.ProcessEnv): boolean {
14
+ const source = env.ELANOUS_GRAPH_CONTEXT;
15
+ if (!source) return false;
16
+ try {
17
+ const raw = JSON.parse(source.trimStart().startsWith('{') ? source : readFileSync(source, 'utf8')) as Record<string, unknown>;
18
+ return raw.graphId === 'release-loop' && raw.nodeId === 'gate';
19
+ } catch {
20
+ return false;
21
+ }
22
+ }
23
+
24
+ function withTrafficLock<T>(path: string, action: () => T): T {
25
+ const lock = `${path}.lock.d`;
26
+ const deadline = Date.now() + 10_000;
27
+ for (let attempt = 1; attempt <= 100; attempt++) {
28
+ try { mkdirSync(lock); }
29
+ catch (error) {
30
+ if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
31
+ try {
32
+ const stat = statSync(lock).mtimeMs;
33
+ const owner = Number(readFileSync(join(lock, 'pid'), 'utf8').trim());
34
+ if (Date.now() - stat > 60_000 && Number.isInteger(owner) && owner > 0) {
35
+ try { process.kill(owner, 0); }
36
+ catch (probeError) {
37
+ if ((probeError as NodeJS.ErrnoException).code === 'ESRCH'
38
+ && Number(readFileSync(join(lock, 'pid'), 'utf8').trim()) === owner) {
39
+ try { renameSync(lock, `${lock}.stale.${process.pid}.${attempt}`); } catch { /* owner may have released it */ }
40
+ }
41
+ }
42
+ }
43
+ } catch { /* unknown owner and unreadable locks are never reaped */ }
44
+ if (attempt === 100 || Date.now() >= deadline) throw new Error(`traffic lock timeout: ${lock}`);
45
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 100);
46
+ continue;
47
+ }
48
+ try {
49
+ writeFileSync(join(lock, 'pid'), `${process.pid}\n`);
50
+ return action();
51
+ } finally {
52
+ rmSync(join(lock, 'pid'), { force: true });
53
+ try { rmdirSync(lock); } catch { /* another writer replaced the lock directory */ }
54
+ }
55
+ }
56
+ throw new Error(`traffic lock timeout: ${lock}`);
57
+ }
58
+
59
+ function patchGlobal(path: string, next: string, reason: string, onlyIf?: string): PatchResult {
60
+ return withTrafficLock(path, () => {
61
+ if (!existsSync(path)) throw new Error(`traffic board missing: ${path}`);
62
+ const board = JSON.parse(readFileSync(path, 'utf8')) as TrafficBoard;
63
+ if (!board || typeof board !== 'object' || Array.isArray(board) || typeof board.global !== 'string') {
64
+ throw new Error(`invalid traffic board: ${path}`);
65
+ }
66
+ if (onlyIf !== undefined && board.global !== onlyIf) return { changed: false, current: board.global };
67
+ const updated: TrafficBoard = { ...board, global: next, reason, updatedAt: new Date().toISOString() };
68
+ const at = new Date();
69
+ const stamp = [at.getMonth() + 1, at.getDate(), at.getHours(), at.getMinutes(), at.getSeconds()].map(value => String(value).padStart(2, '0')).join('');
70
+ const suffix = `bak-${stamp}-${randomUUID().slice(0, 8)}-global-${next}`;
71
+ const backup = `${path}.${suffix}`;
72
+ const temp = `${path}.tmp.${process.pid}.${randomUUID()}`;
73
+ const payload = `${JSON.stringify(updated)}\n`;
74
+ writeFileSync(backup, readFileSync(path));
75
+ writeFileSync(temp, payload, { mode: statSync(path).mode & 0o777 });
76
+ try { renameSync(temp, path); }
77
+ finally { rmSync(temp, { force: true }); }
78
+ try {
79
+ const verify = JSON.parse(readFileSync(path, 'utf8')) as TrafficBoard;
80
+ if (verify.global !== next) return { changed: true, previous: board.global, warning: `traffic verification mismatch: ${path}` };
81
+ } catch (error) {
82
+ // rename already committed the requested mode; keep the lease so the caller still attempts cleanup.
83
+ return { changed: true, previous: board.global, warning: `traffic verification failed: ${String(error)}` };
84
+ }
85
+ return { changed: true, previous: board.global };
86
+ });
87
+ }
88
+
89
+ /** Hold dispatcher traffic at gate only for the release-loop's gate node, then restore conditionally. */
90
+ export async function runWithReleaseGateTraffic<T>(env: NodeJS.ProcessEnv, run: () => Promise<T>): Promise<T> {
91
+ if (!releaseGateContext(env)) return run();
92
+ const path = trafficPath(env);
93
+ let started: PatchResult;
94
+ try { started = patchGlobal(path, 'gate', 'release gate running', 'green'); }
95
+ catch (error) {
96
+ debug.log('release-loop.gate', 'traffic gate set failed', { path, error: String(error) }, { level: 'warn' });
97
+ return run(); // The traffic board is advisory; it must not block publication.
98
+ }
99
+ if (!started.changed) return run(); // Existing gate/yellow (or any operator change) belongs to its owner.
100
+ if (started.warning) debug.log('release-loop.gate', 'traffic gate verification failed', { path, error: started.warning }, { level: 'warn' });
101
+ let restoreResult: Promise<void> | undefined;
102
+ const restoreOnce = () => restoreResult ??= Promise.resolve().then(() => {
103
+ try {
104
+ const restored = patchGlobal(path, started.previous, 'release gate finished', 'gate');
105
+ if (!restored.changed) debug.log('release-loop.gate', 'traffic restore skipped', { path, current: restored.current }, { level: 'warn' });
106
+ else if (restored.warning) debug.log('release-loop.gate', 'traffic restore verification failed', { path, error: restored.warning }, { level: 'warn' });
107
+ } catch (error) {
108
+ debug.log('release-loop.gate', 'traffic restore failed', { path, error: String(error) }, { level: 'warn' });
109
+ }
110
+ });
111
+ const interrupt = (signal: NodeJS.Signals) => {
112
+ void restoreOnce().finally(() => {
113
+ process.removeListener(signal, signal === 'SIGINT' ? onSigint : onSigterm);
114
+ process.kill(process.pid, signal);
115
+ });
116
+ };
117
+ const onSigint = () => interrupt('SIGINT');
118
+ const onSigterm = () => interrupt('SIGTERM');
119
+ process.once('SIGINT', onSigint);
120
+ process.once('SIGTERM', onSigterm);
121
+ try {
122
+ return await run();
123
+ } finally {
124
+ process.removeListener('SIGINT', onSigint);
125
+ process.removeListener('SIGTERM', onSigterm);
126
+ await restoreOnce();
127
+ }
128
+ }
@@ -120,7 +120,7 @@ export function gateTriageShadowFromContext(context: GraphContext, evidence?: Ga
120
120
  && item.files.every((file: unknown) => typeof file === 'string') && typeof item.reason === 'string')))
121
121
  || (value.loadFlaky !== undefined && (!Array.isArray(value.loadFlaky)
122
122
  || !value.loadFlaky.every((item) => typeof item === 'string')))
123
- || !['ok', 'regression', 'error'].includes(String(value.outcome))) {
123
+ || !['ok', 'regression', 'fail', 'error'].includes(String(value.outcome))) {
124
124
  throw new Error('gate output is missing or malformed');
125
125
  }
126
126
  const input = context.input.gateTriageEvidence;
@@ -140,7 +140,10 @@ export function gateTriageShadowFromContext(context: GraphContext, evidence?: Ga
140
140
  && item.failureIds.every((id: unknown) => typeof id === 'string'))
141
141
  ? { priorDecisions: supplied.priorDecisions as GateTriageEvidence['priorDecisions'] } : {}),
142
142
  };
143
- return classifyGateShadow(value as unknown as GateResult, observed);
143
+ // The graph adapter maps a measured gate regression to `fail` for edge routing.
144
+ // Restore the gate verdict before classifying the shadow; keep the context untouched.
145
+ const gate = value.outcome === 'fail' ? { ...value, outcome: 'regression' } : value;
146
+ return classifyGateShadow(gate as unknown as GateResult, observed);
144
147
  }
145
148
 
146
149
  export function logGateTriageShadow(result: GateTriageShadow, version: string): void {