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
@@ -628,17 +628,74 @@ function sameProviderStep(child: ExplicitChildForEscalation): EscalateTarget | n
628
628
  return null;
629
629
  }
630
630
 
631
+ /** escalateChild 매핑 해석 근원 — `env` 가 `config` 를 이기고, 둘 다 없으면 같은 provider 사다리(`ladder`). */
632
+ export type EscalateTargetSource = 'config' | 'env' | 'ladder';
633
+
634
+ /** `self-dev.rework`/`escalate-target` 관측 한 건. 관측 전용 — 판정·분기는 이 값을 읽지 않는다. */
635
+ export interface EscalateTargetObservation {
636
+ readonly category: 'self-dev.rework';
637
+ readonly event: 'escalate-target';
638
+ readonly data: {
639
+ readonly source: EscalateTargetSource;
640
+ readonly from: string;
641
+ readonly to: string;
642
+ readonly applied: boolean;
643
+ /** `applied=false` 의 이유 — provider 경계 또는 불완전한 모델 ID. */
644
+ readonly reason?: 'cross-provider' | 'invalid-target';
645
+ };
646
+ }
647
+
648
+ /** 명시 자식 승급 해석 옵션. `escalateChild`(`tools.selfImplement.escalateChild` from→to 맵)는 호출부가
649
+ * config 에서 읽어 전달한다 — ⚠️ Pod 안엔 운영 config 가 없다(POD-CONFIG-SNAPSHOT 전) → Pod 자식은
650
+ * `ELANOUS_SELFDEV_ESCALATE_CHILD` env 로만 받는다. 관측 싱크도 호출부가 주입한다(정책은 순수 유지). */
651
+ export interface ExplicitChildEscalateOptions {
652
+ readonly escalateChild?: Readonly<Record<string, string>>;
653
+ readonly observe?: (observation: EscalateTargetObservation) => void;
654
+ }
655
+
656
+ /** 모델 id(`openrouter/z-ai/glm-5.3` 등)의 provider 세그먼트 — 첫 `/` 앞. */
657
+ function escalateModelProvider(model: string): string {
658
+ return (model.split('/')[0] ?? '').trim();
659
+ }
660
+
661
+ /** 명시 매핑은 provider/model, OpenRouter 는 openrouter/vendor/model 형태의 완전한 ID 여야 한다. */
662
+ function validEscalateModelId(model: string): boolean {
663
+ const segments = model.split('/');
664
+ const required = segments[0] === 'openrouter' ? 3 : 2;
665
+ return segments.length === required && segments.every((segment) => segment.length > 0 && !/\s/.test(segment));
666
+ }
667
+
668
+ /** `ELANOUS_SELFDEV_ESCALATE_CHILD=<from>=<to>` 해석 — 첫 `=` 기준. 빈 조각·구문 파손은 매핑 없음. */
669
+ function parseEscalateChildEnv(raw: string | undefined): { from: string; to: string } | undefined {
670
+ const value = raw?.trim();
671
+ if (!value) return undefined;
672
+ const separator = value.indexOf('=');
673
+ if (separator <= 0) return undefined;
674
+ const from = value.slice(0, separator).trim();
675
+ const to = value.slice(separator + 1).trim();
676
+ return from && to ? { from, to } : undefined;
677
+ }
678
+
631
679
  /** 명시 자식의 승급 타깃 — 같은 provider 안에서만(위 규칙). `ELANOUS_SELFDEV_SOL_*` 는 운영자의 명시
632
680
  * 덮어쓰기라 그대로 우선한다. ⛔ `effort` 가 빈 문자열이면 «안 보낸다»는 뜻이다(OpenRouter 는 노력을
633
- * 보내면 덜 생각했다 — 2026-09-23 실측). */
681
+ * 보내면 덜 생각했다 — 2026-09-23 실측).
682
+ * escalateChild 짝지기(env 가 config 를 이긴다)는 사다리(`sameProviderStep`)보다 «먼저» —
683
+ * 같은 provider 타깃이면 그 타깃으로, provider 가 다르면 무시 ⊕ 관측하고 사다리로 돌아간다.
684
+ * 둘 다 없으면 종전 그대로(바이트 불변). */
634
685
  export function resolveExplicitChildEscalateTarget(
635
686
  tier: EscalateTier,
636
687
  env: Record<string, string | undefined> = process.env,
637
688
  child?: ExplicitChildForEscalation,
689
+ options?: ExplicitChildEscalateOptions,
638
690
  ): EscalateTarget | null {
639
691
  if (tier === 'none') return null;
692
+ const sink = options?.observe;
693
+ const emit = sink
694
+ ? (data: EscalateTargetObservation['data']): void => sink({ category: 'self-dev.rework', event: 'escalate-target', data })
695
+ : undefined;
640
696
  const overrideModel = env.ELANOUS_SELFDEV_SOL_MODEL?.trim();
641
697
  if (overrideModel) {
698
+ if (child) emit?.({ source: 'env', from: child.model, to: overrideModel, applied: true });
642
699
  return {
643
700
  model: overrideModel,
644
701
  provider: env.ELANOUS_SELFDEV_SOL_PROVIDER?.trim() || child?.provider || 'openai-codex',
@@ -646,9 +703,30 @@ export function resolveExplicitChildEscalateTarget(
646
703
  };
647
704
  }
648
705
  if (child === undefined) return null;
706
+ const effortOverride = env.ELANOUS_SELFDEV_SOL_EFFORT?.trim();
707
+ // env → config 순서로 이 자식 모델에 대한 매핑을 모은다(없는 층은 건너뛴다).
708
+ const pairings: Array<{ source: 'env' | 'config'; to: string }> = [];
709
+ const envPairing = parseEscalateChildEnv(env.ELANOUS_SELFDEV_ESCALATE_CHILD);
710
+ if (envPairing && envPairing.from === child.model) pairings.push({ source: 'env', to: envPairing.to });
711
+ const configMap = options?.escalateChild;
712
+ const configTo = configMap && Object.hasOwn(configMap, child.model) ? configMap[child.model]?.trim() : undefined;
713
+ if (configTo) pairings.push({ source: 'config', to: configTo });
714
+ for (const pairing of pairings) {
715
+ if (!validEscalateModelId(pairing.to)) {
716
+ emit?.({ source: pairing.source, from: child.model, to: pairing.to, applied: false, reason: 'invalid-target' });
717
+ continue;
718
+ }
719
+ if (escalateModelProvider(pairing.to) === child.provider) {
720
+ emit?.({ source: pairing.source, from: child.model, to: pairing.to, applied: true });
721
+ return effortOverride
722
+ ? { model: pairing.to, provider: child.provider, effort: effortOverride }
723
+ : { model: pairing.to, provider: child.provider, effort: '' };
724
+ }
725
+ emit?.({ source: pairing.source, from: child.model, to: pairing.to, applied: false, reason: 'cross-provider' });
726
+ }
649
727
  const step = sameProviderStep(child);
650
728
  if (step === null) return null;
651
- const effortOverride = env.ELANOUS_SELFDEV_SOL_EFFORT?.trim();
729
+ emit?.({ source: 'ladder', from: child.model, to: step.model, applied: true });
652
730
  return effortOverride ? { ...step, effort: effortOverride } : step;
653
731
  }
654
732
 
@@ -456,11 +456,36 @@ function parseGoals(req: SelfImplementRuntimeRequest): string[] | undefined {
456
456
  return goals;
457
457
  }
458
458
 
459
- function formatOrchestrationResults(results: readonly SelfDevJobResult[]): string {
459
+ interface SelfImplementGoalResult {
460
+ goal: string;
461
+ status: SelfDevJobResult['status'];
462
+ runId?: string;
463
+ stage?: string;
464
+ reason?: string;
465
+ parentPrNumber?: number;
466
+ }
467
+
468
+ function observedGoalResults(results: readonly SelfDevJobResult[]): SelfImplementGoalResult[] {
469
+ return results.map((result, index) => ({
470
+ goal: result.feature || `골 ${index + 1}`,
471
+ status: result.status,
472
+ ...(result.runId ? { runId: result.runId } : {}),
473
+ ...(result.stage ? { stage: result.stage } : {}),
474
+ ...(result.error?.message || result.stopReason || result.blockReason
475
+ ? { reason: result.error?.message || result.stopReason || (result.blockReason === 'parent-unlanded'
476
+ ? result.parentPrNumber ? `선행 PR #${result.parentPrNumber} 미착지` : '선행 골 미착지'
477
+ : result.blockReason) }
478
+ : {}),
479
+ ...(result.parentPrNumber ? { parentPrNumber: result.parentPrNumber } : {}),
480
+ }));
481
+ }
482
+
483
+ function formatOrchestrationResults(results: readonly SelfDevJobResult[], goals: readonly SelfImplementGoalResult[]): string {
460
484
  const done = results.filter(result => result.status === 'done').length;
461
485
  const merged = results.filter(result => result.merged).length;
462
486
  const failed = results.filter(result => result.status === 'failed').length;
463
- return `[self-implement] parallel ${results.length} goal · done ${done}${merged ? ` (병합 ${merged})` : ''}${failed ? ` · 실패 ${failed}` : ''}`;
487
+ const summary = `[self-implement] parallel ${results.length} goal · done ${done}${merged ? ` (병합 ${merged})` : ''}${failed ? ` · 실패 ${failed}` : ''}`;
488
+ return [summary, ...goals.map(goal => `- ${goal.goal}: ${goal.status} · 런 ${goal.runId ?? '미관측'}${goal.stage ? ` · 단계 ${goal.stage}` : ''}${goal.reason ? ` · 이유 ${goal.reason}` : ''}`)].join('\n');
464
489
  }
465
490
 
466
491
  export function resolveDocumentReferences(paths: readonly string[], repositoryRoot = process.cwd()): DocumentReferenceStatus[] {
@@ -476,7 +501,15 @@ function documentReferenceStatusLines(references: readonly DocumentReferenceStat
476
501
 
477
502
  function formatResult(feature: string, r: SelfImplementResult, references: readonly DocumentReferenceStatus[] = []): string {
478
503
  const head = `[self-implement] ${r.stage}`;
479
- const lines: string[] = [head, ...documentReferenceStatusLines(references)];
504
+ const next = r.runId ? `eln self run ${r.runId}` : r.ok ? 'eln self running-runs' : '실패 이유를 확인한 뒤 다시 요청';
505
+ const lines: string[] = [
506
+ r.ok
507
+ ? r.runId
508
+ ? `하니스가 맡았습니다 · 런 ${r.runId} · 노드 ${r.node || '미관측'} · 다음 조회: ${next} · 이 턴에서 파일을 직접 편집하지 마세요`
509
+ : `하니스 응답을 받았지만 런 식별자는 아직 미관측입니다 · 진행 중 런 조회: ${next} · 일치하는 런이 없으면 요청을 다시 보내세요 · 이 턴에서 파일을 직접 편집하지 마세요`
510
+ : `하니스가 발사되지 않았거나 실패했습니다: ${r.detail ?? r.stage} · 다음: ${next} · 이 턴에서 파일을 직접 편집하지 마세요`,
511
+ head, ...documentReferenceStatusLines(references),
512
+ ];
480
513
  if (r.branch) lines.push(`branch: ${r.branch}`);
481
514
  if (r.worktreePath) lines.push(`worktree: ${r.worktreePath}`);
482
515
  if (r.stage === 'pr-opened' && r.prUrl) {
@@ -494,6 +527,12 @@ function formatResult(feature: string, r: SelfImplementResult, references: reado
494
527
  export interface SelfImplementRunResult {
495
528
  output: string;
496
529
  ok: boolean;
530
+ kind: 'launched' | 'observed-only' | 'failed' | 'pending';
531
+ next?: string;
532
+ reason?: string;
533
+ missing?: string[];
534
+ /** 병렬 골마다 실제로 관측된 종료 상태·식별자·이유. */
535
+ goalResults?: SelfImplementGoalResult[];
497
536
  // ⛔ 관측 전용 반환에는 **단계가 없다** — 아무 노드도 돌지 않았다. 없는 것을 있는 값으로 적으면
498
537
  // «안 돌았다» 와 «어느 단계에서 끝났다» 가 같은 값이 된다(부재와 미지를 같은 값으로 적지 않는다).
499
538
  stage?: SelfImplementResult['stage'];
@@ -506,8 +545,10 @@ export interface SelfImplementRunResult {
506
545
  observed?: true;
507
546
  /** 명시적 Fabric 분해의 상태·기존 3결말·미산출 사유. */
508
547
  fabricDecomposition?: FabricDecompositionOutcome;
509
- /** 즉시 반환 모드에서 할당한 런 식별자. 블로킹 기본 경로에는 없어 기존 반환 모양을 보존한다. */
548
+ /** 실제 런 결과에서 확인한 식별자. 선할당된 비동기 ID는 반환하지 않는다. */
510
549
  runId?: string;
550
+ /** 비동기 요청 조회용 상관 ID. 발사된 런의 증거가 아니다. */
551
+ requestId?: string;
511
552
  /** 호출이 블로킹인지 비블로킹인지. 기본 경로에는 없어 기존 반환 모양을 보존한다. */
512
553
  invocationMode?: 'blocking' | 'non-blocking';
513
554
  }
@@ -516,6 +557,7 @@ export const selfImplementRuntime: ToolRuntime<SelfImplementRuntimeRequest, Self
516
557
  id: 'self_implement',
517
558
  spec: buildSelfImplementSpec(),
518
559
  async run(req, ctx: ToolRuntimeContext): Promise<SelfImplementRunResult> {
560
+ try {
519
561
  const hasFeature = req.feature !== undefined;
520
562
  const hasGoals = req.goals !== undefined;
521
563
  if (hasFeature === hasGoals) {
@@ -604,7 +646,7 @@ export const selfImplementRuntime: ToolRuntime<SelfImplementRuntimeRequest, Self
604
646
  if (observeOnly.enabled) {
605
647
  const requestSummary = feature ?? goals!.join(' · ');
606
648
  debug.log('self-implement', 'runtime.observed', { surface: ctx.surface, ...(feature ? { feature: feature.slice(0, 120) } : { goals: goals!.length }), observeOnly: observeOnly.enabled, observeOnlySource: observeOnly.source });
607
- return { output: `SelfImplement 관측 전용 측정 모드 — 호출이 접수되어 기록되었습니다. 이 세션에서는 구현을 진행하지 않습니다. 같은 요청을 이 턴에서 직접 구현하지 말고 응답을 마무리하세요: ${requestSummary.slice(0, 120)}${documentReferenceStatusLines(documentReferences).length ? `\n${documentReferenceStatusLines(documentReferences).join('\n')}` : ''}`, ok: true, observed: true };
649
+ return { output: `관측 전용이라 런을 발사하지 않았습니다 · 코드도 안 바꿨습니다 · 이 결과를 구현 실패로 읽고 직접 Write/Edit 하지 마세요. 실제 발사를 원하면 관측 전용 모드를 끈 뒤 다시 요청하세요: ${requestSummary.slice(0, 120)}${documentReferenceStatusLines(documentReferences).length ? `\n${documentReferenceStatusLines(documentReferences).join('\n')}` : ''}`, ok: true, kind: 'observed-only', observed: true, missing: ['runId', 'node'], next: '관측 전용 모드를 끈 뒤 다시 요청' };
608
650
  }
609
651
 
610
652
  const allocatedRunId = nonBlocking ? mintRunId() : undefined;
@@ -637,9 +679,14 @@ export const selfImplementRuntime: ToolRuntime<SelfImplementRuntimeRequest, Self
637
679
  fabricDecomposition = fabricDecompositionOutcome(fabricResult);
638
680
  observeFabricDecomposition(fabricDecomposition, ctx.surface, fabricResult.status === 'decomposed' ? fabricResult.goals.length : undefined);
639
681
  if (fabricResult.status !== 'decomposed') {
682
+ const observedOnly = fabricDecomposition.outcome !== 'llm-failed';
640
683
  return {
641
- output: formatFabricDecompositionOutcome(fabricDecomposition),
642
- ok: fabricDecomposition.outcome !== 'llm-failed',
684
+ output: `${formatFabricDecompositionOutcome(fabricDecomposition)}${observedOnly ? '\n관측 전용이라 런을 발사하지 않았습니다 · 코드도 안 바꿨습니다 · 이 결과를 구현 실패로 읽고 직접 Write/Edit 하지 마세요.' : '\n이 턴에서 파일을 직접 편집하지 마세요.'}`,
685
+ ok: observedOnly,
686
+ kind: observedOnly ? 'observed-only' : 'failed',
687
+ next: observedOnly ? '분해 결과를 확인하고 구현 골을 다시 요청' : '분해 실패 이유를 확인한 뒤 다시 요청',
688
+ missing: ['runId', 'node'],
689
+ ...(observedOnly ? { observed: true as const } : { reason: formatFabricDecompositionOutcome(fabricDecomposition) }),
643
690
  fabricDecomposition,
644
691
  };
645
692
  }
@@ -674,10 +721,40 @@ export const selfImplementRuntime: ToolRuntime<SelfImplementRuntimeRequest, Self
674
721
  ...(forwardedSupervise === undefined ? {} : { supervise: forwardedSupervise }),
675
722
  ...(deliverable === undefined ? {} : { deliverable }),
676
723
  });
677
- const failed = results.filter(result => result.status === 'failed').length;
724
+ const goalResults = observedGoalResults(results);
725
+ const failedGoals = goalResults.filter(result => result.status === 'failed' || result.status === 'cancelled');
726
+ const blockedGoals = goalResults.filter(result => result.status === 'blocked');
727
+ const allRunsIdentified = goalResults.length > 0 && goalResults.every(result => result.runId);
728
+ const kind = goalResults.length === 0 || failedGoals.length > 0 || blockedGoals.length > 0 ? 'failed' : allRunsIdentified ? 'launched' : 'pending';
729
+ const parentBlockedPrNumbers = [...new Set(results.flatMap(result => result.status === 'blocked' && result.blockReason === 'parent-unlanded' && result.parentPrNumber ? [result.parentPrNumber] : []))];
730
+ const parentBlockedWithoutPr = results.some(result => result.status === 'blocked' && result.blockReason === 'parent-unlanded' && !result.parentPrNumber && !result.runId);
731
+ const otherFailureWithoutId = results.some(result => !result.runId && ['failed', 'cancelled', 'blocked'].includes(result.status) && result.blockReason !== 'parent-unlanded');
732
+ const runQueries = [...new Set(goalResults.flatMap(result => result.runId ? [`eln self run ${result.runId}`] : []))];
733
+ const availableQueries = [
734
+ ...parentBlockedPrNumbers.map(pr => `eln gh pr view ${pr}`),
735
+ ...runQueries,
736
+ ];
737
+ const pendingWithoutId = goalResults.some(result => !result.runId && !['failed', 'cancelled', 'blocked'].includes(result.status));
738
+ const next = [
739
+ ...availableQueries,
740
+ ...(pendingWithoutId ? ['eln self running-runs', '일치하는 런이 없으면 ID 미확정 골을 다시 요청'] : []),
741
+ ...(parentBlockedPrNumbers.length ? ['선행 PR 착지 뒤 차단된 골을 재실행하세요'] : []),
742
+ ...(parentBlockedWithoutPr ? ['선행 골 상태를 확인한 뒤 해당 골을 재실행하세요'] : []),
743
+ ...(otherFailureWithoutId ? ['실패·차단 이유를 확인한 뒤 해당 골을 다시 요청'] : []),
744
+ ].join('\n') || '골 결과가 0건이라 상태를 확인한 뒤 다시 요청';
745
+ const reason = goalResults.length === 0
746
+ ? '골 결과가 0건이라 상태 미관측'
747
+ : [...failedGoals, ...blockedGoals].map(result => `${result.goal}: ${result.reason ?? '이유 미관측'}`).join('; ');
678
748
  return {
679
- output: formatOrchestrationResults(results),
680
- ok: failed === 0,
749
+ output: formatOrchestrationResults(results, goalResults)
750
+ + (kind === 'failed' ? `\n실패/차단: ${reason} · 다음: ${next}` : `\n다음 조회: ${next}`)
751
+ + '\n이 턴에서 파일을 직접 편집하지 마세요',
752
+ ok: kind !== 'failed',
753
+ kind,
754
+ next,
755
+ missing: [ ...(goalResults.length === 0 || goalResults.some(result => !result.runId) ? ['runId'] : []), 'node' ],
756
+ goalResults,
757
+ ...(kind === 'failed' ? { reason } : {}),
681
758
  ...(fabricDecomposition ? { fabricDecomposition } : {}),
682
759
  };
683
760
  }
@@ -751,12 +828,12 @@ export const selfImplementRuntime: ToolRuntime<SelfImplementRuntimeRequest, Self
751
828
  ...(runId ? { runId } : {}),
752
829
  }, { pipelineDeps });
753
830
  if (!outcome.ok) {
754
- return { output: withGoalAuthorFailure(`[self-implement] failed: ${outcome.message}`), ok: false };
831
+ return { output: withGoalAuthorFailure(`하니스가 발사되지 않았거나 실패했습니다: ${outcome.message} · 다음: 실패 이유를 확인한 뒤 다시 요청하세요 · 이 턴에서 파일을 직접 편집하지 마세요`), ok: false, kind: 'failed', reason: outcome.message, next: '실패 이유를 확인한 뒤 다시 요청', missing: ['runId', 'node'] };
755
832
  }
756
833
  if (outcome.kind !== 'self') {
757
834
  return {
758
- output: withGoalAuthorFailure('[self-implement] observed by central CLI'),
759
- ok: true,
835
+ output: withGoalAuthorFailure('관측 전용이라 런을 발사하지 않았습니다 · 코드도 안 바꿨습니다 · 이 결과를 구현 실패로 읽고 직접 Write/Edit 하지 마세요'),
836
+ ok: true, kind: 'observed-only', observed: true, missing: ['runId', 'node'], next: '관측 전용 모드를 끈 뒤 다시 요청',
760
837
  };
761
838
  }
762
839
 
@@ -764,6 +841,11 @@ export const selfImplementRuntime: ToolRuntime<SelfImplementRuntimeRequest, Self
764
841
  return {
765
842
  output: withGoalAuthorFailure(formatResult(feature, result, documentReferences)),
766
843
  ok: result.ok,
844
+ kind: result.ok ? (result.runId ? 'launched' : 'pending') : 'failed',
845
+ next: result.runId ? `eln self run ${result.runId}` : result.ok ? 'eln self running-runs' : '실패 이유를 확인한 뒤 다시 요청',
846
+ missing: [...(result.runId ? [] : ['runId']), ...(result.node ? [] : ['node'])],
847
+ ...(!result.ok ? { reason: result.detail ?? result.stage } : {}),
848
+ runId: result.runId,
767
849
  stage: result.stage,
768
850
  node: result.node,
769
851
  ...(result.prUrl ? { prUrl: result.prUrl } : {}),
@@ -775,28 +857,51 @@ export const selfImplementRuntime: ToolRuntime<SelfImplementRuntimeRequest, Self
775
857
 
776
858
  if (nonBlocking) {
777
859
  const runId = allocatedRunId ?? mintRunId();
860
+ const requestId = runId;
861
+ const lookup = `eln logs --category self-implement --event runtime.non-blocking-completed,runtime.non-blocking-failed --grep ${requestId} --since 3h --all --include-test`;
778
862
  void drive(runId).then(
779
863
  (result) => {
780
- debug.log('self-implement', 'runtime.non-blocking-completed', {
781
- runId,
864
+ debug.log('self-implement', result.kind === 'failed' ? 'runtime.non-blocking-failed' : 'runtime.non-blocking-completed', {
865
+ requestId,
866
+ kind: result.kind,
867
+ ...(result.runId ? { runId: result.runId } : {}),
868
+ ...(result.reason ? { reason: result.reason } : {}),
869
+ next: result.next ?? '요청 결과를 확인한 뒤 다시 요청',
782
870
  ok: result.ok,
783
871
  ...(result.stage === undefined ? {} : { stage: result.stage }),
784
- });
872
+ }, result.kind === 'failed' ? { level: 'warn' } : undefined);
785
873
  },
786
874
  (error) => {
875
+ const reason = error instanceof Error ? error.message : String(error);
787
876
  debug.log('self-implement', 'runtime.non-blocking-failed', {
788
- runId,
789
- error: error instanceof Error ? error.message : String(error),
877
+ requestId,
878
+ kind: 'failed',
879
+ reason,
880
+ next: '요청과 실패 이유를 확인한 뒤 다시 요청',
790
881
  }, { level: 'warn' });
791
882
  },
792
883
  );
793
884
  return {
794
885
  ok: true,
795
- runId,
886
+ kind: 'pending',
796
887
  invocationMode: 'non-blocking',
797
- output: `[self-implement] non-blocking accepted\nrunId: ${runId}`,
888
+ requestId,
889
+ next: lookup,
890
+ missing: ['runId', 'node'],
891
+ output: `하니스 요청을 접수했습니다 · 런과 노드는 아직 미관측 · 요청 ID ${requestId}(발사 증거 아님) · 다음 조회: ${lookup} · 이 턴에서 파일을 직접 편집하지 마세요\n[self-implement] non-blocking pending`,
892
+ };
893
+ }
894
+ return await drive();
895
+ } catch (error) {
896
+ const reason = error instanceof Error ? error.message : String(error);
897
+ return {
898
+ ok: false,
899
+ kind: 'failed',
900
+ reason,
901
+ next: '요청과 실패 이유를 확인한 뒤 다시 요청',
902
+ missing: ['runId', 'node'],
903
+ output: `SelfImplement 실패: ${reason} · 다음: 요청과 실패 이유를 확인한 뒤 다시 요청 · 이 턴에서 파일을 직접 편집하지 마세요`,
798
904
  };
799
905
  }
800
- return drive();
801
906
  },
802
907
  };
@@ -2,7 +2,7 @@ import { spawnSync } from 'node:child_process';
2
2
  import { readFileSync, readdirSync } from 'node:fs';
3
3
  import { dirname, isAbsolute, join, resolve } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
- import { prodInstanceRoot } from '../instance/resolve.js';
5
+ import { prodInstanceRoot, releaseLedgerRoot } from '../instance/resolve.js';
6
6
  import { seatOfTree } from '../loops/orchestrator/traffic.js';
7
7
  import { getUserConfig, ORCHESTRATOR_DEFAULTS } from '../user-config.js';
8
8
  import { isRunStatus } from './run-status-mapping.js';
@@ -30,6 +30,7 @@ export interface SpeedClaimsMeasurement {
30
30
  rows: SpeedClaimsRow[];
31
31
  note: string;
32
32
  parent: ParentSpeedClaimsMeasurement;
33
+ daily: SpeedClaimsDailyMeasurement;
33
34
  }
34
35
  export interface ParentSpeedClaimsEvent {
35
36
  ts: string;
@@ -58,6 +59,32 @@ export interface ParentSpeedClaimsMeasurement {
58
59
  note: string;
59
60
  }
60
61
 
62
+ export interface SpeedClaimsReleaseEvent {
63
+ ts: string;
64
+ version: string;
65
+ }
66
+
67
+ export interface SpeedClaimsDailyRow {
68
+ date: string;
69
+ landedPrCount: number | null;
70
+ releasedCount: number | null;
71
+ }
72
+
73
+ export interface SpeedClaimsDailySourceStatus {
74
+ status: 'measured' | 'unmeasured';
75
+ undatedCount: number;
76
+ }
77
+
78
+ export interface SpeedClaimsDailyMeasurement {
79
+ scope: 'last-30-days';
80
+ windowStart: string;
81
+ windowEnd: string;
82
+ landedPrs: SpeedClaimsDailySourceStatus;
83
+ releases: SpeedClaimsDailySourceStatus;
84
+ rows: SpeedClaimsDailyRow[];
85
+ note: string;
86
+ }
87
+
61
88
  export interface SpeedClaimsOptions {
62
89
  dir?: string;
63
90
  list?: (path: string) => string[];
@@ -65,6 +92,8 @@ export interface SpeedClaimsOptions {
65
92
  readSeat?: (runId: string, ledgerDirectory: string, entries: readonly RunLedgerEntry[]) => SpeedClaimsSeat;
66
93
  readParentEvents?: () => readonly ParentSpeedClaimsEvent[];
67
94
  readParentQueue?: () => unknown;
95
+ now?: number;
96
+ readReleaseEvents?: () => readonly SpeedClaimsReleaseEvent[];
68
97
  }
69
98
 
70
99
  const NOTE = 'Child-ledger elapsed minutes from the first child event (or checkpoint creation when unavailable) to the recorded child end, including retries and resumes; this is not the parent launch-to-done interval. Only finished runs enter quartiles (linear interpolation at ranks (n-1)*p). In-progress runs (including parked) and missing, unreadable or invalid timestamps are excluded, not assigned zero. Seats come from the same state root checkpoint, launch queue or recorded launch tree; unresolved seats are counted separately. A completed run without a recorded merge is not a merge.';
@@ -192,6 +221,98 @@ function readParentQueueFromState(): unknown {
192
221
  return JSON.parse(readFileSync(join(prodInstanceRoot(), 'harness', 'queue.json'), 'utf8')) as unknown;
193
222
  }
194
223
 
224
+ const DAILY_WINDOW_DAYS = 30;
225
+ const DAY_MS = 86_400_000;
226
+ const DAILY_NOTE = 'Daily rows cover the last 30 UTC calendar days ending on the measurement day. Landed PRs are child run-ledger merged events (merged=true), identified by PR number with the run id as fallback when no number is recorded, de-duplicated across runs, retries and resumes, and attributed to the earliest recorded landing timestamp. Issued releases use the release ledger publishedAt, never a tag target commit date, and are de-duplicated by version. Identities without any valid timestamp are counted as undated, never as a day. A count of zero means the day was queried and had no events; unmeasured means the source could not be queried, and the JSON value is null. Start-to-land and external-change-to-reflection timings stay with the parent and child measurements above.';
227
+ const VERSION_TAG = /^v?\d+(?:\.\d+)+(?:-[\w.]+)?$/;
228
+
229
+ interface DailyObservation {
230
+ key: string;
231
+ ts: number | null;
232
+ }
233
+
234
+ function utcDayOf(ts: number): string {
235
+ return new Date(ts).toISOString().slice(0, 10);
236
+ }
237
+
238
+ /** Landed-PR identity: the recorded PR number, else the run id (one run lands at most one PR). */
239
+ function landedPrIdentity(data: unknown, runId: string): string {
240
+ const number = object(data)?.number;
241
+ if (typeof number === 'number' && Number.isInteger(number) && number > 0) return `pr-${number}`;
242
+ if (typeof number === 'string' && /^\d+$/.test(number.trim()) && Number(number.trim()) > 0) return `pr-${Number(number.trim())}`;
243
+ return runId;
244
+ }
245
+
246
+ /** De-duplicate repeated identities by their earliest valid timestamp; identities without any valid timestamp are reported as undated, never dropped silently. */
247
+ function dailyBuckets(observations: readonly DailyObservation[]): { counts: Map<string, number>; undated: number } {
248
+ const earliest = new Map<string, number>();
249
+ const undated = new Set<string>();
250
+ for (const { key, ts } of observations) {
251
+ if (key.length === 0) continue;
252
+ if (ts === null) { undated.add(key); continue; }
253
+ const known = earliest.get(key);
254
+ if (known === undefined || ts < known) earliest.set(key, ts);
255
+ }
256
+ const counts = new Map<string, number>();
257
+ for (const ts of earliest.values()) {
258
+ const day = utcDayOf(ts);
259
+ counts.set(day, (counts.get(day) ?? 0) + 1);
260
+ }
261
+ let undatedCount = 0;
262
+ for (const key of undated) if (!earliest.has(key)) undatedCount++;
263
+ return { counts, undated: undatedCount };
264
+ }
265
+
266
+ /** Read-only daily census of landed PRs and issued releases over the last 30 UTC days. */
267
+ function measureDailySpeedClaims(
268
+ landed: readonly DailyObservation[],
269
+ landedMeasured: boolean,
270
+ releases: readonly SpeedClaimsReleaseEvent[] | null,
271
+ now: number,
272
+ ): SpeedClaimsDailyMeasurement {
273
+ const windowEnd = utcDayOf(now);
274
+ const startMs = Date.parse(`${windowEnd}T00:00:00.000Z`) - (DAILY_WINDOW_DAYS - 1) * DAY_MS;
275
+ const landedBuckets = dailyBuckets(landed);
276
+ const releaseBuckets = releases === null
277
+ ? null
278
+ : dailyBuckets(releases.map((event) => ({ key: event.version.trim().replace(/^v(?=\d)/, ''), ts: parseTime(event.ts) })));
279
+ const landedComplete = landedMeasured && landedBuckets.undated === 0;
280
+ const releasesComplete = releaseBuckets !== null && releaseBuckets.undated === 0;
281
+ return {
282
+ scope: 'last-30-days', windowStart: utcDayOf(startMs), windowEnd,
283
+ landedPrs: { status: landedComplete ? 'measured' : 'unmeasured', undatedCount: landedBuckets.undated },
284
+ releases: { status: releasesComplete ? 'measured' : 'unmeasured', undatedCount: releaseBuckets?.undated ?? 0 },
285
+ rows: Array.from({ length: DAILY_WINDOW_DAYS }, (_, index) => {
286
+ const date = utcDayOf(startMs + index * DAY_MS);
287
+ return {
288
+ date,
289
+ landedPrCount: landedComplete ? (landedBuckets.counts.get(date) ?? 0) : null,
290
+ releasedCount: releasesComplete ? (releaseBuckets!.counts.get(date) ?? 0) : null,
291
+ };
292
+ }),
293
+ note: DAILY_NOTE,
294
+ };
295
+ }
296
+
297
+ /** Release manifests record the actual publication time; lightweight tags have no creation timestamp. */
298
+ function readReleaseEventsFromLedger(): SpeedClaimsReleaseEvent[] {
299
+ const root = join(releaseLedgerRoot(), 'release');
300
+ const versions = readdirSync(root).filter((version) => VERSION_TAG.test(version));
301
+ const events: SpeedClaimsReleaseEvent[] = [];
302
+ for (const version of versions) {
303
+ // Absence and corruption are both unmeasured; only a readable manifest proves preparation.
304
+ const value: unknown = JSON.parse(readFileSync(join(root, version, 'release.json'), 'utf8')) as unknown;
305
+ const record = object(value);
306
+ if (record?.version !== version) throw new Error(`release manifest version mismatch: ${version}`);
307
+ if (!Object.hasOwn(record, 'publishedAt')) continue; // Prepared, not yet published.
308
+ if (typeof record.publishedAt !== 'string' || parseTime(record.publishedAt) === null) throw new Error(`invalid release publication time: ${version}`);
309
+ events.push({ version, ts: record.publishedAt });
310
+ }
311
+ // A checkout without tags or a release history cannot establish a queried zero.
312
+ if (versions.length === 0) throw new Error('release publication history unavailable');
313
+ return events;
314
+ }
315
+
195
316
  /** Parent launch-to-done census; no child ledger id is used as a join key. */
196
317
  export function measureParentSpeedClaims(
197
318
  events: readonly ParentSpeedClaimsEvent[], queue?: unknown,
@@ -262,7 +383,13 @@ export function measureParentSpeedClaims(
262
383
 
263
384
  /** Read-only elapsed-time census of canonical local run ledgers, including unsuccessful runs. */
264
385
  export function measureSpeedClaims(options: SpeedClaimsOptions = {}): SpeedClaimsMeasurement {
386
+ const now = options.now ?? Date.now();
265
387
  const ledgerDirectory = resolve(options.dir ?? runLedgerDir());
388
+ let releaseEvents: readonly SpeedClaimsReleaseEvent[] | null = null;
389
+ try {
390
+ const events = (options.readReleaseEvents ?? readReleaseEventsFromLedger)();
391
+ releaseEvents = Array.isArray(events) ? events : null;
392
+ } catch { /* An unreadable release source marks releases unmeasured; it does not erase landed-PR observations. */ }
266
393
  let parent: ParentSpeedClaimsMeasurement;
267
394
  try {
268
395
  const events = (options.readParentEvents ?? readParentEventsFromLogs)();
@@ -272,21 +399,27 @@ export function measureSpeedClaims(options: SpeedClaimsOptions = {}): SpeedClaim
272
399
  } catch {
273
400
  parent = { ...measureParentSpeedClaims([]), status: 'cannot-measure' };
274
401
  }
275
- const result: SpeedClaimsMeasurement = {
402
+ const result: Omit<SpeedClaimsMeasurement, 'daily'> = {
276
403
  scope: 'self-implement-run-ledger', ledgerDirectory, status: 'measured',
277
404
  completedCount: 0, inProgressCount: 0, cannotMeasureCount: 0, unknownSeatCount: 0, rows: [], note: NOTE, parent,
278
405
  };
279
406
  let files: string[];
280
407
  try { files = (options.list ?? readdirSync)(ledgerDirectory); }
281
- catch { return { ...result, status: 'cannot-measure' }; }
408
+ catch { return { ...result, status: 'cannot-measure', daily: measureDailySpeedClaims([], false, releaseEvents, now) }; }
282
409
 
410
+ const landed: DailyObservation[] = [];
411
+ let ledgerUnreadable = false;
283
412
  const groups = new Map<string, { seat: SpeedClaimsSeat; outcome: SpeedClaimsOutcome; values: number[] }>();
284
413
  for (const file of files.sort()) {
285
414
  const runId = file.endsWith('.jsonl') ? file.slice(0, -'.jsonl'.length) : '';
286
415
  if (!CANONICAL_RUN_ID.test(runId)) continue;
287
416
  try {
288
417
  const entries = loadRunLedger(runId, ledgerDirectory, options.read);
289
- if (!entries || entries.length === 0) { result.cannotMeasureCount++; continue; }
418
+ if (!entries || entries.length === 0) { ledgerUnreadable = true; result.cannotMeasureCount++; continue; }
419
+ const merges = entries.filter((entry) => entry.event === 'merged' && entry.data.merged === true);
420
+ const numbered = new Set(merges.map((entry) => landedPrIdentity(entry.data, runId)).filter((key) => key !== runId));
421
+ if (numbered.size > 1) ledgerUnreadable = true; // Conflicting PR identities cannot establish a daily count.
422
+ else for (const entry of merges) landed.push({ key: numbered.size === 1 ? [...numbered][0]! : runId, ts: parseTime(entry.timestamp) });
290
423
  const end = endOfRun(entries);
291
424
  const first = time(entries[0]);
292
425
  const createdAt = checkpointFor(runId, ledgerDirectory)?.createdAt;
@@ -306,14 +439,14 @@ export function measureSpeedClaims(options: SpeedClaimsOptions = {}): SpeedClaim
306
439
  group.values.push((endTime! - start) / 60_000);
307
440
  groups.set(key, group);
308
441
  result.completedCount++;
309
- } catch { result.cannotMeasureCount++; }
442
+ } catch { result.cannotMeasureCount++; ledgerUnreadable = true; }
310
443
  }
311
444
  result.rows = [...groups.values()].map(({ seat, outcome, values }) => {
312
445
  values.sort((a, b) => a - b);
313
446
  return { seat, outcome, count: values.length, underOneMinuteCount: values.filter((minutes) => minutes < 1).length, medianMinutes: percentile(values, 0.5), q1Minutes: percentile(values, 0.25), q3Minutes: percentile(values, 0.75) };
314
447
  }).sort((a, b) => SEATS.indexOf(a.seat) - SEATS.indexOf(b.seat) || a.outcome.localeCompare(b.outcome));
315
448
  if (result.cannotMeasureCount > 0) result.status = 'cannot-measure';
316
- return result;
449
+ return { ...result, daily: measureDailySpeedClaims(landed, !ledgerUnreadable, releaseEvents, now) };
317
450
  }
318
451
 
319
452
  export function renderSpeedClaims(result: SpeedClaimsMeasurement): string {
@@ -334,5 +467,11 @@ export function renderSpeedClaims(result: SpeedClaimsMeasurement): string {
334
467
  `parent cannot measure (excluded): ${result.parent.cannotMeasureCount}`,
335
468
  ...result.parent.rows.map((row) => `seat=${row.seat} stage=${row.stage} merged=${row.merged} count=${row.count} median=${row.medianMinutes}m q1=${row.q1Minutes}m q3=${row.q3Minutes}m`),
336
469
  `parent note: ${result.parent.note}`,
470
+ 'daily landed PRs / issued releases (last 30 days, UTC):',
471
+ `daily window: ${result.daily.windowStart}..${result.daily.windowEnd}`,
472
+ `landed PRs status: ${result.daily.landedPrs.status} (undated: ${result.daily.landedPrs.undatedCount})`,
473
+ `issued releases status: ${result.daily.releases.status} (undated: ${result.daily.releases.undatedCount})`,
474
+ ...result.daily.rows.map((row) => `date=${row.date} landed=${row.landedPrCount ?? 'unmeasured'} released=${row.releasedCount ?? 'unmeasured'}`),
475
+ `daily note: ${result.daily.note}`,
337
476
  ].join('\n');
338
477
  }
@@ -142,10 +142,20 @@ function migrateLegacySessions(): void {
142
142
  }
143
143
 
144
144
  function stateRoot(): string {
145
- const base = process.env.XDG_STATE_HOME && process.env.XDG_STATE_HOME.trim()
146
- ? process.env.XDG_STATE_HOME
147
- : join(homedir(), '.local', 'state');
148
- return join(base, 'elanous');
145
+ const universe = process.env.ELANOUS_STATE_DIR?.trim();
146
+ const root = universe
147
+ ? join(universe, 'elanous')
148
+ : join(
149
+ process.env.XDG_STATE_HOME && process.env.XDG_STATE_HOME.trim()
150
+ ? process.env.XDG_STATE_HOME
151
+ : join(homedir(), '.local', 'state'),
152
+ 'elanous',
153
+ );
154
+ try {
155
+ const { debug } = require('../debug/log.js') as typeof import('../debug/log.js');
156
+ debug.log('session.active', 'pointer-root', { root, universe });
157
+ } catch { /* 관측 실패가 포인터 경로 결정을 막지 않는다 */ }
158
+ return root;
149
159
  }
150
160
 
151
161
  function indexPath(root: string = sessionRoot()): string { return join(root, 'index.json'); }