elanous 0.2.20 → 0.2.22

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 (456) 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/aBi6EyMtLsrl7J8eH4kF7/_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-08b716476f1d50af.js +1 -0
  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/{2489-ff55a35b202c0f94.js → 2489-d238d3e2eb5b2bf5.js} +1 -1
  27. package/apps/pwa/out/_next/static/chunks/2549-a8515c86f9e8cb96.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2655.72cc6a97276c5acc.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/2743-ef6e8c3ced74cce5.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/2840-5458ad50c49cac6a.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/2856.f75d5baef7f49b74.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/3344.0de4b0ec5a454b7a.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/335.a13f339e97893437.js +94 -0
  34. package/apps/pwa/out/_next/static/chunks/3394.646348f34b2500be.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/3443.6276909b53d72d53.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/3495.abd4254d808bf2dc.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/3522-1de1319d028ac89c.js +25 -0
  38. package/apps/pwa/out/_next/static/chunks/3721.ba5b82db0d9a5572.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/3857-5985236d9307f676.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/3919.9ff7dc25afefee33.js +74 -0
  41. package/apps/pwa/out/_next/static/chunks/3998.bd9a69d9b50e9bd2.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/4003.ab6db1dc266576c9.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/4216.e8ac94b9fe6b96ab.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/4327.c44dc2b55cb43ee5.js +34 -0
  45. package/apps/pwa/out/_next/static/chunks/4401.94257d497b6d26bc.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/4559.17ca2cdbaa8d414f.js +115 -0
  47. package/apps/pwa/out/_next/static/chunks/4588.ad3364cb7fb97ada.js +86 -0
  48. package/apps/pwa/out/_next/static/chunks/4706.97e734bd4c9816ca.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/4982.6e44352f751b9a1b.js +77 -0
  50. package/apps/pwa/out/_next/static/chunks/5342.49c5c5a530e62b50.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/5360-08a80abdf3a4da99.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/5431.66c58ab1327ee0b7.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/5633-d6852385000738ef.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/5717.bcfcb96e701b82e0.js +90 -0
  55. package/apps/pwa/out/_next/static/chunks/5755-30d102586789bf7f.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/5957.3f1a4012388dd962.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/5977-5b9e8a45a5b0c823.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/602.9bf02ac8e9502598.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/6020.3a1b347e2d2dfacc.js +8 -0
  60. package/apps/pwa/out/_next/static/chunks/{6158.af2e93b7adff709b.js → 6158.566c890adc06a459.js} +1 -1
  61. package/apps/pwa/out/_next/static/chunks/6375.fce1d174ba6d0324.js +64 -0
  62. package/apps/pwa/out/_next/static/chunks/6546.92a828aef441271f.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/658.957fdb0a8c6c6c8e.js +287 -0
  64. package/apps/pwa/out/_next/static/chunks/6706.323590aabb8b1293.js +226 -0
  65. package/apps/pwa/out/_next/static/chunks/6747.b67947f293a4c02d.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/6767.8e6c5f6e744fcd9f.js +38 -0
  67. package/apps/pwa/out/_next/static/chunks/6868.000b24a3045c5574.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/6946.bba74e9c96050f30.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/7033.a558f1b2f6912a53.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/7065.46029c26c53eac50.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/7087.6fae577d09249bc1.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/7175-350f4ce2873bc038.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/722.de8d9801e0f6d414.js +41 -0
  74. package/apps/pwa/out/_next/static/chunks/7497.57981e9d7a75a52a.js +24 -0
  75. package/apps/pwa/out/_next/static/chunks/7630.2b9a68659053ff4e.js +193 -0
  76. package/apps/pwa/out/_next/static/chunks/7672.4af122ad892117c7.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/771.6ab889a233e7dae7.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/7899.597af4a9996c907f.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/79b78388.663188133449d707.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/8096.f7da0776cfde6300.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/8194.484f51e273d9abd2.js +217 -0
  82. package/apps/pwa/out/_next/static/chunks/8769.815ca69879f608b4.js +104 -0
  83. package/apps/pwa/out/_next/static/chunks/8857-aa1cc4ac90af4d40.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/88729267.9c6c000157c83f2f.js +62 -0
  85. package/apps/pwa/out/_next/static/chunks/8f835b4f.048eeb02299e3e12.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/9068.5f2764fccf86c1b2.js +93 -0
  87. package/apps/pwa/out/_next/static/chunks/9247-55bcfbb0e8057f59.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/9451.ebfb0dcc9c02250f.js +23 -0
  89. package/apps/pwa/out/_next/static/chunks/9559.e648369b1d57cf9a.js +62 -0
  90. package/apps/pwa/out/_next/static/chunks/9832.2ab2e818013574c7.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/a9f2b54d-40c16bd0efdb04b8.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/app/approvals/{page-c855c24de7db74fe.js → page-88459e0903da2803.js} +1 -1
  93. package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-85b316bb4235980b.js → page-d85c43f46f58716b.js} +1 -1
  94. package/apps/pwa/out/_next/static/chunks/app/board/{page-9b2b26a33cb2cd42.js → page-4e645600579ae952.js} +1 -1
  95. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-c9955fde67b1bcb5.js → page-87edbfd19117823b.js} +1 -1
  96. package/apps/pwa/out/_next/static/chunks/app/bots/page-7f0c2b7801ac6a54.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/app/ceo/page-bf1f030a9d1adffd.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/app/chat/{page-4ba4abe096b6c2a8.js → page-cbaf66b1e1804adf.js} +1 -1
  99. package/apps/pwa/out/_next/static/chunks/app/consult/{page-575d4a8b4f7becb2.js → page-80ed511e02c81412.js} +1 -1
  100. package/apps/pwa/out/_next/static/chunks/app/control/{page-f5d56ff5f45ad164.js → page-9f5610caaa06178c.js} +1 -1
  101. package/apps/pwa/out/_next/static/chunks/app/decisions/{page-2260e9eef0357421.js → page-ec8f3575e339de74.js} +1 -1
  102. package/apps/pwa/out/_next/static/chunks/app/design-check/page-86d753e460f1a989.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/app/editor/page-4166a7d51cf0a57d.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/app/exec/{page-466bf0ee16ad1432.js → page-2a19c5cd3b562580.js} +1 -1
  105. package/apps/pwa/out/_next/static/chunks/app/field/{page-a23bc71713bcc08f.js → page-b6a09cde2e07521a.js} +1 -1
  106. package/apps/pwa/out/_next/static/chunks/app/inside/page-f7eae1518ca0c353.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/app/intake/page-456a8573ad811381.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/app/layout-8462d6a543cbb2d9.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/app/live/page-2974cdfb87c0f9c9.js +1 -0
  110. package/apps/pwa/out/_next/static/chunks/app/live-run/page-f927ffa25f3c86d1.js +1 -0
  111. package/apps/pwa/out/_next/static/chunks/app/loops/{page-35bfec2e631d3eae.js → page-abd86cd832aaa011.js} +1 -1
  112. package/apps/pwa/out/_next/static/chunks/app/market/page-79daf68f6256adaa.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-f9ccb1b1adbea14d.js → page-8c3f168fc3f91939.js} +1 -1
  114. package/apps/pwa/out/_next/static/chunks/app/morning/{page-ec102c9b8b691e37.js → page-786fb4504d0f2fa9.js} +1 -1
  115. package/apps/pwa/out/_next/static/chunks/app/not-found-75209f14597dcb8f.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/app/observatory/page-bf30affe3a85af7e.js +1 -0
  117. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1187a44b7515434b.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-3d0910185a66ed54.js +1 -0
  119. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f401ee1dd5a26e25.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/app/outputs/page-5dfe7604fd842784.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/app/page-76cf838050a8d681.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/app/reflection/{page-db03ae3589a81c9e.js → page-7ec8e289b4c14df6.js} +1 -1
  123. package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-4a6463b97f0ad94f.js → page-8c8ac17d52be2a79.js} +1 -1
  124. package/apps/pwa/out/_next/static/chunks/app/sessions/{page-75bffecc9aa1004e.js → page-d359ab048d63a9d9.js} +1 -1
  125. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-b8b345c880bf78d1.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/settings/{page-31d5b875760bb62d.js → page-f151cc10ad026940.js} +1 -1
  127. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-9875f103057da433.js +1 -0
  128. package/apps/pwa/out/_next/static/chunks/app/setup/page-9789328af9a9aa62.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/share/{page-46d5201d2207a511.js → page-bf495a1e284f4e00.js} +1 -1
  130. package/apps/pwa/out/_next/static/chunks/app/showroom/page-a263d94bca4be22a.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/tasks/page-3962eab8ec58d16e.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/term/{page-cf0a4b4abead71ea.js → page-9abcfac07e02a542.js} +1 -1
  133. package/apps/pwa/out/_next/static/chunks/app/today/page-bee49f927c333abb.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/trace/page-bac2d56dcb02d955.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/vault/page-937cbcc13176295c.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d09495594f7f480d.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/workflows/{page-0acc8e198f7b0dfb.js → page-69cb93d1a2e57e0e.js} +1 -1
  138. package/apps/pwa/out/_next/static/chunks/app/workspace/{page-34882c84f84021c5.js → page-72ec543928d67c3e.js} +1 -1
  139. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-82906eeda0186a7b.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/d70879db.c66c56a95a91efc6.js +66 -0
  141. package/apps/pwa/out/_next/static/chunks/f8626309.a4e5d362b1d55e59.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/webpack-f2b4016b9b981bc8.js +1 -0
  143. package/apps/pwa/out/_next/static/css/{d3427996da11e480.css → b9d8151e48998417.css} +1 -1
  144. package/apps/pwa/out/_next/static/css/f2bd5257e6af88d8.css +1 -0
  145. package/apps/pwa/out/approvals/index.html +1 -1
  146. package/apps/pwa/out/approvals/index.txt +14 -14
  147. package/apps/pwa/out/autopilot/index.html +1 -1
  148. package/apps/pwa/out/autopilot/index.txt +14 -14
  149. package/apps/pwa/out/board/index.html +1 -1
  150. package/apps/pwa/out/board/index.txt +14 -14
  151. package/apps/pwa/out/botlab/index.html +1 -1
  152. package/apps/pwa/out/botlab/index.txt +14 -14
  153. package/apps/pwa/out/bots/index.html +1 -1
  154. package/apps/pwa/out/bots/index.txt +14 -14
  155. package/apps/pwa/out/ceo/index.html +1 -1
  156. package/apps/pwa/out/ceo/index.txt +14 -14
  157. package/apps/pwa/out/chat/index.html +1 -1
  158. package/apps/pwa/out/chat/index.txt +14 -14
  159. package/apps/pwa/out/consult/index.html +1 -1
  160. package/apps/pwa/out/consult/index.txt +14 -14
  161. package/apps/pwa/out/control/index.html +1 -1
  162. package/apps/pwa/out/control/index.txt +14 -14
  163. package/apps/pwa/out/decisions/index.html +1 -1
  164. package/apps/pwa/out/decisions/index.txt +14 -14
  165. package/apps/pwa/out/design-check/index.html +1 -1
  166. package/apps/pwa/out/design-check/index.txt +14 -14
  167. package/apps/pwa/out/editor/index.html +1 -1
  168. package/apps/pwa/out/editor/index.txt +15 -15
  169. package/apps/pwa/out/exec/index.html +1 -1
  170. package/apps/pwa/out/exec/index.txt +14 -14
  171. package/apps/pwa/out/field/index.html +1 -1
  172. package/apps/pwa/out/field/index.txt +14 -14
  173. package/apps/pwa/out/healthz +1 -1
  174. package/apps/pwa/out/index.html +1 -1
  175. package/apps/pwa/out/index.txt +14 -14
  176. package/apps/pwa/out/inside/index.html +1 -1
  177. package/apps/pwa/out/inside/index.txt +15 -15
  178. package/apps/pwa/out/intake/index.html +1 -1
  179. package/apps/pwa/out/intake/index.txt +14 -14
  180. package/apps/pwa/out/live/index.html +1 -1
  181. package/apps/pwa/out/live/index.txt +14 -14
  182. package/apps/pwa/out/live-run/index.html +1 -0
  183. package/apps/pwa/out/live-run/index.txt +35 -0
  184. package/apps/pwa/out/loops/index.html +1 -1
  185. package/apps/pwa/out/loops/index.txt +14 -14
  186. package/apps/pwa/out/market/index.html +1 -1
  187. package/apps/pwa/out/market/index.txt +14 -14
  188. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  189. package/apps/pwa/out/missions/placeholder/index.txt +14 -14
  190. package/apps/pwa/out/morning/index.html +1 -1
  191. package/apps/pwa/out/morning/index.txt +14 -14
  192. package/apps/pwa/out/observatory/index.html +1 -1
  193. package/apps/pwa/out/observatory/index.txt +14 -14
  194. package/apps/pwa/out/ops/checklist/index.html +1 -1
  195. package/apps/pwa/out/ops/checklist/index.txt +14 -14
  196. package/apps/pwa/out/ops/release/index.html +1 -1
  197. package/apps/pwa/out/ops/release/index.txt +14 -14
  198. package/apps/pwa/out/ops/seats/index.html +1 -1
  199. package/apps/pwa/out/ops/seats/index.txt +14 -14
  200. package/apps/pwa/out/outputs/index.html +1 -1
  201. package/apps/pwa/out/outputs/index.txt +14 -14
  202. package/apps/pwa/out/reflection/index.html +1 -1
  203. package/apps/pwa/out/reflection/index.txt +15 -15
  204. package/apps/pwa/out/scheduler/index.html +1 -1
  205. package/apps/pwa/out/scheduler/index.txt +14 -14
  206. package/apps/pwa/out/sessions/index.html +1 -1
  207. package/apps/pwa/out/sessions/index.txt +14 -14
  208. package/apps/pwa/out/settings/devices/index.html +1 -1
  209. package/apps/pwa/out/settings/devices/index.txt +14 -14
  210. package/apps/pwa/out/settings/index.html +1 -1
  211. package/apps/pwa/out/settings/index.txt +14 -14
  212. package/apps/pwa/out/setup/done/index.html +1 -1
  213. package/apps/pwa/out/setup/done/index.txt +14 -14
  214. package/apps/pwa/out/setup/index.html +1 -1
  215. package/apps/pwa/out/setup/index.txt +14 -14
  216. package/apps/pwa/out/share/index.html +1 -1
  217. package/apps/pwa/out/share/index.txt +14 -14
  218. package/apps/pwa/out/showroom/index.html +1 -1
  219. package/apps/pwa/out/showroom/index.txt +14 -14
  220. package/apps/pwa/out/tasks/index.html +1 -1
  221. package/apps/pwa/out/tasks/index.txt +14 -14
  222. package/apps/pwa/out/term/index.html +1 -1
  223. package/apps/pwa/out/term/index.txt +14 -14
  224. package/apps/pwa/out/today/index.html +1 -1
  225. package/apps/pwa/out/today/index.txt +14 -14
  226. package/apps/pwa/out/trace/index.html +1 -1
  227. package/apps/pwa/out/trace/index.txt +14 -14
  228. package/apps/pwa/out/vault/index.html +1 -1
  229. package/apps/pwa/out/vault/index.txt +14 -14
  230. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  231. package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
  232. package/apps/pwa/out/workflows/index.html +1 -1
  233. package/apps/pwa/out/workflows/index.txt +14 -14
  234. package/apps/pwa/out/workspace/index.html +1 -1
  235. package/apps/pwa/out/workspace/index.txt +14 -14
  236. package/apps/pwa/out/worktrees/index.html +1 -1
  237. package/apps/pwa/out/worktrees/index.txt +14 -14
  238. package/graphs/catalog/catalog.yaml +1 -1
  239. package/graphs/drafts/recipes.yaml +6 -6
  240. package/graphs/implement-loop.yaml +277 -5
  241. package/graphs/test-diet/recipes.yaml +3 -0
  242. package/graphs/test-diet/test-diet.yaml +3 -1
  243. package/package.json +1 -1
  244. package/scripts/coord-channel-watch.sh +31 -4
  245. package/scripts/docs-lint.ts +4 -1
  246. package/scripts/f-session-monitor.ts +69 -0
  247. package/scripts/hq/standby-snapshot.ts +7 -3
  248. package/scripts/knowledge-compress.ts +11 -0
  249. package/scripts/marketing/generate-ax-brief.ts +18 -1
  250. package/scripts/measure-nl-routing-corpus.ts +11 -3
  251. package/scripts/nightly-docops.ts +90 -3
  252. package/scripts/release-loop/gate-node.ts +5 -5
  253. package/scripts/release-loop/shard-plan.ts +50 -5
  254. package/scripts/research-coo-admin-dates.ts +39 -0
  255. package/scripts/rhythm/daily.ts +2 -1
  256. package/scripts/skills/public-check.ts +7 -2
  257. package/scripts/smart-merge/resync-shadow.ts +5 -3
  258. package/scripts/test-diet/lib.ts +81 -1
  259. package/scripts/test-diet/node.ts +48 -2
  260. package/scripts/verify-coo-admin-evidence.ts +83 -0
  261. package/src/acp/client.ts +4 -2
  262. package/src/agent/run-context.ts +4 -0
  263. package/src/agent-env/profile.ts +25 -5
  264. package/src/agent-mission/driver.ts +26 -12
  265. package/src/agent-mission/supervisor-metrics.ts +17 -2
  266. package/src/agent-substrate/acp-reviewer.ts +12 -5
  267. package/src/agent-substrate/pr-reviewer.ts +11 -4
  268. package/src/agent-substrate/review-intent.ts +102 -3
  269. package/src/autopilot/build/llm-conflict-merge.ts +22 -8
  270. package/src/autopilot/pr-manager.ts +18 -8
  271. package/src/away/release-watch.ts +47 -8
  272. package/src/briefing/brief-items.ts +104 -11
  273. package/src/browser-cdp/client.ts +10 -5
  274. package/src/chat/index.ts +1 -0
  275. package/src/cli/brief-cli.ts +20 -2
  276. package/src/cli/config-classify.ts +83 -0
  277. package/src/cli/control-cli.ts +14 -0
  278. package/src/cli/decisions-cli.ts +26 -16
  279. package/src/cli/logs-model-input.ts +1 -1
  280. package/src/cli/ops-cli.ts +11 -0
  281. package/src/cli/ops-overview-cli.ts +50 -0
  282. package/src/cli/pod-cli.ts +1 -0
  283. package/src/cli/pr-cli.ts +65 -8
  284. package/src/cli/release-cli.ts +21 -4
  285. package/src/cli/tasks-cli.ts +214 -1
  286. package/src/connectors/types.ts +6 -6
  287. package/src/context-bus/context-now-surfaces.ts +18 -0
  288. package/src/context-bus/coord-events.ts +32 -4
  289. package/src/context-bus/long-term-memory.ts +23 -6
  290. package/src/context-bus/nightly-condense.ts +169 -21
  291. package/src/control/control-status.ts +87 -0
  292. package/src/control-plane/light-member.ts +5 -3
  293. package/src/control-plane/member.ts +172 -7
  294. package/src/core-turn/run-goal-loop.ts +22 -1
  295. package/src/dashboard/index.ts +40 -4
  296. package/src/dashboard/slash-runtime/dashboard-handlers.ts +9 -0
  297. package/src/decisions/decision-cards.ts +20 -1
  298. package/src/decisions/decision-ledger.ts +11 -8
  299. package/src/decisions/web-push-decision-cards.ts +19 -3
  300. package/src/discord-self-message.ts +2 -2
  301. package/src/domains/coo-admin-dates.evidence.json +876 -0
  302. package/src/domains/coo-admin-tool.ts +48 -14
  303. package/src/domains/ops-status.ts +53 -0
  304. package/src/domains/outbound-alert.ts +24 -1
  305. package/src/esc-abort-gate.ts +3 -1
  306. package/src/exec-requests/planner.ts +6 -0
  307. package/src/graph-kinds/registry.ts +6 -6
  308. package/src/graph-runner/graph-cli.ts +91 -2
  309. package/src/graph-runner/graph-run-api.ts +20 -0
  310. package/src/graph-runner/graph-tick.ts +73 -12
  311. package/src/graph-runner/runner.ts +43 -11
  312. package/src/graph-unify/wf-to-graph.ts +79 -0
  313. package/src/graph-wizard/archetypes.ts +88 -0
  314. package/src/graph-wizard/generate.ts +577 -0
  315. package/src/graph-wizard/saved-steps.ts +120 -0
  316. package/src/graph-wizard/steps.ts +206 -0
  317. package/src/harness/harness-cli-command.ts +186 -32
  318. package/src/harness/harness-queue.ts +58 -14
  319. package/src/harness/launch-advice.ts +23 -0
  320. package/src/index.ts +43 -11
  321. package/src/intake-plane/digest.ts +18 -11
  322. package/src/knowledge/conclusion-compress.ts +93 -0
  323. package/src/knowledge/know-find.ts +9 -2
  324. package/src/knowledge/query.ts +20 -0
  325. package/src/llm.ts +26 -16
  326. package/src/loops/neighbor-runtime.ts +88 -0
  327. package/src/loops/orchestrator/author-depth.ts +29 -11
  328. package/src/loops/orchestrator/overlap.ts +7 -1
  329. package/src/loops/orchestrator/resync-shadow.ts +1 -1
  330. package/src/loops/orchestrator/tick.ts +35 -2
  331. package/src/loops/orchestrator/traffic.ts +2 -0
  332. package/src/loops/registry.ts +5 -0
  333. package/src/market/publish.ts +17 -1
  334. package/src/maturity/feature-maturity.ts +4 -2
  335. package/src/mss/logging/redaction.ts +17 -0
  336. package/src/nexus/api/draft-metrics.ts +128 -0
  337. package/src/nexus/api/graph-peer-edit.ts +77 -0
  338. package/src/nexus/api/graph-wizard-api.ts +61 -0
  339. package/src/nexus/api/graphs-api.ts +193 -17
  340. package/src/nexus/api/harness-traversal.ts +205 -0
  341. package/src/nexus/api/http-server.ts +122 -6
  342. package/src/nexus/api/plugins-market.ts +24 -0
  343. package/src/nexus/index.ts +3 -2
  344. package/src/nexus/outbound/router.ts +28 -8
  345. package/src/oauth/claude-pty-maturity.ts +24 -0
  346. package/src/oauth/fallback-chain.ts +27 -14
  347. package/src/onboarding/first-run.ts +3 -0
  348. package/src/ops/overview-collect.ts +345 -0
  349. package/src/ops/overview-seat-era.ts +90 -0
  350. package/src/ops/overview.ts +223 -0
  351. package/src/plugins/maker/market-bundle.ts +9 -0
  352. package/src/plugins/maker/plugin-maker.ts +9 -3
  353. package/src/plugins/maker/wizard-generate.ts +73 -9
  354. package/src/policy/llm-policy.ts +11 -6
  355. package/src/release-loop/checklist-board.ts +27 -0
  356. package/src/release-loop/checklist.ts +38 -0
  357. package/src/release-loop/gate-shards.ts +7 -2
  358. package/src/seat-address/seat-address.ts +1 -0
  359. package/src/seat-loop/seat-loop.ts +174 -20
  360. package/src/seat-loop/seat-report.ts +29 -1
  361. package/src/self-dev/ask-launch-flow.ts +4 -2
  362. package/src/self-dev/ask-launch-io.ts +3 -1
  363. package/src/self-dev/dev-cli.ts +90 -8
  364. package/src/self-dev/dev-pipeline.ts +20 -1
  365. package/src/self-dev/draft-sweep.ts +45 -8
  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/orchestrate.ts +3 -0
  370. package/src/self-dev/release-path-guard.ts +7 -4
  371. package/src/self-dev/review-provider-fallback.ts +83 -0
  372. package/src/self-dev/run-supervisor.ts +5 -1
  373. package/src/self-implement/frozen-merges.ts +23 -0
  374. package/src/self-implement/goal-author.ts +14 -0
  375. package/src/self-implement/harness-policy-gates.ts +7 -5
  376. package/src/self-implement/heal-intake.ts +3 -0
  377. package/src/self-implement/host-regate.ts +28 -14
  378. package/src/self-implement/l8-merge-queue.ts +80 -8
  379. package/src/self-implement/orchestrator.ts +84 -28
  380. package/src/self-implement/seams.ts +8 -3
  381. package/src/self-implement/self-implement-cli.ts +4 -0
  382. package/src/self-implement/self-implement-runtime.ts +12 -0
  383. package/src/session-runtime/index.ts +5 -3
  384. package/src/shell-env-bootstrap.ts +35 -6
  385. package/src/steward/triage.ts +25 -8
  386. package/src/storage/file-lock.ts +33 -4
  387. package/src/task-agent/card-evidence.ts +6 -0
  388. package/src/task-agent/completion-evidence.ts +31 -3
  389. package/src/task-agent/cover.ts +61 -0
  390. package/src/task-agent/live-moves.ts +126 -3
  391. package/src/task-agent/parent-host.ts +315 -0
  392. package/src/task-agent/shadow.ts +19 -4
  393. package/src/task-agent/task-hand.ts +11 -2
  394. package/src/task-orchestrator/surfaces/pod-command-job.ts +41 -6
  395. package/src/task-orchestrator/surfaces/pod-gate-deps.ts +123 -0
  396. package/src/task-orchestrator/surfaces/pod-pool.ts +138 -15
  397. package/src/task-orchestrator/surfaces/self-implement-pod.ts +39 -6
  398. package/src/task-orchestrator/surfaces/self-implement.ts +7 -0
  399. package/src/telegram-commands.ts +8 -0
  400. package/src/telegram-project-command.ts +6 -0
  401. package/src/telegram.ts +2 -2
  402. package/src/tool-runtime/browser-runtime.ts +87 -3
  403. package/src/tool-runtime/index.ts +1 -0
  404. package/src/typecheck-ratchet.ts +2 -1
  405. package/src/user-config.ts +55 -8
  406. package/src/version/packed-revision.json +1 -1
  407. package/src/workflow-runtime/executor.ts +24 -2
  408. package/src/workflow-runtime/triggers/registry.ts +27 -0
  409. package/src/workflow-runtime/triggers/schedule-source.ts +30 -3
  410. package/apps/pwa/out/_next/static/Bam1YziK2iH9bN4K0utGk/_buildManifest.js +0 -1
  411. package/apps/pwa/out/_next/static/chunks/1307-77fad80cf54477c6.js +0 -1
  412. package/apps/pwa/out/_next/static/chunks/1612-f60c0c93ec773f81.js +0 -1
  413. package/apps/pwa/out/_next/static/chunks/1844-9ec4b1d12f6e80eb.js +0 -1
  414. package/apps/pwa/out/_next/static/chunks/1866-e7a6999f25548b30.js +0 -1
  415. package/apps/pwa/out/_next/static/chunks/1948.15f356b15a625597.js +0 -5
  416. package/apps/pwa/out/_next/static/chunks/2116-6429c188eb846ab8.js +0 -1
  417. package/apps/pwa/out/_next/static/chunks/224-e7be8f762fe74986.js +0 -1
  418. package/apps/pwa/out/_next/static/chunks/2549-89c518a4c8ed4d00.js +0 -1
  419. package/apps/pwa/out/_next/static/chunks/2957-96ffab3d521fb08f.js +0 -1
  420. package/apps/pwa/out/_next/static/chunks/3857-995ea9f5ef33d71e.js +0 -1
  421. package/apps/pwa/out/_next/static/chunks/5360-be1f1d8f9fcc9755.js +0 -1
  422. package/apps/pwa/out/_next/static/chunks/5755-ea7ed88eb956f47b.js +0 -1
  423. package/apps/pwa/out/_next/static/chunks/5977-94ed6f78e9814b2b.js +0 -1
  424. package/apps/pwa/out/_next/static/chunks/7748-6e51bfe927019f5c.js +0 -25
  425. package/apps/pwa/out/_next/static/chunks/9247-c1753a059e7bac08.js +0 -1
  426. package/apps/pwa/out/_next/static/chunks/a9f2b54d-33c0689b70ed8cf3.js +0 -1
  427. package/apps/pwa/out/_next/static/chunks/app/bots/page-7a852956c74015a3.js +0 -1
  428. package/apps/pwa/out/_next/static/chunks/app/ceo/page-c5a5c621be0e04ae.js +0 -1
  429. package/apps/pwa/out/_next/static/chunks/app/design-check/page-7c058069c5e7fa63.js +0 -1
  430. package/apps/pwa/out/_next/static/chunks/app/editor/page-dc5da0b8a2edb8a4.js +0 -1
  431. package/apps/pwa/out/_next/static/chunks/app/inside/page-ed4a6f799f005d09.js +0 -1
  432. package/apps/pwa/out/_next/static/chunks/app/intake/page-9cd565005b93aca6.js +0 -1
  433. package/apps/pwa/out/_next/static/chunks/app/layout-55f0f40c6db95f15.js +0 -1
  434. package/apps/pwa/out/_next/static/chunks/app/live/page-474cb5888ea0e8f3.js +0 -1
  435. package/apps/pwa/out/_next/static/chunks/app/market/page-9ca21f9ad3208a61.js +0 -1
  436. package/apps/pwa/out/_next/static/chunks/app/not-found-a9c2cfa8dda922a5.js +0 -1
  437. package/apps/pwa/out/_next/static/chunks/app/observatory/page-6da35144878feb6d.js +0 -1
  438. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-f6055495e9ed7ea8.js +0 -1
  439. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-25204694fa9984a5.js +0 -1
  440. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-a60e035f151d84d3.js +0 -1
  441. package/apps/pwa/out/_next/static/chunks/app/outputs/page-189c587f36874538.js +0 -1
  442. package/apps/pwa/out/_next/static/chunks/app/page-c56634bde705a65d.js +0 -1
  443. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a8d2fe0c669e0de4.js +0 -1
  444. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-c26fa10aac6c21c4.js +0 -1
  445. package/apps/pwa/out/_next/static/chunks/app/setup/page-68793c6e10331d66.js +0 -1
  446. package/apps/pwa/out/_next/static/chunks/app/showroom/page-d81c07a7b4f618ae.js +0 -1
  447. package/apps/pwa/out/_next/static/chunks/app/tasks/page-be1c4e8697f22360.js +0 -1
  448. package/apps/pwa/out/_next/static/chunks/app/today/page-a4717e2c4bf7a28f.js +0 -1
  449. package/apps/pwa/out/_next/static/chunks/app/trace/page-0fd895fab9b62c1b.js +0 -1
  450. package/apps/pwa/out/_next/static/chunks/app/vault/page-8b6c1454a8e8f306.js +0 -1
  451. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-c6e4364d9de8f717.js +0 -1
  452. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-0506a1e0e4bdb2f5.js +0 -1
  453. package/apps/pwa/out/_next/static/chunks/webpack-c260571513bf43c2.js +0 -1
  454. package/apps/pwa/out/_next/static/css/c23d959d3ee15e57.css +0 -1
  455. /package/apps/pwa/out/_next/static/{Bam1YziK2iH9bN4K0utGk → aBi6EyMtLsrl7J8eH4kF7}/_ssgManifest.js +0 -0
  456. /package/skills/project-onboarding/{skill.md → SKILL.md} +0 -0
@@ -1,4 +1,4 @@
1
- import { closeSync, mkdirSync, openSync, readFileSync, readSync } from 'node:fs';
1
+ import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync } from 'node:fs';
2
2
  import { tmpdir } from 'node:os';
3
3
  import { loadRunLedger as loadRunLedgerSync } from '../self-implement/run-ledger.js';
4
4
  import { join, resolve } from 'node:path';
@@ -12,6 +12,9 @@ import type { Task } from '../task-orchestrator/types.js';
12
12
  import { cardPrNumber, COMPLETION_KINDS, handTask, nextMoveFor, readTaskAgentState, readTaskCard, taskAgentStatePath as taskStatePathDefault, shellQuote, TASK_CARD_PREFIX, TASK_SEATS, type CompletionKind, type TaskCard, type TaskLaunchContext, type TaskLaunchReceipt, type TaskLauncher, type TaskSeat } from '../task-agent/task-hand.js';
13
13
  import { readCardSalvageBranch, refreshCardRunBinding, type CardRunEvidenceDeps } from '../task-agent/card-evidence.js';
14
14
  import { advanceMission, handMission, MissionAdvanceError, openMissionIds, recordPieceRef, type AdvanceMissionResult, type MissionDecompose, type PieceEvidenceReader } from '../task-agent/mission.js';
15
+ import { formatTaskAgentCover, measureTaskAgentCover, type CoverRow, type TaskAgentCoverInput } from '../task-agent/cover.js';
16
+ import { LogStore, STORE_SAFETY_MAX, type LogQuery, type LogStoreRow } from '../mss/logging/log-store.js';
17
+ import { resolveLogTargets } from './logs-cli.js';
15
18
 
16
19
  type TaskRow = Pick<Task, 'id' | 'title' | 'priority' | 'status' | 'createdAt' | 'approval' | 'generatedBy'>;
17
20
 
@@ -34,6 +37,13 @@ export interface TasksCliDeps {
34
37
  releaseBoard?: () => BoardNode[];
35
38
  /** `tasks show`·`advance` 의 런 근거 읽기(시험 주입) — 기본 = 런 원장(이 우주 ⊕ 연합) ⊕ 호스트 logs 의 Pod job-finished. */
36
39
  cardRunEvidence?: CardRunEvidenceDeps;
40
+ /** HARNESS-PARENT-ON-MSB1 — 원격 부모 카드의 원장 거울(시험 주입). 기본 = ssh 로 `<prodRoot>/remote-parents/<host>/run-ledger`. */
41
+ remoteLedgerMirror?: (card: TaskCard) => void;
42
+ /** `tasks parents` 의 낡은 부모 보고 의존(시험 주입). */
43
+ staleParents?: import('../task-agent/parent-host.js').StaleParentDeps;
44
+ /** `tasks cover` logs 조회 seam. 기본은 등록된 모든 우주(운영 ⊕ test)의 logs.db 를 read-only 로 연다 —
45
+ * pr land 는 작업 트리마다 파생 test 우주에 기록되므로 현재 우주만 읽으면 분모가 늘 0 이다. */
46
+ coverLogs?: (query: LogQuery) => LogStoreRow[] | CoverLogRead;
37
47
  }
38
48
 
39
49
  /** `<조각id>=<값>` 반복 인자. */
@@ -97,6 +107,60 @@ export interface DefaultTaskLauncherDeps {
97
107
  writeStop?: import('../self-implement/run-ledger.js').RunLedgerWriter;
98
108
  /** 이미 stop 이 있나(시험 주입). 기본 = 이 우주 런 원장 ⊕ 이 프로세스 기록. */
99
109
  hasStop?: (runId: string) => boolean;
110
+ /** HARNESS-PARENT-ON-MSB1 — 원격 부모 대상(시험 주입). undefined = 설정·env 에서 푼다 · null = 끔. */
111
+ parentHost?: import('../task-agent/parent-host.js').ParentHostConfig | null;
112
+ /** 원격 ssh(시험 주입). */
113
+ ssh?: import('../task-agent/parent-host.js').SshRunner;
114
+ /** HQ 발사 동결(holdLaunches) 여부(시험 주입). */
115
+ launchHoldActive?: () => boolean;
116
+ /** HQ 판(시험 주입). */
117
+ localVersion?: string;
118
+ /** 원격 → 로컬 되돌림 알림(시험 주입). 기본 = stderr 한 줄. */
119
+ notice?: (line: string) => void;
120
+ }
121
+
122
+ /**
123
+ * HARNESS-PARENT-ON-MSB1 — 켜져 있고 발사 전 점검이 통과하면 원격(ssh)에서 부모를 띄운다(PR 까지만 · 병합은 HQ).
124
+ * 막히면 null — 호출부가 종전 로컬 발사로 간다(관측: task-agent parent-host-fallback).
125
+ */
126
+ async function tryRemoteParent(args: string[], cwd: string | undefined, context: TaskLaunchContext, deps: DefaultTaskLauncherDeps): Promise<TaskLaunchReceipt | null> {
127
+ const parentHostModule = await import('../task-agent/parent-host.js');
128
+ let target = deps.parentHost;
129
+ if (target === undefined) {
130
+ const { getUserConfig } = await import('../user-config.js');
131
+ let configured: Parameters<typeof parentHostModule.resolveParentHost>[0];
132
+ try { configured = getUserConfig().taskAgent?.parentHost; } catch { configured = undefined; }
133
+ target = parentHostModule.resolveParentHost(configured);
134
+ }
135
+ if (!target) return null;
136
+ const notice = deps.notice ?? ((line: string) => { try { process.stderr.write(`${line}\n`); } catch { /* ignore */ } });
137
+ const fallback = (gaps: string[], facts: Record<string, string> = {}): null => {
138
+ try { debug.log('task-agent', 'parent-host-fallback', { host: target!.host, runId: context.runId, gaps, facts }); } catch { /* fail-soft */ }
139
+ notice(`[parent-host] ${target!.host} 로 못 띄움 — 로컬 발사로 간다: ${gaps.join(' · ')}`);
140
+ return null;
141
+ };
142
+ if (cwd) return fallback(['project-target-is-local']);
143
+ let launchHoldActive = false;
144
+ try {
145
+ if (deps.launchHoldActive) launchHoldActive = deps.launchHoldActive();
146
+ else {
147
+ const { readLandingFreeze } = await import('../release-loop/landing-freeze.js');
148
+ const { prodInstanceRoot } = await import('../instance/resolve.js');
149
+ launchHoldActive = readLandingFreeze(prodInstanceRoot())?.holdLaunches === true;
150
+ }
151
+ } catch { launchHoldActive = true; /* 못 읽으면 막힌 것으로(fail-closed) */ }
152
+ const preflight = parentHostModule.preflightParentHost({ target, localVersion: deps.localVersion ?? parentHostModule.localElanousVersion(), launchHoldActive, ...(deps.ssh ? { ssh: deps.ssh } : {}) });
153
+ if (!preflight.ok) return fallback(preflight.gaps, preflight.facts);
154
+ try {
155
+ const launched = parentHostModule.launchRemoteParent(target, args, context.env, deps.ssh);
156
+ try { debug.log('task-agent', 'parent-host-launched', { host: target.host, runId: context.runId, pid: launched.pid, log: launched.log }); } catch { /* fail-soft */ }
157
+ return { runId: context.runId, parentHost: { host: target.host, ...(target.configDir ? { configDir: target.configDir } : {}), pid: launched.pid, log: launched.log } };
158
+ } catch (error) {
159
+ // «확실히 안 떴다»일 때만 로컬로 — 불확실(ssh 끊김 등)은 던져 카드를 launch-failed 로 남긴다(중복 발사 방지).
160
+ if (error instanceof parentHostModule.RemoteParentNotLaunchedError) return fallback([error.message]);
161
+ try { debug.log('task-agent', 'parent-host-launch-uncertain', { host: target.host, runId: context.runId, error: error instanceof Error ? error.message : String(error) }); } catch { /* fail-soft */ }
162
+ throw error;
163
+ }
100
164
  }
101
165
 
102
166
  /** stderr 파일의 첫 비어 있지 않은 줄(최대 4KB 만 읽는다 — 원 로그 꼬리를 싣지 않는다). */
@@ -112,6 +176,10 @@ function firstStderrLine(path: string): string {
112
176
  }
113
177
 
114
178
  export async function defaultTaskLauncher(args: string[], cwd?: string, context?: TaskLaunchContext, deps: DefaultTaskLauncherDeps = {}): Promise<void | TaskLaunchReceipt> {
179
+ if (context) {
180
+ const remote = await tryRemoteParent(args, cwd, context, deps);
181
+ if (remote) return remote;
182
+ }
115
183
  const spawn = deps.spawn ?? ((await import('node:child_process')).spawn as unknown as DetachedSpawn);
116
184
  const { getElanousConfigDirOverride } = await import('../elanous-config-dir.js');
117
185
  const { prodInstanceRoot } = await import('../instance/resolve.js');
@@ -208,6 +276,21 @@ export function spawnDetachedConfirmed(spawnFn: DetachedSpawn, command: string,
208
276
  });
209
277
  }
210
278
 
279
+ /** HARNESS-PARENT-ON-MSB1 — 카드가 원격 부모면 그 런 원장(⊕ Pod 자식)을 HQ 거울로 당긴다. 실패는 관측만(«없음»으로 읽히지 않게). */
280
+ async function mirrorRemoteCardLedger(cardId: string, deps: TasksCliDeps): Promise<void> {
281
+ let card: TaskCard | undefined;
282
+ try { card = readTaskCard(cardId, deps.taskStatePath); } catch { return; }
283
+ if (!card?.parentHost || !card.runId) return;
284
+ try {
285
+ if (deps.remoteLedgerMirror) { deps.remoteLedgerMirror(card); return; }
286
+ const { mirrorRemoteRunLedgers } = await import('../task-agent/parent-host.js');
287
+ const { prodInstanceRoot } = await import('../instance/resolve.js');
288
+ mirrorRemoteRunLedgers(card.parentHost, card.runId, prodInstanceRoot());
289
+ } catch (error) {
290
+ try { debug.log('task-agent', 'remote-ledger-mirror-failed', { card: cardId, host: card.parentHost.host, runId: card.runId, error: error instanceof Error ? error.message : String(error) }); } catch { /* fail-soft */ }
291
+ }
292
+ }
293
+
211
294
  function moveLine(card: TaskCard): string {
212
295
  const move = nextMoveFor(card);
213
296
  return `${move.kind}${move.command ? ` — elanous ${move.command.map(shellQuote).join(' ')}` : ''} (${move.reason})`;
@@ -313,6 +396,74 @@ function handleError(error: unknown, out: (line: string) => void): void {
313
396
  }
314
397
  }
315
398
 
399
+ const COVER_QUERY_LIMIT = STORE_SAFETY_MAX;
400
+
401
+ /** Federated read: rows from every store, whether any single store hit the bound, and how many stores could not be read. */
402
+ export interface CoverLogRead { rows: LogStoreRow[]; limitReached: boolean; unreadableStores: number; storesRead: number }
403
+
404
+ function queryCoverLogs(query: LogQuery): CoverLogRead {
405
+ const { targets, error } = resolveLogTargets({ all: true, includeTest: true });
406
+ if (error) throw new Error(error);
407
+ const read: CoverLogRead = { rows: [], limitReached: false, unreadableStores: 0, storesRead: 0 };
408
+ for (const target of targets) {
409
+ if (!existsSync(target.dbPath)) continue;
410
+ let store: LogStore | undefined;
411
+ try {
412
+ store = LogStore.openReadOnly(target.dbPath);
413
+ const rows = store.query(query);
414
+ if (query.limit !== undefined && rows.length >= query.limit) read.limitReached = true;
415
+ read.rows.push(...rows);
416
+ read.storesRead++;
417
+ } catch { read.unreadableStores++; }
418
+ finally { try { store?.close(); } catch { /* already closed */ } }
419
+ }
420
+ if (read.storesRead === 0) throw new Error('읽을 수 있는 logs.db 없음');
421
+ return read;
422
+ }
423
+
424
+ function coverEvents<T>(rows: readonly LogStoreRow[], category: string, event: string, parse: (data: Record<string, unknown>) => T | null): T[] {
425
+ const events: T[] = [];
426
+ for (const row of rows) {
427
+ if (row.category !== category || row.event !== event || !row.data) continue;
428
+ try {
429
+ const data: unknown = JSON.parse(row.data);
430
+ if (data === null || typeof data !== 'object' || Array.isArray(data)) continue;
431
+ const parsed = parse(data as Record<string, unknown>);
432
+ if (parsed !== null) events.push(parsed);
433
+ } catch { /* malformed log data is not evidence of an action */ }
434
+ }
435
+ return events;
436
+ }
437
+
438
+ export function readTaskAgentCover(
439
+ window: { sinceMs?: number; untilMs?: number } = {},
440
+ query: NonNullable<TasksCliDeps['coverLogs']> = queryCoverLogs,
441
+ ): { rows: CoverRow[]; limitReached: boolean; unreadableStores: number } {
442
+ let unreadableStores = 0;
443
+ const read = (category: 'task-agent' | 'pr.land', event: 'action' | 'step'): { rows: LogStoreRow[]; limitReached: boolean } | null => {
444
+ try {
445
+ const result = query({ ...window, limit: COVER_QUERY_LIMIT, exactCategories: [category], events: [event] });
446
+ if (Array.isArray(result)) return { rows: result, limitReached: result.length >= COVER_QUERY_LIMIT };
447
+ unreadableStores = Math.max(unreadableStores, result.unreadableStores);
448
+ return { rows: result.rows, limitReached: result.limitReached };
449
+ } catch { return null; }
450
+ };
451
+ const actionRead = read('task-agent', 'action');
452
+ const landRead = read('pr.land', 'step');
453
+ const actions = actionRead?.rows ?? null;
454
+ const lands = landRead?.rows ?? null;
455
+ const truncated = { taskAgentActions: actionRead?.limitReached === true, allLands: landRead?.limitReached === true };
456
+ const input: TaskAgentCoverInput = {
457
+ taskAgentActions: actions === null ? null : coverEvents(actions, 'task-agent', 'action', (data) =>
458
+ typeof data.kind === 'string' && typeof data.result === 'string'
459
+ ? { kind: data.kind as NonNullable<TaskAgentCoverInput['taskAgentActions']>[number]['kind'], result: data.result } : null),
460
+ allLands: lands === null ? null : coverEvents(lands, 'pr.land', 'step', (data) =>
461
+ typeof data.step === 'string' && typeof data.ok === 'boolean' ? { step: data.step, ok: data.ok } : null),
462
+ truncated,
463
+ };
464
+ return { rows: measureTaskAgentCover(input), limitReached: truncated.taskAgentActions || truncated.allLands, unreadableStores };
465
+ }
466
+
316
467
  export function registerTasksCommands(program: Command, deps: TasksCliDeps = {}): void {
317
468
  let activeToken: string | undefined;
318
469
  const output = deps.output ?? console.log;
@@ -325,6 +476,39 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
325
476
  };
326
477
  const tasks = program.command('tasks').alias('task').description('TOX 태스크 조회 및 승인 · TASK-AGENT 과제 넘기기(hand)');
327
478
 
479
+ tasks.command('cover')
480
+ .description('TASK-AGENT 가 대신한 착지 비율과 리뷰·재발사·green 의 아직 없는 분모를 조회(읽기 전용)')
481
+ .option('--since <iso>', '시작 시각 (ISO)')
482
+ .option('--until <iso>', '끝 시각 (ISO)')
483
+ .option('--json', 'JSON 출력')
484
+ .option('--lines', '동사별 한 줄 요약 출력 (읽기 전용)')
485
+ .action((opts: { since?: string; until?: string; json?: boolean; lines?: boolean }) => {
486
+ const parseIso = (value: string | undefined, flag: string): number | undefined => {
487
+ if (value === undefined) return undefined;
488
+ const time = Date.parse(value);
489
+ if (!/^\d{4}-\d{2}-\d{2}T.*(?:Z|[+-]\d\d:\d\d)$/.test(value) || !Number.isFinite(time)) throw new Error(`${flag} 는 ISO 시각이어야 한다`);
490
+ return time;
491
+ };
492
+ try {
493
+ const sinceMs = parseIso(opts.since, '--since');
494
+ const untilMs = parseIso(opts.until, '--until');
495
+ if (sinceMs !== undefined && untilMs !== undefined && sinceMs > untilMs) throw new Error('--since 가 --until 보다 늦다');
496
+ const { rows, limitReached, unreadableStores } = readTaskAgentCover({ sinceMs, untilMs }, deps.coverLogs);
497
+ const limitWarning = limitReached ? `조회 상한 ${COVER_QUERY_LIMIT} 도달 — 더 오래된 사건이 있을 수 있다(절단된 수를 전체로 읽지 말 것)` : null;
498
+ const storeWarning = unreadableStores > 0 ? `logs.db ${unreadableStores}개를 못 읽었다 — 그 우주의 사건은 수에 없다(«없다»로 읽지 말 것)` : null;
499
+ if (opts.json) out(JSON.stringify({ rows, limitReached, unreadableStores, ...(limitWarning ? { warning: limitWarning } : {}), ...(storeWarning ? { storeWarning } : {}) }));
500
+ else {
501
+ if (opts.lines) for (const row of rows) out(formatTaskAgentCover(row));
502
+ else {
503
+ out('동사\t대신\t전체\t비율\t상태\t관측 사건');
504
+ for (const row of rows) out(`${row.verb}\t${row.byTaskAgent ?? '-'}\t${row.total ?? '-'}\t${row.ratio ?? '-'}\t${row.state}\t${row.observedActions ?? '-'}`);
505
+ }
506
+ if (limitWarning) out(`⚠ ${limitWarning}`);
507
+ if (storeWarning) out(`⚠ ${storeWarning}`);
508
+ }
509
+ } catch (error) { out(error instanceof Error ? error.message : '커버 조회 실패'); process.exitCode = 1; }
510
+ });
511
+
328
512
  tasks.command('list')
329
513
  .description('우선순위와 생성 시각 순으로 태스크 보기')
330
514
  .option('--status <s>', '상태로 필터')
@@ -442,6 +626,32 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
442
626
  }
443
627
  });
444
628
 
629
+ tasks.command('parents')
630
+ .description('낡은 하니스 부모 보고(읽기 전용 · 죽이지 않는다) — 나이 ≥ N 시간이고 런 원장이 끝났거나(run-status) Pod 이 없는 `harness say|ask` 부모')
631
+ .option('--older-than <hours>', '나이 하한(시간)', '6')
632
+ .option('--no-pods', 'Pod 목록을 재지 않는다(원장 근거만)')
633
+ .option('--json', 'JSON 출력')
634
+ .action(async (opts: { olderThan: string; pods?: boolean; json?: boolean }) => {
635
+ const hours = Number(opts.olderThan);
636
+ if (!Number.isFinite(hours) || hours < 0) { out(`--older-than 는 0 이상 수다: ${opts.olderThan}`); process.exitCode = 1; return; }
637
+ const { staleHarnessParents } = await import('../task-agent/parent-host.js');
638
+ const injected = deps.staleParents ?? {};
639
+ const loadLedger = injected.loadLedger ?? ((runId: string) => loadRunLedgerSync(runId));
640
+ let podRuns = injected.podRuns;
641
+ let labelOf = injected.labelOf;
642
+ if (!podRuns && opts.pods !== false) {
643
+ const queue = await import('../harness/harness-queue.js');
644
+ podRuns = () => queue.readHarnessQueuePodRuns();
645
+ labelOf = queue.queueRunLabelValue;
646
+ }
647
+ const report = staleHarnessParents(hours, { ...injected, loadLedger, ...(podRuns ? { podRuns } : {}), ...(labelOf ? { labelOf } : {}) });
648
+ try { debug.log('task-agent', 'stale-parents', { olderThanHours: hours, scanned: report.scanned, stale: report.rows.length, podRunsMeasured: report.podRunsMeasured }); } catch { /* fail-soft */ }
649
+ if (opts.json) { out(JSON.stringify({ olderThanHours: hours, ...report })); return; }
650
+ out(`하니스 부모 ${report.scanned} · 나이 ≥ ${hours}h 중 낡음 후보 ${report.rows.length}${report.podRunsMeasured ? '' : ' · ⚠ Pod 목록 못 잼(원장 근거만)'} — 읽기 전용(죽이지 않는다)`);
651
+ out('pid\t나이h\t자리\t판\t런\t사유');
652
+ for (const row of report.rows) out([row.pid, row.ageHours, row.seat ?? '-', row.version ?? '-', row.runId ?? '-', row.reasons.join(',')].join('\t'));
653
+ });
654
+
445
655
  tasks.command('board')
446
656
  .description('보드 — 프로젝트 › 목표 › 이정표 › 과제. 과제 카드(project·goal·milestone)와 내부 팩(릴리스 체크리스트 = elanous › 판 › 칸)을 합쳐 읽는다(쓰지 않는다)')
447
657
  .option('--project <id>', '이 프로젝트만')
@@ -569,6 +779,8 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
569
779
  // 런 id 가 있으면 원장에서 골 id · PR 을 묶는다(찾았을 때만 적는다 · 실패는 그대로 보인다).
570
780
  const bindable = card?.pieces ? card.pieces : card ? [card.id] : [];
571
781
  for (const cardId of bindable) {
782
+ // HARNESS-PARENT-ON-MSB1: 원격 부모 런은 원장이 그 호스트에 있다 — HQ 거울로 먼저 당긴다(읽기 전용).
783
+ await mirrorRemoteCardLedger(cardId, deps);
572
784
  try { await refreshCardRunBinding(cardId, deps.taskStatePath ?? taskStatePathDefault(), deps.cardRunEvidence ?? {}); } catch (error) {
573
785
  // 근거 없음으로 보인다 — 원장 부재와 갈리게 실패는 남긴다.
574
786
  try { debug.log('task-agent', 'card-bind-failed', { card: cardId, via: 'show', error: error instanceof Error ? error.message : String(error) }); } catch { /* fail-soft */ }
@@ -608,6 +820,7 @@ export function registerTasksCommands(program: Command, deps: TasksCliDeps = {})
608
820
  if (opts.json) out(JSON.stringify({ card, nextMove: nextMoveFor(card), salvageBranch: salvage.salvageBranch, ...(salvageUnread ? { salvageBranchUnread: true } : {}) }));
609
821
  else {
610
822
  for (const line of taskCardLines(card)) out(line);
823
+ if (card.parentHost) out(`부모 호스트: ${card.parentHost.host}${card.parentHost.pid ? ` (pid ${card.parentHost.pid})` : ''} · PR 까지만 — 병합은 HQ${card.parentHost.log ? ` · 로그 ${card.parentHost.host}:${card.parentHost.log}` : ''}`);
611
824
  if (salvage.salvageBranch) out(`수확 가지: ${salvage.salvageBranch} (draft PR 대신)`);
612
825
  else if (salvageUnread) out('수확 가지: 못 읽음(원장 읽기 실패)');
613
826
  }
@@ -1,5 +1,5 @@
1
- export interface ExternalTaskEvent {
2
- provider: 'linear' | 'asana';
1
+ export interface ExternalTaskEvent<Provider extends string = 'linear' | 'asana'> {
2
+ provider: Provider;
3
3
  eventId: string;
4
4
  kind: 'created' | 'updated';
5
5
  ref: string;
@@ -11,9 +11,9 @@ export interface ExternalTaskEvent {
11
11
  occurredAt: string;
12
12
  }
13
13
 
14
- export interface TaskConnector {
15
- provider: ExternalTaskEvent['provider'];
14
+ export interface TaskConnector<Provider extends string = ExternalTaskEvent['provider']> {
15
+ provider: Provider;
16
16
  verify(req: { rawBody: string | Uint8Array; signature: string; secret: string; now?: number }): { ok: true } | { ok: false; reason: string };
17
- parse(body: unknown, deliveryId: string): ExternalTaskEvent | null;
18
- idempotencyKey(event: ExternalTaskEvent): string;
17
+ parse(body: unknown, deliveryId: string): ExternalTaskEvent<Provider> | null;
18
+ idempotencyKey(event: ExternalTaskEvent<Provider>): string;
19
19
  }
@@ -53,6 +53,24 @@ export function renderTelegramNow(input: ContextNowAnswer, audience: ContextNowA
53
53
  return lines.map(line => line.replace(/\s+/g, ' ').trim()).join('\n');
54
54
  }
55
55
 
56
+ /** Context-first opening for chat connectors: up to three operational rows (live runs, an unfinished
57
+ * release run, late schedules) ahead of the same `/now` body. `/now` itself stays unchanged. */
58
+ export function renderContextFirstNow(input: ContextNowAnswer, audience: ContextNowAudience = 'operator'): string {
59
+ const answer = forAudience(input, audience);
60
+ const labels = { run: '도는 런', release: '발행 런', 'schedule-late': '지연 스케줄' } as const;
61
+ const operational = answer.facts.filter(fact => fact.kind === 'run' ||
62
+ (fact.kind === 'release' && (fact.unreadable || fact.status !== 'done')) ||
63
+ (fact.kind === 'schedule-late' && (fact.unreadable || !!fact.count)));
64
+ // Run goals are whole asks (often thousands of characters); one line each keeps the opening to one screen.
65
+ const short = (text: string) => { const chars = Array.from(text); return chars.length > 60 ? `${chars.slice(0, 60).join('')}…` : text; };
66
+ const lines = operational.slice(0, 3).map(fact => {
67
+ const text = fact.kind === 'run' && !fact.unreadable ? `${short(fact.goal.replace(/\s+/g, ' ').trim())} · ${fact.phase} · ${fact.elapsed}` : factText(fact);
68
+ return `${labels[fact.kind as keyof typeof labels]}: ${text} — ${fact.source}`.replace(/\s+/g, ' ').trim();
69
+ });
70
+ if (operational.length > lines.length) lines.push(`… 운영 항목 ${operational.length - lines.length}개 더 (/now)`);
71
+ return [...lines, renderTelegramNow(answer, 'operator')].join('\n');
72
+ }
73
+
56
74
  export function renderTuiNow(input: ContextNowAnswer, audience: ContextNowAudience = 'operator'): string[] {
57
75
  const answer = forAudience(input, audience);
58
76
  const shortText = (text: string) => {
@@ -2,7 +2,9 @@ import { Database } from 'bun:sqlite';
2
2
  import { readFileSync, existsSync } from 'node:fs';
3
3
  import { resolve } from 'node:path';
4
4
  import { debug, redactSecretText } from '../debug/log.js';
5
+ import { DecisionLedger, type Seat } from '../decisions/decision-ledger.js';
5
6
  import { openSurfaceEventsDb, recordEvent, surfaceEventsDbPath } from '../domains/surface-events.js';
7
+ import { registeredSeats } from '../seat-address/seat-address.js';
6
8
 
7
9
  export interface CoordEventInput {
8
10
  seat: string;
@@ -31,6 +33,13 @@ function seatIds(): Set<string> {
31
33
  }
32
34
  const kinds = new Set(['요청', '결정', '사고', '보고', '정정']);
33
35
 
36
+ function cleanCoordHeadline(value: string): string {
37
+ const first = value.split(/\r?\n/, 1)[0] ?? '';
38
+ const withoutPrefix = first.replace(/^\*\*\[[A-Z]+\]\*\*\s*/, '')
39
+ .replace(/^(?:📌안내\s*)?\d{4}-\d\d-\d\d\s+\d\d:\d\d\s+KST\s*/, '');
40
+ return redactSecretText(withoutPrefix).trim().slice(0, 120);
41
+ }
42
+
34
43
  /** Mirror the sender's ordered envelope fields; only the first line enters this parser. */
35
44
  export function parseCoordHeader(seat: string, header: string): Omit<CoordEventInput, 'url' | 'at'> {
36
45
  const first = header.split(/\r?\n/, 1)[0] ?? '';
@@ -69,10 +78,7 @@ export function recordCoordEvent(input: CoordEventInput, deps: { db?: Database }
69
78
  try {
70
79
  db = deps.db ?? openSurfaceEventsDb();
71
80
  const at = input.at ?? new Date().toISOString();
72
- const first = input.headline.split(/\r?\n/, 1)[0] ?? '';
73
- const withoutPrefix = first.replace(/^\*\*\[[A-Z]+\]\*\*\s*/, '')
74
- .replace(/^(?:📌안내\s*)?\d{4}-\d\d-\d\d\s+\d\d:\d\d\s+KST\s*/, '');
75
- const headline = redactSecretText(withoutPrefix).trim().slice(0, 120);
81
+ const headline = cleanCoordHeadline(input.headline);
76
82
  const safe = (value: string) => redactSecretText(value.split(/\r?\n/, 1)[0] ?? '');
77
83
  const refs = { seat: safe(input.seat), recipients: input.recipients.map(safe), all: input.all,
78
84
  kind: input.kind === null ? null : safe(input.kind), slot: input.slot === null ? null : safe(input.slot),
@@ -98,6 +104,28 @@ export function recordCoordEvent(input: CoordEventInput, deps: { db?: Database }
98
104
  } finally { if (!deps.db) db?.close(); }
99
105
  }
100
106
 
107
+ /** A failed decision mirror must never turn a successful channel/context record into a failure. */
108
+ export function mirrorCoordDecision(input: CoordEventInput, deps: { ledger?: Pick<DecisionLedger, 'recordSeatDecision'> } = {}): void {
109
+ if (input.kind !== '결정') return;
110
+ try {
111
+ if (!registeredSeats().some(({ id }) => id === input.seat)) return;
112
+ const entry = (deps.ledger ?? new DecisionLedger()).recordSeatDecision({
113
+ seat: input.seat as Seat,
114
+ title: input.slot ? redactSecretText(input.slot.split(/\r?\n/, 1)[0] ?? '').trim() : cleanCoordHeadline(input.headline),
115
+ decision: cleanCoordHeadline(input.headline),
116
+ delegation: '조율 채널 — 자리 위임 범위',
117
+ ...(input.url ? { refs: [input.url] } : {}),
118
+ decidedAt: input.at ?? new Date().toISOString(),
119
+ });
120
+ try { debug.log('context.coord', 'decision-mirrored', { seat: entry.seat, id: entry.id }); }
121
+ catch { /* observation cannot turn a successful write into a failed send */ }
122
+ } catch (error) {
123
+ if (error instanceof Error && error.message === 'seat decision source already recorded') return;
124
+ try { debug.log('context.coord', 'decision-mirror-failed', { seat: redactSecretText(input.seat) }); }
125
+ catch { /* observation cannot turn a successful send into a failure */ }
126
+ }
127
+ }
128
+
101
129
  /** Read-only chronological list; a missing store is an empty ledger, not a newly created one. */
102
130
  export function listCoordEvents(opts: { since: string; seat?: string; channelOnly?: boolean }, deps: { db?: Database } = {}): CoordEvent[] {
103
131
  if (!deps.db && !existsSync(surfaceEventsDbPath())) return [];
@@ -18,6 +18,14 @@ export interface MemoryItem {
18
18
  conflict?: { owner: 'OP'; sources: [string, string] };
19
19
  }
20
20
 
21
+ /** Guardian retention boundary: lifecycle hygiene, independent of librarian promotion. */
22
+ export function guardianRetireAged(items: readonly MemoryItem[], now: Date): MemoryItem[] {
23
+ const cutoff = now.getTime() - 30 * DAY_MS;
24
+ return items.map(item => ({ ...item,
25
+ status: Date.parse(item.updatedAt) < cutoff ? 'retired' as const : item.status,
26
+ }));
27
+ }
28
+
21
29
  export type MemorySource =
22
30
  | { kind: 'event'; at: string; seat: string; text: string; source: string }
23
31
  | { kind: 'decision'; at: string; seat: string; text: string; source: string };
@@ -28,6 +36,8 @@ export interface MemorySummary {
28
36
  summary: string;
29
37
  /** Only supply this when the two strings identify the same fact and mutually exclusive values. */
30
38
  claim?: { key: string; value: string };
39
+ /** Sleep-review task agent's promotion decision; not stored in a MemoryItem. */
40
+ promote?: boolean;
31
41
  }
32
42
 
33
43
  type CondenseEvent = CoordEvent & { surface?: string };
@@ -38,7 +48,7 @@ export interface MemoryDeps {
38
48
  decisions?: () => DecisionEntry[];
39
49
  }
40
50
 
41
- export type CondenseSkipReason = 'no-seat' | 'no-project' | 'no-topic' | 'no-summary' | 'bad-link' | 'bad-claim' | 'no-claim' | 'summarize-failed';
51
+ export type CondenseSkipReason = 'not-promoted' | 'no-seat' | 'no-project' | 'no-topic' | 'no-summary' | 'bad-link' | 'bad-claim' | 'no-claim' | 'summarize-failed';
42
52
  export type CondenseSkipped = Partial<Record<CondenseSkipReason, number>>;
43
53
  export interface FunnelCount { total: number; bySeat: Record<string, number> }
44
54
  export interface CondenseFunnel {
@@ -77,22 +87,25 @@ export function readCondenseEvents(since: string): CondenseEvent[] {
77
87
  /** Condense one UTC day; callers persist the returned snapshot, not raw transcripts. Read-only ledger access. */
78
88
  export async function condenseContextDay(
79
89
  day: string, previous: readonly MemoryItem[], deps: MemoryDeps, now: Date = new Date(),
90
+ policy: { promotion?: 'sleep-review'; retention?: 'guardian' } = {},
80
91
  ): Promise<MemoryItem[]> {
81
92
  if (!/^\d{4}-\d\d-\d\d$/.test(day) || new Date(`${day}T00:00:00.000Z`).toISOString().slice(0, 10) !== day) {
82
93
  throw new Error('day must be a valid UTC YYYY-MM-DD');
83
94
  }
84
- return condenseContextWindow(`${day}T00:00:00.000Z`, new Date(Date.parse(`${day}T00:00:00.000Z`) + DAY_MS).toISOString(), previous, deps, now);
95
+ return condenseContextWindow(`${day}T00:00:00.000Z`, new Date(Date.parse(`${day}T00:00:00.000Z`) + DAY_MS).toISOString(), previous, deps, now, policy);
85
96
  }
86
97
 
87
98
  /** Read-only condensation over an exact rolling UTC window; the caller owns any persistence. */
88
99
  export async function condenseContextWindow(
89
100
  since: string, until: string, previous: readonly MemoryItem[], deps: MemoryDeps, now: Date = new Date(),
101
+ policy: { promotion?: 'sleep-review'; retention?: 'guardian' } = {},
90
102
  ): Promise<MemoryItem[]> {
91
- return (await condenseContextWindowWithReport(since, until, previous, deps, now)).cards;
103
+ return (await condenseContextWindowWithReport(since, until, previous, deps, now, policy)).cards;
92
104
  }
93
105
 
94
106
  export async function condenseContextWindowWithReport(
95
107
  since: string, until: string, previous: readonly MemoryItem[], deps: MemoryDeps, now: Date = new Date(),
108
+ policy: { promotion?: 'sleep-review'; retention?: 'guardian' } = {},
96
109
  ): Promise<CondenseWindowReport> {
97
110
  const start = Date.parse(since);
98
111
  const end = Date.parse(until);
@@ -228,6 +241,7 @@ export async function condenseContextWindowWithReport(
228
241
  try { summary = await deps.summarize(source); }
229
242
  catch { skip('summarize-failed', source); continue; }
230
243
  bump(funnel.summarized, source.seat);
244
+ if (policy.promotion === 'sleep-review' && summary.promote !== true) { skip('not-promoted', source); continue; }
231
245
  const project = safeLine(summary?.project);
232
246
  const topic = safeLine(summary?.topic);
233
247
  const text = safeLine(summary?.summary);
@@ -253,17 +267,20 @@ export async function condenseContextWindowWithReport(
253
267
  updatedKeys.add(key);
254
268
  }
255
269
  const cutoff = now.getTime() - 30 * DAY_MS;
256
- // Compare only the winning, non-retired assertions; a superseded claim cannot keep a decision candidate alive.
270
+ // The nightly librarian makes promotion judgments; guardian retention runs at the record boundary.
271
+ // The ordinary condense command keeps its existing retention behavior.
257
272
  const finalItems: MemoryItem[] = [...items.values()].map(({ conflict: _conflict, ...item }) => ({ ...item,
258
- status: Date.parse(item.updatedAt) < cutoff ? 'retired' as const : 'active' as const,
273
+ status: policy.retention === 'guardian' ? item.status
274
+ : Date.parse(item.updatedAt) < cutoff ? 'retired' as const : 'active' as const,
259
275
  }));
260
276
  finalItems.sort((a, b) => Date.parse(a.updatedAt) - Date.parse(b.updatedAt) || a.source.localeCompare(b.source)
261
277
  || a.seat.localeCompare(b.seat));
262
278
  for (let index = 0; index < finalItems.length; index++) {
263
279
  const item = finalItems[index]!;
264
- if (item.status !== 'active' || !item.claim) continue;
280
+ if (item.status !== 'active' || !item.claim || (policy.retention === 'guardian' && Date.parse(item.updatedAt) < cutoff)) continue;
265
281
  const { key, value } = item.claim;
266
282
  const conflicting = finalItems.slice(0, index).find(other => other.status === 'active'
283
+ && (policy.retention !== 'guardian' || Date.parse(other.updatedAt) >= cutoff)
267
284
  && other.project === item.project
268
285
  && other.claim?.key === key && other.claim.value !== value);
269
286
  if (conflicting) {