elanous 0.2.18 → 0.2.19

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 (404) 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/{P6YTVgzuZz5oTZhY4Din_ → J5H7BvqgSI8DUjtRRcA_S}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1157-cba0e8553d35a980.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/{726.83532d69452ee24b.js → 1470.98a6ab61ccb4131c.js} +1 -1
  7. package/apps/pwa/out/_next/static/chunks/171-23a877ed11720de9.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/2336-e830c777eb6bce04.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/{9247-269e4464cc4b491c.js → 2375-e061bb24cd2b5cde.js} +1 -1
  12. package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/{211-9c9090c59e2c5907.js → 2977-e38442e86289f1d3.js} +1 -1
  14. package/apps/pwa/out/_next/static/chunks/{f6b7c9ba.64a895b8b2fedbdc.js → 2ea96958.e8c7e112de50b067.js} +1 -1
  15. package/apps/pwa/out/_next/static/chunks/3094.67339318c7f72a3a.js +2 -0
  16. package/apps/pwa/out/_next/static/chunks/338-ceb956612dcfaa86.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/3395-835e4ceaa6335ba4.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +5 -0
  19. package/apps/pwa/out/_next/static/chunks/3441-be82e3595aef3a20.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/3689-89905039bf3e7257.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/3822-6f268bc26c6a84ac.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/{1268.0840ff91e083fcbd.js → 4323.c5dfb6f503304775.js} +1 -1
  25. package/apps/pwa/out/_next/static/chunks/4457-d7df81c90f9d0268.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/4551-77dadae76f6cbace.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/{6110.1297d4093fdfbb0d.js → 4847.56f5bc454fbcc827.js} +2 -2
  28. package/apps/pwa/out/_next/static/chunks/4978.6fd93b974441e87f.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/5012-f746f6549b869f91.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/{7327-2d69fb321ba73389.js → 6062-73e68343a9814f6a.js} +1 -1
  33. package/apps/pwa/out/_next/static/chunks/62-5bfed1e23790b745.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/6320-9bc8561fde416e07.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/649-ca96d1c5d09e02b8.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/6726-85a7db59b288c53f.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/6735-33267c4630d0d963.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/7633-141682015873beb6.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/{935.4ddaa99fc1d1126c.js → 7647.2fcd30d894d5ee30.js} +1 -1
  42. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/{f6231284-45726550aba08fca.js → 8046cadf-211ca3c0e00f2b72.js} +1 -1
  45. package/apps/pwa/out/_next/static/chunks/8067-143791231c99bd02.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/8430-a10cfba8ef282d4e.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/8870-9614a213b990b10b.js +25 -0
  49. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/9454-12c2f30dea62291c.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/976-4a9b4ebdca7c739a.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/9989-15cdf568bafa9fc8.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/ae787e4c-e5b1ef00b2dbceca.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/approvals/page-deef5ee191830f14.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-b193da485f9a14b2.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/board/page-b6e1a79df0e214f4.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/botlab/page-e84df4d907f703c7.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/bots/page-a5fb9a5db89ae4ea.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/ceo/page-1499f481d7fc52d6.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/chat/page-c7cf753aeb648790.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/consult/page-4118469e514e10fd.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/control/page-e968f7630654332a.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/decisions/page-505915052f947022.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/design-check/page-c2f962fb4a3b06c6.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/editor/page-7b5ed0bd8a589ec4.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/exec/page-cf8193250dd42dd4.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/field/page-cf3f5dfa35dea8e9.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/inside/page-5b29d9b06ceadade.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/intake/page-a15e7fcc283e9ca7.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/layout-d2915e7b70ff3d0e.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/live/page-b70cd005d9169e25.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/loops/page-2595f0eba29f57e1.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/market/page-c776322454156e36.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-77bbd17f4435aa10.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/morning/page-049689e611ecc936.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/not-found-85954a4fb09c44eb.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/observatory/page-e773e552e1068ee6.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-5633c4bfa7684b4a.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-d829b92bd01b4994.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-061c317fc771becf.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a45ece8865ed4b6.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/page-0be990618c0d56bd.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/app/reflection/page-bfea4865dc244b73.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-0d6ba8e337741484.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/app/sessions/page-76a88b55d816482e.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-bba1542df59f61ce.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/app/settings/page-4185c95689bb372e.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-b699e1a8d5d34b8b.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/app/setup/page-c225e338c31bf274.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/app/share/page-449353f70507ca1f.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/app/showroom/page-90b889714102476d.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f1ee7fbeb7f96ddc.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/app/term/page-d59a95e1434dfaa0.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/app/today/page-71491a23414d1a47.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/app/trace/page-de7112bdb65fa4fe.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/app/vault/page-feb4d0d88079660d.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-f17d887b40dd6b1d.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/app/workflows/page-8da77be2192bcd5d.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/app/workspace/page-72c3b9a5e9582b29.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3afe4c8d0c48c39.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +1 -0
  106. package/apps/pwa/out/_next/static/chunks/main-app-b9308f2d8a931531.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/webpack-6f35f9d892fa630b.js +1 -0
  110. package/apps/pwa/out/_next/static/css/{e99e3cff94ced78a.css → 599369d853c61df7.css} +1 -1
  111. package/apps/pwa/out/_next/static/css/85f53c6f35d11382.css +1 -0
  112. package/apps/pwa/out/_next/static/css/d3427996da11e480.css +1 -0
  113. package/apps/pwa/out/approvals/index.html +1 -1
  114. package/apps/pwa/out/approvals/index.txt +23 -23
  115. package/apps/pwa/out/autopilot/index.html +1 -1
  116. package/apps/pwa/out/autopilot/index.txt +23 -23
  117. package/apps/pwa/out/board/index.html +1 -1
  118. package/apps/pwa/out/board/index.txt +23 -23
  119. package/apps/pwa/out/botlab/index.html +1 -1
  120. package/apps/pwa/out/botlab/index.txt +22 -22
  121. package/apps/pwa/out/bots/index.html +1 -1
  122. package/apps/pwa/out/bots/index.txt +22 -22
  123. package/apps/pwa/out/ceo/index.html +1 -1
  124. package/apps/pwa/out/ceo/index.txt +23 -23
  125. package/apps/pwa/out/chat/index.html +1 -1
  126. package/apps/pwa/out/chat/index.txt +22 -22
  127. package/apps/pwa/out/consult/index.html +1 -1
  128. package/apps/pwa/out/consult/index.txt +23 -23
  129. package/apps/pwa/out/control/index.html +1 -1
  130. package/apps/pwa/out/control/index.txt +23 -23
  131. package/apps/pwa/out/decisions/index.html +1 -1
  132. package/apps/pwa/out/decisions/index.txt +22 -22
  133. package/apps/pwa/out/design-check/index.html +1 -1
  134. package/apps/pwa/out/design-check/index.txt +23 -23
  135. package/apps/pwa/out/editor/index.html +1 -1
  136. package/apps/pwa/out/editor/index.txt +25 -24
  137. package/apps/pwa/out/exec/index.html +1 -1
  138. package/apps/pwa/out/exec/index.txt +23 -23
  139. package/apps/pwa/out/field/index.html +1 -1
  140. package/apps/pwa/out/field/index.txt +23 -23
  141. package/apps/pwa/out/healthz +1 -1
  142. package/apps/pwa/out/index.html +1 -1
  143. package/apps/pwa/out/index.txt +22 -22
  144. package/apps/pwa/out/inside/index.html +1 -1
  145. package/apps/pwa/out/inside/index.txt +24 -23
  146. package/apps/pwa/out/intake/index.html +1 -1
  147. package/apps/pwa/out/intake/index.txt +23 -23
  148. package/apps/pwa/out/live/index.html +1 -1
  149. package/apps/pwa/out/live/index.txt +23 -23
  150. package/apps/pwa/out/loops/index.html +1 -1
  151. package/apps/pwa/out/loops/index.txt +28 -27
  152. package/apps/pwa/out/market/index.html +1 -1
  153. package/apps/pwa/out/market/index.txt +22 -22
  154. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  155. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  156. package/apps/pwa/out/morning/index.html +1 -1
  157. package/apps/pwa/out/morning/index.txt +23 -23
  158. package/apps/pwa/out/observatory/index.html +1 -1
  159. package/apps/pwa/out/observatory/index.txt +23 -23
  160. package/apps/pwa/out/ops/checklist/index.html +1 -1
  161. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  162. package/apps/pwa/out/ops/release/index.html +1 -1
  163. package/apps/pwa/out/ops/release/index.txt +22 -22
  164. package/apps/pwa/out/ops/seats/index.html +1 -1
  165. package/apps/pwa/out/ops/seats/index.txt +22 -22
  166. package/apps/pwa/out/outputs/index.html +1 -1
  167. package/apps/pwa/out/outputs/index.txt +22 -22
  168. package/apps/pwa/out/reflection/index.html +1 -1
  169. package/apps/pwa/out/reflection/index.txt +23 -23
  170. package/apps/pwa/out/scheduler/index.html +1 -1
  171. package/apps/pwa/out/scheduler/index.txt +23 -23
  172. package/apps/pwa/out/sessions/index.html +1 -1
  173. package/apps/pwa/out/sessions/index.txt +22 -22
  174. package/apps/pwa/out/settings/devices/index.html +1 -1
  175. package/apps/pwa/out/settings/devices/index.txt +23 -23
  176. package/apps/pwa/out/settings/index.html +1 -1
  177. package/apps/pwa/out/settings/index.txt +23 -23
  178. package/apps/pwa/out/setup/done/index.html +1 -1
  179. package/apps/pwa/out/setup/done/index.txt +23 -23
  180. package/apps/pwa/out/setup/index.html +1 -1
  181. package/apps/pwa/out/setup/index.txt +23 -23
  182. package/apps/pwa/out/share/index.html +1 -1
  183. package/apps/pwa/out/share/index.txt +23 -23
  184. package/apps/pwa/out/showroom/index.html +1 -1
  185. package/apps/pwa/out/showroom/index.txt +22 -22
  186. package/apps/pwa/out/tasks/index.html +1 -1
  187. package/apps/pwa/out/tasks/index.txt +23 -23
  188. package/apps/pwa/out/term/index.html +1 -1
  189. package/apps/pwa/out/term/index.txt +23 -23
  190. package/apps/pwa/out/today/index.html +1 -1
  191. package/apps/pwa/out/today/index.txt +23 -23
  192. package/apps/pwa/out/trace/index.html +1 -1
  193. package/apps/pwa/out/trace/index.txt +23 -23
  194. package/apps/pwa/out/vault/index.html +1 -1
  195. package/apps/pwa/out/vault/index.txt +23 -23
  196. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  197. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  198. package/apps/pwa/out/workflows/index.html +1 -1
  199. package/apps/pwa/out/workflows/index.txt +23 -23
  200. package/apps/pwa/out/workspace/index.html +1 -1
  201. package/apps/pwa/out/workspace/index.txt +23 -23
  202. package/apps/pwa/out/worktrees/index.html +1 -1
  203. package/apps/pwa/out/worktrees/index.txt +23 -23
  204. package/graphs/ops/guardian.yaml +26 -0
  205. package/graphs/ops/recipes.yaml +3 -0
  206. package/package.json +1 -1
  207. package/scripts/coord-channel-watch.sh +2 -2
  208. package/scripts/discord-seat-probe.ts +3 -1
  209. package/scripts/lib/pch-regress-map.ts +23 -0
  210. package/scripts/morning-report.ts +18 -4
  211. package/scripts/pch-regress.ts +88 -0
  212. package/scripts/release-loop/gate-diff.ts +18 -0
  213. package/scripts/release-loop/gate-node.ts +338 -46
  214. package/scripts/release-loop/gate-partial.ts +106 -0
  215. package/scripts/release-loop/gate-timing.ts +120 -0
  216. package/scripts/release-loop/resume-release.ts +64 -5
  217. package/scripts/release-loop/shard-plan.ts +65 -0
  218. package/scripts/release-loop/unattended-release.ts +2 -0
  219. package/scripts/video/scene-table.ts +159 -0
  220. package/src/acp/core-turn-bridge.ts +39 -2
  221. package/src/away/presence.ts +40 -0
  222. package/src/away/release-watch.ts +255 -0
  223. package/src/chat/index.ts +2 -1
  224. package/src/chat/short-question.ts +3 -40
  225. package/src/cli/away-cli.ts +68 -0
  226. package/src/cli/guardian-cli.ts +49 -0
  227. package/src/cli/intake-cli.ts +13 -7
  228. package/src/cli/log-event-names.ts +1 -0
  229. package/src/cli/pod-cli.ts +5 -1
  230. package/src/cli/pty-drive-cli.ts +14 -3
  231. package/src/cli/release-cli.ts +32 -7
  232. package/src/cli/setup-cli.ts +31 -1
  233. package/src/cli/tasks-cli.ts +104 -11
  234. package/src/context-bus/context-now-public.ts +1 -1
  235. package/src/context-bus/context-now-surfaces.ts +16 -6
  236. package/src/context-bus/context-now.ts +97 -3
  237. package/src/core-turn/short-question.ts +100 -0
  238. package/src/dashboard/dashboard-status-lines.ts +24 -4
  239. package/src/dashboard/index.ts +50 -5
  240. package/src/dashboard/input/slash-executor.ts +6 -2
  241. package/src/dashboard/slash-runtime/dashboard-handlers.ts +63 -36
  242. package/src/dashboard/slash-runtime/decide-slash.ts +60 -0
  243. package/src/dashboard/slash-runtime/help-from-registry.ts +21 -8
  244. package/src/dashboard/slash-runtime/help-groups.ts +44 -0
  245. package/src/dashboard/slash-runtime/model-choices.ts +125 -0
  246. package/src/domains/guardian-tick.ts +138 -0
  247. package/src/domains/outbound-alert.ts +44 -13
  248. package/src/domains/schedule-registry.ts +23 -0
  249. package/src/domains/telegram-kind-route.ts +39 -0
  250. package/src/domains/watch-default-scheduler.ts +95 -0
  251. package/src/esc-abort-gate.ts +44 -0
  252. package/src/graph-runner/runner.ts +25 -5
  253. package/src/harness/harness-cli-command.ts +2 -0
  254. package/src/harness/harness-salvage-cli.ts +78 -0
  255. package/src/harness/harness-worktree-add.ts +68 -0
  256. package/src/harness/ledger-write-guard.ts +6 -16
  257. package/src/hq/hq.ts +6 -1
  258. package/src/index.ts +52 -3
  259. package/src/instance/test-write-guard-spy-first.fixture.ts +13 -0
  260. package/src/instance/test-write-guard.ts +200 -0
  261. package/src/intake-plane/digest.ts +39 -15
  262. package/src/intake-plane/harness-target.ts +4 -3
  263. package/src/intake-plane/intake-sources.ts +1 -0
  264. package/src/intake-plane/items.ts +1 -0
  265. package/src/loops/orchestrator/degradation.ts +57 -0
  266. package/src/loops/orchestrator/finish-rate.ts +33 -3
  267. package/src/loops/orchestrator/tick.ts +166 -45
  268. package/src/maturity/feature-maturity.ts +6 -4
  269. package/src/maturity/tui-slash-maturity.ts +1 -0
  270. package/src/mss/logging/log-store.ts +3 -0
  271. package/src/nexus/api/http-server.ts +16 -5
  272. package/src/nexus/api/loop-edges.ts +290 -17
  273. package/src/nexus/api/ops-api.ts +96 -28
  274. package/src/nexus/index.ts +7 -0
  275. package/src/release-loop/checklist.ts +2 -0
  276. package/src/release-loop/feature-store.ts +54 -7
  277. package/src/release-loop/light-rc.ts +173 -0
  278. package/src/release-loop/placement.ts +51 -17
  279. package/src/release-loop/rubric.ts +57 -1
  280. package/src/self-dev/dev-cli.ts +36 -0
  281. package/src/self-dev/orchestrate.ts +1 -0
  282. package/src/self-dev/pr-preview.ts +179 -0
  283. package/src/self-dev/shard-run.ts +23 -7
  284. package/src/self-implement/gate-cli.ts +29 -8
  285. package/src/self-implement/goal-author-clarification.ts +88 -13
  286. package/src/self-implement/goal-clarification-escalation.ts +2 -0
  287. package/src/self-implement/orchestrator.ts +2 -1
  288. package/src/self-implement/salvage-branches.ts +254 -0
  289. package/src/task-agent/actions.ts +63 -2
  290. package/src/task-agent/board.ts +132 -0
  291. package/src/task-agent/card-evidence.ts +203 -0
  292. package/src/task-agent/completion-evidence.ts +126 -0
  293. package/src/task-agent/judge.ts +18 -0
  294. package/src/task-agent/live-moves.ts +176 -0
  295. package/src/task-agent/mission.ts +32 -6
  296. package/src/task-agent/shadow.ts +183 -15
  297. package/src/task-agent/task-hand.ts +117 -15
  298. package/src/task-orchestrator/surfaces/pod-command-job.ts +64 -4
  299. package/src/task-orchestrator/surfaces/pod-lease.ts +142 -13
  300. package/src/task-orchestrator/surfaces/pod-pool.ts +13 -5
  301. package/src/task-orchestrator/surfaces/self-implement-pod.ts +2 -2
  302. package/src/telegram-away-command.ts +38 -0
  303. package/src/telegram-commands.ts +11 -0
  304. package/src/telegram-report-log-probe.ts +21 -0
  305. package/src/telegram-report.ts +27 -3
  306. package/src/tui-client/dashboard-session.ts +1 -0
  307. package/src/user-config.ts +134 -2
  308. package/src/version/packed-revision.json +1 -1
  309. package/apps/pwa/out/_next/static/chunks/1307-30dff22c9e1f3073.js +0 -1
  310. package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +0 -1
  311. package/apps/pwa/out/_next/static/chunks/1844-9ec4b1d12f6e80eb.js +0 -1
  312. package/apps/pwa/out/_next/static/chunks/1866-e7a6999f25548b30.js +0 -1
  313. package/apps/pwa/out/_next/static/chunks/1948.15f356b15a625597.js +0 -5
  314. package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +0 -1
  315. package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +0 -1
  316. package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +0 -1
  317. package/apps/pwa/out/_next/static/chunks/224-798346143007ff65.js +0 -1
  318. package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +0 -1
  319. package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +0 -1
  320. package/apps/pwa/out/_next/static/chunks/2957-96ffab3d521fb08f.js +0 -1
  321. package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +0 -1
  322. package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +0 -1
  323. package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +0 -1
  324. package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +0 -1
  325. package/apps/pwa/out/_next/static/chunks/3857-4f936cdb30322aa6.js +0 -1
  326. package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +0 -1
  327. package/apps/pwa/out/_next/static/chunks/4144-a0041c8dc3be57ac.js +0 -1
  328. package/apps/pwa/out/_next/static/chunks/4554-5761612531d0146b.js +0 -1
  329. package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +0 -1
  330. package/apps/pwa/out/_next/static/chunks/4795-f372490b178583fc.js +0 -1
  331. package/apps/pwa/out/_next/static/chunks/5360-be1f1d8f9fcc9755.js +0 -1
  332. package/apps/pwa/out/_next/static/chunks/5977-a11f37254196b659.js +0 -1
  333. package/apps/pwa/out/_next/static/chunks/5994-ae92c0de046ab9ba.js +0 -1
  334. package/apps/pwa/out/_next/static/chunks/6158.af2e93b7adff709b.js +0 -2
  335. package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +0 -1
  336. package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +0 -1
  337. package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +0 -1
  338. package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +0 -1
  339. package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +0 -1
  340. package/apps/pwa/out/_next/static/chunks/8401-8615a660c84cdb03.js +0 -1
  341. package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +0 -1
  342. package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +0 -1
  343. package/apps/pwa/out/_next/static/chunks/8868-10a3a4440fb0ccd8.js +0 -25
  344. package/apps/pwa/out/_next/static/chunks/9070.067c106528ec1ae6.js +0 -1
  345. package/apps/pwa/out/_next/static/chunks/9103-60fbef33e66f0a46.js +0 -1
  346. package/apps/pwa/out/_next/static/chunks/a9f2b54d-7865aba0f4d2ee49.js +0 -1
  347. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +0 -1
  348. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d92f7ee3994e84c7.js +0 -1
  349. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-4733c967b56a4277.js +0 -1
  350. package/apps/pwa/out/_next/static/chunks/app/board/page-d22fd849cc22f104.js +0 -1
  351. package/apps/pwa/out/_next/static/chunks/app/botlab/page-bb742f995c6769e7.js +0 -1
  352. package/apps/pwa/out/_next/static/chunks/app/bots/page-02b073a2ac793b83.js +0 -1
  353. package/apps/pwa/out/_next/static/chunks/app/ceo/page-60c3da708f9a0ccc.js +0 -1
  354. package/apps/pwa/out/_next/static/chunks/app/chat/page-8a9b3594bc23289b.js +0 -1
  355. package/apps/pwa/out/_next/static/chunks/app/consult/page-a0eb02638b1e6f31.js +0 -1
  356. package/apps/pwa/out/_next/static/chunks/app/control/page-308111abadf35670.js +0 -1
  357. package/apps/pwa/out/_next/static/chunks/app/decisions/page-6eaf7bac37830c21.js +0 -1
  358. package/apps/pwa/out/_next/static/chunks/app/design-check/page-5d97b2901e0d1ea6.js +0 -1
  359. package/apps/pwa/out/_next/static/chunks/app/editor/page-c39554c321576982.js +0 -1
  360. package/apps/pwa/out/_next/static/chunks/app/exec/page-8c3d8e63ae2e1c98.js +0 -1
  361. package/apps/pwa/out/_next/static/chunks/app/field/page-9b72a07d424701c6.js +0 -1
  362. package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +0 -1
  363. package/apps/pwa/out/_next/static/chunks/app/inside/page-bd5d586429167fea.js +0 -1
  364. package/apps/pwa/out/_next/static/chunks/app/intake/page-a572f58cd3ca5864.js +0 -1
  365. package/apps/pwa/out/_next/static/chunks/app/layout-59b7834d497c2255.js +0 -1
  366. package/apps/pwa/out/_next/static/chunks/app/live/page-be5ea965a07fbc41.js +0 -1
  367. package/apps/pwa/out/_next/static/chunks/app/loops/page-c130b63862437814.js +0 -1
  368. package/apps/pwa/out/_next/static/chunks/app/market/page-fa244bd146a00ae8.js +0 -1
  369. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-44d56d6391965a6a.js +0 -1
  370. package/apps/pwa/out/_next/static/chunks/app/morning/page-83a700d4c754d802.js +0 -1
  371. package/apps/pwa/out/_next/static/chunks/app/not-found-7b3170f374f77b9d.js +0 -1
  372. package/apps/pwa/out/_next/static/chunks/app/observatory/page-5f23d0c66a3b4793.js +0 -1
  373. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-a3c58f5c72a5f3f9.js +0 -1
  374. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-a2c4625596f8d69c.js +0 -1
  375. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-0e9099531d239f11.js +0 -1
  376. package/apps/pwa/out/_next/static/chunks/app/outputs/page-e8e1ecaea7cd71d2.js +0 -1
  377. package/apps/pwa/out/_next/static/chunks/app/page-a1b7ffed710a93e0.js +0 -1
  378. package/apps/pwa/out/_next/static/chunks/app/reflection/page-1467047104141297.js +0 -1
  379. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-2da6f9b8a38c6978.js +0 -1
  380. package/apps/pwa/out/_next/static/chunks/app/sessions/page-102310930791197a.js +0 -1
  381. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-f331e38fefc00393.js +0 -1
  382. package/apps/pwa/out/_next/static/chunks/app/settings/page-6ed0b73bae8351d8.js +0 -1
  383. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-44d5f767491692bf.js +0 -1
  384. package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +0 -1
  385. package/apps/pwa/out/_next/static/chunks/app/setup/page-43336055451f6e37.js +0 -1
  386. package/apps/pwa/out/_next/static/chunks/app/share/page-3cc47dca0922ab9a.js +0 -1
  387. package/apps/pwa/out/_next/static/chunks/app/showroom/page-a77ee355a2304ff0.js +0 -1
  388. package/apps/pwa/out/_next/static/chunks/app/tasks/page-b17cd46cf0e9b8cf.js +0 -1
  389. package/apps/pwa/out/_next/static/chunks/app/term/page-61553851aa606146.js +0 -1
  390. package/apps/pwa/out/_next/static/chunks/app/today/page-a1f585e154e61c59.js +0 -1
  391. package/apps/pwa/out/_next/static/chunks/app/trace/page-7bf54c9bb9274af8.js +0 -1
  392. package/apps/pwa/out/_next/static/chunks/app/vault/page-5150ef17b5383a57.js +0 -1
  393. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b9d8ce70792c1672.js +0 -1
  394. package/apps/pwa/out/_next/static/chunks/app/workflows/page-a5a3de231f2cc7d5.js +0 -1
  395. package/apps/pwa/out/_next/static/chunks/app/workspace/page-134da468538baf48.js +0 -1
  396. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31ac4afa2dcd915c.js +0 -1
  397. package/apps/pwa/out/_next/static/chunks/framework-5c169757551134ed.js +0 -1
  398. package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +0 -1
  399. package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +0 -1
  400. package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +0 -1
  401. package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +0 -1
  402. package/apps/pwa/out/_next/static/chunks/webpack-c2782b330447ffb6.js +0 -1
  403. package/apps/pwa/out/_next/static/css/7fa358fec7da7c47.css +0 -1
  404. /package/apps/pwa/out/_next/static/{P6YTVgzuZz5oTZhY4Din_ → J5H7BvqgSI8DUjtRRcA_S}/_ssgManifest.js +0 -0
@@ -0,0 +1,254 @@
1
+ /**
2
+ * SALVAGE-VISIBLE — Pod 런이 멈추며 남긴 수확 가지(`salvage/<job>/<worktree>`)를 «런 하나 → PR | 수확 가지 | 없음 | 모름» 으로 보인다.
3
+ *
4
+ * S: Pod Job 은 자식 rc≠0 이면 워크트리를 `salvage/si-task-<job>/self-impl-…-r<run 16진 앞 6자>` 로 push 한다
5
+ * (`src/task-orchestrator/surfaces/self-implement-pod.ts` salvage 단계 · 이름은 `plannedSelfImplBranch` 의 `-r` 접미).
6
+ * C: 10-07 세 자리가 PR·`salvage-pushed` 로그만 보고 멈춘 Pod 런 여덟을 «수확 0» 으로 읽었다 — 여덟 다 수백 줄짜리 가지가 있었다.
7
+ * Q: 런 id 하나로 그 가지를 «한 번의 목록»에서 찾고, 크기를 재고, 못 읽었을 때 «없음» 이라 말하지 않으려면?
8
+ * A: `git ls-remote origin 'refs/heads/salvage/*'` 한 번 → `-r<6>` 접미로 Job 을 고르고, 같은 Job 의 형제(`/repo`·`-early`)까지
9
+ * numstat 로 재서 `.gitignore`·`docs/goals/**` 만 건드린 가지는 버린다. 목록 실패는 `unknown` 이다(⛔ `none` 이 아니다).
10
+ *
11
+ * 순수 모듈 — git 은 주입된 러너로만 부른다(시험은 네트워크 0).
12
+ */
13
+ import { runGitCommand } from '../git-fs/runner.js';
14
+
15
+ export interface SalvageGitResult {
16
+ status: number | null;
17
+ stdout: string;
18
+ stderr: string;
19
+ }
20
+
21
+ export type SalvageGitRunner = (args: string[]) => SalvageGitResult;
22
+
23
+ export interface SalvageDiffstat {
24
+ files: number;
25
+ insertions: number;
26
+ deletions: number;
27
+ }
28
+
29
+ export interface SalvageBranch {
30
+ branch: string;
31
+ sha: string;
32
+ diffstat: SalvageDiffstat;
33
+ }
34
+
35
+ export type SalvageLookup =
36
+ | { status: 'found'; branches: SalvageBranch[] }
37
+ | { status: 'none'; trivialSkipped: number }
38
+ | { status: 'unknown'; reason: string }
39
+ /** PR 이 이겨서 가지를 «조회하지 않았다» — ⛔ 부재(`none`)가 아니다. */
40
+ | { status: 'not-checked' };
41
+
42
+ export interface RemoteSalvageRef {
43
+ branch: string;
44
+ sha: string;
45
+ }
46
+
47
+ const SALVAGE_REF_PREFIX = 'refs/heads/';
48
+
49
+ /** `run-bfe2b5c1-…` → `bfe2b5` — `plannedSelfImplBranch` 의 `-r` 접미와 같은 규칙. 6자가 안 되면 null(⛔ 짧은 접두는 남의 가지를 문다). */
50
+ export function runSuffixKey(runId: string): string | null {
51
+ // `run-` 뒤에 «연속된» 16진 6자가 있어야 한다 — 구분자를 지우고 이어 붙이면 `run-ab-cdef` 가 남의 `abcdef` 를 문다.
52
+ const m = /^(?:run-)?([0-9a-f]{6})/i.exec(runId.trim());
53
+ return m ? m[1]!.toLowerCase() : null;
54
+ }
55
+
56
+ /** `git ls-remote` 출력 → `{ branch, sha }`. salvage/ 가 아닌 «올바른» 줄은 버리고, 형식이 깨진 비어 있지 않은 줄은 `unparsed` 로 센다
57
+ * (⛔ rc=0 ⊕ 못 읽은 출력을 «없음» 으로 접지 않는다). */
58
+ export function parseLsRemote(stdout: string): RemoteSalvageRef[] & { unparsed?: number } {
59
+ const rows: RemoteSalvageRef[] & { unparsed?: number } = [];
60
+ let unparsed = 0;
61
+ const lines = stdout.split(/\r?\n/);
62
+ if (lines.at(-1) === '') lines.pop();
63
+ for (const line of lines) {
64
+ // 공백만 있는 줄도 «못 읽은 줄» 이다(⛔ 건너뛰면 깨진 목록이 «없음» 이 된다).
65
+ const m = /^([0-9a-f]{40}|[0-9a-f]{64})\t(refs\/\S+)$/i.exec(line);
66
+ if (!m) {
67
+ unparsed += 1;
68
+ continue;
69
+ }
70
+ if (!m[2]!.startsWith(`${SALVAGE_REF_PREFIX}salvage/`)) continue;
71
+ rows.push({ sha: m[1]!.toLowerCase(), branch: m[2]!.slice(SALVAGE_REF_PREFIX.length) });
72
+ }
73
+ if (unparsed > 0) rows.unparsed = unparsed;
74
+ return rows;
75
+ }
76
+
77
+ /** 워크트리 조각(마지막 경로 성분)이 `-r<key>` 또는 `-r<key>-early` 로 끝나는가. */
78
+ export function branchMatchesRun(branch: string, key: string): boolean {
79
+ const leaf = branch.slice(branch.lastIndexOf('/') + 1).toLowerCase();
80
+ return leaf.endsWith(`-r${key}`) || leaf.endsWith(`-r${key}-early`);
81
+ }
82
+
83
+ function jobDir(branch: string): string {
84
+ return branch.slice(0, branch.lastIndexOf('/'));
85
+ }
86
+
87
+ /** 런 하나에 속하는 원격 가지 — `-r<key>` 로 맞은 Job 디렉토리의 형제 전부(`/repo` 포함). */
88
+ export function selectRunSalvageRefs(refs: readonly RemoteSalvageRef[], key: string): RemoteSalvageRef[] {
89
+ const jobs = new Set(refs.filter((ref) => branchMatchesRun(ref.branch, key)).map((ref) => jobDir(ref.branch)));
90
+ return refs.filter((ref) => jobs.has(jobDir(ref.branch)));
91
+ }
92
+
93
+ /** 루트 `.gitignore`·`docs/goals/**` 만 바꾼 가지는 «수확할 일» 이 아니다(Pod 가 `/repo` 에 골 문서만 남긴다). */
94
+ export function isTrivialSalvagePath(path: string): boolean {
95
+ // ⛔ trim 하지 않는다 — `-z` 경로는 원 바이트이고 ` .gitignore` 는 다른 파일이다.
96
+ return path === '.gitignore' || path.startsWith('docs/goals/');
97
+ }
98
+
99
+ /** `git diff --numstat -z --no-renames` → diffstat ⊕ 경로. 레코드는 NUL 로 끊기고 경로는 따옴표 없이 «원 바이트» 그대로다
100
+ * (⛔ `-z` 없이는 한글·특수문자 경로가 `"docs/goals/\355…"` 로 감싸져 제외 규칙을 못 탄다). 바이너리(`-`)는 줄 수 0. */
101
+ export function parseNumstat(stdout: string): { diffstat: SalvageDiffstat; paths: string[]; unparsed: number } {
102
+ const paths: string[] = [];
103
+ let insertions = 0;
104
+ let deletions = 0;
105
+ let unparsed = 0;
106
+ const records = stdout.split('\0');
107
+ // `-z` 출력은 레코드마다 NUL 로 «끝난다» — 마지막 조각이 비어 있지 않으면 잘린 출력이다(⛔ 정상 레코드로 받지 않는다).
108
+ const tail = records.pop() ?? '';
109
+ if (tail !== '') unparsed += 1;
110
+ for (const record of records) {
111
+ if (record === '') {
112
+ unparsed += 1;
113
+ continue;
114
+ }
115
+ const m = /^(\d+|-)\t(\d+|-)\t([^\0]+)$/.exec(record.replace(/^\n+/, ''));
116
+ if (!m) {
117
+ unparsed += 1;
118
+ continue;
119
+ }
120
+ insertions += m[1] === '-' ? 0 : Number(m[1]);
121
+ deletions += m[2] === '-' ? 0 : Number(m[2]);
122
+ paths.push(m[3]!);
123
+ }
124
+ return { diffstat: { files: paths.length, insertions, deletions }, paths, unparsed };
125
+ }
126
+
127
+ /** 러너가 «던져도» 결과로 접는다 — 실행 예외는 rc=null ⊕ 메시지(호출부가 `unknown` 으로 옮긴다). */
128
+ function runSafely(git: SalvageGitRunner, args: string[]): SalvageGitResult {
129
+ try {
130
+ return git(args);
131
+ } catch (error) {
132
+ return { status: null, stdout: '', stderr: `threw: ${error instanceof Error ? error.message : String(error)}` };
133
+ }
134
+ }
135
+
136
+ function failureReason(step: string, result: SalvageGitResult): string {
137
+ const tail = result.stderr.trim().split(/\r?\n/).at(-1) ?? '';
138
+ return `${step} rc=${result.status ?? 'signal'}${tail ? ` · ${tail.slice(0, 160)}` : ''}`;
139
+ }
140
+
141
+ export interface FindSalvageOptions {
142
+ /** 이미 읽은 ls-remote 목록(여러 런을 한 번의 목록으로). 없으면 러너로 한 번 읽는다. */
143
+ listing?: RemoteSalvageRef[] | { error: string };
144
+ }
145
+
146
+ /** 비교 기준 — fetch 가 함께 갱신하는 `origin/main` 하나로 고정한다(다른 기준은 fetch 와 어긋난다). */
147
+ const SALVAGE_DIFF_BASE = 'origin/main';
148
+
149
+ /** `git ls-remote origin 'refs/heads/salvage/*'` 한 번. 실패는 `{ error }` — 호출부가 `unknown` 으로 옮긴다. */
150
+ export function listRemoteSalvageRefs(git: SalvageGitRunner): RemoteSalvageRef[] | { error: string } {
151
+ const result = runSafely(git, ['ls-remote', 'origin', 'refs/heads/salvage/*']);
152
+ if (result.status !== 0) return { error: failureReason('ls-remote', result) };
153
+ const rows = parseLsRemote(result.stdout);
154
+ if (rows.unparsed) return { error: `ls-remote 출력 ${rows.unparsed}줄을 못 읽음` };
155
+ return rows;
156
+ }
157
+
158
+ /** 런 id → 그 런의 수확 가지. 목록·fetch·diff 중 하나라도 못 읽으면 `unknown`(⛔ `none` 으로 접지 않는다). */
159
+ export function findRunSalvageBranches(runId: string, git: SalvageGitRunner, options: FindSalvageOptions = {}): SalvageLookup {
160
+ const key = runSuffixKey(runId);
161
+ if (key === null) return { status: 'unknown', reason: `런 id 에서 16진 6자를 못 뽑았다 (${runId})` };
162
+ const listing = options.listing ?? listRemoteSalvageRefs(git);
163
+ if (!Array.isArray(listing)) return { status: 'unknown', reason: listing.error };
164
+ const refs = selectRunSalvageRefs(listing, key);
165
+ if (refs.length === 0) return { status: 'none', trivialSkipped: 0 };
166
+ const fetched = runSafely(git, ['fetch', '--no-tags', '--quiet', 'origin', 'main', ...refs.map((ref) => `${SALVAGE_REF_PREFIX}${ref.branch}`)]);
167
+ if (fetched.status !== 0) return { status: 'unknown', reason: failureReason('fetch', fetched) };
168
+ const branches: SalvageBranch[] = [];
169
+ let trivialSkipped = 0;
170
+ for (const ref of refs) {
171
+ const diff = runSafely(git, ['diff', '--numstat', '-z', '--no-renames', `${SALVAGE_DIFF_BASE}...${ref.sha}`]);
172
+ if (diff.status !== 0) return { status: 'unknown', reason: failureReason(`diff ${ref.branch}`, diff) };
173
+ const { diffstat, paths, unparsed } = parseNumstat(diff.stdout);
174
+ if (unparsed > 0) return { status: 'unknown', reason: `diff ${ref.branch} 출력 ${unparsed}줄을 못 읽음` };
175
+ if (paths.every(isTrivialSalvagePath)) {
176
+ trivialSkipped += 1;
177
+ continue;
178
+ }
179
+ branches.push({ branch: ref.branch, sha: ref.sha, diffstat });
180
+ }
181
+ branches.sort((a, b) => b.diffstat.insertions + b.diffstat.deletions - (a.diffstat.insertions + a.diffstat.deletions));
182
+ return branches.length > 0 ? { status: 'found', branches } : { status: 'none', trivialSkipped };
183
+ }
184
+
185
+ /** 실물 러너 — 공용 git 심(`runGitCommand` · 락 재시도) 위에서 cwd 를 «인자로» 받는다. */
186
+ export function gitSalvageRunner(cwd: string): SalvageGitRunner {
187
+ return (args) => {
188
+ const result = runGitCommand(cwd, args, { encoding: 'utf8', timeout: 120_000, maxBuffer: 64 * 1024 * 1024 });
189
+ return { status: result.status, stdout: result.stdout, stderr: result.stderr };
190
+ };
191
+ }
192
+
193
+ export type RunPrState =
194
+ | { status: 'pr'; number: number }
195
+ | { status: 'none' }
196
+ | { status: 'unknown'; reason: string };
197
+
198
+ export type RunSalvageOutcome = 'pr' | 'salvage' | 'none' | 'unknown';
199
+
200
+ export interface RunSalvageVerdict {
201
+ runId: string;
202
+ outcome: RunSalvageOutcome;
203
+ pr: RunPrState;
204
+ salvage: SalvageLookup;
205
+ line: string;
206
+ }
207
+
208
+ function branchLine(branch: SalvageBranch): string {
209
+ return `수확 가지 ${branch.branch} (+${branch.diffstat.insertions}/-${branch.diffstat.deletions} · ${branch.diffstat.files} files)`;
210
+ }
211
+
212
+ /** 판정 — PR 이 있으면 PR 이 이긴다. 없으면 수확 가지 → 없음 → 모름 순. PR 을 못 읽었으면 «PR 모름» 을 같이 단다. */
213
+ export function decideRunSalvage(runId: string, pr: RunPrState, salvage: SalvageLookup): RunSalvageVerdict {
214
+ const prNote = pr.status === 'unknown' ? ` · PR 모름(${pr.reason})` : '';
215
+ if (pr.status === 'pr') return { runId, outcome: 'pr', pr, salvage, line: `${runId} PR #${pr.number}` };
216
+ if (salvage.status === 'found') {
217
+ const [first, ...rest] = salvage.branches;
218
+ const more = rest.length > 0 ? ` ⊕ 가지 ${rest.length}개 더` : '';
219
+ return { runId, outcome: 'salvage', pr, salvage, line: `${runId} ${branchLine(first!)}${more}${prNote}` };
220
+ }
221
+ if (salvage.status === 'not-checked') return { runId, outcome: 'unknown', pr, salvage, line: `${runId} 모름(수확 가지 미조회)${prNote}` };
222
+ if (salvage.status === 'unknown') return { runId, outcome: 'unknown', pr, salvage, line: `${runId} 모름(${salvage.reason})${prNote}` };
223
+ const skipped = salvage.trivialSkipped > 0 ? ` (골 문서·.gitignore 만 있는 가지 ${salvage.trivialSkipped}개 제외)` : '';
224
+ // PR 을 못 읽었으면 «없음» 이라 단정하지 않는다.
225
+ if (pr.status === 'unknown') return { runId, outcome: 'unknown', pr, salvage, line: `${runId} 모름(수확 가지 없음${skipped} · PR 모름 — ${pr.reason})` };
226
+ return { runId, outcome: 'none', pr, salvage, line: `${runId} 없음${skipped}` };
227
+ }
228
+
229
+ /** 원장 항목 → PR 상태. 마지막 `pr-opened` 의 번호(없으면 `run-rollup.prNumber`).
230
+ * ⛔ 번호가 없다고 곧 «PR 없음» 이 아니다 — 원장이 «종료(`run-status`)» 를 PR 아닌 단계로 적었을 때만 `none`.
231
+ * 원장 파일이 없거나(호스트가 모르는 Pod 런) 종료 기록이 없거나 종료가 PR 단계인데 번호가 없으면 `unknown`. */
232
+ export function prStateFromLedger(entries: ReadonlyArray<{ event: string; data: Record<string, unknown> }> | null): RunPrState {
233
+ if (entries === null) return { status: 'unknown', reason: '호스트 원장 없음' };
234
+ const pick = (value: unknown): number | null => (typeof value === 'number' && Number.isSafeInteger(value) && value > 0 ? value : null);
235
+ const prNumberOf = (data: Record<string, unknown>): number | null => pick(data.number) ?? pick(data.prNumber)
236
+ ?? (typeof data.prUrl === 'string' ? pick(Number(/\/pull\/(\d+)/.exec(data.prUrl)?.[1])) : null);
237
+ const openedEntries = entries.filter((entry) => entry.event === 'pr-opened');
238
+ const opened = openedEntries.at(-1)?.data;
239
+ // 번호 «있는» 마지막 pr-opened — 뒤에 번호 없는 기록이 와도 알려진 PR 을 버리지 않는다.
240
+ const number = openedEntries.map((entry) => prNumberOf(entry.data)).filter((n): n is number => n !== null).at(-1)
241
+ ?? pick(entries.filter((entry) => entry.event === 'run-rollup').at(-1)?.data.prNumber) ?? null;
242
+ if (number !== null) return { status: 'pr', number };
243
+ if (opened !== undefined) return { status: 'unknown', reason: '원장에 pr-opened 가 있으나 번호 없음' };
244
+ const terminal = entries.filter((entry) => entry.event === 'run-status').at(-1)?.data.stage;
245
+ if (typeof terminal !== 'string') return { status: 'unknown', reason: '원장에 PR·종료 기록 없음' };
246
+ if (!NO_PR_TERMINAL_STAGES.has(terminal)) return { status: 'unknown', reason: `마지막 단계 ${terminal} — PR 없는 종료로 확인 안 됨` };
247
+ return { status: 'none' };
248
+ }
249
+
250
+ /** PR 을 열기 «전에» 끝나는 종료 단계(`run-status-mapping.ts` `SelfImplementStage` 중). ⛔ merged·pr-opened·merge-ready·merge-conflict 는
251
+ * PR 이 있었을 단계라 빠진다 — 그 단계인데 번호가 없으면 `unknown`. 진행 중 단계도 여기 없으므로 `unknown`. */
252
+ const NO_PR_TERMINAL_STAGES: ReadonlySet<string> = new Set([
253
+ 'worktree-completed', 'gate-failed', 'review-blocked', 'no-changes', 'aborted', 'timed-out', 'pr-declined', 'soft-stopped', 'parked',
254
+ ]);
@@ -1,4 +1,29 @@
1
- export type NextActionKind = 'review' | 'land' | 'retry' | 'green' | 'decision';
1
+ import { copyFileSync, lstatSync, mkdirSync, realpathSync } from 'node:fs';
2
+ import { join, relative, resolve, sep } from 'node:path';
3
+ import { debug } from '../debug/log.js';
4
+ import { judgeNextMove } from './judge.js';
5
+ import { checkCompletionEvidence } from './completion-evidence.js';
6
+
7
+ export type DeliveryKind = 'research-report' | 'artifact' | 'content';
8
+ export interface DeliveryEvidence { ok: boolean; files: string[]; reason: string }
9
+
10
+ /**
11
+ * DEFAULT-HANDS-a: 판독은 복사된 최종 산출물에서만 하고, 규칙은 `completion-evidence.ts` 의
12
+ * `checkCompletionEvidence` 하나를 쓴다(종류별 규칙을 여기서 다시 짓지 않는다).
13
+ * 후보 중 하나라도 그 종류의 증거를 다 갖추면 ok — 후보 목록엔 곁 파일(미리보기 등)도 섞인다.
14
+ */
15
+ export function readDeliveryEvidence(kind: DeliveryKind, outputDir: string, files: readonly string[]): DeliveryEvidence {
16
+ if (!files.length) return { ok: false, files: [], reason: '전달 후보 없음' };
17
+ const missing = new Set<string>();
18
+ for (const file of files) {
19
+ const verdict = checkCompletionEvidence(kind, { dir: outputDir, ref: relative(outputDir, file) });
20
+ if (verdict.ok) return { ok: true, files: [...files], reason: `전달 산출물 확인: ${verdict.ref}` };
21
+ for (const item of verdict.missing) missing.add(item);
22
+ }
23
+ return { ok: false, files: [...files], reason: `전달 증거 부족: ${[...missing].join(', ')}` };
24
+ }
25
+
26
+ export type NextActionKind = 'review' | 'land' | 'retry' | 'green' | 'decision' | 'deliver';
2
27
  /**
3
28
  * `retry` 의 갈래(RFC §A9 G1·G2). 없으면 종전 그대로 «must-fix 로 좁힌 재발사»다(must-fix 가 없으면 카드).
4
29
  * - narrow: 범위를 좁혀 한 번 재발사(must-fix 없이도 간다 — no-progress)
@@ -22,6 +47,7 @@ export interface NextAction {
22
47
  mustFix?: string[];
23
48
  cwd?: string;
24
49
  risk?: 'money' | 'security' | 'irreversible-publication';
50
+ delivery?: { kind: DeliveryKind; worktreePath: string; projectTarget: string; files: string[] };
25
51
  }
26
52
  export interface ActionDeps {
27
53
  command(args: string[]): Promise<{ status: number; stdout: string; stderr?: string }>;
@@ -63,7 +89,11 @@ export async function executeNextAction(action: NextAction, deps: ActionDeps): P
63
89
  deps.observe('task-agent.action', { taskId: action.taskId, kind: action.kind, result, reason });
64
90
  return result;
65
91
  };
66
- if (deps.mode !== 'live') return record('shadow', `would ${action.kind}${action.variant ? `:${action.variant}` : ''}: ${action.rationale}`);
92
+ if (deps.mode !== 'live') {
93
+ const destination = action.delivery && action.kind === 'deliver'
94
+ ? ` → ${join(action.delivery.projectTarget, 'elanous-out', action.taskId)}/${action.delivery.files.join(', ')}` : '';
95
+ return record('shadow', `would ${action.kind}${action.variant ? `:${action.variant}` : ''}: ${action.rationale}${destination}`);
96
+ }
67
97
  const card = async (reason: string) => {
68
98
  const evidence = JSON.stringify({ runId: action.runId, pr: action.pr, mustFix: action.mustFix ?? [], history: action.history ?? [], selected: action.kind, reason });
69
99
  const result = await deps.command(['decisions', 'raise', '--title', `${action.taskId}: ${reason}`, '--category', action.risk === 'money' ? 'money' : action.risk === 'security' ? 'security' : action.risk === 'irreversible-publication' ? 'publish' : 'other', '--s', `Task ${action.taskId} needs a decision.`, '--c', reason, '--ref', evidence, '--q', 'Which action should be taken?', '--option', 'h=Keep on hold:no automatic execution', '--option', 'p=Approve action:execute after approval', '--skip-recommend', 'requires human decision', '--no-xcheck', 'automated escalation']);
@@ -87,6 +117,37 @@ export async function executeNextAction(action: NextAction, deps: ActionDeps): P
87
117
  if (deps.reserveLanding ? !deps.reserveLanding(day, limit) : (deps.landingsToday ?? 0) >= limit) return card('daily landing limit');
88
118
  }
89
119
  if (action.kind === 'decision') return card(action.rationale);
120
+ if (action.kind === 'deliver') {
121
+ const delivery = action.delivery;
122
+ if (!delivery?.files.length) return record('waiting', '전달 후보 없음');
123
+ if (!/^[a-zA-Z0-9_-]+$/.test(action.taskId)) return record('failed', 'invalid delivery taskId');
124
+ const output = join(delivery.projectTarget, 'elanous-out', action.taskId);
125
+ const copied: string[] = [];
126
+ try {
127
+ mkdirSync(output, { recursive: true });
128
+ // 경로 비교는 «실경로끼리» 한다 — macOS 의 /var→/private/var 처럼 조상 링크가 있으면 resolve() 와 realpath 가 늘 갈린다.
129
+ const outputReal = realpathSync(output);
130
+ if (outputReal !== join(realpathSync(delivery.projectTarget), 'elanous-out', action.taskId)) throw new Error('delivery output points outside target');
131
+ const root = realpathSync(delivery.worktreePath) + sep;
132
+ for (const name of delivery.files) {
133
+ const source = resolve(root, name);
134
+ if (!source.startsWith(root) || !lstatSync(source).isFile() || !realpathSync(source).startsWith(root)) throw new Error(`invalid delivery file: ${name}`);
135
+ const destination = resolve(output, name);
136
+ if (!destination.startsWith(resolve(output) + sep)) throw new Error(`invalid destination: ${name}`);
137
+ mkdirSync(join(destination, '..'), { recursive: true });
138
+ const parent = realpathSync(join(destination, '..'));
139
+ if (parent !== outputReal && !parent.startsWith(outputReal + sep)) throw new Error(`invalid destination parent: ${name}`);
140
+ try { if (!lstatSync(destination).isFile()) throw new Error(`invalid destination: ${name}`); }
141
+ catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; }
142
+ copyFileSync(source, destination);
143
+ copied.push(destination);
144
+ }
145
+ } catch (error) { return record('failed', `delivery copy failed: ${String(error)}`); }
146
+ const evidence = readDeliveryEvidence(delivery.kind, resolve(output), copied);
147
+ debug.log('task-agent', 'deliver', { taskId: action.taskId, kind: delivery.kind, files: copied, evidenceOk: evidence.ok });
148
+ const judgement = judgeNextMove({ deliveryEvidence: evidence });
149
+ return record(judgement.move === 'propose-green' ? 'done' : 'waiting', `${judgement.move}: ${judgement.reason}`);
150
+ }
90
151
  if (action.kind === 'green') return card('green requires a verified landing in this execution');
91
152
  // G1 — 기다림은 발사가 아니다: 대기 표지만 남기고 다음 틱이 다시 판단한다(카드로 올리지 않는다).
92
153
  if (action.kind === 'retry' && action.variant === 'wait') return record('waiting', `wait marker: ${action.rationale}`);
@@ -0,0 +1,132 @@
1
+ /**
2
+ * 보드 일반형 — 프로젝트 › 목표 › 이정표 › 과제 (RFC-loop-agent-map §A4b①).
3
+ *
4
+ * «판(0.2.x)·칸» 체크리스트는 이 보드의 한 사례(내부 팩)다. 어댑터 둘은 «읽기»만 한다:
5
+ * - `cardsBoard(statePath)` — task-agent-actions.json 의 과제 카드(project.id · goal · milestone)를 트리로.
6
+ * - `releasePackBoard(version)` — 릴리스 체크리스트를 project=elanous · milestone=판 · task=칸 으로 사상. 원장은 쓰지 않는다.
7
+ */
8
+ import { existsSync } from 'node:fs';
9
+ import { join } from 'node:path';
10
+ import { releaseLedgerRoot } from '../instance/resolve.js';
11
+ import { listChecklist, type ChecklistStatus } from '../release-loop/checklist.js';
12
+ import { readTaskAgentState, type CompletionKind, type TaskCard } from './task-hand.js';
13
+
14
+ export type BoardLevel = 'project' | 'goal' | 'milestone' | 'task';
15
+ export type BoardStatus = 'open' | 'done' | 'blocked';
16
+
17
+ export interface BoardNode {
18
+ level: BoardLevel;
19
+ id: string;
20
+ title: string;
21
+ status: BoardStatus;
22
+ /** 과제 노드만 — 카드의 종결 종류(비면 code-pr 로 읽는다 · 칸은 싣지 않는다). */
23
+ completion?: CompletionKind;
24
+ /** 부모 노드 id(프로젝트는 없다). */
25
+ parent?: string;
26
+ /** 프로젝트 노드만 — 프로젝트 없는 카드의 자리(id 는 빈 문자열이라 실제 프로젝트 id 와 겹치지 않는다). */
27
+ unassigned?: true;
28
+ children?: BoardNode[];
29
+ }
30
+
31
+ /** 프로젝트가 없는 카드가 모이는 자리의 id — 빈 문자열(실제 프로젝트 id 는 비지 않는다 · `handTask` 가 빈 id 를 거부). */
32
+ export const UNASSIGNED_PROJECT = '';
33
+ /** 내부 팩(릴리스 체크리스트)의 프로젝트 id. */
34
+ export const INTERNAL_PACK_PROJECT = 'elanous';
35
+
36
+ function branch(level: BoardLevel, id: string, title: string, parent?: string): BoardNode {
37
+ // 무소속 프로젝트의 id 는 '' — 빈 문자열도 부모 id 로 보존한다.
38
+ return { level, id, title, status: 'open', ...(parent !== undefined ? { parent } : {}), children: [] };
39
+ }
40
+
41
+ function child(parent: BoardNode, level: BoardLevel, id: string, title = id): BoardNode {
42
+ let node = parent.children!.find((candidate) => candidate.level === level && candidate.id === id);
43
+ if (!node) {
44
+ node = branch(level, id, title, parent.id);
45
+ parent.children!.push(node);
46
+ }
47
+ return node;
48
+ }
49
+
50
+ /** 자식이 있는 노드의 상태 = 막힘 하나라도 → blocked · 전부 done → done · 그 밖 open. */
51
+ function aggregate(node: BoardNode): void {
52
+ if (!node.children?.length) return;
53
+ for (const next of node.children) aggregate(next);
54
+ node.status = node.children.some((next) => next.status === 'blocked') ? 'blocked'
55
+ : node.children.every((next) => next.status === 'done') ? 'done' : 'open';
56
+ }
57
+
58
+ function cardStatus(card: TaskCard): BoardStatus {
59
+ if (card.status === 'launch-failed') return 'blocked';
60
+ return card.greenProposal ? 'done' : 'open';
61
+ }
62
+
63
+ /** 과제 카드 → 프로젝트 트리. 프로젝트 없는 카드는 «무소속»(`unassigned: true` · id '') 아래 — 어떤 프로젝트 id 로도 걸러지지 않는다. */
64
+ export function cardsBoard(statePath: string): BoardNode[] {
65
+ const projects: BoardNode[] = [];
66
+ const cards = readTaskAgentState<{ tasks?: Record<string, TaskCard> }>(statePath).tasks ?? {};
67
+ for (const card of Object.values(cards)) {
68
+ const projectId = card.project?.id?.trim() || UNASSIGNED_PROJECT;
69
+ const unassigned = projectId === UNASSIGNED_PROJECT;
70
+ let project = projects.find((node) => node.id === projectId && (node.unassigned === true) === unassigned);
71
+ if (!project) {
72
+ project = unassigned ? { ...branch('project', UNASSIGNED_PROJECT, '무소속'), unassigned: true } : branch('project', projectId, projectId);
73
+ projects.push(project);
74
+ }
75
+ const goal = card.goal ? child(project, 'goal', card.goal) : project;
76
+ const milestone = card.milestone ? child(goal, 'milestone', card.milestone) : goal;
77
+ milestone.children!.push({
78
+ level: 'task', id: card.id, title: card.text, status: cardStatus(card),
79
+ ...(card.completion ? { completion: card.completion } : {}),
80
+ parent: milestone.id,
81
+ });
82
+ }
83
+ for (const project of projects) aggregate(project);
84
+ return projects;
85
+ }
86
+
87
+ function releaseStatus(status: ChecklistStatus): BoardStatus {
88
+ return status === 'green' || status === 'done' ? 'done' : status === 'red' ? 'blocked' : 'open';
89
+ }
90
+
91
+ /**
92
+ * 릴리스 체크리스트 → 내부 팩 보드(읽기 사상). 원장이 없으면 빈 보드 — 만들지 않는다.
93
+ * root 를 명시해 넘겨 옛 JSON 가져오기(쓰기)를 건너뛴다: 이 어댑터는 원장을 바꾸지 않는다.
94
+ */
95
+ export function releasePackBoard(version: string, root = releaseLedgerRoot()): BoardNode[] {
96
+ if (!existsSync(join(root, 'release', 'features.sqlite'))) return [];
97
+ const checklist = listChecklist(version, root);
98
+ const project = branch('project', INTERNAL_PACK_PROJECT, 'elanous (내부 팩)');
99
+ const milestone = child(project, 'milestone', checklist.version, `판 ${checklist.version}`);
100
+ for (const item of checklist.items) {
101
+ milestone.children!.push({ level: 'task', id: item.id, title: item.title, status: releaseStatus(item.status), parent: milestone.id });
102
+ }
103
+ aggregate(project);
104
+ return [project];
105
+ }
106
+
107
+ function cloneNode(node: BoardNode): BoardNode {
108
+ return { ...node, ...(node.children ? { children: node.children.map(cloneNode) } : {}) };
109
+ }
110
+
111
+ /** 같은 수준·id 의 프로젝트·목표·이정표는 한 노드로 합친다(과제는 그대로 잇는다 — 다른 원천의 과제를 숨기지 않는다). */
112
+ function mergeInto(target: BoardNode[], incoming: readonly BoardNode[]): void {
113
+ for (const node of incoming) {
114
+ const existing = node.level === 'task' ? undefined
115
+ : target.find((candidate) => candidate.level === node.level && candidate.id === node.id && candidate.unassigned === node.unassigned);
116
+ if (existing) mergeInto(existing.children ??= [], node.children ?? []);
117
+ else target.push(cloneNode(node));
118
+ }
119
+ }
120
+
121
+ /** 두 어댑터를 합친다 — 같은 수준·id 의 노드는 한 노드로(예: 카드의 이정표 0.2.19 ⊕ 체크리스트의 판 0.2.19) · 상태를 다시 모은다. 입력은 바꾸지 않는다. */
122
+ export function mergeBoards(...boards: readonly BoardNode[][]): BoardNode[] {
123
+ const merged: BoardNode[] = [];
124
+ for (const board of boards) mergeInto(merged, board);
125
+ for (const project of merged) aggregate(project);
126
+ return merged;
127
+ }
128
+
129
+ /** 트리 안의 과제 노드 수. */
130
+ export function countBoardTasks(nodes: readonly BoardNode[]): number {
131
+ return nodes.reduce((count, node) => count + (node.level === 'task' ? 1 : 0) + countBoardTasks(node.children ?? []), 0);
132
+ }