elanous 0.2.20 → 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 (339) 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/{1307-77fad80cf54477c6.js → 1307-c97eba9fb72a9a5e.js} +1 -1
  10. package/apps/pwa/out/_next/static/chunks/1358d292.5c87f12cad6eaa91.js +122 -0
  11. package/apps/pwa/out/_next/static/chunks/1438.27a1a7b8f2308004.js +149 -0
  12. package/apps/pwa/out/_next/static/chunks/1442.02d85cd88f4c1eac.js +79 -0
  13. package/apps/pwa/out/_next/static/chunks/1501-1aad662d82ba43f7.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/1584.9e2dd53da6e6d4b1.js +24 -0
  15. package/apps/pwa/out/_next/static/chunks/1586.b989b0614afb77e6.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/1609.2ad355062870151f.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/1612-f6df0fc0eed7157b.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/1923.37f1d5fe7ebe3860.js +27 -0
  19. package/apps/pwa/out/_next/static/chunks/1948.5f35b39eef222a94.js +5 -0
  20. package/apps/pwa/out/_next/static/chunks/1993.3fd074a2a246234e.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/2140.d2daf9ee96e295d1.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/2212.0359851457473efc.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/224.583e562365a52f74.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/2328.0f37465646bafa7d.js +36 -0
  25. package/apps/pwa/out/_next/static/chunks/2372.bf71372184991937.js +286 -0
  26. package/apps/pwa/out/_next/static/chunks/2549-a8515c86f9e8cb96.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/2655.72cc6a97276c5acc.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2743-ef6e8c3ced74cce5.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/2840-5458ad50c49cac6a.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/2856.f75d5baef7f49b74.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/3344.0de4b0ec5a454b7a.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/335.a13f339e97893437.js +94 -0
  33. package/apps/pwa/out/_next/static/chunks/3394.646348f34b2500be.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/3443.6276909b53d72d53.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/3495.abd4254d808bf2dc.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/3522-1de1319d028ac89c.js +25 -0
  37. package/apps/pwa/out/_next/static/chunks/3721.ba5b82db0d9a5572.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/3919.9ff7dc25afefee33.js +74 -0
  39. package/apps/pwa/out/_next/static/chunks/3998.bd9a69d9b50e9bd2.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/4003.ab6db1dc266576c9.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/4216.e8ac94b9fe6b96ab.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/4327.c44dc2b55cb43ee5.js +34 -0
  43. package/apps/pwa/out/_next/static/chunks/4401.94257d497b6d26bc.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/4559.17ca2cdbaa8d414f.js +115 -0
  45. package/apps/pwa/out/_next/static/chunks/4588.ad3364cb7fb97ada.js +86 -0
  46. package/apps/pwa/out/_next/static/chunks/4706.97e734bd4c9816ca.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/4982.6e44352f751b9a1b.js +77 -0
  48. package/apps/pwa/out/_next/static/chunks/5342.49c5c5a530e62b50.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/5360-08a80abdf3a4da99.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/5431.66c58ab1327ee0b7.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/5717.bcfcb96e701b82e0.js +90 -0
  52. package/apps/pwa/out/_next/static/chunks/5755-30d102586789bf7f.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/5957.3f1a4012388dd962.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/5977-59fedea50d13c762.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/602.9bf02ac8e9502598.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/6020.3a1b347e2d2dfacc.js +8 -0
  57. package/apps/pwa/out/_next/static/chunks/{6158.af2e93b7adff709b.js → 6158.566c890adc06a459.js} +1 -1
  58. package/apps/pwa/out/_next/static/chunks/6375.fce1d174ba6d0324.js +64 -0
  59. package/apps/pwa/out/_next/static/chunks/6546.92a828aef441271f.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/658.957fdb0a8c6c6c8e.js +287 -0
  61. package/apps/pwa/out/_next/static/chunks/6706.323590aabb8b1293.js +226 -0
  62. package/apps/pwa/out/_next/static/chunks/6747.b67947f293a4c02d.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/6767.8e6c5f6e744fcd9f.js +38 -0
  64. package/apps/pwa/out/_next/static/chunks/6868.000b24a3045c5574.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/6946.bba74e9c96050f30.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/7033.a558f1b2f6912a53.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/7065.46029c26c53eac50.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/7087.6fae577d09249bc1.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/7175-350f4ce2873bc038.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/722.de8d9801e0f6d414.js +41 -0
  71. package/apps/pwa/out/_next/static/chunks/7497.57981e9d7a75a52a.js +24 -0
  72. package/apps/pwa/out/_next/static/chunks/7630.2b9a68659053ff4e.js +193 -0
  73. package/apps/pwa/out/_next/static/chunks/7672.4af122ad892117c7.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/771.6ab889a233e7dae7.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/7899.597af4a9996c907f.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/79b78388.663188133449d707.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/8096.f7da0776cfde6300.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/8194.484f51e273d9abd2.js +217 -0
  79. package/apps/pwa/out/_next/static/chunks/8769.815ca69879f608b4.js +104 -0
  80. package/apps/pwa/out/_next/static/chunks/8857-f1762140ef9a96b1.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/88729267.9c6c000157c83f2f.js +62 -0
  82. package/apps/pwa/out/_next/static/chunks/8f835b4f.048eeb02299e3e12.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/9068.5f2764fccf86c1b2.js +93 -0
  84. package/apps/pwa/out/_next/static/chunks/9247-7581825d511a64c0.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/9451.ebfb0dcc9c02250f.js +23 -0
  86. package/apps/pwa/out/_next/static/chunks/9497-b0e758d1cd4ae8d7.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/9559.e648369b1d57cf9a.js +62 -0
  88. package/apps/pwa/out/_next/static/chunks/9832.2ab2e818013574c7.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/a9f2b54d-40c16bd0efdb04b8.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/app/approvals/page-937c92292a4ab4f0.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-85b316bb4235980b.js → page-d781be3e3a9cfd74.js} +1 -1
  92. package/apps/pwa/out/_next/static/chunks/app/board/{page-9b2b26a33cb2cd42.js → page-b0a80997903852d8.js} +1 -1
  93. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-c9955fde67b1bcb5.js → page-2b1263165630d9c8.js} +1 -1
  94. package/apps/pwa/out/_next/static/chunks/app/bots/{page-7a852956c74015a3.js → page-ea998f7f5d156bea.js} +1 -1
  95. package/apps/pwa/out/_next/static/chunks/app/ceo/page-33c328b64ca508f8.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/app/chat/{page-4ba4abe096b6c2a8.js → page-e2d6bed48ac20120.js} +1 -1
  97. package/apps/pwa/out/_next/static/chunks/app/consult/{page-575d4a8b4f7becb2.js → page-111cabe4d1fd69ae.js} +1 -1
  98. package/apps/pwa/out/_next/static/chunks/app/control/{page-f5d56ff5f45ad164.js → page-544dd4bd4847eeb7.js} +1 -1
  99. package/apps/pwa/out/_next/static/chunks/app/decisions/{page-2260e9eef0357421.js → page-3ee191319bf97d87.js} +1 -1
  100. package/apps/pwa/out/_next/static/chunks/app/design-check/page-e4bcb2c503dab04b.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/app/editor/page-300897dcbbd5ed76.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/app/exec/{page-466bf0ee16ad1432.js → page-a449f1ed472b81c2.js} +1 -1
  103. package/apps/pwa/out/_next/static/chunks/app/field/{page-a23bc71713bcc08f.js → page-9045094d6d3b5cd0.js} +1 -1
  104. package/apps/pwa/out/_next/static/chunks/app/inside/page-c06673596849ef45.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/app/intake/page-103fa3191c1b2e08.js +1 -0
  106. package/apps/pwa/out/_next/static/chunks/app/layout-9f81233f29dcd409.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/app/live/page-be8b4615c919e36c.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/app/live-run/page-c685b104e0dfa609.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/app/loops/page-8d693331cfbb6add.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/app/market/page-cef4f30a4c22fae1.js +1 -0
  111. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66ea1b54fc83fa15.js +1 -0
  112. package/apps/pwa/out/_next/static/chunks/app/morning/{page-ec102c9b8b691e37.js → page-7a12f61be4a579d1.js} +1 -1
  113. package/apps/pwa/out/_next/static/chunks/app/not-found-c85b0589591a99c9.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/app/observatory/page-725a4da02dd129eb.js +1 -0
  115. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-f6055495e9ed7ea8.js → page-8cc707bd09a497d9.js} +1 -1
  116. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-90b55be1f15ce0e8.js +1 -0
  117. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cb16606e08be65db.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/app/outputs/page-16dc64b74ed6a40a.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/app/page-21bec4afeb2f0917.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/app/reflection/{page-db03ae3589a81c9e.js → page-dc39ee7a36d26385.js} +1 -1
  121. package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-4a6463b97f0ad94f.js → page-84ae759f76053672.js} +1 -1
  122. package/apps/pwa/out/_next/static/chunks/app/sessions/{page-75bffecc9aa1004e.js → page-3db58ddb61fe2be9.js} +1 -1
  123. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c5315e30f3528dc4.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/settings/{page-31d5b875760bb62d.js → page-2fe368efb6de3b6a.js} +1 -1
  125. package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-c26fa10aac6c21c4.js → page-8d04132dd0ac5e10.js} +1 -1
  126. package/apps/pwa/out/_next/static/chunks/app/setup/{page-68793c6e10331d66.js → page-f23484de467c9ea3.js} +1 -1
  127. package/apps/pwa/out/_next/static/chunks/app/share/{page-46d5201d2207a511.js → page-c69762987b751174.js} +1 -1
  128. package/apps/pwa/out/_next/static/chunks/app/showroom/page-5456692bda39aafb.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/tasks/{page-be1c4e8697f22360.js → page-d75f7cd65845ce98.js} +1 -1
  130. package/apps/pwa/out/_next/static/chunks/app/term/page-5603f5e568b25ce3.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/today/page-d2cc0f4e89f42028.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/trace/{page-0fd895fab9b62c1b.js → page-a6eb73b8a4840f1c.js} +1 -1
  133. package/apps/pwa/out/_next/static/chunks/app/vault/page-c0a3f4d10edb83fa.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/{page-c6e4364d9de8f717.js → page-6c0b88068c143f15.js} +1 -1
  135. package/apps/pwa/out/_next/static/chunks/app/workflows/{page-0acc8e198f7b0dfb.js → page-7f8d7fcdf058a845.js} +1 -1
  136. package/apps/pwa/out/_next/static/chunks/app/workspace/{page-34882c84f84021c5.js → page-22163556cd38bc88.js} +1 -1
  137. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-141436fd26dd56fc.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/d70879db.c66c56a95a91efc6.js +66 -0
  139. package/apps/pwa/out/_next/static/chunks/f8626309.a4e5d362b1d55e59.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/webpack-f2b4016b9b981bc8.js +1 -0
  141. package/apps/pwa/out/_next/static/css/{d3427996da11e480.css → b9d8151e48998417.css} +1 -1
  142. package/apps/pwa/out/_next/static/css/e81df11490b4413b.css +1 -0
  143. package/apps/pwa/out/approvals/index.html +1 -1
  144. package/apps/pwa/out/approvals/index.txt +14 -14
  145. package/apps/pwa/out/autopilot/index.html +1 -1
  146. package/apps/pwa/out/autopilot/index.txt +14 -14
  147. package/apps/pwa/out/board/index.html +1 -1
  148. package/apps/pwa/out/board/index.txt +14 -14
  149. package/apps/pwa/out/botlab/index.html +1 -1
  150. package/apps/pwa/out/botlab/index.txt +14 -14
  151. package/apps/pwa/out/bots/index.html +1 -1
  152. package/apps/pwa/out/bots/index.txt +14 -14
  153. package/apps/pwa/out/ceo/index.html +1 -1
  154. package/apps/pwa/out/ceo/index.txt +14 -14
  155. package/apps/pwa/out/chat/index.html +1 -1
  156. package/apps/pwa/out/chat/index.txt +14 -14
  157. package/apps/pwa/out/consult/index.html +1 -1
  158. package/apps/pwa/out/consult/index.txt +14 -14
  159. package/apps/pwa/out/control/index.html +1 -1
  160. package/apps/pwa/out/control/index.txt +14 -14
  161. package/apps/pwa/out/decisions/index.html +1 -1
  162. package/apps/pwa/out/decisions/index.txt +14 -14
  163. package/apps/pwa/out/design-check/index.html +1 -1
  164. package/apps/pwa/out/design-check/index.txt +14 -14
  165. package/apps/pwa/out/editor/index.html +1 -1
  166. package/apps/pwa/out/editor/index.txt +15 -15
  167. package/apps/pwa/out/exec/index.html +1 -1
  168. package/apps/pwa/out/exec/index.txt +14 -14
  169. package/apps/pwa/out/field/index.html +1 -1
  170. package/apps/pwa/out/field/index.txt +14 -14
  171. package/apps/pwa/out/healthz +1 -1
  172. package/apps/pwa/out/index.html +1 -1
  173. package/apps/pwa/out/index.txt +14 -14
  174. package/apps/pwa/out/inside/index.html +1 -1
  175. package/apps/pwa/out/inside/index.txt +15 -15
  176. package/apps/pwa/out/intake/index.html +1 -1
  177. package/apps/pwa/out/intake/index.txt +14 -14
  178. package/apps/pwa/out/live/index.html +1 -1
  179. package/apps/pwa/out/live/index.txt +14 -14
  180. package/apps/pwa/out/live-run/index.html +1 -0
  181. package/apps/pwa/out/live-run/index.txt +35 -0
  182. package/apps/pwa/out/loops/index.html +1 -1
  183. package/apps/pwa/out/loops/index.txt +14 -14
  184. package/apps/pwa/out/market/index.html +1 -1
  185. package/apps/pwa/out/market/index.txt +14 -14
  186. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  187. package/apps/pwa/out/missions/placeholder/index.txt +14 -14
  188. package/apps/pwa/out/morning/index.html +1 -1
  189. package/apps/pwa/out/morning/index.txt +14 -14
  190. package/apps/pwa/out/observatory/index.html +1 -1
  191. package/apps/pwa/out/observatory/index.txt +14 -14
  192. package/apps/pwa/out/ops/checklist/index.html +1 -1
  193. package/apps/pwa/out/ops/checklist/index.txt +14 -14
  194. package/apps/pwa/out/ops/release/index.html +1 -1
  195. package/apps/pwa/out/ops/release/index.txt +14 -14
  196. package/apps/pwa/out/ops/seats/index.html +1 -1
  197. package/apps/pwa/out/ops/seats/index.txt +14 -14
  198. package/apps/pwa/out/outputs/index.html +1 -1
  199. package/apps/pwa/out/outputs/index.txt +14 -14
  200. package/apps/pwa/out/reflection/index.html +1 -1
  201. package/apps/pwa/out/reflection/index.txt +15 -15
  202. package/apps/pwa/out/scheduler/index.html +1 -1
  203. package/apps/pwa/out/scheduler/index.txt +14 -14
  204. package/apps/pwa/out/sessions/index.html +1 -1
  205. package/apps/pwa/out/sessions/index.txt +14 -14
  206. package/apps/pwa/out/settings/devices/index.html +1 -1
  207. package/apps/pwa/out/settings/devices/index.txt +14 -14
  208. package/apps/pwa/out/settings/index.html +1 -1
  209. package/apps/pwa/out/settings/index.txt +14 -14
  210. package/apps/pwa/out/setup/done/index.html +1 -1
  211. package/apps/pwa/out/setup/done/index.txt +14 -14
  212. package/apps/pwa/out/setup/index.html +1 -1
  213. package/apps/pwa/out/setup/index.txt +14 -14
  214. package/apps/pwa/out/share/index.html +1 -1
  215. package/apps/pwa/out/share/index.txt +14 -14
  216. package/apps/pwa/out/showroom/index.html +1 -1
  217. package/apps/pwa/out/showroom/index.txt +14 -14
  218. package/apps/pwa/out/tasks/index.html +1 -1
  219. package/apps/pwa/out/tasks/index.txt +14 -14
  220. package/apps/pwa/out/term/index.html +1 -1
  221. package/apps/pwa/out/term/index.txt +14 -14
  222. package/apps/pwa/out/today/index.html +1 -1
  223. package/apps/pwa/out/today/index.txt +14 -14
  224. package/apps/pwa/out/trace/index.html +1 -1
  225. package/apps/pwa/out/trace/index.txt +14 -14
  226. package/apps/pwa/out/vault/index.html +1 -1
  227. package/apps/pwa/out/vault/index.txt +14 -14
  228. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  229. package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
  230. package/apps/pwa/out/workflows/index.html +1 -1
  231. package/apps/pwa/out/workflows/index.txt +14 -14
  232. package/apps/pwa/out/workspace/index.html +1 -1
  233. package/apps/pwa/out/workspace/index.txt +14 -14
  234. package/apps/pwa/out/worktrees/index.html +1 -1
  235. package/apps/pwa/out/worktrees/index.txt +14 -14
  236. package/graphs/drafts/recipes.yaml +4 -4
  237. package/graphs/implement-loop.yaml +277 -5
  238. package/package.json +1 -1
  239. package/scripts/coord-channel-watch.sh +31 -4
  240. package/scripts/docs-lint.ts +4 -1
  241. package/scripts/marketing/generate-ax-brief.ts +18 -1
  242. package/scripts/measure-nl-routing-corpus.ts +11 -3
  243. package/scripts/release-loop/gate-node.ts +5 -5
  244. package/scripts/release-loop/shard-plan.ts +50 -5
  245. package/scripts/rhythm/daily.ts +2 -1
  246. package/src/browser-cdp/client.ts +10 -5
  247. package/src/cli/ops-cli.ts +11 -0
  248. package/src/cli/ops-overview-cli.ts +50 -0
  249. package/src/cli/pod-cli.ts +1 -0
  250. package/src/cli/tasks-cli.ts +106 -0
  251. package/src/dashboard/index.ts +40 -4
  252. package/src/esc-abort-gate.ts +3 -1
  253. package/src/graph-runner/graph-cli.ts +57 -0
  254. package/src/graph-runner/graph-run-api.ts +13 -0
  255. package/src/graph-wizard/archetypes.ts +88 -0
  256. package/src/graph-wizard/generate.ts +545 -0
  257. package/src/graph-wizard/saved-steps.ts +120 -0
  258. package/src/graph-wizard/steps.ts +193 -0
  259. package/src/harness/harness-cli-command.ts +68 -12
  260. package/src/index.ts +7 -4
  261. package/src/intake-plane/digest.ts +18 -11
  262. package/src/maturity/feature-maturity.ts +2 -1
  263. package/src/nexus/api/draft-metrics.ts +114 -0
  264. package/src/nexus/api/graph-wizard-api.ts +53 -0
  265. package/src/nexus/api/graphs-api.ts +50 -12
  266. package/src/nexus/api/harness-traversal.ts +205 -0
  267. package/src/nexus/api/http-server.ts +31 -0
  268. package/src/nexus/outbound/router.ts +28 -8
  269. package/src/onboarding/first-run.ts +3 -0
  270. package/src/ops/overview-collect.ts +345 -0
  271. package/src/ops/overview-seat-era.ts +90 -0
  272. package/src/ops/overview.ts +223 -0
  273. package/src/release-loop/gate-shards.ts +7 -2
  274. package/src/self-dev/ask-launch-flow.ts +4 -2
  275. package/src/self-dev/ask-launch-io.ts +3 -1
  276. package/src/self-dev/graph-journey-nodes.ts +157 -0
  277. package/src/self-dev/graph-journey-route.ts +105 -0
  278. package/src/self-dev/launch-preflight.ts +43 -3
  279. package/src/self-dev/release-path-guard.ts +7 -4
  280. package/src/self-implement/frozen-merges.ts +23 -0
  281. package/src/self-implement/goal-author.ts +14 -0
  282. package/src/self-implement/heal-intake.ts +3 -0
  283. package/src/self-implement/orchestrator.ts +16 -1
  284. package/src/self-implement/seams.ts +3 -0
  285. package/src/self-implement/self-implement-runtime.ts +12 -0
  286. package/src/session-runtime/index.ts +5 -3
  287. package/src/shell-env-bootstrap.ts +17 -3
  288. package/src/task-agent/card-evidence.ts +6 -0
  289. package/src/task-agent/parent-host.ts +315 -0
  290. package/src/task-agent/task-hand.ts +9 -2
  291. package/src/task-orchestrator/surfaces/pod-command-job.ts +41 -6
  292. package/src/task-orchestrator/surfaces/pod-gate-deps.ts +123 -0
  293. package/src/task-orchestrator/surfaces/self-implement-pod.ts +39 -6
  294. package/src/tool-runtime/browser-runtime.ts +87 -3
  295. package/src/tool-runtime/index.ts +1 -0
  296. package/src/user-config.ts +20 -4
  297. package/src/version/packed-revision.json +1 -1
  298. package/apps/pwa/out/_next/static/Bam1YziK2iH9bN4K0utGk/_buildManifest.js +0 -1
  299. package/apps/pwa/out/_next/static/chunks/1612-f60c0c93ec773f81.js +0 -1
  300. package/apps/pwa/out/_next/static/chunks/1844-9ec4b1d12f6e80eb.js +0 -1
  301. package/apps/pwa/out/_next/static/chunks/1866-e7a6999f25548b30.js +0 -1
  302. package/apps/pwa/out/_next/static/chunks/1948.15f356b15a625597.js +0 -5
  303. package/apps/pwa/out/_next/static/chunks/2116-6429c188eb846ab8.js +0 -1
  304. package/apps/pwa/out/_next/static/chunks/224-e7be8f762fe74986.js +0 -1
  305. package/apps/pwa/out/_next/static/chunks/2549-89c518a4c8ed4d00.js +0 -1
  306. package/apps/pwa/out/_next/static/chunks/2957-96ffab3d521fb08f.js +0 -1
  307. package/apps/pwa/out/_next/static/chunks/5360-be1f1d8f9fcc9755.js +0 -1
  308. package/apps/pwa/out/_next/static/chunks/5755-ea7ed88eb956f47b.js +0 -1
  309. package/apps/pwa/out/_next/static/chunks/5977-94ed6f78e9814b2b.js +0 -1
  310. package/apps/pwa/out/_next/static/chunks/7748-6e51bfe927019f5c.js +0 -25
  311. package/apps/pwa/out/_next/static/chunks/9247-c1753a059e7bac08.js +0 -1
  312. package/apps/pwa/out/_next/static/chunks/a9f2b54d-33c0689b70ed8cf3.js +0 -1
  313. package/apps/pwa/out/_next/static/chunks/app/approvals/page-c855c24de7db74fe.js +0 -1
  314. package/apps/pwa/out/_next/static/chunks/app/ceo/page-c5a5c621be0e04ae.js +0 -1
  315. package/apps/pwa/out/_next/static/chunks/app/design-check/page-7c058069c5e7fa63.js +0 -1
  316. package/apps/pwa/out/_next/static/chunks/app/editor/page-dc5da0b8a2edb8a4.js +0 -1
  317. package/apps/pwa/out/_next/static/chunks/app/inside/page-ed4a6f799f005d09.js +0 -1
  318. package/apps/pwa/out/_next/static/chunks/app/intake/page-9cd565005b93aca6.js +0 -1
  319. package/apps/pwa/out/_next/static/chunks/app/layout-55f0f40c6db95f15.js +0 -1
  320. package/apps/pwa/out/_next/static/chunks/app/live/page-474cb5888ea0e8f3.js +0 -1
  321. package/apps/pwa/out/_next/static/chunks/app/loops/page-35bfec2e631d3eae.js +0 -1
  322. package/apps/pwa/out/_next/static/chunks/app/market/page-9ca21f9ad3208a61.js +0 -1
  323. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-f9ccb1b1adbea14d.js +0 -1
  324. package/apps/pwa/out/_next/static/chunks/app/not-found-a9c2cfa8dda922a5.js +0 -1
  325. package/apps/pwa/out/_next/static/chunks/app/observatory/page-6da35144878feb6d.js +0 -1
  326. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-25204694fa9984a5.js +0 -1
  327. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-a60e035f151d84d3.js +0 -1
  328. package/apps/pwa/out/_next/static/chunks/app/outputs/page-189c587f36874538.js +0 -1
  329. package/apps/pwa/out/_next/static/chunks/app/page-c56634bde705a65d.js +0 -1
  330. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a8d2fe0c669e0de4.js +0 -1
  331. package/apps/pwa/out/_next/static/chunks/app/showroom/page-d81c07a7b4f618ae.js +0 -1
  332. package/apps/pwa/out/_next/static/chunks/app/term/page-cf0a4b4abead71ea.js +0 -1
  333. package/apps/pwa/out/_next/static/chunks/app/today/page-a4717e2c4bf7a28f.js +0 -1
  334. package/apps/pwa/out/_next/static/chunks/app/vault/page-8b6c1454a8e8f306.js +0 -1
  335. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-0506a1e0e4bdb2f5.js +0 -1
  336. package/apps/pwa/out/_next/static/chunks/webpack-c260571513bf43c2.js +0 -1
  337. package/apps/pwa/out/_next/static/css/c23d959d3ee15e57.css +0 -1
  338. /package/apps/pwa/out/_next/static/{Bam1YziK2iH9bN4K0utGk → MObU8ujkMciAKr6ljAV05}/_ssgManifest.js +0 -0
  339. /package/skills/project-onboarding/{skill.md → SKILL.md} +0 -0
@@ -0,0 +1,223 @@
1
+ /**
2
+ * OPS-OVERVIEW-CLI 첫 조각 — 흐름 한 장의 «순수» 층(판정 · 이유 사슬 · 사람 표기).
3
+ * 설계 = 내부 문서 `RFC-ops-overview-flow-health-cli-2026-10-08` §A1·A2.
4
+ *
5
+ * ⛔ 이 파일은 I/O 를 하지 않는다 — 수집은 `overview-collect.ts`, 자리 시대 임시 원천은 `overview-seat-era.ts`.
6
+ * ⛔ «못 잼»은 `value:null` ⊕ `reason` 이다 — 절대 0 으로 읽지 않는다(판정도 그렇다).
7
+ */
8
+
9
+ export type CellStatus = 'ok' | 'unmeasured' | 'partial';
10
+ export interface Cell<T> {
11
+ value: T | null;
12
+ source: string;
13
+ observedAt: string | null;
14
+ status: CellStatus;
15
+ reason?: string;
16
+ }
17
+
18
+ export function okCell<T>(value: T, source: string, observedAt: string | null): Cell<T> {
19
+ return { value, source, observedAt, status: 'ok' };
20
+ }
21
+ export function unmeasured<T>(source: string, reason: string, observedAt: string | null = null): Cell<T> {
22
+ return { value: null, source, observedAt, status: 'unmeasured', reason };
23
+ }
24
+ export function measured(cell: Cell<unknown>): boolean {
25
+ return cell.status !== 'unmeasured' && cell.value !== null;
26
+ }
27
+
28
+ /** 닫힌 판정 어휘 — 배열 순서가 곧 우선순위(위가 이긴다 · RFC §A1). */
29
+ export const VERDICTS = ['unobservable', 'blocked', 'loop-down', 'saturated', 'supply-empty', 'degraded', 'flowing'] as const;
30
+ export type Verdict = typeof VERDICTS[number];
31
+ export const VERDICT_LABEL: Record<Verdict, string> = {
32
+ unobservable: '관측 불가', blocked: '막힘', 'loop-down': '루프 정지', saturated: '자원 포화',
33
+ 'supply-empty': '공급 부족', degraded: '흐름 저하', flowing: '흐름 정상',
34
+ };
35
+
36
+ export interface ReleaseRunInfo {
37
+ runId: string; status: string; currentNode: string | null; startedAt: string | null;
38
+ /** approval · freeze · gate · process-gone · none */
39
+ waitingOn: 'approval' | 'gate' | 'process-gone' | 'none';
40
+ elapsedMinutes: number | null;
41
+ }
42
+ export interface ScheduleInfo { cutAt: string; landBy: string | null; publishAt: string | null; freezeNow: boolean }
43
+ export interface ChecklistCounts { green: number; yellow: number; red: number; done: number; p0Yellow: number }
44
+ export interface OwnerTaskRow { handed: number; launched: number; prBound: number; failed: number; launchFailed: number }
45
+ export interface LaunchableInfo {
46
+ count: number; ids: string[];
47
+ skipped: { noTargetPath: number; predecessorOpen: number; alreadyHanded: number };
48
+ /** 오케스트레이터의 «발사 가능» 술어가 아직 export 되지 않아 overview 가 임시로 계산했다(OVW-DISPATCH-LEDGER 가 대체). */
49
+ interim: true;
50
+ }
51
+ export interface CapacityInfo { podUsed: number; podTarget: number; localUsed: number; localMax: number; free: number }
52
+ export interface MachineLoad { load1: number; load5: number; load15: number; cpuCount: number; freeGb: number; totalGb: number; ageSeconds: number }
53
+ export interface MachineRow { name: string; load: Cell<MachineLoad> }
54
+ export interface PodMemberRow { context: string; capacity: number; occupied: Cell<number> }
55
+ export interface ParentsInfo { count: number; staleOver6h: number; oldestHours: number | null }
56
+ export interface LoopRow {
57
+ name: string;
58
+ /** product = 제품 원장 · seat-era = 자리 시대 원천(제품 밖 · 임시) */
59
+ era: 'product' | 'seat-era';
60
+ beat: Cell<{ lastAt: string; ageSeconds: number; alive: boolean | null }>;
61
+ /** 이 주기 이상 박동이 없으면 정지로 본다. null = 주기를 모른다 → 판정에서 뺀다(«죽었다»로 읽지 않는다). */
62
+ staleAfterSeconds: number | null;
63
+ }
64
+
65
+ export interface OverviewSnapshot {
66
+ generatedAt: string;
67
+ instanceRoot: string;
68
+ since: string;
69
+ release: {
70
+ version: Cell<string>;
71
+ run: Cell<ReleaseRunInfo | 'none'>;
72
+ schedule: Cell<ScheduleInfo | 'none'>;
73
+ checklist: Cell<ChecklistCounts>;
74
+ };
75
+ tasks: {
76
+ byOwner: Cell<Record<string, OwnerTaskRow>>;
77
+ done: Cell<number>;
78
+ launchable: Cell<LaunchableInfo>;
79
+ capacity: Cell<CapacityInfo>;
80
+ };
81
+ machines: {
82
+ hosts: MachineRow[];
83
+ pods: PodMemberRow[];
84
+ parents: Cell<ParentsInfo>;
85
+ };
86
+ landings: Cell<{ count: number; recent: string[] }>;
87
+ loops: LoopRow[];
88
+ }
89
+
90
+ export interface VerdictResult { verdict: Verdict; reasons: string[]; nextActions: string[] }
91
+
92
+ /** 부하/코어가 이 값을 넘으면 «기계 압박»(흐름 저하의 사유). 디스크 I/O 신호는 OVW-MACHINE-HB 전까지 없다. */
93
+ export const LOAD_PER_CORE_PRESSURE = 3;
94
+
95
+ export function computeVerdict(s: OverviewSnapshot): VerdictResult {
96
+ const hits = new Map<Verdict, { reasons: string[]; actions: string[] }>();
97
+ const hit = (v: Verdict, reason: string, action?: string): void => {
98
+ const row = hits.get(v) ?? { reasons: [], actions: [] };
99
+ row.reasons.push(reason);
100
+ if (action) row.actions.push(action);
101
+ hits.set(v, row);
102
+ };
103
+
104
+ // 관측 불가 — 판·공급·용량 셋 중 하나라도 못 잼.
105
+ const core: Array<[string, Cell<unknown>]> = [
106
+ ['판(발행 런)', s.release.run], ['판(체크리스트)', s.release.checklist],
107
+ ['공급(발사 가능 칸)', s.tasks.launchable], ['용량(빈 자리)', s.tasks.capacity],
108
+ ];
109
+ for (const [name, cell] of core) {
110
+ if (!measured(cell)) hit('unobservable', `${name} 못 잼(${cell.reason ?? '사유 없음'}) · 원천 ${cell.source}`, `계측 누락 칸: ${name}`);
111
+ }
112
+
113
+ // 막힘 — 발행 런이 승인을 기다리거나 동결 창.
114
+ const run = s.release.run.value;
115
+ if (run && run !== 'none' && run.waitingOn === 'approval') {
116
+ hit('blocked', `발행 런 ${run.runId.slice(0, 8)} 노드 ${run.currentNode ?? '?'} 승인 대기${run.elapsedMinutes !== null ? ` ${run.elapsedMinutes}분` : ''}`, '결정 카드 확인 — `elanous decisions list`');
117
+ }
118
+ const sched = s.release.schedule.value;
119
+ if (sched && sched !== 'none' && sched.freezeNow) hit('blocked', `동결 창 안(${s.release.version.value ?? '?'})`, '동결 해제 시각까지 착지 보류');
120
+
121
+ // 루프 정지 — 주기를 아는 루프의 박동이 끊김 · 프로세스 부재.
122
+ for (const loop of s.loops) {
123
+ const beat = loop.beat.value;
124
+ if (!beat) continue; // 못 잼은 «죽었다»가 아니다
125
+ const tag = loop.era === 'seat-era' ? ' [자리 시대 원천]' : '';
126
+ if (beat.alive === false) hit('loop-down', `${loop.name} 프로세스 없음(마지막 ${beat.lastAt})${tag}`, `${loop.name} 재기동`);
127
+ else if (loop.staleAfterSeconds !== null && beat.ageSeconds > loop.staleAfterSeconds) {
128
+ hit('loop-down', `${loop.name} 박동 ${Math.round(beat.ageSeconds / 60)}분 없음(한계 ${Math.round(loop.staleAfterSeconds / 60)}분)${tag}`, `${loop.name} 재기동`);
129
+ }
130
+ }
131
+
132
+ // 자원 포화 / 공급 부족 — 같은 스냅숏의 두 칸으로만.
133
+ const launchable = s.tasks.launchable.value;
134
+ const capacity = s.tasks.capacity.value;
135
+ if (launchable && capacity) {
136
+ if (launchable.count > 0 && capacity.free <= 0) {
137
+ hit('saturated', `발사 가능 칸 ${launchable.count} · 빈 자리 0 (Pod ${capacity.podUsed}/${capacity.podTarget} · 로컬 ${capacity.localUsed}/${capacity.localMax})`, '발사 늦추기 · 저우선 칸 보류(오케스트레이터)');
138
+ } else if (launchable.count === 0 && capacity.free > 0) {
139
+ const k = launchable.skipped;
140
+ hit('supply-empty', `빈 자리 ${capacity.free} · 발사 가능 칸 0 — 대상 경로 없음 ${k.noTargetPath} · 선행 미완 ${k.predecessorOpen} · 이미 넘김 ${k.alreadyHanded}`, '노란 칸 접지(대상 경로 채우기) — 스튜어드');
141
+ }
142
+ }
143
+
144
+ // 흐름 저하 — 묵은 부모 · 기계 압박.
145
+ const parents = s.machines.parents.value;
146
+ if (parents && parents.staleOver6h > 0) hit('degraded', `6시간 넘은 하니스 부모 ${parents.staleOver6h}(최고 ${parents.oldestHours}h)`, '고아 정리 — reaper');
147
+ for (const host of s.machines.hosts) {
148
+ const l = host.load.value;
149
+ if (l && l.cpuCount > 0 && l.load5 / l.cpuCount > LOAD_PER_CORE_PRESSURE) {
150
+ hit('degraded', `${host.name} 부하 ${l.load5.toFixed(0)}/${l.cpuCount}코어(5분)`, `${host.name} 로컬 레인 닫기 검토`);
151
+ }
152
+ }
153
+
154
+ for (const v of VERDICTS) {
155
+ const row = hits.get(v);
156
+ if (!row) continue;
157
+ // 이유 사슬: 이긴 판정의 사유 먼저, 그 밑에 진 판정들의 사유도 «부가»로 남긴다(한 사슬에 두 원인이 보이게).
158
+ const rest = VERDICTS.filter((o) => o !== v && hits.has(o)).flatMap((o) => hits.get(o)!.reasons.map((r) => `(+${VERDICT_LABEL[o]}) ${r}`));
159
+ return { verdict: v, reasons: [...row.reasons, ...rest], nextActions: row.actions };
160
+ }
161
+ return { verdict: 'flowing', reasons: [], nextActions: [] };
162
+ }
163
+
164
+ /** exit code — flowing·degraded 0 · 그 밖의 판정 3(수집기 자체 실패 1 은 호출자 몫). */
165
+ export function verdictExitCode(v: Verdict): number {
166
+ return v === 'flowing' || v === 'degraded' ? 0 : 3;
167
+ }
168
+
169
+ export function unmeasuredCells(s: OverviewSnapshot): string[] {
170
+ const out: string[] = [];
171
+ const visit = (name: string, cell: Cell<unknown>): void => { if (cell.status === 'unmeasured') out.push(`${name}: ${cell.reason ?? ''}`); };
172
+ visit('release.version', s.release.version); visit('release.run', s.release.run);
173
+ visit('release.schedule', s.release.schedule); visit('release.checklist', s.release.checklist);
174
+ visit('tasks.byOwner', s.tasks.byOwner); visit('tasks.done', s.tasks.done);
175
+ visit('tasks.launchable', s.tasks.launchable); visit('tasks.capacity', s.tasks.capacity);
176
+ for (const h of s.machines.hosts) visit(`machines.${h.name}`, h.load);
177
+ for (const p of s.machines.pods) visit(`pods.${p.context}`, p.occupied);
178
+ visit('machines.parents', s.machines.parents); visit('landings', s.landings);
179
+ for (const l of s.loops) visit(`loops.${l.name}`, l.beat);
180
+ return out;
181
+ }
182
+
183
+ /** 사람 표기의 한 칸 — 못 잼은 `못 잼(<사유>)`. */
184
+ export function show<T>(cell: Cell<T>, fmt: (v: T) => string): string {
185
+ if (cell.status === 'unmeasured' || cell.value === null) return `못 잼(${cell.reason ?? '사유 없음'})`;
186
+ return fmt(cell.value) + (cell.status === 'partial' && cell.reason ? ` (${cell.reason})` : '');
187
+ }
188
+
189
+ function kst(iso: string | null | undefined): string {
190
+ if (!iso) return '?';
191
+ const t = Date.parse(iso);
192
+ if (Number.isNaN(t)) return iso;
193
+ const d = new Date(t + 9 * 3600_000);
194
+ return `${String(d.getUTCMonth() + 1).padStart(2, '0')}-${String(d.getUTCDate()).padStart(2, '0')} ${String(d.getUTCHours()).padStart(2, '0')}:${String(d.getUTCMinutes()).padStart(2, '0')}`;
195
+ }
196
+
197
+ export function renderOverview(s: OverviewSnapshot, v: VerdictResult, meta: { elapsedMs: number }): string {
198
+ const lines: string[] = [];
199
+ const mark = v.verdict === 'flowing' ? '✅' : v.verdict === 'degraded' ? '⚠️' : '⛔';
200
+ lines.push(`${mark} ${VERDICT_LABEL[v.verdict]} (${v.verdict})`);
201
+ for (const r of v.reasons) lines.push(` ↳ ${r}`);
202
+ for (const a of v.nextActions) lines.push(` → 다음 행동(제안): ${a}`);
203
+ const rel = s.release;
204
+ const runText = show(rel.run, (r) => r === 'none' ? '런 없음' : `런 ${r.runId.slice(0, 8)} ${r.status} · 노드 ${r.currentNode ?? '-'} · 대기 ${r.waitingOn}${r.elapsedMinutes !== null ? ` · ${r.elapsedMinutes}분` : ''}`);
205
+ const schedText = show(rel.schedule, (x) => x === 'none' ? '컷 미정' : `컷 ${kst(x.cutAt)} · 착지 마감 ${kst(x.landBy)}${x.publishAt ? ` · 발행 ${kst(x.publishAt)}` : ''}${x.freezeNow ? ' · 동결 중' : ''}`);
206
+ const clText = show(rel.checklist, (c) => `초록 ${c.green} 노랑 ${c.yellow} 빨강 ${c.red} · P0 노랑 ${c.p0Yellow}`);
207
+ lines.push(`판 ${show(rel.version, (x) => x)} · ${runText}`);
208
+ lines.push(` ${schedText} · ${clText}`);
209
+ const owners = show(s.tasks.byOwner, (rows) => Object.entries(rows).sort().map(([o, r]) => `${o} 넘김 ${r.handed} 발사 ${r.launched}(PR ${r.prBound}) 실패 ${r.failed + r.launchFailed}`).join(' · ') || '오늘 카드 0');
210
+ lines.push(`일꾼 ${owners} · 끝 ${show(s.tasks.done, String)}`);
211
+ lines.push(` 발사 가능 ${show(s.tasks.launchable, (l) => `${l.count}${l.interim ? '(임시 술어)' : ''} · 대상 경로 없음 ${l.skipped.noTargetPath} · 선행 미완 ${l.skipped.predecessorOpen} · 이미 넘김 ${l.skipped.alreadyHanded}`)}`);
212
+ lines.push(` 빈 자리 ${show(s.tasks.capacity, (c) => `${c.free} (Pod ${c.podUsed}/${c.podTarget} · 로컬 ${c.localUsed}/${c.localMax})`)} [${s.tasks.capacity.source}]`);
213
+ const hosts = s.machines.hosts.map((h) => `${h.name} ${show(h.load, (l) => `${l.load1.toFixed(0)}/${l.cpuCount}코어 여유 ${l.freeGb.toFixed(0)}/${l.totalGb.toFixed(0)}GB (${l.ageSeconds}s 전)`)}`);
214
+ lines.push(`기계 ${hosts.join(' · ') || '못 잼(하트비트 원장 비어 있음)'}`);
215
+ lines.push(` Pod ${s.machines.pods.map((p) => `${p.context} ${show(p.occupied, String)}/${p.capacity}`).join(' · ') || '못 잼(풀 설정 없음)'}`);
216
+ lines.push(` 하니스 부모(이 기계) ${show(s.machines.parents, (p) => `${p.count} · 6h 넘음 ${p.staleOver6h}${p.oldestHours !== null ? ` · 최고 ${p.oldestHours}h` : ''}`)}`);
217
+ lines.push(`착지 ${s.since} ${show(s.landings, (l) => `${l.count}`)} [${s.landings.source}]`);
218
+ for (const l of s.loops) {
219
+ lines.push(`루프 ${l.name}${l.era === 'seat-era' ? ' [자리 시대 원천]' : ''} ${show(l.beat, (b) => `${kst(b.lastAt)} (${Math.round(b.ageSeconds / 60)}분 전)${b.alive === null ? '' : b.alive ? ' 살아 있음' : ' 프로세스 없음'}${l.staleAfterSeconds === null ? ' · 주기 미상(판정 제외)' : ''}`)}`);
220
+ }
221
+ lines.push(`우주 ${s.instanceRoot} · ${kst(s.generatedAt)} KST · ${meta.elapsedMs}ms`);
222
+ return lines.join('\n');
223
+ }
@@ -86,6 +86,9 @@ export interface GateShardsSummary {
86
86
  waitReasons: Array<{ reason: string; count: number }>;
87
87
  /** 남은 분 추정 — 계획 분이 모자라면 null(«추정 불가»). */
88
88
  etaMin: number | null;
89
+ /** 끝났지만 rc≠0 인 조각 수 — 쓰는 쪽의 `done` 은 «조각 끝»일 뿐 «통과»가 아니다(판정은 게이트 노드가 기존/새로 가른다).
90
+ * 0.2.20 컷 실측: 끝난 넷이 모두 rc 1 인데 화면이 «통과»로 읽었다. */
91
+ doneWithFailures: number;
89
92
  /** 도는 조각 중 계획 분을 가장 많이 넘긴 분(없으면 0). 넘겼으면 etaMin 은 «하한»일 뿐이라 화면이 그렇게 말한다
90
93
  * — canary 10-07 실측: 계획 0.1분 · 실제 22분 → 넘김을 안 보이면 내내 «남은 약 0분». */
91
94
  overrunMin: number;
@@ -130,6 +133,7 @@ export function summarizeShards(file: GateShardsFile, now: number = Date.now()):
130
133
  total: file.shards.length,
131
134
  counts,
132
135
  waitReasons: [...reasons].map(([reason, count]) => ({ reason, count })).sort((a, b) => b.count - a.count),
136
+ doneWithFailures: file.shards.filter((s) => s.state === 'done' && s.rc !== undefined && s.rc !== 0).length,
133
137
  etaMin: estimate(file.shards, now),
134
138
  overrunMin: Math.floor(overrun),
135
139
  staleMin: stale === null ? null : Math.floor(stale),
@@ -137,7 +141,7 @@ export function summarizeShards(file: GateShardsFile, now: number = Date.now()):
137
141
  }
138
142
 
139
143
  export const SHARD_WORD: Record<ShardState, string> = {
140
- pending: '대기', running: '돌기', done: '통과', retry: '재시도', timeout: '잘림', failed: '실패',
144
+ pending: '대기', running: '돌기', done: '끝', retry: '재시도', timeout: '잘림', failed: '실패',
141
145
  };
142
146
 
143
147
  /** 외출 알림·/release 한 줄 — «조각 24 · 돌기 5 · 대기 8(CPU 부족 8) · 잘림 2 · 통과 9 · 남은 약 40분». */
@@ -149,7 +153,8 @@ export function shardsLine(summary: GateShardsSummary): string {
149
153
  if (!n) continue;
150
154
  const why = state === 'pending' && summary.waitReasons.length
151
155
  ? `(${summary.waitReasons.slice(0, 2).map((w) => `${w.reason} ${w.count}`).join(' · ')})` : '';
152
- parts.push(`${SHARD_WORD[state]} ${n}${why}`);
156
+ const reported = state === 'done' && summary.doneWithFailures ? `(실패 보고 ${summary.doneWithFailures})` : '';
157
+ parts.push(`${SHARD_WORD[state]} ${n}${why}${reported}`);
153
158
  }
154
159
  if (summary.overrunMin >= 1) parts.push(`계획보다 ${summary.overrunMin}분 넘게 도는 중 — 남은 시간 추정 불가`);
155
160
  else parts.push(summary.etaMin === null ? '남은 시간 추정 불가(계획 분 없음)' : `남은 약 ${summary.etaMin}분`);
@@ -852,7 +852,7 @@ export async function runAskLaunchFlow(
852
852
  if (launchPreflight === false) return { kind: 'stopped-before-authoring' };
853
853
 
854
854
  await deps.beforeAuthoring?.();
855
- const authorAndPreflight = async (authorAskText: string = input.askText): Promise<{
855
+ const authorAndPreflight = async (authorAskText: string = input.askText, machineSuggestedTargetPaths: readonly string[] = []): Promise<{
856
856
  readonly goalFile: string;
857
857
  readonly decision: ReturnType<typeof decideAskPreflight>;
858
858
  }> => {
@@ -950,6 +950,8 @@ export async function runAskLaunchFlow(
950
950
  recentChangeWindowDays: input.recentChangeWindowDays,
951
951
  openPrsLimit: OPEN_PR_LIMIT,
952
952
  ...(askOutsidePathHints.length > 0 ? { askOutsidePathHints } : {}),
953
+ // AUTO-TARGET: paths injected by the candidate search (not written in the ask) — a NOT-GROUNDED goal stops on them.
954
+ ...(machineSuggestedTargetPaths.length > 0 ? { machineSuggestedTargetPaths } : {}),
953
955
  declaredPathsRoot: deps.cwd(),
954
956
  },
955
957
  preflightDeps,
@@ -1032,7 +1034,7 @@ export async function runAskLaunchFlow(
1032
1034
  }
1033
1035
  authored = await authorAndPreflight(selectedPaths.length > 0
1034
1036
  ? `대상 경로: ${selectedPaths.join(' · ')}\n${input.askText}`
1035
- : input.askText);
1037
+ : input.askText, selectedPaths);
1036
1038
  deps.log('ask-auto-reauthor', {
1037
1039
  candidatePathCount: candidates.paths.length,
1038
1040
  firstGoalFile: first.goalFile,
@@ -97,7 +97,7 @@ function inspectLaunchingTree(cwd: string): LaunchingTreeProbe {
97
97
 
98
98
  /** 발사 전 검사가 쓰는 조회 구현 — ⛔ 이 «한 벌»을 모든 표면이 공유한다. */
99
99
  export async function buildAskPreflightDeps(options: BuildAskPreflightDepsOptions = {}): Promise<AskPreflightDeps & { inspectLaunchingTree(cwd: string): LaunchingTreeProbe }> {
100
- const [{ tracedPathReferences, verbatimOriginalAsk }, { loadRunLedger, queryFederatedCompletedRunLedgers, queryFederatedInterruptedRunLedgers, queryFederatedUnfinishedRunLedgers }] = await Promise.all([
100
+ const [{ tracedPathReferences, verbatimOriginalAsk, isNotGroundedWithoutPersistentEvidence }, { loadRunLedger, queryFederatedCompletedRunLedgers, queryFederatedInterruptedRunLedgers, queryFederatedUnfinishedRunLedgers }] = await Promise.all([
101
101
  import('../self-implement/goal-author.js'),
102
102
  import('../self-implement/run-ledger.js'),
103
103
  ]);
@@ -166,6 +166,8 @@ export async function buildAskPreflightDeps(options: BuildAskPreflightDepsOption
166
166
  // ⛔ ask 표지가 없으면 빈 배열 — 그때는 표시가 «안 붙는다»(지어내지 않는다).
167
167
  askTargetPaths: (document: string) => parseAskTargetPathHints(verbatimOriginalAsk(document) ?? ''),
168
168
  askTargetPathRejections: (document: string) => parseAskTargetPathHintsResult(verbatimOriginalAsk(document) ?? '').rejected,
169
+ // AUTO-TARGET: a NOT-GROUNDED goal (no persistent evidence) is a scope card — machine-suggested paths do not launch it.
170
+ notGroundedWithoutPersistentEvidence: isNotGroundedWithoutPersistentEvidence,
169
171
  // ⭐ 충돌 문면이 경로마다 「대상/근거」를 말하게 한다 — 판정은 «안» 바꾼다(문면만 는다).
170
172
  // ⛔ TRACED PATHS 는 「바꿀 파일」과 「근거로 읽은 파일」을 섞는다(실측: TRACED 8 · 변경 3).
171
173
  // ⇒ 사람이 막혔을 때 「그게 근거였네」를 «즉시» 알 수 있어야 한다.
@@ -0,0 +1,157 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { debug } from '../debug/log.js';
3
+ import { GRAPH_SPECS, GRAPH_TEMPLATES, GRAPH_TEMPLATES_SOURCE, pipelineNodeEntryPayload, templateForGoalType, type GraphTemplate } from '../self-implement/graph-templates.js';
4
+ import type { GoalType } from '../self-implement/goal-author.js';
5
+
6
+ /** ⭐ HARNESS-FULL-GRAPH (0.2.21 · 대표 10-08) — 하니스 «여정» 전 단계를 그래프 노드로 선언·관측한다.
7
+ *
8
+ * 🩸 계기: `내부 문서 `TECH-harness-map-2026-10-06`` 차트의 단계 중 «입구·대기열·발사 관문·배치·Pod 입장·
9
+ * 이미지 동기화·자격 선갱신·동결 검사·보류·회수·실패 쪽(결과 해석·수확·heal·다음 한 수)»이
10
+ * 그래프 밖의 «맨 코드»였다. 공개 문서는 「그래프 선언이 모든 걸음을 대조한다」고 말한다 — 거짓이 되지 않게.
11
+ *
12
+ * ⛔ 이 모듈은 «관측»이다(1단계) — 흐름을 바꾸지 않는다. 진입은 기존 노드와 «같은 사건»
13
+ * (`self-implement` / `pipeline-node-entry` · 같은 `pipelineNodeEntryPayload`)으로 남겨
14
+ * 그래프 권위의 declared/undeclared 판정과 원장 질의가 이 노드들을 «같은 자»로 본다.
15
+ * 출구는 `pipeline-node-exit`(결과 `outcome` 포함)로 남긴다.
16
+ * ⛔ fail-soft — 관측이 발사·착지를 죽이지 않는다.
17
+ *
18
+ * 🔗 조인(런 id 가 없는 앞단): 입구에서 `ELANOUS_JOURNEY_KEY` 를 한 번 만들어 환경에 싣는다.
19
+ * 자식 프로세스는 그 값을 물려받고, 오케스트레이터의 모든 파이프라인 관측에도 `journeyKey` 가 실린다.
20
+ * ⇒ `journeyKey` 로 「runId 없이 남은 앞단 걸음」과 「runId 가 있는 후반 걸음」을 한 질의로 묶는다.
21
+ * 대기열을 거친 발사는 부모·자식 프로세스가 다르다 — 그 자리는 `queueLaunchId`(=`ELANOUS_HARNESS_QUEUE_LAUNCH`)가 잇는다.
22
+ * 설계 = 내부 문서 `RFC-harness-full-journey-as-graph-2026-10-08` §4. */
23
+
24
+ /** 여정 노드 — ⛔ id 는 원장의 조인 키이자 라이브 그래프 화면(HARNESS-RUN-LIVE-GRAPH)의 키다. 바꾸지 마라. */
25
+ export const JOURNEY_NODE_IDS = [
26
+ 'intake',
27
+ 'queue',
28
+ 'launch-gate',
29
+ 'dispatch',
30
+ 'pool-admit',
31
+ 'image-sync',
32
+ 'credential-refresh',
33
+ 'freeze-check',
34
+ 'hold',
35
+ 'cleanup',
36
+ 'result-parse',
37
+ 'salvage',
38
+ 'heal',
39
+ 'next-move',
40
+ 'stopped',
41
+ ] as const;
42
+ export type JourneyNodeId = typeof JOURNEY_NODE_IDS[number];
43
+
44
+ export const JOURNEY_KEY_ENV = 'ELANOUS_JOURNEY_KEY';
45
+ const QUEUE_LAUNCH_ENV = 'ELANOUS_HARNESS_QUEUE_LAUNCH';
46
+ const RUN_ID_ENV = 'ELANOUS_RUN_ID';
47
+
48
+ /** 이 여정의 조인 키 — 없으면 만들어 환경에 싣는다(자식이 물려받는다). */
49
+ export function ensureJourneyKey(env: NodeJS.ProcessEnv = process.env): string {
50
+ const existing = env[JOURNEY_KEY_ENV]?.trim();
51
+ if (existing) return existing;
52
+ const key = `jrn-${randomUUID()}`;
53
+ env[JOURNEY_KEY_ENV] = key;
54
+ return key;
55
+ }
56
+
57
+ /** 관측 자리마다 펼치는 조인 칸(있는 것만). */
58
+ export function journeyJoinFields(env: NodeJS.ProcessEnv = process.env): { journeyKey?: string; queueLaunchId?: string } {
59
+ const journeyKey = env[JOURNEY_KEY_ENV]?.trim();
60
+ const queueLaunchId = env[QUEUE_LAUNCH_ENV]?.trim();
61
+ return {
62
+ ...(journeyKey ? { journeyKey } : {}),
63
+ ...(queueLaunchId ? { queueLaunchId } : {}),
64
+ };
65
+ }
66
+
67
+ /** 여정 노드를 선언한 템플릿 — 골 종류의 템플릿이 그 노드를 선언하면 그것, 아니면 구현 루프. */
68
+ export function journeyTemplate(node: string, goalType?: GoalType | null): GraphTemplate {
69
+ const own = goalType ? templateForGoalType(goalType) : null;
70
+ if (own && own.nodes.some((n) => n.nodeId === node)) return own;
71
+ return GRAPH_TEMPLATES['self-implement'] as GraphTemplate;
72
+ }
73
+
74
+ export interface JourneyObservation {
75
+ readonly provenance: string;
76
+ readonly outcome?: string;
77
+ readonly runId?: string;
78
+ readonly goalType?: GoalType | null;
79
+ readonly data?: Readonly<Record<string, unknown>>;
80
+ }
81
+
82
+ function emit(entry: boolean, node: JourneyNodeId, o: JourneyObservation): void {
83
+ try {
84
+ const template = journeyTemplate(node, o.goalType);
85
+ const runId = o.runId ?? process.env[RUN_ID_ENV]?.trim();
86
+ const fields = {
87
+ node,
88
+ round: 0,
89
+ phase: 'journey',
90
+ provenance: o.provenance,
91
+ ...(o.outcome === undefined ? {} : { outcome: o.outcome }),
92
+ ...(runId ? { runId } : {}),
93
+ ...(o.goalType ? { goalType: o.goalType } : {}),
94
+ ...journeyJoinFields(),
95
+ ...o.data,
96
+ graphTemplatesSource: GRAPH_TEMPLATES_SOURCE,
97
+ };
98
+ // ⛔ 진입은 «공용 payload» 를 그 호출 안에서 펼친다 — 우회 가드(graph-templates.ts)가 행 단위로 문다.
99
+ if (entry) debug.log('self-implement', 'pipeline-node-entry', { ...pipelineNodeEntryPayload(template, node), ...fields });
100
+ else debug.log('self-implement', 'pipeline-node-exit', { ...pipelineNodeEntryPayload(template, node), ...fields });
101
+ } catch { /* 관측은 fail-soft */ }
102
+ }
103
+
104
+ /** 노드 진입. */
105
+ export function enterJourneyNode(node: JourneyNodeId, o: JourneyObservation): void {
106
+ emit(true, node, o);
107
+ }
108
+
109
+ /** 노드 출구 — `outcome` 이 그 노드에서 나간 간선의 라벨(YAML `map` 키)이다.
110
+ * ⭐ 그 라벨이 선언에서 종결 노드 `stopped` 로 가면 `stopped` 진입도 남긴다 — 거절·포기 런이 «선언된 종결점»에서 끝난다.
111
+ * `outcome: 'error'` = 노드 안 작업이 예외를 던졌다(예외는 호출자에게 그대로 간다). */
112
+ export function exitJourneyNode(node: JourneyNodeId, o: JourneyObservation & { readonly outcome: string }): void {
113
+ emit(false, node, o);
114
+ if (node !== 'stopped' && journeyEdgeTarget(node, o.outcome) === 'stopped') {
115
+ const { outcome: _outcome, ...rest } = o;
116
+ emit(true, 'stopped', { ...rest, provenance: `${node}:${o.outcome}` });
117
+ }
118
+ }
119
+
120
+ /** 선언(기준 YAML)에서 `from` 의 `label` 간선이 가는 노드. 없으면 undefined. */
121
+ export function journeyEdgeTarget(from: string, label: string): string | undefined {
122
+ try {
123
+ for (const edge of GRAPH_SPECS['self-implement']?.edges ?? []) {
124
+ if (edge.from === from && edge.map !== undefined && Object.hasOwn(edge.map, label)) return edge.map[label];
125
+ }
126
+ } catch { /* 관측 보조 — 실패해도 출구는 이미 남았다 */ }
127
+ return undefined;
128
+ }
129
+
130
+ /** 노드 안 작업을 감싼다 — 예외면 출구 `error` 를 남기고 다시 던진다(진입 뒤 출구가 빠지지 않게). */
131
+ export function withJourneyNode<T>(node: JourneyNodeId, o: JourneyObservation, work: () => Promise<T>): Promise<T> {
132
+ const onError = (error: unknown): void => {
133
+ exitJourneyNode(node, { ...o, outcome: 'error', data: { ...o.data, error: error instanceof Error ? error.message : String(error) } });
134
+ };
135
+ let pending: Promise<T>;
136
+ try { pending = work(); }
137
+ catch (error) { onError(error); throw error; }
138
+ // ⛔ 같은 약속을 «그대로» 돌려준다 — async/await 로 감싸면 마이크로태스크가 늘어 호출자의 순서가 밀린다
139
+ // (2판에서 self-implement-pod 시험이 그 차이를 잡았다). 관측은 곁가지(.then)로만 붙인다.
140
+ pending.then(undefined, onError);
141
+ return pending;
142
+ }
143
+
144
+ /** 동기 판. */
145
+ export function withJourneyNodeSync<T>(node: JourneyNodeId, o: JourneyObservation, work: () => T): T {
146
+ try { return work(); }
147
+ catch (error) {
148
+ exitJourneyNode(node, { ...o, outcome: 'error', data: { ...o.data, error: error instanceof Error ? error.message : String(error) } });
149
+ throw error;
150
+ }
151
+ }
152
+
153
+ /** 진입·출구를 한 번에 — 판정만 하는 노드(대기 없는 관문)용. */
154
+ export function passJourneyNode(node: JourneyNodeId, o: JourneyObservation & { readonly outcome: string }): void {
155
+ enterJourneyNode(node, o);
156
+ exitJourneyNode(node, o);
157
+ }
@@ -0,0 +1,105 @@
1
+ import { debug } from '../debug/log.js';
2
+ import { GRAPH_SPECS } from '../self-implement/graph-templates.js';
3
+ import { applyGraphOverlays, defaultOverlaysDir, loadGraphOverlays, selectOverlays, type GraphOverlaySpec } from '../self-implement/graph-overlay-yaml.js';
4
+ import type { GraphTemplateSpec } from '../self-implement/graph-yaml.js';
5
+ import { journeyJoinFields } from './graph-journey-nodes.js';
6
+
7
+ /** ⭐ HARNESS-FULL-GRAPH 2판 — 「다음 노드」를 코드가 아니라 «그래프»가 정한다.
8
+ *
9
+ * 대표 10-08: *«그래프에 따라 실제 경로가 바뀌어야 한다. 기본은 풀세트, 환경 셋업·운영 방법에 따라 일부만 걷거나 생략.»*
10
+ * ⇒ 코드는 «상태 값»(queue=queued|direct · substrate=local|pod · freeze=open|frozen …)만 낸다.
11
+ * 그 값으로 «어느 간선을 타나»는 YAML 의 `on:`/`map:`(⊕ `stage: launch` 오버레이)이 정한다.
12
+ * ⛔ 기본 YAML 은 오늘 코드와 «같은 길»을 낸다 — 시험이 그 동치를 문다(graph-journey-route.test.ts).
13
+ * ⛔ 그래프가 답을 못 내면(간선 없음·로드 실패) 코드 기본으로 간다 — 그 사실을 `source: 'code-default'` 로 남긴다.
14
+ * 🔎 관측: `self-implement` / `graph-edge-taken` { from, on, label, to, source, overlays } */
15
+
16
+ export interface JourneyRoute {
17
+ readonly from: string;
18
+ readonly on: string | null;
19
+ readonly label: string;
20
+ /** 그래프가 고른 다음 노드. 못 고르면 null. */
21
+ readonly to: string | null;
22
+ readonly source: 'graph' | 'code-default';
23
+ readonly overlays: readonly string[];
24
+ }
25
+
26
+ /** ⛔ 캐시하지 않는다 — 여러 런을 처리하는 프로세스(데몬·대기열 tick)가 다음 발사에서 오버레이 변경을 봐야 한다.
27
+ * 여정 판정은 발사당 몇 번뿐이라 디렉터리 한 번 읽기가 싸다. */
28
+ let overlaysForTesting: readonly GraphOverlaySpec[] | undefined;
29
+ function launchOverlays(): readonly GraphOverlaySpec[] {
30
+ if (overlaysForTesting !== undefined) return overlaysForTesting;
31
+ try { return loadGraphOverlays(defaultOverlaysDir()).overlays; }
32
+ catch { return []; }
33
+ }
34
+
35
+ /** 시험 전용 — 오버레이 묶음을 고정한다(undefined 면 디스크에서 읽는다). */
36
+ export function setJourneyOverlaysForTesting(overlays: readonly GraphOverlaySpec[] | undefined): void {
37
+ overlaysForTesting = overlays;
38
+ }
39
+
40
+ /** 상태 값으로 고른 «이 런의 여정 선언» — 기준 YAML ⊕ 조건이 맞는 launch 오버레이. */
41
+ export function journeySpec(
42
+ state: Readonly<Record<string, unknown>>,
43
+ base: GraphTemplateSpec | undefined = GRAPH_SPECS['self-implement'],
44
+ overlays: readonly GraphOverlaySpec[] = launchOverlays(),
45
+ ): { readonly spec: GraphTemplateSpec | undefined; readonly applied: readonly string[] } {
46
+ if (base === undefined) return { spec: undefined, applied: [] };
47
+ try {
48
+ const { applied } = selectOverlays(overlays, { graphId: base.graphId, stage: 'launch', state });
49
+ if (applied.length === 0) return { spec: base, applied: [] };
50
+ const result = applyGraphOverlays(base, applied);
51
+ // ⛔ 거절된 오버레이는 길을 바꾸지 않는다 — 기준 선언으로 간다.
52
+ return result.ok ? { spec: result.template, applied: result.overlayIds } : { spec: base, applied: [] };
53
+ } catch {
54
+ return { spec: base, applied: [] };
55
+ }
56
+ }
57
+
58
+ /** `from` 노드에서 `label` 로 나가는 다음 노드를 그래프에서 «읽는다»(기록하지 않는다).
59
+ * ⛔ 판독과 기록을 가른다 — 실제로 탄 간선은 전제 검사·실제 단계 경계 «뒤»에 `recordJourneyEdge` 로 남긴다. */
60
+ export function readJourneyEdge(
61
+ from: string,
62
+ label: string,
63
+ state: Readonly<Record<string, unknown>> = {},
64
+ deps: { readonly base?: GraphTemplateSpec; readonly overlays?: readonly GraphOverlaySpec[] } = {},
65
+ ): JourneyRoute {
66
+ const { spec, applied } = journeySpec(state, deps.base ?? GRAPH_SPECS['self-implement'], deps.overlays ?? launchOverlays());
67
+ let route: JourneyRoute = { from, on: null, label, to: null, source: 'code-default', overlays: applied };
68
+ for (const edge of spec?.edges ?? []) {
69
+ if (edge.from !== from) continue;
70
+ if (edge.map !== undefined && Object.hasOwn(edge.map, label)) {
71
+ route = { from, on: edge.on ?? null, label, to: edge.map[label]!, source: 'graph', overlays: applied };
72
+ break;
73
+ }
74
+ }
75
+ return route;
76
+ }
77
+
78
+ /** 실제로 탄 간선을 남긴다 — `taken` 은 실제 다음 노드(그래프 답과 다르면 `honored: false` · 그 이유를 `reason` 에). */
79
+ export function recordJourneyEdge(route: JourneyRoute, taken: string, reason?: string): void {
80
+ try {
81
+ debug.log('self-implement', 'graph-edge-taken', {
82
+ graphId: 'self-implement', ...route, taken, honored: route.to === null || route.to === taken,
83
+ ...(reason === undefined ? {} : { reason }), ...journeyJoinFields(),
84
+ });
85
+ } catch { /* 관측은 fail-soft */ }
86
+ }
87
+
88
+ /** 판독 ⊕ 그 자리에서 기록 — 판독 자리가 곧 실제 단계 경계일 때만 쓴다. */
89
+ export function routeJourneyEdge(
90
+ from: string,
91
+ label: string,
92
+ state: Readonly<Record<string, unknown>> = {},
93
+ deps: { readonly base?: GraphTemplateSpec; readonly overlays?: readonly GraphOverlaySpec[] } = {},
94
+ ): JourneyRoute {
95
+ const route = readJourneyEdge(from, label, state, deps);
96
+ recordJourneyEdge(route, route.to ?? label);
97
+ return route;
98
+ }
99
+
100
+ /** 입구의 대기열 갈래 — 그래프가 고른 다음 노드로 정한다. 그래프가 답을 못 내면 코드 라벨을 따른다.
101
+ * ⛔ `eligible`(자리 있음 · 대기열 자식 아님 · --no-queue 아님)은 그래프가 못 넘는 전제다. */
102
+ export function intakeTakesQueue(label: 'queued' | 'direct', route: Pick<JourneyRoute, 'source' | 'to'>, eligible: boolean): boolean {
103
+ const graphQueues = route.source === 'graph' ? route.to === 'queue' : label === 'queued';
104
+ return graphQueues && eligible;
105
+ }