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
@@ -0,0 +1,63 @@
1
+ // LOOPCHECK-FENCE (0.2.20): an hq-fence cron execution is one process, not a loop.
2
+ // Each wrapped run leaves its rc in a per-role streak file; the wrapper's owner alert
3
+ // raises a seat request only when the same role failed in consecutive runs, keyed per
4
+ // role so the seat request journal holds at most one `loopcheck:hq-fence:<role>` per day.
5
+ // No loops/ import here: hq.ts records through this module and loops/registry imports hq.ts.
6
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
7
+ import { dirname, join } from 'node:path';
8
+ import { Database } from 'bun:sqlite';
9
+ import { debug } from '../debug/log.js';
10
+
11
+ /** The role is the file name; `lastAt` is shown in the owner alert so TC sees when the streak last grew. */
12
+ export interface FenceOutcome { consecutiveFailures: number; lastRc: number; lastAt: string }
13
+
14
+ /** Two failures in a row: one rc=1 from a job that recovers on the next fire is not a fence fault. */
15
+ export const FENCE_FAILURE_STREAK = 2;
16
+
17
+ const safeRole = (role: string): string => role.replace(/[^\w.-]/g, '_') || 'unknown';
18
+
19
+ export function fenceOutcomePath(dir: string, role: string): string {
20
+ return join(dir, `${safeRole(role)}.json`);
21
+ }
22
+
23
+ /** Default directory next to the host-local lease view (`<config>/hq/fence-outcomes`). */
24
+ export function fenceOutcomeDir(localPath: string): string {
25
+ return join(dirname(localPath), 'fence-outcomes');
26
+ }
27
+
28
+ export function readFenceOutcome(dir: string, role: string): FenceOutcome | null {
29
+ try {
30
+ const parsed = JSON.parse(readFileSync(fenceOutcomePath(dir, role), 'utf8')) as Partial<FenceOutcome>;
31
+ return typeof parsed.consecutiveFailures === 'number' ? parsed as FenceOutcome : null;
32
+ } catch (error) {
33
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null;
34
+ debug.log('hq.fence-outcome', 'read-failed', { role, error: String(error) });
35
+ return null;
36
+ }
37
+ }
38
+
39
+ /** rc 0 resets the streak; any other rc extends it. A skipped (not-holder) run is not recorded.
40
+ * Overlapping runs of one role serialize the read-modify-write on an SQLite lock (released even on SIGKILL). */
41
+ export function recordFenceOutcome(dir: string, role: string, rc: number, now: Date = new Date()): FenceOutcome {
42
+ const path = fenceOutcomePath(dir, role);
43
+ mkdirSync(dirname(path), { recursive: true });
44
+ const lock = new Database(`${path}.lock.sqlite`);
45
+ try {
46
+ lock.exec('PRAGMA busy_timeout = 10000');
47
+ lock.exec('BEGIN EXCLUSIVE');
48
+ try {
49
+ const previous = readFenceOutcome(dir, role);
50
+ const outcome: FenceOutcome = { consecutiveFailures: rc === 0 ? 0 : (previous?.consecutiveFailures ?? 0) + 1, lastRc: rc, lastAt: now.toISOString() };
51
+ const tmp = `${path}.${process.pid}.tmp`;
52
+ writeFileSync(tmp, JSON.stringify(outcome));
53
+ renameSync(tmp, path);
54
+ lock.exec('COMMIT');
55
+ return outcome;
56
+ } catch (error) { lock.exec('ROLLBACK'); throw error; }
57
+ } finally { lock.close(); }
58
+ }
59
+
60
+ /** `hq-fence: fence failed (rc=1, role=seat-loop)` → `seat-loop`; other wrapper reasons have no role. */
61
+ export function fenceAlertRole(reason: string): string | null {
62
+ return /\brole=([\w.-]+)/.exec(reason)?.[1] ?? null;
63
+ }
@@ -3,6 +3,9 @@ import { chmodSync, constants, copyFileSync, lstatSync, mkdirSync, mkdtempSync,
3
3
  import { dirname, isAbsolute, join } from 'node:path';
4
4
  import { homedir } from 'node:os';
5
5
  import { createPatch } from 'diff';
6
+ import { debug } from '../debug/log.js';
7
+ import { notifyOwners } from '../loops/checker.js';
8
+ import { FENCE_FAILURE_STREAK, fenceAlertRole, readFenceOutcome } from './fence-outcomes.js';
6
9
 
7
10
  const quote = (value: string): string => `'${value.replaceAll("'", "'\"'\"'")}'`;
8
11
 
@@ -121,3 +124,33 @@ export function installHqFenceWrapper(configDir: string, body: string, yes: bool
121
124
  } finally { rmSync(tempDir, { recursive: true, force: true }); }
122
125
  return { path, preview, ...(backup ? { backup } : {}) };
123
126
  }
127
+
128
+ export interface FenceAlertDeps { root: string; outcomeDir: string; now?: Date }
129
+
130
+ /**
131
+ * The wrapper's owner alert. Returns the number of seat requests queued (0 or 1).
132
+ * - `fence failed (rc, role)`: queued only when that role's streak reached FENCE_FAILURE_STREAK
133
+ * (an unknown streak is not read as «first failure» — it is queued, still capped per role/day).
134
+ * - Any other wrapper failure (entry missing, bun missing, role missing): queued under `hq-fence:wrapper`.
135
+ */
136
+ export function hqFenceAlert(reason: string, deps: FenceAlertDeps): number {
137
+ const now = deps.now ?? new Date();
138
+ const fenceFailure = /fence failed \(rc=/.test(reason);
139
+ const role = fenceAlertRole(reason);
140
+ let streak: number | null = null;
141
+ let lastAt: string | undefined;
142
+ if (fenceFailure && role) {
143
+ const outcome = readFenceOutcome(deps.outcomeDir, role);
144
+ streak = outcome?.consecutiveFailures ?? null;
145
+ lastAt = outcome?.lastAt;
146
+ if (streak !== null && streak < FENCE_FAILURE_STREAK) {
147
+ debug.log('hq.fence-wrapper', 'alert-suppressed', { role, streak, threshold: FENCE_FAILURE_STREAK, reason });
148
+ return 0;
149
+ }
150
+ }
151
+ const id = `hq-fence:${fenceFailure && role ? role : 'wrapper'}`;
152
+ const text = streak !== null ? `${reason} · ${streak} consecutive runs (last ${lastAt ?? 'unknown'})` : reason;
153
+ const added = notifyOwners([{ id, owner: 'TC', enabled: true, registered: true, state: 'failing', reason: text }], { root: deps.root, now });
154
+ debug.log('hq.fence-wrapper', added ? 'alert-queued' : 'alert-deduplicated', { id, role, streak, reason });
155
+ return added;
156
+ }
package/src/hq/hq.ts CHANGED
@@ -10,6 +10,7 @@ import { debug } from '../debug/log.js';
10
10
  import { DEFAULT_NEXUS_HTTP_PORT } from '../nexus/default-port.js';
11
11
  import { getElanousConfigDir } from '../elanous-config-dir.js';
12
12
  import { getUserConfig, type HqConfig, type HqFenceRole } from '../user-config.js';
13
+ import { fenceOutcomeDir, recordFenceOutcome } from './fence-outcomes.js';
13
14
  import { promoteHq, proposeHqPromotion, writeOpSeatRequest } from './promote.js';
14
15
  import type { HqOpRequestWriter, PromoteResult } from './promote.js';
15
16
  import {
@@ -42,6 +43,8 @@ export interface HqDeps {
42
43
  /** Holder/standby views and the arbiter's check use this tailnet probe — never ssh (OP 10-04 09:04). */
43
44
  probe?: HostProbe;
44
45
  localPath?: string;
46
+ /** Per-role streak files of wrapped-run rc (LOOPCHECK-FENCE · default next to localPath). */
47
+ outcomeDir?: string;
45
48
  /** Host-local identity, deliberately outside the replicated config/state directory. */
46
49
  hostPath?: string;
47
50
  /** Seen-generation marker (default hq.seenGenerationFile · ~/.elanous-hq/seen-generation). */
@@ -391,5 +394,12 @@ export function hqFenceRun(role: HqFenceRole, command: string[], deps: HqDeps =
391
394
  const [cmd, ...args] = command;
392
395
  if (!cmd) throw new Error('hq fence: command required after --');
393
396
  const r = spawnSync(cmd, args, { stdio: 'inherit', env: { ...process.env, ELANOUS_HQ_GENERATION: String(decided.generation ?? '') } });
394
- return r.status ?? 1;
397
+ const rc = r.status ?? 1;
398
+ // LOOPCHECK-FENCE: the wrapper's alert reads this streak, so a lone failure is not a seat request.
399
+ try {
400
+ const dir = deps.outcomeDir ?? fenceOutcomeDir(deps.localPath ?? join(getElanousConfigDir(), 'hq', 'local.json'));
401
+ const outcome = recordFenceOutcome(dir, role, rc, new Date(deps.now?.() ?? Date.now()));
402
+ if (rc !== 0) debug.log('hq.fence', 'run-failed', { role, rc, consecutiveFailures: outcome.consecutiveFailures });
403
+ } catch (error) { try { debug.log('hq.fence', 'outcome-record-failed', { role, rc, error: String(error) }); } catch { /* fail-soft */ } }
404
+ return rc;
395
405
  }
package/src/index.ts CHANGED
@@ -7,6 +7,7 @@ import type { SelfDevRunParticipant, SelfDevRunState } from './self-dev/run-stor
7
7
  import { applyConfigDirFlagFromArgv } from './cli/config-dir-flag.js';
8
8
  import { LOGS_SINCE_OPTION, registerLogsCommands } from './cli/logs-cli.js';
9
9
  import { registerDocsCommands } from './cli/docs-cli.js';
10
+ import { registerRetroCommands } from './cli/retro-cli.js';
10
11
  import { registerA2ACommands } from './cli/a2a-cli.js';
11
12
  import { registerPluginCommands } from './cli/plugin-cli.js';
12
13
  import { registerMakeCommand } from './cli/make-cli.js';
@@ -219,13 +220,14 @@ program.enablePositionalOptions();
219
220
 
220
221
  program.command('preview <target>')
221
222
  .description('Isolated PR/branch-head TUI + PWA preview; publish the TUI-COMFORT friction table to a PR')
222
- .option('--scenario <id>', 'TUI-COMFORT scenario (default S2)', 'S2')
223
+ .option('--scenario <id>', 'TUI-COMFORT scenario IDs (comma-separated; default S2)', 'S2')
223
224
  .option('--flag <path>', 'Enable an existing config feature flag in main-based preview (repeatable)', (value: string, all: string[]) => [...all, value], [] as string[])
225
+ .option('--bundle-pr <number>', 'Merge PR head into isolated main preview (repeatable; no flags)', (value: string, all: number[]) => [...all, Number(value)], [] as number[])
224
226
  .option('--comment-pr <number>', 'PR to receive a branch/main preview report', (value: string) => Number(value))
225
- .action(async (target: string, opts: { scenario: string; flag: string[]; commentPr?: number }) => {
227
+ .action(async (target: string, opts: { scenario: string; flag: string[]; bundlePr: number[]; commentPr?: number }) => {
226
228
  try {
227
229
  const { runPrPreview } = await import('./self-dev/pr-preview.js');
228
- const result = await runPrPreview({ target, repoRoot: process.cwd(), scenario: opts.scenario, flags: opts.flag, ...(opts.commentPr === undefined ? {} : { commentPr: opts.commentPr }) });
230
+ const result = await runPrPreview({ target, repoRoot: process.cwd(), scenario: opts.scenario, flags: opts.flag, bundlePrs: opts.bundlePr, ...(opts.commentPr === undefined ? {} : { commentPr: opts.commentPr }) });
229
231
  console.log(`preview ${result.status} · ${result.head} · PR #${result.prNumber} · comment posted`);
230
232
  if (result.status !== 'passed') process.exitCode = 1;
231
233
  } catch (error) {
@@ -1059,10 +1061,12 @@ fenceWrapperCmd.command('install').description('래퍼 제안 또는 백업 후
1059
1061
  fenceWrapperCmd.command('alert <reason>').description('HQ 울타리 실패를 기존 루프 소유자 요청 원장에 적재')
1060
1062
  .action(async (reason: string) => {
1061
1063
  try {
1062
- const { notifyOwners } = await import('./loops/checker.js');
1064
+ // LOOPCHECK-FENCE: keyed per role (not per pid) and only after consecutive failures of that role.
1065
+ const { fenceOutcomeDir } = await import('./hq/fence-outcomes.js');
1066
+ const { hqFenceAlert } = await import('./hq/fence-wrapper.js');
1063
1067
  const { getElanousConfigDir } = await import('./elanous-config-dir.js');
1064
- notifyOwners([{ id: `hq-fence:${Date.now()}:${process.pid}`, owner: 'TC', enabled: true, registered: true,
1065
- state: 'failing', reason }], { root: getElanousConfigDir() });
1068
+ const root = getElanousConfigDir();
1069
+ hqFenceAlert(reason, { root, outcomeDir: fenceOutcomeDir(_joinPath(root, 'hq', 'local.json')) });
1066
1070
  } catch (error) { console.error(`hq fence-wrapper alert: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; }
1067
1071
  });
1068
1072
  hqCmd.command('fence-audit').description('운영 crontab 의 HQ 울타리 밖 쓰기 잡 목록과 감싼 줄 제안(읽기 전용)')
@@ -1905,6 +1909,7 @@ program
1905
1909
  });
1906
1910
 
1907
1911
  registerAutopilotCommands(program);
1912
+ registerRetroCommands(program);
1908
1913
 
1909
1914
  // ── memory ──
1910
1915
  const memCmd = program.command('memory').description('Persistent memories injected into every chat turn (user / feedback / project / reference)');
@@ -4460,8 +4465,10 @@ selfCmd
4460
4465
  };
4461
4466
  try {
4462
4467
  const { dispatchHeavyCheck } = await import('./self-implement/gate-remote.js');
4463
- const { execFileSync } = await import('node:child_process');
4464
- const repo = execFileSync('git', ['rev-parse', '--show-toplevel'], { encoding: 'utf8' }).trim();
4468
+ const { runGitCommand } = await import('./git-fs/runner.js');
4469
+ const topLevel = runGitCommand(process.cwd(), ['rev-parse', '--show-toplevel'], { encoding: 'utf8' });
4470
+ if (topLevel.status !== 0) throw new Error(`git rev-parse --show-toplevel failed: ${topLevel.stderr.trim() || `rc=${topLevel.status}`}`);
4471
+ const repo = topLevel.stdout.trim();
4465
4472
  // Only a committed --base range is reproducible on another host: the default mode measures uncommitted files, --pr
4466
4473
  // reads files through gh on this host, and the remote checkout runs from the repository root — those stay local.
4467
4474
  const { realpathSync } = await import('node:fs');
@@ -4886,7 +4893,7 @@ const selfOrchestrateCmd = selfCmd
4886
4893
  // ☸️ 풀이 «원격 노드뿐»이면 이 기계의 이미지는 아무도 안 쓴다 — 로컬 판정·굽기를 건너뛰고 기준 판 = HEAD 로 노드 동기화만 한다.
4887
4894
  // 🩸 2026-09-26 실측: 원격 전용 풀인데 로컬 이미지가 없다고 로컬에서 굽다가 로컬 클러스터(elanous-h1)가 없어 런이 시작도 전에 죽었다.
4888
4895
  const remoteOnlyPool = poolMembers.length > 0 && poolMembers.every((m) => Boolean(m.sshHost));
4889
- const headCommit = remoteOnlyPool ? ((await import('node:child_process')).spawnSync('git', ['rev-parse', 'HEAD'], { encoding: 'utf8' }).stdout?.trim() || null) : null;
4896
+ const headCommit = remoteOnlyPool ? ((await import('./git-fs/runner.js')).runGitCommand(process.cwd(), ['rev-parse', 'HEAD'], { encoding: 'utf8' }).stdout.trim() || null) : null;
4890
4897
  let image = remoteOnlyPool
4891
4898
  ? { imageCommit: headCommit, headCommit, fresh: true, reason: 'remote-only pool — 노드 쪽에서 맞춘다' }
4892
4899
  : podImageFreshness();
@@ -4896,7 +4903,8 @@ const selfOrchestrateCmd = selfCmd
4896
4903
  } else {
4897
4904
  if (!opts.json) ui.info(`[pod] 이미지 다시 굽기 — ${image.reason} (~1분)`);
4898
4905
  const { spawnSync } = await import('node:child_process');
4899
- const top = spawnSync('git', ['rev-parse', '--show-toplevel'], { encoding: 'utf8' }).stdout.trim() || process.cwd();
4906
+ const { runGitCommand } = await import('./git-fs/runner.js');
4907
+ const top = runGitCommand(process.cwd(), ['rev-parse', '--show-toplevel'], { encoding: 'utf8' }).stdout.trim() || process.cwd();
4900
4908
  // bun 1.4 applies the 1 MiB default maxBuffer: a docker build log past it kills the child (ENOBUFS · status null).
4901
4909
  const b = spawnSync('bash', [`${top}/docker/harness/build.sh`], { encoding: 'utf8', timeout: 900_000, maxBuffer: 64 * 1024 * 1024 });
4902
4910
  if (b.status !== 0) { ui.error(`--substrate pod: 이미지 굽기 실패 rc=${b.status}${b.error ? ` (${(b.error as NodeJS.ErrnoException).code ?? b.error.message})` : ''}: ${(b.stdout + b.stderr).slice(-400)}`); process.exit(2); }
@@ -7679,6 +7687,11 @@ configCmd
7679
7687
  .description('Print all config or one dotted path, e.g. llm.provider (secrets redacted by default)')
7680
7688
  .option('--reveal', 'Print unredacted output and record the reveal')
7681
7689
  .action(async (path: string | undefined, opts: { reveal?: boolean }) => {
7690
+ const { resolveCurrentInstance } = await import('./instance/current.js');
7691
+ const instance = resolveCurrentInstance();
7692
+ if (instance.kind === 'test' && instance.layer === 'tree-derived') {
7693
+ console.error('⚠️ test 우주의 config를 읽습니다 — 운영 값은 --config-dir ~/.elanous');
7694
+ }
7682
7695
  const cfg = getUserConfig() as unknown as Record<string, unknown>;
7683
7696
  const value = path ? getConfigPath(cfg, path) : cfg;
7684
7697
  if (value === undefined) {
@@ -7773,6 +7786,59 @@ configCmd
7773
7786
  ui.info(`unset ${path}`);
7774
7787
  });
7775
7788
 
7789
+ // ── config drift / explain (CFG-STORE1 · 0.2.20 · 2026-10-08) — 읽기 전용 «유효 config ⊕ 출처» ──
7790
+ // 집(~/.elanous)과 파생 시험 우주(<트리>/.elanous-test) config 사본의 드리프트를 한 명령으로.
7791
+ // ⛔ 쓰기 0 · 못 읽음 ≠ 같음 · 비밀 값 가림. 계산 = src/cli/config-drift.ts.
7792
+ async function configDriftDiscover(o: { registry?: string | boolean; scanRoot?: string[]; treeScan?: boolean }) {
7793
+ const { discoverUniverses } = await import('./cli/config-drift.js');
7794
+ return discoverUniverses({
7795
+ ...(o.registry === false ? { registryPath: null } : typeof o.registry === 'string' ? { registryPath: o.registry } : {}),
7796
+ ...(o.treeScan === false ? { treeScanRoots: [] } : o.scanRoot?.length ? { treeScanRoots: o.scanRoot } : {}),
7797
+ });
7798
+ }
7799
+
7800
+ configCmd
7801
+ .command('drift')
7802
+ .description('읽기 전용 — 운영 config 와 파생 시험 우주(.elanous-test) config 사본이 다른 키 표 (CFG-STORE1)')
7803
+ .option('--key <key>', '이 키(또는 그 아래)만')
7804
+ .option('--json', 'JSON 출력')
7805
+ .option('--limit <n>', '표시할 키 수 (0=전부 · 기본 50)', '50')
7806
+ .option('--registry <path>', '인스턴스 레지스트리 경로 (기본 ~/.elanous/logs/instances.json)')
7807
+ .option('--scan-root <dir...>', '명시 트리 뿌리 (기본 = config-drift.ts defaultTreeScanRoots)')
7808
+ .option('--no-tree-scan', '트리 뿌리 훑기 끔 — 레지스트리만')
7809
+ .option('--no-registry', '레지스트리 끔 — 명시 트리 뿌리(자리·작업 트리)만')
7810
+ .option('--include-expected', 'sync-test 의 test-safe 변환(비밀 제거·발송 끔)과 _test 메타 키 차이도 드리프트로 센다')
7811
+ .action(async (o: { key?: string; json?: boolean; limit: string; includeExpected?: boolean; registry?: string | boolean; scanRoot?: string[]; treeScan?: boolean }) => {
7812
+ const { computeDrift, renderDrift } = await import('./cli/config-drift.js');
7813
+ const report = computeDrift(await configDriftDiscover(o), { key: o.key, includeExpected: o.includeExpected });
7814
+ debug.log('config.drift', 'computed', {
7815
+ key: o.key ?? null, rows: report.rows.length, prodStatus: report.prodStatus, ...report.universes,
7816
+ });
7817
+ debug.flush();
7818
+ console.log(o.json ? JSON.stringify(report, null, 2) : renderDrift(report, { limit: Number(o.limit) || 0 }));
7819
+ if (report.prodStatus !== 'ok') process.exit(1);
7820
+ });
7821
+
7822
+ configCmd
7823
+ .command('explain <key>')
7824
+ .description('읽기 전용 — 운영 값 ⊕ 출처 층 ⊕ 파생 시험 우주별 값 (CFG-STORE1)')
7825
+ .option('--json', 'JSON 출력')
7826
+ .option('--registry <path>', '인스턴스 레지스트리 경로')
7827
+ .option('--scan-root <dir...>', '명시 트리 뿌리')
7828
+ .option('--no-tree-scan', '트리 뿌리 훑기 끔 — 레지스트리만')
7829
+ .option('--no-registry', '레지스트리 끔 — 명시 트리 뿌리만')
7830
+ .action(async (key: string, o: { json?: boolean; registry?: string | boolean; scanRoot?: string[]; treeScan?: boolean }) => {
7831
+ const { computeExplain, renderExplain } = await import('./cli/config-drift.js');
7832
+ const { buildUserConfig } = await import('./user-config.js');
7833
+ const report = computeExplain(await configDriftDiscover(o), key, {
7834
+ codeDefaults: () => buildUserConfig('/nonexistent/elanous-config-drift/config.json'),
7835
+ });
7836
+ debug.log('config.explain', 'computed', { key, layer: report.prod.layer, ...report.universes });
7837
+ debug.flush();
7838
+ console.log(o.json ? JSON.stringify(report, null, 2) : renderExplain(report));
7839
+ if (report.prod.layer === 'prod-unreadable') process.exit(1);
7840
+ });
7841
+
7776
7842
  // ── config 격리 sync (ISO-1 · 2026-07-13) — 운영→테스트 물질화 동기화 ──
7777
7843
  // 격리 테스트 인스턴스는 운영 config 를 공유하지 않는다(overlay 은퇴 방향).
7778
7844
  configCmd
@@ -125,6 +125,19 @@ export function buildIntakeDigest(root: string, day: string, readFile: (p: strin
125
125
  };
126
126
  }
127
127
 
128
+ function digestNoteRef(e: DigestEntry): string {
129
+ return e.noteName ? `[[${e.noteName}]]` : e.note?.trim() || e.url?.trim() || e.id;
130
+ }
131
+
132
+ /** 참고 줄에 이름을 다는 상한 — 많은 날에도 화면 한 장을 지킨다(M 은 그대로 센다). */
133
+ const DIGEST_REFERENCE_NAMED = 10;
134
+
135
+ function digestReferenceLine(entries: DigestEntry[]): string {
136
+ const named = entries.slice(0, DIGEST_REFERENCE_NAMED).map(digestNoteRef).join(', ');
137
+ const rest = entries.length - DIGEST_REFERENCE_NAMED;
138
+ return `참고 ${entries.length}편 — 노트: ${named}${rest > 0 ? ` 외 ${rest}편 · 원장 \`elanous intake items\`` : ''}`;
139
+ }
140
+
128
141
  /** 노트 절 — 축별 묶음 · 노트 링크 · 한 줄 요약 · 골 후보. */
129
142
  export function renderDigestMarkdown(d: IntakeDigest): string {
130
143
  const L: string[] = [`## 📰 오늘의 흡수 요약 (${d.absorbed.length})`, ''];
@@ -165,14 +178,9 @@ export function renderDigestMarkdown(d: IntakeDigest): string {
165
178
 
166
179
  /** 텔레그램 — 흡수 렌즈가 대조한 항목만 SCQA 짧은 판으로 낸다. */
167
180
  export function renderDigestTelegram(d: IntakeDigest, _opts: { vaultRoot?: string; notePath?: string } = {}): string {
168
- if (d.seat && !d.absorbed.length) return '오늘 새 소식 없음';
169
- if (d.seat) return [
170
- `흡수 ${d.absorbed.length}`,
171
- ...d.absorbed.map((item) => `- ${item.oneLiner ?? item.noteName ?? item.id}${item.url ? ` · ${item.url}` : ''}`),
172
- ].join('\n');
173
181
  // «Touching» is decided by the lens verdict alone; a missing note summary only changes how S reads (ACP must-fix).
174
182
  const touching = d.absorbed.filter((e) => e.impact);
175
- const L = [`흡수 ${d.absorbed.length} → 우리에게 닿는 것 ${touching.length}`];
183
+ const L = [`흡수 ${d.absorbed.length}편 → 우리에게 닿는 것 ${touching.length}`];
176
184
  // 관심 뉴스는 최대 셋 — 기사마다 S(요약 첫 줄) · A(엘라누스 함의 첫 줄) · 링크를 한 덩어리로(NEWS-INTAKE).
177
185
  for (const article of (d.news ?? []).slice(0, 3)) {
178
186
  L.push(`📰 ${article.title}`);
@@ -181,9 +189,7 @@ export function renderDigestTelegram(d: IntakeDigest, _opts: { vaultRoot?: strin
181
189
  L.push(article.url);
182
190
  }
183
191
  if (d.shadowSuggestions?.length) L.push(`뉴스 칸 제안 ${d.shadowSuggestions.length}건 (그림자·판 미등록): ${d.shadowSuggestions.slice(0, 3).map((s) => `[${s.verdict}] ${s.fact}${s.url ? ` ${s.url}` : ''}`).join(' · ')}`);
184
- if (!touching.length) return L.join('\n');
185
- // At most three items are shown, each as its own S·C·A·link block so context and source stay paired;
186
- // a later item sharing (verdict, target) says «위와 같은 행동» instead of repeating the A sentence (ACP must-fix).
192
+ // At most three items are shown, each as its own S·C·A·note block so context and source stay paired.
187
193
  // S must be the note's own summary: items without one still count as touching but are not shown (ACP must-fix).
188
194
  const shownItems = touching.filter((e) => e.oneLiner?.trim()).slice(0, 3);
189
195
  const actionSeen = new Set<string>();
@@ -205,10 +211,11 @@ export function renderDigestTelegram(d: IntakeDigest, _opts: { vaultRoot?: strin
205
211
  `S 무엇: ${e.oneLiner?.trim() || '노트 한 줄 요약 없음'}`,
206
212
  `C 우리에게 왜: ${impact.why}`,
207
213
  `A 그래서 무엇을 하나: ${action}`,
208
- `🔗 원문 링크: ${e.url?.trim() || '링크 없음'}`,
214
+ `🔗 노트: ${digestNoteRef(e)}`,
209
215
  );
210
216
  }
211
217
  if (touching.length > shownItems.length) L.push('', `닿는 것 ${touching.length - shownItems.length}건 더(요약 없는 것 포함) · 원장 \`elanous intake items\``);
212
- if (d.absorbed.length > touching.length) L.push('', `그 밖 ${d.absorbed.length - touching.length}건 · 참고`);
218
+ const reference = d.absorbed.filter((e) => !e.impact);
219
+ if (reference.length) L.push('', digestReferenceLine(reference));
213
220
  return L.join('\n');
214
221
  }
@@ -16,10 +16,11 @@ export const FEATURE_MATURITY = {
16
16
  '/today': { pwa: 'stable' },
17
17
  '/term': { pwa: 'stable' },
18
18
  '/live': { pwa: 'stable' },
19
+ '/live-run': { pwa: 'beta' }, // HARNESS-RUN-LIVE-GRAPH (0.2.21) — 하니스 런 하나가 도는 장면
19
20
  '/market': { pwa: 'stable' },
20
21
  '/sessions': { pwa: 'stable' },
21
22
  '/settings': { pwa: 'beta' },
22
- '/editor': { pwa: 'beta' },
23
+ '/editor': { pwa: 'stable' }, // EDITOR-NAV-PUBLIC (0.2.21) — 데모·0순위 피처: 역할을 안 고른 기기에도 «만들기 › 편집기»
23
24
  '/intake': { pwa: 'beta' },
24
25
  '/trace': { pwa: 'beta' },
25
26
  '/inside': { pwa: 'beta' },
@@ -74,7 +75,7 @@ export const FEATURE_MATURITY = {
74
75
  loops: 'beta', buzz: 'beta', harness: 'stable', self: 'beta', factcheck: 'beta',
75
76
  provider: 'stable', 'provider:set': 'beta', 'provider:restore': 'beta',
76
77
  'provider:rotate': 'beta', 'provider:use': 'beta', 'status-bar': 'beta',
77
- schedule: 'beta', guardian: 'beta', intake: 'beta', logs: 'stable', ad: 'beta', docs: 'beta',
78
+ schedule: 'beta', guardian: 'beta', intake: 'beta', logs: 'stable', retro: 'beta', ad: 'beta', docs: 'beta',
78
79
  decisions: 'beta', directives: 'beta', claims: 'beta', lesson: 'beta', fleet: 'beta', ops: 'stable', outputs: 'beta', publish: 'beta',
79
80
  env: 'beta', 'agent-mission': 'beta', dev: 'beta', keys: 'beta', finance: 'beta',
80
81
  config: 'stable', tasks: 'stable', brief: 'beta', notify: 'beta', away: 'beta', labels: 'beta', msg: 'beta', ask: 'stable',
@@ -160,7 +161,7 @@ export const FEATURE_MATURITY = {
160
161
  ping: 'stable', provider: 'stable', sessions: 'stable', fork: 'stable', resume: 'stable',
161
162
  decisions: 'stable', work: 'stable', cancel: 'stable',
162
163
  skills: 'beta', skill: 'beta', digest: 'beta', intake: 'beta', ad: 'beta',
163
- taste: 'beta', missions: 'beta', attach: 'beta', detach: 'beta', now: 'beta', loops: 'beta', release: 'beta', away: 'beta', project: 'beta', wish: 'beta',
164
+ taste: 'beta', missions: 'beta', attach: 'beta', detach: 'beta', now: 'beta', loops: 'beta', runs: 'beta', release: 'beta', away: 'beta', project: 'beta', wish: 'beta',
164
165
  brain: 'tool', cc: 'tool', cdx: 'tool', gem: 'tool', local: 'tool',
165
166
  harness: 'ops',
166
167
  // Existing commands outside the CXO four-grade menu list are owner-only.
@@ -0,0 +1,114 @@
1
+ import { join } from 'node:path';
2
+ import { debug } from '../../debug/log.js';
3
+ import type { DraftMetrics } from '../../self-dev/draft-sweep.js';
4
+
5
+ /**
6
+ * DRAFT-METRIC — CEO view source. Opening the page must never trigger a GitHub read:
7
+ * `read()` answers from memory and at most one background collection runs at a time,
8
+ * no more often than the TTL (or the failure back-off). The collection itself is
9
+ * `elanous harness drafts metrics --json` in a child process, which reads PR evidence
10
+ * through the batched GraphQL path (#24918) — one call per 40 drafts, no per-draft comments call.
11
+ */
12
+ export type DraftMetricsState = 'measuring' | 'ready' | 'unavailable';
13
+
14
+ export interface DraftMetricsSnapshot {
15
+ /** measuring = no value yet and a collection is running · unavailable = no value and the last collection failed. */
16
+ readonly state: DraftMetricsState;
17
+ readonly metrics: DraftMetrics | null;
18
+ /** When the served value was collected (ISO). null without a value. */
19
+ readonly measuredAt: string | null;
20
+ /** A background collection is in flight. */
21
+ readonly refreshing: boolean;
22
+ /** Last collection failure; with a value it means the value is older than intended. */
23
+ readonly reason: string | null;
24
+ }
25
+
26
+ export interface DraftMetricsSourceOptions {
27
+ readonly collect?: () => Promise<DraftMetrics>;
28
+ readonly ttlMs?: number;
29
+ readonly failureRetryMs?: number;
30
+ readonly now?: () => number;
31
+ }
32
+
33
+ export const DRAFT_METRICS_TTL_MS = 10 * 60_000;
34
+ export const DRAFT_METRICS_FAILURE_RETRY_MS = 2 * 60_000;
35
+ const COLLECT_TIMEOUT_MS = 5 * 60_000;
36
+
37
+ function isMetrics(value: unknown): value is DraftMetrics {
38
+ if (!value || typeof value !== 'object') return false;
39
+ const row = value as Record<string, unknown>;
40
+ const count = (key: string) => Number.isInteger(row[key]) && (row[key] as number) >= 0;
41
+ const nullableNumber = (key: string) => row[key] === null || (typeof row[key] === 'number' && Number.isFinite(row[key]));
42
+ return count('inventory') && count('needsOwner') && count('converted48h') && count('cohort48h')
43
+ && nullableNumber('oldestAgeHours') && nullableNumber('conversion48h');
44
+ }
45
+
46
+ /** Parses the CLI's single JSON line; anything else is a failed collection, never a zero. */
47
+ export function parseDraftMetricsOutput(stdout: string): DraftMetrics {
48
+ const line = stdout.trim().split('\n').reverse().find((text) => text.trim().startsWith('{'));
49
+ if (!line) throw new Error('draft metrics: no JSON output');
50
+ const parsed: unknown = JSON.parse(line);
51
+ if (!isMetrics(parsed)) throw new Error('draft metrics: malformed output');
52
+ const { inventory, oldestAgeHours, needsOwner, converted48h, cohort48h, conversion48h } = parsed;
53
+ return { inventory, oldestAgeHours, needsOwner, converted48h, cohort48h, conversion48h };
54
+ }
55
+
56
+ async function collectViaCli(): Promise<DraftMetrics> {
57
+ const root = join(import.meta.dir, '../../..');
58
+ const proc = Bun.spawn(['bun', join(root, 'bin/elanous.mjs'), 'harness', 'drafts', 'metrics', '--json'], {
59
+ cwd: root, stdout: 'pipe', stderr: 'pipe',
60
+ });
61
+ const timer = setTimeout(() => proc.kill(), COLLECT_TIMEOUT_MS);
62
+ try {
63
+ const [stdout, stderr, exitCode] = await Promise.all([new Response(proc.stdout).text(), new Response(proc.stderr).text(), proc.exited]);
64
+ if (exitCode !== 0) throw new Error(stderr.trim().split('\n').at(-1) || `draft metrics exited ${exitCode}`);
65
+ return parseDraftMetricsOutput(stdout);
66
+ } finally { clearTimeout(timer); }
67
+ }
68
+
69
+ export interface DraftMetricsSource {
70
+ read(): DraftMetricsSnapshot;
71
+ }
72
+
73
+ export function createDraftMetricsSource(options: DraftMetricsSourceOptions = {}): DraftMetricsSource {
74
+ const collect = options.collect ?? collectViaCli;
75
+ const ttl = options.ttlMs ?? DRAFT_METRICS_TTL_MS;
76
+ const retry = options.failureRetryMs ?? DRAFT_METRICS_FAILURE_RETRY_MS;
77
+ const now = options.now ?? Date.now;
78
+ let value: { metrics: DraftMetrics; at: number } | null = null;
79
+ let failure: { reason: string; at: number } | null = null;
80
+ let inFlight: Promise<void> | null = null;
81
+
82
+ const refresh = (): void => {
83
+ if (inFlight) return;
84
+ const started = now();
85
+ inFlight = (async () => {
86
+ try {
87
+ const metrics = await collect();
88
+ value = { metrics, at: now() };
89
+ failure = null;
90
+ try { debug.log('nexus.draft-metrics', 'collected', { ms: now() - started, inventory: metrics.inventory }); } catch { /* fail-soft */ }
91
+ } catch (error) {
92
+ failure = { reason: error instanceof Error ? error.message : String(error), at: now() };
93
+ try { debug.log('nexus.draft-metrics', 'failed', { ms: now() - started, reason: failure.reason }); } catch { /* fail-soft */ }
94
+ } finally { inFlight = null; }
95
+ })();
96
+ };
97
+
98
+ return {
99
+ read() {
100
+ const clock = now();
101
+ const due = failure && (!value || failure.at >= value.at)
102
+ ? clock - failure.at >= retry
103
+ : !value || clock - value.at >= ttl;
104
+ if (due) refresh();
105
+ return {
106
+ state: value ? 'ready' : failure && !inFlight ? 'unavailable' : 'measuring',
107
+ metrics: value?.metrics ?? null,
108
+ measuredAt: value ? new Date(value.at).toISOString() : null,
109
+ refreshing: inFlight !== null,
110
+ reason: failure?.reason ?? null,
111
+ };
112
+ },
113
+ };
114
+ }
@@ -58,12 +58,18 @@ export async function handleGraphsValidatePost(req: Request, opts: MetaApiOpts):
58
58
  return jsonResponse({ error: 'bad_request', reason: 'graph (harness|workflow) and yaml (string) required' }, 400);
59
59
  }
60
60
  const { graph, yaml } = body as { graph: GraphKind; yaml: string };
61
+ const { ok, errors, ignoredKeys } = validateGraphYaml(graph, yaml);
62
+ debug.log('graph.kinds', 'validate', { graph, ok, errors, ignored: ignoredKeys });
63
+ return jsonResponse({ ok, errors, ignoredKeys }, ok ? 200 : 422);
64
+ }
65
+
66
+ /** The one validator behind `POST /v1/graphs/validate` and the graph save path. */
67
+ export function validateGraphYaml(graph: GraphKind, yaml: string): { ok: boolean; errors: readonly unknown[]; ignoredKeys: string[]; graphId?: string } {
61
68
  const result = graph === 'harness' ? parseGraphTemplateYaml(yaml) : parseWorkflowYaml(yaml);
62
- const errors = graph === 'harness' ? (result as ReturnType<typeof parseGraphTemplateYaml>).errors : (result as ReturnType<typeof parseWorkflowYaml>).issues;
69
+ const errors: readonly unknown[] = graph === 'harness' ? (result as ReturnType<typeof parseGraphTemplateYaml>).errors : (result as ReturnType<typeof parseWorkflowYaml>).issues;
63
70
  let input: unknown;
64
71
  try { input = parseYaml(yaml); } catch { /* The parser already reports the YAML error. */ }
65
72
  const ignoredKeys = ignoredPaths(input, graph === 'harness' ? harnessShape : workflowShape);
66
- const ok = errors.length === 0;
67
- debug.log('graph.kinds', 'validate', { graph, ok, errors, ignored: ignoredKeys });
68
- return jsonResponse({ ok, errors, ignoredKeys }, ok ? 200 : 422);
73
+ const graphId = graph === 'harness' ? (result as ReturnType<typeof parseGraphTemplateYaml>).template?.graphId : undefined;
74
+ return { ok: errors.length === 0, errors, ignoredKeys, ...(graphId === undefined ? {} : { graphId }) };
69
75
  }
@@ -0,0 +1,53 @@
1
+ import { debug } from '../../debug/log.js';
2
+ import { GraphWizardInputError, generateGraphFromPrompt, type GraphWizardDeps, type GraphWizardTurn } from '../../graph-wizard/generate.js';
3
+ import { jsonResponse } from './json-response.js';
4
+
5
+ const MAX_PROMPT = 4000;
6
+ const MAX_YAML = 200_000;
7
+
8
+ /**
9
+ * POST /v1/graphs/wizard {prompt, kind?, currentYaml?, history?}
10
+ * → 200 {ok:true, id, yaml, base, baseReason, issues:[], attempts, summary, labels, steps, recipes, dryRun} · 422 {ok:false, …} · 400 빈 prompt.
11
+ * (labels·steps·recipes·dryRun·baseReason 는 v2 추가 필드 — 기존 필드는 그대로.)
12
+ * currentYaml 이 빈 문자열이면 «빈 캔버스»로 보고 새 그래프를 짓는다(의도) · 내용이 있는데 id 가 없거나 형식 밖이면 400.
13
+ * 저장하지 않는다 — 저장은 POST /v1/graphs(새 그래프) 또는 PUT /v1/graphs/<id>/yaml(내 그래프).
14
+ */
15
+ export async function handleGraphWizardPost(req: Request, deps: GraphWizardDeps = {}): Promise<Response> {
16
+ let body: Record<string, unknown>;
17
+ try {
18
+ const parsed: unknown = await req.json();
19
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error('object');
20
+ body = parsed as Record<string, unknown>;
21
+ } catch { return jsonResponse({ error: 'bad_request', reason: 'json body required' }, 400); }
22
+ const prompt = typeof body.prompt === 'string' ? body.prompt.trim() : '';
23
+ if (!prompt) return jsonResponse({ error: 'bad_request', reason: 'prompt required' }, 400);
24
+ if (prompt.length > MAX_PROMPT) return jsonResponse({ error: 'bad_request', reason: `prompt longer than ${MAX_PROMPT}` }, 400);
25
+ if (body.kind !== undefined && body.kind !== 'harness' && body.kind !== 'workflow') {
26
+ return jsonResponse({ error: 'bad_request', reason: 'kind must be harness or workflow' }, 400);
27
+ }
28
+ if (body.currentYaml !== undefined && (typeof body.currentYaml !== 'string' || body.currentYaml.length > MAX_YAML)) {
29
+ return jsonResponse({ error: 'bad_request', reason: 'currentYaml must be a string' }, 400);
30
+ }
31
+ let history: GraphWizardTurn[] | undefined;
32
+ if (body.history !== undefined) {
33
+ if (!Array.isArray(body.history) || !body.history.every((t) => t && typeof t === 'object' &&
34
+ ((t as GraphWizardTurn).role === 'user' || (t as GraphWizardTurn).role === 'assistant') && typeof (t as GraphWizardTurn).text === 'string')) {
35
+ return jsonResponse({ error: 'bad_request', reason: 'history must be [{role, text}]' }, 400);
36
+ }
37
+ history = body.history as GraphWizardTurn[];
38
+ }
39
+ try {
40
+ const result = await generateGraphFromPrompt({
41
+ prompt,
42
+ ...(body.kind ? { kind: body.kind as 'harness' | 'workflow' } : {}),
43
+ ...(typeof body.currentYaml === 'string' && body.currentYaml.trim() ? { currentYaml: body.currentYaml } : {}),
44
+ ...(history ? { history } : {}),
45
+ }, deps);
46
+ return jsonResponse({ ...result, base: result.base ?? null }, result.ok ? 200 : 422);
47
+ } catch (error) {
48
+ const message = error instanceof Error ? error.message : String(error);
49
+ if (error instanceof GraphWizardInputError) return jsonResponse({ error: 'bad_request', reason: message }, 400);
50
+ debug.log('graph.wizard', 'error', { message });
51
+ return jsonResponse({ error: 'wizard-failed', reason: message }, 502);
52
+ }
53
+ }