elanous 0.2.19 → 0.2.21

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 (509) 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/MObU8ujkMciAKr6ljAV05/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/088d8e8a.8069543d082bec12.js +160 -0
  5. package/apps/pwa/out/_next/static/chunks/1109.78a7a3288c0259d9.js +148 -0
  6. package/apps/pwa/out/_next/static/chunks/115.1c6690b8604f81c4.js +32 -0
  7. package/apps/pwa/out/_next/static/chunks/119fc678.6f591cdb84ca5791.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1262.76a3f08e67c5f816.js +309 -0
  9. package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 1268.0840ff91e083fcbd.js} +1 -1
  10. package/apps/pwa/out/_next/static/chunks/1307-c97eba9fb72a9a5e.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/1358d292.5c87f12cad6eaa91.js +122 -0
  12. package/apps/pwa/out/_next/static/chunks/1438.27a1a7b8f2308004.js +149 -0
  13. package/apps/pwa/out/_next/static/chunks/1442.02d85cd88f4c1eac.js +79 -0
  14. package/apps/pwa/out/_next/static/chunks/1501-1aad662d82ba43f7.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/1584.9e2dd53da6e6d4b1.js +24 -0
  16. package/apps/pwa/out/_next/static/chunks/1586.b989b0614afb77e6.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/1609.2ad355062870151f.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/1612-f6df0fc0eed7157b.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/1923.37f1d5fe7ebe3860.js +27 -0
  21. package/apps/pwa/out/_next/static/chunks/1948.5f35b39eef222a94.js +5 -0
  22. package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/1993.3fd074a2a246234e.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/{2977-e38442e86289f1d3.js → 211-9c9090c59e2c5907.js} +1 -1
  26. package/apps/pwa/out/_next/static/chunks/2140.d2daf9ee96e295d1.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2212.0359851457473efc.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/224.583e562365a52f74.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/2328.0f37465646bafa7d.js +36 -0
  31. package/apps/pwa/out/_next/static/chunks/2372.bf71372184991937.js +286 -0
  32. package/apps/pwa/out/_next/static/chunks/2489-ff55a35b202c0f94.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/2549-a8515c86f9e8cb96.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/2655.72cc6a97276c5acc.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/2743-ef6e8c3ced74cce5.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/2840-5458ad50c49cac6a.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/2856.f75d5baef7f49b74.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/3344.0de4b0ec5a454b7a.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/335.a13f339e97893437.js +94 -0
  45. package/apps/pwa/out/_next/static/chunks/3394.646348f34b2500be.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/3443.6276909b53d72d53.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/3495.abd4254d808bf2dc.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/3522-1de1319d028ac89c.js +25 -0
  49. package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/3721.ba5b82db0d9a5572.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/3857-995ea9f5ef33d71e.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/3919.9ff7dc25afefee33.js +74 -0
  54. package/apps/pwa/out/_next/static/chunks/3998.bd9a69d9b50e9bd2.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/4003.ab6db1dc266576c9.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/4144-fad095b48a1862ee.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/4216.e8ac94b9fe6b96ab.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/4327.c44dc2b55cb43ee5.js +34 -0
  59. package/apps/pwa/out/_next/static/chunks/4401.94257d497b6d26bc.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/4559.17ca2cdbaa8d414f.js +115 -0
  61. package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/4588.ad3364cb7fb97ada.js +86 -0
  63. package/apps/pwa/out/_next/static/chunks/4706.97e734bd4c9816ca.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/4795-f372490b178583fc.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/4982.6e44352f751b9a1b.js +77 -0
  66. package/apps/pwa/out/_next/static/chunks/5342.49c5c5a530e62b50.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/5360-08a80abdf3a4da99.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/5431.66c58ab1327ee0b7.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/5717.bcfcb96e701b82e0.js +90 -0
  70. package/apps/pwa/out/_next/static/chunks/5755-30d102586789bf7f.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/5957.3f1a4012388dd962.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/5977-59fedea50d13c762.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/602.9bf02ac8e9502598.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/6020.3a1b347e2d2dfacc.js +8 -0
  75. package/apps/pwa/out/_next/static/chunks/{4847.56f5bc454fbcc827.js → 6110.1297d4093fdfbb0d.js} +2 -2
  76. package/apps/pwa/out/_next/static/chunks/6158.566c890adc06a459.js +2 -0
  77. package/apps/pwa/out/_next/static/chunks/6375.fce1d174ba6d0324.js +64 -0
  78. package/apps/pwa/out/_next/static/chunks/6546.92a828aef441271f.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/658.957fdb0a8c6c6c8e.js +287 -0
  80. package/apps/pwa/out/_next/static/chunks/6706.323590aabb8b1293.js +226 -0
  81. package/apps/pwa/out/_next/static/chunks/6747.b67947f293a4c02d.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/6767.8e6c5f6e744fcd9f.js +38 -0
  83. package/apps/pwa/out/_next/static/chunks/6868.000b24a3045c5574.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/6946.bba74e9c96050f30.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/7033.a558f1b2f6912a53.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/7065.46029c26c53eac50.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/7087.6fae577d09249bc1.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/7175-350f4ce2873bc038.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/722.de8d9801e0f6d414.js +41 -0
  91. package/apps/pwa/out/_next/static/chunks/{1470.98a6ab61ccb4131c.js → 726.83532d69452ee24b.js} +1 -1
  92. package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 7327-2d69fb321ba73389.js} +1 -1
  94. package/apps/pwa/out/_next/static/chunks/7497.57981e9d7a75a52a.js +24 -0
  95. package/apps/pwa/out/_next/static/chunks/7630.2b9a68659053ff4e.js +193 -0
  96. package/apps/pwa/out/_next/static/chunks/7672.4af122ad892117c7.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/771.6ab889a233e7dae7.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/7899.597af4a9996c907f.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/79b78388.663188133449d707.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/8096.f7da0776cfde6300.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8194.484f51e273d9abd2.js +217 -0
  105. package/apps/pwa/out/_next/static/chunks/8401-35e94e9091f5eb8d.js +1 -0
  106. package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/8769.815ca69879f608b4.js +104 -0
  109. package/apps/pwa/out/_next/static/chunks/8857-f1762140ef9a96b1.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/88729267.9c6c000157c83f2f.js +62 -0
  111. package/apps/pwa/out/_next/static/chunks/8f835b4f.048eeb02299e3e12.js +1 -0
  112. package/apps/pwa/out/_next/static/chunks/9068.5f2764fccf86c1b2.js +93 -0
  113. package/apps/pwa/out/_next/static/chunks/9070.067c106528ec1ae6.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/9103-60fbef33e66f0a46.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/9247-7581825d511a64c0.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/{7647.2fcd30d894d5ee30.js → 935.4ddaa99fc1d1126c.js} +1 -1
  117. package/apps/pwa/out/_next/static/chunks/9451.ebfb0dcc9c02250f.js +23 -0
  118. package/apps/pwa/out/_next/static/chunks/9497-b0e758d1cd4ae8d7.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/9559.e648369b1d57cf9a.js +62 -0
  120. package/apps/pwa/out/_next/static/chunks/9832.2ab2e818013574c7.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/a9f2b54d-40c16bd0efdb04b8.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +1 -0
  123. package/apps/pwa/out/_next/static/chunks/app/approvals/page-937c92292a4ab4f0.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-d781be3e3a9cfd74.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/board/page-b0a80997903852d8.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/botlab/page-2b1263165630d9c8.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/bots/page-ea998f7f5d156bea.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/ceo/page-33c328b64ca508f8.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/chat/page-e2d6bed48ac20120.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/consult/page-111cabe4d1fd69ae.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/control/page-544dd4bd4847eeb7.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/decisions/page-3ee191319bf97d87.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/design-check/page-e4bcb2c503dab04b.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/editor/page-300897dcbbd5ed76.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/exec/page-a449f1ed472b81c2.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/field/page-9045094d6d3b5cd0.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/inside/page-c06673596849ef45.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/intake/page-103fa3191c1b2e08.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/layout-9f81233f29dcd409.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/live/{page-b70cd005d9169e25.js → page-be8b4615c919e36c.js} +1 -1
  142. package/apps/pwa/out/_next/static/chunks/app/live-run/page-c685b104e0dfa609.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/loops/page-8d693331cfbb6add.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/market/page-cef4f30a4c22fae1.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66ea1b54fc83fa15.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/morning/page-7a12f61be4a579d1.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/not-found-c85b0589591a99c9.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/observatory/page-725a4da02dd129eb.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-8cc707bd09a497d9.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-90b55be1f15ce0e8.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cb16606e08be65db.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/outputs/page-16dc64b74ed6a40a.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/page-21bec4afeb2f0917.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/reflection/page-dc39ee7a36d26385.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-84ae759f76053672.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/sessions/page-3db58ddb61fe2be9.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c5315e30f3528dc4.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/settings/page-2fe368efb6de3b6a.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8d04132dd0ac5e10.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/page-f23484de467c9ea3.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/share/page-c69762987b751174.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/showroom/page-5456692bda39aafb.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/tasks/page-d75f7cd65845ce98.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/term/page-5603f5e568b25ce3.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/today/page-d2cc0f4e89f42028.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/trace/page-a6eb73b8a4840f1c.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/vault/page-c0a3f4d10edb83fa.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6c0b88068c143f15.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/workflows/page-7f8d7fcdf058a845.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workspace/page-22163556cd38bc88.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-141436fd26dd56fc.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/d70879db.c66c56a95a91efc6.js +66 -0
  174. package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → f6231284-45726550aba08fca.js} +1 -1
  175. package/apps/pwa/out/_next/static/chunks/{2ea96958.e8c7e112de50b067.js → f6b7c9ba.64a895b8b2fedbdc.js} +1 -1
  176. package/apps/pwa/out/_next/static/chunks/f8626309.a4e5d362b1d55e59.js +1 -0
  177. package/apps/pwa/out/_next/static/chunks/framework-5c169757551134ed.js +1 -0
  178. package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +1 -0
  179. package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +1 -0
  180. package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/webpack-f2b4016b9b981bc8.js +1 -0
  183. package/apps/pwa/out/_next/static/css/{d3427996da11e480.css → b9d8151e48998417.css} +1 -1
  184. package/apps/pwa/out/_next/static/css/e81df11490b4413b.css +1 -0
  185. package/apps/pwa/out/approvals/index.html +1 -1
  186. package/apps/pwa/out/approvals/index.txt +23 -23
  187. package/apps/pwa/out/autopilot/index.html +1 -1
  188. package/apps/pwa/out/autopilot/index.txt +23 -23
  189. package/apps/pwa/out/board/index.html +1 -1
  190. package/apps/pwa/out/board/index.txt +23 -23
  191. package/apps/pwa/out/botlab/index.html +1 -1
  192. package/apps/pwa/out/botlab/index.txt +22 -22
  193. package/apps/pwa/out/bots/index.html +1 -1
  194. package/apps/pwa/out/bots/index.txt +22 -22
  195. package/apps/pwa/out/ceo/index.html +1 -1
  196. package/apps/pwa/out/ceo/index.txt +23 -23
  197. package/apps/pwa/out/chat/index.html +1 -1
  198. package/apps/pwa/out/chat/index.txt +22 -22
  199. package/apps/pwa/out/consult/index.html +1 -1
  200. package/apps/pwa/out/consult/index.txt +23 -23
  201. package/apps/pwa/out/control/index.html +1 -1
  202. package/apps/pwa/out/control/index.txt +23 -23
  203. package/apps/pwa/out/decisions/index.html +1 -1
  204. package/apps/pwa/out/decisions/index.txt +22 -22
  205. package/apps/pwa/out/design-check/index.html +1 -1
  206. package/apps/pwa/out/design-check/index.txt +23 -23
  207. package/apps/pwa/out/editor/index.html +1 -1
  208. package/apps/pwa/out/editor/index.txt +24 -24
  209. package/apps/pwa/out/exec/index.html +1 -1
  210. package/apps/pwa/out/exec/index.txt +23 -23
  211. package/apps/pwa/out/field/index.html +1 -1
  212. package/apps/pwa/out/field/index.txt +23 -23
  213. package/apps/pwa/out/healthz +1 -1
  214. package/apps/pwa/out/index.html +1 -1
  215. package/apps/pwa/out/index.txt +22 -22
  216. package/apps/pwa/out/inside/index.html +1 -1
  217. package/apps/pwa/out/inside/index.txt +23 -23
  218. package/apps/pwa/out/intake/index.html +1 -1
  219. package/apps/pwa/out/intake/index.txt +23 -23
  220. package/apps/pwa/out/live/index.html +1 -1
  221. package/apps/pwa/out/live/index.txt +23 -23
  222. package/apps/pwa/out/live-run/index.html +1 -0
  223. package/apps/pwa/out/live-run/index.txt +35 -0
  224. package/apps/pwa/out/loops/index.html +1 -1
  225. package/apps/pwa/out/loops/index.txt +22 -22
  226. package/apps/pwa/out/market/index.html +1 -1
  227. package/apps/pwa/out/market/index.txt +22 -22
  228. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  229. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  230. package/apps/pwa/out/morning/index.html +1 -1
  231. package/apps/pwa/out/morning/index.txt +23 -23
  232. package/apps/pwa/out/observatory/index.html +1 -1
  233. package/apps/pwa/out/observatory/index.txt +23 -23
  234. package/apps/pwa/out/ops/checklist/index.html +1 -1
  235. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  236. package/apps/pwa/out/ops/release/index.html +1 -1
  237. package/apps/pwa/out/ops/release/index.txt +22 -22
  238. package/apps/pwa/out/ops/seats/index.html +1 -1
  239. package/apps/pwa/out/ops/seats/index.txt +22 -22
  240. package/apps/pwa/out/outputs/index.html +1 -1
  241. package/apps/pwa/out/outputs/index.txt +22 -22
  242. package/apps/pwa/out/reflection/index.html +1 -1
  243. package/apps/pwa/out/reflection/index.txt +23 -23
  244. package/apps/pwa/out/scheduler/index.html +1 -1
  245. package/apps/pwa/out/scheduler/index.txt +23 -23
  246. package/apps/pwa/out/sessions/index.html +1 -1
  247. package/apps/pwa/out/sessions/index.txt +22 -22
  248. package/apps/pwa/out/settings/devices/index.html +1 -1
  249. package/apps/pwa/out/settings/devices/index.txt +23 -23
  250. package/apps/pwa/out/settings/index.html +1 -1
  251. package/apps/pwa/out/settings/index.txt +23 -23
  252. package/apps/pwa/out/setup/done/index.html +1 -1
  253. package/apps/pwa/out/setup/done/index.txt +23 -23
  254. package/apps/pwa/out/setup/index.html +1 -1
  255. package/apps/pwa/out/setup/index.txt +23 -23
  256. package/apps/pwa/out/share/index.html +1 -1
  257. package/apps/pwa/out/share/index.txt +23 -23
  258. package/apps/pwa/out/showroom/index.html +1 -1
  259. package/apps/pwa/out/showroom/index.txt +22 -22
  260. package/apps/pwa/out/tasks/index.html +1 -1
  261. package/apps/pwa/out/tasks/index.txt +23 -23
  262. package/apps/pwa/out/term/index.html +1 -1
  263. package/apps/pwa/out/term/index.txt +23 -23
  264. package/apps/pwa/out/today/index.html +1 -1
  265. package/apps/pwa/out/today/index.txt +23 -23
  266. package/apps/pwa/out/trace/index.html +1 -1
  267. package/apps/pwa/out/trace/index.txt +23 -23
  268. package/apps/pwa/out/vault/index.html +1 -1
  269. package/apps/pwa/out/vault/index.txt +23 -23
  270. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  271. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  272. package/apps/pwa/out/workflows/index.html +1 -1
  273. package/apps/pwa/out/workflows/index.txt +23 -23
  274. package/apps/pwa/out/workspace/index.html +1 -1
  275. package/apps/pwa/out/workspace/index.txt +23 -23
  276. package/apps/pwa/out/worktrees/index.html +1 -1
  277. package/apps/pwa/out/worktrees/index.txt +23 -23
  278. package/graphs/demo/editor-recipes.yaml +22 -0
  279. package/graphs/drafts/recipes.yaml +4 -4
  280. package/graphs/implement-loop.yaml +277 -5
  281. package/graphs/retro/daily.yaml +25 -0
  282. package/graphs/retro/recipes.yaml +24 -0
  283. package/package.json +1 -1
  284. package/scripts/ask-marker-check.ts +3 -1
  285. package/scripts/coord-channel-watch.sh +31 -4
  286. package/scripts/docs-lint.ts +4 -1
  287. package/scripts/land-global-checks.ts +89 -0
  288. package/scripts/lib/pch-regress-map.ts +2 -1
  289. package/scripts/marketing/generate-ax-brief.ts +18 -1
  290. package/scripts/measure-nl-routing-corpus.ts +11 -3
  291. package/scripts/release-loop/gate-node.ts +296 -25
  292. package/scripts/release-loop/gate-timing.ts +116 -7
  293. package/scripts/release-loop/preflight.ts +6 -4
  294. package/scripts/release-loop/publish-window.ts +141 -0
  295. package/scripts/release-loop/release-docs.ts +1 -4
  296. package/scripts/release-loop/release-universe.ts +38 -0
  297. package/scripts/release-loop/resume-release.ts +4 -1
  298. package/scripts/release-loop/shard-plan.ts +50 -5
  299. package/scripts/release-loop/unattended-release.ts +10 -2
  300. package/scripts/release-story/draft.ts +5 -1
  301. package/scripts/retro/shadow-compare.ts +71 -0
  302. package/scripts/rhythm/daily.ts +8 -2
  303. package/src/agent-mission/driver.ts +34 -6
  304. package/src/autopilot/build/llm-conflict-merge.ts +132 -7
  305. package/src/autopilot/build/merge-intent.ts +135 -7
  306. package/src/away/release-watch.ts +20 -3
  307. package/src/browser-cdp/client.ts +10 -5
  308. package/src/cli/config-drift.ts +513 -0
  309. package/src/cli/log-event-names.ts +4 -0
  310. package/src/cli/ops-cli.ts +11 -0
  311. package/src/cli/ops-overview-cli.ts +50 -0
  312. package/src/cli/pod-cli.ts +1 -0
  313. package/src/cli/pr-cli.ts +20 -0
  314. package/src/cli/release-cli.ts +20 -5
  315. package/src/cli/retro-cli.ts +78 -0
  316. package/src/cli/tasks-cli.ts +200 -8
  317. package/src/dashboard/index.ts +40 -4
  318. package/src/dashboard/slash-runtime/dashboard-handlers.ts +5 -4
  319. package/src/dashboard/slash-runtime/help-from-registry.ts +57 -31
  320. package/src/domains/outbound-log-probe.ts +6 -1
  321. package/src/esc-abort-gate.ts +3 -1
  322. package/src/graph-runner/graph-cli.ts +57 -0
  323. package/src/graph-runner/graph-run-api.ts +218 -0
  324. package/src/graph-runner/runner.ts +11 -1
  325. package/src/graph-wizard/archetypes.ts +88 -0
  326. package/src/graph-wizard/generate.ts +545 -0
  327. package/src/graph-wizard/saved-steps.ts +120 -0
  328. package/src/graph-wizard/steps.ts +193 -0
  329. package/src/harness/harness-cli-command.ts +482 -59
  330. package/src/harness/harness-queue.ts +315 -25
  331. package/src/harness/harness-salvage-cli.ts +62 -0
  332. package/src/hq/fence-outcomes.ts +63 -0
  333. package/src/hq/fence-wrapper.ts +33 -0
  334. package/src/hq/hq.ts +11 -1
  335. package/src/index.ts +76 -10
  336. package/src/intake-plane/digest.ts +18 -11
  337. package/src/maturity/feature-maturity.ts +4 -3
  338. package/src/nexus/api/draft-metrics.ts +114 -0
  339. package/src/nexus/api/graph-kinds.ts +10 -4
  340. package/src/nexus/api/graph-wizard-api.ts +53 -0
  341. package/src/nexus/api/graphs-api.ts +220 -10
  342. package/src/nexus/api/harness-traversal.ts +205 -0
  343. package/src/nexus/api/http-server.ts +69 -3
  344. package/src/nexus/api/loop-edges.ts +4 -2
  345. package/src/nexus/api/ops-api.ts +22 -7
  346. package/src/nexus/api/ops-board-route.ts +48 -0
  347. package/src/nexus/outbound/router.ts +28 -8
  348. package/src/onboarding/entry-hints.ts +5 -0
  349. package/src/onboarding/first-run.ts +3 -0
  350. package/src/onboarding.ts +24 -6
  351. package/src/ops/overview-collect.ts +345 -0
  352. package/src/ops/overview-seat-era.ts +90 -0
  353. package/src/ops/overview.ts +223 -0
  354. package/src/release-loop/feature-store.ts +14 -11
  355. package/src/release-loop/gate-shards.ts +163 -0
  356. package/src/release-loop/release-schedule.ts +6 -2
  357. package/src/retro/daily.ts +372 -0
  358. package/src/seat-loop/op-decide.ts +173 -0
  359. package/src/seat-loop/seat-loop.ts +60 -3
  360. package/src/seat-loop/seat-report.ts +58 -3
  361. package/src/self-dev/ask-launch-flow.ts +9 -6
  362. package/src/self-dev/ask-launch-io.ts +52 -2
  363. package/src/self-dev/ask-path-candidates.ts +40 -2
  364. package/src/self-dev/draft-sweep.ts +371 -16
  365. package/src/self-dev/draft-triage-rules.ts +19 -4
  366. package/src/self-dev/graph-journey-nodes.ts +157 -0
  367. package/src/self-dev/graph-journey-route.ts +105 -0
  368. package/src/self-dev/launch-preflight.ts +43 -3
  369. package/src/self-dev/pr-preview.ts +84 -16
  370. package/src/self-dev/release-path-guard.ts +7 -4
  371. package/src/self-implement/blocked-draft-policy.ts +101 -0
  372. package/src/self-implement/execution-clarification.ts +11 -0
  373. package/src/self-implement/frozen-merges.ts +23 -0
  374. package/src/self-implement/gate-env-retry.ts +83 -0
  375. package/src/self-implement/goal-author.ts +14 -0
  376. package/src/self-implement/heal-intake.ts +3 -0
  377. package/src/self-implement/orchestrator.ts +234 -6
  378. package/src/self-implement/run-stop.ts +123 -0
  379. package/src/self-implement/run-ttl.ts +258 -0
  380. package/src/self-implement/salvage-retention.ts +123 -0
  381. package/src/self-implement/seams.ts +7 -0
  382. package/src/self-implement/self-implement-cli.ts +2 -0
  383. package/src/self-implement/self-implement-runtime.ts +12 -0
  384. package/src/self-implement/sibling-resync.ts +223 -0
  385. package/src/self-implement/stop-autoheal.ts +204 -0
  386. package/src/session-runtime/index.ts +5 -3
  387. package/src/shell-env-bootstrap.ts +17 -3
  388. package/src/task-agent/board.ts +1 -1
  389. package/src/task-agent/card-evidence.ts +105 -10
  390. package/src/task-agent/judge.ts +7 -0
  391. package/src/task-agent/live-moves.ts +120 -20
  392. package/src/task-agent/mission.ts +1 -1
  393. package/src/task-agent/parent-host.ts +315 -0
  394. package/src/task-agent/shadow.ts +10 -1
  395. package/src/task-agent/task-hand.ts +16 -4
  396. package/src/task-orchestrator/surfaces/pod-command-job.ts +60 -11
  397. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +4 -0
  398. package/src/task-orchestrator/surfaces/pod-gate-deps.ts +123 -0
  399. package/src/task-orchestrator/surfaces/pod-install-slots.ts +89 -0
  400. package/src/task-orchestrator/surfaces/pod-lease.ts +93 -10
  401. package/src/task-orchestrator/surfaces/pod-pool.ts +147 -34
  402. package/src/task-orchestrator/surfaces/self-implement-pod.ts +58 -9
  403. package/src/telegram-commands.ts +41 -0
  404. package/src/testing/real-cli-spawn.ts +106 -0
  405. package/src/tool-runtime/browser-runtime.ts +87 -3
  406. package/src/tool-runtime/index.ts +1 -0
  407. package/src/user-config.ts +76 -7
  408. package/src/version/packed-revision.json +1 -1
  409. package/apps/pwa/out/_next/static/J5H7BvqgSI8DUjtRRcA_S/_buildManifest.js +0 -1
  410. package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
  411. package/apps/pwa/out/_next/static/chunks/1157-cba0e8553d35a980.js +0 -1
  412. package/apps/pwa/out/_next/static/chunks/171-23a877ed11720de9.js +0 -1
  413. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
  414. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
  415. package/apps/pwa/out/_next/static/chunks/2336-e830c777eb6bce04.js +0 -1
  416. package/apps/pwa/out/_next/static/chunks/2375-e061bb24cd2b5cde.js +0 -1
  417. package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +0 -1
  418. package/apps/pwa/out/_next/static/chunks/3094.67339318c7f72a3a.js +0 -2
  419. package/apps/pwa/out/_next/static/chunks/338-ceb956612dcfaa86.js +0 -1
  420. package/apps/pwa/out/_next/static/chunks/3395-835e4ceaa6335ba4.js +0 -1
  421. package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
  422. package/apps/pwa/out/_next/static/chunks/3441-be82e3595aef3a20.js +0 -1
  423. package/apps/pwa/out/_next/static/chunks/3689-89905039bf3e7257.js +0 -1
  424. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
  425. package/apps/pwa/out/_next/static/chunks/3822-6f268bc26c6a84ac.js +0 -1
  426. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
  427. package/apps/pwa/out/_next/static/chunks/4457-d7df81c90f9d0268.js +0 -1
  428. package/apps/pwa/out/_next/static/chunks/4551-77dadae76f6cbace.js +0 -1
  429. package/apps/pwa/out/_next/static/chunks/4978.6fd93b974441e87f.js +0 -1
  430. package/apps/pwa/out/_next/static/chunks/5012-f746f6549b869f91.js +0 -1
  431. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
  432. package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
  433. package/apps/pwa/out/_next/static/chunks/62-5bfed1e23790b745.js +0 -1
  434. package/apps/pwa/out/_next/static/chunks/6320-9bc8561fde416e07.js +0 -1
  435. package/apps/pwa/out/_next/static/chunks/649-ca96d1c5d09e02b8.js +0 -1
  436. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
  437. package/apps/pwa/out/_next/static/chunks/6726-85a7db59b288c53f.js +0 -1
  438. package/apps/pwa/out/_next/static/chunks/6735-33267c4630d0d963.js +0 -1
  439. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
  440. package/apps/pwa/out/_next/static/chunks/7633-141682015873beb6.js +0 -1
  441. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
  442. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
  443. package/apps/pwa/out/_next/static/chunks/8067-143791231c99bd02.js +0 -1
  444. package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
  445. package/apps/pwa/out/_next/static/chunks/8430-a10cfba8ef282d4e.js +0 -1
  446. package/apps/pwa/out/_next/static/chunks/8870-9614a213b990b10b.js +0 -25
  447. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
  448. package/apps/pwa/out/_next/static/chunks/9454-12c2f30dea62291c.js +0 -1
  449. package/apps/pwa/out/_next/static/chunks/976-4a9b4ebdca7c739a.js +0 -1
  450. package/apps/pwa/out/_next/static/chunks/9989-15cdf568bafa9fc8.js +0 -1
  451. package/apps/pwa/out/_next/static/chunks/ae787e4c-e5b1ef00b2dbceca.js +0 -1
  452. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
  453. package/apps/pwa/out/_next/static/chunks/app/approvals/page-deef5ee191830f14.js +0 -1
  454. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-b193da485f9a14b2.js +0 -1
  455. package/apps/pwa/out/_next/static/chunks/app/board/page-b6e1a79df0e214f4.js +0 -1
  456. package/apps/pwa/out/_next/static/chunks/app/botlab/page-e84df4d907f703c7.js +0 -1
  457. package/apps/pwa/out/_next/static/chunks/app/bots/page-a5fb9a5db89ae4ea.js +0 -1
  458. package/apps/pwa/out/_next/static/chunks/app/ceo/page-1499f481d7fc52d6.js +0 -1
  459. package/apps/pwa/out/_next/static/chunks/app/chat/page-c7cf753aeb648790.js +0 -1
  460. package/apps/pwa/out/_next/static/chunks/app/consult/page-4118469e514e10fd.js +0 -1
  461. package/apps/pwa/out/_next/static/chunks/app/control/page-e968f7630654332a.js +0 -1
  462. package/apps/pwa/out/_next/static/chunks/app/decisions/page-505915052f947022.js +0 -1
  463. package/apps/pwa/out/_next/static/chunks/app/design-check/page-c2f962fb4a3b06c6.js +0 -1
  464. package/apps/pwa/out/_next/static/chunks/app/editor/page-7b5ed0bd8a589ec4.js +0 -1
  465. package/apps/pwa/out/_next/static/chunks/app/exec/page-cf8193250dd42dd4.js +0 -1
  466. package/apps/pwa/out/_next/static/chunks/app/field/page-cf3f5dfa35dea8e9.js +0 -1
  467. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
  468. package/apps/pwa/out/_next/static/chunks/app/inside/page-5b29d9b06ceadade.js +0 -1
  469. package/apps/pwa/out/_next/static/chunks/app/intake/page-a15e7fcc283e9ca7.js +0 -1
  470. package/apps/pwa/out/_next/static/chunks/app/layout-d2915e7b70ff3d0e.js +0 -1
  471. package/apps/pwa/out/_next/static/chunks/app/loops/page-2595f0eba29f57e1.js +0 -1
  472. package/apps/pwa/out/_next/static/chunks/app/market/page-c776322454156e36.js +0 -1
  473. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-77bbd17f4435aa10.js +0 -1
  474. package/apps/pwa/out/_next/static/chunks/app/morning/page-049689e611ecc936.js +0 -1
  475. package/apps/pwa/out/_next/static/chunks/app/not-found-85954a4fb09c44eb.js +0 -1
  476. package/apps/pwa/out/_next/static/chunks/app/observatory/page-e773e552e1068ee6.js +0 -1
  477. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-5633c4bfa7684b4a.js +0 -1
  478. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-d829b92bd01b4994.js +0 -1
  479. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-061c317fc771becf.js +0 -1
  480. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a45ece8865ed4b6.js +0 -1
  481. package/apps/pwa/out/_next/static/chunks/app/page-0be990618c0d56bd.js +0 -1
  482. package/apps/pwa/out/_next/static/chunks/app/reflection/page-bfea4865dc244b73.js +0 -1
  483. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-0d6ba8e337741484.js +0 -1
  484. package/apps/pwa/out/_next/static/chunks/app/sessions/page-76a88b55d816482e.js +0 -1
  485. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-bba1542df59f61ce.js +0 -1
  486. package/apps/pwa/out/_next/static/chunks/app/settings/page-4185c95689bb372e.js +0 -1
  487. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-b699e1a8d5d34b8b.js +0 -1
  488. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
  489. package/apps/pwa/out/_next/static/chunks/app/setup/page-c225e338c31bf274.js +0 -1
  490. package/apps/pwa/out/_next/static/chunks/app/share/page-449353f70507ca1f.js +0 -1
  491. package/apps/pwa/out/_next/static/chunks/app/showroom/page-90b889714102476d.js +0 -1
  492. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f1ee7fbeb7f96ddc.js +0 -1
  493. package/apps/pwa/out/_next/static/chunks/app/term/page-d59a95e1434dfaa0.js +0 -1
  494. package/apps/pwa/out/_next/static/chunks/app/today/page-71491a23414d1a47.js +0 -1
  495. package/apps/pwa/out/_next/static/chunks/app/trace/page-de7112bdb65fa4fe.js +0 -1
  496. package/apps/pwa/out/_next/static/chunks/app/vault/page-feb4d0d88079660d.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-f17d887b40dd6b1d.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/app/workflows/page-8da77be2192bcd5d.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/app/workspace/page-72c3b9a5e9582b29.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3afe4c8d0c48c39.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/main-app-b9308f2d8a931531.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
  505. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
  506. package/apps/pwa/out/_next/static/chunks/webpack-6f35f9d892fa630b.js +0 -1
  507. package/apps/pwa/out/_next/static/css/85f53c6f35d11382.css +0 -1
  508. /package/apps/pwa/out/_next/static/{J5H7BvqgSI8DUjtRRcA_S → MObU8ujkMciAKr6ljAV05}/_ssgManifest.js +0 -0
  509. /package/skills/project-onboarding/{skill.md → SKILL.md} +0 -0
@@ -1,6 +1,7 @@
1
1
  import { CLAIM_IDLE_HOURS, PR_LABELS, STALLED_DRAFT_HOURS } from '../github/pr-labels.js';
2
2
  import { debug } from '../debug/log.js';
3
- import { decideDraft, sameGoalPr, type DraftTriagePr } from './draft-triage-rules.js';
3
+ import { decideDraft, laterMergedTwins, sameGoalPr, type DraftTriagePr } from './draft-triage-rules.js';
4
+ import { branchLineageSlug } from '../cli/pr-lineage.js';
4
5
 
5
6
  export interface SweepDraft extends DraftTriagePr {
6
7
  readonly title: string;
@@ -13,12 +14,17 @@ export interface SweepDraft extends DraftTriagePr {
13
14
 
14
15
  export interface SweepMergedPr extends DraftTriagePr {
15
16
  readonly title: string;
17
+ readonly createdAt?: string;
18
+ readonly mergedAt?: string;
19
+ readonly labels?: readonly string[];
16
20
  }
17
21
 
18
22
  export interface DraftSweepAdapters {
19
23
  /** Each call returns one complete page; a rejection must not be mistaken for an empty page. */
20
24
  listDrafts(page: number, perPage: number, repository: string): Promise<readonly SweepDraft[]>;
21
25
  listMerged(page: number, perPage: number, repository: string): Promise<readonly SweepMergedPr[]>;
26
+ /** Complete closed PR inventory for the creation cohort (including closed without merge). */
27
+ listRecentClosed?(repository: string, createdSince: Date): Promise<readonly SweepMergedPr[]>;
22
28
  /** Undefined means no run assessment; a merged twin or 24h idle draft can still authorize a close. */
23
29
  getRunStatus(draft: SweepDraft, repository: string): Promise<string | undefined>;
24
30
  /** Undefined means liveness could not be established. */
@@ -27,6 +33,8 @@ export interface DraftSweepAdapters {
27
33
  closeDraft(repository: string, number: number, comment: string): Promise<void>;
28
34
  /** Resolve the latest claim comment owner; a failed lookup must not authorize a close. */
29
35
  getClaimOwner?(repository: string, number: number): Promise<string | undefined>;
36
+ /** Current owner, accounting for a later claim release; only draft metrics need this distinction. */
37
+ getActiveClaimOwner?(repository: string, number: number): Promise<string | undefined>;
30
38
  /** Whether the current owned run has a self-implement.result final observation, even if its worktree remains. */
31
39
  hasFinalRunResult?(draft: SweepDraft, repository: string): Promise<boolean | undefined>;
32
40
  /** Per-file last change on the draft branch; undefined means file coverage is unverified. */
@@ -38,6 +46,8 @@ export interface DraftSweepAdapters {
38
46
  * `review: 'pass'` is a review PASS; `mustFix` means must-fix remains; `gate: 'pass'` is a green gate.
39
47
  */
40
48
  getReviewGate?(draft: SweepDraft, repository: string): Promise<SweepReviewGate | undefined>;
49
+ /** One PR by number (any state); undefined means it could not be read. Only overlap metrics need it. */
50
+ getPr?(repository: string, number: number): Promise<SweepMergedPr | undefined>;
41
51
  }
42
52
 
43
53
  /** Review PASS and gate posture used only by the daily old-draft count. */
@@ -96,6 +106,307 @@ export interface DraftSweepDaily {
96
106
  readonly blocked: number;
97
107
  }
98
108
 
109
+ /** Open harness drafts now; conversion is the 48h creation cohort, not the daily over-24h census. */
110
+ export interface DraftMetrics {
111
+ readonly inventory: number;
112
+ readonly oldestAgeHours: number | null;
113
+ readonly needsOwner: number;
114
+ readonly converted48h: number;
115
+ readonly cohort48h: number;
116
+ readonly conversion48h: number | null;
117
+ }
118
+
119
+ export async function collectDraftMetrics(repository: string, adapters: Pick<DraftSweepAdapters, 'listDrafts' | 'listRecentClosed' | 'getActiveClaimOwner'>, now: Date): Promise<DraftMetrics> {
120
+ const clock = now.getTime();
121
+ if (!Number.isFinite(clock)) throw new Error('Invalid draft metric clock');
122
+ // A fixed, fully matured creation cohort: each PR has had its full 48h to land.
123
+ const cutoff = clock - 96 * 3_600_000;
124
+ const matured = clock - 48 * 3_600_000;
125
+ if (!adapters.listRecentClosed) throw new Error('48h closed cohort unavailable');
126
+ const [drafts, closed] = await Promise.all([
127
+ collectPages((page, size) => adapters.listDrafts(page, size, repository)),
128
+ adapters.listRecentClosed(repository, new Date(cutoff)),
129
+ ]);
130
+ const valid = (pr: SweepDraft | SweepMergedPr): boolean => Number.isInteger(pr.number) && pr.number > 0
131
+ && typeof pr.branch === 'string' && pr.branch.length > 0 && typeof pr.title === 'string'
132
+ && typeof pr.createdAt === 'string' && Number.isFinite(Date.parse(pr.createdAt));
133
+ if (drafts.some((pr) => !valid(pr) || !Array.isArray(pr.labels) || pr.labels.some((name) => typeof name !== 'string'))
134
+ || closed.some((pr) => !valid(pr)
135
+ || (pr.mergedAt !== undefined && !Number.isFinite(Date.parse(pr.mergedAt)))
136
+ || !Array.isArray(pr.labels) || pr.labels.some((name) => typeof name !== 'string')))
137
+ throw new Error('Incomplete draft metric inventory');
138
+ if (new Set(drafts.map((pr) => pr.number)).size !== drafts.length
139
+ || new Set(closed.map((pr) => pr.number)).size !== closed.length) throw new Error('Duplicate draft metric PR');
140
+ const open = drafts.filter(harnessDraft);
141
+ const owners = await Promise.all(open.map(async (draft) => {
142
+ if (!adapters.getActiveClaimOwner) throw new Error('Draft owner lookup unavailable');
143
+ const owner = await adapters.getActiveClaimOwner(repository, draft.number);
144
+ if (owner !== undefined && typeof owner !== 'string') throw new Error('Invalid draft claim owner');
145
+ return owner;
146
+ }));
147
+ const cohort = [...open.filter((pr) => Date.parse(pr.createdAt) >= cutoff && Date.parse(pr.createdAt) <= matured),
148
+ ...closed.filter((pr) => (pr.branch.startsWith('self-impl/') || pr.labels!.some((name) => name.startsWith('elanous:')))
149
+ && pr.createdAt && Date.parse(pr.createdAt) >= cutoff && Date.parse(pr.createdAt) <= matured)];
150
+ if (new Set(cohort.map((pr) => pr.number)).size !== cohort.length) throw new Error('Overlapping draft metric inventory');
151
+ const converted48h = cohort.filter((pr) => 'mergedAt' in pr && typeof pr.mergedAt === 'string'
152
+ && Date.parse(pr.mergedAt) >= Date.parse(pr.createdAt!) && Date.parse(pr.mergedAt) <= clock
153
+ && Date.parse(pr.mergedAt) - Date.parse(pr.createdAt!) <= 48 * 3_600_000).length;
154
+ return {
155
+ inventory: open.length,
156
+ oldestAgeHours: open.length ? Math.max(...open.map((pr) => Math.max(0, (clock - Date.parse(pr.createdAt)) / 3_600_000))) : null,
157
+ needsOwner: owners.filter((owner) => !owner?.trim()).length,
158
+ converted48h,
159
+ cohort48h: cohort.length,
160
+ conversion48h: cohort.length ? converted48h / cohort.length : null,
161
+ };
162
+ }
163
+
164
+ /** One `dev-pipeline` `ask-preflight` observation (written by ask-launch-flow before each launch). */
165
+ export interface OverlapPreflightRow {
166
+ /** ISO time as the log store returns it; anything unparseable makes the source incomplete. */
167
+ readonly ts: unknown;
168
+ /** Either the payload itself, or a Pod-forwarded wrapper `{ origin: 'pod', originalData: '<json>' }`. */
169
+ readonly data: Record<string, unknown> | null;
170
+ }
171
+
172
+ /**
173
+ * Overlap launches = ask runs launched in the last 24h (no blocker, or forced past them) whose preflight
174
+ * names a concrete overlap: an `open-pr` / `sibling-pr` warning naming `#N`, or a `live-run` warning naming a run.
175
+ * The second sibling is this launch's own PR (branch run suffix `-r<runId[0..6]>`), else the PR on the warned sibling's lineage,
176
+ * else (DRAFT-NOT-ARCHIVE `needs-owner-only`) a `salvaged` salvage branch of the same run or lineage with no PR yet —
177
+ * linked as «salvaged — not landed», never auto-landed; once that branch gets a PR, the PR's merge counts as usual.
178
+ * ⛔ `null` is «unmeasured», never «zero»:
179
+ * - source incomplete (rows unreadable, zero rows, or a row whose payload cannot be parsed) ⇒ every count null, `sourceIncomplete`;
180
+ * - a launch with no proof whose scan was blind (open-PR scan capped/unknown, live-run scan unknown, no planned paths) ⇒ `unmeasured`, not «no overlap»;
181
+ * - GitHub unreadable ⇒ landing fields null;
182
+ * - salvage source unreadable ⇒ launches with no PR sibling count as `salvageUnmeasured`, not as «no sibling».
183
+ */
184
+ export interface OverlapMetrics {
185
+ /** Launched runs whose preflight proves an overlap. */
186
+ readonly launches24h: number | null;
187
+ /** Launched runs in the window (one per runId). */
188
+ readonly launched: number | null;
189
+ /** Launched runs with no proven overlap whose overlap scan was blind. */
190
+ readonly unmeasured: number | null;
191
+ readonly sourceIncomplete: boolean;
192
+ /** Overlap launches whose second sibling PR was found (open draft or closed). */
193
+ readonly linked: number | null;
194
+ /** Linked second siblings merged with no human-hand label (approval / from-human / keep). */
195
+ readonly autoLanded: number | null;
196
+ readonly autoRate: number | null;
197
+ /** Median hours from the overlap launch to the second sibling's merge, over landed second siblings. */
198
+ readonly secondSiblingMedianHours: number | null;
199
+ /** Linked second siblings that live only on a salvage branch (no PR): «salvaged — not landed». Included in `linked`. */
200
+ readonly salvaged: number | null;
201
+ /** Linkable launches with no PR sibling whose salvage source could not be read — `linked` is a lower bound. */
202
+ readonly salvageUnmeasured: number | null;
203
+ }
204
+
205
+ export type OverlapMetricAdapters = Pick<DraftSweepAdapters, 'listDrafts' | 'listRecentClosed' | 'getPr'> & {
206
+ /** Every ask-preflight row since `since`, all universes; null means the store could not be read. */
207
+ listPreflightRows(since: Date): Promise<readonly OverlapPreflightRow[] | null>;
208
+ /** Every `self-implement.draft-not-archive` `salvaged` row since `since`; null (or absent) means unreadable. */
209
+ listSalvagedRows?(since: Date): Promise<readonly OverlapPreflightRow[] | null>;
210
+ };
211
+
212
+ const HUMAN_HAND_LABELS: ReadonlySet<string> = new Set(PR_LABELS
213
+ .filter((entry) => entry.sweep.action === 'exclude' || (entry.axis === 'state' && entry.sweep.action === 'none')
214
+ || (entry.axis === 'origin' && (entry.appliedBy as readonly string[]).includes('human')))
215
+ .map((entry) => entry.name));
216
+ const isRecord = (value: unknown): value is Record<string, unknown> =>
217
+ typeof value === 'object' && value !== null && !Array.isArray(value);
218
+
219
+ /** Unwraps a Pod-forwarded row (`originalData` as a JSON string or object); null when the payload is unreadable. */
220
+ export function overlapPreflightPayload(data: unknown): Record<string, unknown> | null {
221
+ if (!isRecord(data)) return null;
222
+ let payload: unknown = data;
223
+ if ('originalData' in data) {
224
+ payload = data.originalData;
225
+ if (typeof payload === 'string') {
226
+ try { payload = JSON.parse(payload); } catch { return null; }
227
+ }
228
+ }
229
+ if (!isRecord(payload) || !Array.isArray(payload.warnings) || !Array.isArray(payload.blockers)) return null;
230
+ return payload;
231
+ }
232
+
233
+ /** Unwraps a DRAFT-NOT-ARCHIVE `salvaged` row (Pod-forwarded or local); null when it names no salvage branch. */
234
+ export function salvagedPayload(data: unknown): Record<string, unknown> | null {
235
+ if (!isRecord(data)) return null;
236
+ let payload: unknown = data;
237
+ if ('originalData' in data) {
238
+ payload = data.originalData;
239
+ if (typeof payload === 'string') {
240
+ try { payload = JSON.parse(payload); } catch { return null; }
241
+ }
242
+ }
243
+ if (!isRecord(payload) || typeof payload.salvageBranch !== 'string' || !payload.salvageBranch.startsWith('salvage/')) return null;
244
+ return payload;
245
+ }
246
+
247
+ type SalvagedSibling = { at: number; runKey: string | null; slug: string | null; salvageBranch: string };
248
+ const runKeyOf = (runId: unknown): string | null => {
249
+ const key = typeof runId === 'string' ? runId.replace(/^run-/i, '').replace(/[^a-z0-9]/gi, '').slice(0, 6).toLowerCase() : '';
250
+ return key.length === 6 ? key : null;
251
+ };
252
+
253
+ /** Salvaged siblings in [since, clock]; null when the source is unreadable or any row cannot be parsed. */
254
+ function salvagedSiblings(rows: readonly OverlapPreflightRow[] | null | undefined, since: number, clock: number): SalvagedSibling[] | null {
255
+ if (!Array.isArray(rows)) return null;
256
+ const out: SalvagedSibling[] = [];
257
+ for (const row of rows) {
258
+ const at = Date.parse(String(row?.ts));
259
+ const payload = salvagedPayload(row?.data);
260
+ if (!Number.isFinite(at) || payload === null) return null;
261
+ if (at < since || at > clock) continue;
262
+ const salvageBranch = payload.salvageBranch as string;
263
+ const runKey = runKeyOf(payload.runId) ?? (/^salvage\/run-([0-9a-f]{6})\//i.exec(salvageBranch)?.[1]?.toLowerCase() ?? null);
264
+ out.push({ at, runKey, slug: typeof payload.branch === 'string' ? branchLineageSlug(payload.branch) : null, salvageBranch });
265
+ }
266
+ return out;
267
+ }
268
+
269
+ /**
270
+ * «Salvage branches today N» for the hourly report: distinct salvage branches whose `salvaged` event falls in [dayStart, now].
271
+ * ⛔ Unreadable source or an unparsable row ⇒ null, never 0.
272
+ */
273
+ export function countSalvagedToday(rows: readonly OverlapPreflightRow[] | null | undefined, dayStart: Date, now: Date): number | null {
274
+ const siblings = salvagedSiblings(rows, dayStart.getTime(), now.getTime());
275
+ return siblings === null ? null : new Set(siblings.map((sibling) => sibling.salvageBranch)).size;
276
+ }
277
+
278
+ type OverlapLaunch = { at: number; runKey: string | null; prs: number[]; siblings: number[]; proven: boolean; blind: boolean };
279
+
280
+ /** Reads one launched preflight: proven overlap, blind (unmeasured), or measured clean. */
281
+ function overlapLaunch(payload: Record<string, unknown>, at: number): OverlapLaunch | null {
282
+ const blockers = payload.blockers as unknown[];
283
+ const forced = payload.bypassed === true;
284
+ // Only a launch counts: a clean preflight, or one the caller forced past its blockers.
285
+ if (blockers.length > 0 && !forced) return null;
286
+ const entries = [...(payload.warnings as unknown[]), ...(forced ? blockers : [])]
287
+ .filter(isRecord).map((entry) => ({ kind: entry.kind, name: typeof entry.name === 'string' ? entry.name : '' }));
288
+ const numbers = (kind: string) => entries.filter((entry) => entry.kind === kind)
289
+ .flatMap((entry) => [...entry.name.matchAll(/#(\d+)/g)].map((match) => Number(match[1])));
290
+ const siblings = numbers('sibling-pr');
291
+ const prs = [...new Set([...numbers('open-pr'), ...siblings])];
292
+ const liveRuns = entries.filter((entry) => entry.kind === 'live-run' && entry.name.trim() !== '').length;
293
+ const axisChecked = (axis: unknown) => isRecord(axis) && axis.state === 'checked';
294
+ // ⛔ A capped open-PR scan («열린 PR 조회 상한 N») saw only part of the PRs — its «no hit» is not a measurement.
295
+ const capped = entries.some((entry) => entry.kind === 'open-pr' && !/#\d+/.test(entry.name));
296
+ const blind = capped || !axisChecked(payload.openPrs) || !axisChecked(payload.liveRuns)
297
+ || !Array.isArray(payload.paths) || payload.paths.length === 0;
298
+ return { at, runKey: runKeyOf(payload.runId), prs, siblings, proven: prs.length > 0 || liveRuns > 0, blind };
299
+ }
300
+
301
+ export async function collectOverlapMetrics(repository: string, adapters: OverlapMetricAdapters, now: Date): Promise<OverlapMetrics> {
302
+ const clock = now.getTime();
303
+ if (!Number.isFinite(clock)) throw new Error('Invalid overlap metric clock');
304
+ const since = clock - 24 * 3_600_000;
305
+ const landingUnmeasured = { linked: null, autoLanded: null, autoRate: null, secondSiblingMedianHours: null, salvaged: null, salvageUnmeasured: null };
306
+ const incomplete = (reason: string, extra: Record<string, unknown> = {}): OverlapMetrics => {
307
+ try { debug.log('drafts.metrics', 'overlap-source-incomplete', { reason, ...extra }); } catch { /* fail-soft */ }
308
+ return { launches24h: null, launched: null, unmeasured: null, sourceIncomplete: true, ...landingUnmeasured };
309
+ };
310
+ const rows = await adapters.listPreflightRows(new Date(since));
311
+ if (rows === null || !Array.isArray(rows)) return incomplete('unreadable');
312
+ if (rows.length === 0) return incomplete('zero-events');
313
+ // One launch per run: a run re-authored after a block logs several preflights (and one store can be reached twice).
314
+ const runs = new Map<string, OverlapLaunch>();
315
+ let unparsable = 0;
316
+ for (const row of rows) {
317
+ const at = Date.parse(String(row?.ts));
318
+ if (!Number.isFinite(at)) { unparsable++; continue; }
319
+ if (at < since || at > clock) continue;
320
+ const payload = overlapPreflightPayload(row?.data);
321
+ if (payload === null) { unparsable++; continue; }
322
+ const launch = overlapLaunch(payload, at);
323
+ if (!launch) continue;
324
+ const key = launch.runKey ?? `${at}:${String(payload.goalFile ?? '')}`;
325
+ const prior = runs.get(key);
326
+ runs.set(key, prior ? {
327
+ at: Math.min(prior.at, launch.at), runKey: launch.runKey,
328
+ prs: [...new Set([...prior.prs, ...launch.prs])], siblings: [...new Set([...prior.siblings, ...launch.siblings])],
329
+ proven: prior.proven || launch.proven, blind: prior.blind || launch.blind,
330
+ } : launch);
331
+ }
332
+ if (unparsable > 0) return incomplete('unparsable-rows', { unparsable, rows: rows.length });
333
+ const all = [...runs.values()];
334
+ const launches = all.filter((launch) => launch.proven);
335
+ const counts = { launches24h: launches.length, launched: all.length, unmeasured: all.filter((launch) => !launch.proven && launch.blind).length, sourceIncomplete: false };
336
+ const linkable = launches.filter((launch) => launch.runKey !== null || launch.siblings.length > 0);
337
+ // No overlap launch is a measured empty sample; overlaps that name neither a run nor a sibling leave landing unmeasured.
338
+ if (launches.length === 0) return { ...counts, linked: 0, autoLanded: 0, autoRate: null, secondSiblingMedianHours: null, salvaged: 0, salvageUnmeasured: 0 };
339
+ if (linkable.length === 0) return { ...counts, ...landingUnmeasured };
340
+ try {
341
+ if (!adapters.getPr || !adapters.listRecentClosed) throw new Error('Overlap PR lookup unavailable');
342
+ const [drafts, closed] = await Promise.all([
343
+ collectPages((page, size) => adapters.listDrafts(page, size, repository)),
344
+ adapters.listRecentClosed(repository, new Date(since)),
345
+ ]);
346
+ const candidates = [...drafts, ...closed].filter((pr) => Number.isInteger(pr.number) && typeof pr.branch === 'string'
347
+ && typeof pr.createdAt === 'string' && Number.isFinite(Date.parse(pr.createdAt)));
348
+ const firsts = new Map<number, SweepMergedPr | undefined>();
349
+ let linked = 0, autoLanded = 0, salvaged = 0, salvageUnmeasured = 0;
350
+ const hours: number[] = [];
351
+ // Read lazily: only a launch with no PR sibling needs the salvage source. ⛔ A read failure is «unmeasured», not «none».
352
+ let salvage: SalvagedSibling[] | null | undefined;
353
+ const salvageSource = async (): Promise<SalvagedSibling[] | null> => {
354
+ if (salvage !== undefined) return salvage;
355
+ let rows: readonly OverlapPreflightRow[] | null = null;
356
+ try { rows = adapters.listSalvagedRows ? await adapters.listSalvagedRows(new Date(since)) : null; } catch { rows = null; }
357
+ salvage = salvagedSiblings(rows, since, clock);
358
+ if (salvage === null) try { debug.log('drafts.metrics', 'overlap-salvage-unmeasured', { reason: rows === null ? 'unreadable' : 'unparsable-rows' }); } catch { /* fail-soft */ }
359
+ return salvage;
360
+ };
361
+ for (const launch of linkable) {
362
+ const after = candidates.filter((pr) => !launch.prs.includes(pr.number) && Date.parse(pr.createdAt!) >= launch.at)
363
+ .sort((a, b) => Date.parse(a.createdAt!) - Date.parse(b.createdAt!));
364
+ let second = launch.runKey === null ? undefined
365
+ : after.find((pr) => pr.branch.endsWith(`-r${launch.runKey}`) || pr.branch.endsWith(`-r${launch.runKey}-early`));
366
+ let slug: string | null = null;
367
+ if (!second && launch.siblings.length > 0) {
368
+ const number = launch.siblings[0]!;
369
+ if (!firsts.has(number)) firsts.set(number, await adapters.getPr(repository, number));
370
+ const first = firsts.get(number);
371
+ if (!first) throw new Error('Sibling PR unreadable');
372
+ slug = branchLineageSlug(first.branch);
373
+ if (slug !== null) second = after.find((pr) => branchLineageSlug(pr.branch) === slug);
374
+ }
375
+ if (!second) {
376
+ const source = await salvageSource();
377
+ if (source === null) { salvageUnmeasured++; continue; }
378
+ const branch = source.filter((sibling) => sibling.at >= launch.at
379
+ && ((launch.runKey !== null && sibling.runKey === launch.runKey) || (slug !== null && sibling.slug === slug)))
380
+ .sort((a, b) => a.at - b.at)[0]?.salvageBranch;
381
+ if (branch === undefined) continue;
382
+ // A salvage branch later landed through its own PR counts through that PR's merge.
383
+ second = after.find((pr) => pr.branch === branch);
384
+ if (!second) { linked++; salvaged++; continue; }
385
+ }
386
+ linked++;
387
+ const mergedAt = 'mergedAt' in second && typeof second.mergedAt === 'string' ? Date.parse(second.mergedAt) : NaN;
388
+ if (!Number.isFinite(mergedAt) || mergedAt < launch.at || mergedAt > clock) continue;
389
+ hours.push((mergedAt - launch.at) / 3_600_000);
390
+ if (!(second.labels ?? []).some((name) => HUMAN_HAND_LABELS.has(name))) autoLanded++;
391
+ }
392
+ hours.sort((a, b) => a - b);
393
+ const mid = Math.floor(hours.length / 2);
394
+ return {
395
+ ...counts,
396
+ linked,
397
+ autoLanded,
398
+ autoRate: linked ? autoLanded / linked : null,
399
+ secondSiblingMedianHours: hours.length ? (hours.length % 2 ? hours[mid]! : (hours[mid - 1]! + hours[mid]!) / 2) : null,
400
+ // ⛔ A salvage source we could not read leaves the salvaged count unknown, not zero.
401
+ salvaged: salvage === null ? null : salvaged,
402
+ salvageUnmeasured,
403
+ };
404
+ } catch (error) {
405
+ try { debug.log('drafts.metrics', 'overlap-landing-unmeasured', { error: error instanceof Error ? error.message : String(error) }); } catch { /* fail-soft */ }
406
+ return { ...counts, ...landingUnmeasured };
407
+ }
408
+ }
409
+
99
410
  const stateLabels = PR_LABELS.filter((label) => label.axis === 'state');
100
411
  const label = (action: 'mark-stalled' | 'close', name?: string): string => {
101
412
  const match = PR_LABELS.find((entry) => entry.axis === 'state' && entry.sweep.action === action && (name === undefined || entry.name.endsWith(name)));
@@ -204,6 +515,31 @@ export function sweepFailureReason(error: unknown): string {
204
515
  return line && line.length > 0 ? line : 'sweep failed';
205
516
  }
206
517
 
518
+ /** Harness draft created at least STALLED_DRAFT_HOURS before `now` — the daily census population. */
519
+ function dailyCensusDraft(draft: SweepDraft, now: Date): boolean {
520
+ if (!harnessDraft(draft)) return false;
521
+ const created = Date.parse(draft.createdAt);
522
+ return Number.isFinite(created) && created <= now.getTime() - STALLED_DRAFT_HOURS * 3_600_000;
523
+ }
524
+
525
+ /** In-flight per-PR lookups during one sweep (GitHub calls and ledger reads). */
526
+ export const DRAFT_SWEEP_CONCURRENCY = 6;
527
+
528
+ /** Runs `work` over `items` with at most `limit` in flight; the first rejection rejects the whole map. */
529
+ export async function mapBounded<T, R>(items: readonly T[], limit: number, work: (item: T) => Promise<R>): Promise<R[]> {
530
+ const results = new Array<R>(items.length);
531
+ let next = 0;
532
+ let failed = false;
533
+ const workers = Array.from({ length: Math.max(1, Math.min(limit, items.length)) }, async () => {
534
+ while (!failed && next < items.length) {
535
+ const index = next++;
536
+ try { results[index] = await work(items[index]!); } catch (error) { failed = true; throw error; }
537
+ }
538
+ });
539
+ await Promise.all(workers);
540
+ return results;
541
+ }
542
+
207
543
  function harnessDraft(draft: SweepDraft): boolean {
208
544
  return draft.branch.startsWith('self-impl/') || draft.labels.some((name) => name.startsWith('elanous:'));
209
545
  }
@@ -219,15 +555,12 @@ export function draftSweepDaily(
219
555
  now: Date,
220
556
  reviewGate: ReadonlyMap<number, SweepReviewGate | undefined>,
221
557
  ): DraftSweepDaily {
222
- const cutoff = now.getTime() - STALLED_DRAFT_HOURS * 3_600_000;
223
558
  let over24h = 0;
224
559
  let closable = 0;
225
560
  let harvestable = 0;
226
561
  let blocked = 0;
227
562
  for (const draft of drafts) {
228
- if (!harnessDraft(draft)) continue;
229
- const created = Date.parse(draft.createdAt);
230
- if (!Number.isFinite(created) || created > cutoff) continue;
563
+ if (!dailyCensusDraft(draft, now)) continue;
231
564
  over24h += 1;
232
565
  if (merged.some((pr) => pr.number !== draft.number && sameGoalPr(draft, pr))) closable += 1;
233
566
  const posture = reviewGate.get(draft.number);
@@ -251,6 +584,17 @@ export async function runDraftSweep({ repository, adapters, apply = false, now =
251
584
  if (!result.complete && result.error) debug.log('drafts.cleanup', 'failed', { reason: sweepFailureReason(result.error) });
252
585
  return result;
253
586
  };
587
+ const started = Date.now();
588
+ let phaseStarted = started;
589
+ // A failed phase still reports its elapsed time (and the total) — a timeout cause must be visible either way.
590
+ const timing = (phase: string, count: number, outcome?: 'failed'): void => {
591
+ const at = Date.now();
592
+ try {
593
+ debug.log('self-dev.draft-sweep', 'timing', { phase, ms: at - phaseStarted, count, ...(outcome ? { outcome } : {}) });
594
+ if (outcome) debug.log('self-dev.draft-sweep', 'timing', { phase: 'total', ms: at - started, count, outcome });
595
+ } catch { /* fail-soft */ }
596
+ phaseStarted = at;
597
+ };
254
598
  let drafts: SweepDraft[] = [];
255
599
  let merged: SweepMergedPr[] = [];
256
600
  let liveBranches: ReadonlySet<string> | undefined;
@@ -264,40 +608,49 @@ export async function runDraftSweep({ repository, adapters, apply = false, now =
264
608
  if (drafts.some((pr) => !Number.isInteger(pr.number) || !pr.branch || !Array.isArray(pr.labels) || !Number.isFinite(Date.parse(pr.createdAt)))
265
609
  || merged.some((pr) => !Number.isInteger(pr.number) || !pr.branch)) throw new Error('Invalid PR inventory');
266
610
  } catch (error) {
611
+ timing('inventory', drafts.length + merged.length, 'failed');
267
612
  result.error = sweepFailureReason(error);
268
613
  if (drafts.length > 0) result.daily = draftSweepDaily(drafts, merged, now, new Map());
269
614
  return finish();
270
615
  }
271
- let statuses: Map<number, string | undefined>;
272
- let finality: Map<number, boolean | undefined>;
273
- let latestFileChanges: Map<number, Readonly<Record<string, string>> | undefined>;
616
+ timing('inventory', drafts.length + merged.length);
617
+ const statuses = new Map<number, string | undefined>();
618
+ const finality = new Map<number, boolean | undefined>();
619
+ const latestFileChanges = new Map<number, Readonly<Record<string, string>> | undefined>();
274
620
  try {
275
- statuses = new Map<number, string | undefined>();
276
- finality = new Map<number, boolean | undefined>();
277
- latestFileChanges = new Map<number, Readonly<Record<string, string>> | undefined>();
278
- for (const draft of drafts) {
621
+ // Per-draft lookups are independent: bounded concurrency, results keyed by PR number (decisions stay in list order).
622
+ await mapBounded(drafts, DRAFT_SWEEP_CONCURRENCY, async (draft) => {
279
623
  const status = await adapters.getRunStatus(draft, repository);
280
624
  // A prior final cannot terminate a new active run on the same draft.
281
625
  const active = status === 'running' || status === 'probable-running';
282
626
  const final = active ? false : await adapters.hasFinalRunResult?.(draft, repository);
283
627
  finality.set(draft.number, final);
284
628
  statuses.set(draft.number, final ? 'self-implement.result final' : status);
285
- if (draft.changedFiles?.length && adapters.getLatestFileChanges) {
629
+ // Per-file history only matters to all-files-landed, which needs a later same-lineage merge.
630
+ if (draft.changedFiles?.length && adapters.getLatestFileChanges && laterMergedTwins(draft, merged).length > 0) {
286
631
  latestFileChanges.set(draft.number, await adapters.getLatestFileChanges(draft, repository));
287
632
  }
288
- }
633
+ });
289
634
  } catch (error) {
635
+ timing('run-status', drafts.length, 'failed');
290
636
  result.error = sweepFailureReason(error);
291
637
  return finish();
292
638
  }
293
- let reviewGate = new Map<number, SweepReviewGate | undefined>();
639
+ timing('run-status', drafts.length);
640
+ const reviewGate = new Map<number, SweepReviewGate | undefined>();
294
641
  if (adapters.getReviewGate) {
642
+ // Review/gate posture only feeds the daily census, which only counts harness drafts older than 24h.
643
+ const censused = drafts.filter((draft) => dailyCensusDraft(draft, now));
295
644
  try {
296
- for (const draft of drafts) reviewGate.set(draft.number, await adapters.getReviewGate(draft, repository));
645
+ await mapBounded(censused, DRAFT_SWEEP_CONCURRENCY, async (draft) => {
646
+ reviewGate.set(draft.number, await adapters.getReviewGate!(draft, repository));
647
+ });
297
648
  } catch (error) {
649
+ timing('review-gate', censused.length, 'failed');
298
650
  result.error = sweepFailureReason(error);
299
651
  return finish();
300
652
  }
653
+ timing('review-gate', censused.length);
301
654
  }
302
655
  result.daily = draftSweepDaily(drafts, merged, now, reviewGate);
303
656
  result.complete = true;
@@ -418,6 +771,8 @@ export async function runDraftSweep({ repository, adapters, apply = false, now =
418
771
  entry.error = String(error);
419
772
  }
420
773
  }
774
+ timing('decide', drafts.length);
775
+ try { debug.log('self-dev.draft-sweep', 'timing', { phase: 'total', ms: Date.now() - started, count: drafts.length }); } catch { /* fail-soft */ }
421
776
  result.unobserved = unobserved;
422
777
  result.closed = closes;
423
778
  result.claimed = claimed;
@@ -58,6 +58,24 @@ const referencesDraft = (text: string, number: number): boolean =>
58
58
  const landingCommentReferencesDraft = (text: string, number: number): boolean =>
59
59
  new RegExp(`\\blanding-verified\\s*:\\s*(?:draft|superseded|수확)\\s*#${number}(?!\\d)\\b`, 'i').test(text);
60
60
 
61
+ /**
62
+ * Merged PRs that can cover a draft's files (all-files-landed): merged after the draft was created, on the same
63
+ * goal lineage, slot or branch stem. Empty ⇒ all-files-landed cannot fire, so the draft's per-file history is not needed.
64
+ */
65
+ export function laterMergedTwins(draft: DraftTriagePr, mergedTwins: readonly DraftTriagePr[]): DraftTriagePr[] {
66
+ const lineage = goalId(draft.branch);
67
+ const draftSlot = slot(draft.body);
68
+ const stem = branchStem(draft.branch);
69
+ const created = Date.parse(draft.createdAt ?? '');
70
+ const compatible = (pr: DraftTriagePr): boolean =>
71
+ !(lineage && goalId(pr.branch) && goalId(pr.branch) !== lineage)
72
+ && !(draftSlot && slot(pr.body) && slot(pr.body) !== draftSlot);
73
+ return mergedTwins.filter((pr) => pr.number !== draft.number && Number.isFinite(created)
74
+ && Number.isFinite(Date.parse(pr.mergedAt ?? '')) && Date.parse(pr.mergedAt!) > created
75
+ && compatible(pr) && ((lineage && goalId(pr.branch) === lineage)
76
+ || (draftSlot && slot(pr.body) === draftSlot) || (stem && branchStem(pr.branch) === stem)));
77
+ }
78
+
61
79
  export function decideDraft({ draft, runStatus, mergedTwins, openDrafts, ageHours, liveBranches, finalRunResult }: DraftTriageInput): DraftDecision {
62
80
  const held = draft.labels?.find((label) => PROTECTED_LABELS.has(label));
63
81
  if (held) return { action: 'keep', reason: `label:${held}` };
@@ -90,10 +108,7 @@ export function decideDraft({ draft, runStatus, mergedTwins, openDrafts, ageHour
90
108
  if (harvest) return { action: 'close', reason: `superseded-by #${harvest.number} (harvest #${draft.number})` };
91
109
  const created = Date.parse(draft.createdAt ?? '');
92
110
  const files = draft.changedFiles;
93
- const later = mergedTwins.filter((pr) => pr.number !== draft.number && Number.isFinite(created)
94
- && Number.isFinite(Date.parse(pr.mergedAt ?? '')) && Date.parse(pr.mergedAt!) > created
95
- && compatible(pr) && ((lineage && goalId(pr.branch) === lineage)
96
- || (draftSlot && slot(pr.body) === draftSlot) || (stem && branchStem(pr.branch) === stem)));
111
+ const later = laterMergedTwins(draft, mergedTwins);
97
112
  if (files?.length && files.every((file) => {
98
113
  const changed = Date.parse(draft.latestFileChanges?.[file] ?? '');
99
114
  return Number.isFinite(changed) && changed >= created && later.some((pr) =>