elanous 0.2.38 → 0.2.41

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 (712) hide show
  1. package/README.md +7 -7
  2. package/apps/pwa/out/404/index.html +1 -1
  3. package/apps/pwa/out/404.html +1 -1
  4. package/apps/pwa/out/_next/static/{RrjXTwz-urRAbChP1bX02 → GiOLWmt4MqDZePubBjA4Q}/_buildManifest.js +1 -1
  5. package/apps/pwa/out/_next/static/chunks/1115.434ae2ecb0ad986d.js +149 -0
  6. package/apps/pwa/out/_next/static/chunks/1121.667cec0bcdbb3fdb.js +36 -0
  7. package/apps/pwa/out/_next/static/chunks/1178.1aa9cad0e7695427.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1197-54da59d250a6e476.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/{5203.51e5d8b5f1dcfe53.js → 1219.7e17964e0e7cd8e5.js} +3 -3
  10. package/apps/pwa/out/_next/static/chunks/1245.1cfd13b26dbd71d6.js +287 -0
  11. package/apps/pwa/out/_next/static/chunks/1494-f93046ff5c5a124d.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/1609.2b312797599b8e45.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/1661.0dd330f3d89f73f3.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/167-0c121e4b47e0f9da.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/1700.cdc2bf8cc685e221.js +90 -0
  16. package/apps/pwa/out/_next/static/chunks/1703-78e46acad29f3cfc.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/{7573.2376e13c9c21f1f8.js → 1765.679b29625c8e88d4.js} +2 -2
  18. package/apps/pwa/out/_next/static/chunks/{3528.e4b8ab70d339f965.js → 1800.f7ff51dc4978a958.js} +1 -1
  19. package/apps/pwa/out/_next/static/chunks/1971-7efb628c471dc5a0.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/1999-8de7b7bb36801913.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/2073.689dcb2680113d76.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/2198-557d49184104d165.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/221-440ce67fa9f5c839.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/{8879.ac06755bcf3e44a1.js → 2263.e09b6edea33bad2d.js} +1 -1
  25. package/apps/pwa/out/_next/static/chunks/2427-8ed8e98624cbd664.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/2457-725c5827bc003795.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/2491-2718b15a5ccb75a5.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2527-5c636a43c7cc5dde.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/2692-1027cb16fe4ccf49.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/2743.5d3f76948be32b34.js +41 -0
  31. package/apps/pwa/out/_next/static/chunks/2773.0e80b9d325fb5635.js +32 -0
  32. package/apps/pwa/out/_next/static/chunks/2873-0f3cb56c8e3528ea.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/{9fe1b809.738da85c8b17e3fb.js → 2fdf2931.b431eee4fb468bc3.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/{eaad0cba.771200940c529df7.js → 2fe2c302.41b517f88d6fe502.js} +1 -1
  35. package/apps/pwa/out/_next/static/chunks/3097.864e92bdaa2f4d15.js +79 -0
  36. package/apps/pwa/out/_next/static/chunks/3136-d6870ba63300a93e.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/3173-0289113d434e83b7.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/3230-49298c3b73ef2268.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/325-dd9208a1a0ab7ce4.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/3283.528f430dd043fb4e.js +74 -0
  41. package/apps/pwa/out/_next/static/chunks/3295.cc8cb13b8f0dae1a.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/3688-86e3b88abf18cb3b.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/3804-7220467a5473a8df.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/3808.3e1e247ce7bdf889.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/{b7478fef.5917716286213fcc.js → 385e4574.e9886ab32759eda3.js} +3 -3
  46. package/apps/pwa/out/_next/static/chunks/3927.b5473ad109da44c5.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/3934-98ea94832e194796.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/4133.2056bb4d78f621af.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/4156.ba4de4a3664f04b4.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/4297-239ca82f293faab0.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/4320-dcc5a7734126f4c9.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/4434.94b4c552a2d91e69.js +148 -0
  53. package/apps/pwa/out/_next/static/chunks/{8911.bf5e712acb25fb6c.js → 4527.a69f2894d1bc9f4a.js} +1 -1
  54. package/apps/pwa/out/_next/static/chunks/4579.03e4092f73950965.js +226 -0
  55. package/apps/pwa/out/_next/static/chunks/{5307.d7328020227880c2.js → 4667.97e31aa38c95c475.js} +4 -4
  56. package/apps/pwa/out/_next/static/chunks/{6142.913b44e260a3f925.js → 4702.4a4adc1f16d7c93b.js} +1 -1
  57. package/apps/pwa/out/_next/static/chunks/4815.481501c57b3568d9.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/487-d16e698ef14662d9.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/4888-7a38bf522c9cc6fe.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/5034-30e429d6bb086881.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/5040-35c494e60dadd97d.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/50c6ab1e.81a96e419350909d.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/5154.4b6a836179356f30.js +94 -0
  64. package/apps/pwa/out/_next/static/chunks/{8487.291666583bebe640.js → 5159.115f1c2ca471386c.js} +1 -1
  65. package/apps/pwa/out/_next/static/chunks/5228-a8200df486278f2d.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/530.d26346e18089640a.js +115 -0
  67. package/apps/pwa/out/_next/static/chunks/5379.97af106d09566f69.js +8 -0
  68. package/apps/pwa/out/_next/static/chunks/{89d6c04f-6b8ab341748e468a.js → 53aeb4ee-51c92f8d683f5220.js} +1 -1
  69. package/apps/pwa/out/_next/static/chunks/{3291.f6c1005387d514bd.js → 5471.1e611302ed3a7c53.js} +2 -2
  70. package/apps/pwa/out/_next/static/chunks/5479-b6372e762719f92f.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/5534-699c5cbcbc96c5d7.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/{3086.fb14b60ce98b53d3.js → 5574.f35accf4f132b29d.js} +1 -1
  73. package/apps/pwa/out/_next/static/chunks/5701.9c4e8f250312eeee.js +24 -0
  74. package/apps/pwa/out/_next/static/chunks/{1632.7d87f998e714bede.js → 5744.b9415e18ad623100.js} +1 -1
  75. package/apps/pwa/out/_next/static/chunks/{5300.677c97e0393742e6.js → 5892.2526d962339662d8.js} +1 -1
  76. package/apps/pwa/out/_next/static/chunks/5937.c2df9e3447e1f667.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/5941.8210cc93c9202c0a.js +286 -0
  78. package/apps/pwa/out/_next/static/chunks/{7161-8f22db3ec5ed005e.js → 5947-135e26537d80a6b9.js} +3 -3
  79. package/apps/pwa/out/_next/static/chunks/{3124-561f7f037943cde2.js → 5964-7e21164a8f2cf0ce.js} +1 -1
  80. package/apps/pwa/out/_next/static/chunks/5975.4004f820ae3cecd5.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/6142-7ccd48795d7abd55.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/6160.bf9177b8c1bfb243.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/6398.71ba0690c8027555.js +27 -0
  84. package/apps/pwa/out/_next/static/chunks/6492.2bd5c10edc1a064e.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/6552.a3840aa7da35ad68.js +62 -0
  86. package/apps/pwa/out/_next/static/chunks/6736.1e36d3ac5fa5e41d.js +24 -0
  87. package/apps/pwa/out/_next/static/chunks/6874.d5cf7eb8edb2cabe.js +2 -0
  88. package/apps/pwa/out/_next/static/chunks/6900.3ccbe069bdd4556a.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/71327eb4.add50b365f94bf3a.js +122 -0
  90. package/apps/pwa/out/_next/static/chunks/7136.deca3e3a2ddaa374.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/7139-ee9f8ed2677a40b3.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/{2415.738b777642a02233.js → 7191.fdc955f7d96064a4.js} +1 -1
  93. package/apps/pwa/out/_next/static/chunks/7423-e0aa5d9e2962120e.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/7453.afc1ea7e05d56a8c.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/7503-5e6a3d7fe67e4a0c.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/7761-476fd7bf768abd34.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/7975.eafb12097a0a9e62.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/{1026-44010f405704050c.js → 8050-9b364bdef8f71927.js} +1 -1
  99. package/apps/pwa/out/_next/static/chunks/8152.64e253c35cccfaae.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/8190.620f0231b3924bcb.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/83184dcd-0ee6247ff41eb3f2.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/8448.45b6b1f1a99eeb35.js +104 -0
  103. package/apps/pwa/out/_next/static/chunks/8464-e24f10451b658aac.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8477.e4c944bb3aa5d2f3.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/8710.ca385257d5ca422e.js +1 -0
  106. package/apps/pwa/out/_next/static/chunks/8830.627ccf294ec2465c.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/8880.64c445592f04acaf.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/8981.c706964c8ba27aff.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/{5638.107314dc479e2f37.js → 9048.212083de42d25be3.js} +2 -2
  110. package/apps/pwa/out/_next/static/chunks/9053.bd0fffaff8d3f836.js +1 -0
  111. package/apps/pwa/out/_next/static/chunks/9161-c66e723d7ec9d682.js +1 -0
  112. package/apps/pwa/out/_next/static/chunks/9185.1ecdf9b65c0bb684.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/9356.fccd5f58086b4ff5.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/9446.dc0406109371d329.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/9506.dcf25d467a42627e.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/{8019.21ca554a821dad01.js → 9507.7026657a2f47eb64.js} +16 -16
  117. package/apps/pwa/out/_next/static/chunks/9541.eb8e2f8480c7feca.js +93 -0
  118. package/apps/pwa/out/_next/static/chunks/9577.ed7582186efa5482.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/9579.4a8169600fff2058.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/9642-ff2e039eb62e3b92.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/9659-8f2323348113ad80.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/9724.4c5623af2db43153.js +34 -0
  123. package/apps/pwa/out/_next/static/chunks/{6493.27ad77de7aac7966.js → 9834.63ab2bbc3153172e.js} +1 -1
  124. package/apps/pwa/out/_next/static/chunks/{3389.012c70767a872f9b.js → 9869.81a1e24ca682d5a0.js} +1 -1
  125. package/apps/pwa/out/_next/static/chunks/{8994d93e.33ba491f95345fc2.js → a0a01613.6d1cca322238d7fe.js} +1 -1
  126. package/apps/pwa/out/_next/static/chunks/{de8b930d.d94c0f42acf807fc.js → addc2b32.8c4ff8f0aefe987d.js} +1 -1
  127. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-8d9d76ea322d855c.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/approvals/page-ed2c4e59f9f1fe21.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-2dc99e03c5d76ca0.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/board/page-b5a3a788330e920f.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-866aac5ff226a7f3.js → page-14949db884bfa672.js} +1 -1
  132. package/apps/pwa/out/_next/static/chunks/app/bots/page-ea76c85b36388a97.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/ceo/page-01716d20af62d325.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/chat/page-62baf6881a64b1e9.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/consult/page-f89ba590c2af8afa.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/control/page-22f8b0f78d9637e5.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/decisions/page-817f9d7e6820be80.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/design-check/page-ffb9bc0d2650249a.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/editor/page-f6d0e64873c0f629.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/exec/page-8bfa0d4ba23a209d.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/field/page-f5060462249e32c7.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/healthz/route-8d9d76ea322d855c.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/inside/page-b2049f8b1ef79117.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/intake/page-1446c5fa93907f11.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/layout-b48a7a07c5204156.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/live/page-e974b8c4ca47614c.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/live-run/page-8a919b869bd28594.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/loops/page-d31b8de6bfcd72ed.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/market/page-64765f817909db29.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-d128c8569a36ee9b.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/morning/page-b2679d357e9b9270.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/not-found-6fabec98a9e7b269.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/observatory/page-048f30149dcf1349.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-9113181d6b5a2725.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-40f9e19149024298.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-1e7a755a13227611.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3c8f8b67a203cf07.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/page-18032f5437215062.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/reflection/page-da3b739192fd0ee3.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d332545571bd6fd1.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/sessions/page-5237aa5b3f3b8ec5.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a142dfb0331a05f5.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/settings/page-d16e56c3989c147e.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-6b6593600869e2cd.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/setup/layout-8d9d76ea322d855c.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/setup/page-29e8b8a8396b55cb.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/share/page-b3414d856a1a7e2f.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/showroom/page-c3affa52b7ce961b.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f21dddf9f95b6fab.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/term/page-f5a30a24745bf088.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/today/page-8c8569a09b4d4e34.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/trace/page-2ce32722c0243483.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/app/vault/page-4767b0d9c0a98c6c.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-116ee1339c387a75.js +1 -0
  175. package/apps/pwa/out/_next/static/chunks/app/workflows/page-35e8c50ca4ef7ffc.js +1 -0
  176. package/apps/pwa/out/_next/static/chunks/app/workspace/page-52e17113356bb891.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-7458ae05ed2ac1a0.js +1 -0
  178. package/apps/pwa/out/_next/static/chunks/b2271e54.c1de3347976e90b0.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/{b7459770.6539b11860eb8a9c.js → c0c1a699.0ba79f97005965d4.js} +7 -7
  180. package/apps/pwa/out/_next/static/chunks/framework-ad6c9dd524ad6ef8.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/main-app-2ddd1f2b44d6aa59.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/main-e3d82bdc358d20c5.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/pages/_app-71d5aa3896e1655f.js +1 -0
  184. package/apps/pwa/out/_next/static/chunks/pages/_error-03ffc54e00438546.js +1 -0
  185. package/apps/pwa/out/_next/static/chunks/webpack-56bb89af5a611d0b.js +1 -0
  186. package/apps/pwa/out/approvals/index.html +1 -1
  187. package/apps/pwa/out/approvals/index.txt +22 -22
  188. package/apps/pwa/out/autopilot/index.html +1 -1
  189. package/apps/pwa/out/autopilot/index.txt +22 -22
  190. package/apps/pwa/out/board/index.html +1 -1
  191. package/apps/pwa/out/board/index.txt +22 -22
  192. package/apps/pwa/out/botlab/index.html +1 -1
  193. package/apps/pwa/out/botlab/index.txt +21 -21
  194. package/apps/pwa/out/bots/index.html +1 -1
  195. package/apps/pwa/out/bots/index.txt +21 -21
  196. package/apps/pwa/out/ceo/index.html +1 -1
  197. package/apps/pwa/out/ceo/index.txt +22 -22
  198. package/apps/pwa/out/chat/index.html +1 -1
  199. package/apps/pwa/out/chat/index.txt +21 -21
  200. package/apps/pwa/out/consult/index.html +1 -1
  201. package/apps/pwa/out/consult/index.txt +22 -22
  202. package/apps/pwa/out/control/index.html +1 -1
  203. package/apps/pwa/out/control/index.txt +22 -22
  204. package/apps/pwa/out/decisions/index.html +1 -1
  205. package/apps/pwa/out/decisions/index.txt +21 -21
  206. package/apps/pwa/out/design-check/index.html +1 -1
  207. package/apps/pwa/out/design-check/index.txt +22 -22
  208. package/apps/pwa/out/editor/index.html +1 -1
  209. package/apps/pwa/out/editor/index.txt +22 -22
  210. package/apps/pwa/out/exec/index.html +1 -1
  211. package/apps/pwa/out/exec/index.txt +22 -22
  212. package/apps/pwa/out/field/index.html +1 -1
  213. package/apps/pwa/out/field/index.txt +22 -22
  214. package/apps/pwa/out/healthz +1 -1
  215. package/apps/pwa/out/index.html +1 -1
  216. package/apps/pwa/out/index.txt +21 -21
  217. package/apps/pwa/out/inside/index.html +1 -1
  218. package/apps/pwa/out/inside/index.txt +21 -21
  219. package/apps/pwa/out/intake/index.html +1 -1
  220. package/apps/pwa/out/intake/index.txt +22 -22
  221. package/apps/pwa/out/live/index.html +1 -1
  222. package/apps/pwa/out/live/index.txt +22 -22
  223. package/apps/pwa/out/live-run/index.html +1 -1
  224. package/apps/pwa/out/live-run/index.txt +22 -22
  225. package/apps/pwa/out/loops/index.html +1 -1
  226. package/apps/pwa/out/loops/index.txt +21 -21
  227. package/apps/pwa/out/market/index.html +1 -1
  228. package/apps/pwa/out/market/index.txt +21 -21
  229. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  230. package/apps/pwa/out/missions/placeholder/index.txt +21 -21
  231. package/apps/pwa/out/morning/index.html +1 -1
  232. package/apps/pwa/out/morning/index.txt +22 -22
  233. package/apps/pwa/out/observatory/index.html +1 -1
  234. package/apps/pwa/out/observatory/index.txt +22 -22
  235. package/apps/pwa/out/ops/checklist/index.html +1 -1
  236. package/apps/pwa/out/ops/checklist/index.txt +21 -21
  237. package/apps/pwa/out/ops/release/index.html +1 -1
  238. package/apps/pwa/out/ops/release/index.txt +21 -21
  239. package/apps/pwa/out/ops/seats/index.html +1 -1
  240. package/apps/pwa/out/ops/seats/index.txt +21 -21
  241. package/apps/pwa/out/outputs/index.html +1 -1
  242. package/apps/pwa/out/outputs/index.txt +21 -21
  243. package/apps/pwa/out/reflection/index.html +1 -1
  244. package/apps/pwa/out/reflection/index.txt +22 -22
  245. package/apps/pwa/out/scheduler/index.html +1 -1
  246. package/apps/pwa/out/scheduler/index.txt +22 -22
  247. package/apps/pwa/out/sessions/index.html +1 -1
  248. package/apps/pwa/out/sessions/index.txt +21 -21
  249. package/apps/pwa/out/settings/devices/index.html +1 -1
  250. package/apps/pwa/out/settings/devices/index.txt +22 -22
  251. package/apps/pwa/out/settings/index.html +1 -1
  252. package/apps/pwa/out/settings/index.txt +22 -22
  253. package/apps/pwa/out/setup/done/index.html +1 -1
  254. package/apps/pwa/out/setup/done/index.txt +22 -22
  255. package/apps/pwa/out/setup/index.html +1 -1
  256. package/apps/pwa/out/setup/index.txt +22 -22
  257. package/apps/pwa/out/share/index.html +1 -1
  258. package/apps/pwa/out/share/index.txt +22 -22
  259. package/apps/pwa/out/showroom/index.html +1 -1
  260. package/apps/pwa/out/showroom/index.txt +21 -21
  261. package/apps/pwa/out/tasks/index.html +1 -1
  262. package/apps/pwa/out/tasks/index.txt +22 -22
  263. package/apps/pwa/out/term/index.html +1 -1
  264. package/apps/pwa/out/term/index.txt +22 -22
  265. package/apps/pwa/out/today/index.html +1 -1
  266. package/apps/pwa/out/today/index.txt +22 -22
  267. package/apps/pwa/out/trace/index.html +1 -1
  268. package/apps/pwa/out/trace/index.txt +22 -22
  269. package/apps/pwa/out/vault/index.html +1 -1
  270. package/apps/pwa/out/vault/index.txt +22 -22
  271. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  272. package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
  273. package/apps/pwa/out/workflows/index.html +1 -1
  274. package/apps/pwa/out/workflows/index.txt +22 -22
  275. package/apps/pwa/out/workspace/index.html +1 -1
  276. package/apps/pwa/out/workspace/index.txt +22 -22
  277. package/apps/pwa/out/worktrees/index.html +1 -1
  278. package/apps/pwa/out/worktrees/index.txt +22 -22
  279. package/bin/elanous.mjs +6 -1
  280. package/catalog/models/openai/gpt-6.1-sol.yaml +16 -0
  281. package/catalog/models/openai/gpt-6.1-terra.yaml +16 -0
  282. package/graphs/demo/persona-marketer/fixtures/marketer-scenario.yaml +10 -0
  283. package/graphs/demo/persona-marketer/persona-marketer.yaml +25 -0
  284. package/graphs/demo/persona-marketer/recipes.yaml +19 -0
  285. package/graphs/demo/persona-teacher/lesson-pack.yaml +16 -0
  286. package/graphs/demo/persona-teacher/recipes.yaml +17 -0
  287. package/graphs/demo/persona-teacher/weekly-lesson.yaml +36 -0
  288. package/graphs/overlays/stop-heal-ladder.yaml +23 -2
  289. package/graphs/release/release-loop.yaml +3 -1
  290. package/graphs/units/gate/gate.yaml +20 -0
  291. package/graphs/units/git/git.yaml +26 -0
  292. package/graphs/units/implement/implement.yaml +19 -0
  293. package/graphs/units/landing/landing.yaml +18 -0
  294. package/graphs/units/review/review.yaml +20 -0
  295. package/graphs/units/verbs/recipes.yaml +3 -0
  296. package/graphs/units/verbs/research.yaml +20 -0
  297. package/graphs/video/explainer-line.yaml +4 -1
  298. package/graphs/work/research-report.yaml +11 -1
  299. package/package.json +1 -1
  300. package/scripts/dead-code/report.ts +169 -0
  301. package/scripts/disk-steward.ts +46 -0
  302. package/scripts/fleet/node-setup.sh +50 -0
  303. package/scripts/graph-units/verbs/research.ts +55 -0
  304. package/scripts/lib/morning-report-options.ts +3 -4
  305. package/scripts/lib/tui-regress-evidence.ts +56 -0
  306. package/scripts/llm-cost-ledger.ts +179 -8
  307. package/scripts/measure-nl-routing-corpus.ts +10 -2
  308. package/scripts/morning-report.ts +5 -1
  309. package/scripts/persona-teacher/weekly-lesson-node.ts +172 -0
  310. package/scripts/public-copy-lint.ts +163 -19
  311. package/scripts/public-export.ts +15 -7
  312. package/scripts/ratchets/train-side-invariants.sh +0 -1
  313. package/scripts/release-loop/export-check-node.ts +21 -3
  314. package/scripts/release-loop/gate-autoheal.ts +117 -0
  315. package/scripts/release-loop/gate-baseline-receipt.ts +8 -1
  316. package/scripts/release-loop/gate-diff.ts +22 -3
  317. package/scripts/release-loop/gate-node.ts +583 -64
  318. package/scripts/release-loop/gate-shard-dispatch.ts +128 -0
  319. package/scripts/release-loop/gate-shard-load-flaky.ts +256 -0
  320. package/scripts/release-loop/gate-shard-receipts.ts +89 -0
  321. package/scripts/release-loop/gate-triage-node.ts +70 -10
  322. package/scripts/release-loop/gate-wall.ts +5 -5
  323. package/scripts/release-loop/preflight.ts +6 -2
  324. package/scripts/release-loop/resume-release.ts +267 -20
  325. package/scripts/release-loop/resume.ts +48 -0
  326. package/scripts/release-loop/traffic-node.ts +6 -3
  327. package/scripts/release-loop/unattended-release.ts +4 -2
  328. package/scripts/release-loop/version-node.ts +7 -2
  329. package/scripts/release-story/draft.ts +1 -1
  330. package/scripts/research-bench/README.md +53 -0
  331. package/scripts/research-bench/questions.json +66 -0
  332. package/scripts/research-bench/rubric.md +33 -0
  333. package/scripts/research-bench/run.ts +523 -0
  334. package/scripts/shadow-authoring-probe.ts +20 -0
  335. package/scripts/tui-regress.ts +85 -10
  336. package/scripts/ux-sim.ts +19 -2
  337. package/src/acp/core-turn-bridge.ts +94 -4
  338. package/src/agent/autonomous-tools.ts +10 -3
  339. package/src/agent/monad-agent-turn.ts +4 -1
  340. package/src/agent/registry.ts +26 -2
  341. package/src/agent/shared-app-tools.ts +1 -1
  342. package/src/agent/surface-ux/build.ts +12 -3
  343. package/src/agent/surface-ux/types.ts +1 -1
  344. package/src/agent/task-notification.ts +75 -6
  345. package/src/agent/types.ts +3 -0
  346. package/src/agent-mission/driver.ts +29 -3
  347. package/src/agent-mission/mission-cli.ts +2 -1
  348. package/src/agent-substrate/review-context-pack.ts +365 -0
  349. package/src/agent-substrate/self-review-cli.ts +291 -20
  350. package/src/ask-user-question/index.ts +6 -0
  351. package/src/ask-user-question/inline-resolver.ts +49 -0
  352. package/src/auto-research/question-ladder-contract.ts +22 -3
  353. package/src/auto-research/research-turn.ts +359 -17
  354. package/src/autopilot/agent-loop.ts +1 -1
  355. package/src/autopilot/discovery/wiki-claim-proposer.ts +22 -1
  356. package/src/autopilot/pty-control-loop.ts +8 -1
  357. package/src/boot/daemon-tools/index.ts +4 -3
  358. package/src/boot/daemon-tools/self-implement.ts +312 -7
  359. package/src/boot/daemon-tools/types.ts +2 -0
  360. package/src/budget/llm-cost.ts +44 -5
  361. package/src/budget/shadow-arms.ts +52 -0
  362. package/src/budget/unified-usage.ts +17 -1
  363. package/src/capture/tui-self-report.ts +14 -0
  364. package/src/chat/index.ts +33 -7
  365. package/src/chat/pickers/state.ts +7 -1
  366. package/src/cli/agent-cli.ts +43 -8
  367. package/src/cli/intake-cli.ts +22 -1
  368. package/src/cli/lesson-cli.ts +19 -3
  369. package/src/cli/log-event-names.ts +6 -0
  370. package/src/cli/mcp-login.ts +8 -18
  371. package/src/cli/msg-cli.ts +57 -1
  372. package/src/cli/pod-cli.ts +17 -2
  373. package/src/cli/pr-land-heavy-remote.ts +15 -9
  374. package/src/cli/pty-takeover-cli.ts +67 -7
  375. package/src/cli/release-cli.ts +79 -7
  376. package/src/cli/repo-cli.ts +2 -1
  377. package/src/cli/sim-cli.ts +7 -1
  378. package/src/cli/tasks-cli.ts +41 -13
  379. package/src/codex/models.ts +30 -15
  380. package/src/control-plane/ledger.ts +21 -4
  381. package/src/core-turn/run-core-turn.ts +47 -3
  382. package/src/core-turn/run-goal-loop.ts +240 -10
  383. package/src/core-turn/types.ts +9 -0
  384. package/src/dashboard/acp-boot.ts +72 -3
  385. package/src/dashboard/chat/acp-chat.ts +2 -2
  386. package/src/dashboard/classic-sigquit.ts +16 -0
  387. package/src/dashboard/index.ts +327 -27
  388. package/src/dashboard/input/chat-main-interaction-opts.ts +3 -0
  389. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +7 -2
  390. package/src/dashboard/input/chat-main-slash-command.ts +7 -0
  391. package/src/dashboard/input/dashboard-plain-turn-stream.ts +4 -0
  392. package/src/dashboard/input/dashboard-priority-key-route.ts +21 -5
  393. package/src/dashboard/input/external-input-host.ts +2 -0
  394. package/src/dashboard/sidetab-runtime.ts +228 -0
  395. package/src/dashboard/slash-runtime/ask-clarification.ts +7 -1
  396. package/src/dashboard/slash-runtime/dashboard-handlers.ts +20 -8
  397. package/src/dashboard/slash-runtime/help-from-registry.ts +2 -2
  398. package/src/dashboard/term-slash-runtime.ts +16 -18
  399. package/src/dashboard/turn-preamble.ts +14 -1
  400. package/src/disk-steward/judge.ts +73 -0
  401. package/src/disk-steward/ledger.ts +122 -0
  402. package/src/disk-steward/steward.ts +373 -0
  403. package/src/domains/guardian-tick.ts +191 -2
  404. package/src/domains/knowledge.ts +1 -1
  405. package/src/esc-abort-gate.ts +33 -1
  406. package/src/execution-loop/launch-gate.ts +206 -1
  407. package/src/graph-runner/graph-cli.ts +27 -4
  408. package/src/graph-runner/in-process-registry.ts +37 -0
  409. package/src/graph-runner/runner.ts +63 -11
  410. package/src/graph-wizard/generate.ts +16 -7
  411. package/src/graph-wizard/mission-compose.ts +201 -0
  412. package/src/grok/agent-search.ts +8 -2
  413. package/src/grok/credential.ts +5 -0
  414. package/src/grounding/ref-index.ts +30 -0
  415. package/src/grounding/ref-vector.ts +61 -0
  416. package/src/grounding/sources-cli.ts +11 -3
  417. package/src/grounding/sources.ts +15 -1
  418. package/src/harness/harness-cli-command.ts +305 -23
  419. package/src/harness/harness-incidents.ts +312 -7
  420. package/src/harness/harness-pod-dispatch.ts +7 -1
  421. package/src/harness/harness-space.ts +15 -1
  422. package/src/harness/progress-worker-entry.ts +59 -0
  423. package/src/harness/progress-worker-runtime.ts +11 -0
  424. package/src/index.ts +35 -5
  425. package/src/input/external-input-queue.ts +10 -0
  426. package/src/instance/dev-project.ts +25 -0
  427. package/src/intake-plane/collect-telegram-saved.ts +36 -1
  428. package/src/intake-plane/intake-to-tasks.ts +246 -3
  429. package/src/intelligence-map/model-alias.ts +6 -5
  430. package/src/intelligence-map/model-catalog.ts +59 -2
  431. package/src/lessons/friction-ledger.ts +154 -0
  432. package/src/live-supply/collector.ts +149 -0
  433. package/src/live-supply/probes.ts +52 -0
  434. package/src/live-supply/snapshot.ts +40 -0
  435. package/src/llm/model-defaults.ts +4 -4
  436. package/src/llm.ts +473 -87
  437. package/src/loops/orchestrator/tick.ts +56 -0
  438. package/src/mcp/mcp-oauth.ts +3 -1
  439. package/src/model-tier/llm-tier-map.ts +9 -9
  440. package/src/model-tier/turn-tier.ts +1 -1
  441. package/src/msg/msg-mirror-github.ts +80 -0
  442. package/src/msg/msg-mirror-policy.ts +61 -0
  443. package/src/msg/msg-mirror-runner.ts +219 -0
  444. package/src/msg/msg-mirror-state.ts +33 -0
  445. package/src/msg/msg-store.ts +11 -0
  446. package/src/nexus/api/harness-api.ts +2 -1
  447. package/src/output-truncation.ts +41 -12
  448. package/src/project/project-store.ts +48 -1
  449. package/src/pty/snapshot.py +132 -0
  450. package/src/pty-shell/pty-control-ipc.ts +29 -2
  451. package/src/pty-shell/pty-manifest.ts +59 -3
  452. package/src/pty-shell/registry.ts +2 -0
  453. package/src/pty-shell/tui-run-state.ts +80 -3
  454. package/src/release-loop/gate-host-reservation.ts +157 -4
  455. package/src/release-loop/gate-shard-ledger.ts +68 -0
  456. package/src/release-loop/merged-pr-checklist.ts +25 -2
  457. package/src/release-loop/rehearsal-shard-plan.ts +6 -1
  458. package/src/release-loop/rehearsal.ts +27 -0
  459. package/src/release-loop/release-schedule.ts +90 -1
  460. package/src/research/bloom-exemplars.ts +135 -0
  461. package/src/research/edge-decision.ts +124 -15
  462. package/src/research/gap-followup.ts +249 -0
  463. package/src/research/lenses/counter-evidence.ts +100 -0
  464. package/src/research/lenses/working-backwards.ts +89 -0
  465. package/src/research/plugin-discovery.ts +242 -0
  466. package/src/research-bridge/invoke.ts +4 -2
  467. package/src/research-bridge/types.ts +2 -0
  468. package/src/self-dev/ask-launch-flow.ts +73 -131
  469. package/src/self-dev/dev-cli.ts +19 -17
  470. package/src/self-dev/dev-pipeline.ts +8 -4
  471. package/src/self-dev/graph-front-nodes.ts +23 -0
  472. package/src/self-dev/graph-journey-route.ts +10 -2
  473. package/src/self-dev/launch-preflight-text.ts +2 -2
  474. package/src/self-dev/launch-preflight.ts +47 -11
  475. package/src/self-dev/orchestrate-cli.ts +1 -0
  476. package/src/self-dev/orchestrate.ts +15 -0
  477. package/src/self-dev/run-supervisor.ts +204 -3
  478. package/src/self-dev/run-types.ts +3 -0
  479. package/src/self-implement/device-first.ts +8 -2
  480. package/src/self-implement/doctor-visit.ts +76 -58
  481. package/src/self-implement/gate-artifacts.ts +16 -0
  482. package/src/self-implement/gate-baseline.ts +75 -4
  483. package/src/self-implement/gate-cli.ts +368 -30
  484. package/src/self-implement/gate-receipt-registry.ts +180 -0
  485. package/src/self-implement/gate-remote.ts +165 -24
  486. package/src/self-implement/goal-author-clarification.ts +205 -32
  487. package/src/self-implement/goal-author-decompose.ts +24 -15
  488. package/src/self-implement/goal-author.ts +86 -10
  489. package/src/self-implement/graph-stage-units.ts +172 -0
  490. package/src/self-implement/graph-templates.ts +162 -1
  491. package/src/self-implement/graph-variant.ts +37 -4
  492. package/src/self-implement/host-regate.ts +124 -49
  493. package/src/self-implement/impl-findings-feedback.ts +188 -0
  494. package/src/self-implement/knowhow-pack.ts +227 -10
  495. package/src/self-implement/off-diff-evidence.ts +124 -0
  496. package/src/self-implement/orchestrator.ts +320 -28
  497. package/src/self-implement/run-ledger.ts +110 -14
  498. package/src/self-implement/run-stop.ts +33 -2
  499. package/src/self-implement/running-runs.ts +35 -12
  500. package/src/self-implement/seams.ts +6 -3
  501. package/src/self-implement/self-implement-cli.ts +40 -6
  502. package/src/self-implement/stop-autoheal-operational.ts +43 -0
  503. package/src/self-implement/stop-autoheal.ts +15 -6
  504. package/src/session/chat.ts +66 -15
  505. package/src/session/request-size-guard.ts +123 -0
  506. package/src/session-runtime/provider-circuit.ts +140 -0
  507. package/src/shell-runner/file-engine.ts +1 -0
  508. package/src/shell-runner/types.ts +4 -0
  509. package/src/sidetab-core/model.ts +312 -0
  510. package/src/skills/tools/schedule-wakeup.ts +23 -1
  511. package/src/skills/tools/shell.ts +51 -5
  512. package/src/steward/directive-audit.ts +354 -0
  513. package/src/surface/child-process.ts +31 -0
  514. package/src/surface/tui-stderr-owner.ts +48 -0
  515. package/src/task-agent/cover.ts +30 -12
  516. package/src/task-agent/judge.ts +56 -0
  517. package/src/task-agent/parent-host.ts +14 -1
  518. package/src/task-agent/postland-card.ts +35 -2
  519. package/src/task-agent/rejudge-on-head.ts +46 -2
  520. package/src/task-agent/repair-cycle.ts +1 -1
  521. package/src/task-agent/task-hand.ts +133 -25
  522. package/src/task-agent/tier-split.ts +105 -21
  523. package/src/task-orchestrator/surfaces/pod-command-job.ts +330 -2
  524. package/src/task-orchestrator/surfaces/pod-config-snapshot.ts +123 -0
  525. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +70 -2
  526. package/src/task-orchestrator/surfaces/pod-ledger-events.ts +29 -4
  527. package/src/task-orchestrator/surfaces/pod-mirror-seed.ts +11 -8
  528. package/src/task-orchestrator/surfaces/pod-si-warm-protocol.ts +1 -1
  529. package/src/task-orchestrator/surfaces/pod-source-receive.ts +74 -6
  530. package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +2 -1
  531. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +56 -15
  532. package/src/task-orchestrator/surfaces/self-implement-pod.ts +298 -45
  533. package/src/task-orchestrator/surfaces/self-implement.ts +18 -0
  534. package/src/tool-runtime/skill-exec-runtime.ts +95 -5
  535. package/src/tui/entry.ts +25 -5
  536. package/src/tui/inline-choice.ts +188 -0
  537. package/src/tui/sidetab/list.ts +36 -165
  538. package/src/tui/sidetab/nl-tool.ts +2 -136
  539. package/src/tui/sidetab/peek.ts +52 -0
  540. package/src/tui/slash/term.ts +12 -1
  541. package/src/tui-client/dashboard-session.ts +10 -1
  542. package/src/user-config.ts +45 -9
  543. package/src/ux-sim/classic-tui-seat-scenarios.ts +1 -7
  544. package/src/ux-sim/fake-land-queue.ts +88 -0
  545. package/src/ux-sim/fault-needs-human-reason-repair.ts +107 -0
  546. package/src/ux-sim/fault-pod-child-result.ts +42 -0
  547. package/src/ux-sim/gate-shard-host-reroute.ts +82 -0
  548. package/src/ux-sim/goal-interim-trace.ts +24 -0
  549. package/src/ux-sim/next-tui-seat-scenarios.ts +8 -13
  550. package/src/ux-sim/release-sim-rig.ts +262 -0
  551. package/src/ux-sim/seminar-rehearsal.ts +109 -0
  552. package/src/ux-sim/subagent-tools-device.ts +2 -2
  553. package/src/ux-sim/task-orchestrator-surfaces-self-implement-pod-device.ts +4 -1
  554. package/src/version/packed-revision.json +1 -1
  555. package/src/video-pipeline/recipes/explainer.ts +54 -2
  556. package/src/web-search/grok.ts +32 -6
  557. package/src/web-search/index.ts +8 -7
  558. package/src/workflow-runtime/node-catalog.ts +53 -4
  559. package/apps/pwa/out/_next/static/chunks/1118.a8a74ea29aac3e46.js +0 -27
  560. package/apps/pwa/out/_next/static/chunks/1204-2aa830479adf80f4.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/1341-43b7d2dc77d45c9d.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/1440-c3ea8882badfd9bf.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/1698-9c11cbf97f13717e.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/1770-3b1fc92253ef9f2f.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/1923-5c1695332240246a.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/1938.0e6c60ee91c4d7f5.js +0 -148
  567. package/apps/pwa/out/_next/static/chunks/2104.70e086d5174a4736.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/213-20675f20673ba22a.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/214.186a430da28338f1.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/2277.3bd4c0ac7f1b5996.js +0 -93
  571. package/apps/pwa/out/_next/static/chunks/240.e09f433f71c78ff4.js +0 -104
  572. package/apps/pwa/out/_next/static/chunks/2417.02151393605a7cd0.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/2463.cd2641a76daaa9ff.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/2576.b9e9c629c4b84c52.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/265-c021de53f0865bd9.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/2998-b1cc13956cd6a6e7.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/2e4782d3.ba2f0ab2b63a53e7.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/3130.626b54382b4dc379.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/3221-1bb3f99a55088613.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/3280.24edb1ffd03a7f3f.js +0 -24
  581. package/apps/pwa/out/_next/static/chunks/3288-eb3c22a1cbf2ae68.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/33.ef99e8374f96c6c4.js +0 -36
  583. package/apps/pwa/out/_next/static/chunks/3341-f09b9efbd7a82f70.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/3410-d7c0f14b329d6fda.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/3421.c87d6313a7d670f7.js +0 -287
  586. package/apps/pwa/out/_next/static/chunks/3422-83a3953f9bd67e26.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/3587-002e24e368c515a9.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/3644.f12a7d821ab940af.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/3660.d7516588e2d875c3.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/3695.b82e2610f4cd4ea9.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/3858.ca1f5bc0f577eac9.js +0 -94
  592. package/apps/pwa/out/_next/static/chunks/3959-e6ac4a139d9d4bcc.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/3f0de3de-2340c532c00395db.js +0 -1
  594. package/apps/pwa/out/_next/static/chunks/4032.53f62176514ec0ea.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/4199-f7c6eb40cd0322f8.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/4204-ef8484cbecd5d62f.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/4259-163129ca79913c4b.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/428-eaa77df3d0c85969.js +0 -1
  599. package/apps/pwa/out/_next/static/chunks/4432-97e400ffa1e161f5.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/449-aa427cf2cbb75f50.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/4491-39a0fd32bbaf3840.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/4588.b7cf06d382953979.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/4690.70ec8060825366ab.js +0 -32
  604. package/apps/pwa/out/_next/static/chunks/4804.6012b5cd33c22792.js +0 -90
  605. package/apps/pwa/out/_next/static/chunks/4881.e4d22d730e3b9df5.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/4937.ce2e340dc29cad3e.js +0 -79
  607. package/apps/pwa/out/_next/static/chunks/5-bb1c08d84bd71df3.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/5005.de98df23b37cdf3c.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/5035.dc64a89cad5573cc.js +0 -74
  610. package/apps/pwa/out/_next/static/chunks/5041-5aada92ca215ffdd.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/5207.1a224035455790fd.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/5255-1b60d50eabe9f057.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/5269.d0436ee6d773382a.js +0 -286
  614. package/apps/pwa/out/_next/static/chunks/5309.3367c4d50f52b489.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/552-689827d49780fa00.js +0 -1
  616. package/apps/pwa/out/_next/static/chunks/5539.34cc9d60622cb69b.js +0 -8
  617. package/apps/pwa/out/_next/static/chunks/5568.218dea0c00ca2ec7.js +0 -1
  618. package/apps/pwa/out/_next/static/chunks/5645.b7e3b055455a04ea.js +0 -1
  619. package/apps/pwa/out/_next/static/chunks/5662.18cc0f30353cbd96.js +0 -1
  620. package/apps/pwa/out/_next/static/chunks/5671.da24fc9dc5e98325.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/5b141171.324c40fd49cb6821.js +0 -1
  622. package/apps/pwa/out/_next/static/chunks/6175-d4621f6a10b0cfab.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/6290.3eb53a20746b72ce.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/6415-9307f3eafa90837f.js +0 -1
  625. package/apps/pwa/out/_next/static/chunks/6579-3de35d00aacd71b5.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/680-2f324c4e039a5b67.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/6845-331b3c7481da3ba3.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/7000.28157393caaf5343.js +0 -62
  629. package/apps/pwa/out/_next/static/chunks/7311-60a7ea80d23c7521.js +0 -1
  630. package/apps/pwa/out/_next/static/chunks/7401.525d0a12f7d02c06.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/7438.c2cf72b66dca6aa4.js +0 -1
  632. package/apps/pwa/out/_next/static/chunks/7488.966b116789f3d41a.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/7524.ce973525d30b3d52.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/7577.ea6e9ac72e035520.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/7628.6b16863ac3972333.js +0 -34
  636. package/apps/pwa/out/_next/static/chunks/7657.8d1fb3d2f7ebe273.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/7903-1435d5d2f0dd7807.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/7954.4f7a8832663ee979.js +0 -115
  639. package/apps/pwa/out/_next/static/chunks/7978-30a91192c5dbe3c2.js +0 -1
  640. package/apps/pwa/out/_next/static/chunks/8025-8795821525cf1a07.js +0 -1
  641. package/apps/pwa/out/_next/static/chunks/8191.3674a00f0b565c23.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/8217-604955d8885c5ef2.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/8298-eed6f5f2585a63f4.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/8509.3a54d4e61893d7cb.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/8758.ac421f726a189125.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/891.d6c723a51a067b36.js +0 -149
  647. package/apps/pwa/out/_next/static/chunks/9223.cbcc88e0e9950426.js +0 -41
  648. package/apps/pwa/out/_next/static/chunks/9299.24e2a7d9b6d1f91b.js +0 -226
  649. package/apps/pwa/out/_next/static/chunks/9317.6a9869039c231bb3.js +0 -24
  650. package/apps/pwa/out/_next/static/chunks/9383.e4755f275b3c11ff.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/9581.da63ddf41acfad46.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/9715.8c5529a0a5c41a36.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/9817.3009d65f0889ef1f.js +0 -2
  654. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-76873dd80da4f715.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/app/approvals/page-ed32b6ec5dea8f23.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-fdd40e15babf3d25.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/app/board/page-384efc78e85e400b.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/app/bots/page-d91809c5aeed319a.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/app/ceo/page-128f3522f89a81ab.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/app/chat/page-595961abf3b43ad1.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/app/consult/page-f41dccb76d8c0263.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/app/control/page-a3205c2e28b7d0d9.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/app/decisions/page-c39a80277814a3f4.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/app/design-check/page-1ab3f98194513195.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/app/editor/page-a5246f7e254d77d0.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/app/exec/page-0f92287bdb81217d.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/app/field/page-5a37131ff63793ef.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/app/healthz/route-76873dd80da4f715.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/app/inside/page-ac4bd14bb2348bfd.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/app/intake/page-c348a3959c5b5fc6.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/app/layout-2addd81c0d3e2906.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/app/live/page-da6b765f58a3eefb.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/app/live-run/page-0661e40f8780c32c.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/app/loops/page-b89af1b1c8e4875b.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/app/market/page-da8aeb82ba751cdb.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-cc71317b239439db.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/app/morning/page-bde7fc1d64ffe369.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/app/not-found-e2797214f713fa58.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/app/observatory/page-2a35bcda70defaf6.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-3af03deedde45032.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-d1d1210f99f620ae.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-46d1b5500885861d.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/app/outputs/page-291ab19fe2acb173.js +0 -1
  684. package/apps/pwa/out/_next/static/chunks/app/page-7f569e533ddeecba.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/app/reflection/page-990129c9fee46d8f.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-7737f785ebb1cdaf.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/app/sessions/page-9f3c73a4f7ffd2f8.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-03be7fb347f01058.js +0 -1
  689. package/apps/pwa/out/_next/static/chunks/app/settings/page-96b0280625e4df2b.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-56855545049eb56d.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/app/setup/layout-76873dd80da4f715.js +0 -1
  692. package/apps/pwa/out/_next/static/chunks/app/setup/page-79a909c9981f0338.js +0 -1
  693. package/apps/pwa/out/_next/static/chunks/app/share/page-a9d9a3e66459c1e0.js +0 -1
  694. package/apps/pwa/out/_next/static/chunks/app/showroom/page-c383cbf2013f7069.js +0 -1
  695. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f57c6a2e54dbdca4.js +0 -1
  696. package/apps/pwa/out/_next/static/chunks/app/term/page-6b140f77593576eb.js +0 -1
  697. package/apps/pwa/out/_next/static/chunks/app/today/page-6b767ae2d9d0adfc.js +0 -1
  698. package/apps/pwa/out/_next/static/chunks/app/trace/page-116d6198d5da0e3d.js +0 -1
  699. package/apps/pwa/out/_next/static/chunks/app/vault/page-b9c9b778c7501370.js +0 -1
  700. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-a6c78392ab8f6335.js +0 -1
  701. package/apps/pwa/out/_next/static/chunks/app/workflows/page-455183deb4ed0cfe.js +0 -1
  702. package/apps/pwa/out/_next/static/chunks/app/workspace/page-6a63e99526bd63d0.js +0 -1
  703. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31c22caafe788c08.js +0 -1
  704. package/apps/pwa/out/_next/static/chunks/bb6987f9.3647e1a23b0e3217.js +0 -122
  705. package/apps/pwa/out/_next/static/chunks/framework-4b12a946f352cee2.js +0 -1
  706. package/apps/pwa/out/_next/static/chunks/main-941155250ed2644b.js +0 -1
  707. package/apps/pwa/out/_next/static/chunks/main-app-f8b6765abed6d149.js +0 -1
  708. package/apps/pwa/out/_next/static/chunks/pages/_app-78432194c85388f9.js +0 -1
  709. package/apps/pwa/out/_next/static/chunks/pages/_error-cdf7390829512b1b.js +0 -1
  710. package/apps/pwa/out/_next/static/chunks/webpack-d5f0964a9c2e92d2.js +0 -1
  711. package/scripts/ratchets/next-tui-entry-budget.ts +0 -124
  712. /package/apps/pwa/out/_next/static/{RrjXTwz-urRAbChP1bX02 → GiOLWmt4MqDZePubBjA4Q}/_ssgManifest.js +0 -0
@@ -0,0 +1,128 @@
1
+ /** Pure, fail-closed planning for a release gate measured across hosts. No host command runs here. */
2
+ export interface GateShardIdentity {
3
+ id: string;
4
+ files: string[];
5
+ /** Execution requirement, never a concrete host identity. */
6
+ requiredOs?: 'darwin' | 'linux';
7
+ }
8
+
9
+ export interface GateShardReceipt extends GateShardIdentity {
10
+ head40: string;
11
+ base40: string;
12
+ host: string;
13
+ envKey: string;
14
+ status: 'complete' | 'unmeasured';
15
+ /** A complete measurement can contain existing or introduced failures. */
16
+ failureIds: string[];
17
+ errorIds: string[];
18
+ /** Relative path to the measurement artifact, checked by the caller before reuse. */
19
+ evidencePath: string;
20
+ evidenceSha256: string;
21
+ measuredAt: string;
22
+ attempt: number;
23
+ }
24
+
25
+ export interface GateShardHost {
26
+ id: string;
27
+ platform: 'darwin' | 'linux' | 'other';
28
+ available: boolean;
29
+ reserved: boolean;
30
+ }
31
+
32
+ export interface GateShardDispatchInput {
33
+ head40: string;
34
+ base40: string;
35
+ shards: readonly GateShardIdentity[];
36
+ receipts: readonly GateShardReceipt[];
37
+ hosts: readonly GateShardHost[];
38
+ rerouteShard?: string | 'auto';
39
+ shardHost?: string;
40
+ /** IDs known to pass on macOS, supplied by the caller's env-known policy. */
41
+ macPassShardIds?: readonly string[];
42
+ }
43
+
44
+ export type GateShardAction =
45
+ | { shardId: string; files: string[]; kind: 'reuse'; receipt: GateShardReceipt }
46
+ | { shardId: string; files: string[]; kind: 'run'; host: string; replacing?: GateShardReceipt }
47
+ | { shardId: string; files: string[]; kind: 'unmeasured'; reason: string };
48
+
49
+ const SHA40 = /^[a-f0-9]{40}$/i;
50
+ const safeId = (id: string) => /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(id);
51
+ const sameFiles = (a: readonly string[], b: readonly string[]) => a.length === b.length && a.every((file, i) => file === b[i]);
52
+
53
+ export function validGateShardIdentity(head40: string, base40: string, shards: readonly GateShardIdentity[]): boolean {
54
+ if (!SHA40.test(head40) || !SHA40.test(base40) || shards.length === 0) return false;
55
+ const ids = new Set<string>();
56
+ const files = new Set<string>();
57
+ for (const shard of shards) {
58
+ if (!safeId(shard.id) || ids.has(shard.id) || shard.files.length === 0
59
+ || (shard.requiredOs !== undefined && shard.requiredOs !== 'darwin' && shard.requiredOs !== 'linux')) return false;
60
+ ids.add(shard.id);
61
+ for (const file of shard.files) {
62
+ if (!/^(?:[\w.-]+\/)*[\w.-]+\.test\.(?:tsx?|jsx?|mts|cts)$/.test(file) || file.split('/').includes('..') || files.has(file)) return false;
63
+ files.add(file);
64
+ }
65
+ }
66
+ return true;
67
+ }
68
+
69
+ export function eligibleGateShardReceipt(
70
+ receipt: GateShardReceipt,
71
+ shard: GateShardIdentity,
72
+ head40: string,
73
+ base40: string,
74
+ ): boolean {
75
+ return receipt.status === 'complete' && receipt.id === shard.id
76
+ && receipt.head40 === head40 && receipt.base40 === base40 && sameFiles(receipt.files, shard.files)
77
+ && receipt.envKey.trim().length > 0 && receipt.host.trim().length > 0
78
+ && receipt.evidencePath.trim().length > 0 && /^[a-f0-9]{64}$/i.test(receipt.evidenceSha256)
79
+ && Number.isSafeInteger(receipt.attempt) && receipt.attempt > 0
80
+ && Array.isArray(receipt.failureIds) && receipt.failureIds.every((id) => typeof id === 'string')
81
+ && Array.isArray(receipt.errorIds) && receipt.errorIds.every((id) => typeof id === 'string');
82
+ }
83
+
84
+ /** Reuse only exact complete measurements. A requested remeasure replaces one shard, not the whole sweep. */
85
+ export function planGateShardDispatch(input: GateShardDispatchInput): GateShardAction[] {
86
+ if (!validGateShardIdentity(input.head40, input.base40, input.shards)) throw new Error('invalid gate shard identity');
87
+ if (input.rerouteShard && input.rerouteShard !== 'auto' && !input.shards.some((s) => s.id === input.rerouteShard))
88
+ throw new Error(`unknown reroute shard: ${input.rerouteShard}`);
89
+ if (input.shardHost && !input.hosts.some((host) => host.id === input.shardHost)) throw new Error(`unknown shard host: ${input.shardHost}`);
90
+ const hosts = input.hosts.filter((host) => host.available && !host.reserved);
91
+ const macPass = new Set(input.macPassShardIds ?? []);
92
+ let nextHost = 0;
93
+ return input.shards.map((shard) => {
94
+ const receipt = [...input.receipts].reverse().find((r) => eligibleGateShardReceipt(r, shard, input.head40, input.base40));
95
+ const prior = [...input.receipts].reverse().find((r) => r.id === shard.id && r.head40 === input.head40 && r.base40 === input.base40 && sameFiles(r.files, shard.files));
96
+ const forced = input.rerouteShard === shard.id;
97
+ const auto = input.rerouteShard === 'auto' && (prior?.status === 'unmeasured'
98
+ || (macPass.has(shard.id) && !!prior && (prior.failureIds.length > 0 || prior.errorIds.length > 0)));
99
+ const receiptHost = hosts.find((host) => host.id === receipt?.host);
100
+ if (receipt && !forced && !auto && (!shard.requiredOs || receiptHost?.platform === shard.requiredOs))
101
+ return { shardId: shard.id, files: [...shard.files], kind: 'reuse', receipt };
102
+ let candidates = hosts.filter((host) => !shard.requiredOs || host.platform === shard.requiredOs);
103
+ if (input.shardHost && (forced || auto)) candidates = candidates.filter((host) => host.id === input.shardHost);
104
+ else if (auto && macPass.has(shard.id)) candidates = candidates.filter((host) => host.platform === 'darwin');
105
+ if ((forced || auto) && prior && candidates.some((host) => host.id !== prior.host)) candidates = candidates.filter((host) => host.id !== prior.host);
106
+ if (!candidates.length) return { shardId: shard.id, files: [...shard.files], kind: 'unmeasured', reason: 'no eligible host' };
107
+ const host = candidates[nextHost++ % candidates.length]!;
108
+ return { shardId: shard.id, files: [...shard.files], kind: 'run', host: host.id, ...(prior ? { replacing: prior } : {}) };
109
+ });
110
+ }
111
+
112
+ export type GateShardReduction =
113
+ | { status: 'complete'; failureIds: string[]; errorIds: string[]; receipts: GateShardReceipt[] }
114
+ | { status: 'unmeasured'; reason: string };
115
+
116
+ /** Require exactly one exact, complete receipt per expected shard; classification remains with judgeGate. */
117
+ export function reduceGateShardReceipts(input: { head40: string; base40: string; shards: readonly GateShardIdentity[]; receipts: readonly GateShardReceipt[] }): GateShardReduction {
118
+ if (!validGateShardIdentity(input.head40, input.base40, input.shards)) return { status: 'unmeasured', reason: 'invalid gate shard identity' };
119
+ const selected: GateShardReceipt[] = [];
120
+ for (const shard of input.shards) {
121
+ const matched = input.receipts.filter((r) => eligibleGateShardReceipt(r, shard, input.head40, input.base40));
122
+ if (matched.length !== 1) return { status: 'unmeasured', reason: `${shard.id}: expected one complete receipt, found ${matched.length}` };
123
+ selected.push(matched[0]!);
124
+ }
125
+ return { status: 'complete', receipts: selected,
126
+ failureIds: [...new Set(selected.flatMap((r) => r.failureIds))].sort(),
127
+ errorIds: [...new Set(selected.flatMap((r) => r.errorIds))].sort() };
128
+ }
@@ -0,0 +1,256 @@
1
+ #!/usr/bin/env bun
2
+ import { readFileSync, writeFileSync } from 'node:fs';
3
+
4
+ function decodeXml(value: string): string {
5
+ return value.replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&lt;/g, '<')
6
+ .replace(/&gt;/g, '>').replace(/&amp;/g, '&');
7
+ }
8
+
9
+ /** Remove legal comments and reduce CDATA before tag parsing, without accepting markup inside tags. */
10
+ function stripCommentsAndCdata(xml: string): string | null {
11
+ let source = '';
12
+ let inMarkup = false;
13
+ let quote: '"' | "'" | undefined;
14
+ const offset = xml.startsWith('\uFEFF') ? 1 : 0;
15
+ for (let index = offset; index < xml.length;) {
16
+ if (xml.startsWith('<!--', index)) {
17
+ if (inMarkup) return null;
18
+ const end = xml.indexOf('-->', index + 4);
19
+ if (end < 0 || xml.slice(index + 4, end).includes('--')) return null;
20
+ index = end + 3;
21
+ continue;
22
+ }
23
+ if (xml.startsWith('<?', index)) {
24
+ const end = xml.indexOf('?>', index + 2);
25
+ if (inMarkup || index !== offset || end < 0) return null;
26
+ const declaration = xml.slice(index, end + 2);
27
+ if (!/^<\?xml\s+version=["']1\.0["'](?:\s+encoding=["'][A-Za-z][A-Za-z0-9._-]*["'])?(?:\s+standalone=["'](?:yes|no)["'])?\s*\?>$/.test(declaration)) return null;
28
+ index = end + 2;
29
+ continue;
30
+ }
31
+ if (xml.startsWith('<![CDATA[', index)) {
32
+ if (inMarkup) return null;
33
+ const end = xml.indexOf(']]>', index + 9);
34
+ if (end < 0) return null;
35
+ source += xml.slice(index + 9, end).trim() ? '__CDATA_TEXT__' : ' ';
36
+ index = end + 3;
37
+ continue;
38
+ }
39
+ const character = xml[index]!;
40
+ if (inMarkup) {
41
+ if (quote) {
42
+ if (character === quote) quote = undefined;
43
+ else if (character === '<') return null;
44
+ } else if (character === '"' || character === "'") quote = character;
45
+ else if (character === '>') inMarkup = false;
46
+ else if (character === '<') return null;
47
+ } else if (character === '<') inMarkup = true;
48
+ source += character;
49
+ index++;
50
+ }
51
+ return source;
52
+ }
53
+
54
+ type JunitCase = { file?: string; id?: string; failures: number; errors: number; skipped: number };
55
+ type JunitDocument = { cases: JunitCase[]; suiteFailed: boolean; unattributedSuiteFailure: boolean };
56
+ function parseJunit(xml: string): JunitDocument | null {
57
+ if (/<!DOCTYPE/i.test(xml)) return null;
58
+ for (const character of xml) {
59
+ const codepoint = character.codePointAt(0)!;
60
+ const xml10Character = codepoint === 0x9 || codepoint === 0xa || codepoint === 0xd
61
+ || (codepoint >= 0x20 && codepoint <= 0xd7ff) || (codepoint >= 0xe000 && codepoint <= 0xfffd)
62
+ || (codepoint >= 0x10000 && codepoint <= 0x10ffff);
63
+ if (!xml10Character) return null;
64
+ }
65
+ const source = stripCommentsAndCdata(xml);
66
+ if (source === null) return null;
67
+ if (/<\?|\?>/.test(source)) return null;
68
+ if (/<!--|-->|<!\[CDATA\[|\]\]>/.test(source)) return null;
69
+ const stack: Array<{
70
+ name: string;
71
+ file?: string;
72
+ suiteTitle?: string;
73
+ declaredTests?: number;
74
+ declaredFailures?: number;
75
+ declaredErrors?: number;
76
+ testCount: number;
77
+ caseStart: number;
78
+ }> = [];
79
+ const cases: JunitCase[] = [];
80
+ let roots = 0;
81
+ let sawSuite = false;
82
+ let suiteFailed = false;
83
+ let unattributedSuiteFailure = false;
84
+ let rootName: string | undefined;
85
+ let cursor = 0;
86
+ if (/&(?!(?:amp|lt|gt|quot|apos|#\d+|#x[\da-f]+);)/i.test(source)) return null;
87
+ for (const entity of source.matchAll(/&#(x[\da-f]+|\d+);/gi)) {
88
+ const codepoint = entity[1]!.startsWith('x') ? Number.parseInt(entity[1]!.slice(1), 16) : Number.parseInt(entity[1]!, 10);
89
+ const xml10Character = codepoint === 0x9 || codepoint === 0xa || codepoint === 0xd
90
+ || (codepoint >= 0x20 && codepoint <= 0xd7ff) || (codepoint >= 0xe000 && codepoint <= 0xfffd)
91
+ || (codepoint >= 0x10000 && codepoint <= 0x10ffff);
92
+ if (!Number.isInteger(codepoint) || !xml10Character) return null;
93
+ }
94
+ for (const match of source.matchAll(/<([^<>]+)>/g)) {
95
+ const between = source.slice(cursor, match.index);
96
+ if (between.includes('<') || between.includes(']]>') || (!stack.length && between.trim())) return null;
97
+ cursor = match.index! + match[0].length;
98
+ const body = match[1]!.trim();
99
+ // Comments and CDATA have already been stripped above, and the one XML declaration was
100
+ // removed before tokenization. Every other declaration is unsupported and invalid here.
101
+ if (!body || body.startsWith('!')) return null;
102
+ const close = body.startsWith('/');
103
+ const selfClose = body.endsWith('/');
104
+ const tagMatch = /^\/?([A-Za-z_:][\w:.-]*)/.exec(body);
105
+ if (!tagMatch) return null;
106
+ const name = tagMatch[1]!;
107
+ const attrsText = body.slice(tagMatch[0].length, body.length - (selfClose ? 1 : 0));
108
+ const attrs: Record<string, string> = {};
109
+ const attrPattern = /([A-Za-z_:][\w:.-]*)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
110
+ for (const attr of attrsText.matchAll(attrPattern)) {
111
+ if (attrs[attr[1]!] !== undefined) return null;
112
+ attrs[attr[1]!] = decodeXml(attr[2] ?? attr[3] ?? '');
113
+ }
114
+ if (!close && attrsText.replace(attrPattern, '').trim()) return null;
115
+ if (close && attrsText.trim()) return null;
116
+ if (close) {
117
+ if (selfClose) return null;
118
+ const closed = stack.pop();
119
+ if (!closed || closed.name !== name || (closed.declaredTests !== undefined && closed.declaredTests !== closed.testCount)) return null;
120
+ if (closed.declaredFailures !== undefined
121
+ && closed.declaredFailures !== cases.slice(closed.caseStart).reduce((count, testcase) => count + testcase.failures, 0)) return null;
122
+ if (closed.declaredErrors !== undefined
123
+ && closed.declaredErrors !== cases.slice(closed.caseStart).reduce((count, testcase) => count + testcase.errors, 0)) return null;
124
+ continue;
125
+ }
126
+ if (name === 'testcase' && stack.at(-1)?.name !== 'testsuite') return null;
127
+ if ((name === 'failure' || name === 'error') && stack.at(-1)?.name !== 'testcase') return null;
128
+ if (!stack.length) { roots++; rootName = name; }
129
+ let file = attrs.file;
130
+ let declaredTests: number | undefined;
131
+ let declaredFailures: number | undefined;
132
+ let declaredErrors: number | undefined;
133
+ if ((name === 'testsuite' || name === 'testsuites') && attrs.tests !== undefined) {
134
+ declaredTests = Number(attrs.tests);
135
+ if (!Number.isSafeInteger(declaredTests) || declaredTests < 0) return null;
136
+ }
137
+ if (name === 'testsuite' || name === 'testsuites') {
138
+ for (const [attribute, set] of [['failures', (value: number) => { declaredFailures = value; }], ['errors', (value: number) => { declaredErrors = value; }]] as const) {
139
+ const count = attrs[attribute];
140
+ if (count === undefined) continue;
141
+ if (!/^\d+$/.test(count) || !Number.isSafeInteger(Number(count))) return null;
142
+ set(Number(count));
143
+ }
144
+ }
145
+ if (name === 'testsuite') {
146
+ file ??= attrs.name;
147
+ sawSuite = true;
148
+ if ((declaredFailures ?? 0) > 0 || (declaredErrors ?? 0) > 0) suiteFailed = true;
149
+ const inherited = [...stack].reverse().find((entry) => entry.name === 'testsuite')?.file;
150
+ if (!file && inherited) file = inherited;
151
+ } else {
152
+ const inherited = [...stack].reverse().find((entry) => entry.name === 'testsuite')?.file;
153
+ if (!file && inherited) file = inherited;
154
+ if (name === 'testsuites' && ((declaredFailures ?? 0) > 0 || (declaredErrors ?? 0) > 0)) suiteFailed = true;
155
+ }
156
+ if (name === 'testcase') {
157
+ for (const ancestor of stack) if (ancestor.name === 'testsuite' || ancestor.name === 'testsuites') ancestor.testCount++;
158
+ const suiteEntries = stack.filter((entry) => entry.name === 'testsuite');
159
+ // Some JUnit writers repeat the file path as the outer suite name. Keep the
160
+ // remaining hierarchy so sibling suites with the same testcase name stay distinct.
161
+ const identitySuites = suiteEntries[0]?.suiteTitle === file ? suiteEntries.slice(1) : suiteEntries;
162
+ const suiteNames = identitySuites.map((entry) => entry.suiteTitle ?? '').filter(Boolean);
163
+ const testcaseName = attrs.name;
164
+ const idName = [...suiteNames, ...(testcaseName ? [testcaseName] : [])].filter(Boolean).join(' > ');
165
+ cases.push({ file, ...(file && idName ? { id: `${file} > ${idName}` } : {}), failures: 0, errors: 0, skipped: 0 });
166
+ }
167
+ if (name === 'failure' || name === 'error' || name === 'skipped') {
168
+ if (stack.some((entry) => entry.name === 'testcase')) {
169
+ const current = cases.at(-1);
170
+ if (current) current[name === 'failure' ? 'failures' : name === 'error' ? 'errors' : 'skipped']++;
171
+ } else if (stack.some((entry) => entry.name === 'testsuite')) { suiteFailed = true; unattributedSuiteFailure = true; }
172
+ }
173
+ if (!selfClose) stack.push({ name, file, ...(name === 'testsuite' && attrs.name ? { suiteTitle: attrs.name } : {}), ...(declaredTests !== undefined ? { declaredTests } : {}),
174
+ ...(declaredFailures !== undefined ? { declaredFailures } : {}), ...(declaredErrors !== undefined ? { declaredErrors } : {}),
175
+ testCount: 0, caseStart: cases.length });
176
+ else if ((declaredTests !== undefined && declaredTests !== 0) || (declaredFailures !== undefined && declaredFailures !== 0)
177
+ || (declaredErrors !== undefined && declaredErrors !== 0)) return null;
178
+ }
179
+ const trailingText = source.slice(cursor);
180
+ if (trailingText.includes('<') || trailingText.includes(']]>') || trailingText.trim() || stack.length || roots !== 1
181
+ || !['testsuite', 'testsuites'].includes(rootName ?? '') || !sawSuite || cases.length === 0) return null;
182
+ return { cases, suiteFailed, unattributedSuiteFailure };
183
+ }
184
+
185
+ /** A null count means the XML was malformed or incomplete. */
186
+ export function junitTestcaseCount(xml: string): number | null {
187
+ return parseJunit(xml)?.cases.length ?? null;
188
+ }
189
+
190
+ /** Unique file attribution for every testcase; null means malformed, incomplete, or unattributed evidence. */
191
+ export function junitTestcaseFiles(xml: string): string[] | null {
192
+ const parsed = parseJunit(xml);
193
+ if (!parsed || parsed.unattributedSuiteFailure || parsed.cases.some((testcase) => !testcase.file)) return null;
194
+ return [...new Set(parsed.cases.map((testcase) => testcase.file!))].sort();
195
+ }
196
+
197
+ /** Named failing/error cases, preserving duplicate identities so a retry must rerun each occurrence. */
198
+ export function junitFailedCaseIds(xml: string): string[] | null {
199
+ const parsed = parseJunit(xml);
200
+ if (!parsed || parsed.unattributedSuiteFailure) return null;
201
+ const failed = parsed.cases.filter((testcase) => testcase.failures > 0 || testcase.errors > 0);
202
+ if (failed.some((testcase) => !testcase.id)) return null;
203
+ const identityCounts = new Map<string, number>();
204
+ for (const testcase of parsed.cases) if (testcase.id) identityCounts.set(testcase.id, (identityCounts.get(testcase.id) ?? 0) + 1);
205
+ // A duplicate testcase identity cannot show which occurrence failed. Treat it as
206
+ // ambiguous evidence instead of allowing a passing sibling to clear the failure.
207
+ if (failed.some((testcase) => identityCounts.get(testcase.id!)! > 1)) return null;
208
+ return failed.map((testcase) => testcase.id!).sort();
209
+ }
210
+
211
+ /** Named cases that completed without failure, error, or skip. */
212
+ export function junitPassedCaseIds(xml: string): string[] | null {
213
+ const parsed = parseJunit(xml);
214
+ if (!parsed) return null;
215
+ return parsed.cases.flatMap((testcase) => testcase.id && !testcase.failures && !testcase.errors && !testcase.skipped ? [testcase.id] : []).sort();
216
+ }
217
+
218
+ /** Attribute files for testcases that have a failure/error node; malformed XML returns null (fail closed). */
219
+ export function failedJunitFiles(xml: string, allowed: readonly string[]): string[] | null {
220
+ const allow = new Set(allowed);
221
+ const parsed = parseJunit(xml);
222
+ if (!parsed || parsed.unattributedSuiteFailure || parsed.cases.some((testcase) => !testcase.file || !allow.has(testcase.file))) return null;
223
+ return [...new Set(parsed.cases.filter((testcase) => testcase.failures > 0 || testcase.errors > 0).map((testcase) => testcase.file!))].sort();
224
+ }
225
+
226
+ interface RetryVerdict { clean: boolean; tests: number; failures: number; errors: number; }
227
+ export function cleanRetryVerdict(rc: number | undefined, log: string | undefined, junit: string | undefined, file: string, expectedFailureIds?: readonly string[]): RetryVerdict {
228
+ const summary = log?.match(/Ran (\d+) tests? across ([1-9]\d*) files?\.?/);
229
+ const pass = Number(log?.match(/^\s*(\d+)\s+pass\s*$/m)?.[1] ?? NaN);
230
+ const failures = Number(log?.match(/^\s*(\d+)\s+fail\s*$/m)?.[1] ?? NaN);
231
+ const errors = Number(log?.match(/^\s*(\d+)\s+errors?\s*$/m)?.[1] ?? 0);
232
+ const tests = Number(summary?.[1] ?? 0);
233
+ const parsed = junit ? parseJunit(junit) : null;
234
+ const cases = parsed?.cases;
235
+ const exactFile = cases?.every((testcase) => testcase.file === file) ?? false;
236
+ const hasFailure = parsed?.suiteFailed || (cases?.some((testcase) => testcase.failures > 0 || testcase.errors > 0 || testcase.skipped > 0) ?? true);
237
+ const rerunIds = junitPassedCaseIds(junit ?? '') ?? [];
238
+ const rerunCounts = new Map<string, number>();
239
+ for (const id of rerunIds) rerunCounts.set(id, (rerunCounts.get(id) ?? 0) + 1);
240
+ const expectedCounts = new Map<string, number>();
241
+ for (const id of expectedFailureIds ?? []) expectedCounts.set(id, (expectedCounts.get(id) ?? 0) + 1);
242
+ const reranOriginalFailures = expectedFailureIds === undefined
243
+ || (expectedFailureIds.length > 0 && [...expectedCounts].every(([id, count]) => (rerunCounts.get(id) ?? 0) >= count));
244
+ const clean = rc === 0 && tests > 0 && Number(summary?.[2]) === 1 && pass === tests && failures === 0 && errors === 0
245
+ && cases?.length === tests && exactFile && !hasFailure && reranOriginalFailures && !/^# Unhandled error between tests/m.test(log ?? '');
246
+ return { clean, tests, failures: Number.isFinite(failures) ? failures : -1, errors: Number.isFinite(errors) ? errors : -1 };
247
+ }
248
+
249
+ if (import.meta.main) {
250
+ const [xmlPath, outputPath, manifestPath] = process.argv.slice(2);
251
+ if (!xmlPath || !outputPath || !manifestPath) process.exit(2);
252
+ let result: string[] | null = null;
253
+ try { result = failedJunitFiles(readFileSync(xmlPath, 'utf8'), readFileSync(manifestPath, 'utf8').split(/\r?\n/).filter(Boolean)); } catch { /* fail closed */ }
254
+ if (!result) process.exit(1);
255
+ writeFileSync(outputPath, result.length ? `${result.join('\n')}\n` : '');
256
+ }
@@ -0,0 +1,89 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { basename, dirname, join } from 'node:path';
4
+ import { eligibleGateShardReceipt, type GateShardIdentity, type GateShardReceipt } from './gate-shard-dispatch.js';
5
+
6
+ export interface GateShardSavedRun {
7
+ output: string;
8
+ rc: number;
9
+ pass: number;
10
+ fail: number;
11
+ errors: number;
12
+ ran: number;
13
+ files: number;
14
+ passedIds: string[];
15
+ }
16
+
17
+ export interface GateShardSavedResult {
18
+ receipt: GateShardReceipt;
19
+ runs: GateShardSavedRun[];
20
+ }
21
+
22
+ export function gateShardReceiptPath(root: string, shardId: string): string {
23
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(shardId)) throw new Error('invalid shard id');
24
+ return join(root, `${shardId}.receipt.json`);
25
+ }
26
+
27
+ export function nextGateShardAttempt(root: string, shardId: string): number {
28
+ try {
29
+ const previous = JSON.parse(readFileSync(gateShardReceiptPath(root, shardId), 'utf8')) as { attempt?: unknown };
30
+ return Number.isSafeInteger(previous.attempt) && (previous.attempt as number) > 0 ? (previous.attempt as number) + 1 : 1;
31
+ } catch { return 1; }
32
+ }
33
+
34
+ /** Planning may inspect an unmeasured predecessor; only readGateShardResult authorizes reuse. */
35
+ export function readGateShardManifest(root: string, shardId: string): GateShardReceipt | null {
36
+ try {
37
+ const value = JSON.parse(readFileSync(gateShardReceiptPath(root, shardId), 'utf8')) as GateShardReceipt;
38
+ return value && value.id === shardId && Array.isArray(value.files)
39
+ && Array.isArray(value.failureIds) && value.failureIds.every((id) => typeof id === 'string')
40
+ && Array.isArray(value.errorIds) && value.errorIds.every((id) => typeof id === 'string')
41
+ && (value.status === 'complete' || value.status === 'unmeasured') ? value : null;
42
+ } catch { return null; }
43
+ }
44
+
45
+ function atomicJson(path: string, value: unknown): void {
46
+ mkdirSync(dirname(path), { recursive: true });
47
+ const temp = join(dirname(path), `.${basename(path)}-${process.pid}-${randomUUID()}.tmp`);
48
+ try { writeFileSync(temp, JSON.stringify(value) + '\n', { mode: 0o600 }); renameSync(temp, path); }
49
+ finally { if (existsSync(temp)) rmSync(temp); }
50
+ }
51
+
52
+ /** A new attempt invalidates an old success before host execution starts. */
53
+ export function markGateShardUnmeasured(root: string, receipt: GateShardReceipt): void {
54
+ atomicJson(gateShardReceiptPath(root, receipt.id), { ...receipt, status: 'unmeasured' });
55
+ }
56
+
57
+ /** Save the actual measured bytes and bind their digest to the manifest. */
58
+ export function saveGateShardResult(root: string, result: GateShardSavedResult): GateShardReceipt {
59
+ const { receipt, runs } = result;
60
+ if (!eligibleGateShardReceipt(receipt, receipt, receipt.head40, receipt.base40) || !runs.length
61
+ || runs.some((run) => !Number.isSafeInteger(run.ran) || run.ran < 0 || !Number.isSafeInteger(run.files) || run.files < 1))
62
+ throw new Error('incomplete gate shard result');
63
+ const payloadName = `${receipt.id}.attempt-${receipt.attempt}-${randomUUID()}.result.json`;
64
+ const payloadBody = { runs, failureIds: receipt.failureIds, errorIds: receipt.errorIds };
65
+ const payload = JSON.stringify(payloadBody) + '\n';
66
+ const digest = createHash('sha256').update(payload).digest('hex');
67
+ atomicJson(join(root, payloadName), payloadBody);
68
+ const saved = { ...receipt, evidencePath: payloadName, evidenceSha256: digest };
69
+ atomicJson(gateShardReceiptPath(root, receipt.id), saved);
70
+ return saved;
71
+ }
72
+
73
+ /** No manifest, stale identity, missing bytes or digest mismatch can become a reused pass. */
74
+ export function readGateShardResult(root: string, expected: { shard: GateShardIdentity; head40: string; base40: string; envKey: string }): GateShardSavedResult | null {
75
+ try {
76
+ const receipt = JSON.parse(readFileSync(gateShardReceiptPath(root, expected.shard.id), 'utf8')) as GateShardReceipt;
77
+ if (!eligibleGateShardReceipt(receipt, expected.shard, expected.head40, expected.base40) || receipt.envKey !== expected.envKey
78
+ || !/^[A-Za-z0-9][A-Za-z0-9._-]*\.result\.json$/.test(receipt.evidencePath)) return null;
79
+ const payload = readFileSync(join(root, receipt.evidencePath), 'utf8');
80
+ if (createHash('sha256').update(payload).digest('hex') !== receipt.evidenceSha256) return null;
81
+ const parsed = JSON.parse(payload) as { runs?: GateShardSavedRun[]; failureIds?: string[]; errorIds?: string[] };
82
+ if (!Array.isArray(parsed.runs) || parsed.runs.length === 0 || parsed.runs.some((run) =>
83
+ typeof run.output !== 'string' || !Number.isSafeInteger(run.rc) || !Number.isSafeInteger(run.ran)
84
+ || !Number.isSafeInteger(run.files) || !Array.isArray(run.passedIds))
85
+ || JSON.stringify(parsed.failureIds) !== JSON.stringify(receipt.failureIds)
86
+ || JSON.stringify(parsed.errorIds) !== JSON.stringify(receipt.errorIds)) return null;
87
+ return { receipt, runs: parsed.runs };
88
+ } catch { return null; }
89
+ }
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env bun
2
- import { readFileSync } from 'node:fs';
2
+ import { readFileSync, readdirSync, statSync } from 'node:fs';
3
+ import { join } from 'node:path';
3
4
  import { debug } from '../../src/debug/log.js';
5
+ import { effectiveInstanceRoot } from '../../src/instance/resolve.js';
4
6
  import { registerStandaloneLogSink } from '../../src/domains/standalone-log-sink.js';
5
7
  import type { GateResult } from './gate-node.js';
6
8
  import { emitNodeResult, readGraphContext, type GraphContext } from './node-verdict.js';
@@ -39,8 +41,32 @@ export interface GateTriageShadow {
39
41
  loadFlaky: string[] | null;
40
42
  stalledEnv: Array<{ file: string; local: string }>;
41
43
  };
42
- /** Shadow always goes to the existing failure exit. */
43
- outcome: 'fail';
44
+ /** Classification succeeded; the release graph still routes to its failure exit. */
45
+ outcome: 'ok' | 'error';
46
+ }
47
+
48
+ /** A prescription is selected from observed gate facts. Execution waits until the failed run is terminal. */
49
+ export type GatePrescription =
50
+ | { action: 'rerun-pod'; reason: string; runId: string | null; commit: string }
51
+ | { action: 'repair-card'; reason: string; runId: string | null; commit: string; failureIds: string[] }
52
+ | { action: 'decision-card'; reason: string; runId: string | null; commit: string }
53
+ | { action: 'none'; reason: string; runId: string | null; commit: string };
54
+
55
+ export function chooseGatePrescription(context: GraphContext, triage: GateTriageShadow): GatePrescription {
56
+ const runId = context.runId ?? null;
57
+ const common = { runId, commit: triage.commit };
58
+ const gate = context.outputs.gate;
59
+ const unattributed = (Array.isArray(gate?.stalledShards)
60
+ && gate.stalledShards.some((shard: unknown) => shard && typeof shard === 'object'
61
+ && (shard as { reason?: unknown }).reason === 'unattributed'))
62
+ || (typeof gate?.error === 'string' && /failure attribution (?:incomplete|summary=)|unattributed/.test(gate.error));
63
+ if (triage.classification === 'exhausted')
64
+ return { ...common, action: 'decision-card', reason: 'same gate symptom recurred twice' };
65
+ if (unattributed && triage.introduced.length === 0 && context.input.gateExecutor === 'host')
66
+ return { ...common, action: 'rerun-pod', reason: 'host gate attribution incomplete' };
67
+ if (triage.classification === 'regression' && triage.introduced.length > 0)
68
+ return { ...common, action: 'repair-card', reason: 'introduced gate regressions', failureIds: [...triage.introduced] };
69
+ return { ...common, action: 'none', reason: `no evidence-backed automatic remedy for ${triage.classification}` };
44
70
  }
45
71
 
46
72
  function timeoutShape(gate: GateResult): boolean {
@@ -104,11 +130,12 @@ export function classifyGateShadow(gate: GateResult, evidence: GateTriageEvidenc
104
130
  environmentFailureIds: evidence.environmentFailureIds ? [...evidence.environmentFailureIds] : null,
105
131
  loadFlaky: gate.loadFlaky ? [...gate.loadFlaky] : null,
106
132
  stalledEnv: gate.stalledEnv.map((item) => ({ file: item.file, local: item.local })) },
107
- outcome: 'fail' };
133
+ outcome: 'ok' };
108
134
  }
109
135
 
110
136
  /** Read only the completed gate output. The graph wiring is a separate change after M3. */
111
- export function gateTriageShadowFromContext(context: GraphContext, evidence?: GateTriageEvidence): GateTriageShadow {
137
+ export function gateTriageShadowFromContext(context: GraphContext, evidence?: GateTriageEvidence,
138
+ ledgerHistory: NonNullable<GateTriageEvidence['priorDecisions']> = []): GateTriageShadow {
112
139
  const value = context.outputs.gate;
113
140
  if (!value || typeof value.commit !== 'string' || !Array.isArray(value.introduced)
114
141
  || !value.introduced.every((item) => typeof item === 'string')
@@ -140,6 +167,7 @@ export function gateTriageShadowFromContext(context: GraphContext, evidence?: Ga
140
167
  && item.failureIds.every((id: unknown) => typeof id === 'string'))
141
168
  ? { priorDecisions: supplied.priorDecisions as GateTriageEvidence['priorDecisions'] } : {}),
142
169
  };
170
+ if (!observed.priorDecisions && ledgerHistory.length) observed.priorDecisions = ledgerHistory;
143
171
  // The graph adapter maps a measured gate regression to `fail` for edge routing.
144
172
  // Restore the gate verdict before classifying the shadow; keep the context untouched.
145
173
  const gate = value.outcome === 'fail' ? { ...value, outcome: 'regression' } : value;
@@ -150,6 +178,34 @@ export function logGateTriageShadow(result: GateTriageShadow, version: string):
150
178
  debug.log('graph.ground', 'gate-triage-shadow', { version, ...result });
151
179
  }
152
180
 
181
+ /** Last two completed triage facts from distinct release runs on this exact cut commit. */
182
+ export function priorGateDecisionsFromLedger(context: GraphContext, root = effectiveInstanceRoot()): NonNullable<GateTriageEvidence['priorDecisions']> {
183
+ const commit = context.outputs.gate?.commit;
184
+ if (typeof commit !== 'string' || !commit) return [];
185
+ const dir = join(root, 'graph-runs', 'release-loop');
186
+ try {
187
+ const files = readdirSync(dir).filter(name => name.endsWith('.json'))
188
+ .map(name => ({ name, mtime: statSync(join(dir, name)).mtimeMs }))
189
+ .sort((a, b) => b.mtime - a.mtime).slice(0, 200);
190
+ const prior: NonNullable<GateTriageEvidence['priorDecisions']> = [];
191
+ for (const { name } of files) {
192
+ try {
193
+ const state = JSON.parse(readFileSync(join(dir, name), 'utf8')) as Record<string, unknown>;
194
+ if (state.runId === context.runId || (state.input as Record<string, unknown> | undefined)?.version !== context.input.version) continue;
195
+ const nodes = Array.isArray(state.nodes) ? state.nodes as Array<Record<string, unknown>> : [];
196
+ const node = [...nodes].reverse().find(item => item.nodeId === 'gate-triage' && item.executed === true);
197
+ const raw = node?.output;
198
+ const output = typeof raw === 'string' ? JSON.parse(raw) as Record<string, unknown> : raw as Record<string, unknown> | undefined;
199
+ if (output?.commit !== commit || !['unmeasured', 'load', 'env', 'regression', 'clean'].includes(String(output.classification))
200
+ || !Array.isArray(output.failureIds) || !output.failureIds.every(id => typeof id === 'string')) continue;
201
+ prior.push({ classification: output.classification as GateTriageClass, commit, failureIds: output.failureIds as string[] });
202
+ if (prior.length === 2) break;
203
+ } catch { /* Other runs may be incomplete while their ledger is being written. */ }
204
+ }
205
+ return prior.reverse();
206
+ } catch { return []; }
207
+ }
208
+
153
209
  /** A saved verdict has fewer fields than the live gate output. Keep the absent evidence absent. */
154
210
  export function gateTriageShadowFromSavedVerdict(value: unknown): { version: string; result: GateTriageShadow } {
155
211
  if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('invalid saved gate verdict');
@@ -174,20 +230,24 @@ if (import.meta.main) {
174
230
  try {
175
231
  const savedPath = process.argv[2] === '--gate-verdict' ? process.argv[3] : undefined;
176
232
  if (process.argv[2] && !savedPath) throw new Error('usage: gate-triage-node.ts [--gate-verdict <path>]');
233
+ const context = savedPath ? undefined : readGraphContext();
234
+ const history = context ? priorGateDecisionsFromLedger(context) : [];
177
235
  const { version, result } = savedPath
178
236
  ? gateTriageShadowFromSavedVerdict(JSON.parse(readFileSync(savedPath, 'utf8')))
179
- : (() => { const context = readGraphContext(); return { version: context.input.version,
180
- result: gateTriageShadowFromContext(context) }; })();
237
+ : { version: context!.input.version, result: gateTriageShadowFromContext(context!, undefined, history) };
181
238
  logGateTriageShadow(result, version);
182
- emitNodeResult({ ...result, verdict: 'fail', summary: `gate-triage shadow: ${result.classification} — ${result.reason}` });
239
+ const prescription = context ? chooseGatePrescription(context, result) : undefined;
240
+ if (prescription) debug.log('release-loop.heal', 'prescribed-shadow', { version, ...prescription });
241
+ emitNodeResult({ ...result, ...(prescription ? { prescription } : {}), verdict: 'ok',
242
+ summary: `gate-triage shadow: ${result.classification} — ${result.reason}` });
183
243
  } catch (error) {
184
244
  const reason = error instanceof Error ? error.message : String(error);
185
- const result: GateTriageShadow = { outcome: 'fail', classification: 'unmeasured', reason,
245
+ const result: GateTriageShadow = { outcome: 'error', classification: 'unmeasured', reason,
186
246
  commit: 'unknown', introduced: [], stalledFiles: [], failureIds: [],
187
247
  evidence: { highLoad: null, isolatedReproductions: null, isolatedCheckedFailureIds: null,
188
248
  isolatedPassedFiles: null, environmentFailureIds: null, loadFlaky: null, stalledEnv: [] } };
189
249
  logGateTriageShadow(result, 'unknown');
190
250
  emitNodeResult({ ...result, verdict: 'fail', summary: `gate-triage shadow: ${reason}` });
251
+ process.exitCode = 1;
191
252
  }
192
- process.exitCode = 1;
193
253
  }
@@ -79,12 +79,12 @@ function attempt(dir: string, commit: string, pods: TimedPod[]): GateWallAttempt
79
79
  const SHARDS_WINDOW_SLACK_MS = 5 * 60_000;
80
80
  /**
81
81
  * shards.json is a snapshot of the latest attempt, not an archive of every commit, and has no attempt identity.
82
- * A row is attributed to the attempt only when its root id is one of the attempt's roots AND
82
+ * A row is attributed to the attempt only when its id is one of the attempt's pod records AND
83
83
  * a row starts inside the attempt's pod-record window (from 5 min before its first start to its last end) — a
84
84
  * previous attempt's snapshot reusing `pod-0` started earlier and is rejected. The end is free: a longer
85
85
  * snapshot is exactly the disagreement this report keeps.
86
86
  */
87
- function shardsSpan(dir: string, rootNames: Set<string>, window: { start: number; end: number }): number | 'outside-attempt-window' | null {
87
+ function shardsSpan(dir: string, podNames: Set<string>, window: { start: number; end: number }): number | 'outside-attempt-window' | null {
88
88
  try {
89
89
  const file: unknown = JSON.parse(readFileSync(join(dir, 'shards.json'), 'utf8'));
90
90
  if (!file || typeof file !== 'object' || !('shards' in file) || !Array.isArray(file.shards)) return null;
@@ -94,7 +94,7 @@ function shardsSpan(dir: string, rootNames: Set<string>, window: { start: number
94
94
  const ends: number[] = [];
95
95
  for (const row of rows) {
96
96
  if (!row || typeof row !== 'object' || !('id' in row) || typeof row.id !== 'string'
97
- || !rootNames.has(`${row.id}.json`)) return null;
97
+ || !podNames.has(`${row.id}.json`)) return null;
98
98
  const start = 'startedAt' in row && typeof row.startedAt === 'string' ? Date.parse(row.startedAt) : NaN;
99
99
  const end = 'endedAt' in row && typeof row.endedAt === 'string' ? Date.parse(row.endedAt) : NaN;
100
100
  if (!Number.isFinite(start) || !Number.isFinite(end) || end < start) return null;
@@ -134,10 +134,10 @@ export function gateWall(dir: string, opts: { commit?: string; targetMin?: numbe
134
134
  const chosen = matching.find(([, pods]) => pods.some((p) => p.root));
135
135
  const attempts = ordered.map(([commit, pods]) => attempt(dir, commit, pods));
136
136
  const selected = chosen && attempts.find((a) => a.commit === chosen[0]);
137
- // The snapshot has no commit field. Only the latest recorded attempt can own it; require its root row IDs too.
137
+ // The snapshot has no commit field. Only the latest recorded attempt can own it; require every row's pod record.
138
138
  const latest = chosen !== undefined && ordered[0]?.[0] === chosen[0];
139
139
  const span = chosen && latest
140
- ? shardsSpan(dir, new Set(chosen[1].filter((p) => p.root).map((p) => p.name)),
140
+ ? shardsSpan(dir, new Set(chosen[1].map((p) => p.name)),
141
141
  { start: Math.min(...chosen[1].map((p) => p.start)), end: Math.max(...chosen[1].map((p) => p.end)) }) : null;
142
142
  const snapshot = typeof span === 'number' ? span : null;
143
143
  const shardsWallMin = snapshot;