elanous 0.2.15 → 0.2.17

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 (309) 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/{1307-33ffcc49ab45123b.js → 1307-aac7332576c4d6fc.js} +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{6877-7c6208705c284124.js → 6877-fe94e76a5841ee9b.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/8401-4ce1c42c8d89903f.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/899-f41be85e8a680651.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/{9103-7a3ae7f60af3b99f.js → 9103-60fbef33e66f0a46.js} +1 -1
  8. package/apps/pwa/out/_next/static/chunks/9247-269e4464cc4b491c.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/app/approvals/{page-0e2c57fe6add4b2a.js → page-3143b07a817d1da9.js} +1 -1
  10. package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-e05a77df7e005b04.js → page-17ba086ef82d339b.js} +1 -1
  11. package/apps/pwa/out/_next/static/chunks/app/board/page-7eb661ff23af7da2.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/app/botlab/page-6b8444914da74ca6.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/app/bots/{page-b8c49f6b4fa2df91.js → page-6e5c3a867557298b.js} +1 -1
  14. package/apps/pwa/out/_next/static/chunks/app/ceo/page-7110f732f45bf944.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/app/chat/{page-976813c91d7394ea.js → page-9a41d65355fc25b9.js} +1 -1
  16. package/apps/pwa/out/_next/static/chunks/app/consult/page-5e77cd1802567c89.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/app/control/{page-1d4901eb7c2d8822.js → page-41afec0b255ed2bb.js} +1 -1
  18. package/apps/pwa/out/_next/static/chunks/app/design-check/{page-72d3e7ff35a8c954.js → page-4187666132fc98c4.js} +1 -1
  19. package/apps/pwa/out/_next/static/chunks/app/editor/{page-60c7a8e1a4fe62ce.js → page-09328d499162bae4.js} +1 -1
  20. package/apps/pwa/out/_next/static/chunks/app/exec/{page-2a52b316cb143d9c.js → page-29ff3a8f84f03b6a.js} +1 -1
  21. package/apps/pwa/out/_next/static/chunks/app/field/{page-c3ce483c8d5db370.js → page-80ec7401bc83f511.js} +1 -1
  22. package/apps/pwa/out/_next/static/chunks/app/inside/page-ab93b240f687d69a.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/app/intake/{page-96f074f6538e8a2c.js → page-fc7d86467291ae38.js} +1 -1
  24. package/apps/pwa/out/_next/static/chunks/app/{layout-e97be5334d83a9f5.js → layout-de43a186e5ba5683.js} +1 -1
  25. package/apps/pwa/out/_next/static/chunks/app/live/page-8b08cafad27759c9.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/app/loops/page-0e2d6985fb42cd2f.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/app/market/page-13e4108999f58d13.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e8296ee284aa1293.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/app/morning/{page-c1343f9499d86f3f.js → page-043a9111e74f6b24.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/app/{not-found-8739a2e0ae793130.js → not-found-23a4601ceb7f452e.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/app/observatory/page-1ed1324b1dc36754.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-d23b12bb92242558.js → page-10d958605a77b3bd.js} +1 -1
  33. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-166745e51ac0c7b0.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/app/ops/seats/{page-c7fea475488201a8.js → page-a04260b8ccc968d4.js} +1 -1
  35. package/apps/pwa/out/_next/static/chunks/app/outputs/page-7a1d85e121065cc8.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/app/page-1f50e3d4e13435f4.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/app/reflection/{page-5efbf81124c474ae.js → page-12916918988ac97e.js} +1 -1
  38. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d8a3347a9d0c3f4a.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/app/sessions/page-a9117d2cd3093d36.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-06ee266953d9c879.js → page-5a64cb5208c6a8fd.js} +1 -1
  41. package/apps/pwa/out/_next/static/chunks/app/settings/{page-153c2b3bcc89e8de.js → page-1407ec83ea64c1f9.js} +1 -1
  42. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-1d824510df025d18.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/app/setup/page-6e878ebbe1737d00.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/app/share/{page-0d6d23b40263a7ea.js → page-7f49fe645d5132f7.js} +1 -1
  45. package/apps/pwa/out/_next/static/chunks/app/showroom/page-81a4e7f8dbfe74cb.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/app/tasks/page-27d71f131acd9320.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/app/term/page-3ab59d19913b200e.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/app/today/page-bf85b2f5bf077925.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/app/trace/{page-73735b64bfe1ae67.js → page-424659bacfb8cdd7.js} +1 -1
  50. package/apps/pwa/out/_next/static/chunks/app/vault/page-ab32a8f522a60371.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/{page-f7e631e9b4daf66e.js → page-fc2df261f8a2c7df.js} +1 -1
  52. package/apps/pwa/out/_next/static/chunks/app/workflows/page-09461366af82ef13.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/workspace/{page-9817c7f9007466cc.js → page-5d4fef9ccd7d406b.js} +1 -1
  54. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-ea90a8ac73ce2749.js +1 -0
  55. package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +1 -0
  56. package/apps/pwa/out/_next/static/css/{04dabcbcc4bfa6db.css → 085673d826b98b62.css} +1 -1
  57. package/apps/pwa/out/approvals/index.html +1 -1
  58. package/apps/pwa/out/approvals/index.txt +15 -15
  59. package/apps/pwa/out/autopilot/index.html +1 -1
  60. package/apps/pwa/out/autopilot/index.txt +15 -15
  61. package/apps/pwa/out/board/index.html +1 -1
  62. package/apps/pwa/out/board/index.txt +15 -15
  63. package/apps/pwa/out/botlab/index.html +1 -1
  64. package/apps/pwa/out/botlab/index.txt +15 -15
  65. package/apps/pwa/out/bots/index.html +1 -1
  66. package/apps/pwa/out/bots/index.txt +15 -15
  67. package/apps/pwa/out/ceo/index.html +1 -1
  68. package/apps/pwa/out/ceo/index.txt +15 -15
  69. package/apps/pwa/out/chat/index.html +1 -1
  70. package/apps/pwa/out/chat/index.txt +15 -15
  71. package/apps/pwa/out/consult/index.html +1 -1
  72. package/apps/pwa/out/consult/index.txt +15 -15
  73. package/apps/pwa/out/control/index.html +1 -1
  74. package/apps/pwa/out/control/index.txt +15 -15
  75. package/apps/pwa/out/design-check/index.html +1 -1
  76. package/apps/pwa/out/design-check/index.txt +15 -15
  77. package/apps/pwa/out/editor/index.html +1 -1
  78. package/apps/pwa/out/editor/index.txt +15 -15
  79. package/apps/pwa/out/exec/index.html +1 -1
  80. package/apps/pwa/out/exec/index.txt +15 -15
  81. package/apps/pwa/out/field/index.html +1 -1
  82. package/apps/pwa/out/field/index.txt +15 -15
  83. package/apps/pwa/out/healthz +1 -1
  84. package/apps/pwa/out/index.html +1 -1
  85. package/apps/pwa/out/index.txt +15 -15
  86. package/apps/pwa/out/inside/index.html +1 -1
  87. package/apps/pwa/out/inside/index.txt +15 -15
  88. package/apps/pwa/out/intake/index.html +1 -1
  89. package/apps/pwa/out/intake/index.txt +15 -15
  90. package/apps/pwa/out/live/index.html +1 -1
  91. package/apps/pwa/out/live/index.txt +15 -15
  92. package/apps/pwa/out/loops/index.html +1 -1
  93. package/apps/pwa/out/loops/index.txt +15 -15
  94. package/apps/pwa/out/market/index.html +1 -1
  95. package/apps/pwa/out/market/index.txt +15 -15
  96. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  97. package/apps/pwa/out/missions/placeholder/index.txt +15 -15
  98. package/apps/pwa/out/morning/index.html +1 -1
  99. package/apps/pwa/out/morning/index.txt +15 -15
  100. package/apps/pwa/out/observatory/index.html +1 -1
  101. package/apps/pwa/out/observatory/index.txt +15 -15
  102. package/apps/pwa/out/ops/checklist/index.html +1 -1
  103. package/apps/pwa/out/ops/checklist/index.txt +15 -15
  104. package/apps/pwa/out/ops/release/index.html +1 -1
  105. package/apps/pwa/out/ops/release/index.txt +15 -15
  106. package/apps/pwa/out/ops/seats/index.html +1 -1
  107. package/apps/pwa/out/ops/seats/index.txt +15 -15
  108. package/apps/pwa/out/outputs/index.html +1 -1
  109. package/apps/pwa/out/outputs/index.txt +15 -15
  110. package/apps/pwa/out/reflection/index.html +1 -1
  111. package/apps/pwa/out/reflection/index.txt +16 -16
  112. package/apps/pwa/out/scheduler/index.html +1 -1
  113. package/apps/pwa/out/scheduler/index.txt +15 -15
  114. package/apps/pwa/out/sessions/index.html +1 -1
  115. package/apps/pwa/out/sessions/index.txt +15 -15
  116. package/apps/pwa/out/settings/devices/index.html +1 -1
  117. package/apps/pwa/out/settings/devices/index.txt +15 -15
  118. package/apps/pwa/out/settings/index.html +1 -1
  119. package/apps/pwa/out/settings/index.txt +15 -15
  120. package/apps/pwa/out/setup/done/index.html +1 -1
  121. package/apps/pwa/out/setup/done/index.txt +15 -15
  122. package/apps/pwa/out/setup/index.html +1 -1
  123. package/apps/pwa/out/setup/index.txt +15 -15
  124. package/apps/pwa/out/share/index.html +1 -1
  125. package/apps/pwa/out/share/index.txt +15 -15
  126. package/apps/pwa/out/showroom/index.html +1 -1
  127. package/apps/pwa/out/showroom/index.txt +15 -15
  128. package/apps/pwa/out/tasks/index.html +1 -1
  129. package/apps/pwa/out/tasks/index.txt +15 -15
  130. package/apps/pwa/out/term/index.html +1 -1
  131. package/apps/pwa/out/term/index.txt +15 -15
  132. package/apps/pwa/out/today/index.html +1 -1
  133. package/apps/pwa/out/today/index.txt +15 -15
  134. package/apps/pwa/out/trace/index.html +1 -1
  135. package/apps/pwa/out/trace/index.txt +15 -15
  136. package/apps/pwa/out/vault/index.html +1 -1
  137. package/apps/pwa/out/vault/index.txt +15 -15
  138. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  139. package/apps/pwa/out/workflows/chat-ui/index.txt +15 -15
  140. package/apps/pwa/out/workflows/index.html +1 -1
  141. package/apps/pwa/out/workflows/index.txt +15 -15
  142. package/apps/pwa/out/workspace/index.html +1 -1
  143. package/apps/pwa/out/workspace/index.txt +15 -15
  144. package/apps/pwa/out/worktrees/index.html +1 -1
  145. package/apps/pwa/out/worktrees/index.txt +15 -15
  146. package/graphs/doc-gen/doc-gen.yaml +24 -0
  147. package/graphs/doc-gen/recipes.yaml +3 -0
  148. package/graphs/intake/intake-daily.yaml +9 -2
  149. package/graphs/intake/news-sources.yaml +4 -0
  150. package/graphs/intake/recipes.yaml +6 -1
  151. package/graphs/orchestrator/orchestrator.yaml +7 -0
  152. package/graphs/orchestrator/recipes.yaml +3 -0
  153. package/graphs/steward/recipes.yaml +2 -1
  154. package/package.json +1 -1
  155. package/scripts/__fixtures__/intake-news.xml +7 -0
  156. package/scripts/ci-public-export-import-gate.ts +154 -0
  157. package/scripts/ci-test-interference-gate.ts +5 -1
  158. package/scripts/ci-typecheck-changed.ts +34 -15
  159. package/scripts/docs/doc-rot.ts +164 -0
  160. package/scripts/docs/generate-graph-anatomy.ts +131 -9
  161. package/scripts/docs-cli-check.ts +4 -4
  162. package/scripts/goals/goal-census.ts +343 -0
  163. package/scripts/intake-news.ts +169 -0
  164. package/scripts/measure/author-section-contribution.ts +84 -0
  165. package/scripts/release-loop/cut-branch.ts +43 -13
  166. package/scripts/release-loop/docs-land-node.ts +69 -10
  167. package/scripts/release-loop/gate-node.ts +3 -1
  168. package/scripts/release-loop/publish-node.ts +2 -2
  169. package/scripts/release-loop/release-docs.ts +134 -0
  170. package/scripts/release-loop/vault-note-node.ts +19 -2
  171. package/scripts/release-loop/version-node.ts +7 -4
  172. package/scripts/release-story/draft.ts +42 -14
  173. package/scripts/rhythm/monthly.ts +97 -0
  174. package/src/acp/client.ts +17 -2
  175. package/src/agent-mission/driver.ts +104 -11
  176. package/src/agent-mission/supervisor-metrics-cli.ts +40 -0
  177. package/src/agent-mission/supervisor-metrics.ts +218 -0
  178. package/src/autopilot/track-agent-shadow.ts +2 -1
  179. package/src/autopilot/track-agent.ts +5 -3
  180. package/src/briefing/brief-items.ts +191 -36
  181. package/src/budget/usage-outcomes.ts +204 -0
  182. package/src/cli/autopilot-cli.ts +66 -0
  183. package/src/cli/brief-cli.ts +73 -0
  184. package/src/cli/decisions-cli.ts +4 -1
  185. package/src/cli/intake-cli.ts +1 -1
  186. package/src/cli/know-cli.ts +20 -0
  187. package/src/cli/log-event-names.ts +1 -0
  188. package/src/cli/logs-cli.ts +43 -1
  189. package/src/cli/pr-cli.ts +92 -13
  190. package/src/cli/provider-cli.ts +14 -0
  191. package/src/cli/release-cli.ts +11 -8
  192. package/src/cli/self-update-canary.ts +46 -0
  193. package/src/cli/self-update.ts +399 -1
  194. package/src/cli/usage-cli.ts +26 -0
  195. package/src/context-card/index.ts +45 -0
  196. package/src/context-card/task.ts +76 -0
  197. package/src/dashboard/slash-runtime/dashboard-handlers.ts +2 -0
  198. package/src/decisions/decision-ledger.ts +10 -1
  199. package/src/discord-self-message.ts +9 -0
  200. package/src/flow-loop/split.ts +24 -0
  201. package/src/harness/harness-cli-command.ts +178 -12
  202. package/src/harness/harness-pod-dispatch.ts +36 -55
  203. package/src/harness/harness-queue.ts +86 -7
  204. package/src/harness/harness-seams.ts +13 -3
  205. package/src/harness/helper-repair.ts +139 -8
  206. package/src/harness/helper-scan.ts +13 -2
  207. package/src/harness/nested-elanous-policy.ts +133 -0
  208. package/src/hq/fence-wrapper.ts +123 -0
  209. package/src/hq/hq.ts +16 -2
  210. package/src/hq/lease.ts +5 -3
  211. package/src/hq/transfer.ts +185 -0
  212. package/src/index.ts +168 -13
  213. package/src/intake-plane/digest.ts +85 -28
  214. package/src/knowledge/know-find.ts +105 -0
  215. package/src/llm.ts +75 -19
  216. package/src/loops/orchestrator/finish-rate.ts +14 -3
  217. package/src/loops/orchestrator/tick.ts +96 -25
  218. package/src/maturity/bot-slash-catalog.ts +11 -2
  219. package/src/maturity/feature-maturity.ts +2 -2
  220. package/src/maturity/tui-slash-availability.ts +9 -0
  221. package/src/nexus/api/grid.ts +61 -0
  222. package/src/nexus/api/http-server.ts +19 -1
  223. package/src/nexus/api/task-cards-api.ts +19 -0
  224. package/src/oauth/codex.ts +82 -2
  225. package/src/oauth/overload-failover-launch.ts +75 -0
  226. package/src/oauth/overload-failover.ts +137 -0
  227. package/src/release-loop/checklist.ts +10 -5
  228. package/src/release-loop/feature-store.ts +10 -9
  229. package/src/release-loop/landing-freeze.ts +18 -10
  230. package/src/release-loop/placement.ts +17 -8
  231. package/src/seat-dispatch/seat-ask.ts +33 -6
  232. package/src/seat-loop/seat-loop.ts +172 -21
  233. package/src/self-dev/ask-launch-flow.ts +48 -8
  234. package/src/self-dev/authored-goal-trace.ts +64 -0
  235. package/src/self-dev/dev-cli.ts +19 -1
  236. package/src/self-dev/dev-pipeline.ts +6 -5
  237. package/src/self-dev/entrance-registry.ts +10 -6
  238. package/src/self-dev/old-door.ts +73 -0
  239. package/src/self-dev/run-store.ts +5 -1
  240. package/src/self-dev/run-supervisor.ts +7 -1
  241. package/src/self-dev/supervisor-verdict-edges.ts +46 -0
  242. package/src/self-implement/app-api-budget.ts +166 -0
  243. package/src/self-implement/budget-gate.ts +7 -5
  244. package/src/self-implement/follow-up-goals.ts +289 -0
  245. package/src/self-implement/frozen-merges.ts +25 -20
  246. package/src/self-implement/goal-author-clarification.ts +81 -0
  247. package/src/self-implement/goal-author-cli.ts +33 -5
  248. package/src/self-implement/goal-author.ts +107 -25
  249. package/src/self-implement/goal-clarification-escalation.ts +37 -1
  250. package/src/self-implement/goal-document-write.ts +28 -0
  251. package/src/self-implement/heal-intake.ts +4 -0
  252. package/src/self-implement/heal-triage.ts +44 -0
  253. package/src/self-implement/host-regate.ts +3 -1
  254. package/src/self-implement/orchestrator.ts +157 -8
  255. package/src/self-implement/run-ledger.ts +4 -0
  256. package/src/self-implement/seams.ts +44 -15
  257. package/src/session-runtime/index.ts +2 -0
  258. package/src/session-runtime/retry-api.ts +87 -0
  259. package/src/session-runtime/retry-policy.ts +107 -6
  260. package/src/steward/working-backwards.ts +16 -1
  261. package/src/task-agent/actions.ts +118 -0
  262. package/src/task-agent/judge.ts +96 -0
  263. package/src/task-cards/card-cli.ts +25 -0
  264. package/src/task-cards/card-store.ts +9 -4
  265. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +33 -4
  266. package/src/task-orchestrator/surfaces/pod-lease.ts +5 -2
  267. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +4 -3
  268. package/src/task-orchestrator/surfaces/pod-ledger-prod-sink.ts +75 -0
  269. package/src/task-orchestrator/surfaces/pod-pool.ts +95 -18
  270. package/src/task-orchestrator/surfaces/pod-source-receive.ts +37 -1
  271. package/src/task-orchestrator/surfaces/self-implement-pod.ts +168 -37
  272. package/src/task-orchestrator/surfaces/self-implement.ts +2 -0
  273. package/src/telegram-commands.ts +6 -1
  274. package/src/telegram-loops-command.ts +21 -14
  275. package/src/telegram.ts +47 -0
  276. package/src/user-config.ts +59 -4
  277. package/src/version/packed-revision.json +1 -1
  278. package/apps/pwa/out/_next/static/chunks/8827-e2f5b09a8d37e83b.js +0 -1
  279. package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +0 -1
  280. package/apps/pwa/out/_next/static/chunks/9247-29d889b809288e65.js +0 -1
  281. package/apps/pwa/out/_next/static/chunks/app/board/page-f544338193a9a762.js +0 -1
  282. package/apps/pwa/out/_next/static/chunks/app/botlab/page-5491c115b1f9a923.js +0 -1
  283. package/apps/pwa/out/_next/static/chunks/app/ceo/page-96398e364fbd4548.js +0 -1
  284. package/apps/pwa/out/_next/static/chunks/app/consult/page-28b9448091776c06.js +0 -1
  285. package/apps/pwa/out/_next/static/chunks/app/inside/page-13c7b53e0c2519a9.js +0 -1
  286. package/apps/pwa/out/_next/static/chunks/app/live/page-65e484302921a932.js +0 -1
  287. package/apps/pwa/out/_next/static/chunks/app/loops/page-34d08facaf08f556.js +0 -1
  288. package/apps/pwa/out/_next/static/chunks/app/market/page-955ec2da3be4c560.js +0 -1
  289. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-9f88fbc15d9058d1.js +0 -1
  290. package/apps/pwa/out/_next/static/chunks/app/observatory/page-c9fc5706b8abaa11.js +0 -1
  291. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8d47f45a0dc044ac.js +0 -1
  292. package/apps/pwa/out/_next/static/chunks/app/outputs/page-e2ee2896811e98d0.js +0 -1
  293. package/apps/pwa/out/_next/static/chunks/app/page-83b19dae8619de05.js +0 -1
  294. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f1a1fc0a82738c.js +0 -1
  295. package/apps/pwa/out/_next/static/chunks/app/sessions/page-da6c08aae0f92768.js +0 -1
  296. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-87a11e6a05f15199.js +0 -1
  297. package/apps/pwa/out/_next/static/chunks/app/setup/page-7d4182964407c09c.js +0 -1
  298. package/apps/pwa/out/_next/static/chunks/app/showroom/page-7e8c39241f8eb6d0.js +0 -1
  299. package/apps/pwa/out/_next/static/chunks/app/tasks/page-008c1a96618a4887.js +0 -1
  300. package/apps/pwa/out/_next/static/chunks/app/term/page-f5597e894c02cd33.js +0 -1
  301. package/apps/pwa/out/_next/static/chunks/app/today/page-0337fdf7fb5e37b6.js +0 -1
  302. package/apps/pwa/out/_next/static/chunks/app/vault/page-8fcec4c9ea2e7f70.js +0 -1
  303. package/apps/pwa/out/_next/static/chunks/app/workflows/page-4881b8f9f63694b4.js +0 -1
  304. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-97f9f43172a3a90e.js +0 -1
  305. package/apps/pwa/out/_next/static/css/1f7c958c41ffa3b9.css +0 -1
  306. package/src/launch-head/seat-goal-queue-lock.fixture.ts +0 -15
  307. package/src/launch-head/seat-goal-queue.ts +0 -198
  308. /package/apps/pwa/out/_next/static/{LZcstaO9_NXypPFmujolv → yRA5ur6DcPl0YHSRheH2O}/_buildManifest.js +0 -0
  309. /package/apps/pwa/out/_next/static/{LZcstaO9_NXypPFmujolv → yRA5ur6DcPl0YHSRheH2O}/_ssgManifest.js +0 -0
@@ -11,6 +11,7 @@
11
11
  // ⛔ 종료(`process.exit`)는 여기가 «안» 한다 — 호출자가 자기 표면에 맞게 끝낸다.
12
12
  // 그래서 결과를 «세 값»으로 낸다: 발사 / 저작 전 중단 / 전제 검사 중단.
13
13
  import { linesOutsideFencedCode } from '../self-implement/goal-author.js';
14
+ import { recordAuthoredGoal } from './authored-goal-trace.js';
14
15
  import { retiredEntranceNotice, type EntranceDeclaration } from './entrance-registry.js';
15
16
  import {
16
17
  classifyConcurrentAuthoring,
@@ -34,6 +35,8 @@ import { dirname, isAbsolute as isAbsolutePath, relative as relativePath, resolv
34
35
  import {
35
36
  applyClarificationReply,
36
37
  planClarificationIntake,
38
+ selfAnswerGoalDocumentClarifications,
39
+ type GoalClarificationSelfResolver,
37
40
  } from '../self-implement/goal-author-clarification.js';
38
41
  import { classifyReauthoredAsk, countMissingAuthoredConstraintMarkers, askGoalTypeDeclaration, declaredGoalType, parseGoalId } from '../self-implement/goal-author.js';
39
42
  import { observeFrontNodeEntry } from './graph-front-nodes.js';
@@ -58,7 +61,7 @@ function resolveSelfResolveClarifications(explicit: boolean | undefined): Resolv
58
61
  /** 저작기 산출 — `runGoalAuthorCli` 의 반환에서 이 흐름이 «실제로 쓰는» 것만. */
59
62
  export interface AskAuthoredGoal {
60
63
  readonly path: string;
61
- readonly authored?: { readonly document?: string; readonly grounded?: boolean | null } | null;
64
+ readonly authored?: { readonly document?: string; readonly grounded?: boolean | null; readonly authorRunId?: string } | null;
62
65
  }
63
66
 
64
67
  export type LaunchingTreeProbe =
@@ -142,6 +145,9 @@ export interface AskLaunchFlowDeps {
142
145
  /** Optional structured clarification adapter. Omitted callers retain the
143
146
  * existing CLI line-input contract exactly. */
144
147
  readClarification?(clarification: GoalDocumentClarification): Promise<string>;
148
+ selfResolveClarification?: GoalClarificationSelfResolver;
149
+ /** Replaces the goal document after a self-answer; production entrances pass an atomic writer. */
150
+ writeGoalDocument?(path: string, document: string): void;
145
151
  readFile(path: string): string;
146
152
  writeFile(path: string, data: string): void;
147
153
  cwd(): string;
@@ -158,6 +164,8 @@ export interface AskLaunchFlowDeps {
158
164
  /** ⛔ 못 얻으면 `null` — 같은 이유. */
159
165
  recentAuthoringSamples(): RecentAuthoringSample[] | null;
160
166
  authorGoal(args: readonly string[], options: Record<string, unknown>): Promise<AskAuthoredGoal>;
167
+ /** AUTHOR-TRACE seam (tests). */
168
+ recordAuthoredGoal?: typeof recordAuthoredGoal;
161
169
  /** Pod launches reserve a host slot before goal authoring begins. */
162
170
  beforeAuthoring?(): Promise<void>;
163
171
  /** 발사 전 권고용 분해 seam. 생략하면 전용 관측명을 준 기존 분해기를 쓴다. */
@@ -185,6 +193,7 @@ export interface AskLaunchFlowInput {
185
193
  readonly askFile?: string;
186
194
  /** Optional repository root used only for code grounding during goal authoring. */
187
195
  readonly groundingCwd?: string;
196
+ readonly authorGrade?: import('../self-implement/goal-author.js').GoalAuthorGradeSelection;
188
197
  readonly liveRunWindowMinutes: number;
189
198
  readonly recentChangeWindowDays: number;
190
199
  readonly forceRequested: boolean;
@@ -721,16 +730,27 @@ async function intakeClarifications(
721
730
  return effectiveGoalFile;
722
731
  }
723
732
  if (mode === 'deferred-noninteractive') {
724
- // ⛔⭐ 종전엔 「N건 있다」만 말하고 «무엇을 묻는지»도 «어떻게 답하는지»도 안 줬다.
725
- // ⇒ 비대화형 표면(무인 런 · TUI 슬래시)에서 그 물음은 사실상 «사라졌다».
726
- // ⛔ 여기에 새 인터뷰 UI 를 만들지 «않는다» — 답변 창구는 `elanous self clarify answer` 로 «이미» 있다.
727
- // 이 자리는 그것을 «가리키기»만 한다(2026-08-11 72차: 오늘만 `F12` 를 여섯 번 셌다).
728
- deps.print(`[ask] ⑴b ⚠️ 미답 되묻기 ${pending.length}건 — 저작은 그대로 간다 · 구현 전에 LLM 릴레이가 먼저 답하고, 못 하면 미답으로 진행한다 (사람을 기다리지 않는다)`);
729
- for (const item of pending) {
733
+ // The resolver comes in through deps (I/O seam): production entrances pass the author
734
+ // self-resolver; a caller that injects none keeps the previous deferred behaviour.
735
+ if (selfResolveClarifications.value && deps.selfResolveClarification) try {
736
+ const resolution = await selfAnswerGoalDocumentClarifications(document, {
737
+ path: 'ask', ask: input.askText, resolver: deps.selfResolveClarification,
738
+ });
739
+ if (resolution.newlyAnswered > 0) {
740
+ (deps.writeGoalDocument ?? deps.writeFile)(effectiveGoalFile, resolution.document);
741
+ document = resolution.document;
742
+ }
743
+ } catch (error) {
744
+ deps.log('ask-clarify', { goalFile: effectiveGoalFile, reason: 'self-answer-failed', error: error instanceof Error ? error.message : String(error) }, 'warn');
745
+ }
746
+ const remaining = planClarificationIntake(document, false).pending;
747
+ // 미답으로 남은 문항만 기존 답변 창구를 안내한다.
748
+ deps.print(`[ask] ⑴b ⚠️ 미답 되묻기 ${remaining.length}건 — 저작은 그대로 간다 · 구현 전에 LLM 릴레이가 먼저 답하고, 못 하면 미답으로 진행한다 (사람을 기다리지 않는다)`);
749
+ for (const item of remaining) {
730
750
  deps.print(`[ask] ❓ [${item.questionId}] ${item.question}`);
731
751
  item.options.forEach((option, index) => deps.print(`[ask] ${index}) ${option.label}`));
732
752
  }
733
- if (pending.length > 0) {
753
+ if (remaining.length > 0) {
734
754
  const goalArg = deps.relativeToCwd(effectiveGoalFile);
735
755
  deps.print(`[ask] ▶ 답하려면: elanous self clarify answer ${goalArg} <questionId> <옵션번호> (자유 답은 --other "<문장>")`);
736
756
  deps.print(`[ask] ▶ 답한 뒤 재저작: elanous self author --supersedes ${goalArg}`);
@@ -768,6 +788,7 @@ async function intakeClarifications(
768
788
  const reauthored = await deps.authorGoal([], {
769
789
  cwd: deps.cwd(),
770
790
  ...(input.groundingCwd === undefined ? {} : { groundingCwd: input.groundingCwd }),
791
+ ...(input.authorGrade === undefined ? {} : { authorGrade: input.authorGrade }),
771
792
  fromClarification: `${deps.relativeToCwd(effectiveGoalFile)}#${seedQuestionId}`,
772
793
  reauthorFromAnsweredClarification: true,
773
794
  selfResolveClarifications: selfResolveClarifications.value,
@@ -850,6 +871,7 @@ export async function runAskLaunchFlow(
850
871
  const authored = await deps.authorGoal([input.askText], {
851
872
  cwd: deps.cwd(),
852
873
  ...(input.groundingCwd === undefined ? {} : { groundingCwd: input.groundingCwd }),
874
+ ...(input.authorGrade === undefined ? {} : { authorGrade: input.authorGrade }),
853
875
  ...(goalType === null ? {} : { goalType }),
854
876
  // ⛔ 인라인 ask(`harness say`)는 askFile 이 없다 — 그 칸을 넘기면 저작기가 빈 줄을 쓴다.
855
877
  ...(() => {
@@ -861,6 +883,24 @@ export async function runAskLaunchFlow(
861
883
  selfResolveClarifications: selfResolveClarifications.value,
862
884
  });
863
885
  deps.print(`[ask] 저작됨: ${authored.path}`);
886
+ // AUTHOR-TRACE: the authored goal file can be cleaned up later — keep path ⊕ sha256 ⊕ size in the run ledger and
887
+ // archive the body, so authoring length is measurable after the fact (GOAL-BENCH · AUTHOR-LITE).
888
+ const traceIds = {
889
+ ...(input.runId === undefined ? {} : { runId: input.runId }),
890
+ ...(authored.authored?.authorRunId === undefined ? {} : { authorRunId: authored.authored.authorRunId }),
891
+ };
892
+ if (authored.authored?.document !== undefined && (traceIds.runId !== undefined || traceIds.authorRunId !== undefined)) {
893
+ try {
894
+ const trace = (deps.recordAuthoredGoal ?? recordAuthoredGoal)(traceIds, authored.path, authored.authored.document);
895
+ deps.log('goal-authored-trace', { ...traceIds, ...trace }, 'info');
896
+ } catch (error) {
897
+ deps.log('goal-authored-trace-failed', { ...traceIds, error: error instanceof Error ? error.message : String(error) }, 'warn');
898
+ }
899
+ } else {
900
+ deps.log('goal-authored-trace-skipped', {
901
+ reason: authored.authored?.document === undefined ? 'no-authored-document' : 'no-run-or-author-id', path: authored.path,
902
+ }, 'warn');
903
+ }
864
904
  const markerCounts = authored.authored?.document === undefined
865
905
  ? null
866
906
  : countMissingAuthoredConstraintMarkers(input.askText, authored.authored.document);
@@ -0,0 +1,64 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { appendRunLedgerEntry, runLedgerDir } from '../self-implement/run-ledger.js';
5
+ import { debug } from '../debug/log.js';
6
+
7
+ /** AUTHOR-TRACE: what `harness say`/`ask` authored, kept with the run — the goal file itself may be cleaned up later,
8
+ * so the ledger carries path ⊕ sha256 ⊕ size and the body is archived next to the ledger. */
9
+ export interface AuthoredGoalTrace {
10
+ path: string;
11
+ sha256: string;
12
+ chars: number;
13
+ sections: number;
14
+ archivedAt: string;
15
+ }
16
+
17
+ /** `## ` headings outside fenced code (a fence closes only on the same marker, at least as long). */
18
+ function countSections(document: string): number {
19
+ let fence: { char: string; length: number } | null = null;
20
+ let count = 0;
21
+ for (const line of document.split('\n')) {
22
+ const marker = /^ {0,3}(`{3,}|~{3,})/.exec(line);
23
+ if (fence) {
24
+ if (marker && marker[1]![0] === fence.char && marker[1]!.length >= fence.length && line.trim() === marker[1]) fence = null;
25
+ continue;
26
+ }
27
+ if (marker) { fence = { char: marker[1]![0]!, length: marker[1]!.length }; continue; }
28
+ if (/^ {0,3}##\s/.test(line)) count += 1;
29
+ }
30
+ return count;
31
+ }
32
+
33
+ export function authoredGoalTrace(path: string, document: string, archivedAt: string): AuthoredGoalTrace {
34
+ return {
35
+ path,
36
+ sha256: createHash('sha256').update(document).digest('hex'),
37
+ chars: [...document].length,
38
+ sections: countSections(document),
39
+ archivedAt,
40
+ };
41
+ }
42
+
43
+ /** Archive the body, emit one `goal-author`/`goal-authored` observation and — when the run id is already known —
44
+ * append a `goal-authored` run-ledger event. At `harness say` authoring time the run id usually does not exist yet;
45
+ * the authoring run id (`authorRunId`, joined to the goal by `goal-id-assigned`) keys the record then. */
46
+ export function recordAuthoredGoal(
47
+ ids: { runId?: string; authorRunId?: string },
48
+ path: string,
49
+ document: string,
50
+ options: { ledgerDir?: string; archiveDir?: string } = {},
51
+ ): AuthoredGoalTrace {
52
+ const key = ids.runId ?? ids.authorRunId;
53
+ if (!key) throw new Error('recordAuthoredGoal needs a runId or an authorRunId');
54
+ const ledgerDir = options.ledgerDir ?? runLedgerDir();
55
+ const archiveDir = options.archiveDir ?? join(dirname(ledgerDir), 'authored-goals');
56
+ const digest = createHash('sha256').update(document).digest('hex');
57
+ const archived = join(archiveDir, `${key}-${digest.slice(0, 12)}.md`);
58
+ if (!existsSync(archiveDir)) mkdirSync(archiveDir, { recursive: true });
59
+ writeFileSync(archived, document, 'utf8');
60
+ const trace = authoredGoalTrace(path, document, archived);
61
+ debug.log('goal-author', 'goal-authored', { ...ids, ...trace });
62
+ if (ids.runId) appendRunLedgerEntry({ runId: ids.runId, event: 'goal-authored', data: { ...trace, ...(ids.authorRunId ? { authorRunId: ids.authorRunId } : {}) } }, ledgerDir);
63
+ return trace;
64
+ }
@@ -43,6 +43,7 @@ import { observeDeliverables, type DeliverableObservationResult, type Deliverabl
43
43
  import { debug } from '../debug/log.js';
44
44
  import { getUserConfig, type UserConfig } from '../user-config.js';
45
45
  import { resolveChildLlmPreference, type ChildLlmPreferenceInput, type ResolvedChildLlmPreference } from '../self-implement/child-llm-preference.js';
46
+ import { applyLaunchOverloadFailover, codexProviderNamed } from '../oauth/overload-failover-launch.js';
46
47
  import { readCachedGrokQuota } from '../oauth/codex-account-store.js';
47
48
  import { queryAbandonedDraftPrs, type AbandonedDraftPr } from '../cli/logs-abandoned-draft-prs.js';
48
49
  import { branchLineageSlug } from '../cli/pr-lineage.js';
@@ -1631,6 +1632,18 @@ function resolveChildLlmFromParts(
1631
1632
  return undefined;
1632
1633
  }
1633
1634
 
1635
+ /** Pinned `tools.selfImplement.childLlm` naming codex. Fallback-chain codex is not pinned. */
1636
+ function childLlmCodexPinned(flagProvider: string | undefined): boolean {
1637
+ if (flagProvider !== undefined) return false;
1638
+ try {
1639
+ const preference = resolveChildLlmPreference(getUserConfig());
1640
+ const provider = preference.chain[0]?.provider;
1641
+ return preference.mode === 'pinned' && preference.source.chain === 'pinned' && codexProviderNamed(provider);
1642
+ } catch {
1643
+ return false;
1644
+ }
1645
+ }
1646
+
1634
1647
  export function buildChildLlmSelection(
1635
1648
  opts: Pick<DevCliOpts, 'childLlmProvider' | 'childLlmModel' | 'childLlmEffort'> & {
1636
1649
  /** 발사 인자가 있을 때 관측용 선호를 읽는다. 없으면 getUserConfig. 기존 두 인자 시그니처는 그대로다. */
@@ -1967,7 +1980,12 @@ export function buildDevCliSpec(
1967
1980
  }
1968
1981
 
1969
1982
  if (path === 'self-mission') {
1970
- const childLlm = buildChildLlmSelection({ ...opts, readPreference: () => getUserConfig() });
1983
+ const builtChild = buildChildLlmSelection({ ...opts, readPreference: () => getUserConfig() });
1984
+ const childLlm = applyLaunchOverloadFailover(builtChild, {
1985
+ provider: opts.childLlmProvider,
1986
+ model: opts.childLlmModel,
1987
+ codexPinned: childLlmCodexPinned(opts.childLlmProvider),
1988
+ });
1971
1989
  // 잔량 조회(약 3초)는 한 발사에 한 번만 — 자식·부모가 같은 값을 쓴다.
1972
1990
  let grokQuotaOnce: ReturnType<typeof readCachedGrokQuota> | undefined;
1973
1991
  const readGrokQuotaOnce = () => (grokQuotaOnce ??= readGrokQuota());
@@ -1863,9 +1863,9 @@ async function observeDevTaskDispatch(spec: DevPipelineSpec, plan: ResolvedDevPl
1863
1863
  }
1864
1864
  }
1865
1865
 
1866
- async function inspectReleasePathBeforeMerge(seams: SelfImplementSeams, number: number, cwd: string): Promise<string | undefined> {
1866
+ async function inspectReleasePathBeforeMerge(seams: SelfImplementSeams, number: number, cwd: string, base?: string): Promise<string | undefined> {
1867
1867
  if (!seams.readPrFiles) throw new Error('release-path inspection unavailable — automatic merge refused');
1868
- const files = await seams.readPrFiles({ number, cwd });
1868
+ const files = await seams.readPrFiles({ number, cwd, ...(base ? { base } : {}) });
1869
1869
  if (!Array.isArray(files) || !files.every((path) => typeof path === 'string')) throw new Error('invalid PR file list — automatic merge refused');
1870
1870
  const path = releasePathHold(files);
1871
1871
  if (!path) return undefined;
@@ -2107,10 +2107,11 @@ async function runDevPipelineDispatch(
2107
2107
  ...assembledSeams,
2108
2108
  ...(assembledSeams.mergePr ? { mergePr: async (input) => {
2109
2109
  let path: string | undefined;
2110
- try { path = await inspectReleasePathBeforeMerge(assembledSeams, input.number, input.cwd); }
2110
+ try { path = await inspectReleasePathBeforeMerge(assembledSeams, input.number, input.cwd, plan.base); }
2111
2111
  catch (error) {
2112
- debug.log('self-dev.merge', 'release-path-inspection-failed', { number: input.number, error: String(error) });
2113
- return { merged: false, detail: `automatic merge refused: ${String(error)}` };
2112
+ const rateLimited = error instanceof Error && (error as { rateLimited?: boolean }).rateLimited === true;
2113
+ debug.log('self-dev.merge', 'release-path-inspection-failed', { number: input.number, error: String(error), ...(rateLimited ? { rateLimit: true } : {}) });
2114
+ return { merged: false, detail: rateLimited ? `automatic merge refused: rate-limit: ${String(error)}` : `automatic merge refused: ${String(error)}` };
2114
2115
  }
2115
2116
  if (path) return { merged: false, detail: `OP approval required: ${path}` };
2116
2117
  return assembledSeams.mergePr!(input);
@@ -1,6 +1,6 @@
1
1
  import { findNativeTool, isNativeToolModelExposed } from '../native-tool-catalog.js';
2
2
 
3
- export type EntranceStatus = 'live' | 'retired';
3
+ export type EntranceStatus = 'live' | 'retired' | 'closed';
4
4
 
5
5
  export type EntranceStampability = 'stampable' | 'structurally-unstampable' | 'unknown';
6
6
 
@@ -181,12 +181,14 @@ const ENTRANCE_DECLARATIONS = [
181
181
  // `dev --ask` 와 ***같은*** `runAskLaunchFlow` 를 탄다. 두 문의 차이는 `entrance` 태그뿐이다.
182
182
  // ⇒ 판정값이 `'verified' | 'unknown'` 둘뿐이라 「구조적 부재」를 적을 칸이 없다. `unknown` 으로 둔다.
183
183
  // ⛔ 그러니 이 `unknown` 을 「미완」으로 읽지 마라 — 그 구분은 이 주석이 canonical 이다.
184
- { id: 'cli-dev-ask', surface: 'cli', status: 'live', verification: { actualMission: 'verified', legacyParity: 'unknown' } },
184
+ // OLD-DOOR-CLOSE — a direct `dev --ask` from a terminal or seat session refuses.
185
+ // Harness-internal calls still pass, marked with the ONEDOOR-1 entrance stamp.
186
+ { id: 'cli-dev-ask', surface: 'cli', status: 'closed', verification: { actualMission: 'verified', legacyParity: 'unknown' } },
185
187
  // `elanous drive` 는 `elanous dev` 의 Commander alias 로 살아 있고, buildDriveAliasDevSpec 이 entrance 를
186
188
  // `cli-drive` 로 넘기며 completion 을 `worktree-only` 로 못 박는다. 그래서 별도 무인 권한 축이 아니다.
187
189
  { id: 'cli-drive', surface: 'cli', status: 'live' },
188
- { id: 'cli-self-implement', surface: 'cli', status: 'live' },
189
- { id: 'cli-self-orchestrate', surface: 'cli', status: 'live' },
190
+ { id: 'cli-self-implement', surface: 'cli', status: 'closed' },
191
+ { id: 'cli-self-orchestrate', surface: 'cli', status: 'closed' },
190
192
  // ⭐ 2026-08-22 · 🅣 — ***사람이 «돌려 보고» 적은 판정***이다(⛔ 도구가 추측한 값이 아니다).
191
193
  // actualMission: 그 입구로 실제 골을 «끝까지» 돌렸다 — 17발 · 자동 병합 5 · 회수 착지 7.
192
194
  // 📏 원장 각인이 그것을 말한다: `"entrance":"cli-harness-ask"` (아침엔 0건이었다).
@@ -331,6 +333,7 @@ export function summarizeEntrances(registry: readonly EntranceDeclaration[] = EN
331
333
  readonly total: number;
332
334
  readonly live: number;
333
335
  readonly retired: number;
336
+ readonly closed: number;
334
337
  readonly bySurface: ReadonlyArray<{ surface: EntranceSurface; total: number; retired: number }>;
335
338
  } {
336
339
  const surfaces = [...new Set(registry.map((e) => e.surface))];
@@ -338,6 +341,7 @@ export function summarizeEntrances(registry: readonly EntranceDeclaration[] = EN
338
341
  total: registry.length,
339
342
  live: registry.filter((e) => e.status === 'live').length,
340
343
  retired: registry.filter((e) => e.status === 'retired').length,
344
+ closed: registry.filter((e) => e.status === 'closed').length,
341
345
  bySurface: surfaces.map((surface) => ({
342
346
  surface,
343
347
  total: registry.filter((e) => e.surface === surface).length,
@@ -350,12 +354,12 @@ export function summarizeEntrances(registry: readonly EntranceDeclaration[] = EN
350
354
  export function renderLaunchEntrances(registry: readonly EntranceDeclaration[] = ENTRANCE_REGISTRY): string {
351
355
  const summary = summarizeEntrances(registry);
352
356
  const rows = listEntrancesWithModelExposure(registry)
353
- .map((e) => ` ${e.status === 'retired' ? '🪦' : '✅'} ${e.surface.padEnd(7)} ${e.id} (${e.stampability}) verification=actualMission:${e.verification.actualMission},legacyParity:${e.verification.legacyParity} imprintEvidence=${e.imprintEvidence} evidenceSource=${e.stampabilityEvidenceSource} evidenceSourceFilePath=${e.evidenceSourceFilePath ?? 'missing'} modelExposed=${e.modelExposed ?? 'missing'}`);
357
+ .map((e) => ` ${e.status === 'retired' ? '🪦' : e.status === 'closed' ? '닫힘' : '✅'} ${e.surface.padEnd(7)} ${e.id} (${e.stampability}) verification=actualMission:${e.verification.actualMission},legacyParity:${e.verification.legacyParity} imprintEvidence=${e.imprintEvidence} evidenceSource=${e.stampabilityEvidenceSource} evidenceSourceFilePath=${e.evidenceSourceFilePath ?? 'missing'} modelExposed=${e.modelExposed ?? 'missing'}`);
354
358
  const surfaces = summary.bySurface
355
359
  .map((s) => `${s.surface}=${s.total}${s.retired > 0 ? `(은퇴 ${s.retired})` : ''}`)
356
360
  .join(' · ');
357
361
  return [
358
- `launch entrances: ${summary.total} (live ${summary.live} · retired ${summary.retired})`,
362
+ `launch entrances: ${summary.total} (live ${summary.live} · retired ${summary.retired} · closed ${summary.closed})`,
359
363
  ` 표면별: ${surfaces}`,
360
364
  ...rows,
361
365
  ].join('\n');
@@ -0,0 +1,73 @@
1
+ /**
2
+ * OLD-DOOR-CLOSE — the three outside doors refuse a direct call.
3
+ *
4
+ * `elanous dev --ask`, `elanous self implement`, and `elanous self orchestrate`
5
+ * stay callable from inside the harness. The harness marks those calls with
6
+ * the same stamp ONEDOOR-1 uses for a door (`ELANOUS_HARNESS_ENTRANCE`) or with
7
+ * the explicit internal flag `--harness-internal`. A terminal or seat session
8
+ * carries neither, so it exits non-zero and is told where to go.
9
+ */
10
+ import type { LaunchDoor } from '../harness/launch-stamp.js';
11
+
12
+ export const OLD_DOORS = ['dev-ask', 'self-implement', 'self-orchestrate'] as const;
13
+ export type OldDoor = (typeof OLD_DOORS)[number];
14
+
15
+ /** Same env ONEDOOR-1 already stamps onto a harness launch. */
16
+ export const OLD_DOOR_STAMP_ENV = 'ELANOUS_HARNESS_ENTRANCE';
17
+ /** Same meaning as the env, for a caller that can pass an argument but not an env. */
18
+ export const OLD_DOOR_STAMP_FLAG = '--harness-internal';
19
+
20
+ const STAMP_BY_DOOR: Readonly<Record<OldDoor, readonly string[]>> = {
21
+ 'dev-ask': ['dev-ask', 'cli-dev-ask', 'cli-drive', 'harness-say', 'cli-harness-say', 'harness-ask', 'cli-harness-ask', 'queue-tick', 'agent-mission', 'daemon-tool'],
22
+ 'self-implement': ['self-implement', 'cli-self-implement', 'nl-self-implement', 'daemon-self-implement', 'daemon-tool', 'harness-say', 'cli-harness-say', 'harness-ask', 'cli-harness-ask', 'queue-tick', 'agent-mission'],
23
+ 'self-orchestrate': ['self-orchestrate', 'cli-self-orchestrate', 'nl-self-orchestrate', 'cli-harness-orchestrate', 'harness-say', 'cli-harness-say', 'harness-ask', 'cli-harness-ask', 'queue-tick', 'agent-mission', 'daemon-tool'],
24
+ };
25
+
26
+ export interface OldDoorRefusal {
27
+ readonly door: OldDoor;
28
+ readonly caller: 'outside';
29
+ readonly exitCode: 1;
30
+ readonly message: string;
31
+ }
32
+
33
+ export function oldDoorStampAccepted(door: OldDoor, stamp: string | undefined | null): boolean {
34
+ const value = stamp?.trim();
35
+ if (!value) return false;
36
+ return (STAMP_BY_DOOR[door] as readonly string[]).includes(value);
37
+ }
38
+
39
+ /** A harness-internal call carries the stamp in the environment or on the argv. Anything else is outside. */
40
+ export function isOldDoorInternalCall(
41
+ door: OldDoor,
42
+ env: NodeJS.ProcessEnv = process.env,
43
+ argv: readonly string[] = process.argv,
44
+ ): boolean {
45
+ if (argv.includes(OLD_DOOR_STAMP_FLAG)) return true;
46
+ return oldDoorStampAccepted(door, env[OLD_DOOR_STAMP_ENV]);
47
+ }
48
+
49
+ /** Env a harness parent sets so the child door stays open. */
50
+ export function oldDoorInternalEnv(door: LaunchDoor | OldDoor): Record<string, string> {
51
+ return { [OLD_DOOR_STAMP_ENV]: door };
52
+ }
53
+
54
+ const REPLACEMENT: Readonly<Record<OldDoor, string>> = {
55
+ 'dev-ask': 'elanous harness ask <goal.md>',
56
+ 'self-implement': 'elanous harness say "<sentence>"',
57
+ 'self-orchestrate': 'elanous harness say "<sentence>"',
58
+ };
59
+
60
+ const DOOR_LABEL: Readonly<Record<OldDoor, string>> = {
61
+ 'dev-ask': 'elanous dev --ask',
62
+ 'self-implement': 'elanous self implement',
63
+ 'self-orchestrate': 'elanous self orchestrate',
64
+ };
65
+
66
+ /** One line: the closed door, the harness door to use, and a command of the same meaning. */
67
+ export function oldDoorRefusalMessage(door: OldDoor): string {
68
+ return `${DOOR_LABEL[door]} 는 닫혔습니다 — harness say / harness ask 로. 예: ${REPLACEMENT[door]}`;
69
+ }
70
+
71
+ export function refuseOldDoor(door: OldDoor): OldDoorRefusal {
72
+ return { door, caller: 'outside', exitCode: 1, message: oldDoorRefusalMessage(door) };
73
+ }
@@ -28,6 +28,7 @@ import type { RunIdSource } from '../harness/harness-space.js';
28
28
  import { withFileLockSync } from '../storage/file-lock.js';
29
29
  import type { SelfDevJobResult, SelfDevGoal } from './orchestrate.js';
30
30
  import type { SupervisorStopReason } from './run-supervisor.js';
31
+ import type { SupervisorNext } from './supervisor-verdict-edges.js';
31
32
  import { loadMirroredRunRecord, mirrorRunRecord } from './run-record-mirror.js';
32
33
 
33
34
  export interface SelfDevRunParticipant {
@@ -92,6 +93,8 @@ export interface SelfDevRunState {
92
93
  parkedResolution?: SelfDevRunResolution;
93
94
  /** The closed supervisor termination reason, persisted independently from per-goal results. */
94
95
  supervisorStopReason?: SupervisorStopReason;
96
+ /** Named next step at supervisor termination; legacy checkpoints omit it. */
97
+ next?: SupervisorNext;
95
98
  /** ★ C(2026-07-21·HITL 정황 품질) — 이 run 을 구동하는 오케스트레이터 프로세스 pid. 살아있으면
96
99
  * non-terminal goal 은 **실행중(running)** 이지 막힌(interrupted) 게 아니다. 죽었으면(오케스트레이터
97
100
  * killed) 진짜 interrupted. isPidAlive 로 구분해 라이브 잡을 parked/repair-signals 에서 제외. */
@@ -311,13 +314,14 @@ export function recordSelfDevRunSupervisorStop(
311
314
  runId: string,
312
315
  supervisorStopReason: SupervisorStopReason,
313
316
  dir = selfDevRunsDir(),
317
+ next?: SupervisorNext,
314
318
  ): SupervisorStopPersistenceOutcome {
315
319
  try {
316
320
  if (!existsSync(dir)) return { outcome: 'missing-directory' };
317
321
  return withFileLockSync(join(dir, `${runId}.lock`), () => {
318
322
  const state = loadSelfDevRunFromPath(runPath(runId, dir));
319
323
  if (!state) return { outcome: 'missing-record' };
320
- writeSelfDevRun({ ...state, updatedAt: Date.now(), supervisorStopReason }, dir);
324
+ writeSelfDevRun({ ...state, updatedAt: Date.now(), supervisorStopReason, ...(next === undefined ? {} : { next }) }, dir);
321
325
  return { outcome: 'persisted' };
322
326
  });
323
327
  } catch {
@@ -6,6 +6,7 @@ import type { SelfDevJobResult } from './orchestrate.js';
6
6
  import { recordSelfDevRunSupervisorStop, selfDevRunsDir } from './run-store.js';
7
7
  import { debug } from '../debug/log.js';
8
8
  import { sweepFrozenMerges } from '../self-implement/frozen-merges.js';
9
+ import { routeSupervisorVerdict, type SupervisorNext } from './supervisor-verdict-edges.js';
9
10
 
10
11
  export interface SupervisorRound {
11
12
  round: number;
@@ -186,6 +187,7 @@ function formatGoalPlanRevisionObservation(results: readonly SelfDevJobResult[])
186
187
  export type SupervisorJobResult = SelfDevJobResult & {
187
188
  reviewReason?: string;
188
189
  salvage?: 'launched' | 'parked';
190
+ next?: SupervisorNext;
189
191
  };
190
192
 
191
193
  export function decideNextRun(input: {
@@ -592,9 +594,13 @@ export async function superviseRun(opts: SuperviseRunOptions): Promise<Superviso
592
594
  } catch { /* fail-open */ }
593
595
 
594
596
  if (decision.action === 'stop') {
597
+ const reason = decision.stopReason ?? 'unregistered-stop';
598
+ const next = routeSupervisorVerdict(reason, results).next;
599
+ try { debug.log('self-dev.supervisor', 'verdict-edge', { runId, reason, next }); } catch { /* observation must not interrupt supervision */ }
600
+ results = results.map((result) => ({ ...result, next }));
595
601
  if (runId && decision.stopReason) {
596
602
  const storage = resolveSupervisorStopStorage(opts.runStore);
597
- const persistence = recordSelfDevRunSupervisorStop(runId, decision.stopReason, storage.path);
603
+ const persistence = recordSelfDevRunSupervisorStop(runId, decision.stopReason, storage.path, next);
598
604
  const storageObservation = {
599
605
  storagePath: storage.path,
600
606
  storageSource: storage.source,
@@ -0,0 +1,46 @@
1
+ import type { SelfDevJobResult } from './orchestrate.js';
2
+ import type { SupervisorStopReason } from './run-supervisor.js';
3
+
4
+ export type SupervisorNext = 'harvest' | 'scope-decompose' | 'self-review' | 'human-gate' | 'proposal';
5
+ export type SupervisorVerdictEdge = { readonly meaning: string; readonly next: SupervisorNext };
6
+
7
+ // 목표 규칙: 사람 관문은 결제·공개·자격·비가역 결정에만. 지금 max-rounds·provider-exhausted·step-timeout 이 human-gate 인 것은
8
+ // 그 규칙의 예외로 «그대로» 둔 값이다(시험에 고정 — 바꾸면 보인다).
9
+ export const SUPERVISOR_VERDICT_EDGES = {
10
+ converged: { meaning: 'run converged', next: 'harvest' },
11
+ 'human-stopped': { meaning: 'human stopped the run', next: 'human-gate' },
12
+ 'parent-signals-red': { meaning: 'parent decision signals are red', next: 'human-gate' },
13
+ 'needs-human': { meaning: 'unresolved human or harvest decision', next: 'human-gate' },
14
+ 'no-actionable-work': { meaning: 'no actionable work remains', next: 'self-review' },
15
+ 'harvestable-awaiting-human': { meaning: 'implementation awaits harvest', next: 'harvest' },
16
+ 'handed-off-to-salvage': { meaning: 'salvage already owns the work', next: 'harvest' },
17
+ 'max-rounds': { meaning: 'round budget exhausted', next: 'human-gate' },
18
+ 'no-progress': { meaning: 'run stalled', next: 'scope-decompose' },
19
+ 'provider-exhausted': { meaning: 'provider quota exhausted', next: 'human-gate' },
20
+ 'step-timeout': { meaning: 'step budget exhausted', next: 'human-gate' },
21
+ 'decomposable-no-progress': { meaning: 'stalled with a decomposition proposal', next: 'scope-decompose' },
22
+ 'review-unobserved': { meaning: 'review was not observed', next: 'self-review' },
23
+ 'deliverable-unobserved': { meaning: 'deliverable was not observed', next: 'self-review' },
24
+ 'deliverable-merged': { meaning: 'deliverable is merged', next: 'harvest' },
25
+ } as const satisfies Record<SupervisorStopReason, SupervisorVerdictEdge>;
26
+
27
+ const HUMAN_GATE_MERGE_REASONS: ReadonlySet<string> = new Set(['decision-signal-red', 'signal-incomplete', 'parent-unlanded', 'human-gate']);
28
+
29
+ /** Unknown stop vocabularies are proposals, never silently treated as approved edges. */
30
+ export function routeSupervisorVerdict(
31
+ reason: string,
32
+ results: readonly Partial<Pick<SelfDevJobResult, 'status' | 'stage' | 'merged' | 'prNumber' | 'prUrl' | 'blockReason' | 'mergeReason'>>[] = [],
33
+ ): SupervisorVerdictEdge {
34
+ const edge = Object.hasOwn(SUPERVISOR_VERDICT_EDGES, reason)
35
+ ? SUPERVISOR_VERDICT_EDGES[reason as SupervisorStopReason]
36
+ : undefined;
37
+ if (!edge) return { meaning: 'unregistered supervisor stop reason', next: 'proposal' };
38
+ if (reason !== 'needs-human') return edge;
39
+ if (results.some((result) => result.blockReason === 'parent-unlanded')) return edge;
40
+ // A human-gate reason on ANY result in the run keeps the whole run at the human gate.
41
+ if (results.some((result) => HUMAN_GATE_MERGE_REASONS.has(String(result.mergeReason)))) return edge;
42
+ const openPrs = results.filter((result) => result.status === 'done' && result.stage === 'pr-opened'
43
+ && result.merged !== true && (result.prNumber !== undefined || Boolean(result.prUrl)));
44
+ if (openPrs.length === 0) return edge;
45
+ return { meaning: 'open PR ready for harvest', next: 'harvest' };
46
+ }