elanous 0.2.25 → 0.2.28

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 (337) 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/chunks/{1454-37fd16f479f5554a.js → 1454-29d1d773d5996a49.js} +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{599-a914cc0468f56143.js → 599-56f2c0a5cc4a7295.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/app/approvals/page-5ab1e47978577c5e.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-24453f6a302816b4.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/app/board/page-6c131d30bb1cf871.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-b12149c51f7d04b1.js → page-fa46d4588e8442c2.js} +1 -1
  9. package/apps/pwa/out/_next/static/chunks/app/bots/page-dc0772b11861af51.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/app/ceo/page-66ad46ed0279feb9.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/app/chat/page-5159dc8752c6cd29.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/app/consult/page-c5a2478861c08b87.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/app/control/page-f722a9dec084e08b.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/app/decisions/{page-305cd294e754906e.js → page-256ffd365d32e100.js} +1 -1
  15. package/apps/pwa/out/_next/static/chunks/app/design-check/{page-dea4b795741126fd.js → page-e0de78ae7b99f01c.js} +1 -1
  16. package/apps/pwa/out/_next/static/chunks/app/editor/page-cfbdc44b708d9801.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/app/exec/{page-45889e7070d11126.js → page-4c741eb760f2f77c.js} +1 -1
  18. package/apps/pwa/out/_next/static/chunks/app/field/{page-16a2e893f83ae4d0.js → page-e9ba98f5f1440a21.js} +1 -1
  19. package/apps/pwa/out/_next/static/chunks/app/inside/page-dafc901bb1b72b5f.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/app/intake/{page-56888bc744e08dac.js → page-2f6f303fa114ec24.js} +1 -1
  21. package/apps/pwa/out/_next/static/chunks/app/layout-1634081c5272006c.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/app/live/{page-952c6ceec6d9de38.js → page-ced0bcfc6542b3fc.js} +1 -1
  23. package/apps/pwa/out/_next/static/chunks/app/live-run/{page-6d0202af568f4c63.js → page-bb1f05cd51c38944.js} +1 -1
  24. package/apps/pwa/out/_next/static/chunks/app/loops/page-646b50f63b6fe732.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/app/market/page-db069b48a59c8c00.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-4fb6fc34e20c07b7.js → page-f736324afa2117c1.js} +1 -1
  27. package/apps/pwa/out/_next/static/chunks/app/morning/{page-e9502921f4f01b1f.js → page-6f131b51193a36f9.js} +1 -1
  28. package/apps/pwa/out/_next/static/chunks/app/{not-found-b6169ea2f09cf287.js → not-found-435f5e2faf02bc01.js} +1 -1
  29. package/apps/pwa/out/_next/static/chunks/app/observatory/{page-6a14333d1b1b3967.js → page-497e0eedf20e7890.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-8c91a9a66c9ec90a.js → page-a8d28fdf995e16ea.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8966452228753c5e.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fd5d18656cb432b6.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/app/outputs/page-5663779cb1a37609.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/app/{page-3d3fc73dd187ed52.js → page-66a67eb19ebd976b.js} +1 -1
  35. package/apps/pwa/out/_next/static/chunks/app/reflection/page-3b492aa78cf3c4a6.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-9c04dac0831c2bbb.js → page-c71267e7681da982.js} +1 -1
  37. package/apps/pwa/out/_next/static/chunks/app/sessions/page-a21d1e1797172692.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-04de9ca6d52a3893.js → page-cb249e81ed339318.js} +1 -1
  39. package/apps/pwa/out/_next/static/chunks/app/settings/{page-62527677bbc2fdc4.js → page-781cfc5114e097da.js} +1 -1
  40. package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-9a2e09c4a311764c.js → page-478157214db6a023.js} +1 -1
  41. package/apps/pwa/out/_next/static/chunks/app/setup/{page-bfeb35d69e8b24bb.js → page-86141f12cf090b7f.js} +1 -1
  42. package/apps/pwa/out/_next/static/chunks/app/share/{page-89cb33bced824e4a.js → page-7256c73bb1c36751.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/app/showroom/page-5c738ca7adef3de5.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/app/tasks/page-0138c8091ec17224.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/app/term/page-cc2f24f16d85e735.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/app/today/{page-6445ac4cbb8dd869.js → page-979d318a534e92da.js} +1 -1
  47. package/apps/pwa/out/_next/static/chunks/app/trace/page-182d394f7e6e096b.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/app/vault/page-21050928a697ed7a.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-44b9d807005a9030.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/workflows/{page-20162174b77f96cf.js → page-601fb77340e63c9a.js} +1 -1
  51. package/apps/pwa/out/_next/static/chunks/app/workspace/page-75d3fb5d47af0edf.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/worktrees/{page-05a4b2c9b91034aa.js → page-11bf9e5725a72c22.js} +1 -1
  53. package/apps/pwa/out/approvals/index.html +1 -1
  54. package/apps/pwa/out/approvals/index.txt +13 -13
  55. package/apps/pwa/out/autopilot/index.html +1 -1
  56. package/apps/pwa/out/autopilot/index.txt +13 -13
  57. package/apps/pwa/out/board/index.html +1 -1
  58. package/apps/pwa/out/board/index.txt +13 -13
  59. package/apps/pwa/out/botlab/index.html +1 -1
  60. package/apps/pwa/out/botlab/index.txt +13 -13
  61. package/apps/pwa/out/bots/index.html +1 -1
  62. package/apps/pwa/out/bots/index.txt +13 -13
  63. package/apps/pwa/out/ceo/index.html +1 -1
  64. package/apps/pwa/out/ceo/index.txt +13 -13
  65. package/apps/pwa/out/chat/index.html +1 -1
  66. package/apps/pwa/out/chat/index.txt +13 -13
  67. package/apps/pwa/out/consult/index.html +1 -1
  68. package/apps/pwa/out/consult/index.txt +13 -13
  69. package/apps/pwa/out/control/index.html +1 -1
  70. package/apps/pwa/out/control/index.txt +13 -13
  71. package/apps/pwa/out/decisions/index.html +1 -1
  72. package/apps/pwa/out/decisions/index.txt +13 -13
  73. package/apps/pwa/out/design-check/index.html +1 -1
  74. package/apps/pwa/out/design-check/index.txt +13 -13
  75. package/apps/pwa/out/editor/index.html +1 -1
  76. package/apps/pwa/out/editor/index.txt +13 -13
  77. package/apps/pwa/out/exec/index.html +1 -1
  78. package/apps/pwa/out/exec/index.txt +13 -13
  79. package/apps/pwa/out/field/index.html +1 -1
  80. package/apps/pwa/out/field/index.txt +13 -13
  81. package/apps/pwa/out/healthz +1 -1
  82. package/apps/pwa/out/index.html +1 -1
  83. package/apps/pwa/out/index.txt +13 -13
  84. package/apps/pwa/out/inside/index.html +1 -1
  85. package/apps/pwa/out/inside/index.txt +13 -13
  86. package/apps/pwa/out/intake/index.html +1 -1
  87. package/apps/pwa/out/intake/index.txt +13 -13
  88. package/apps/pwa/out/live/index.html +1 -1
  89. package/apps/pwa/out/live/index.txt +13 -13
  90. package/apps/pwa/out/live-run/index.html +1 -1
  91. package/apps/pwa/out/live-run/index.txt +13 -13
  92. package/apps/pwa/out/loops/index.html +1 -1
  93. package/apps/pwa/out/loops/index.txt +13 -13
  94. package/apps/pwa/out/market/index.html +1 -1
  95. package/apps/pwa/out/market/index.txt +13 -13
  96. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  97. package/apps/pwa/out/missions/placeholder/index.txt +13 -13
  98. package/apps/pwa/out/morning/index.html +1 -1
  99. package/apps/pwa/out/morning/index.txt +13 -13
  100. package/apps/pwa/out/observatory/index.html +1 -1
  101. package/apps/pwa/out/observatory/index.txt +13 -13
  102. package/apps/pwa/out/ops/checklist/index.html +1 -1
  103. package/apps/pwa/out/ops/checklist/index.txt +13 -13
  104. package/apps/pwa/out/ops/release/index.html +1 -1
  105. package/apps/pwa/out/ops/release/index.txt +13 -13
  106. package/apps/pwa/out/ops/seats/index.html +1 -1
  107. package/apps/pwa/out/ops/seats/index.txt +13 -13
  108. package/apps/pwa/out/outputs/index.html +1 -1
  109. package/apps/pwa/out/outputs/index.txt +13 -13
  110. package/apps/pwa/out/reflection/index.html +1 -1
  111. package/apps/pwa/out/reflection/index.txt +14 -14
  112. package/apps/pwa/out/scheduler/index.html +1 -1
  113. package/apps/pwa/out/scheduler/index.txt +13 -13
  114. package/apps/pwa/out/sessions/index.html +1 -1
  115. package/apps/pwa/out/sessions/index.txt +13 -13
  116. package/apps/pwa/out/settings/devices/index.html +1 -1
  117. package/apps/pwa/out/settings/devices/index.txt +13 -13
  118. package/apps/pwa/out/settings/index.html +1 -1
  119. package/apps/pwa/out/settings/index.txt +13 -13
  120. package/apps/pwa/out/setup/done/index.html +1 -1
  121. package/apps/pwa/out/setup/done/index.txt +13 -13
  122. package/apps/pwa/out/setup/index.html +1 -1
  123. package/apps/pwa/out/setup/index.txt +13 -13
  124. package/apps/pwa/out/share/index.html +1 -1
  125. package/apps/pwa/out/share/index.txt +13 -13
  126. package/apps/pwa/out/showroom/index.html +1 -1
  127. package/apps/pwa/out/showroom/index.txt +13 -13
  128. package/apps/pwa/out/tasks/index.html +1 -1
  129. package/apps/pwa/out/tasks/index.txt +13 -13
  130. package/apps/pwa/out/term/index.html +1 -1
  131. package/apps/pwa/out/term/index.txt +13 -13
  132. package/apps/pwa/out/today/index.html +1 -1
  133. package/apps/pwa/out/today/index.txt +13 -13
  134. package/apps/pwa/out/trace/index.html +1 -1
  135. package/apps/pwa/out/trace/index.txt +13 -13
  136. package/apps/pwa/out/vault/index.html +1 -1
  137. package/apps/pwa/out/vault/index.txt +13 -13
  138. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  139. package/apps/pwa/out/workflows/chat-ui/index.txt +13 -13
  140. package/apps/pwa/out/workflows/index.html +1 -1
  141. package/apps/pwa/out/workflows/index.txt +13 -13
  142. package/apps/pwa/out/workspace/index.html +1 -1
  143. package/apps/pwa/out/workspace/index.txt +13 -13
  144. package/apps/pwa/out/worktrees/index.html +1 -1
  145. package/apps/pwa/out/worktrees/index.txt +13 -13
  146. package/graphs/implement-loop.yaml +38 -1
  147. package/graphs/steward/recipes.yaml +6 -0
  148. package/graphs/steward/release-day.yaml +23 -0
  149. package/graphs/steward/steward.yaml +9 -1
  150. package/graphs/work/code-pr.yaml +23 -0
  151. package/package.json +1 -1
  152. package/scripts/browser-observe-cycle.ts +76 -2
  153. package/scripts/ci-typecheck-changed.ts +119 -7
  154. package/scripts/coord-msg-bridge.ts +209 -0
  155. package/scripts/coord-post.sh +40 -7
  156. package/scripts/coord-requests-bridge.ts +10 -5
  157. package/scripts/detect-test-interference.ts +31 -1
  158. package/scripts/expose-rubric-suggest.ts +1 -1
  159. package/scripts/measure-tsc-affected-roots.ts +10 -1
  160. package/scripts/release-loop/docs-node.ts +51 -22
  161. package/scripts/release-loop/export-check-node.ts +8 -5
  162. package/scripts/release-loop/gate-node.ts +37 -4
  163. package/scripts/release-loop/node-verdict.ts +10 -3
  164. package/scripts/release-loop/publish-node.ts +3 -0
  165. package/scripts/release-loop/tui-node-checks.ts +5 -2
  166. package/scripts/skills/public-check.ts +5 -2
  167. package/scripts/test-deterministic.ts +520 -37
  168. package/scripts/ux-sim.ts +7 -1
  169. package/scripts/verify-kpack6-live.ts +230 -0
  170. package/src/acp/codex-plugins.ts +17 -0
  171. package/src/agent/task-notification.ts +32 -2
  172. package/src/agent-mission/in-agent-plugin.ts +8 -3
  173. package/src/agent-substrate/review-observation.ts +9 -2
  174. package/src/agent-substrate/self-review-cli.ts +62 -16
  175. package/src/boot/daemon-tools/self-implement.ts +72 -4
  176. package/src/briefing/brief-items.ts +151 -35
  177. package/src/budget/llm-cost.ts +30 -3
  178. package/src/budget/usage-outcomes.ts +32 -0
  179. package/src/claims/claims-ledger.ts +23 -0
  180. package/src/claims/claims-render.ts +22 -2
  181. package/src/cli/brief-cli.ts +131 -10
  182. package/src/cli/claims-cli.ts +24 -4
  183. package/src/cli/control-cli.ts +29 -0
  184. package/src/cli/doctor-cli.ts +90 -8
  185. package/src/cli/msg-cli.ts +177 -11
  186. package/src/cli/plugin-cli.ts +11 -3
  187. package/src/cli/pr-cli.ts +133 -18
  188. package/src/cli/pr-land-overlap-evidence.ts +18 -10
  189. package/src/cli/pr-land-refusal.ts +20 -0
  190. package/src/cli/release-cli.ts +34 -5
  191. package/src/cli/rfc-cli.ts +24 -0
  192. package/src/cli/seat-cli.ts +121 -0
  193. package/src/cli/self-utterances-cli.ts +140 -0
  194. package/src/cli/tailscale-serve.ts +5 -8
  195. package/src/cli/tasks-cli.ts +528 -31
  196. package/src/cli/usage-cli.ts +9 -4
  197. package/src/context-card/cell.ts +40 -0
  198. package/src/dashboard/boot-timing.ts +35 -0
  199. package/src/dashboard/first-screen-band.ts +63 -13
  200. package/src/dashboard/index.ts +94 -27
  201. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +26 -0
  202. package/src/dashboard/skill-detection-runtime.ts +24 -4
  203. package/src/dashboard/slash-runtime/dashboard-handlers.ts +6 -0
  204. package/src/debug/log.ts +1 -1
  205. package/src/domains/morning-report.ts +3 -1
  206. package/src/domains/release-tool.ts +29 -12
  207. package/src/domains/surface-events.ts +16 -0
  208. package/src/elanous-config-dir.ts +2 -2
  209. package/src/fleet/host-doctor.ts +73 -2
  210. package/src/github/pr-labels.ts +10 -4
  211. package/src/graph-runner/runner.ts +104 -15
  212. package/src/harness/harness-clean.ts +21 -3
  213. package/src/harness/harness-cli-command.ts +68 -10
  214. package/src/harness/harness-pod-dispatch.ts +11 -4
  215. package/src/harness/harness-stop.ts +11 -7
  216. package/src/hq/seats.ts +17 -3
  217. package/src/index.ts +69 -9
  218. package/src/instance/current.ts +2 -6
  219. package/src/instance/resolve.ts +5 -6
  220. package/src/intelligence-map/model-bench-scores.ts +102 -0
  221. package/src/llm/openai-sampling-fields.ts +89 -0
  222. package/src/llm/runtime-model-family.ts +61 -0
  223. package/src/llm/zai-coding-plan.ts +27 -1
  224. package/src/llm.ts +13 -89
  225. package/src/loops/orchestrator/inbox.ts +73 -0
  226. package/src/loops/orchestrator/resync-shadow.ts +100 -12
  227. package/src/loops/orchestrator/tick.ts +73 -2
  228. package/src/machines/machine-ledger.ts +31 -3
  229. package/src/maturity/cli-maturity.ts +13 -3
  230. package/src/maturity/feature-maturity.ts +1 -1
  231. package/src/msg/msg-sse-server.ts +141 -0
  232. package/src/msg/msg-store.ts +149 -17
  233. package/src/ops/overview-collect.ts +3 -2
  234. package/src/ops/overview-owner.ts +29 -0
  235. package/src/patents/ledger.ts +128 -0
  236. package/src/plugin-hooks/shell-hook.ts +17 -2
  237. package/src/plugin-missions/evaluator-runner.ts +18 -2
  238. package/src/plugins/install/market-fetch.ts +32 -1
  239. package/src/plugins/install/plugin-install.ts +11 -1
  240. package/src/pod-lease/admission.ts +116 -41
  241. package/src/prompt-enhance/enhance.ts +11 -0
  242. package/src/release-loop/checklist.ts +52 -4
  243. package/src/release-loop/feature-store.ts +10 -9
  244. package/src/release-loop/merged-pr-checklist.ts +24 -5
  245. package/src/release-loop/rubric.ts +70 -0
  246. package/src/rfc-registry/registry.ts +88 -0
  247. package/src/seat-dispatch/seat-request-ledger.ts +4 -0
  248. package/src/seat-loop/knowhow-lint.ts +233 -0
  249. package/src/seat-loop/seat-loop.ts +5 -1
  250. package/src/seat-loop/seat-pack.ts +16 -4
  251. package/src/seat-loop/traffic-signal.ts +7 -0
  252. package/src/self-dev/ask-launch-flow.ts +48 -8
  253. package/src/self-dev/ask-path-candidates.ts +69 -14
  254. package/src/self-dev/decompose-proposal.ts +43 -1
  255. package/src/self-dev/draft-sweep.ts +23 -14
  256. package/src/self-dev/graph-journey-nodes.ts +2 -0
  257. package/src/self-dev/launch-preflight-text.ts +5 -1
  258. package/src/self-dev/launch-preflight.ts +143 -10
  259. package/src/self-dev/orchestrate.ts +6 -2
  260. package/src/self-dev/run-supervisor.ts +35 -1
  261. package/src/self-implement/gate-cli.ts +6 -2
  262. package/src/self-implement/goal-author.ts +5 -28
  263. package/src/self-implement/goal-command-execution.ts +26 -0
  264. package/src/self-implement/goal-new-paths.ts +4 -5
  265. package/src/self-implement/graph-authority.ts +17 -3
  266. package/src/self-implement/graph-yaml.ts +15 -7
  267. package/src/self-implement/headless-elanous-driver.ts +16 -1
  268. package/src/self-implement/host-regate.ts +44 -9
  269. package/src/self-implement/orchestrator.ts +5 -11
  270. package/src/self-implement/pwa-import-graph.ts +194 -7
  271. package/src/self-implement/rework-policy.ts +80 -2
  272. package/src/self-implement/self-implement-runtime.ts +126 -21
  273. package/src/self-implement/speed-claims.ts +145 -6
  274. package/src/session/index.ts +14 -4
  275. package/src/session-runtime/index.ts +55 -11
  276. package/src/skills/tools/persistent-grounding.ts +59 -4
  277. package/src/skills/tools/shell.ts +79 -1
  278. package/src/steward/rejudge-tick.ts +12 -1
  279. package/src/steward/release-day.ts +158 -0
  280. package/src/steward/resync-tick.ts +123 -0
  281. package/src/task-agent/actions.ts +1 -0
  282. package/src/task-agent/adopt-pr.ts +140 -0
  283. package/src/task-agent/board.ts +11 -1
  284. package/src/task-agent/card-evidence.ts +37 -5
  285. package/src/task-agent/cover.ts +105 -19
  286. package/src/task-agent/failure-notify.ts +61 -0
  287. package/src/task-agent/green-sweep.ts +170 -0
  288. package/src/task-agent/land-worktree.ts +52 -5
  289. package/src/task-agent/live-move-history.ts +1 -0
  290. package/src/task-agent/live-moves.ts +56 -17
  291. package/src/task-agent/parent-host.ts +1 -1
  292. package/src/task-agent/postland-card.ts +139 -0
  293. package/src/task-agent/rejudge-on-head.ts +321 -25
  294. package/src/task-agent/remote-rejudge-bind.ts +62 -0
  295. package/src/task-agent/repair-branch-cas.ts +182 -0
  296. package/src/task-agent/repair-cycle.ts +275 -0
  297. package/src/task-agent/repair-next-move.ts +31 -0
  298. package/src/task-agent/repair-review.ts +24 -0
  299. package/src/task-agent/repair-worker.ts +87 -0
  300. package/src/task-agent/review-result-capture.ts +66 -2
  301. package/src/task-agent/shadow.ts +150 -29
  302. package/src/task-agent/task-hand.ts +133 -8
  303. package/src/task-orchestrator/surfaces/pod-repair-commit.ts +96 -0
  304. package/src/task-orchestrator/surfaces/self-implement-pod.ts +71 -12
  305. package/src/telegram.ts +30 -1
  306. package/src/tool-runtime/tool-search-runtime.ts +25 -0
  307. package/src/tui.ts +20 -1
  308. package/src/user-config.ts +11 -60
  309. package/src/ux-sim/fault-sqlite-busy.ts +50 -0
  310. package/src/version/packed-revision.json +1 -1
  311. package/apps/pwa/out/_next/static/chunks/app/approvals/page-9360b1285b2a346e.js +0 -1
  312. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f08f6529d09ffae1.js +0 -1
  313. package/apps/pwa/out/_next/static/chunks/app/board/page-f000d54d258f9ef6.js +0 -1
  314. package/apps/pwa/out/_next/static/chunks/app/bots/page-22f674e7e4c2c9d5.js +0 -1
  315. package/apps/pwa/out/_next/static/chunks/app/ceo/page-8888733e40cea6d1.js +0 -1
  316. package/apps/pwa/out/_next/static/chunks/app/chat/page-9717ec6964b92276.js +0 -1
  317. package/apps/pwa/out/_next/static/chunks/app/consult/page-44eaad486bc98a45.js +0 -1
  318. package/apps/pwa/out/_next/static/chunks/app/control/page-97fa6cf3d34c7eb9.js +0 -1
  319. package/apps/pwa/out/_next/static/chunks/app/editor/page-35e05d7450461a67.js +0 -1
  320. package/apps/pwa/out/_next/static/chunks/app/inside/page-b2638013c0bf7dfd.js +0 -1
  321. package/apps/pwa/out/_next/static/chunks/app/layout-c44f93a41167536f.js +0 -1
  322. package/apps/pwa/out/_next/static/chunks/app/loops/page-1b810474507f3702.js +0 -1
  323. package/apps/pwa/out/_next/static/chunks/app/market/page-74366ae2693a7d95.js +0 -1
  324. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4e634c5b98fd4e93.js +0 -1
  325. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ed3070d637850548.js +0 -1
  326. package/apps/pwa/out/_next/static/chunks/app/outputs/page-b9cc8a10bc6078b4.js +0 -1
  327. package/apps/pwa/out/_next/static/chunks/app/reflection/page-128458c8d86fbb5f.js +0 -1
  328. package/apps/pwa/out/_next/static/chunks/app/sessions/page-cf0c2b0183991d09.js +0 -1
  329. package/apps/pwa/out/_next/static/chunks/app/showroom/page-b008aff96be56654.js +0 -1
  330. package/apps/pwa/out/_next/static/chunks/app/tasks/page-814394dcbae789b1.js +0 -1
  331. package/apps/pwa/out/_next/static/chunks/app/term/page-6a87325c87f09dd2.js +0 -1
  332. package/apps/pwa/out/_next/static/chunks/app/trace/page-852170303c6cd755.js +0 -1
  333. package/apps/pwa/out/_next/static/chunks/app/vault/page-087ceeb975ef272b.js +0 -1
  334. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-97c7361cc2a19319.js +0 -1
  335. package/apps/pwa/out/_next/static/chunks/app/workspace/page-8c6901833f897496.js +0 -1
  336. /package/apps/pwa/out/_next/static/{dRS6lEhSRRBr1abbzp7fJ → y1ltt7oCZxVpC6zVPhZ2O}/_buildManifest.js +0 -0
  337. /package/apps/pwa/out/_next/static/{dRS6lEhSRRBr1abbzp7fJ → y1ltt7oCZxVpC6zVPhZ2O}/_ssgManifest.js +0 -0
@@ -0,0 +1,87 @@
1
+ /** Detached HQ parent for one same-PR Pod repair. It never pushes or opens a PR. */
2
+ import { spawn } from 'node:child_process';
3
+ import { mkdirSync, renameSync, writeFileSync } from 'node:fs';
4
+ import { dirname } from 'node:path';
5
+ import type { RepairCommitResult } from './repair-branch-cas.js';
6
+ import type { TaskChildLlm } from './task-hand.js';
7
+
8
+ const SHA40 = /^[0-9a-f]{40}$/i;
9
+ const SHA256 = /^[0-9a-f]{64}$/i;
10
+ const MARKER = 'ELANOUS_POD_REPAIR_RESULT ';
11
+
12
+ export interface RepairWorkerRequest {
13
+ card: string; pr: number; head: string; repo: string; resultPath: string;
14
+ entry: string; configDir: string; runId: string; seat?: string;
15
+ model: TaskChildLlm; goal: string;
16
+ }
17
+
18
+ export interface RepairWorkerResult {
19
+ pr: number; head: string; runId: string; ok: boolean; reason?: string;
20
+ job?: string; artifactDir?: string; result?: RepairCommitResult;
21
+ }
22
+
23
+ export function repairHarnessArgs(request: RepairWorkerRequest): string[] {
24
+ if (!Number.isSafeInteger(request.pr) || request.pr < 1 || !SHA40.test(request.head)
25
+ || !request.goal.trim() || !request.runId) throw new Error('repair worker request invalid');
26
+ return [request.entry, '--config-dir', request.configDir, 'harness', 'say', '--substrate', 'pod',
27
+ '--source', `pr:${request.pr}`, '--repair-commit-only', request.head,
28
+ ...(request.seat ? ['--seat', request.seat] : []), '--no-queue', '--no-auto-merge',
29
+ '--child-llm-provider', request.model.provider, '--child-llm-model', request.model.model,
30
+ ...(request.model.effort ? ['--child-llm-effort', request.model.effort] : []), '--', request.goal];
31
+ }
32
+
33
+ export function parseRepairMarker(output: string, pr: number, head: string, runId: string): Pick<RepairWorkerResult, 'job' | 'artifactDir' | 'result'> | null {
34
+ const line = output.split(/\r?\n/).reverse().find((entry: string) => entry.startsWith(MARKER));
35
+ if (!line) return null;
36
+ let value: unknown;
37
+ try { value = JSON.parse(line.slice(MARKER.length)); } catch { return null; }
38
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
39
+ const v = value as Record<string, unknown>;
40
+ if (v.pr !== pr || v.runId !== runId || typeof v.baseHead !== 'string' || v.baseHead.toLowerCase() !== head.toLowerCase()
41
+ || typeof v.commit !== 'string' || !SHA40.test(v.commit) || typeof v.bundleSha256 !== 'string' || !SHA256.test(v.bundleSha256)
42
+ || typeof v.job !== 'string' || !v.job || typeof v.artifactDir !== 'string' || !v.artifactDir.startsWith('/')) return null;
43
+ return { job: v.job, artifactDir: v.artifactDir,
44
+ result: { pr, baseHead: v.baseHead, runId, commit: v.commit, bundleSha256: v.bundleSha256 } };
45
+ }
46
+
47
+ export async function runRepairWorker(request: RepairWorkerRequest): Promise<RepairWorkerResult> {
48
+ const result: RepairWorkerResult = { pr: request.pr, head: request.head, runId: request.runId, ok: false };
49
+ try {
50
+ const args = repairHarnessArgs(request);
51
+ const child = spawn(process.execPath, args, {
52
+ cwd: request.repo, env: { ...process.env, ELANOUS_RUN_ID: request.runId }, stdio: ['ignore', 'pipe', 'pipe'],
53
+ });
54
+ let output = '';
55
+ const append = (data: Buffer) => { output = (output + data.toString('utf8')).slice(-512_000); };
56
+ child.stdout?.on('data', append);
57
+ child.stderr?.on('data', append);
58
+ const code = await new Promise<number | null>((resolve, reject) => {
59
+ child.once('error', reject);
60
+ child.once('close', resolve);
61
+ });
62
+ const marker = parseRepairMarker(output, request.pr, request.head, request.runId);
63
+ if (code !== 0) result.reason = `harness exited ${code}: ${output.slice(-600)}`;
64
+ else if (!marker) result.reason = `repair artifact marker missing or invalid: ${output.slice(-600)}`;
65
+ else Object.assign(result, { ok: true, ...marker });
66
+ } catch (error) {
67
+ result.reason = error instanceof Error ? error.message : String(error);
68
+ }
69
+ mkdirSync(dirname(request.resultPath), { recursive: true });
70
+ const tmp = `${request.resultPath}.${process.pid}.tmp`;
71
+ writeFileSync(tmp, JSON.stringify(result));
72
+ renameSync(tmp, request.resultPath);
73
+ return result;
74
+ }
75
+
76
+ export function parseRepairWorkerResult(text: string, pr: number, head: string, runId: string): RepairWorkerResult | null {
77
+ let value: unknown;
78
+ try { value = JSON.parse(text); } catch { return null; }
79
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
80
+ const v = value as RepairWorkerResult;
81
+ if (v.pr !== pr || typeof v.head !== 'string' || v.head.toLowerCase() !== head.toLowerCase() || v.runId !== runId || typeof v.ok !== 'boolean') return null;
82
+ if (v.ok && (typeof v.artifactDir !== 'string' || !v.artifactDir.startsWith('/') || typeof v.job !== 'string' || !v.job
83
+ || !v.result || v.result.pr !== pr || typeof v.result.baseHead !== 'string'
84
+ || v.result.baseHead.toLowerCase() !== head.toLowerCase() || v.result.runId !== runId || !SHA40.test(v.result.commit)
85
+ || !SHA256.test(v.result.bundleSha256))) return null;
86
+ return v;
87
+ }
@@ -13,15 +13,23 @@
13
13
  import { readFileSync } from 'node:fs';
14
14
  import { dirname, join } from 'node:path';
15
15
  import { isNoneSentinelFinding } from '../agent-substrate/pr-reviewer.js';
16
+ import { repairReviewDigest } from './repair-review.js';
16
17
  import { debug } from '../debug/log.js';
17
- import { LIVE_MOVE_RESULT_EVENT, readTaskAgentState, updateTaskCard, type TaskCard, type TaskCardEvent } from './task-hand.js';
18
+ import { LIVE_MOVE_RESULT_EVENT, cardPrNumber, readTaskAgentState, readTaskCard, updateTaskCard, type TaskCard, type TaskCardEvent } from './task-hand.js';
19
+ import type { RejudgeDeps } from './rejudge-on-head.js';
18
20
 
19
21
  export const REVIEW_RESULT_MISSING_EFFECT = 'review process produced no result';
20
22
  /** 결과 파일은 있는데 읽지 못했다(EACCES 등) — «결과 없음»과 가른다(결과가 있었을 수 있다). */
21
23
  export const REVIEW_RESULT_UNREADABLE_EFFECT = 'review result file unreadable';
24
+ /** Detached self review child: only this card and exact result file may trigger a completion tick. */
25
+ export const TA_REVIEW_COMPLETION_CARD_ENV = 'ELANOUS_TA_REVIEW_COMPLETION_CARD';
26
+ export const TA_REVIEW_COMPLETION_STATE_ENV = 'ELANOUS_TA_REVIEW_COMPLETION_STATE';
27
+ export const TA_REVIEW_COMPLETION_RESULT_ENV = 'ELANOUS_TA_REVIEW_COMPLETION_RESULT';
22
28
  /** 리뷰 한 판(1차 ⊕ 폴백 · ACP 300s 상한 여러 번)을 넉넉히 덮는 대기 상한 — 이 안에는 «아직»으로 본다. */
23
29
  export const REVIEW_RESULT_WAIT_MS = 90 * 60_000;
24
30
 
31
+ const reviewRejudgeInFlight = new Set<string>();
32
+
25
33
  const SHA40 = /^[0-9a-f]{40}$/i;
26
34
 
27
35
  /** 결과 파일 경로 — 카드 상태 파일 옆 `review-results/` · 카드 id 는 파일 이름에 안전한 글자만. */
@@ -88,6 +96,8 @@ export function pendingReviewResults(card: TaskCard): ReviewRequest[] {
88
96
  export interface CaptureReviewResultsOptions {
89
97
  /** 이 카드들만 본다 — 없으면 상태 파일의 모든 카드. */
90
98
  cardIds?: readonly string[];
99
+ /** A detached completion callback may only harvest its own result file. */
100
+ resultPaths?: readonly string[];
91
101
  now?: () => Date;
92
102
  /** 시험 seam — 결과 파일 읽기(없으면 ENOENT 를 던진다). */
93
103
  readFile?: (path: string) => string;
@@ -123,6 +133,7 @@ export function captureReviewResults(statePath: string, opts: CaptureReviewResul
123
133
  if (!card) continue;
124
134
  for (const request of pendingReviewResults(card)) {
125
135
  const resultPath = request.resultPath!;
136
+ if (opts.resultPaths && !opts.resultPaths.includes(resultPath)) continue;
126
137
  let text: string | null = null;
127
138
  let readError: string | null = null;
128
139
  let unreadable = false;
@@ -145,7 +156,7 @@ export function captureReviewResults(statePath: string, opts: CaptureReviewResul
145
156
  at: now.toISOString(), event: LIVE_MOVE_RESULT_EVENT, kind: 'review', pr: request.pr,
146
157
  ...(parsed.head ? { head: parsed.head } : {}),
147
158
  verdict: parsed.verdict, reviewed: parsed.reviewed, mustFix: parsed.mustFix, reviewRoute: parsed.reviewRoute,
148
- resultPath,
159
+ resultPath, reviewSha256: repairReviewDigest(text!),
149
160
  effect: `review result captured · verdict ${parsed.verdict ?? 'none'} · reviewed ${parsed.reviewed ?? 'unknown'} · must-fix ${parsed.mustFix ?? 'unknown'}`,
150
161
  ...(parsed.error ? { detail: parsed.error.slice(0, 300) } : {}),
151
162
  };
@@ -184,6 +195,59 @@ export function captureReviewResults(statePath: string, opts: CaptureReviewResul
184
195
  return out;
185
196
  }
186
197
 
198
+ /** Run after the detached review child has fully written its JSON stdout. Never infer a card from PR alone. */
199
+ export async function rejudgeCompletedTaskReview(
200
+ env: Record<string, string | undefined> = process.env,
201
+ rejudge?: (cardId: string, statePath: string) => Promise<unknown>,
202
+ rejudgeDeps?: RejudgeDeps,
203
+ ): Promise<boolean> {
204
+ const cardId = env[TA_REVIEW_COMPLETION_CARD_ENV];
205
+ const statePath = env[TA_REVIEW_COMPLETION_STATE_ENV];
206
+ const resultPath = env[TA_REVIEW_COMPLETION_RESULT_ENV];
207
+ if (!cardId || !statePath || !resultPath) return false;
208
+ const before = readTaskCard(cardId, statePath);
209
+ const bound = before?.reviewRequests?.find((item) => item.resultPath === resultPath);
210
+ if (before?.status !== 'launched' || before.taLand !== true || !bound || cardPrNumber(before) !== bound.pr
211
+ || !SHA40.test(bound.head) || (before.history ?? []).some((item) => item.event === 'async-result-rejudge' && item.resultPath === resultPath)) return false;
212
+ captureReviewResults(statePath, { cardIds: [cardId], resultPaths: [resultPath] });
213
+ const key = `${statePath}\0${cardId}\0${resultPath}`;
214
+ if (reviewRejudgeInFlight.has(key)) return false;
215
+ const card = readTaskCard(cardId, statePath);
216
+ const request = card?.reviewRequests?.find((item) => item.resultPath === resultPath);
217
+ if (card?.status !== 'launched' || card.taLand !== true || !request || cardPrNumber(card) !== request.pr
218
+ || !(card.history ?? []).some((item) => item.event === LIVE_MOVE_RESULT_EVENT && item.resultPath === resultPath && item.kind === 'review'
219
+ && item.pr === request.pr && SHA40.test(request.head) && SHA40.test(item.head ?? '')
220
+ && item.effect?.startsWith('review result captured') && item.reviewed === true && item.head?.toLowerCase() === request.head.toLowerCase())
221
+ || (card.history ?? []).some((item) => item.event === 'async-result-rejudge' && item.resultPath === resultPath)) return false;
222
+ reviewRejudgeInFlight.add(key);
223
+ try {
224
+ const run = async (): Promise<unknown> => {
225
+ if (rejudgeDeps) {
226
+ const { rejudgeShadowCard } = await import('./rejudge-on-head.js');
227
+ return rejudgeShadowCard(cardId, statePath, rejudgeDeps, { pr: request.pr, head: request.head, kind: 'review', resultPath });
228
+ } else if (rejudge) return rejudge(cardId, statePath);
229
+ else {
230
+ const { rejudgeAsyncResultCard } = await import('./rejudge-on-head.js');
231
+ return rejudgeAsyncResultCard(cardId, statePath, {}, { pr: request.pr, head: request.head, kind: 'review', resultPath });
232
+ }
233
+ };
234
+ const { requireCompletedHeadRejudge } = await import('./rejudge-on-head.js');
235
+ try { requireCompletedHeadRejudge(await run(), request.pr, request.head); }
236
+ catch { requireCompletedHeadRejudge(await run(), request.pr, request.head); } // Bounded retry; no marker on failed outcomes.
237
+ updateTaskCard(statePath, cardId, (current) => current?.status === 'launched' && current.taLand === true && cardPrNumber(current) === request.pr
238
+ && (current.reviewRequests ?? []).some((item) => item.pr === request.pr && SHA40.test(item.head) && item.head.toLowerCase() === request.head.toLowerCase() && item.resultPath === resultPath)
239
+ && (current.history ?? []).some((item) => item.event === LIVE_MOVE_RESULT_EVENT && item.resultPath === resultPath && item.kind === 'review'
240
+ && item.pr === request.pr && SHA40.test(item.head ?? '') && item.head?.toLowerCase() === request.head.toLowerCase()
241
+ && item.reviewed === true && item.effect?.startsWith('review result captured'))
242
+ && !(current.history ?? []).some((item) => item.event === 'async-result-rejudge' && item.resultPath === resultPath)
243
+ ? { ...current, history: [...(current.history ?? []), { at: new Date().toISOString(), event: 'async-result-rejudge', kind: 'review', pr: request.pr, head: request.head, resultPath, effect: 'completion callback succeeded' }] }
244
+ : undefined);
245
+ } finally {
246
+ reviewRejudgeInFlight.delete(key);
247
+ }
248
+ return true;
249
+ }
250
+
187
251
  /**
188
252
  * 다음 수 판단의 입력 — 카드에 회수된 리뷰 결과 중 «그 머리(`head`)»의 «가장 최근» 결과가 실제로 돈(reviewed) pass 이고
189
253
  * 리뷰한 머리가 40자 sha 일 때만 `selfReview` 모양으로 돌려준다(슈퍼바이저가 나르는 `self-implement-cli` 의 규칙과 같다 ·
@@ -10,17 +10,22 @@
10
10
  * - 관측: `debug.log('task-agent', 'shadow-move', …)` → `elanous logs --category task-agent`.
11
11
  */
12
12
  import { lstatSync, readFileSync, readdirSync, realpathSync } from 'node:fs';
13
+ import { randomUUID } from 'node:crypto';
13
14
  import { extname, join, relative, resolve } from 'node:path';
14
15
  import { debug } from '../debug/log.js';
15
- import { readTaskAgentState, readTaskCard, taskAgentStatePath, type CompletionKind, type TaskCard } from './task-hand.js';
16
+ import { readTaskAgentState, readTaskCard, taskAgentStatePath, TASK_CARD_PREFIX, type CompletionKind, type TaskCard } from './task-hand.js';
16
17
  import type { SupervisorJobResult, SupervisorStopReason } from '../self-dev/run-supervisor.js';
17
- import { executeNextAction, type DeliveryKind, type NextAction } from './actions.js';
18
- import { judgeNextMove, type TaskJudgeInput, type TaskJudgement } from './judge.js';
18
+ import { executeNextAction, type ActionDeps, type DeliveryKind, type NextAction } from './actions.js';
19
+ import { judgeNextMove, LIVE_RETRY_MOVES, type TaskJudgeInput, type TaskJudgement } from './judge.js';
19
20
  import { HARNESS_RUN_ID_ENV } from '../harness/harness-space.js';
20
- import { collectWiringEvidence, type WiringEvidenceDeps } from './card-evidence.js';
21
- import { recordLiveMoveHistory } from './live-move-history.js';
21
+ import { appendRunLedgerEntry, loadRunLedger } from '../self-implement/run-ledger.js';
22
+ import { collectWiringEvidence, readCardRunSnapshot, type WiringEvidenceDeps, type CardRunEvidenceDeps } from './card-evidence.js';
23
+ import { remoteLedgerMirrorDir } from './parent-host.js';
24
+ import { prodInstanceRoot } from '../instance/resolve.js';
25
+ import { liveMoveHistoryEntry, recordLiveMoveHistory } from './live-move-history.js';
26
+ import { updateTaskCard } from './task-hand.js';
22
27
  import { capturedSelfReview, captureReviewResults, hasCapturedPass } from './review-result-capture.js';
23
- import { configuredTaskAgentLiveMoves, defaultPrHead, executeLiveGreenProposal, executeLiveLand, executeLiveReview, type LiveMoveDeps, type LiveMoveResult, type TaskAgentLiveMove } from './live-moves.js';
28
+ import { configuredTaskAgentLiveMoves, defaultPrHead, executeLiveDecisionCard, executeLiveGreenProposal, executeLiveLand, executeLiveReview, type LiveMoveDeps, type LiveMoveResult, type TaskAgentLiveMove } from './live-moves.js';
24
29
 
25
30
  export interface TaskAgentShadowInput {
26
31
  runId: string | null;
@@ -46,12 +51,16 @@ export interface TaskAgentShadowMove {
46
51
  pr: number | null;
47
52
  /** TA-JUDGE-LIVE-SAFE — `taskAgent.liveMoves` 가 이 수를 실제로 했을 때만 실린다(없으면 종전 그대로 그림자). */
48
53
  liveMove?: LiveMoveResult;
54
+ /** 원격 카드 결선이 미결일 때 런 원장 대기열 보존 결과. */
55
+ cardBinding?: 'pending' | 'queue-failed';
49
56
  }
50
57
 
51
58
  export interface TaskAgentShadowDeps {
52
59
  log?: (category: string, event: string, data: Record<string, unknown>) => void;
53
- /** 시험용 — shadow 실행부가 명령을 부르면 여기로 온다(정상이면 0회). */
54
- command?: (args: string[]) => Promise<{ status: number; stdout: string; stderr?: string }>;
60
+ /** 재발사 live 실행부 명령(주입되지 않으면 shadow). */
61
+ command?: ActionDeps['command'];
62
+ /** 재발사 live 부하 관문(주입되지 않으면 shadow). */
63
+ loadGate?: ActionDeps['loadGate'];
55
64
  readCard?: (taskId: string) => TaskCard | undefined;
56
65
  /** Explicit live invocation only; supervisor's default hook always uses shadow. */
57
66
  mode?: 'shadow' | 'live';
@@ -65,8 +74,12 @@ export interface TaskAgentShadowDeps {
65
74
  wiring?: Omit<WiringEvidenceDeps, 'card'>;
66
75
  /** Process env for the launched-run id (`ELANOUS_RUN_ID`) — test seam; default process.env. */
67
76
  env?: NodeJS.ProcessEnv;
77
+ /** Remote pending-move ledger seam; defaults to the run ledger. */
78
+ queueMove?: typeof appendRunLedgerEntry;
68
79
  }
69
80
 
81
+ export const REMOTE_CARD_MOVE_PENDING = 'task-agent-card-move-pending';
82
+
70
83
  /** 카드 조회 상태 파일 — 조회 경로 전부가 같은 파일을 본다. */
71
84
  function cardStatePath(deps: TaskAgentShadowDeps): string {
72
85
  return deps.statePath ?? deps.live?.statePath ?? taskAgentStatePath();
@@ -175,6 +188,13 @@ function shadowCompletion(card: TaskCard | undefined, lookupFailed: boolean): Co
175
188
  }
176
189
 
177
190
  export async function recordTaskAgentShadowMove(input: TaskAgentShadowInput, deps: TaskAgentShadowDeps = {}): Promise<TaskAgentShadowMove> {
191
+ const env = deps.env ?? process.env;
192
+ const remoteChild = !!env.ELANOUS_POD_NAME;
193
+ const hintedCard = env.ELANOUS_TASK_CARD_ID?.trim();
194
+ const pendingCardId = hintedCard?.startsWith(TASK_CARD_PREFIX) && hintedCard.length <= 63 && /^[A-Za-z0-9._-]+$/.test(hintedCard) ? hintedCard : null;
195
+ const parentRunId = env.ELANOUS_PARENT_RUN_ID?.trim() || null;
196
+ const matchesLaunchCard = (candidate: TaskCard | undefined): boolean => !!candidate && !!parentRunId
197
+ && candidate.id === pendingCardId && candidate.runId === parentRunId;
178
198
  // TA-REVIEW-RESULT-CAPTURE — 판단 «전»에 지난 live review 의 결과 파일을 회수한다(카드 원장을 아는 때만 · 던지지 않는다).
179
199
  // 대기 중인 요청이 없으면 아무것도 쓰지 않는다(결과 파일 없는 카드의 동작은 종전과 같다).
180
200
  const reviewLedger = cardLedgerPath(deps);
@@ -188,14 +208,22 @@ export async function recordTaskAgentShadowMove(input: TaskAgentShadowInput, dep
188
208
  let card: TaskCard | undefined;
189
209
  let cardLookupFailed = false;
190
210
  try {
191
- card = successful ? (deps.readCard ? deps.readCard(successful.taskId) : readTaskCard(successful.taskId, cardStatePath(deps))) : undefined;
192
- if (!card && successful && !deps.readCard) {
211
+ // Pod 에 발사 카드 id 가 있으면 그 id 만 믿는다. 같은 문장을 가진 다른 카드가 있어도 묶지 않는다.
212
+ if (remoteChild) {
213
+ if (pendingCardId) {
214
+ const found = deps.readCard ? deps.readCard(pendingCardId) : readTaskCard(pendingCardId, cardStatePath(deps));
215
+ if (matchesLaunchCard(found)) card = found;
216
+ }
217
+ } else if (successful) {
218
+ card = deps.readCard ? deps.readCard(successful.taskId) : readTaskCard(successful.taskId, cardStatePath(deps));
219
+ }
220
+ if (!card && successful && !deps.readCard && !remoteChild) {
193
221
  const tasks = readTaskAgentState<{ tasks?: Record<string, TaskCard> }>(cardStatePath(deps)).tasks ?? {};
194
222
  const matches = Object.values(tasks).filter(candidate => candidate.status === 'launched' && candidate.text === successful.feature);
195
223
  if (matches.length === 1) card = matches[0];
196
224
  }
197
225
  // TA-OWN-ALL-RUNS — 런 소유 카드: 입력 runId ⊕ 상속 ELANOUS_RUN_ID(Pod 자식의 부모 런) 중 카드 runId 와 «정확히» 같은 것만.
198
- if (!card && !deps.readCard) card = runCardFor(input.runId, deps);
226
+ if (!card && !deps.readCard && !remoteChild) card = runCardFor(input.runId, deps);
199
227
  } catch {
200
228
  // Card lookup is optional; preserve the original PR judgement when unavailable — but mark the completion kind as unmeasured.
201
229
  cardLookupFailed = true;
@@ -209,7 +237,7 @@ export async function recordTaskAgentShadowMove(input: TaskAgentShadowInput, dep
209
237
  try {
210
238
  const reviewedPr = judgeInput.pr;
211
239
  const prRun = input.results.find((result) => result.prNumber === reviewedPr);
212
- const prCard = prRun ? (card && successful?.taskId === prRun.taskId ? card : liveCardFor([prRun], deps, input.runId)) : undefined;
240
+ const prCard = prRun ? (remoteChild ? card : card && successful?.taskId === prRun.taskId ? card : liveCardFor([prRun], deps, input.runId)) : undefined;
213
241
  // 회수한 판정은 «리뷰한 머리»의 것이다 — 지금 PR 머리의 결과만 입력으로 쓴다(A 의 pass 로 B 를 land 쪽으로 보내지 않게 ·
214
242
  // 여러 머리 결과가 역순으로 회수돼도 지금 머리 것을 고른다). 지금 머리 = 그 런 작업 트리에서 PR 머리 조회 ·
215
243
  // 작업 트리가 없으면(Pod) 런이 낸 머리. 못 정하면 쓰지 않는다(종전처럼 review 수 → 새 머리면 새 리뷰).
@@ -239,6 +267,9 @@ export async function recordTaskAgentShadowMove(input: TaskAgentShadowInput, dep
239
267
  : { move: 'wait', stage: 'waiting', reason: '전달 후보 없음' }
240
268
  : judgeNextMove(judgeInput);
241
269
  if (cardLookupFailed) judgement.reason = `${judgement.reason} · 종결 종류 미확인(카드 조회 실패)`;
270
+ // 판단마다 해석한다 — 허용되지 않은 수를 판단할 때도 모르는 항목 경고가 남게.
271
+ const liveMoves = deps.liveMoves ?? await configuredTaskAgentLiveMoves();
272
+ const retryLaunches = { count: 0 };
242
273
  let executorResult: string | null = null;
243
274
  let wouldDo: string | null = null;
244
275
  if (judgement.executorKind) {
@@ -253,10 +284,13 @@ export async function recordTaskAgentShadowMove(input: TaskAgentShadowInput, dep
253
284
  checklistId: delivery ? card?.checklistId ?? '' : '',
254
285
  ...(delivery ? { delivery } : {}),
255
286
  };
287
+ const liveRetry = !delivery && deps.command !== undefined && deps.loadGate !== undefined
288
+ && (LIVE_RETRY_MOVES as readonly string[]).includes(judgement.move) && liveMoves.has(judgement.move as TaskAgentLiveMove);
256
289
  executorResult = await executeNextAction(action, {
257
- mode: delivery ? (deps.mode ?? 'shadow') : 'shadow',
290
+ mode: delivery ? (deps.mode ?? 'shadow') : liveRetry ? 'live' : 'shadow',
258
291
  command: deps.command ?? (async () => { throw new Error('task-agent shadow path must not run commands'); }),
259
292
  observe: (_event, data) => { wouldDo = data.reason; },
293
+ ...(liveRetry ? { liveMoves, retryLaunches, loadGate: deps.loadGate } : {}),
260
294
  });
261
295
  }
262
296
  const move: TaskAgentShadowMove = {
@@ -278,20 +312,21 @@ export async function recordTaskAgentShadowMove(input: TaskAgentShadowInput, dep
278
312
  move.reason = wouldDo ?? judgement.reason;
279
313
  move.wouldDo = null;
280
314
  }
281
- const liveKind: TaskAgentLiveMove | null = move.move === 'review' || move.move === 'propose-green' || move.move === 'propose-land' ? move.move : null;
282
- // 판단마다 해석한다 — 허용되지 않은 수를 판단할 때도 모르는 항목 경고가 남게.
283
- const liveMoves = deps.liveMoves ?? await configuredTaskAgentLiveMoves();
284
- const live = liveKind !== null && liveMoves.has(liveKind);
315
+ const liveKind: TaskAgentLiveMove | null = move.move === 'review' || move.move === 'propose-green' || move.move === 'propose-land' || move.move === 'decision-card' ? move.move : null;
285
316
  // 카드 id 는 모든 판단 관측에 싣는다(그림자 일치율을 카드로 잇는다) — 읽기 전용 조회.
286
317
  // live 수는 «근거 결과»에 묶는다 — PR 수는 그 PR 을 낸 결과의 카드·작업 트리, 전달은 전달한 카드(다중 결과에서 남의 카드에 쓰지 않게).
287
318
  const prResult = typeof judgeInput.pr === 'number' ? input.results.find((result) => result.prNumber === judgeInput.pr) : undefined;
288
- const boundCard = delivery
319
+ const candidateBoundCard = delivery
289
320
  ? card
290
321
  : prResult
291
- ? (card && successful?.taskId === prResult.taskId ? card : liveCardFor([prResult], deps, input.runId))
292
- : undefined;
322
+ ? (card && (successful?.taskId === prResult.taskId || (remoteChild && pendingCardId === card.id)) ? card : liveCardFor([prResult], deps, input.runId))
323
+ : liveKind === 'decision-card' ? card ?? liveCardFor(input.results, deps, input.runId) : undefined;
324
+ const boundCard = remoteChild && !matchesLaunchCard(candidateBoundCard) ? undefined : candidateBoundCard;
293
325
  // PR 에 묶인 수는 그 PR 의 카드만 관측에 싣는다(못 찾으면 null — 다른 결과의 카드로 대체하지 않는다).
294
- const liveCard = prResult || delivery ? boundCard : card ?? (cardLookupFailed ? undefined : liveCardFor(input.results, deps, input.runId));
326
+ const candidateLiveCard = prResult || delivery ? boundCard : card ?? (cardLookupFailed ? undefined : liveCardFor(input.results, deps, input.runId));
327
+ const liveCard = remoteChild && !matchesLaunchCard(candidateLiveCard) ? undefined : candidateLiveCard;
328
+ // 원격 Pod 는 HQ 카드 파일을 갖지 않는다. id 를 전달받아도 현지 원장이 없으면 실행 수는 보류한다.
329
+ const live = liveKind !== null && liveMoves.has(liveKind) && (!remoteChild || boundCard !== undefined);
295
330
  let liveMove: LiveMoveResult | undefined;
296
331
  if (live) {
297
332
  if (liveKind === 'review') {
@@ -316,6 +351,17 @@ export async function recordTaskAgentShadowMove(input: TaskAgentShadowInput, dep
316
351
  ...(prResult.prUrl ? { prUrl: prResult.prUrl } : {}),
317
352
  } } : {}),
318
353
  }, liveMoveDeps(deps));
354
+ } else if (liveKind === 'decision-card') {
355
+ // Supervisor results carry failure text and reviewer must-fix items, but no screen tail.
356
+ // A stop reason alone is not evidence: the executor correctly raises no card when both are absent.
357
+ const evidenceResults = prResult ? [prResult] : input.results;
358
+ const signature = evidenceResults.find((result) => result.error?.message?.trim())?.error?.message;
359
+ const mustFix = evidenceResults.flatMap((result) => result.reviewMustFix ?? []);
360
+ liveMove = await executeLiveDecisionCard(boundCard, {
361
+ runId: input.runId, stopReason: input.stopReason, reason: move.reason,
362
+ ...(prResult ? { pr: prResult.prNumber } : {}),
363
+ evidence: { ...(signature ? { signature } : {}), mustFix },
364
+ }, liveMoveDeps(deps));
319
365
  } else {
320
366
  const evidence: Record<string, string> = {};
321
367
  if (boundCard && !delivery && judgeInput.prState === 'MERGED' && prResult) evidence[boundCard.id] = `#${prResult.prNumber}`;
@@ -323,11 +369,6 @@ export async function recordTaskAgentShadowMove(input: TaskAgentShadowInput, dep
323
369
  liveMove = executeLiveGreenProposal(boundCard, evidence, liveMoveDeps(deps));
324
370
  }
325
371
  }
326
- // 판단한 수마다 live/shadow 를 남긴다 — OP 가 «오판 건수»·«그림자 일치율»을 카드·수 종류로 잰다.
327
- (deps.log ?? ((category, event, data) => debug.log(category, event, data)))('task-agent', 'shadow-move', {
328
- ...move, completion: shadowCompletion(card, cardLookupFailed), card: liveCard?.id ?? null, live, liveExecuted: liveMove?.executed ?? false, ...(liveMove ? { liveOk: liveMove.ok } : {}),
329
- ...(reviewFromCapture ? { reviewSource: 'captured-review-result' } : {}),
330
- });
331
372
  // TA-WIRING-CHECK — 병합 PR 은 판단한 «뒤» 배선 근거를 관측만 한다(move 불변 · 실패는 unknown 으로 남기고 던지지 않는다).
332
373
  if (judgeInput.prState === 'MERGED' && typeof judgeInput.pr === 'number' && !delivery && liveCard) {
333
374
  try {
@@ -339,19 +380,51 @@ export async function recordTaskAgentShadowMove(input: TaskAgentShadowInput, dep
339
380
  }
340
381
  // TA-LIVE-MOVE-CARD-HISTORY — live-move 수(review·propose-green·propose-land)는 실행이든 그림자든 그 카드 history 에 한 줄.
341
382
  // 결정·실행이 끝난 «뒤»에 덧붙이기만 한다(반환값·관측 무변경) · 실패는 관측만(live-move 를 막지 않는다).
342
- if (liveKind !== null) recordLiveMoveOnCard({
383
+ if (liveKind !== null && (liveKind !== 'decision-card' || live) && (!remoteChild || liveCard)) recordLiveMoveOnCard({
343
384
  liveKind, live, liveMove, card: live ? boundCard : liveCard, runId: input.runId,
344
385
  pr: prResult?.prNumber ?? (typeof judgeInput.pr === 'number' ? judgeInput.pr : undefined),
345
386
  shadowDetail: move.wouldDo ?? move.reason,
346
387
  }, deps);
347
- return liveMove ? { ...move, liveMove } : move;
388
+ let cardBinding: TaskAgentShadowMove['cardBinding'];
389
+ if (liveKind !== null && !liveCard && remoteChild) {
390
+ const inheritedRunId = parentRunId || env[HARNESS_RUN_ID_ENV]?.trim() || null;
391
+ const reason = !parentRunId ? 'parent run id absent; HQ binding required'
392
+ : pendingCardId ? 'card id carried but ledger unavailable' : 'card id absent; parent run lookup required';
393
+ try {
394
+ if (!input.runId) throw new Error('runId absent');
395
+ (deps.queueMove ?? appendRunLedgerEntry)({
396
+ timestamp: new Date().toISOString(), runId: input.runId, event: REMOTE_CARD_MOVE_PENDING,
397
+ data: { id: randomUUID(), card: pendingCardId, parentRunId: inheritedRunId, kind: liveKind, pr: move.pr,
398
+ detail: move.wouldDo ?? move.reason, reason, executorResult: 'shadow' },
399
+ });
400
+ cardBinding = 'pending';
401
+ } catch (error) {
402
+ cardBinding = 'queue-failed';
403
+ try {
404
+ (deps.log ?? ((c, e, d) => debug.log(c, e, d)))('task-agent', 'card-binding-queue-failed',
405
+ { card: pendingCardId, runId: input.runId, parentRunId: inheritedRunId, kind: liveKind, reason: error instanceof Error ? error.message : String(error) });
406
+ } catch { /* queue failure remains visible in the return value */ }
407
+ }
408
+ if (cardBinding === 'pending') try {
409
+ (deps.log ?? ((c, e, d) => debug.log(c, e, d)))('task-agent', 'card-binding-pending',
410
+ { card: pendingCardId, runId: input.runId, parentRunId: inheritedRunId, kind: liveKind, reason });
411
+ } catch { /* queued evidence remains authoritative */ }
412
+ }
413
+ // 판단 관측은 원격 미결 수를 실제 원장에 기록한 결과까지 확정한 뒤 남긴다.
414
+ (deps.log ?? ((category, event, data) => debug.log(category, event, data)))('task-agent', 'shadow-move', {
415
+ ...move, completion: shadowCompletion(card, cardLookupFailed), card: liveCard?.id ?? pendingCardId,
416
+ live, liveExecuted: liveMove?.executed ?? false, ...(liveMove ? { liveOk: liveMove.ok } : {}),
417
+ ...(cardBinding ? { cardBinding } : {}),
418
+ ...(reviewFromCapture ? { reviewSource: 'captured-review-result' } : {}),
419
+ });
420
+ return { ...move, ...(liveMove ? { liveMove } : {}), ...(cardBinding ? { cardBinding } : {}) };
348
421
  }
349
422
 
350
423
  /**
351
424
  * 런 id 폴백 — 입력 runId(슈퍼바이저 · Pod 면 자식 런) → 상속 `ELANOUS_RUN_ID`(발사기가 넘긴 부모 런) 순으로,
352
425
  * 카드 runId 와 정확히 같고 그 카드가 하나뿐일 때만 쓴다. 못 찾으면 추측하지 않고 이유 한 줄을 남긴다(조용한 미스 0).
353
426
  */
354
- function runCardFor(runId: string | null | undefined, deps: TaskAgentShadowDeps): TaskCard | undefined {
427
+ export function runCardFor(runId: string | null | undefined, deps: TaskAgentShadowDeps): TaskCard | undefined {
355
428
  const own = runId?.trim() || undefined;
356
429
  const inherited = (deps.env ?? process.env)[HARNESS_RUN_ID_ENV]?.trim() || undefined;
357
430
  if (!own && !inherited) return undefined;
@@ -370,6 +443,54 @@ function runCardFor(runId: string | null | undefined, deps: TaskAgentShadowDeps)
370
443
  return undefined;
371
444
  }
372
445
 
446
+ /** HQ `tasks show` 가 원격 원장 거울의 미결 수를 카드에 회수한다. 부모→자식 원장 간선과 카드 id 를 함께 확인한다. */
447
+ export async function replayRemoteCardMoves(cardId: string, statePath: string, evidenceDeps: CardRunEvidenceDeps = {},
448
+ log: (category: string, event: string, data: Record<string, unknown>) => void = (c, e, d) => debug.log(c, e, d)): Promise<number> {
449
+ const card = readTaskCard(cardId, statePath);
450
+ if (!card?.runId || !card.parentHost) return 0;
451
+ // 원격 부모와 동일 runId 의 HQ 국소 원장은 발사 영수증만 있을 수 있다. 이 회수는 원격 거울을 우선한다.
452
+ const loadLedger = evidenceDeps.loadLedger ?? ((runId: string) =>
453
+ loadRunLedger(runId, remoteLedgerMirrorDir(prodInstanceRoot(), card.parentHost!.host)));
454
+ const snapshot = await readCardRunSnapshot(card, { ...evidenceDeps, loadLedger });
455
+ if (snapshot.readErrors?.length) {
456
+ log('task-agent', 'card-binding-replay-failed', { card: cardId, runId: card.runId, reason: snapshot.readErrors.join(' · ') });
457
+ return 0;
458
+ }
459
+ // 카드 id 가 빠진 옛 원격 줄은 부모 runId 의 유일 카드일 때만 역조회한다.
460
+ const matches = Object.values(readTaskAgentState<{ tasks?: Record<string, TaskCard> }>(statePath).tasks ?? {})
461
+ .filter((candidate) => candidate.runId === card.runId);
462
+ let written = 0;
463
+ for (const [childRunId, entries] of Object.entries(snapshot.children)) {
464
+ for (const entry of entries ?? []) {
465
+ if (entry.event !== REMOTE_CARD_MOVE_PENDING) continue;
466
+ const data = entry.data;
467
+ const id = data.id;
468
+ const kind = data.kind;
469
+ const linked = data.card === cardId || (data.card == null && matches.length === 1 && matches[0]?.id === cardId);
470
+ const parentMatches = data.parentRunId == null || data.parentRunId === card.runId;
471
+ if (!linked || !parentMatches || typeof id !== 'string' || !/^[a-f0-9-]{36}$/i.test(id)
472
+ || !['review', 'propose-green', 'propose-land', 'decision-card'].includes(String(kind))) {
473
+ log('task-agent', 'card-binding-replay-skipped', { card: cardId, childRunId, pendingId: typeof id === 'string' ? id : null,
474
+ reason: !linked ? 'card mismatch or ambiguous runId' : !parentMatches ? 'parent run mismatch' : 'invalid pending move' });
475
+ continue;
476
+ }
477
+ const at = entry.timestamp ?? '';
478
+ if (!Number.isFinite(Date.parse(at))) continue;
479
+ const pr = Number.isSafeInteger(data.pr) && (data.pr as number) > 0 ? data.pr as number : undefined;
480
+ updateTaskCard(statePath, cardId, (current) => {
481
+ if (!current || current.runId !== card.runId || current.history.some((item) => item.sourceId === id)) return undefined;
482
+ const item = liveMoveHistoryEntry(current, { at, kind: kind as TaskAgentLiveMove, executorResult: 'shadow',
483
+ shadowDetail: typeof data.detail === 'string' ? data.detail : 'remote card binding pending', runId: childRunId,
484
+ ...(pr ? { pr } : {}) });
485
+ written++;
486
+ return { ...current, history: [...current.history, { ...item, sourceId: id }] };
487
+ });
488
+ }
489
+ }
490
+ if (written) log('task-agent', 'card-binding-replayed', { card: cardId, parentRunId: card.runId, written });
491
+ return written;
492
+ }
493
+
373
494
  /**
374
495
  * 카드 history 에 live-move 한 줄 — 상태 파일은 카드를 읽은 곳과 같아야 한다: 주입한 경로(`live.statePath`) 또는
375
496
  * 기본 리더를 썼을 때의 기본 경로. 주입한 카드 리더(`readCard`)만 있고 경로가 없으면 그 카드의 원장을 모른다 — 적지 않는다.