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,9 +1,11 @@
1
1
  import { spawn, spawnSync } from 'node:child_process';
2
2
  import { createHash, randomUUID } from 'node:crypto';
3
- import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readlinkSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
3
+ import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readdirSync, readFileSync, readlinkSync, renameSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
4
4
  import { join, resolve } from 'node:path';
5
5
  import { Database } from 'bun:sqlite';
6
6
  import { debug } from '../debug/log.js';
7
+ import { findTreeRoot } from '../cli/test-flag.js';
8
+ import { readLogInstances, type LogInstanceView } from '../mss/logging/instance-registry.js';
7
9
  import { effectiveInstanceRoot, prodInstanceRoot, releaseLedgerRoot } from '../instance/resolve.js';
8
10
  import { listChecklist, type ChecklistItem } from '../release-loop/checklist.js';
9
11
  import { readLandingFreeze } from '../release-loop/landing-freeze.js';
@@ -29,11 +31,17 @@ export type QueueItem = {
29
31
  priority?: number;
30
32
  };
31
33
  export type QueuePool = { running: number; pending: number; reserved: number; limit: number };
32
- export type QueueTick = { outcome: 'launched' | 'waiting' | 'skipped'; item?: QueueItem; reason: string };
34
+ export type QueueTick = { outcome: 'launched' | 'waiting' | 'skipped'; item?: QueueItem; reason: string;
35
+ /** QUEUE-BURST: every row this tick launched, first one = `item` (present only on a scheduled tick that launched). */ launched?: QueueItem[] };
33
36
  /** progressAt = newest run-ledger updatedAt among the tree's members; stopReason = the ledger's supervisor stop (SEAT-CAP-STALE).
34
37
  * ledgerRunIds = run ids of the members' ledger records — the `elanous.run` label of their Pod Jobs (SEAT-CAP-STALE2). */
35
38
  export type QueueProcess = { pid: number; seat?: QueueSeat; launchId?: string; runId?: string; rootPid?: number;
36
- progressAt?: number; stopReason?: string; ledgerRunIds?: string[] };
39
+ progressAt?: number; stopReason?: string; ledgerRunIds?: string[];
40
+ /** CHILD-UNIV-COUNT: present only when a member's ledger record lives in a derived child universe (not the parent's). */
41
+ universe?: 'child' };
42
+ /** CHILD-UNIV-COUNT: the host inventory plus which derived child universes it read. `unreadable` roots are «못 읽음» —
43
+ * their runs may hold a seat but cannot be attributed, so the count is not trusted as complete. */
44
+ export type QueueInventory = { processes: readonly QueueProcess[]; childUniverses: { roots: readonly string[]; unreadable: readonly string[] } };
37
45
  type ProcessProbe = {
38
46
  run?: typeof spawnSync; platform?: NodeJS.Platform;
39
47
  cwd?: (pid: number) => string;
@@ -41,6 +49,11 @@ type ProcessProbe = {
41
49
  runsDir?: string;
42
50
  environ?: (pid: number) => string;
43
51
  birthId?: (pid: number) => string | undefined;
52
+ /** CHILD-UNIV-COUNT: derived child universe roots whose self-dev ledgers are read with the parent's. Default:
53
+ * `harnessQueueChildUniverseRoots` (never in a test process). */
54
+ childUniverseRoots?: () => readonly string[];
55
+ /** Extra launch trees (queue rows' launchCwd) whose derived universes the default enumeration includes. */
56
+ launchTrees?: readonly string[];
44
57
  };
45
58
  const queueSeatNames = ['OP', 'TC', 'MK', 'UX'] as const;
46
59
 
@@ -65,8 +78,23 @@ export interface HarnessQueueDeps {
65
78
  afterEnqueue?: (item: QueueItem) => void | Promise<void>;
66
79
  /** Script the default launcher runs under the queue child wrapper (default bin/elanous.mjs). */
67
80
  launchCommand?: string;
81
+ /** QUEUE-BURST: most launches one scheduled tick makes. Absent = `harness.queue.burstMax`, else 5. A requested (direct) tick always launches at most one. */
82
+ burstMax?: number;
83
+ /** QUEUE-BURST: least gap between two queue launches, across ticks and processes (default 30 s — simultaneous Pod launches died in a chain). */
84
+ launchStaggerMs?: number;
85
+ /** Test seam: the wait between burst launches (default Bun.sleep). */
86
+ sleep?: (ms: number) => Promise<void>;
68
87
  alive?: (pid: number) => boolean;
69
88
  processes?: () => readonly QueueProcess[];
89
+ /** CHILD-UNIV-COUNT: inventory with child-universe coverage; wins over `processes`. A `processes`-only seam reads no
90
+ * child universe, so its limit line stays parent-only. */
91
+ inventory?: (launchTrees: readonly string[]) => QueueInventory;
92
+ /** Production freeze root a tick also honors (a work-tree launch ticks in its test universe). Default: prodInstanceRoot(),
93
+ * except in a test process where it is the tick's own root. */
94
+ prodFreezeRoot?: string;
95
+ /** CHILD-UNIV-HOLD: ops-health alert for a universe unreadable over an hour (default: sendOutbound 'ops-health'; none in
96
+ * a test process). */
97
+ unreadableAlert?: (text: string) => void | Promise<void>;
70
98
  receipt?: (root: string, launchId: string) => 'started' | 'finished' | 'not-started' | null;
71
99
  now?: () => Date;
72
100
  authorShadow?: (root: string, now: Date) => void | Promise<void>;
@@ -199,11 +227,20 @@ function lastLaunchedSeat(path: string): QueueSeat | undefined {
199
227
  return (value as { lastSeat: QueueSeat }).lastSeat;
200
228
  }
201
229
 
202
- function saveLastLaunchedSeat(path: string, lastSeat: QueueSeat): void {
230
+ /** QUEUE-BURST: when the queue last launched (epoch ms), from the round-robin marker. Missing or unreadable = unknown. */
231
+ function lastQueueLaunchAt(path: string): number | undefined {
232
+ try {
233
+ const value: unknown = JSON.parse(readFileSync(`${path}.round-robin.json`, 'utf8'));
234
+ const at = value && typeof value === 'object' ? (value as { lastLaunchAt?: unknown }).lastLaunchAt : undefined;
235
+ return typeof at === 'number' && Number.isFinite(at) ? at : undefined;
236
+ } catch { return undefined; }
237
+ }
238
+
239
+ function saveLastLaunchedSeat(path: string, lastSeat: QueueSeat, lastLaunchAt?: number): void {
203
240
  const marker = `${path}.round-robin.json`;
204
241
  const temporary = `${marker}.${randomUUID()}.tmp`;
205
242
  try {
206
- writeFileSync(temporary, JSON.stringify({ lastSeat }), { flag: 'wx', mode: 0o600 });
243
+ writeFileSync(temporary, JSON.stringify({ lastSeat, ...(lastLaunchAt === undefined ? {} : { lastLaunchAt }) }), { flag: 'wx', mode: 0o600 });
207
244
  renameSync(temporary, marker);
208
245
  } catch (error) {
209
246
  try { unlinkSync(temporary); } catch { /* write did not finish */ }
@@ -537,8 +574,60 @@ function alive(pid: number): boolean {
537
574
  try { process.kill(pid, 0); return true; } catch { return false; }
538
575
  }
539
576
 
577
+ /** CHILD-UNIV-COUNT: derived child universes a tick reads with its own — never a glob of home directories. Sources: the
578
+ * instance registry's live local test universes (`~/.elanous/logs/instances.json`), the derived universe
579
+ * (`<tree>/.elanous-test`) of every seat tree, of every queue row's launch tree and of this process's own tree. A derived
580
+ * root that does not exist was never a child universe and is skipped; the parent root itself is never a child. */
581
+ export function harnessQueueChildUniverseRoots(input: {
582
+ parentRoot: string;
583
+ seatTrees?: Partial<Record<QueueSeat, string[]>>;
584
+ launchTrees?: readonly string[];
585
+ registry?: () => readonly Pick<LogInstanceView, 'stateDir' | 'kind' | 'liveness'>[];
586
+ exists?: (path: string) => boolean;
587
+ ownTree?: string | null;
588
+ }): string[] {
589
+ const exists = input.exists ?? existsSync;
590
+ const parent = resolve(input.parentRoot);
591
+ const roots = new Set<string>();
592
+ const add = (root: string | null | undefined): void => {
593
+ if (!root || !root.startsWith('/')) return;
594
+ const resolved = resolve(root);
595
+ if (resolved !== parent && exists(resolved)) roots.add(resolved);
596
+ };
597
+ const derived = (tree: string): string | null => {
598
+ if (!tree.startsWith('/')) return null;
599
+ const top = findTreeRoot(tree);
600
+ return top ? join(top, '.elanous-test') : null;
601
+ };
602
+ try {
603
+ for (const entry of (input.registry ?? readLogInstances)()) {
604
+ if (entry.kind === 'test' && entry.liveness === 'alive') add(entry.stateDir);
605
+ }
606
+ } catch (error) {
607
+ try { debug.log('harness.queue', 'child-universe-registry-unavailable', { error: String(error).slice(0, 240) }, { level: 'warn' }); }
608
+ catch { /* Observation cannot affect dispatch. */ }
609
+ }
610
+ for (const trees of Object.values(input.seatTrees ?? {})) for (const tree of trees ?? []) add(derived(tree));
611
+ for (const tree of input.launchTrees ?? []) add(derived(tree));
612
+ add(input.ownTree === undefined ? derived(process.cwd()) : input.ownTree);
613
+ return [...roots].sort();
614
+ }
615
+
616
+ /** A child universe's self-dev ledgers; a store that exists but cannot be listed is «unreadable», never «none». */
617
+ function readChildUniverseRuns(root: string): ReturnType<typeof listSelfDevRuns> | 'unreadable' {
618
+ const dir = selfDevRunsDir(root);
619
+ try { statSync(dir); readdirSync(dir); }
620
+ catch (error) { return (error as NodeJS.ErrnoException).code === 'ENOENT' ? [] : 'unreadable'; }
621
+ return listSelfDevRuns(dir);
622
+ }
623
+
540
624
  /** Host inventory includes harness authoring before it has a run ledger. Unattributed processes count for every seat. */
541
625
  export function readHarnessQueueProcesses(probe: ProcessProbe = {}): readonly QueueProcess[] {
626
+ return readHarnessQueueInventory(probe).processes;
627
+ }
628
+
629
+ /** CHILD-UNIV-COUNT: the host inventory with the parent's ledgers ⊕ the derived child universes' ledgers. */
630
+ export function readHarnessQueueInventory(probe: ProcessProbe = {}): QueueInventory {
542
631
  const run = probe.run ?? spawnSync;
543
632
  const platform = probe.platform ?? process.platform;
544
633
  const result = run('ps', ['-eo', 'pid=,ppid=,args='], { encoding: 'utf8', timeout: 5_000, maxBuffer: 16 * 1024 * 1024 });
@@ -572,7 +661,23 @@ export function readHarnessQueueProcesses(probe: ProcessProbe = {}): readonly Qu
572
661
  const births = new Map<number, string | undefined>();
573
662
  const ledgerProgress = new Map<number, { at: number; stopReason?: string }>();
574
663
  const ledgerRunIds = new Map<number, Set<string>>();
575
- for (const record of listSelfDevRuns(probe.runsDir ?? selfDevRunsDir())) {
664
+ const childPids = new Set<number>();
665
+ const testProcess = process.env.NODE_ENV === 'test' || Boolean(process.env.ELANOUS_TEST_HOME);
666
+ const childRoots = (probe.childUniverseRoots ?? (testProcess ? () => [] : () => harnessQueueChildUniverseRoots({
667
+ parentRoot: effectiveInstanceRoot(), seatTrees: trees, launchTrees: probe.launchTrees ?? [] })))();
668
+ const unreadable: string[] = [];
669
+ const sources: { records: ReturnType<typeof listSelfDevRuns>; child: boolean }[] = [
670
+ { records: listSelfDevRuns(probe.runsDir ?? selfDevRunsDir()), child: false }];
671
+ for (const root of childRoots) {
672
+ const records = readChildUniverseRuns(root);
673
+ if (records === 'unreadable') unreadable.push(root);
674
+ else sources.push({ records, child: true });
675
+ }
676
+ if (unreadable.length) {
677
+ try { debug.log('harness.queue', 'child-universe-unreadable', { roots: unreadable, read: childRoots.length - unreadable.length }, { level: 'warn' }); }
678
+ catch { /* Observation cannot affect dispatch. */ }
679
+ }
680
+ for (const { records, child } of sources) for (const record of records) {
576
681
  const pid = record.pid;
577
682
  if (!pid || !candidatePids.has(pid) || !record.pidStart) continue;
578
683
  if (!births.has(pid)) {
@@ -580,6 +685,7 @@ export function readHarnessQueueProcesses(probe: ProcessProbe = {}): readonly Qu
580
685
  catch { births.set(pid, undefined); }
581
686
  }
582
687
  if (births.get(pid) !== record.pidStart) continue;
688
+ if (child) childPids.add(pid);
583
689
  if (record.runId) ledgerRunIds.set(pid, (ledgerRunIds.get(pid) ?? new Set<string>()).add(record.runId));
584
690
  // Newest updatedAt is the process's latest progress; it is stopped only when every matched record stopped.
585
691
  if (Number.isFinite(record.updatedAt)) {
@@ -659,9 +765,10 @@ export function readHarnessQueueProcesses(probe: ProcessProbe = {}): readonly Qu
659
765
  catch { /* Observation does not block inventory. */ }
660
766
  rows.push({ pid: root.pid, ...(assigned ? { seat: assigned.seat } : {}), ...(launchId ? { launchId } : {}),
661
767
  ...(runId ? { runId } : {}), ...(progressAt !== undefined ? { progressAt } : {}), ...(stopReason ? { stopReason } : {}),
662
- ...(runIds.length ? { ledgerRunIds: runIds } : {}) });
768
+ ...(runIds.length ? { ledgerRunIds: runIds } : {}),
769
+ ...(members.some(({ process: { pid } }) => childPids.has(pid)) ? { universe: 'child' as const } : {}) });
663
770
  }
664
- return rows;
771
+ return { processes: rows, childUniverses: { roots: childRoots, unreadable } };
665
772
  }
666
773
 
667
774
  export function harnessQueueReceiptPath(root: string, launchId: string): string {
@@ -770,7 +877,9 @@ function warnLegacySeatCap(path: string): void {
770
877
 
771
878
  type QueueRun = { seat?: QueueSeat; launchIds: Set<string>; pids: Set<number>; runIds: Set<string>; progressAt?: number; stopReason?: string;
772
879
  /** Every run id of the group (env and ledger) — matched against Pod Job labels only, never used for grouping. */
773
- podRunIds: Set<string> };
880
+ podRunIds: Set<string>;
881
+ /** CHILD-UNIV-COUNT: some member's ledger lives in a derived child universe. */
882
+ universe?: 'child' };
774
883
 
775
884
  function groupQueueRuns(processes: readonly QueueProcess[]): QueueRun[] {
776
885
  const groups: { seats: Set<QueueSeat>; unknown: boolean; launchIds: Set<string>; keys: Set<string>; rows: QueueProcess[] }[] = [];
@@ -806,6 +915,7 @@ function groupQueueRuns(processes: readonly QueueProcess[]): QueueRun[] {
806
915
  runIds: new Set(group.rows.flatMap((row) => row.runId ? [row.runId] : [])),
807
916
  podRunIds: new Set(group.rows.flatMap((row) => [...(row.runId ? [row.runId] : []), ...(row.ledgerRunIds ?? [])])),
808
917
  ...(progressAt !== undefined ? { progressAt } : {}), ...(stopReason ? { stopReason } : {}),
918
+ ...(group.rows.some((row) => row.universe === 'child') ? { universe: 'child' as const } : {}),
809
919
  };
810
920
  });
811
921
  }
@@ -941,18 +1051,145 @@ export function requestIdleSeats(root: string, now: Date, items: readonly QueueI
941
1051
  }
942
1052
 
943
1053
  /** ORCH2 ③ — 발사를 막는 동결이면 사유(waitingReason)와 관측 칸을, 아니면 null. 동결 파일을 못 읽으면 닫힌 쪽(막는다). */
944
- function launchFreezeHold(root: string, now: Date): { reason: string; freezeReason: string; until: string | null } | null {
945
- let freeze: ReturnType<typeof readLandingFreeze>;
946
- try { freeze = readLandingFreeze(root, now); }
1054
+ type UnreadableUniverseState = { holding: boolean; penalty: number };
1055
+ /** CHILD-UNIV-HOLD: ticks a newly unreadable child universe holds every launch before the pool penalty takes over. */
1056
+ export const UNREADABLE_UNIVERSE_HOLD_TICKS = 2;
1057
+ /** CHILD-UNIV-HOLD: a universe unreadable this long raises one ops-health alert per universe per (KST) day. */
1058
+ export const UNREADABLE_UNIVERSE_ALERT_MS = 60 * 60 * 1_000;
1059
+
1060
+ export function unreadableUniverseStatePath(root: string): string {
1061
+ return join(root, 'harness', 'child-universe-unreadable.sqlite');
1062
+ }
1063
+
1064
+ /** CHILD-UNIV-HOLD — the «못 읽음» hold is finite. Per unreadable universe the queue state keeps when it first became
1065
+ * unreadable and how many ticks it held: the first `UNREADABLE_UNIVERSE_HOLD_TICKS` scheduled/direct passes hold every
1066
+ * launch; afterwards launches go on with one pool slot charged per unreadable universe. A universe readable again (or no
1067
+ * longer enumerated) drops its row. Unpersistable state never holds (a hold that cannot count its ticks would be endless). */
1068
+ async function trackUnreadableUniverses(root: string, unreadable: readonly string[], now: Date, followUp: boolean,
1069
+ deps: HarnessQueueDeps): Promise<UnreadableUniverseState> {
1070
+ const nowMs = now.getTime();
1071
+ const day = new Date(nowMs + 9 * 3_600_000).toISOString().slice(0, 10);
1072
+ const alerts: string[] = [];
1073
+ let holding = false;
1074
+ let rows: { universe: string; firstAt: number; heldTicks: number }[] = [];
1075
+ try {
1076
+ mkdirSync(join(root, 'harness'), { recursive: true, mode: 0o700 });
1077
+ const db = new Database(unreadableUniverseStatePath(root), { create: true, strict: true });
1078
+ try {
1079
+ db.exec('CREATE TABLE IF NOT EXISTS unreadable_universe (universe TEXT PRIMARY KEY, first_at INTEGER NOT NULL, held_ticks INTEGER NOT NULL, alerted_day TEXT)');
1080
+ const known = db.query('SELECT universe, first_at AS firstAt, held_ticks AS heldTicks, alerted_day AS alertedDay FROM unreadable_universe')
1081
+ .all() as { universe: string; firstAt: number; heldTicks: number; alertedDay: string | null }[];
1082
+ const current = new Set(unreadable);
1083
+ const cleared = known.filter((row) => !current.has(row.universe)).map((row) => row.universe);
1084
+ for (const universe of cleared) db.query('DELETE FROM unreadable_universe WHERE universe = ?').run(universe);
1085
+ if (cleared.length) {
1086
+ try { debug.log('harness.queue', 'unreadable-universe-cleared', { universes: cleared }); }
1087
+ catch { /* Observation cannot affect dispatch. */ }
1088
+ }
1089
+ for (const universe of unreadable) {
1090
+ db.query('INSERT OR IGNORE INTO unreadable_universe (universe, first_at, held_ticks, alerted_day) VALUES (?, ?, 0, NULL)').run(universe, nowMs);
1091
+ }
1092
+ const live = db.query('SELECT universe, first_at AS firstAt, held_ticks AS heldTicks, alerted_day AS alertedDay FROM unreadable_universe')
1093
+ .all() as { universe: string; firstAt: number; heldTicks: number; alertedDay: string | null }[];
1094
+ holding = live.some((row) => row.heldTicks < UNREADABLE_UNIVERSE_HOLD_TICKS);
1095
+ // A burst follow-up pass belongs to a tick already counted.
1096
+ if (holding && !followUp) db.query('UPDATE unreadable_universe SET held_ticks = held_ticks + 1 WHERE held_ticks < ?').run(UNREADABLE_UNIVERSE_HOLD_TICKS);
1097
+ for (const row of live) {
1098
+ if (nowMs - row.firstAt < UNREADABLE_UNIVERSE_ALERT_MS || row.alertedDay === day) continue;
1099
+ db.query('UPDATE unreadable_universe SET alerted_day = ? WHERE universe = ?').run(day, row.universe);
1100
+ alerts.push(row.universe);
1101
+ }
1102
+ rows = live;
1103
+ } finally { db.close(); }
1104
+ } catch (error) {
1105
+ holding = false;
1106
+ try { debug.log('harness.queue', 'unreadable-universe-state-unavailable', { error: String(error).slice(0, 240), universes: unreadable }, { level: 'warn' }); }
1107
+ catch { /* Observation cannot affect dispatch. */ }
1108
+ }
1109
+ const penalty = holding ? 0 : unreadable.length;
1110
+ if (unreadable.length) {
1111
+ try { debug.log('harness.queue', 'unreadable-universe', { universes: unreadable, holding, penalty,
1112
+ rows: rows.map((row) => ({ universe: row.universe, firstAt: new Date(row.firstAt).toISOString(), heldTicks: row.heldTicks })) }, { level: 'warn' }); }
1113
+ catch { /* Observation cannot affect dispatch. */ }
1114
+ }
1115
+ for (const universe of alerts) {
1116
+ const first = rows.find((row) => row.universe === universe)?.firstAt ?? nowMs;
1117
+ const text = `⚠️ 하니스 대기열: 자식 우주를 ${Math.floor((nowMs - first) / 60_000)}분째 못 읽는다 — ${universe} · 발사는 Pod 풀 여유에서 1칸 보수 차감 중`;
1118
+ try {
1119
+ const send = deps.unreadableAlert ?? (process.env.NODE_ENV === 'test' || process.env.ELANOUS_TEST_HOME ? () => {}
1120
+ : async (body: string) => { (await import('../domains/outbound-alert.js')).sendOutbound(body, 'ops-health'); });
1121
+ await send(text);
1122
+ debug.log('harness.queue', 'unreadable-universe-alert', { universe, firstAt: new Date(first).toISOString(), day });
1123
+ } catch (error) {
1124
+ try { debug.log('harness.queue', 'unreadable-universe-alert-failed', { universe, error: String(error).slice(0, 240) }, { level: 'warn' }); }
1125
+ catch { /* Observation cannot affect dispatch. */ }
1126
+ }
1127
+ }
1128
+ return { holding, penalty };
1129
+ }
1130
+
1131
+ /** CHILD-UNIV-COUNT: the production freeze binds a tick in any universe (a work-tree launch ticks in its derived test
1132
+ * universe and used not to see it); the tick's own root still counts. A test process defaults to its own root only. */
1133
+ function queueProdFreezeRoot(root: string, deps: HarnessQueueDeps): string {
1134
+ if (deps.prodFreezeRoot !== undefined) return deps.prodFreezeRoot;
1135
+ return process.env.NODE_ENV === 'test' || process.env.ELANOUS_TEST_HOME ? root : prodInstanceRoot();
1136
+ }
1137
+
1138
+ function launchFreezeHold(root: string, now: Date, prodRoot: string = root): { reason: string; freezeReason: string; until: string | null } | null {
1139
+ let freeze: ReturnType<typeof readLandingFreeze> = null;
1140
+ try {
1141
+ for (const authority of new Set([prodRoot, root])) {
1142
+ const found = readLandingFreeze(authority, now);
1143
+ if (found?.holdLaunches === true) { freeze = found; break; }
1144
+ }
1145
+ }
947
1146
  catch { return { reason: '동결 상태 읽기 실패', freezeReason: '동결 상태 읽기 실패', until: null }; }
948
1147
  if (freeze?.holdLaunches !== true) return null;
949
1148
  return { reason: `동결 — ${freeze.reason}${freeze.until ? ` · ~${freeze.until}` : ''}`, freezeReason: freeze.reason, until: freeze.until };
950
1149
  }
951
1150
 
1151
+ /** QUEUE-BURST default: five launches 30 s apart fit one two-minute cron interval. */
1152
+ export const QUEUE_BURST_MAX_DEFAULT = 5;
1153
+ export const QUEUE_LAUNCH_STAGGER_MS = 30_000;
1154
+ const queueStaggerPrefix = '발사 간격';
1155
+
1156
+ /**
1157
+ * One scheduled tick launches several queued heads when seats and the Pod pool have room (QUEUE-BURST). Each launch is its
1158
+ * own locked pass that re-measures seat counts, pool headroom, the finish gate and the freeze, so the pool headroom is what
1159
+ * bounds the burst; launches are `launchStaggerMs` apart. A requested tick (direct `harness say|ask`) launches at most its row.
1160
+ */
952
1161
  export async function tickHarnessQueue(deps: HarnessQueueDeps = {}, requestedId?: string): Promise<QueueTick> {
1162
+ if (requestedId) return tickHarnessQueueOnce(deps, requestedId, false);
1163
+ const first = await tickHarnessQueueOnce(deps, undefined, false);
1164
+ if (first.outcome !== 'launched' || !first.item) return first;
1165
+ const configured = deps.burstMax ?? (deps.configPath === undefined ? getUserConfig() : getUserConfig(deps.configPath)).harness?.queue?.burstMax;
1166
+ const burstMax = typeof configured === 'number' && Number.isSafeInteger(configured) && configured > 0 ? configured : QUEUE_BURST_MAX_DEFAULT;
1167
+ const stagger = deps.launchStaggerMs ?? QUEUE_LAUNCH_STAGGER_MS;
1168
+ const launched: QueueItem[] = [first.item];
1169
+ let stop = burstMax <= 1 ? 'burst-max' : '';
1170
+ while (!stop && launched.length < burstMax) {
1171
+ // Nothing left to launch: end the burst without spending the stagger wait.
1172
+ if (!listHarnessQueue(deps).some((row) => row.status === 'queued')) { stop = 'empty'; break; }
1173
+ if (stagger > 0) await (deps.sleep ?? ((ms: number) => Bun.sleep(ms)))(stagger);
1174
+ let next: QueueTick;
1175
+ // Launches already made stand: a failing follow-up pass ends the burst (its own row stays for reconcile) instead of
1176
+ // turning the whole tick into an error.
1177
+ try { next = await tickHarnessQueueOnce(deps, undefined, true); }
1178
+ catch (error) { stop = `follow-up failed: ${String(error).slice(0, 240)}`; break; }
1179
+ if (next.outcome !== 'launched' || !next.item) { stop = next.reason.split('\n')[0] ?? next.outcome; break; }
1180
+ launched.push(next.item);
1181
+ }
1182
+ try { debug.log('harness.queue', 'burst', { launched: launched.length, burstMax, staggerMs: stagger, stop: stop || 'burst-max',
1183
+ ids: launched.map((row) => row.id), seats: launched.map((row) => row.seat) }); }
1184
+ catch { /* Observation cannot change launches already made. */ }
1185
+ return { ...first, launched };
1186
+ }
1187
+
1188
+ async function tickHarnessQueueOnce(deps: HarnessQueueDeps, requestedId: string | undefined, followUp: boolean): Promise<QueueTick> {
953
1189
  const root = deps.root ?? effectiveInstanceRoot();
954
1190
  return locked(root, async (path) => {
955
- try {
1191
+ // A burst follow-up already ran the author shadow and the idle request in this tick's first pass.
1192
+ if (!followUp) try {
956
1193
  const now = (deps.now ?? (() => new Date()))();
957
1194
  // A test process must not run the real author shadow — it reads operational ledgers and blew the 5s test budget (10-05 P0).
958
1195
  if (!deps.authorShadow && process.env.ELANOUS_AUTHOR_SHADOW_LIVE !== '1' && (process.env.NODE_ENV === 'test' || process.env.ELANOUS_TEST_HOME)) {
@@ -967,7 +1204,7 @@ export async function tickHarnessQueue(deps: HarnessQueueDeps = {}, requestedId?
967
1204
  }
968
1205
  const items = read(path);
969
1206
  // ORCH2 ③ 동결 창의 «발사» 절반 — `freeze on --hold-launches` 면 이 틱은 발사 0. 동결 파일을 못 읽으면 착지 쪽과 같이 닫힌 쪽(발사 0).
970
- const hold = launchFreezeHold(root, (deps.now ?? (() => new Date()))());
1207
+ const hold = launchFreezeHold(root, (deps.now ?? (() => new Date()))(), queueProdFreezeRoot(root, deps));
971
1208
  if (hold) {
972
1209
  const reason = hold.reason;
973
1210
  const queued = items.filter((item) => item.status === 'queued');
@@ -982,7 +1219,7 @@ export async function tickHarnessQueue(deps: HarnessQueueDeps = {}, requestedId?
982
1219
  return subject ? { outcome: 'waiting', item: { ...subject, waitingReason: reason }, reason }
983
1220
  : { outcome: 'skipped', reason: requestedId ? '이미 처리됨 또는 항목 없음' : 'empty' };
984
1221
  }
985
- try { await (deps.idleRequest ?? requestIdleSeats)(root, (deps.now ?? (() => new Date()))(), items, deps); }
1222
+ if (!followUp) try { await (deps.idleRequest ?? requestIdleSeats)(root, (deps.now ?? (() => new Date()))(), items, deps); }
986
1223
  catch (error) {
987
1224
  try { debug.log('loop.orchestrator', 'idle-request-failed', { reason: String(error) }); }
988
1225
  catch { /* Idle observation cannot affect queue dispatch. */ }
@@ -1043,7 +1280,22 @@ export async function tickHarnessQueue(deps: HarnessQueueDeps = {}, requestedId?
1043
1280
  }
1044
1281
  let current = items;
1045
1282
  let processes: readonly QueueProcess[];
1046
- try { processes = (deps.processes ?? readHarnessQueueProcesses)(); }
1283
+ let childUniverses: QueueInventory['childUniverses'] | undefined;
1284
+ let unreadableState: UnreadableUniverseState = { holding: false, penalty: 0 };
1285
+ try {
1286
+ const launchTrees = [...new Set(items.flatMap((row) => row.launchCwd ? [row.launchCwd] : []))];
1287
+ const inventory: QueueInventory = deps.inventory ? deps.inventory(launchTrees)
1288
+ : deps.processes ? { processes: deps.processes(), childUniverses: { roots: [], unreadable: [] } }
1289
+ : readHarnessQueueInventory({ launchTrees });
1290
+ processes = inventory.processes;
1291
+ // A seam or host that read no child universe keeps the parent-only line; any consulted universe shows the split.
1292
+ if (inventory.childUniverses.roots.length || inventory.childUniverses.unreadable.length) childUniverses = inventory.childUniverses;
1293
+ // CHILD-UNIV-HOLD: a `processes`-only seam consulted no child universe, so it neither sets nor clears the state.
1294
+ if (deps.inventory || !deps.processes) {
1295
+ unreadableState = await trackUnreadableUniverses(root, inventory.childUniverses.unreadable,
1296
+ (deps.now ?? (() => new Date()))(), followUp, deps);
1297
+ }
1298
+ }
1047
1299
  catch (error) {
1048
1300
  for (const item of candidates) {
1049
1301
  const injectedCap = deps.cap?.(item.seat);
@@ -1176,11 +1428,35 @@ export async function tickHarnessQueue(deps: HarnessQueueDeps = {}, requestedId?
1176
1428
  let pool: QueuePool | undefined;
1177
1429
  let poolReason: string | undefined;
1178
1430
  let finishHeld: string | null | undefined;
1431
+ // CHILD-UNIV-COUNT: a seat's live runs whose ledger lives in a derived child universe (a work-tree launch's children).
1432
+ const childActive = (seat: QueueSeat): number => runs.filter((run) => run.seat === seat && run.universe === 'child'
1433
+ && !runVerdict(run, seat)).length;
1434
+ const universeSplit = (seat: QueueSeat, active: number): string => {
1435
+ if (!childUniverses) return '';
1436
+ const child = childActive(seat);
1437
+ const parent = active - child;
1438
+ const n = childUniverses.unreadable.length;
1439
+ return !n ? ` · 부모 ${parent} ⊕ 자식 우주 ${child}`
1440
+ : unreadableState.holding ? ` · 부모 ${parent} ⊕ 자식 우주 ${child} ⊕ 못 읽음 ${n}곳`
1441
+ : ` · 부모 ${parent} ⊕ 자식 우주 ${child} ⊕ 못 읽음 ${n}곳(보수 차감)`;
1442
+ };
1443
+ const seatLine = (seat: QueueSeat, active: number, details: ReturnType<typeof seatCapDetails>): string =>
1444
+ `${seatCapReason(seat, active, details)}${universeSplit(seat, active)}`;
1445
+ if (childUniverses?.unreadable.length && unreadableState.holding) {
1446
+ // «못 읽음» is not «0»: an unreadable child universe may hold this seat's runs, so no seat launches on a partial count —
1447
+ // but only for the first ticks (CHILD-UNIV-HOLD); afterwards the pool headroom is reduced instead.
1448
+ for (const item of candidates) {
1449
+ const details = seatCapDetails({ seat: item.seat, caps, gate, injectedCap: deps.cap?.(item.seat) });
1450
+ const active = seatActive(item.seat);
1451
+ blocked.set(item.seat, { reason: `${seatLine(item.seat, active, details)} — 자식 우주 못 읽음: ${childUniverses.unreadable.join(', ')}`, active });
1452
+ }
1453
+ return waiting();
1454
+ }
1179
1455
  for (const item of candidates) {
1180
1456
  const injectedCap = deps.cap?.(item.seat);
1181
1457
  const details = seatCapDetails({ seat: item.seat, caps, gate, injectedCap });
1182
1458
  const active = seatActive(item.seat);
1183
- if (active >= details.cap) { blocked.set(item.seat, { reason: seatCapReason(item.seat, active, details), active }); continue; }
1459
+ if (active >= details.cap) { blocked.set(item.seat, { reason: seatLine(item.seat, active, details), active }); continue; }
1184
1460
  if (!pool && !poolReason) {
1185
1461
  try { pool = (deps.pool ?? readHarnessQueuePool)(); }
1186
1462
  catch (error) { poolReason = `pod lease status unavailable: ${String(error)}`; }
@@ -1189,20 +1465,22 @@ export async function tickHarnessQueue(deps: HarnessQueueDeps = {}, requestedId?
1189
1465
  }
1190
1466
  if (pool && !poolReason) {
1191
1467
  const queueLaunching = current.filter((row) => row.status === 'launching' || row.status === 'launched').length;
1192
- if (pool.running + pool.pending + pool.reserved + queueLaunching >= pool.limit) {
1193
- poolReason = `pool: ${pool.running}+${pool.pending}+${pool.reserved}+${queueLaunching}/${pool.limit}`;
1468
+ // CHILD-UNIV-HOLD: each unreadable child universe is charged one pool slot (conservative), never a seat slot.
1469
+ const penalty = unreadableState.penalty;
1470
+ if (pool.running + pool.pending + pool.reserved + queueLaunching + penalty >= pool.limit) {
1471
+ poolReason = `pool: ${pool.running}+${pool.pending}+${pool.reserved}+${queueLaunching}${penalty ? `+못 읽음 ${penalty}곳(보수 차감)` : ''}/${pool.limit}`;
1194
1472
  }
1195
1473
  }
1196
1474
  }
1197
1475
  if (poolReason) { blocked.set(item.seat, { reason: poolReason, active }); continue; }
1198
1476
  const budget = decideSpawn({ seat: item.seat, running: active, caps, gate, injectedCap });
1199
1477
  if (!budget.allow) {
1200
- blocked.set(item.seat, { reason: `${budget.reason}: ${seatCapReason(item.seat, active, details)}`, active });
1478
+ blocked.set(item.seat, { reason: `${budget.reason}: ${seatLine(item.seat, active, details)}`, active });
1201
1479
  continue;
1202
1480
  }
1203
1481
  if (finishHeld === undefined) {
1204
1482
  finishHeld = await finishGateReason(root, (deps.now ?? (() => new Date()))(), config.loops?.orchestrator?.finishGate ?? 'shadow',
1205
- pool!.running + pool!.pending + pool!.reserved
1483
+ pool!.running + pool!.pending + pool!.reserved + unreadableState.penalty
1206
1484
  + current.filter((row) => row.status === 'launching' || row.status === 'launched').length, pool!.limit, deps);
1207
1485
  }
1208
1486
  if (finishHeld) { blocked.set(item.seat, { reason: finishHeld, active }); continue; }
@@ -1214,9 +1492,9 @@ export async function tickHarnessQueue(deps: HarnessQueueDeps = {}, requestedId?
1214
1492
  const ownCap = deps.cap?.(requested.seat);
1215
1493
  const details = seatCapDetails({ seat: requested.seat, caps, gate, injectedCap: ownCap });
1216
1494
  const active = seatActive(requested.seat);
1217
- if (active >= details.cap) return seatCapReason(requested.seat, active, details);
1495
+ if (active >= details.cap) return seatLine(requested.seat, active, details);
1218
1496
  const budget = decideSpawn({ seat: requested.seat, running: active, caps, gate, injectedCap: ownCap });
1219
- return budget.allow ? undefined : `${budget.reason}: ${seatCapReason(requested.seat, active, details)}`;
1497
+ return budget.allow ? undefined : `${budget.reason}: ${seatLine(requested.seat, active, details)}`;
1220
1498
  })();
1221
1499
  const reason = own ?? `다른 자리 차례 — ${item.seat}`;
1222
1500
  if (requested.waitingReason !== reason) save(path, current.map((row) => row.id === requestedId ? { ...row, waitingReason: reason } : row));
@@ -1224,7 +1502,7 @@ export async function tickHarnessQueue(deps: HarnessQueueDeps = {}, requestedId?
1224
1502
  return { outcome: 'waiting', item: { ...requested, waitingReason: reason }, reason };
1225
1503
  }
1226
1504
  // 발사 직전 다시 본다 — 틱 앞의 확인과 여기 사이에 await(idleRequest·pool·finish 측정)가 있어 그새 켜진 동결을 놓치지 않게.
1227
- const lateHold = launchFreezeHold(root, (deps.now ?? (() => new Date()))());
1505
+ const lateHold = launchFreezeHold(root, (deps.now ?? (() => new Date()))(), queueProdFreezeRoot(root, deps));
1228
1506
  if (lateHold) {
1229
1507
  save(path, current.map((row) => row.id === item.id ? { ...row, waitingReason: lateHold.reason } : row));
1230
1508
  observe('waiting', { id: item.id, seat: item.seat, reason: lateHold.reason }, deps);
@@ -1232,6 +1510,18 @@ export async function tickHarnessQueue(deps: HarnessQueueDeps = {}, requestedId?
1232
1510
  catch { /* Observation cannot affect dispatch. */ }
1233
1511
  return { outcome: 'waiting', item: { ...item, waitingReason: lateHold.reason }, reason: lateHold.reason };
1234
1512
  }
1513
+ // QUEUE-BURST: scheduled launches stay `launchStaggerMs` after the last queue launch — any tick or process, a direct
1514
+ // launch included — so an overlapping cron tick cannot double a burst. A requested (direct say/ask) tick keeps its
1515
+ // launch-at-once contract (ONEDOOR-2) and only records its time for the next scheduled tick.
1516
+ const stagger = requestedId ? 0 : deps.launchStaggerMs ?? QUEUE_LAUNCH_STAGGER_MS;
1517
+ const lastAt = stagger > 0 ? lastQueueLaunchAt(path) : undefined;
1518
+ const sinceLast = lastAt === undefined ? undefined : (deps.now ?? (() => new Date()))().getTime() - lastAt;
1519
+ if (sinceLast !== undefined && sinceLast >= 0 && sinceLast < stagger) {
1520
+ const reason = `${queueStaggerPrefix} — 마지막 발사 ${Math.floor(sinceLast / 1000)}s 전 · ${Math.round(stagger / 1000)}s 간격`;
1521
+ save(path, current.map((row) => row.id === item.id ? { ...row, waitingReason: reason } : row));
1522
+ observe('waiting', { id: item.id, seat: item.seat, reason, sinceLastMs: sinceLast, staggerMs: stagger }, deps);
1523
+ return { outcome: 'waiting', item: { ...item, waitingReason: reason }, reason };
1524
+ }
1235
1525
  // UUIDv7 carries the launch time, so the dead-row grace can run from launch without a new queue field.
1236
1526
  const launching: QueueItem = { ...item, status: 'launching', launchId: `hq-${Bun.randomUUIDv7()}` };
1237
1527
  delete launching.waitingReason;
@@ -1241,7 +1531,7 @@ export async function tickHarnessQueue(deps: HarnessQueueDeps = {}, requestedId?
1241
1531
  if (!Number.isSafeInteger(pid) || pid < 1) throw new Error('launcher returned no pid');
1242
1532
  const launched: QueueItem = { ...launching, pid, status: 'launched' };
1243
1533
  save(path, current.map((row) => row.id === item.id ? launched : row));
1244
- try { saveLastLaunchedSeat(path, item.seat); }
1534
+ try { saveLastLaunchedSeat(path, item.seat, (deps.now ?? (() => new Date()))().getTime()); }
1245
1535
  catch (error) {
1246
1536
  try { debug.log('harness.queue', 'round-robin-save-failed', { id: item.id, seat: item.seat, reason: String(error) }, { level: 'warn' }); }
1247
1537
  catch { /* Observation cannot change an already persisted launch. */ }
@@ -6,6 +6,18 @@ import type { Command } from 'commander';
6
6
  import { debug } from '../debug/log.js';
7
7
  import { normalizeRunId } from './harness-space.js';
8
8
  import { loadFederatedRunLedger } from '../self-implement/run-ledger.js';
9
+ import { getUserConfig } from '../user-config.js';
10
+ import { resolveRepositoryName } from './repository-name.js';
11
+ import {
12
+ classifySalvageRetention,
13
+ formatSalvageRetention,
14
+ listDatedSalvageRefs,
15
+ listOpenPrRefs,
16
+ type DatedSalvageRef,
17
+ type SalvageReferenceSources,
18
+ type SalvageRetentionMode,
19
+ type SalvageRetentionReport,
20
+ } from '../self-implement/salvage-retention.js';
9
21
  import {
10
22
  decideRunSalvage,
11
23
  findRunSalvageBranches,
@@ -76,3 +88,53 @@ export function installHarnessSalvageCommand(harnessCmd: Command, deps: HarnessS
76
88
  if (verdicts.some((verdict) => verdict.outcome === 'unknown')) process.exitCode = 1;
77
89
  });
78
90
  }
91
+
92
+ export interface HarnessSalvageRetentionDeps {
93
+ repository?: () => string;
94
+ listRefs?: (repository: string) => DatedSalvageRef[];
95
+ listOpenPrs?: (repository: string) => SalvageReferenceSources['openPrs'];
96
+ listCards?: () => SalvageReferenceSources['cards'];
97
+ mode?: () => SalvageRetentionMode;
98
+ now?: () => Date;
99
+ print?: (line: string) => void;
100
+ }
101
+
102
+ async function defaultCards(): Promise<SalvageReferenceSources['cards']> {
103
+ const { readTaskAgentState, taskAgentStatePath } = await import('../task-agent/task-hand.js');
104
+ const tasks = readTaskAgentState<{ tasks?: Record<string, { id: string; runId?: string }> }>(taskAgentStatePath()).tasks ?? {};
105
+ return Object.values(tasks).map((card) => ({ id: card.id, ...(card.runId ? { runId: card.runId } : {}), text: JSON.stringify(card) }));
106
+ }
107
+
108
+ /** SALVAGE-RETENTION — 그림자 보고(지우지 않는다). 조회가 하나라도 실패하면 수를 내지 않고 실패로 끝난다(⛔ 부분 수를 «전체»로 안 읽는다). */
109
+ export async function runHarnessSalvageRetention(deps: HarnessSalvageRetentionDeps = {}): Promise<SalvageRetentionReport> {
110
+ const repository = (deps.repository ?? (() => resolveRepositoryName({})))();
111
+ let mode: SalvageRetentionMode = 'shadow';
112
+ try {
113
+ mode = deps.mode ? deps.mode() : (getUserConfig().tools?.selfImplement?.salvageRetention?.mode ?? 'shadow');
114
+ } catch { mode = 'shadow'; }
115
+ const refs = (deps.listRefs ?? ((repo: string) => listDatedSalvageRefs(repo)))(repository);
116
+ const openPrs = (deps.listOpenPrs ?? ((repo: string) => listOpenPrRefs(repo)))(repository);
117
+ const cards = deps.listCards ? deps.listCards() : await defaultCards();
118
+ const report = classifySalvageRetention({ refs, sources: { openPrs, cards }, now: (deps.now ?? (() => new Date()))(), mode });
119
+ debug.log('self-implement.salvage-retention', 'shadow-report', { repository, ...report, openPrs: openPrs.length, cards: cards.length });
120
+ return report;
121
+ }
122
+
123
+ export function installHarnessSalvageRetentionCommand(harnessCmd: Command, deps: HarnessSalvageRetentionDeps = {}): Command {
124
+ return harnessCmd
125
+ .command('salvage-retention')
126
+ .description('원격 salvage/* 가지 보존 규칙(14일 초과 ⊕ 열린 PR·카드 참조 0) — 그림자: 지울 후보 수만 보고하고 지우지 않는다')
127
+ .option('--json', '{ mode, total, older, referenced, candidates, unknownAge, sample, deleted } JSON')
128
+ .action(async (opts: { json?: boolean }) => {
129
+ const print = deps.print ?? ((line: string) => console.log(line));
130
+ try {
131
+ const report = await runHarnessSalvageRetention(deps);
132
+ print(opts.json ? JSON.stringify(report) : formatSalvageRetention(report));
133
+ } catch (error) {
134
+ const reason = error instanceof Error ? error.message : String(error);
135
+ debug.log('self-implement.salvage-retention', 'shadow-report-failed', { error: reason });
136
+ print(`수확 가지 보존: 못 잼 — ${reason}`);
137
+ process.exitCode = 1;
138
+ }
139
+ });
140
+ }