elanous 0.2.11 → 0.2.13

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 (342) 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/{1394-556de6d71bbdb0fb.js → 1394-931c9827067b2276.js} +1 -1
  4. package/apps/pwa/out/_next/static/chunks/3869-3cd46526879f454c.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/{3992-2e522a8ac5f2afde.js → 3992-fe5c06d6b31f8b25.js} +1 -1
  6. package/apps/pwa/out/_next/static/chunks/5250-c78ca295585bbf95.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/6511-ff8360be3d65aaa4.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/6588-ba380869b67c4fa7.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/7758-85c2b53f731da024.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/{8605-ea5bee9dfae512c7.js → 8605-9da4678f8a310d18.js} +1 -1
  11. package/apps/pwa/out/_next/static/chunks/9906-e7b8b5310a169dfe.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/app/approvals/page-e5b4e1b256f41076.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-6a061bd97f0b9398.js → page-e49b9b76c565a0b1.js} +1 -1
  14. package/apps/pwa/out/_next/static/chunks/app/board/page-b4f032c893b21cc9.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/app/botlab/page-f14987d5850a31e0.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/app/bots/page-b433308aad13ceb6.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/app/chat/{page-7c97dcdfe2e37a60.js → page-4c1dbb21861beab2.js} +1 -1
  18. package/apps/pwa/out/_next/static/chunks/app/consult/page-c85b816e3b269dbb.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/app/control/{page-5721537ca8a3598c.js → page-f6bc66b919b2ea7d.js} +1 -1
  20. package/apps/pwa/out/_next/static/chunks/app/design-check/{page-b654240a572e4c48.js → page-60603c1e056c9b25.js} +1 -1
  21. package/apps/pwa/out/_next/static/chunks/app/editor/{page-b9750abc897508c3.js → page-a0ea8f5a2341ee6b.js} +1 -1
  22. package/apps/pwa/out/_next/static/chunks/app/exec/page-45c5bf04b4a605e6.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/app/field/page-36c6c4183353ac9e.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/app/inside/page-cb2eb0fd809980da.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/app/intake/{page-c8b1ccda6ba7ae98.js → page-31012264da8a341f.js} +1 -1
  26. package/apps/pwa/out/_next/static/chunks/app/{layout-d6b1d156fb95e74b.js → layout-50309aeb0bdd554a.js} +1 -1
  27. package/apps/pwa/out/_next/static/chunks/app/live/page-bc00e964e855f4df.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/app/market/{page-9688f5b4f1ff950f.js → page-0696ee3fac9fe56c.js} +1 -1
  29. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66c27608f651b866.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/app/morning/{page-35414e0a007b4082.js → page-6ddd2109b7fc3a80.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/app/not-found-4e50b7298c1f69a5.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/app/observatory/{page-e001823c35c9dd67.js → page-7a2bf830038a2264.js} +1 -1
  33. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-371423d1a573d64b.js → page-1f327f18694fabca.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-4b764b125339ac17.js → page-e5cef2fe6e4028df.js} +1 -1
  35. package/apps/pwa/out/_next/static/chunks/app/ops/seats/{page-cdf8e4b01067bcf1.js → page-cf596128d2b420ba.js} +1 -1
  36. package/apps/pwa/out/_next/static/chunks/app/outputs/{page-3a24072b35470157.js → page-f672536f14f0969f.js} +1 -1
  37. package/apps/pwa/out/_next/static/chunks/app/page-b52365662232db89.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/app/reflection/page-e7a6c80f288b1948.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-d37cb0d52f9b6bd5.js → page-29a709616df9b050.js} +1 -1
  40. package/apps/pwa/out/_next/static/chunks/app/sessions/{page-686da0df9bf23e5c.js → page-fee842d7c2151129.js} +1 -1
  41. package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-4dc614f3ce043e63.js → page-78b40a344ed22f24.js} +1 -1
  42. package/apps/pwa/out/_next/static/chunks/app/settings/page-75d199f448171014.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-d02c782fc792e2af.js → page-a011dd5b0bff29c5.js} +1 -1
  44. package/apps/pwa/out/_next/static/chunks/app/setup/{page-239c17b911b84e72.js → page-d92e32d51ab5157e.js} +1 -1
  45. package/apps/pwa/out/_next/static/chunks/app/share/{page-dc6a79fb003f746c.js → page-5f21aed6932ef375.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/app/showroom/{page-0fffb84cafdc9da6.js → page-1e1a53946804debf.js} +1 -1
  47. package/apps/pwa/out/_next/static/chunks/app/tasks/{page-1218753c82d585f6.js → page-a1833cd43d832601.js} +1 -1
  48. package/apps/pwa/out/_next/static/chunks/app/term/{page-4b5935e67531efb5.js → page-63d14a4d8fe1e1c1.js} +1 -1
  49. package/apps/pwa/out/_next/static/chunks/app/today/{page-3285bc87caf1eaa5.js → page-2a511d320f762e06.js} +1 -1
  50. package/apps/pwa/out/_next/static/chunks/app/trace/{page-ad553b0e06505ecd.js → page-1c3e03f88dac1e21.js} +1 -1
  51. package/apps/pwa/out/_next/static/chunks/app/vault/{page-fca096e3e860306f.js → page-c4d5289c2ea97a89.js} +1 -1
  52. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/{page-9ebfa78f0d9885c3.js → page-b91f8952deaeaf27.js} +1 -1
  53. package/apps/pwa/out/_next/static/chunks/app/workflows/{page-3dc5f21f7e39622d.js → page-71b43a2588489987.js} +1 -1
  54. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2b256feed331546f.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-505bb96d69a8622c.js +1 -0
  56. package/apps/pwa/out/_next/static/css/128fb7fb670b2fbf.css +1 -0
  57. package/apps/pwa/out/approvals/index.html +1 -1
  58. package/apps/pwa/out/approvals/index.txt +14 -14
  59. package/apps/pwa/out/autopilot/index.html +1 -1
  60. package/apps/pwa/out/autopilot/index.txt +14 -14
  61. package/apps/pwa/out/board/index.html +1 -1
  62. package/apps/pwa/out/board/index.txt +14 -14
  63. package/apps/pwa/out/botlab/index.html +1 -1
  64. package/apps/pwa/out/botlab/index.txt +14 -14
  65. package/apps/pwa/out/bots/index.html +1 -1
  66. package/apps/pwa/out/bots/index.txt +14 -14
  67. package/apps/pwa/out/chat/index.html +1 -1
  68. package/apps/pwa/out/chat/index.txt +14 -14
  69. package/apps/pwa/out/consult/index.html +1 -1
  70. package/apps/pwa/out/consult/index.txt +14 -14
  71. package/apps/pwa/out/control/index.html +1 -1
  72. package/apps/pwa/out/control/index.txt +14 -14
  73. package/apps/pwa/out/design-check/index.html +1 -1
  74. package/apps/pwa/out/design-check/index.txt +14 -14
  75. package/apps/pwa/out/editor/index.html +1 -1
  76. package/apps/pwa/out/editor/index.txt +14 -14
  77. package/apps/pwa/out/exec/index.html +1 -1
  78. package/apps/pwa/out/exec/index.txt +14 -14
  79. package/apps/pwa/out/field/index.html +1 -1
  80. package/apps/pwa/out/field/index.txt +14 -14
  81. package/apps/pwa/out/healthz +1 -1
  82. package/apps/pwa/out/index.html +1 -1
  83. package/apps/pwa/out/index.txt +14 -14
  84. package/apps/pwa/out/inside/index.html +1 -1
  85. package/apps/pwa/out/inside/index.txt +14 -14
  86. package/apps/pwa/out/intake/index.html +1 -1
  87. package/apps/pwa/out/intake/index.txt +14 -14
  88. package/apps/pwa/out/live/index.html +1 -1
  89. package/apps/pwa/out/live/index.txt +14 -14
  90. package/apps/pwa/out/market/index.html +1 -1
  91. package/apps/pwa/out/market/index.txt +14 -14
  92. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  93. package/apps/pwa/out/missions/placeholder/index.txt +14 -14
  94. package/apps/pwa/out/morning/index.html +1 -1
  95. package/apps/pwa/out/morning/index.txt +14 -14
  96. package/apps/pwa/out/observatory/index.html +1 -1
  97. package/apps/pwa/out/observatory/index.txt +14 -14
  98. package/apps/pwa/out/ops/checklist/index.html +1 -1
  99. package/apps/pwa/out/ops/checklist/index.txt +14 -14
  100. package/apps/pwa/out/ops/release/index.html +1 -1
  101. package/apps/pwa/out/ops/release/index.txt +14 -14
  102. package/apps/pwa/out/ops/seats/index.html +1 -1
  103. package/apps/pwa/out/ops/seats/index.txt +14 -14
  104. package/apps/pwa/out/outputs/index.html +1 -1
  105. package/apps/pwa/out/outputs/index.txt +14 -14
  106. package/apps/pwa/out/reflection/index.html +1 -1
  107. package/apps/pwa/out/reflection/index.txt +15 -15
  108. package/apps/pwa/out/scheduler/index.html +1 -1
  109. package/apps/pwa/out/scheduler/index.txt +14 -14
  110. package/apps/pwa/out/sessions/index.html +1 -1
  111. package/apps/pwa/out/sessions/index.txt +14 -14
  112. package/apps/pwa/out/settings/devices/index.html +1 -1
  113. package/apps/pwa/out/settings/devices/index.txt +14 -14
  114. package/apps/pwa/out/settings/index.html +1 -1
  115. package/apps/pwa/out/settings/index.txt +14 -14
  116. package/apps/pwa/out/setup/done/index.html +1 -1
  117. package/apps/pwa/out/setup/done/index.txt +14 -14
  118. package/apps/pwa/out/setup/index.html +1 -1
  119. package/apps/pwa/out/setup/index.txt +14 -14
  120. package/apps/pwa/out/share/index.html +1 -1
  121. package/apps/pwa/out/share/index.txt +14 -14
  122. package/apps/pwa/out/showroom/index.html +1 -1
  123. package/apps/pwa/out/showroom/index.txt +14 -14
  124. package/apps/pwa/out/tasks/index.html +1 -1
  125. package/apps/pwa/out/tasks/index.txt +14 -14
  126. package/apps/pwa/out/term/index.html +1 -1
  127. package/apps/pwa/out/term/index.txt +14 -14
  128. package/apps/pwa/out/today/index.html +1 -1
  129. package/apps/pwa/out/today/index.txt +14 -14
  130. package/apps/pwa/out/trace/index.html +1 -1
  131. package/apps/pwa/out/trace/index.txt +14 -14
  132. package/apps/pwa/out/vault/index.html +1 -1
  133. package/apps/pwa/out/vault/index.txt +14 -14
  134. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  135. package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
  136. package/apps/pwa/out/workflows/index.html +1 -1
  137. package/apps/pwa/out/workflows/index.txt +14 -14
  138. package/apps/pwa/out/workspace/index.html +1 -1
  139. package/apps/pwa/out/workspace/index.txt +14 -14
  140. package/apps/pwa/out/worktrees/index.html +1 -1
  141. package/apps/pwa/out/worktrees/index.txt +14 -14
  142. package/examples/teams/personas/backend.yaml +4 -0
  143. package/examples/teams/personas/pm.yaml +4 -0
  144. package/examples/teams/personas/qa.yaml +4 -0
  145. package/examples/teams/small-product.yaml +20 -0
  146. package/graphs/context/nightly-condense.yaml +21 -0
  147. package/graphs/context/recipes.yaml +12 -0
  148. package/graphs/demo/inside-seed.yaml +20 -0
  149. package/graphs/demo/recipes.yaml +12 -0
  150. package/graphs/lecture/lecture-note.yaml +28 -0
  151. package/graphs/lecture/recipes.yaml +17 -0
  152. package/package.json +2 -1
  153. package/plugins/card-followup/examples/sample-card.png +0 -0
  154. package/scripts/context-hooks/emit.ts +36 -14
  155. package/scripts/coord-channel-watch.sh +1 -0
  156. package/scripts/discord-seat-probe.ts +144 -0
  157. package/scripts/hq/hq-standby.sh +65 -0
  158. package/scripts/hq/standby-promote.ts +173 -0
  159. package/scripts/hq/standby-role.ts +59 -0
  160. package/scripts/hq/standby-snapshot.ts +226 -0
  161. package/scripts/hq/standby-verify.ts +219 -0
  162. package/scripts/lecture-note/node.ts +256 -0
  163. package/scripts/lib/telegram-inject-args.ts +31 -0
  164. package/scripts/measure-pod-memory-by-goal.ts +130 -0
  165. package/scripts/release-loop/auto-approve-node.ts +1 -1
  166. package/scripts/release-loop/checklist-gate-node.ts +2 -1
  167. package/scripts/release-loop/gate-node.ts +61 -17
  168. package/scripts/release-loop/ops-upgrade-node.ts +59 -6
  169. package/scripts/release-loop/publish-node.ts +4 -1
  170. package/scripts/release-loop/version-node.ts +13 -3
  171. package/scripts/telegram-inject.ts +14 -6
  172. package/scripts/test-deterministic.ts +53 -3
  173. package/scripts/test-diet/remeasure-list.ts +112 -0
  174. package/scripts/ux/demo-rehearsal.ts +17 -13
  175. package/scripts/ux/demo-seed.ts +108 -0
  176. package/scripts/ux/lib/rehearsal-verdict.ts +12 -5
  177. package/skills/omni-crawl/SKILL.md +16 -16
  178. package/skills/omni-crawl/package-lock.json +1 -544
  179. package/skills/omni-crawl/package.json +3 -4
  180. package/skills/omni-crawl/scripts/main.ts +13 -13
  181. package/skills/omni-crawl/scripts/monitor.ts +8 -8
  182. package/skills/omni-crawl/src/apify.ts +13 -2
  183. package/skills/omni-crawl/src/capture.ts +6 -0
  184. package/skills/omni-crawl/src/registry.ts +2 -2
  185. package/skills/omni-crawl/src/stdout-json.ts +4 -0
  186. package/skills/omni-digest/SKILL.md +10 -11
  187. package/skills/omni-digest/package-lock.json +0 -545
  188. package/skills/omni-digest/package.json +2 -5
  189. package/skills/omni-digest/scripts/main.ts +3 -3
  190. package/skills/omni-digest/src/delegate.ts +5 -5
  191. package/skills/omni-digest/src/fetch.ts +2 -2
  192. package/skills/omni-digest/src/media.ts +25 -11
  193. package/skills/omni-digest/src/visual.ts +3 -2
  194. package/skills/omni-market/scripts/main.ts +1 -1
  195. package/skills/omni-market/scripts/stdout-json.ts +4 -0
  196. package/src/agent/identity-env.ts +11 -0
  197. package/src/agent-mission/driver.ts +150 -23
  198. package/src/agent-mission/duo.ts +94 -0
  199. package/src/agent-mission/pty-decision.ts +2 -2
  200. package/src/autopilot/pty-control-loop.ts +4 -1
  201. package/src/autopilot/track-agent-shadow.ts +2 -1
  202. package/src/autopilot/track-agent.ts +4 -1
  203. package/src/boot/daemon-prompt-request.ts +3 -0
  204. package/src/boot/daemon-prompt-turn.ts +6 -2
  205. package/src/boot/daemon-runtime.ts +48 -16
  206. package/src/chat/index.ts +1 -0
  207. package/src/cli/decisions-cli.ts +51 -19
  208. package/src/cli/outputs-cli.ts +92 -0
  209. package/src/cli/persona-cli.ts +45 -13
  210. package/src/cli/pod-cli.ts +27 -3
  211. package/src/cli/pod-memory-advice.ts +35 -0
  212. package/src/cli/pr-cli.ts +15 -1
  213. package/src/cli/release-cli.ts +135 -15
  214. package/src/context-bus/condense.ts +194 -0
  215. package/src/context-bus/context-day.ts +82 -0
  216. package/src/context-bus/context-now-surfaces.ts +32 -2
  217. package/src/context-bus/context-now.ts +2 -2
  218. package/src/context-bus/coord-events.ts +2 -2
  219. package/src/context-bus/long-term-memory.ts +118 -22
  220. package/src/context-bus/nightly-condense.ts +153 -0
  221. package/src/context-bus/session-events.ts +35 -0
  222. package/src/dashboard/dashboard-status-lines.ts +2 -0
  223. package/src/dashboard/index.ts +8 -0
  224. package/src/dashboard/slash-runtime/dashboard-handlers.ts +24 -0
  225. package/src/decisions/decision-cards.ts +13 -7
  226. package/src/decisions/decision-ledger.ts +43 -17
  227. package/src/domains/outbound-alert.ts +99 -31
  228. package/src/domains/outbound-log-probe.ts +8 -0
  229. package/src/execution-loop/launch-gate.ts +17 -8
  230. package/src/field-note/telegram.ts +142 -0
  231. package/src/graph-kinds/registry.ts +2 -2
  232. package/src/graph-runner/graph-approval-card.ts +2 -2
  233. package/src/graph-runner/graph-grow-llm.ts +23 -13
  234. package/src/graph-runner/graph-grow.ts +69 -13
  235. package/src/graph-runner/graph-tick.ts +9 -2
  236. package/src/graph-runner/runner.ts +92 -24
  237. package/src/harness/harness-cli-command.ts +165 -17
  238. package/src/harness/harness-queue-child.ts +37 -0
  239. package/src/harness/harness-queue.ts +306 -0
  240. package/src/hooks/consult-intake.ts +153 -0
  241. package/src/hooks/github-shadow.ts +72 -2
  242. package/src/hooks/receiver.ts +82 -3
  243. package/src/hq/hq.ts +318 -0
  244. package/src/hq/lease.ts +210 -0
  245. package/src/hq/ledger-write-target.ts +41 -0
  246. package/src/index.ts +232 -16
  247. package/src/intake-plane/discord-seat-work.ts +36 -3
  248. package/src/intake-plane/linear-wish.ts +44 -0
  249. package/src/intake-plane/persona-answer.ts +5 -0
  250. package/src/intake-plane/telegram-seat-work.ts +13 -6
  251. package/src/intake-plane/wish-card.ts +33 -0
  252. package/src/llm/difficulty.ts +1 -1
  253. package/src/loops/checker.ts +178 -0
  254. package/src/loops/loop-cli.ts +37 -3
  255. package/src/loops/observe.ts +49 -0
  256. package/src/loops/orchestrator/overlap.ts +141 -0
  257. package/src/loops/orchestrator/traffic.ts +150 -0
  258. package/src/loops/package/verify.ts +125 -0
  259. package/src/loops/verdict.ts +22 -0
  260. package/src/maturity/feature-maturity.ts +5 -4
  261. package/src/memory.ts +37 -4
  262. package/src/nexus/api/card-followup-route.ts +118 -0
  263. package/src/nexus/api/decisions-route.ts +68 -0
  264. package/src/nexus/api/http-server.ts +78 -8
  265. package/src/nexus/api/outbound-report.ts +40 -7
  266. package/src/nexus/api/personas.ts +97 -54
  267. package/src/nexus/api/skill-exec-route.ts +97 -0
  268. package/src/nexus/api/task-cards-api.ts +19 -0
  269. package/src/nexus/api/workflow-history.ts +88 -0
  270. package/src/nexus/api/workflows.ts +52 -0
  271. package/src/nexus/outbound/delivery-ledger.ts +17 -8
  272. package/src/nexus/outbound/router.ts +8 -3
  273. package/src/persona/loader.ts +18 -0
  274. package/src/persona/persona-todo.ts +14 -9
  275. package/src/persona/presets.ts +10 -2
  276. package/src/persona/team.ts +105 -0
  277. package/src/persona/types.ts +4 -0
  278. package/src/plugins/maker/market-bundle.ts +2 -1
  279. package/src/pod-lease/host-lease.ts +130 -20
  280. package/src/release-loop/checklist.ts +15 -3
  281. package/src/release-loop/feature-store.ts +24 -10
  282. package/src/release-loop/landed-but-yellow.ts +60 -0
  283. package/src/seat-dispatch/seat-ask.ts +57 -12
  284. package/src/seat-loop/seat-loop.ts +253 -18
  285. package/src/seat-loop/seat-pack.ts +127 -0
  286. package/src/self-dev/ask-launch-flow.ts +3 -0
  287. package/src/self-dev/draft-sweep.ts +44 -10
  288. package/src/self-dev/draft-triage-rules.ts +54 -2
  289. package/src/self-implement/budget-gate.ts +29 -12
  290. package/src/self-implement/gate-candidate-ranking.ts +60 -0
  291. package/src/self-implement/gate-scope.ts +9 -8
  292. package/src/self-implement/headless-elanous-driver.ts +7 -2
  293. package/src/self-implement/l8-merge-queue.ts +157 -1
  294. package/src/self-implement/orchestrator.ts +15 -0
  295. package/src/self-implement/test-seams.ts +1 -0
  296. package/src/steward/launch.ts +5 -1
  297. package/src/steward/readiness.ts +125 -0
  298. package/src/steward/rescue.ts +75 -0
  299. package/src/steward/triage.ts +48 -9
  300. package/src/task-cards/card-cli.ts +78 -0
  301. package/src/task-orchestrator/surfaces/pod-command-job.ts +14 -10
  302. package/src/task-orchestrator/surfaces/pod-lease.ts +18 -6
  303. package/src/task-orchestrator/surfaces/pod-pool.ts +27 -10
  304. package/src/task-orchestrator/surfaces/self-implement-pod.ts +46 -8
  305. package/src/telegram-commands.ts +21 -0
  306. package/src/telegram-dispatch-forward.ts +1 -1
  307. package/src/telegram-fabric-plan.ts +90 -0
  308. package/src/telegram-project-command.ts +84 -4
  309. package/src/telegram.ts +37 -5
  310. package/src/tool-runtime/elanous-control-runtimes.ts +2 -1
  311. package/src/user-config.ts +101 -13
  312. package/src/version/packed-revision.json +1 -1
  313. package/src/workflow-runtime/executor.ts +4 -0
  314. package/src/workflow-runtime/node-catalog.ts +13 -0
  315. package/src/workflow-runtime/nodes/knowledge.ts +38 -0
  316. package/src/workflow-runtime/schema.ts +17 -1
  317. package/src/workflow-runtime/types.ts +13 -1
  318. package/apps/pwa/out/_next/static/chunks/2310-7a59f043f6db207e.js +0 -1
  319. package/apps/pwa/out/_next/static/chunks/2548-6e316138d347cefe.js +0 -1
  320. package/apps/pwa/out/_next/static/chunks/3869-a39d1d9921e10d43.js +0 -1
  321. package/apps/pwa/out/_next/static/chunks/507-5acd98bd6dab2d8a.js +0 -1
  322. package/apps/pwa/out/_next/static/chunks/5445-745eb795c5963d79.js +0 -1
  323. package/apps/pwa/out/_next/static/chunks/9906-482c6619b3fdda20.js +0 -1
  324. package/apps/pwa/out/_next/static/chunks/app/approvals/page-1dcdc58fc2de2454.js +0 -1
  325. package/apps/pwa/out/_next/static/chunks/app/board/page-cb95a7432c9ee744.js +0 -1
  326. package/apps/pwa/out/_next/static/chunks/app/botlab/page-b12b6d2f8d2d1aea.js +0 -1
  327. package/apps/pwa/out/_next/static/chunks/app/bots/page-b9efe868bfd36255.js +0 -1
  328. package/apps/pwa/out/_next/static/chunks/app/consult/page-8fd36984f549270a.js +0 -1
  329. package/apps/pwa/out/_next/static/chunks/app/exec/page-4cd1de371f8ad048.js +0 -1
  330. package/apps/pwa/out/_next/static/chunks/app/field/page-d99f89657828421c.js +0 -1
  331. package/apps/pwa/out/_next/static/chunks/app/inside/page-50cb2a0eb15e6c83.js +0 -1
  332. package/apps/pwa/out/_next/static/chunks/app/live/page-2a9932ddb62d76bd.js +0 -1
  333. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6b15dd6456c5df09.js +0 -1
  334. package/apps/pwa/out/_next/static/chunks/app/not-found-e7a529e0507ac86c.js +0 -1
  335. package/apps/pwa/out/_next/static/chunks/app/page-94c82eb0ae5abb32.js +0 -1
  336. package/apps/pwa/out/_next/static/chunks/app/reflection/page-0be8d4b2ced75ba0.js +0 -1
  337. package/apps/pwa/out/_next/static/chunks/app/settings/page-53f80edc6b8dc07c.js +0 -1
  338. package/apps/pwa/out/_next/static/chunks/app/workspace/page-c6da2b468454532d.js +0 -1
  339. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-93bc70e36f595414.js +0 -1
  340. package/apps/pwa/out/_next/static/css/543f56bcb1f79fca.css +0 -1
  341. /package/apps/pwa/out/_next/static/{Y4zKzL62sakG5momz3SoQ → D51l8jUBplJrIpWvENsDK}/_buildManifest.js +0 -0
  342. /package/apps/pwa/out/_next/static/{Y4zKzL62sakG5momz3SoQ → D51l8jUBplJrIpWvENsDK}/_ssgManifest.js +0 -0
@@ -1,12 +1,55 @@
1
1
  #!/usr/bin/env bun
2
- import { existsSync, readFileSync } from 'node:fs';
3
- import { homedir } from 'node:os';
2
+ import { existsSync, mkdtempSync, readFileSync, rmSync, symlinkSync } from 'node:fs';
3
+ import { homedir, tmpdir } from 'node:os';
4
4
  import { join, isAbsolute } from 'node:path';
5
5
  import { debug } from '../../src/debug/log.js';
6
6
  import { errorResult, finishNode, lastResult, nodeOutput, readGraphContext, runCommand, type CommandResult, type CommandRunner } from './node-verdict.js';
7
7
 
8
8
  interface HostResult { host: string; ok: boolean; before: string; after: string; error?: string; skipped?: string; hooks?: string; restart?: 'no-service' }
9
- interface OpsUpgradeDeps { exists?: (path: string) => boolean; installSource?: () => string | null }
9
+ /** Where the internal feed is packed from: a checkout of the release tag, never the run's working tree. */
10
+ export interface FeedSource { checkout: string; commit: string; deps: 'linked' | 'installed'; cleanup: () => void }
11
+ interface OpsUpgradeDeps { exists?: (path: string) => boolean; installSource?: () => string | null; feedSource?: (version: string) => FeedSource }
12
+
13
+ /** Detached worktree at refs/tags/v<version> with the PWA built — the feed ships what was released (10-04 0.2.11:
14
+ * packing process.cwd() shipped main HEAD ee689ad2 as «0.2.11» to node-b·cloud-vm instead of the cut 31129c91). */
15
+ export function tagFeedSource(run: CommandRunner, repo: string = process.cwd()): (version: string) => FeedSource {
16
+ return (version) => {
17
+ const tag = run('git', ['rev-parse', '--verify', `refs/tags/v${version}^{commit}`], repo);
18
+ const commit = tag.stdout.trim();
19
+ if (tag.status !== 0 || !/^[0-9a-f]{40,64}$/.test(commit)) throw new Error(`태그 없음: v${version}`);
20
+ const dir = mkdtempSync(join(tmpdir(), 'ops-feed-'));
21
+ const tree = join(dir, 'tree');
22
+ const cleanup = () => {
23
+ run('git', ['worktree', 'remove', '--force', tree], repo);
24
+ rmSync(dir, { recursive: true, force: true });
25
+ };
26
+ try {
27
+ const added = run('git', ['worktree', 'add', '--detach', tree, commit], repo);
28
+ if (added.status !== 0) throw new Error(`태그 체크아웃 실패: ${reason(added)}`);
29
+ const sameDeps = ['bun.lock', 'apps/pwa/bun.lock', 'package.json', 'apps/pwa/package.json'].every((file) => {
30
+ try { return readFileSync(join(repo, file), 'utf8') === readFileSync(join(tree, file), 'utf8'); }
31
+ catch { return false; }
32
+ });
33
+ const deps: FeedSource['deps'] = sameDeps ? 'linked' : 'installed';
34
+ if (sameDeps) {
35
+ symlinkSync(join(repo, 'node_modules'), join(tree, 'node_modules'));
36
+ if (existsSync(join(repo, 'apps/pwa/node_modules'))) symlinkSync(join(repo, 'apps/pwa/node_modules'), join(tree, 'apps/pwa/node_modules'));
37
+ } else {
38
+ for (const cwd of [tree, join(tree, 'apps/pwa')]) {
39
+ const installed = run('bun', ['install', '--frozen-lockfile'], cwd, 900_000);
40
+ if (installed.status !== 0) throw new Error(`태그 의존성 설치 실패: ${reason(installed)}`);
41
+ }
42
+ }
43
+ // The feed carries the PWA build (apps/pwa/out is ignored, so a fresh tag checkout has none until built).
44
+ const built = run('bun', ['bin/elanous.mjs', 'nexus', 'build'], tree, 900_000);
45
+ if (built.status !== 0) throw new Error(`태그 PWA 빌드 실패: ${reason(built)}`);
46
+ return { checkout: tree, commit, deps, cleanup };
47
+ } catch (error) {
48
+ cleanup();
49
+ throw error;
50
+ }
51
+ };
52
+ }
10
53
 
11
54
  function localInstallSource(): string | null {
12
55
  try {
@@ -54,11 +97,21 @@ export function runOpsUpgrade(run: CommandRunner = runCommand, deps: OpsUpgradeD
54
97
  : (deps.exists ?? existsSync)(defaultFeed) ? defaultFeed : undefined;
55
98
  let feedError = '';
56
99
  if (feedPath !== undefined) {
100
+ let source: FeedSource | undefined;
57
101
  try {
58
- const feed = run('bun', ['scripts/publish-internal-dist.ts', '--checkout', process.cwd(), '--out', feedPath]);
59
- const data = lastResult(feed);
60
- if (feed.status !== 0 || data?.ok !== true || data.version !== version) feedError = `내부 피드 발행 실패: ${feed.status === 0 && data?.ok === true ? `판 불일치 (${String(data.version)})` : reason(feed)}`;
102
+ source = (deps.feedSource ?? tagFeedSource(run))(version);
103
+ const released = context.outputs['version-release']?.commit;
104
+ if (typeof released === 'string' && released && released !== source.commit) {
105
+ feedError = `내부 피드 발행 실패: 태그 v${version}(${source.commit.slice(0, 12)})가 컷(${released.slice(0, 12)})과 다르다`;
106
+ } else {
107
+ const feed = run('bun', ['scripts/publish-internal-dist.ts', '--checkout', source.checkout, '--out', feedPath]);
108
+ const data = lastResult(feed);
109
+ if (feed.status !== 0 || data?.ok !== true || data.version !== version) feedError = `내부 피드 발행 실패: ${feed.status === 0 && data?.ok === true ? `판 불일치 (${String(data.version)})` : reason(feed)}`;
110
+ else if (data.commit !== source.commit) feedError = `내부 피드 발행 실패: 묶은 커밋 ${String(data.commit).slice(0, 12)} ≠ 태그 ${source.commit.slice(0, 12)}`;
111
+ debug.log('release-loop.ops-upgrade', 'feed-source', { version, commit: source.commit, source: `refs/tags/v${version}`, deps: source.deps, packed: data?.commit, pwaBuild: data?.pwaBuild });
112
+ }
61
113
  } catch (error) { feedError = `내부 피드 발행 실패: ${firstLine(String(error))}`; }
114
+ finally { source?.cleanup(); }
62
115
  }
63
116
  const results: HostResult[] = [];
64
117
  for (const host of hosts as string[]) {
@@ -2,6 +2,7 @@
2
2
  import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
3
3
  import { tmpdir } from 'node:os';
4
4
  import { join } from 'node:path';
5
+ import { WARNING_ONLY } from './auto-approve-node.js';
5
6
  import { errorResult, finishNode, lastResult, nodeOutput, readGraphContext, runCommand, type CommandRunner } from './node-verdict.js';
6
7
 
7
8
  export function publicNotes(markdown: string, pages: { pages: Array<{ id: string; slug?: string; source?: string }> }): string {
@@ -40,7 +41,9 @@ export function runPublish(run: CommandRunner = runCommand) {
40
41
  const auto = context.outputs['auto-approve'];
41
42
  if (context.outputs['approve-publish']?.outcome !== 'approved' &&
42
43
  !(auto?.outcome === 'ok' && auto.decidedBy === 'release-loop metrics'
43
- && Array.isArray(auto.metrics) && auto.metrics.length === 8
44
+ // The 8 blocking metrics; warning-only rows (tui-regress · mac-smoke) are shown but never counted (V1g · MAC1).
45
+ && Array.isArray(auto.metrics)
46
+ && auto.metrics.filter((metric: unknown) => !(metric && typeof metric === 'object' && 'name' in metric && WARNING_ONLY.has(String(metric.name)))).length === 8
44
47
  && auto.metrics.every((metric: unknown) => metric && typeof metric === 'object' && 'verdict' in metric && metric.verdict === 'pass'))) {
45
48
  return { outcome: 'fail' as const, verdict: 'fail' as const, summary: 'publish blocked: neither auto-approve nor approve-publish approved' };
46
49
  }
@@ -7,7 +7,7 @@ import { emitNodeResult } from './node-verdict.js';
7
7
  import { debug } from '../../src/debug/log.js';
8
8
 
9
9
  type Kind = 'release' | 'dev-bump';
10
- type Output = { outcome: 'ok' | 'error'; kind: Kind | null; version: string | null; commit: string | null; pr: number | null; worktree?: string; error?: string };
10
+ type Output = { outcome: 'ok' | 'error'; kind: Kind | null; version: string | null; commit: string | null; pr: number | null; files?: string[]; worktree?: string; error?: string };
11
11
 
12
12
  export function nextDevVersion(version: string): string {
13
13
  const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.exec(version);
@@ -120,12 +120,22 @@ function landing(repo: string, kind: Kind, releaseVersion: string, cut?: string,
120
120
  const old = `"version": "${current}"`;
121
121
  if (!text.includes(old)) throw new Error(`package.json version field not found: ${current}`);
122
122
  writeFileSync(path, text.replace(old, `"version": "${target}"`));
123
+ const files = ['package.json'];
123
124
  const serverPath = join(worktree, 'server.json');
124
125
  if (existsSync(serverPath)) {
125
126
  const server = JSON.parse(readFileSync(serverPath, 'utf8')) as { version: string; packages: Array<{ version: string }> };
126
127
  server.version = target;
127
128
  server.packages[0]!.version = target;
128
129
  writeFileSync(serverPath, `${JSON.stringify(server, null, 2)}\n`);
130
+ files.push('server.json');
131
+ }
132
+ const agentPath = join(worktree, 'integrations', 'acp-registry', 'elanous', 'agent.json');
133
+ if (existsSync(agentPath)) {
134
+ const agent = JSON.parse(readFileSync(agentPath, 'utf8')) as { version: string; distribution: { npx: { package: string } } };
135
+ agent.version = target;
136
+ agent.distribution.npx.package = `elanous@${target}`;
137
+ writeFileSync(agentPath, `${JSON.stringify(agent, null, 2)}\n`);
138
+ files.push('agent.json');
129
139
  }
130
140
  const lockPath = join(worktree, 'bun.lock');
131
141
  const lock = readFileSync(lockPath, 'utf8');
@@ -185,7 +195,7 @@ function landing(repo: string, kind: Kind, releaseVersion: string, cut?: string,
185
195
  fetchMain(repo);
186
196
  const commit = git(repo, 'rev-parse', 'origin/main');
187
197
  if (versionAt(repo) !== target || commit === before) throw new Error(`origin/main did not advance to ${target} after merge`);
188
- const output: Output = { outcome: 'ok', kind, version: target, commit, pr: pr ? Number(pr[1]) : null };
198
+ const output: Output = { outcome: 'ok', kind, version: target, commit, pr: pr ? Number(pr[1]) : null, files };
189
199
  if (kind === 'release') debug.log('release-loop.cut', 'chosen', { version: releaseVersion, source: 'main', commit });
190
200
  git(repo, 'worktree', 'remove', '--force', worktree);
191
201
  rmSync(temp, { recursive: true, force: true });
@@ -240,7 +250,7 @@ function main(args: string[] = process.argv.slice(2), repo = process.cwd()): Out
240
250
 
241
251
  if (import.meta.main) {
242
252
  const output = main();
243
- debug.log(`release-loop.version-${output.kind ?? 'unknown'}`, 'result', { version: output.version, outcome: output.outcome });
253
+ debug.log(`release-loop.version-${output.kind ?? 'unknown'}`, 'result', { version: output.version, outcome: output.outcome, files: output.files ?? [] });
244
254
  emitNodeResult({ ...output, verdict: output.outcome === 'ok' ? 'pass' : 'fail', summary: output.outcome === 'ok' ? `${output.kind} ${output.version} · ${output.commit}` : `${output.kind ?? 'version'}: ${output.error ?? 'failed'}` });
245
255
  if (output.outcome === 'error') process.exitCode = 1;
246
256
  }
@@ -16,6 +16,7 @@
16
16
  import { TelegramClient, Api } from 'telegram';
17
17
  import { StringSession } from 'telegram/sessions';
18
18
  import { createInterface } from 'node:readline/promises';
19
+ import { parseInjectArgs } from './lib/telegram-inject-args.js';
19
20
 
20
21
  const args = process.argv.slice(2);
21
22
  const has = (f: string): boolean => args.includes(f);
@@ -201,15 +202,22 @@ if (has('--buttons') || has('--click') || has('--react')) {
201
202
 
202
203
  // 무인 주입: SESSION STRING 으로 로그인 → 봇에게 메시지.
203
204
  const sessionStr = process.env.TELEGRAM_USER_SESSION ?? val('--session');
204
- const to = val('--to') ?? process.env.TELEGRAM_TEST_BOT ?? configBot;
205
- const text = val('--text');
206
205
  if (!sessionStr) { console.error('❌ TELEGRAM_USER_SESSION 필요 — 먼저 `--login` 으로 발급.'); process.exit(2); }
207
- if (!to || !text) { console.error('사용: telegram-inject.ts --to @monad_test_bot --text "<objective>"'); process.exit(2); }
208
-
206
+ let inject: ReturnType<typeof parseInjectArgs>;
207
+ try {
208
+ inject = parseInjectArgs(args, { TELEGRAM_TEST_BOT: process.env.TELEGRAM_TEST_BOT ?? configBot });
209
+ } catch (error) {
210
+ console.error((error as Error).message);
211
+ process.exit(2);
212
+ }
213
+ const { to, text, photos, caption } = inject;
209
214
  const client = new TelegramClient(new StringSession(sessionStr), apiId, apiHash, { connectionRetries: 5 });
210
215
  await client.connect();
211
- const msg = await client.sendMessage(to, { message: text });
212
- console.log(`✅ 주입 완료 → ${to} (msgId=${(msg as { id?: number }).id ?? '?'}). 이 계정의 텔레그램 앱에도 대화가 보입니다.`);
216
+ const msg = photos.length > 0
217
+ ? await client.sendFile(to, { file: photos.length === 1 ? photos[0]! : photos, caption })
218
+ : await client.sendMessage(to, { message: text! });
219
+ const sent = Array.isArray(msg) ? msg[0] : msg;
220
+ console.log(`✅ 주입 완료 → ${to} (msgId=${(sent as { id?: number } | undefined)?.id ?? '?'}). 이 계정의 텔레그램 앱에도 대화가 보입니다.`);
213
221
  console.log(' → 데몬 처리 후 watchdog 검증: log/watchdog-stall.log (stall 없으면 A subprocess 격리 정상).');
214
222
  await client.disconnect();
215
223
  process.exit(0);
@@ -31,8 +31,9 @@
31
31
  // reports the failure.
32
32
 
33
33
  import { mkdtempSync, rmSync } from 'node:fs';
34
+ import { spawnSync } from 'node:child_process';
34
35
  import { tmpdir } from 'node:os';
35
- import { join } from 'node:path';
36
+ import { join, resolve } from 'node:path';
36
37
  // The predicate lives in its own side-effect-free module so it can be tested:
37
38
  // importing THIS file used to spawn the suite at load time. The runner is now
38
39
  // behind `import.meta.main` / `runDeterministicTests()`, but the filter stays
@@ -56,6 +57,8 @@ const HARNESS_TEST_ENV_KEYS = [
56
57
  // Discovery: rg -n "process\.env\.ELANOUS_(HOST|RUN|ORIGIN|SUPERVISOR|PARENT)" src
57
58
  // These are ambient execution identities, not inputs to a deterministic test.
58
59
  // debug.log also attributes SUBSTRATE and ARM_ID to every plain-object record.
60
+ const GIT_LOCATION_ENV_KEYS = ['GIT_DIR', 'GIT_WORK_TREE', 'GIT_COMMON_DIR'] as const;
61
+
59
62
  const EXECUTION_ORIGIN_ENV_KEYS = [
60
63
  'ELANOUS_HOST_ID',
61
64
  'ELANOUS_RUN_ID',
@@ -120,6 +123,7 @@ export type DeterministicRunnerDeps = {
120
123
  childGraceMs?: number;
121
124
  killProcessGroup?: KillProcessGroup;
122
125
  getProcessGroupId?: GetProcessGroupId;
126
+ gitConfig?: (cwd: string, args: string[]) => string;
123
127
  };
124
128
 
125
129
  export function isLivePid(pid: number): boolean {
@@ -269,11 +273,22 @@ export async function terminateDirectChild(
269
273
  reportVisible(`failed to terminate child pid=${pid}`, report);
270
274
  }
271
275
 
276
+ function gitConfig(cwd: string, args: string[]): string {
277
+ const env = { ...process.env };
278
+ for (const key of GIT_LOCATION_ENV_KEYS) delete env[key];
279
+ const result = spawnSync('git', args, { cwd, env, encoding: 'utf8' });
280
+ if (result.error || (result.status !== 0 && !(result.status === 1 && args.includes('--get-all') && !result.stderr))) {
281
+ throw new Error(`git ${args.join(' ')} failed: ${result.error?.message ?? result.stderr}`);
282
+ }
283
+ return result.stdout.trim();
284
+ }
285
+
272
286
  export function prepareIsolatedTestEnv(sourceEnv: NodeJS.ProcessEnv, testRoot: string): NodeJS.ProcessEnv {
273
287
  const env = Object.fromEntries(
274
288
  Object.entries(sourceEnv).filter(([key]) => !isCredentialKey(key)
275
289
  && !HARNESS_TEST_ENV_KEYS.includes(key as typeof HARNESS_TEST_ENV_KEYS[number])
276
- && !EXECUTION_ORIGIN_ENV_KEYS.includes(key as typeof EXECUTION_ORIGIN_ENV_KEYS[number])),
290
+ && !EXECUTION_ORIGIN_ENV_KEYS.includes(key as typeof EXECUTION_ORIGIN_ENV_KEYS[number])
291
+ && !GIT_LOCATION_ENV_KEYS.includes(key as typeof GIT_LOCATION_ENV_KEYS[number])),
277
292
  );
278
293
  env.HOME = testRoot;
279
294
  // userConfigPath() warns whenever XDG_CONFIG_HOME is set, including when
@@ -392,6 +407,38 @@ export async function runDeterministicTests(deps: DeterministicRunnerDeps = {}):
392
407
  const report = deps.report ?? console.error;
393
408
  const spawn = deps.spawn ?? defaultSpawn;
394
409
  const argv = deps.argv ?? process.argv.slice(2);
410
+ const cwd = deps.cwd ?? process.cwd();
411
+ const config = deps.gitConfig ?? gitConfig;
412
+ const commonDir = config(cwd, ['rev-parse', '--path-format=absolute', '--git-common-dir']);
413
+ const configFile = join(resolve(cwd, commonDir), 'config');
414
+ const bareArgs = ['config', '--file', configFile];
415
+ const readBare = () => config(cwd, [...bareArgs, '--get-all', 'core.bare']);
416
+ const bareBefore = readBare();
417
+ let bareChanged = false;
418
+ let bareChecked = false;
419
+ const checkBare = (): void => {
420
+ bareChecked = true;
421
+ let bareAfter = '<unreadable>';
422
+ const visible = (values: string) => values ? values.replaceAll('\n', ',') : '<unset>';
423
+ try {
424
+ bareAfter = readBare();
425
+ if (bareAfter === bareBefore) return;
426
+ bareChanged = true;
427
+ const originalValues = bareBefore ? bareBefore.split('\n') : [];
428
+ if (originalValues.length > 0) {
429
+ config(cwd, [...bareArgs, '--replace-all', 'core.bare', originalValues[0]!]);
430
+ for (const value of originalValues.slice(1)) config(cwd, [...bareArgs, '--add', 'core.bare', value]);
431
+ } else if (bareAfter) {
432
+ config(cwd, [...bareArgs, '--unset-all', 'core.bare']);
433
+ }
434
+ const restored = readBare();
435
+ if (restored !== bareBefore) throw new Error(`expected ${visible(bareBefore)}, got ${visible(restored)}`);
436
+ report(`[test-deterministic] core.bare changed ${visible(bareBefore)} -> ${visible(bareAfter)}; restored ${visible(bareBefore)}; failing run`);
437
+ } catch (error) {
438
+ bareChanged = true;
439
+ report(`[test-deterministic] core.bare changed ${visible(bareBefore)} -> ${visible(bareAfter)}; restore failed: ${formatError(error)}`);
440
+ }
441
+ };
395
442
  // ⛔ 사람이 «경로»를 직접 준 창에서는 아무것도 빼지 않는다 — 그 창은 「이것만 돌려라」다.
396
443
  const explicitPaths = argv.some((arg) => !arg.startsWith('-'));
397
444
  const cdpPatterns = explicitPaths ? [] : (deps.deriveCdpPatterns ?? deriveCdpTestPatterns)();
@@ -451,6 +498,7 @@ export async function runDeterministicTests(deps: DeterministicRunnerDeps = {}):
451
498
  });
452
499
  cleanupTemporaryRoot(testRoot, { rmSync: deps.rmSync, report });
453
500
  rootCleanupAttempted = true;
501
+ checkBare();
454
502
  gate?.dispose();
455
503
  gate = undefined;
456
504
  if (deps.killSelf) {
@@ -465,7 +513,8 @@ export async function runDeterministicTests(deps: DeterministicRunnerDeps = {}):
465
513
  }
466
514
 
467
515
  cleanupRoot();
468
- return outcome.code ?? 1;
516
+ checkBare();
517
+ return bareChanged ? 1 : (outcome.code ?? 1);
469
518
  } catch (error) {
470
519
  if (child) {
471
520
  try {
@@ -484,6 +533,7 @@ export async function runDeterministicTests(deps: DeterministicRunnerDeps = {}):
484
533
  } finally {
485
534
  gate?.dispose();
486
535
  cleanupRoot();
536
+ if (!bareChecked) checkBare();
487
537
  }
488
538
  }
489
539
 
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env bun
2
+ import { spawn } from 'node:child_process';
3
+ import { readFileSync } from 'node:fs';
4
+ import { release } from 'node:os';
5
+
6
+ export const FILE_TIMEOUT_MS = 300_000;
7
+ export type Execution = { rc: number | null; stdout: string; stderr: string; secs: number; timedOut: boolean };
8
+ export type Executor = (file: string, timeoutMs: number) => Promise<Execution>;
9
+ export type Measurement = {
10
+ file: string; rc: number | null; pass: number | null; fail: number | null; skip: number | null;
11
+ secs: number; platform: string; release: string; verdict: 'pass' | 'fail' | 'skip' | '못 잼';
12
+ };
13
+
14
+ export const TIMEOUT_EXIT_GRACE_MS = 5_000;
15
+
16
+ // The wrapper handles SIGTERM by stopping its detached test group. Do not
17
+ // start another file until the wrapper closes, even after SIGKILL.
18
+ export function execute(
19
+ file: string,
20
+ timeoutMs: number,
21
+ deps: {
22
+ spawnChild?: typeof spawn;
23
+ signalGroup?: (pid: number, signal: NodeJS.Signals) => void;
24
+ graceMs?: number;
25
+ } = {},
26
+ ): Promise<Execution> {
27
+ return new Promise((resolve, reject) => {
28
+ const started = performance.now();
29
+ const child = (deps.spawnChild ?? spawn)('bun', ['run', 'test:deterministic', file], { detached: true, stdio: ['ignore', 'pipe', 'pipe'] });
30
+ let stdout = '';
31
+ let stderr = '';
32
+ let timedOut = false;
33
+ let closed = false;
34
+ let escalation: ReturnType<typeof setTimeout> | undefined;
35
+ let terminationCheck: ReturnType<typeof setTimeout> | undefined;
36
+ const seconds = () => Math.round((performance.now() - started) / 1000 * 100) / 100;
37
+ const signal = (name: NodeJS.Signals) => {
38
+ if (child.pid === undefined) return;
39
+ try { (deps.signalGroup ?? ((pid, sig) => process.kill(-pid, sig)))(child.pid, name); }
40
+ catch (groupError) {
41
+ try { child.kill(name); }
42
+ catch (directError) { stderr += `\n${name} failed: ${String(groupError)}; ${String(directError)}`; }
43
+ }
44
+ };
45
+ // Bun's summary is at the end; bounded capture avoids an unbounded memory cost.
46
+ const collect = (previous: string, chunk: Buffer) => (previous + chunk.toString('utf8')).slice(-256_000);
47
+ child.stdout.on('data', (chunk: Buffer) => { stdout = collect(stdout, chunk); });
48
+ child.stderr.on('data', (chunk: Buffer) => { stderr = collect(stderr, chunk); });
49
+ const graceMs = deps.graceMs ?? TIMEOUT_EXIT_GRACE_MS;
50
+ const timer = setTimeout(() => {
51
+ timedOut = true;
52
+ signal('SIGTERM');
53
+ escalation = setTimeout(() => {
54
+ if (closed) return;
55
+ signal('SIGKILL');
56
+ terminationCheck = setTimeout(() => {
57
+ if (!closed) reject(new Error(`Cannot confirm termination of timed-out test: ${file}; stopping measurements`));
58
+ }, graceMs);
59
+ }, graceMs);
60
+ }, timeoutMs);
61
+ child.on('error', (error) => { stderr += `\n${error.message}`; });
62
+ child.on('close', (code) => {
63
+ closed = true;
64
+ clearTimeout(timer);
65
+ if (escalation) clearTimeout(escalation);
66
+ if (terminationCheck) clearTimeout(terminationCheck);
67
+ resolve({ rc: timedOut ? null : code, stdout, stderr, secs: seconds(), timedOut });
68
+ });
69
+ });
70
+ }
71
+
72
+ export async function measureList(files: readonly string[], runner: Executor = execute): Promise<Measurement[]> {
73
+ const measurements: Measurement[] = [];
74
+ for (const file of files) {
75
+ const result = await runner(file, FILE_TIMEOUT_MS);
76
+ const output = `${result.stdout}\n${result.stderr}`;
77
+ const count = (kind: 'pass' | 'fail' | 'skip'): number | null => {
78
+ const matches = [...output.matchAll(new RegExp(`^\\s*(\\d+) ${kind}\\b`, 'gm'))];
79
+ return matches.length ? Number(matches.at(-1)![1]) : null;
80
+ };
81
+ const pass = result.timedOut ? null : count('pass');
82
+ const fail = result.timedOut ? null : count('fail');
83
+ const skip = result.timedOut ? null : count('skip');
84
+ const measured = pass !== null || fail !== null || skip !== null;
85
+ const verdict = result.timedOut || !measured || result.rc === null ? '못 잼'
86
+ : fail !== null && fail > 0 ? 'fail'
87
+ : result.rc !== 0 ? '못 잼'
88
+ : skip !== null && skip > 0 ? 'skip'
89
+ : pass !== null && pass > 0 ? 'pass' : '못 잼';
90
+ measurements.push({ file, rc: result.rc, pass, fail, skip, secs: result.secs, platform: process.platform, release: release(), verdict });
91
+ }
92
+ return measurements;
93
+ }
94
+
95
+ if (import.meta.main) {
96
+ const args = process.argv.slice(2);
97
+ if (args.length < 1 || args.length > 2 || (args.length === 2 && args[1] !== '--json')) {
98
+ console.error('usage: bun scripts/test-diet/remeasure-list.ts <목록 파일> [--json]');
99
+ process.exitCode = 2;
100
+ } else {
101
+ try {
102
+ const files = readFileSync(args[0]!, 'utf8').split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith('#'));
103
+ if (!files.length) throw new Error('empty test list');
104
+ const results = await measureList(files);
105
+ if (args[1] === '--json') console.log(JSON.stringify(results, null, 2));
106
+ else for (const row of results) console.log([row.file, row.rc ?? 'null', row.pass ?? '?', row.fail ?? '?', row.skip ?? '?', row.secs, row.platform, row.release, row.verdict].join('\t'));
107
+ } catch (error) {
108
+ console.error(error);
109
+ process.exitCode = 2;
110
+ }
111
+ }
112
+ }
@@ -5,6 +5,11 @@ import { Frames, launch, PUBLIC_LEAK_CHECK, sleep } from './lib/cdp.js';
5
5
  import { failedApiResponse, judgeRun, judgeScene, worstSceneState, type SceneObservation, type SceneState } from './lib/rehearsal-verdict.js';
6
6
 
7
7
  const TITLES = ['문서 아키텍처', '라이브 트레이스', '루프 에이전트', '그래프 편집기', '마법사 → 마켓', 'PTY 인텔리전스'];
8
+ export const EMPTY_STATE_PHRASES = ['지금 도는 런이 없습니다', '마법사가 아직 돌지 않았습니다', '터미널을 기다리는 중', '판단을 기다리는 중', '못 읽음'] as const;
9
+
10
+ export function sceneSnapshotScript(): string {
11
+ return `(()=>{const sections=[...document.querySelectorAll('[data-inside-scene]')]; const visible=sections.filter(s=>!s.hidden && s.getClientRects().length>0); const text=visible.length===1?visible[0].innerText:''; return {sectionsInDom:sections.length,visibleScene:visible.length===1?Number(visible[0].getAttribute('data-inside-scene')):null,textLength:text.length,emptyStates:${JSON.stringify(EMPTY_STATE_PHRASES)}.filter(phrase=>text.includes(phrase))}})()`;
12
+ }
8
13
 
9
14
  type Args = { url: string; out: string; secs: number; width: number; height: number; demo: boolean; tokenStdin: boolean };
10
15
 
@@ -168,23 +173,20 @@ export async function run(args: Args): Promise<number> {
168
173
  const initialEvents = observer.take();
169
174
  const scenes = [];
170
175
  for (let scene = 1; scene <= 6; scene++) {
171
- const hiddenByDemo = args.demo && scene === 5;
172
- if (hiddenByDemo) {
173
- const result = judgeScene({ scene, title: TITLES[scene - 1]!, hiddenByDemo, secs: 0, frames: 0, exceptions: [], failedRequests: [], textLength: 0, leaks: [], sectionsInDom: 0, visibleScene: null });
174
- scenes.push(result);
175
- console.log(`scene ${scene} ${result.verdict} 0s frames=0`);
176
- continue;
177
- }
176
+ const hiddenByDemo = false;
178
177
  if (scene !== 1) {
179
178
  await cdp.ev<boolean>(`(()=>{const n=${scene}; const nav=document.querySelector('nav[aria-label="장면 선택"]'); const button=[...(nav?.querySelectorAll('button')??[])].find(b=>b.textContent?.trim().startsWith(n+' ')); button?.click(); return !!button})()`);
180
179
  await sleep(300);
181
180
  }
182
181
  const frames = new Frames(join(args.out, `scene-${scene}`));
183
182
  const states: SceneState[] = [];
183
+ const emptyStates = new Set<string>();
184
184
  const sceneRect = await cdp.ev<FrameRect | null>(`(()=>{const s=document.querySelector('[data-inside-scene="${scene}"]'); if (!s || s.hidden) return null; const r=s.getBoundingClientRect(); return {x:r.x,y:r.y,width:r.width,height:r.height}})()`);
185
185
  let blankFrames = 0;
186
186
  const inspect = async () => {
187
- states.push(await cdp.ev<SceneState>(`(()=>{const sections=[...document.querySelectorAll('[data-inside-scene]')]; const visible=sections.filter(s=>!s.hidden && s.getClientRects().length>0); return {sectionsInDom:sections.length,visibleScene:visible.length===1?Number(visible[0].getAttribute('data-inside-scene')):null,textLength:visible.length===1?visible[0].innerText.length:0}})()`));
187
+ const { emptyStates: observedEmpty, ...state } = await cdp.ev<SceneState & { emptyStates: string[] }>(sceneSnapshotScript());
188
+ states.push(state);
189
+ for (const phrase of observedEmpty) emptyStates.add(phrase);
188
190
  };
189
191
  const until = Date.now() + args.secs * 1000;
190
192
  await inspect();
@@ -203,7 +205,7 @@ export async function run(args: Args): Promise<number> {
203
205
  failedRequests: [...initialEvents.failedRequests, ...observed.failedRequests],
204
206
  } : observed;
205
207
  frames.toMp4(join(args.out, `scene-${scene}.mp4`), args.width, args.height);
206
- const obs: SceneObservation = { scene, title: TITLES[scene - 1]!, hiddenByDemo, secs: args.secs, frames: frames.list.length, ...events, ...dom, leaks: bad, blankFrames };
208
+ const obs: SceneObservation = { scene, title: TITLES[scene - 1]!, hiddenByDemo, secs: args.secs, frames: frames.list.length, ...events, ...dom, leaks: bad, blankFrames, emptyStates: [...emptyStates] };
207
209
  const result = judgeScene(obs);
208
210
  scenes.push(result);
209
211
  console.log(`scene ${scene} ${result.verdict} ${args.secs}s frames=${result.frames}`);
@@ -222,13 +224,15 @@ export async function run(args: Args): Promise<number> {
222
224
  '|---|---|---:|---:|---|',
223
225
  ...scenes.map((s) => `| ${s.scene} ${s.title} | ${s.verdict} | ${s.secs} | ${s.frames} | ${s.reasons.join('; ') || '없음'} |`),
224
226
  '',
225
- `ok ${result.ok} · broken ${result.broken} · 미검증 ${result.unverified}`,
227
+ `ok ${result.ok} · broken ${result.broken} · 미검증 ${result.unverified} · 실데이터 없음 ${result.noData}`,
228
+ '',
229
+ '종료 코드: ok=0 · broken=1 · unverified=2 · no-data=3',
226
230
  '',
227
231
  ].join('\n'));
228
232
  for (const line of result.banner) console.log(`⚠️ ${line}`);
229
- console.log(`rehearsal ${result.verdict} ok=${result.ok} broken=${result.broken} unverified=${result.unverified}`);
230
- // 0 = all seen and ok · 1 = broken · 2 = nothing broken but a scene was not seen (not a pass).
231
- return result.verdict === 'broken' ? 1 : result.verdict === 'unverified' ? 2 : 0;
233
+ console.log(`rehearsal ${result.verdict} ok=${result.ok} broken=${result.broken} unverified=${result.unverified} no-data=${result.noData}`);
234
+ // 0 = ok · 1 = broken · 2 = unverified · 3 = no-data.
235
+ return result.verdict === 'broken' ? 1 : result.verdict === 'unverified' ? 2 : result.verdict === 'no-data' ? 3 : 0;
232
236
  } finally { observer?.close(); cdp.close(); }
233
237
  }
234
238
 
@@ -0,0 +1,108 @@
1
+ import { mkdirSync, mkdtempSync, writeFileSync } from 'node:fs';
2
+ import { join, resolve } from 'node:path';
3
+
4
+ export type SeedArgs = { only: 'graph' | 'wizard' | 'all'; dryRun: boolean; json: boolean };
5
+ export type SeedEvent = { target: 'graph' | 'wizard'; phase: 'start' | 'finish' | 'failure'; command: string[]; exitCode?: number; output?: string };
6
+
7
+ export const SCENE_SIX_GUIDANCE = '⑥ PTY 인텔리전스는 이 시드로 생성하지 않습니다. 실제 격리 미션을 실행하고 read → judge → answer 이벤트를 관측하세요.';
8
+ const GRAPH = 'graphs/demo/inside-seed.yaml';
9
+ const WIZARD_REQUEST = '데모용 자료 조사 커넥터와 스킬·그래프 초안을 만들어 주세요. 설치하거나 실행하지 마세요.';
10
+ const DRAFT = { description: 'Demo research draft only; no connector credentials or outbound messages.', connectors: [],
11
+ skill: { description: 'Demo research draft', instructions: 'Research draft only. Implement and validate processing before installing or running.', requires: [] } };
12
+
13
+ export function parseArgs(argv: string[]): SeedArgs {
14
+ const result: SeedArgs = { only: 'all', dryRun: false, json: false };
15
+ const seen = new Set<string>();
16
+ for (let i = 0; i < argv.length; i++) {
17
+ const flag = argv[i]!;
18
+ if (!['--only', '--dry-run', '--json'].includes(flag)) throw new Error(`알 수 없는 인자: ${flag}`);
19
+ if (seen.has(flag)) throw new Error(`중복 인자: ${flag}`);
20
+ seen.add(flag);
21
+ if (flag === '--only') {
22
+ const value = argv[++i];
23
+ if (value !== 'all' && value !== 'graph' && value !== 'wizard') throw new Error('--only 값은 graph|wizard|all 이어야 합니다');
24
+ result.only = value;
25
+ } else if (flag === '--dry-run') result.dryRun = true;
26
+ else result.json = true;
27
+ }
28
+ return result;
29
+ }
30
+
31
+ export type SeedDeps = {
32
+ repo: string;
33
+ run: (command: string[], cwd: string) => Promise<{ exitCode: number; output: string; stdout?: string }>;
34
+ prepareDraft: (repo: string) => { file: string; parent: string };
35
+ };
36
+
37
+ function prepareDraft(repo: string): { file: string; parent: string } {
38
+ const root = resolve(repo, '.elanous-test', 'demo-seed');
39
+ mkdirSync(root, { recursive: true });
40
+ const parent = mkdtempSync(join(root, 'wizard-'));
41
+ const file = join(parent, 'research-draft.json');
42
+ writeFileSync(file, JSON.stringify(DRAFT) + '\n');
43
+ return { file, parent };
44
+ }
45
+
46
+ const defaultDeps: SeedDeps = {
47
+ repo: resolve(import.meta.dir, '../..'),
48
+ prepareDraft,
49
+ run: async (command, cwd) => {
50
+ const child = Bun.spawn(command, { cwd, env: { ...process.env, NODE_ENV: 'test' }, stdout: 'pipe', stderr: 'pipe' });
51
+ const [output, errors, exitCode] = await Promise.all([new Response(child.stdout).text(), new Response(child.stderr).text(), child.exited]);
52
+ return { exitCode, stdout: output, output: [output, errors].filter(Boolean).join('\n').trim() };
53
+ },
54
+ };
55
+
56
+ export async function seed(args: SeedArgs, deps: SeedDeps = defaultDeps, onEvent?: (event: SeedEvent) => void): Promise<{ events: SeedEvent[]; guidance: string; ok: boolean }> {
57
+ const events: SeedEvent[] = [];
58
+ const emit = (event: SeedEvent): void => { events.push(event); onEvent?.(event); };
59
+ let ok = true;
60
+ for (const target of (args.only === 'all' ? ['graph', 'wizard'] : [args.only]) as Array<'graph' | 'wizard'>) {
61
+ const base = ['bun', 'bin/elanous.mjs', '--test'];
62
+ const graphCommand = [...base, 'graph', 'run', GRAPH, '--json'];
63
+ const wizardCommand = [...base, 'plugin', 'make', WIZARD_REQUEST, '--name', 'inside-demo-draft', '--draft-file', '<isolated-draft-file>', '--dir', '<isolated-parent>', '--json'];
64
+ const command = target === 'graph' ? graphCommand : wizardCommand;
65
+ if (args.dryRun) {
66
+ emit({ target, phase: 'start', command });
67
+ emit({ target, phase: 'finish', command, exitCode: 0, output: 'dry-run: no process started' });
68
+ continue;
69
+ }
70
+ try {
71
+ if (target === 'wizard') {
72
+ const { file, parent } = deps.prepareDraft(deps.repo);
73
+ command.splice(command.indexOf('<isolated-draft-file>'), 1, file);
74
+ command.splice(command.indexOf('<isolated-parent>'), 1, parent);
75
+ }
76
+ emit({ target, phase: 'start', command });
77
+ const result = await deps.run(command, deps.repo);
78
+ const parsed = (() => {
79
+ try { return JSON.parse(result.stdout ?? result.output) as { status?: string; dryRun?: boolean; errors?: string[]; timings?: { install?: number } }; } catch { return null; }
80
+ })();
81
+ const complete = target === 'wizard'
82
+ ? parsed?.status === 'draft' && parsed.timings?.install === 0 && parsed.errors?.length === 0
83
+ : parsed?.status === 'done' && parsed.dryRun === false;
84
+ const phase = result.exitCode === 0 && complete ? 'finish' : 'failure';
85
+ emit({ target, phase, command, exitCode: result.exitCode, output: result.output });
86
+ if (phase === 'failure') ok = false;
87
+ } catch (error) {
88
+ ok = false;
89
+ emit({ target, phase: 'failure', command, output: error instanceof Error ? error.message : String(error) });
90
+ }
91
+ }
92
+ return { events, guidance: SCENE_SIX_GUIDANCE, ok };
93
+ }
94
+
95
+ if (import.meta.main) {
96
+ try {
97
+ const args = parseArgs(process.argv.slice(2));
98
+ const report = await seed(args, defaultDeps, args.json ? undefined : (event) => {
99
+ console.log(`${event.target} ${event.phase}${event.exitCode === undefined ? '' : ` (exit ${event.exitCode})`}: ${event.command.join(' ')}${event.output ? `\n${event.output}` : ''}`);
100
+ });
101
+ if (args.json) console.log(JSON.stringify(report));
102
+ else console.log(report.guidance);
103
+ if (!report.ok) process.exitCode = 1;
104
+ } catch (error) {
105
+ console.error(error instanceof Error ? error.message : String(error));
106
+ process.exitCode = 1;
107
+ }
108
+ }