elanous 0.2.18 → 0.2.20

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 (350) 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-30dff22c9e1f3073.js → 1307-77fad80cf54477c6.js} +1 -1
  4. package/apps/pwa/out/_next/static/chunks/1612-f60c0c93ec773f81.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/2116-6429c188eb846ab8.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/224-e7be8f762fe74986.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/2489-ff55a35b202c0f94.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/2549-89c518a4c8ed4d00.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/3857-995ea9f5ef33d71e.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/4144-fad095b48a1862ee.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/5755-ea7ed88eb956f47b.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/5977-94ed6f78e9814b2b.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/7748-6e51bfe927019f5c.js +25 -0
  14. package/apps/pwa/out/_next/static/chunks/8401-35e94e9091f5eb8d.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/9247-c1753a059e7bac08.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/a9f2b54d-33c0689b70ed8cf3.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/app/approvals/{page-d92f7ee3994e84c7.js → page-c855c24de7db74fe.js} +1 -1
  18. package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-4733c967b56a4277.js → page-85b316bb4235980b.js} +1 -1
  19. package/apps/pwa/out/_next/static/chunks/app/board/{page-d22fd849cc22f104.js → page-9b2b26a33cb2cd42.js} +1 -1
  20. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-bb742f995c6769e7.js → page-c9955fde67b1bcb5.js} +1 -1
  21. package/apps/pwa/out/_next/static/chunks/app/bots/{page-02b073a2ac793b83.js → page-7a852956c74015a3.js} +1 -1
  22. package/apps/pwa/out/_next/static/chunks/app/ceo/page-c5a5c621be0e04ae.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/app/chat/{page-8a9b3594bc23289b.js → page-4ba4abe096b6c2a8.js} +1 -1
  24. package/apps/pwa/out/_next/static/chunks/app/consult/page-575d4a8b4f7becb2.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/app/control/{page-308111abadf35670.js → page-f5d56ff5f45ad164.js} +1 -1
  26. package/apps/pwa/out/_next/static/chunks/app/decisions/page-2260e9eef0357421.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/app/design-check/{page-5d97b2901e0d1ea6.js → page-7c058069c5e7fa63.js} +1 -1
  28. package/apps/pwa/out/_next/static/chunks/app/editor/page-dc5da0b8a2edb8a4.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/app/exec/{page-8c3d8e63ae2e1c98.js → page-466bf0ee16ad1432.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/app/field/{page-9b72a07d424701c6.js → page-a23bc71713bcc08f.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/app/inside/page-ed4a6f799f005d09.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/app/intake/{page-a572f58cd3ca5864.js → page-9cd565005b93aca6.js} +1 -1
  33. package/apps/pwa/out/_next/static/chunks/app/layout-55f0f40c6db95f15.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/app/live/{page-be5ea965a07fbc41.js → page-474cb5888ea0e8f3.js} +1 -1
  35. package/apps/pwa/out/_next/static/chunks/app/loops/page-35bfec2e631d3eae.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/app/market/{page-fa244bd146a00ae8.js → page-9ca21f9ad3208a61.js} +1 -1
  37. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-44d56d6391965a6a.js → page-f9ccb1b1adbea14d.js} +1 -1
  38. package/apps/pwa/out/_next/static/chunks/app/morning/{page-83a700d4c754d802.js → page-ec102c9b8b691e37.js} +1 -1
  39. package/apps/pwa/out/_next/static/chunks/app/{not-found-7b3170f374f77b9d.js → not-found-a9c2cfa8dda922a5.js} +1 -1
  40. package/apps/pwa/out/_next/static/chunks/app/observatory/page-6da35144878feb6d.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-f6055495e9ed7ea8.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-25204694fa9984a5.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-a60e035f151d84d3.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/app/outputs/page-189c587f36874538.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/app/page-c56634bde705a65d.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/app/reflection/{page-1467047104141297.js → page-db03ae3589a81c9e.js} +1 -1
  47. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-4a6463b97f0ad94f.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/app/sessions/{page-102310930791197a.js → page-75bffecc9aa1004e.js} +1 -1
  49. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a8d2fe0c669e0de4.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/settings/{page-6ed0b73bae8351d8.js → page-31d5b875760bb62d.js} +1 -1
  51. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-c26fa10aac6c21c4.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/setup/{page-43336055451f6e37.js → page-68793c6e10331d66.js} +1 -1
  53. package/apps/pwa/out/_next/static/chunks/app/share/{page-3cc47dca0922ab9a.js → page-46d5201d2207a511.js} +1 -1
  54. package/apps/pwa/out/_next/static/chunks/app/showroom/{page-a77ee355a2304ff0.js → page-d81c07a7b4f618ae.js} +1 -1
  55. package/apps/pwa/out/_next/static/chunks/app/tasks/{page-b17cd46cf0e9b8cf.js → page-be1c4e8697f22360.js} +1 -1
  56. package/apps/pwa/out/_next/static/chunks/app/term/page-cf0a4b4abead71ea.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/today/page-a4717e2c4bf7a28f.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/trace/{page-7bf54c9bb9274af8.js → page-0fd895fab9b62c1b.js} +1 -1
  59. package/apps/pwa/out/_next/static/chunks/app/vault/page-8b6c1454a8e8f306.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-c6e4364d9de8f717.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/workflows/page-0acc8e198f7b0dfb.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/workspace/{page-134da468538baf48.js → page-34882c84f84021c5.js} +1 -1
  63. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-0506a1e0e4bdb2f5.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/{webpack-c2782b330447ffb6.js → webpack-c260571513bf43c2.js} +1 -1
  65. package/apps/pwa/out/_next/static/css/{e99e3cff94ced78a.css → 599369d853c61df7.css} +1 -1
  66. package/apps/pwa/out/_next/static/css/c23d959d3ee15e57.css +1 -0
  67. package/apps/pwa/out/_next/static/css/d3427996da11e480.css +1 -0
  68. package/apps/pwa/out/approvals/index.html +1 -1
  69. package/apps/pwa/out/approvals/index.txt +14 -14
  70. package/apps/pwa/out/autopilot/index.html +1 -1
  71. package/apps/pwa/out/autopilot/index.txt +14 -14
  72. package/apps/pwa/out/board/index.html +1 -1
  73. package/apps/pwa/out/board/index.txt +14 -14
  74. package/apps/pwa/out/botlab/index.html +1 -1
  75. package/apps/pwa/out/botlab/index.txt +14 -14
  76. package/apps/pwa/out/bots/index.html +1 -1
  77. package/apps/pwa/out/bots/index.txt +14 -14
  78. package/apps/pwa/out/ceo/index.html +1 -1
  79. package/apps/pwa/out/ceo/index.txt +14 -14
  80. package/apps/pwa/out/chat/index.html +1 -1
  81. package/apps/pwa/out/chat/index.txt +14 -14
  82. package/apps/pwa/out/consult/index.html +1 -1
  83. package/apps/pwa/out/consult/index.txt +14 -14
  84. package/apps/pwa/out/control/index.html +1 -1
  85. package/apps/pwa/out/control/index.txt +14 -14
  86. package/apps/pwa/out/decisions/index.html +1 -1
  87. package/apps/pwa/out/decisions/index.txt +14 -14
  88. package/apps/pwa/out/design-check/index.html +1 -1
  89. package/apps/pwa/out/design-check/index.txt +14 -14
  90. package/apps/pwa/out/editor/index.html +1 -1
  91. package/apps/pwa/out/editor/index.txt +16 -15
  92. package/apps/pwa/out/exec/index.html +1 -1
  93. package/apps/pwa/out/exec/index.txt +14 -14
  94. package/apps/pwa/out/field/index.html +1 -1
  95. package/apps/pwa/out/field/index.txt +14 -14
  96. package/apps/pwa/out/healthz +1 -1
  97. package/apps/pwa/out/index.html +1 -1
  98. package/apps/pwa/out/index.txt +14 -14
  99. package/apps/pwa/out/inside/index.html +1 -1
  100. package/apps/pwa/out/inside/index.txt +16 -15
  101. package/apps/pwa/out/intake/index.html +1 -1
  102. package/apps/pwa/out/intake/index.txt +14 -14
  103. package/apps/pwa/out/live/index.html +1 -1
  104. package/apps/pwa/out/live/index.txt +14 -14
  105. package/apps/pwa/out/loops/index.html +1 -1
  106. package/apps/pwa/out/loops/index.txt +24 -23
  107. package/apps/pwa/out/market/index.html +1 -1
  108. package/apps/pwa/out/market/index.txt +14 -14
  109. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  110. package/apps/pwa/out/missions/placeholder/index.txt +14 -14
  111. package/apps/pwa/out/morning/index.html +1 -1
  112. package/apps/pwa/out/morning/index.txt +14 -14
  113. package/apps/pwa/out/observatory/index.html +1 -1
  114. package/apps/pwa/out/observatory/index.txt +14 -14
  115. package/apps/pwa/out/ops/checklist/index.html +1 -1
  116. package/apps/pwa/out/ops/checklist/index.txt +14 -14
  117. package/apps/pwa/out/ops/release/index.html +1 -1
  118. package/apps/pwa/out/ops/release/index.txt +14 -14
  119. package/apps/pwa/out/ops/seats/index.html +1 -1
  120. package/apps/pwa/out/ops/seats/index.txt +14 -14
  121. package/apps/pwa/out/outputs/index.html +1 -1
  122. package/apps/pwa/out/outputs/index.txt +14 -14
  123. package/apps/pwa/out/reflection/index.html +1 -1
  124. package/apps/pwa/out/reflection/index.txt +15 -15
  125. package/apps/pwa/out/scheduler/index.html +1 -1
  126. package/apps/pwa/out/scheduler/index.txt +14 -14
  127. package/apps/pwa/out/sessions/index.html +1 -1
  128. package/apps/pwa/out/sessions/index.txt +14 -14
  129. package/apps/pwa/out/settings/devices/index.html +1 -1
  130. package/apps/pwa/out/settings/devices/index.txt +14 -14
  131. package/apps/pwa/out/settings/index.html +1 -1
  132. package/apps/pwa/out/settings/index.txt +14 -14
  133. package/apps/pwa/out/setup/done/index.html +1 -1
  134. package/apps/pwa/out/setup/done/index.txt +14 -14
  135. package/apps/pwa/out/setup/index.html +1 -1
  136. package/apps/pwa/out/setup/index.txt +14 -14
  137. package/apps/pwa/out/share/index.html +1 -1
  138. package/apps/pwa/out/share/index.txt +14 -14
  139. package/apps/pwa/out/showroom/index.html +1 -1
  140. package/apps/pwa/out/showroom/index.txt +14 -14
  141. package/apps/pwa/out/tasks/index.html +1 -1
  142. package/apps/pwa/out/tasks/index.txt +14 -14
  143. package/apps/pwa/out/term/index.html +1 -1
  144. package/apps/pwa/out/term/index.txt +14 -14
  145. package/apps/pwa/out/today/index.html +1 -1
  146. package/apps/pwa/out/today/index.txt +14 -14
  147. package/apps/pwa/out/trace/index.html +1 -1
  148. package/apps/pwa/out/trace/index.txt +14 -14
  149. package/apps/pwa/out/vault/index.html +1 -1
  150. package/apps/pwa/out/vault/index.txt +14 -14
  151. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  152. package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
  153. package/apps/pwa/out/workflows/index.html +1 -1
  154. package/apps/pwa/out/workflows/index.txt +14 -14
  155. package/apps/pwa/out/workspace/index.html +1 -1
  156. package/apps/pwa/out/workspace/index.txt +14 -14
  157. package/apps/pwa/out/worktrees/index.html +1 -1
  158. package/apps/pwa/out/worktrees/index.txt +14 -14
  159. package/graphs/demo/editor-recipes.yaml +22 -0
  160. package/graphs/ops/guardian.yaml +26 -0
  161. package/graphs/ops/recipes.yaml +3 -0
  162. package/graphs/retro/daily.yaml +25 -0
  163. package/graphs/retro/recipes.yaml +24 -0
  164. package/package.json +1 -1
  165. package/scripts/ask-marker-check.ts +3 -1
  166. package/scripts/coord-channel-watch.sh +2 -2
  167. package/scripts/discord-seat-probe.ts +3 -1
  168. package/scripts/land-global-checks.ts +89 -0
  169. package/scripts/lib/pch-regress-map.ts +24 -0
  170. package/scripts/morning-report.ts +18 -4
  171. package/scripts/pch-regress.ts +88 -0
  172. package/scripts/release-loop/gate-diff.ts +18 -0
  173. package/scripts/release-loop/gate-node.ts +628 -65
  174. package/scripts/release-loop/gate-partial.ts +106 -0
  175. package/scripts/release-loop/gate-timing.ts +229 -0
  176. package/scripts/release-loop/preflight.ts +6 -4
  177. package/scripts/release-loop/publish-window.ts +141 -0
  178. package/scripts/release-loop/release-docs.ts +1 -4
  179. package/scripts/release-loop/release-universe.ts +38 -0
  180. package/scripts/release-loop/resume-release.ts +67 -5
  181. package/scripts/release-loop/shard-plan.ts +65 -0
  182. package/scripts/release-loop/unattended-release.ts +12 -2
  183. package/scripts/release-story/draft.ts +5 -1
  184. package/scripts/retro/shadow-compare.ts +71 -0
  185. package/scripts/rhythm/daily.ts +6 -1
  186. package/scripts/video/scene-table.ts +159 -0
  187. package/src/acp/core-turn-bridge.ts +39 -2
  188. package/src/agent-mission/driver.ts +34 -6
  189. package/src/autopilot/build/llm-conflict-merge.ts +132 -7
  190. package/src/autopilot/build/merge-intent.ts +135 -7
  191. package/src/away/presence.ts +40 -0
  192. package/src/away/release-watch.ts +272 -0
  193. package/src/chat/index.ts +2 -1
  194. package/src/chat/short-question.ts +3 -40
  195. package/src/cli/away-cli.ts +68 -0
  196. package/src/cli/config-drift.ts +513 -0
  197. package/src/cli/guardian-cli.ts +49 -0
  198. package/src/cli/intake-cli.ts +13 -7
  199. package/src/cli/log-event-names.ts +5 -0
  200. package/src/cli/pod-cli.ts +5 -1
  201. package/src/cli/pr-cli.ts +20 -0
  202. package/src/cli/pty-drive-cli.ts +14 -3
  203. package/src/cli/release-cli.ts +52 -12
  204. package/src/cli/retro-cli.ts +78 -0
  205. package/src/cli/setup-cli.ts +31 -1
  206. package/src/cli/tasks-cli.ts +193 -14
  207. package/src/context-bus/context-now-public.ts +1 -1
  208. package/src/context-bus/context-now-surfaces.ts +16 -6
  209. package/src/context-bus/context-now.ts +97 -3
  210. package/src/core-turn/short-question.ts +100 -0
  211. package/src/dashboard/dashboard-status-lines.ts +24 -4
  212. package/src/dashboard/index.ts +50 -5
  213. package/src/dashboard/input/slash-executor.ts +6 -2
  214. package/src/dashboard/slash-runtime/dashboard-handlers.ts +68 -40
  215. package/src/dashboard/slash-runtime/decide-slash.ts +60 -0
  216. package/src/dashboard/slash-runtime/help-from-registry.ts +62 -23
  217. package/src/dashboard/slash-runtime/help-groups.ts +44 -0
  218. package/src/dashboard/slash-runtime/model-choices.ts +125 -0
  219. package/src/domains/guardian-tick.ts +138 -0
  220. package/src/domains/outbound-alert.ts +44 -13
  221. package/src/domains/outbound-log-probe.ts +6 -1
  222. package/src/domains/schedule-registry.ts +23 -0
  223. package/src/domains/telegram-kind-route.ts +39 -0
  224. package/src/domains/watch-default-scheduler.ts +95 -0
  225. package/src/esc-abort-gate.ts +44 -0
  226. package/src/graph-runner/graph-run-api.ts +205 -0
  227. package/src/graph-runner/runner.ts +36 -6
  228. package/src/harness/harness-cli-command.ts +420 -51
  229. package/src/harness/harness-queue.ts +315 -25
  230. package/src/harness/harness-salvage-cli.ts +140 -0
  231. package/src/harness/harness-worktree-add.ts +68 -0
  232. package/src/harness/ledger-write-guard.ts +6 -16
  233. package/src/hq/fence-outcomes.ts +63 -0
  234. package/src/hq/fence-wrapper.ts +33 -0
  235. package/src/hq/hq.ts +17 -2
  236. package/src/index.ts +118 -6
  237. package/src/instance/test-write-guard-spy-first.fixture.ts +13 -0
  238. package/src/instance/test-write-guard.ts +200 -0
  239. package/src/intake-plane/digest.ts +39 -15
  240. package/src/intake-plane/harness-target.ts +4 -3
  241. package/src/intake-plane/intake-sources.ts +1 -0
  242. package/src/intake-plane/items.ts +1 -0
  243. package/src/loops/orchestrator/degradation.ts +57 -0
  244. package/src/loops/orchestrator/finish-rate.ts +33 -3
  245. package/src/loops/orchestrator/tick.ts +166 -45
  246. package/src/maturity/feature-maturity.ts +6 -4
  247. package/src/maturity/tui-slash-maturity.ts +1 -0
  248. package/src/mss/logging/log-store.ts +3 -0
  249. package/src/nexus/api/graph-kinds.ts +10 -4
  250. package/src/nexus/api/graphs-api.ts +178 -6
  251. package/src/nexus/api/http-server.ts +54 -8
  252. package/src/nexus/api/loop-edges.ts +292 -17
  253. package/src/nexus/api/ops-api.ts +112 -29
  254. package/src/nexus/api/ops-board-route.ts +48 -0
  255. package/src/nexus/index.ts +7 -0
  256. package/src/onboarding/entry-hints.ts +5 -0
  257. package/src/onboarding.ts +24 -6
  258. package/src/release-loop/checklist.ts +2 -0
  259. package/src/release-loop/feature-store.ts +68 -18
  260. package/src/release-loop/gate-shards.ts +158 -0
  261. package/src/release-loop/light-rc.ts +173 -0
  262. package/src/release-loop/placement.ts +51 -17
  263. package/src/release-loop/release-schedule.ts +6 -2
  264. package/src/release-loop/rubric.ts +57 -1
  265. package/src/retro/daily.ts +372 -0
  266. package/src/seat-loop/op-decide.ts +173 -0
  267. package/src/seat-loop/seat-loop.ts +60 -3
  268. package/src/seat-loop/seat-report.ts +58 -3
  269. package/src/self-dev/ask-launch-flow.ts +5 -4
  270. package/src/self-dev/ask-launch-io.ts +49 -1
  271. package/src/self-dev/ask-path-candidates.ts +40 -2
  272. package/src/self-dev/dev-cli.ts +36 -0
  273. package/src/self-dev/draft-sweep.ts +371 -16
  274. package/src/self-dev/draft-triage-rules.ts +19 -4
  275. package/src/self-dev/orchestrate.ts +1 -0
  276. package/src/self-dev/pr-preview.ts +247 -0
  277. package/src/self-dev/shard-run.ts +23 -7
  278. package/src/self-implement/blocked-draft-policy.ts +101 -0
  279. package/src/self-implement/execution-clarification.ts +11 -0
  280. package/src/self-implement/gate-cli.ts +29 -8
  281. package/src/self-implement/gate-env-retry.ts +83 -0
  282. package/src/self-implement/goal-author-clarification.ts +88 -13
  283. package/src/self-implement/goal-clarification-escalation.ts +2 -0
  284. package/src/self-implement/orchestrator.ts +220 -6
  285. package/src/self-implement/run-stop.ts +123 -0
  286. package/src/self-implement/run-ttl.ts +258 -0
  287. package/src/self-implement/salvage-branches.ts +254 -0
  288. package/src/self-implement/salvage-retention.ts +123 -0
  289. package/src/self-implement/seams.ts +4 -0
  290. package/src/self-implement/self-implement-cli.ts +2 -0
  291. package/src/self-implement/sibling-resync.ts +223 -0
  292. package/src/self-implement/stop-autoheal.ts +204 -0
  293. package/src/task-agent/actions.ts +63 -2
  294. package/src/task-agent/board.ts +132 -0
  295. package/src/task-agent/card-evidence.ts +292 -0
  296. package/src/task-agent/completion-evidence.ts +126 -0
  297. package/src/task-agent/judge.ts +25 -0
  298. package/src/task-agent/live-moves.ts +276 -0
  299. package/src/task-agent/mission.ts +33 -7
  300. package/src/task-agent/shadow.ts +192 -15
  301. package/src/task-agent/task-hand.ts +124 -17
  302. package/src/task-orchestrator/surfaces/pod-command-job.ts +82 -8
  303. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +4 -0
  304. package/src/task-orchestrator/surfaces/pod-install-slots.ts +89 -0
  305. package/src/task-orchestrator/surfaces/pod-lease.ts +233 -21
  306. package/src/task-orchestrator/surfaces/pod-pool.ts +155 -34
  307. package/src/task-orchestrator/surfaces/self-implement-pod.ts +21 -5
  308. package/src/telegram-away-command.ts +38 -0
  309. package/src/telegram-commands.ts +52 -0
  310. package/src/telegram-report-log-probe.ts +21 -0
  311. package/src/telegram-report.ts +27 -3
  312. package/src/testing/real-cli-spawn.ts +106 -0
  313. package/src/tui-client/dashboard-session.ts +1 -0
  314. package/src/user-config.ts +190 -5
  315. package/src/version/packed-revision.json +1 -1
  316. package/apps/pwa/out/_next/static/chunks/224-798346143007ff65.js +0 -1
  317. package/apps/pwa/out/_next/static/chunks/3857-4f936cdb30322aa6.js +0 -1
  318. package/apps/pwa/out/_next/static/chunks/4144-a0041c8dc3be57ac.js +0 -1
  319. package/apps/pwa/out/_next/static/chunks/4554-5761612531d0146b.js +0 -1
  320. package/apps/pwa/out/_next/static/chunks/5977-a11f37254196b659.js +0 -1
  321. package/apps/pwa/out/_next/static/chunks/5994-ae92c0de046ab9ba.js +0 -1
  322. package/apps/pwa/out/_next/static/chunks/8401-8615a660c84cdb03.js +0 -1
  323. package/apps/pwa/out/_next/static/chunks/8868-10a3a4440fb0ccd8.js +0 -25
  324. package/apps/pwa/out/_next/static/chunks/9247-269e4464cc4b491c.js +0 -1
  325. package/apps/pwa/out/_next/static/chunks/a9f2b54d-7865aba0f4d2ee49.js +0 -1
  326. package/apps/pwa/out/_next/static/chunks/app/ceo/page-60c3da708f9a0ccc.js +0 -1
  327. package/apps/pwa/out/_next/static/chunks/app/consult/page-a0eb02638b1e6f31.js +0 -1
  328. package/apps/pwa/out/_next/static/chunks/app/decisions/page-6eaf7bac37830c21.js +0 -1
  329. package/apps/pwa/out/_next/static/chunks/app/editor/page-c39554c321576982.js +0 -1
  330. package/apps/pwa/out/_next/static/chunks/app/inside/page-bd5d586429167fea.js +0 -1
  331. package/apps/pwa/out/_next/static/chunks/app/layout-59b7834d497c2255.js +0 -1
  332. package/apps/pwa/out/_next/static/chunks/app/loops/page-c130b63862437814.js +0 -1
  333. package/apps/pwa/out/_next/static/chunks/app/observatory/page-5f23d0c66a3b4793.js +0 -1
  334. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-a3c58f5c72a5f3f9.js +0 -1
  335. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-a2c4625596f8d69c.js +0 -1
  336. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-0e9099531d239f11.js +0 -1
  337. package/apps/pwa/out/_next/static/chunks/app/outputs/page-e8e1ecaea7cd71d2.js +0 -1
  338. package/apps/pwa/out/_next/static/chunks/app/page-a1b7ffed710a93e0.js +0 -1
  339. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-2da6f9b8a38c6978.js +0 -1
  340. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-f331e38fefc00393.js +0 -1
  341. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-44d5f767491692bf.js +0 -1
  342. package/apps/pwa/out/_next/static/chunks/app/term/page-61553851aa606146.js +0 -1
  343. package/apps/pwa/out/_next/static/chunks/app/today/page-a1f585e154e61c59.js +0 -1
  344. package/apps/pwa/out/_next/static/chunks/app/vault/page-5150ef17b5383a57.js +0 -1
  345. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b9d8ce70792c1672.js +0 -1
  346. package/apps/pwa/out/_next/static/chunks/app/workflows/page-a5a3de231f2cc7d5.js +0 -1
  347. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31ac4afa2dcd915c.js +0 -1
  348. package/apps/pwa/out/_next/static/css/7fa358fec7da7c47.css +0 -1
  349. /package/apps/pwa/out/_next/static/{P6YTVgzuZz5oTZhY4Din_ → Bam1YziK2iH9bN4K0utGk}/_buildManifest.js +0 -0
  350. /package/apps/pwa/out/_next/static/{P6YTVgzuZz5oTZhY4Din_ → Bam1YziK2iH9bN4K0utGk}/_ssgManifest.js +0 -0
@@ -71,7 +71,7 @@ export function registerPodCommands(program: Command, deps: PodCliDeps = {}): vo
71
71
  const current = spec ? null : kubectl(['config', 'current-context']);
72
72
  const context = spec ?? (current?.status === 0 ? current.stdout.trim() : '');
73
73
  const members = context ? parsePodPool(context) : [];
74
- const measure: PoolLeaseMeasure = members.length ? measurePoolLease(members, { kubectl, dns: (context) => (deps.dns ?? probePoolDns)(context, kubectl) }) : { members: [] };
74
+ const measure: PoolLeaseMeasure = members.length ? measurePoolLease(members, { kubectl, dns: (context) => (deps.dns ?? probePoolDns)(context, kubectl), measureUsage: true }) : { members: [] };
75
75
  let accounts: number | null = null;
76
76
  let accountReason: string | null = null;
77
77
  try { accounts = (deps.accounts ?? (() => listCodexAccountsInStore().length))(); }
@@ -96,6 +96,10 @@ export function registerPodCommands(program: Command, deps: PodCliDeps = {}): vo
96
96
  }
97
97
  io.log(`실측 점유: ${measure.members.map((m) => `${m.context} ${m.running === null || m.pending === null ? '못 쟀다' : m.running + m.pending}/${m.capacity}`).join(' · ') || '?'}`);
98
98
  io.log(`권장 지금 ${decision.recommended ?? '?'} 개 더 (limitedBy=${decision.limitedBy ?? 'unknown'})${decision.reason ? ` · ${decision.reason}` : ''}`);
99
+ if (decision.admission) {
100
+ const g = (v: number | null) => v === null ? '?' : `${(v / 1024 ** 3).toFixed(1)}Gi`;
101
+ io.log(`골당 입장 ${g(decision.admission.admitBytes)} (실사용 표본 ${decision.admission.samples} · p95 ${g(decision.admission.p95Bytes)}${decision.admission.fallback ? ` · 폴백: ${decision.admission.fallback}` : ''})`);
102
+ }
99
103
  io.log(`예약(저작 중) ${host.reserved} · 대기 Job ${waitingJobs ?? '?'} · 실행 ${decision.running ?? '?'}`);
100
104
  io.log(`임대 없는 실행 ${decision.unleasedRunning ?? '?'}`);
101
105
  io.log(`capacity: ${decision.capacitySlots ?? '?'} 칸 (상한 ${members.reduce((sum, m) => sum + m.capacity, 0)} − Running ${decision.running ?? '?'} − Pending ${decision.pending ?? '?'}; 권장 수에서 건강한 멤버의 임대 없는 실행 차감)`);
package/src/cli/pr-cli.ts CHANGED
@@ -22,6 +22,7 @@ import { exportLeakCheck, publicExportChangedFiles, type ExportLeakCheck } from
22
22
  import { exportImportCheck, type ExportImportCheck } from '../../scripts/ci-public-export-import-gate.js';
23
23
  import { checkCommands, extractElanousCommands, type Finding as DocsCliFinding } from '../../scripts/docs-cli-check.js';
24
24
  import { runTestInterferenceGate } from '../../scripts/ci-test-interference-gate.js';
25
+ import { landGlobalChecks, runLandGlobalChecksGate } from '../../scripts/land-global-checks.js';
25
26
  import { isGoalDocumentFileName } from '../self-implement/goal-document.js';
26
27
  import { queryFederatedUnfinishedRunLedgers, type FederatedUnfinishedRunLedgerEntry, type FederatedUnfinishedRunLedgerQuery } from '../self-implement/run-ledger.js';
27
28
  import { queryRunningRuns, type RunningRunsResult } from '../self-implement/running-runs.js';
@@ -120,6 +121,8 @@ export interface PrLandDeps {
120
121
  runDocsCliCheck?: (docFiles: readonly string[]) => DocsCliFinding[];
121
122
  /** 변경 시험 파일 간 간섭 검사 심(시험 주입용). */
122
123
  runTestInterferenceGate?: (out: { log: (message: string) => void; error: (message: string) => void }, changedFiles: readonly string[]) => Promise<number>;
124
+ /** 전역 검사 게이트 심(시험 주입용) — 바뀐 파일 꼴로 끌려온 전역 시험(scripts/land-global-checks.ts)을 돌린다. */
125
+ runLandGlobalChecks?: (out: { log: (message: string) => void; error: (message: string) => void }, changedFiles: readonly string[]) => boolean;
123
126
  /** 안드로이드 단위 시험 게이트 심(시험 주입용). */
124
127
  runAndroidGate?: (out: { log: (message: string) => void; error: (message: string) => void }, changedFiles: readonly string[]) => boolean;
125
128
  /** iOS 시험 게이트 심(시험 주입용). */
@@ -1471,6 +1474,23 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
1471
1474
  out,
1472
1475
  );
1473
1476
  record('test-interference-gate', true, { measured: testInterference.measured });
1477
+ // LAND-GLOBAL-CHECKS — 바뀐 파일의 경로 꼴이 부르는 «전역 검사»를 끌어와 돌린다(표 = scripts/land-global-checks.ts).
1478
+ // 시험이 실패하면 막는다 · 러너가 못 돌았으면 「검사 못 함」으로 말하고 통과시킨다(gateVerdict).
1479
+ const landGlobal = gateVerdict(
1480
+ 'land-global-checks',
1481
+ deps.runLandGlobalChecks
1482
+ ? (o) => deps.runLandGlobalChecks!(o, changedPaths)
1483
+ : (o) => runLandGlobalChecksGate({ changedFiles: changedPaths, cwd, log: o.log, error: o.error }),
1484
+ out,
1485
+ );
1486
+ record('land-global-checks', landGlobal.ok, { measured: landGlobal.measured });
1487
+ if (!landGlobal.ok) {
1488
+ out.error('✗ land-global-checks: scripts/land-global-checks.ts blocked pr land — 바뀐 파일이 끌어온 전역 시험이 실패했다.');
1489
+ return 1;
1490
+ }
1491
+ if (landGlobal.measured && landGlobalChecks(changedPaths).length > 0) {
1492
+ out.log(`✓ land-global-checks: scripts/land-global-checks.ts PASS — 끌려온 전역 시험 ${landGlobalChecks(changedPaths).join(' · ')} 실패 0.`);
1493
+ }
1474
1494
 
1475
1495
  const androidGate = gateVerdict(
1476
1496
  'android-gate',
@@ -51,6 +51,18 @@ export interface TuiWorkdirDecision {
51
51
  readonly message?: string;
52
52
  }
53
53
 
54
+ /** 「명시 격리」 판정 — `--test`(명시 플래그 층) 이거나 `--isolated-root` 를 줬을 때만 참.
55
+ *
56
+ * ⭐ 이 식은 «한 벌»이다 — `runPtyDriveInner`(거부)와 `src/index.ts` dev 의 자동 워크트리
57
+ * 판정(`decideAutoWorktree`)이 같이 쓴다. 둘이 갈리면 「자동으로 만들었어야 하는데 거부」나
58
+ * 「거부했어야 하는데 자동」이 조용히 생긴다. 3층(트리 파생)은 «명시»가 아니라서 빠진다. */
59
+ export function isExplicitlyIsolated(
60
+ resolution: { readonly kind: string; readonly layer: string },
61
+ isolatedRoot?: string,
62
+ ): boolean {
63
+ return (resolution.kind === 'test' && resolution.layer === 'explicit-flag') || Boolean(isolatedRoot);
64
+ }
65
+
54
66
  /** Reject the unsafe combination before a TUI child can inherit the caller's tree. */
55
67
  export function decideTuiWorkdir(isolated: boolean, cwd: string | undefined): TuiWorkdirDecision {
56
68
  const workdirProvided = Boolean(cwd?.trim());
@@ -62,7 +74,7 @@ export function decideTuiWorkdir(isolated: boolean, cwd: string | undefined): Tu
62
74
  // ⛔⭐ 거부 문면은 **무엇을 주면 되는지**까지 말한다(교차 세션 제안 · 2026-08-02).
63
75
  // 상대가 헤맨 이유가 *"어디에 config 를 쓰나"* 였다 — 실효 자리를 확인하는 한 줄을 함께 준다.
64
76
  ...(rejected
65
- ? { message: '격리 우주에서는 작업 디렉토리를 명시해야 한다 — `--cwd <worktree-path>` 를 주십시오. '
77
+ ? { message: '격리 우주에서는 작업 디렉토리를 명시해야 한다 — `--worktree` 를 주거나 `--cwd <worktree-path>` 를 주십시오. '
66
78
  + '(안 주면 명령을 친 트리가 자식의 쓰기 허용 구역이 됩니다.) '
67
79
  + '지금 어느 우주인지는 `elanous where` 로 확인할 수 있습니다.' }
68
80
  : {}),
@@ -499,8 +511,7 @@ async function runPtyDriveInner(opts: PtyDriveOpts): Promise<{ exitCode: number
499
511
  const { effectiveInstanceRoot } = await import('../instance/resolve.js');
500
512
  const scopeRoot = effectiveInstanceRoot();
501
513
  const resolution = resolveCurrentInstance();
502
- const explicitlyIsolated = (resolution.kind === 'test' && resolution.layer === 'explicit-flag')
503
- || Boolean(opts.isolatedRoot);
514
+ const explicitlyIsolated = isExplicitlyIsolated(resolution, opts.isolatedRoot);
504
515
  const workdirDecision = decideTuiWorkdir(explicitlyIsolated, opts.cwd);
505
516
  debug.log('pty.drive', 'tui-workdir-decision', workdirDecision);
506
517
  if (workdirDecision.rejected) throw new Error(workdirDecision.message);
@@ -45,8 +45,10 @@ import type { PrereleaseKind } from '../../scripts/release-loop/release-version.
45
45
  import { releaseReadiness } from '../../scripts/release-loop/release-readiness.js';
46
46
  import { runReleaseIfReady } from './release-run-if-ready.js';
47
47
  import { formatPreflight, releasePreflight } from '../../scripts/release-loop/preflight.js';
48
+ import { defaultReleaseGraphRunsRoot, formatPublishProposal, measurePublishWindow, proposePublishAt, publishWindowWarning } from '../../scripts/release-loop/publish-window.js';
48
49
  import { autoStartScheduledRelease, type AutoStartDeferral } from '../release-loop/auto-start.js';
49
50
  import { landingFreezeMessage, LandingFrozenError, readLandingFreeze } from '../release-loop/landing-freeze.js';
51
+ import { formatLightRc, runLightRc, type LightRcDeps } from '../release-loop/light-rc.js';
50
52
 
51
53
  export const DEFAULT_PUBLIC_REPO = 'ElanvitalAI/elanous';
52
54
  const SEMVER = /^\d+\.\d+\.\d+(?:-(?:rc|alpha|beta)\.\d+)?$/;
@@ -594,7 +596,7 @@ function compareReleaseVersions(a: string, b: string): number {
594
596
  return 0;
595
597
  }
596
598
 
597
- export function registerReleaseCommands(program: Command, releaseRunDeps: UnattendedReleaseDeps = {}, landedDeps: LandedButYellowDeps = {}, hqDeps: HqDeps = {}): void {
599
+ export function registerReleaseCommands(program: Command, releaseRunDeps: UnattendedReleaseDeps = {}, landedDeps: LandedButYellowDeps = {}, hqDeps: HqDeps = {}, lightRcDeps: LightRcDeps = {}): void {
598
600
  const release = program.command('release').description('공개 배포 한 판 — prepare(로컬) → publish(--yes) → verify (docs/manual/MANUAL-versioning-and-release-2026-09-25.md)');
599
601
  const schedule = release.command('schedule').description('판별 컷·착지 마감 조회·갱신');
600
602
  const scheduleLedgerRoot = () => releaseRunDeps.ledgerRoot ?? releaseLedgerRoot();
@@ -608,6 +610,8 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
608
610
  }
609
611
  return hqCliWriteAllowed(command, override, ledgerFenceDeps());
610
612
  };
613
+ // PUBLISH-TIME-FROM-GATE: the measured cut→publish window comes from the same ledger as the schedule (prod by default).
614
+ const publishMeasure = () => measurePublishWindow({ root: defaultReleaseGraphRunsRoot(scheduleLedgerRoot()) });
611
615
  schedule.command('list').option('--json', '결과 JSON').action((o: { json?: boolean }) => {
612
616
  const rows = listSchedules(scheduleLedgerRoot());
613
617
  if (o.json) console.log(JSON.stringify(rows));
@@ -617,15 +621,27 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
617
621
  .action((o: { version: string; json?: boolean }) => {
618
622
  const row = getSchedule(o.version, scheduleLedgerRoot());
619
623
  if (!row) throw new CliUserError(`없는 판 일정: ${o.version}`, '판 일정 등록: elanous release schedule set --version <v> --cut-at <iso> --land-by <iso>');
620
- if (o.json) console.log(JSON.stringify(row)); else console.log(formatSchedule(row));
624
+ const measure = publishMeasure();
625
+ const proposedPublishAt = proposePublishAt(row.cutAt, measure);
626
+ if (o.json) console.log(JSON.stringify({ ...row, proposedPublishAt, measuredPublishMedianSeconds: measure.medianSeconds, measuredPublishSamples: measure.samples.map(({ version, seconds }) => ({ version, seconds })) }));
627
+ else { console.log(formatSchedule(row)); console.log(formatPublishProposal(row.cutAt, measure)); }
621
628
  });
622
629
  schedule.command('set').requiredOption('--version <v>', '갱신할 판')
623
630
  .option('--cut-at <iso>', '오프셋 포함 컷 시각').option('--land-by <iso>', '오프셋 포함 착지 마감')
624
- .option('--freeze-from <iso>', '동결 시작').option('--freeze-until <iso>', '동결 종료').option('--json', '결과 JSON').option('--hq-override')
625
- .action((o: { version: string; cutAt?: string; landBy?: string; freezeFrom?: string; freezeUntil?: string; json?: boolean; hqOverride?: boolean }) => {
626
- if (o.cutAt === undefined && o.landBy === undefined && o.freezeFrom === undefined && o.freezeUntil === undefined) throw new CliUserError('갱신할 시각을 지정하라', '--cut-at <iso> 또는 --land-by <iso>');
631
+ .option('--freeze-from <iso>', '동결 시작').option('--freeze-until <iso>', '동결 종료')
632
+ .option('--publish-at <iso>', '오프셋 포함 발행 시각(실측 컷→발행 중앙값보다 짧으면 경고 · 제안은 schedule show)')
633
+ .option('--json', '결과 JSON').option('--hq-override')
634
+ .action((o: { version: string; cutAt?: string; landBy?: string; freezeFrom?: string; freezeUntil?: string; publishAt?: string; json?: boolean; hqOverride?: boolean }) => {
635
+ if (o.cutAt === undefined && o.landBy === undefined && o.freezeFrom === undefined && o.freezeUntil === undefined && o.publishAt === undefined) throw new CliUserError('갱신할 시각을 지정하라', '--cut-at <iso> 또는 --land-by <iso> 또는 --publish-at <iso>');
627
636
  if (!mayWriteLedger('release schedule set', Boolean(o.hqOverride), scheduleLedgerRoot())) return;
628
- const row = setSchedule(o.version, { cutAt: o.cutAt, landBy: o.landBy, freezeFrom: o.freezeFrom, freezeUntil: o.freezeUntil }, process.env.ELANOUS_TRACK || 'cli', scheduleLedgerRoot());
637
+ const row = setSchedule(o.version, { cutAt: o.cutAt, landBy: o.landBy, freezeFrom: o.freezeFrom, freezeUntil: o.freezeUntil, publishAt: o.publishAt }, process.env.ELANOUS_TRACK || 'cli', scheduleLedgerRoot());
638
+ // A stored publish time shorter than the measured median warns on stderr (one line) — --json stdout stays parseable.
639
+ if (row.publishAt && (o.publishAt !== undefined || o.cutAt !== undefined)) {
640
+ const measure = publishMeasure();
641
+ const warning = publishWindowWarning(row.cutAt, row.publishAt, measure);
642
+ debug.log('release.schedule', 'publish-window', { version: row.version, cutAt: row.cutAt, publishAt: row.publishAt, medianSeconds: measure.medianSeconds, samples: measure.samples.length, warned: warning !== null });
643
+ if (warning) console.error(warning);
644
+ }
629
645
  if (o.json) console.log(JSON.stringify(row)); else console.log(formatSchedule(row));
630
646
  });
631
647
  release.command('place <id>').description('칸의 우선순위·마감·용량으로 판 배치')
@@ -793,17 +809,17 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
793
809
  withContext(checklist.command('set <id>').description('칸 상태·근거·담당·처분 갱신'))
794
810
  .option('--status <status>', 'green|yellow|red|done').option('--evidence <evidence>', '근거').option('--owner <owner>', '담당')
795
811
  .option('--disposition <disposition>', 'move|known-issue|block').option('--kind <kind>', 'screen = 다섯 화면 짝 칸 · 근거에 짝: PWA … · 데스크톱 … · 폴드 … · 아이폰 … · 아이패드 … 한 줄')
796
- .option('--priority <priority>', 'P0|P1|P2').option('--deadline-version <v>', '마감 판').option('--predecessor <id...>', '선행 칸 id')
812
+ .option('--priority <priority>', 'P0|P1|P2').option('--deadline-version <v>', '마감 판').option('--predecessor <id...>', '선행 칸 id').option('--no-predecessor', '선행 칸 비우기(진짜 의존이 아니면 · CHECKLIST-PRED-CLEAR)')
797
813
  .option('--ceo-minutes <minutes>', '대표 손 분량(분)').option('--ceo-date <date>', '대표 손 날짜 YYYY-MM-DD')
798
814
  .option('--accelerator', '가속 등급 부여').option('--no-accelerator', '가속 등급 해제(기본은 없음)')
799
815
  .option('--ref <path...>', '근거 문서 더하기 — 저장소 상대 경로[#절] (반복 가능 · 중복 제거)')
800
- .action((id: string, opts: { ref?: string[]; status?: string; evidence?: string; owner?: string; disposition?: string; kind?: string; priority?: string; deadlineVersion?: string; predecessor?: string[]; ceoMinutes?: string; ceoDate?: string; accelerator?: boolean }, cmd: Command) => {
816
+ .action((id: string, opts: { ref?: string[]; status?: string; evidence?: string; owner?: string; disposition?: string; kind?: string; priority?: string; deadlineVersion?: string; predecessor?: string[] | false; ceoMinutes?: string; ceoDate?: string; accelerator?: boolean }, cmd: Command) => {
801
817
  const { version, json } = context(cmd);
802
818
  if (opts.status !== undefined && !['green', 'yellow', 'red', 'done'].includes(opts.status)) throw new CliUserError(`잘못된 상태: ${opts.status}`, 'green|yellow|red|done');
803
819
  if (opts.disposition !== undefined && !['move', 'known-issue', 'block'].includes(opts.disposition)) throw new CliUserError(`잘못된 처분: ${opts.disposition}`, 'move|known-issue|block');
804
820
  if (opts.status === undefined && opts.evidence === undefined && opts.owner === undefined && opts.disposition === undefined && opts.kind === undefined && opts.priority === undefined && opts.deadlineVersion === undefined && opts.predecessor === undefined && opts.ceoMinutes === undefined && opts.ceoDate === undefined && opts.accelerator === undefined && opts.ref === undefined) throw new CliUserError('갱신할 칸을 지정하라', '--ref · --status · --evidence · --owner · --disposition · --kind · --priority · --deadline-version · --predecessor · --ceo-minutes · --ceo-date · --accelerator · --no-accelerator 중 하나');
805
821
  if (!mayWrite(cmd)) return;
806
- const data = setItem(version, id, { ...(opts.ref !== undefined ? { refs: opts.ref } : {}), ...(opts.status !== undefined ? { status: opts.status as ChecklistStatus } : {}), ...(opts.evidence !== undefined ? { evidence: opts.evidence } : {}), ...(opts.owner !== undefined ? { owner: opts.owner } : {}), ...(opts.disposition !== undefined ? { disposition: opts.disposition as ChecklistDisposition } : {}), ...(opts.kind !== undefined ? { kind: opts.kind as ChecklistKind } : {}), ...(opts.priority !== undefined ? { priority: opts.priority as PlacementPriority } : {}), ...(opts.deadlineVersion !== undefined ? { deadlineVersion: opts.deadlineVersion } : {}), ...(opts.predecessor !== undefined ? { predecessors: opts.predecessor } : {}), ...(opts.ceoMinutes !== undefined ? { ceoMinutes: ceoMinutes(opts.ceoMinutes) } : {}), ...(opts.ceoDate !== undefined ? { ceoDate: opts.ceoDate } : {}), ...(opts.accelerator !== undefined ? { accelerator: opts.accelerator ? true : null } : {}) }, process.env.ELANOUS_TRACK || 'cli');
822
+ const data = setItem(version, id, { ...(opts.ref !== undefined ? { refs: opts.ref } : {}), ...(opts.status !== undefined ? { status: opts.status as ChecklistStatus } : {}), ...(opts.evidence !== undefined ? { evidence: opts.evidence } : {}), ...(opts.owner !== undefined ? { owner: opts.owner } : {}), ...(opts.disposition !== undefined ? { disposition: opts.disposition as ChecklistDisposition } : {}), ...(opts.kind !== undefined ? { kind: opts.kind as ChecklistKind } : {}), ...(opts.priority !== undefined ? { priority: opts.priority as PlacementPriority } : {}), ...(opts.deadlineVersion !== undefined ? { deadlineVersion: opts.deadlineVersion } : {}), ...(opts.predecessor !== undefined ? { predecessors: opts.predecessor === false ? [] : opts.predecessor } : {}), ...(opts.ceoMinutes !== undefined ? { ceoMinutes: ceoMinutes(opts.ceoMinutes) } : {}), ...(opts.ceoDate !== undefined ? { ceoDate: opts.ceoDate } : {}), ...(opts.accelerator !== undefined ? { accelerator: opts.accelerator ? true : null } : {}) }, process.env.ELANOUS_TRACK || 'cli');
807
823
  const item = data.items.find((entry) => entry.id === id);
808
824
  const why = item?.kind === 'screen' && item.status === 'green' ? parityGap(item.evidence) : null;
809
825
  if (why) {
@@ -1054,16 +1070,40 @@ export function registerReleaseCommands(program: Command, releaseRunDeps: Unatte
1054
1070
  process.exitCode = 1;
1055
1071
  }
1056
1072
  });
1073
+ // LIGHT-RC (0.2.19) — read-only for the ledger · never touches the landing freeze or launches Pods.
1074
+ release.command('light-rc')
1075
+ .description('가벼운 RC 점검 — 직전 판 컷(또는 --base) 이후 변경 범위 self gate ⊕ 상시 목록(release/light-rc-always.txt)을 원격 게이트 호스트에서 · 새 회귀(introduced>0)면 exit 1')
1076
+ .requiredOption('--version <v>', '점검할 판(x.y.z)')
1077
+ .option('--base <ref>', '비교 기준(기본: git merge-base origin/main origin/release/<원장 직전 판>)')
1078
+ .option('--remote <host>', '게이트 호스트(기본 config gateRemote.host)')
1079
+ .option('--local', '원격 대신 이 기계에서 돌린다(원격 자식이 이것으로 다시 불린다)')
1080
+ .option('--shards <n>', '시험 분할 수', '4')
1081
+ .option('--json', '결과 한 줄 JSON(stdout) · 사람 줄은 stderr')
1082
+ .action(async (o: { version: string; base?: string; remote?: string; local?: boolean; shards: string; json?: boolean }) => {
1083
+ // The root strips `--local` from argv (remote-bookmark attach) before Commander sees it — same read as `self gate`.
1084
+ const local = !!o.local || process.argv.slice(2).includes('--local');
1085
+ await jsonAction(o.json, async (log) => {
1086
+ if (o.remote !== undefined && local) throw new Error('release light-rc: --remote and --local cannot be combined');
1087
+ const shards = Number(o.shards);
1088
+ const result = await runLightRc(o.version, { base: o.base, remote: o.remote, local, shards, ledgerRoot: releaseRunDeps.ledgerRoot, log: (l) => console.error(l) }, lightRcDeps);
1089
+ for (const line of formatLightRc(result)) log(line);
1090
+ return result;
1091
+ }, (r) => r.ok);
1092
+ });
1057
1093
  release.command('resume')
1058
1094
  .description('실패한 릴리스 런을 이어 달린다 — cut-branch --append 로 고친 release/<v> 새 끝을 version-release 기록에 반영(빨리감기만·상태 백업) 뒤 --from 노드부터')
1059
1095
  .requiredOption('--run <runId>', '실패한 release-loop 런 ID')
1060
1096
  .requiredOption('--from <node>', '다시 시작할 노드(예: gate)')
1097
+ .option('--partial', 'GATE-PARTIAL — --from gate 에서 앞 게이트 뒤 바뀐 시험 파일 ⊕ 앞 실패 파일만 다시 돈다(시험·문서만 바뀐 경우 · 아니면 전체 게이트로)')
1061
1098
  .option('--json', '결과 한 줄 JSON(stdout)')
1062
- .action(async (o: { run: string; from: string; json?: boolean }) => {
1099
+ .action(async (o: { run: string; from: string; partial?: boolean; json?: boolean }) => {
1063
1100
  await jsonAction(o.json, async (log) => {
1064
- const result = await resumeReleaseRun({ runId: o.run, from: o.from });
1101
+ const result = await resumeReleaseRun({ runId: o.run, from: o.from, partial: o.partial });
1102
+ if (result.partial.requested) log(result.partial.applied
1103
+ ? `◐ 부분 재검: ${result.partial.files}파일 · 나머지는 앞 게이트(${result.partial.priorCommit.slice(0, 12)}) 결과를 잇는다`
1104
+ : `● 전체 게이트로: ${result.partial.reason}`);
1065
1105
  log(`${result.tip.changed ? `↻ ${result.tip.branch} ${result.tip.from.slice(0, 12)} → ${result.tip.to.slice(0, 12)} (백업 ${result.tip.backup})` : `· ${result.tip.branch} 끝 그대로 ${result.tip.to.slice(0, 12)}`} · ${o.from} 부터 → ${result.state.status}`);
1066
- return { tip: result.tip, status: result.state.status, runId: result.state.runId };
1106
+ return { tip: result.tip, partial: result.partial, status: result.state.status, runId: result.state.runId };
1067
1107
  }, (r) => r.status === 'done' || r.status === 'awaiting-approval');
1068
1108
  });
1069
1109
  release.command('auto-start')
@@ -0,0 +1,78 @@
1
+ import { existsSync } from 'node:fs';
2
+ import type { Command } from 'commander';
3
+ import { LogStore, logsDbPath } from '../mss/logging/log-store.js';
4
+
5
+ type RetroFact = { store: string; at: string; category: string; event: string; kind: 'draft-birth' | 'draft-death' | 'stop-reason' | 'ta-outcome'; data: unknown };
6
+
7
+ const FACT_EVENTS = new Map<string, RetroFact['kind']>([
8
+ ['rework-blocked-draft-pr', 'draft-birth'],
9
+ ['lineage-supersede', 'draft-death'],
10
+ ['resolved', 'stop-reason'],
11
+ ['decision', 'stop-reason'],
12
+ ['action', 'ta-outcome'],
13
+ ['task-agent.action', 'ta-outcome'],
14
+ ]);
15
+
16
+ export function parseRetroSince(value: string, now = Date.now()): number {
17
+ const match = /^([1-9]\d*)([smhd])$/.exec(value);
18
+ if (!match) throw new Error('--since requires a positive duration (e.g. 24h)');
19
+ const duration = Number(match[1]) * ({ s: 1000, m: 60000, h: 3600000, d: 86400000 }[match[2]!] ?? 0);
20
+ if (!Number.isSafeInteger(duration) || !Number.isSafeInteger(now - duration)) throw new Error('--since duration is out of range');
21
+ return now - duration;
22
+ }
23
+
24
+ export function collectRetroFacts(sinceMs: number, targets: readonly { name: string; dbPath: string }[] = [{ name: 'current', dbPath: logsDbPath() }]): { facts: RetroFact[]; unavailable: string[] } {
25
+ const facts: RetroFact[] = [];
26
+ const unavailable: string[] = [];
27
+ const untilMs = Date.now();
28
+ for (const target of targets) {
29
+ if (!existsSync(target.dbPath)) { unavailable.push(target.name); continue; }
30
+ try {
31
+ const store = LogStore.openReadOnly(target.dbPath);
32
+ try {
33
+ let beforeId: number | undefined;
34
+ for (;;) {
35
+ const rows = store.query({ sinceMs, beforeId, limit: 1000 });
36
+ for (const row of rows) {
37
+ if (Date.parse(row.ts) > untilMs) continue;
38
+ const kind = FACT_EVENTS.get(row.event);
39
+ if (!kind || (row.event === 'resolved' && row.category !== 'self-dev.supervisor') || ((row.event === 'task-agent.action' || row.event === 'action') && row.category !== 'task-agent')) continue;
40
+ let data: unknown;
41
+ try { data = row.data === null ? null : JSON.parse(row.data); } catch { data = row.data; }
42
+ const record = data && typeof data === 'object' && !Array.isArray(data) ? data as Record<string, unknown> : {};
43
+ if (row.event === 'rework-blocked-draft-pr' && (row.category !== 'self-implement' || !Number.isInteger(record.number) || record.skipped || record.error)) continue;
44
+ if (row.event === 'lineage-supersede' && (row.category !== 'self-implement' || !Array.isArray(record.closed) || record.closed.length === 0)) continue;
45
+ if (row.event === 'resolved' && typeof record.stopReason !== 'string') continue;
46
+ if (row.event === 'decision' && (row.category !== 'self-dev.supervisor' || record.action !== 'stop' || typeof record.stopReason !== 'string')) continue;
47
+ if ((row.event === 'task-agent.action' || row.event === 'action') && (row.category !== 'task-agent' || typeof record.result !== 'string' || !record.result.trim())) continue;
48
+ facts.push({ store: target.name, at: row.ts, category: row.category, event: row.event, kind, data });
49
+ }
50
+ if (rows.length < 1000) break;
51
+ beforeId = rows.at(-1)!.id;
52
+ }
53
+ } finally { store.close(); }
54
+ } catch { unavailable.push(target.name); }
55
+ }
56
+ facts.sort((a, b) => b.at.localeCompare(a.at));
57
+ return { facts, unavailable };
58
+ }
59
+
60
+ export function registerRetroCommands(program: Command): void {
61
+ program.command('retro').description('Read-only retrospective facts from existing logs')
62
+ .command('facts').description('Draft births/deaths, stop reasons and task-agent outcomes')
63
+ .option('--since <duration>', 'Lookback duration', '24h')
64
+ .option('--json', 'JSON output')
65
+ .action((opts: { since: string; json?: boolean }) => {
66
+ try {
67
+ const sinceMs = parseRetroSince(opts.since);
68
+ const result = { since: new Date(sinceMs).toISOString(), ...collectRetroFacts(sinceMs) };
69
+ console.log(opts.json ? JSON.stringify(result) : [
70
+ `retro facts since ${result.since} · ${result.facts.length} facts · unavailable: ${result.unavailable.join(', ') || 'none'}`,
71
+ ...result.facts.map((fact) => `${fact.at} ${fact.kind} ${fact.category}.${fact.event} ${JSON.stringify(fact.data)}`),
72
+ ].join('\n'));
73
+ } catch (error) {
74
+ console.error(`retro facts: ${error instanceof Error ? error.message : String(error)}`);
75
+ process.exitCode = 2;
76
+ }
77
+ });
78
+ }
@@ -1,13 +1,16 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { homedir } from 'node:os';
3
3
  import { join } from 'node:path';
4
+ import { createHash } from 'node:crypto';
5
+ import { addSource, listSources } from '../intake-plane/intake-sources.js';
6
+ import { effectiveInstanceRoot } from '../instance/resolve.js';
4
7
  import { Command } from 'commander';
5
8
  import { formatDoctorReport, registerDoctorCommand, runDoctor, type DoctorOptions, type DoctorReport } from './doctor-cli.js';
6
9
  import { applyClaudePluginSetup, planClaudePluginSetup } from './claude-plugin-setup.js';
7
10
  import { recommendedSetup } from './setup-recommend.js';
8
11
  import { detectSkillCliAuth, type CliAuthStatus } from '../onboarding/cli-auth-status.js';
9
12
  import { debug } from '../debug/log.js';
10
- import { getUserConfig, saveUserConfig, type UserConfig } from '../user-config.js';
13
+ import { getUserConfig, saveUserConfig, userConfigPath, type UserConfig } from '../user-config.js';
11
14
 
12
15
  const ELANOUS_LOGIN_COMMAND = 'elanous login openai-codex';
13
16
  const PROVIDER_COMMAND = 'elanous config set llm.provider openai-codex';
@@ -29,6 +32,9 @@ export interface SetupCliDeps extends DoctorOptions {
29
32
  homeDir?: () => string;
30
33
  getUserConfig?: () => UserConfig;
31
34
  saveUserConfig?: (config: UserConfig) => void;
35
+ watchRoot?: string;
36
+ /** Test seam: injected configs are treated as already provisioned unless explicitly first-run. */
37
+ firstRunTopics?: boolean;
32
38
  planClaudePluginSetup?: typeof planClaudePluginSetup;
33
39
  applyClaudePluginSetup?: typeof applyClaudePluginSetup;
34
40
  prompt?: (message: string) => Promise<string | undefined>;
@@ -209,6 +215,7 @@ export function registerSetupCommand(program: Command, deps: SetupCliDeps = {}):
209
215
  setExitCode(1);
210
216
  return;
211
217
  }
218
+ const isFirstRun = deps.firstRunTopics ?? (!deps.getUserConfig && !existsSync(userConfigPath()));
212
219
  let steps = setupSteps(deps);
213
220
  const provider = steps[1]!;
214
221
  if (!provider.complete) {
@@ -224,6 +231,29 @@ export function registerSetupCommand(program: Command, deps: SetupCliDeps = {}):
224
231
  out.log(formatSetupReport(doctor, steps, false));
225
232
  showCliSkills(false);
226
233
  for (const line of recommendedSetup((deps.getUserConfig ?? getUserConfig)())) out.log(line);
234
+ let configured = (deps.getUserConfig ?? getUserConfig)();
235
+ if (!configured.starter?.topics && configured.watch?.enabled !== false
236
+ && isFirstRun
237
+ && isStdinTty() && !opts.yes) {
238
+ const answer = (await prompt('관심 주제는 무엇인가요? (쉼표로 구분 · 건너뛰려면 Enter) '))?.trim();
239
+ if (answer) {
240
+ configured = { ...configured, starter: { topics: answer.split(',').map((topic) => topic.trim()).filter(Boolean) } };
241
+ (deps.saveUserConfig ?? saveUserConfig)(configured);
242
+ }
243
+ }
244
+ const starter = configured.starter;
245
+ if (Array.isArray(starter?.topics) && configured.watch?.enabled !== false) {
246
+ const root = deps.watchRoot ?? effectiveInstanceRoot();
247
+ const existing = listSources({ seat: 'user' }, root);
248
+ for (const rawTopic of starter.topics) {
249
+ if (typeof rawTopic !== 'string' || !rawTopic.trim()) continue;
250
+ const topic = rawTopic.trim();
251
+ if (existing.some((source) => source.kind === 'github-query' && source.spec === topic)) continue;
252
+ const id = `watch-${createHash('sha256').update(topic).digest('hex').slice(0, 12)}`;
253
+ if (listSources({}, root).some((source) => source.id === id)) continue;
254
+ existing.push(addSource({ id, seat: 'user', kind: 'github-query', spec: topic, every: '1d', why: 'starter.topics' }, root));
255
+ }
256
+ }
227
257
  const accepted = opts.yes || (isStdinTty() && /^(?:y|yes)?$/i.test((await prompt('추천대로 켤까요? [Y/n] '))?.trim() ?? 'n'));
228
258
  if (accepted) {
229
259
  const runDoctorFix = deps.runDoctorFix ?? (async (args: readonly string[]) => {