elanous 0.2.12 → 0.2.14

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 (534) 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/{7334.a6679d9168f37aa8.js → 1268.0840ff91e083fcbd.js} +1 -1
  4. package/apps/pwa/out/_next/static/chunks/1307-6e7e0377c1db0bdc.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/{9656-c1540088f5859c80.js → 1844-9ec4b1d12f6e80eb.js} +1 -1
  7. package/apps/pwa/out/_next/static/chunks/1866-e7a6999f25548b30.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/{390.c373e31b3b9d0c41.js → 1948.15f356b15a625597.js} +2 -2
  9. package/apps/pwa/out/_next/static/chunks/211-9c9090c59e2c5907.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/224-798346143007ff65.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/2957-96ffab3d521fb08f.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/4144-a0041c8dc3be57ac.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/4554-3565eea1f1495ba7.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/4562-071d8bac44a01069.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/{4685-c3c1c625b71accd1.js → 4614-21403aac730d4586.js} +1 -1
  23. package/apps/pwa/out/_next/static/chunks/{8039-df92e08fede5de8f.js → 4795-f372490b178583fc.js} +1 -1
  24. package/apps/pwa/out/_next/static/chunks/5135-44008fe8b1f2aa41.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/5233-bf28bd6bd7a32099.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/5360-be1f1d8f9fcc9755.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/5977-2df489622a52a33a.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/5994-ae92c0de046ab9ba.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/{1452.93700483b9d4fc61.js → 6110.1297d4093fdfbb0d.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/6158.9c0449a9a8d0064b.js +2 -0
  31. package/apps/pwa/out/_next/static/chunks/6877-7c6208705c284124.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/726.83532d69452ee24b.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/{1829-b453a1b22d3999bb.js → 7327-2d69fb321ba73389.js} +1 -1
  35. package/apps/pwa/out/_next/static/chunks/7395-164e238f471b5d7b.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/7787-02a1c0b04e25e811.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/{9906-482c6619b3fdda20.js → 8060-cc008cc6fedfa818.js} +1 -1
  38. package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/8827-e2f5b09a8d37e83b.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/8868-10a3a4440fb0ccd8.js +25 -0
  42. package/apps/pwa/out/_next/static/chunks/{8421-1c4acfb1395a0edf.js → 8949-ecb4128a0858b911.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/9070.067c106528ec1ae6.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/9103-7a3ae7f60af3b99f.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/{3869-3cd46526879f454c.js → 9247-29d889b809288e65.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/935.4ddaa99fc1d1126c.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/{79f8be9e-957d68913b590b08.js → a9f2b54d-7865aba0f4d2ee49.js} +1 -1
  48. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/app/approvals/page-74383f890f075511.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-7b2b4f378354869a.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/board/page-c496bb16da85385f.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/botlab/page-1abfe3aec3be6bdc.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/bots/page-1512d91c7491ed62.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/chat/page-a627db94ec6904a0.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/consult/page-fb260b9283dbf646.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/app/control/page-e6b84ab07d3567d5.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/design-check/page-61ec1d2e92bdd50a.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/editor/page-fed69f9759d248d0.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/exec/{page-a67e11ba962337b5.js → page-15374909d8f1aebd.js} +1 -1
  60. package/apps/pwa/out/_next/static/chunks/app/field/page-012a742f81218b01.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/inside/page-fd7e200d4d83fbd7.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/intake/page-99effd9d893a43dc.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/layout-938de57ae0315e46.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/live/page-d47266766766d227.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/loops/page-160089123a40dd91.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/market/page-629e1ede482ec6eb.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-793fa536b3a6ef37.js → page-2ce5471926761fdd.js} +1 -1
  69. package/apps/pwa/out/_next/static/chunks/app/morning/page-27e99b4e54a849d4.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/not-found-e995d30f6800b6bf.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/observatory/page-39f89c53038a51d4.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-32cde713be47cddb.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-e7e10cd3386222e5.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-5a753bb4ec0cc511.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/outputs/page-04250f0e2c0d03df.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/page-341480ed8e3c1ea8.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/reflection/page-e64caff084f7ce21.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-db07eaffba0df7e3.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/sessions/page-8ac8d71c2adf9c8a.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e1e47d8d940aba3d.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/settings/page-86f91034bd5cabe5.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-94dc383d3d54db1a.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/setup/page-b3759cc14da80908.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/app/share/page-ce4d8a1215ce53a3.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/app/showroom/page-58043db78ba519ae.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/app/tasks/page-357750dfb91bf960.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/app/term/{page-e297866051595d81.js → page-274703bc41b71333.js} +1 -1
  89. package/apps/pwa/out/_next/static/chunks/app/today/page-194dce4a83143fce.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/app/trace/page-35bdeae2e2c17147.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/app/vault/page-3b7e821dc492bdfd.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-16bef3da05a900c3.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/app/workflows/page-f8da5dc6f3752276.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2f810b18d89ecba6.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-134c1bc762aee62c.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/{5cd7e3a9-404fd6b8d0ecd44c.js → f6231284-45726550aba08fca.js} +1 -1
  97. package/apps/pwa/out/_next/static/chunks/{f48170cc-d05d28bf87a43bf4.js → f6b7c9ba.64a895b8b2fedbdc.js} +1 -1
  98. package/apps/pwa/out/_next/static/chunks/framework-5c169757551134ed.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/webpack-539cde99554be20f.js +1 -0
  104. package/apps/pwa/out/_next/static/css/2e33713c444d5b39.css +1 -0
  105. package/apps/pwa/out/_next/static/x6mjI6vqOkVVu0jFuDkz1/_buildManifest.js +1 -0
  106. package/apps/pwa/out/approvals/index.html +1 -1
  107. package/apps/pwa/out/approvals/index.txt +23 -23
  108. package/apps/pwa/out/autopilot/index.html +1 -1
  109. package/apps/pwa/out/autopilot/index.txt +23 -23
  110. package/apps/pwa/out/board/index.html +1 -1
  111. package/apps/pwa/out/board/index.txt +23 -23
  112. package/apps/pwa/out/botlab/index.html +1 -1
  113. package/apps/pwa/out/botlab/index.txt +22 -22
  114. package/apps/pwa/out/bots/index.html +1 -1
  115. package/apps/pwa/out/bots/index.txt +22 -22
  116. package/apps/pwa/out/chat/index.html +1 -1
  117. package/apps/pwa/out/chat/index.txt +22 -22
  118. package/apps/pwa/out/consult/index.html +1 -1
  119. package/apps/pwa/out/consult/index.txt +23 -23
  120. package/apps/pwa/out/control/index.html +1 -1
  121. package/apps/pwa/out/control/index.txt +23 -23
  122. package/apps/pwa/out/design-check/index.html +1 -1
  123. package/apps/pwa/out/design-check/index.txt +23 -23
  124. package/apps/pwa/out/editor/index.html +1 -1
  125. package/apps/pwa/out/editor/index.txt +23 -23
  126. package/apps/pwa/out/exec/index.html +1 -1
  127. package/apps/pwa/out/exec/index.txt +23 -23
  128. package/apps/pwa/out/field/index.html +1 -1
  129. package/apps/pwa/out/field/index.txt +23 -23
  130. package/apps/pwa/out/healthz +1 -1
  131. package/apps/pwa/out/index.html +1 -1
  132. package/apps/pwa/out/index.txt +22 -22
  133. package/apps/pwa/out/inside/index.html +1 -1
  134. package/apps/pwa/out/inside/index.txt +22 -22
  135. package/apps/pwa/out/intake/index.html +1 -1
  136. package/apps/pwa/out/intake/index.txt +23 -23
  137. package/apps/pwa/out/live/index.html +1 -1
  138. package/apps/pwa/out/live/index.txt +23 -23
  139. package/apps/pwa/out/loops/index.html +1 -0
  140. package/apps/pwa/out/loops/index.txt +30 -0
  141. package/apps/pwa/out/market/index.html +1 -1
  142. package/apps/pwa/out/market/index.txt +22 -22
  143. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  144. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  145. package/apps/pwa/out/morning/index.html +1 -1
  146. package/apps/pwa/out/morning/index.txt +23 -23
  147. package/apps/pwa/out/observatory/index.html +1 -1
  148. package/apps/pwa/out/observatory/index.txt +23 -23
  149. package/apps/pwa/out/ops/checklist/index.html +1 -1
  150. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  151. package/apps/pwa/out/ops/release/index.html +1 -1
  152. package/apps/pwa/out/ops/release/index.txt +22 -22
  153. package/apps/pwa/out/ops/seats/index.html +1 -1
  154. package/apps/pwa/out/ops/seats/index.txt +22 -22
  155. package/apps/pwa/out/outputs/index.html +1 -1
  156. package/apps/pwa/out/outputs/index.txt +22 -22
  157. package/apps/pwa/out/reflection/index.html +1 -1
  158. package/apps/pwa/out/reflection/index.txt +23 -23
  159. package/apps/pwa/out/scheduler/index.html +1 -1
  160. package/apps/pwa/out/scheduler/index.txt +23 -23
  161. package/apps/pwa/out/sessions/index.html +1 -1
  162. package/apps/pwa/out/sessions/index.txt +22 -22
  163. package/apps/pwa/out/settings/devices/index.html +1 -1
  164. package/apps/pwa/out/settings/devices/index.txt +23 -23
  165. package/apps/pwa/out/settings/index.html +1 -1
  166. package/apps/pwa/out/settings/index.txt +23 -23
  167. package/apps/pwa/out/setup/done/index.html +1 -1
  168. package/apps/pwa/out/setup/done/index.txt +23 -23
  169. package/apps/pwa/out/setup/index.html +1 -1
  170. package/apps/pwa/out/setup/index.txt +23 -23
  171. package/apps/pwa/out/share/index.html +1 -1
  172. package/apps/pwa/out/share/index.txt +23 -23
  173. package/apps/pwa/out/showroom/index.html +1 -1
  174. package/apps/pwa/out/showroom/index.txt +22 -22
  175. package/apps/pwa/out/tasks/index.html +1 -1
  176. package/apps/pwa/out/tasks/index.txt +23 -23
  177. package/apps/pwa/out/term/index.html +1 -1
  178. package/apps/pwa/out/term/index.txt +23 -23
  179. package/apps/pwa/out/today/index.html +1 -1
  180. package/apps/pwa/out/today/index.txt +23 -23
  181. package/apps/pwa/out/trace/index.html +1 -1
  182. package/apps/pwa/out/trace/index.txt +23 -23
  183. package/apps/pwa/out/vault/index.html +1 -1
  184. package/apps/pwa/out/vault/index.txt +23 -23
  185. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  186. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  187. package/apps/pwa/out/workflows/index.html +1 -1
  188. package/apps/pwa/out/workflows/index.txt +23 -23
  189. package/apps/pwa/out/workspace/index.html +1 -1
  190. package/apps/pwa/out/workspace/index.txt +23 -23
  191. package/apps/pwa/out/worktrees/index.html +1 -1
  192. package/apps/pwa/out/worktrees/index.txt +23 -23
  193. package/examples/teams/personas/backend.yaml +4 -0
  194. package/examples/teams/personas/pm.yaml +4 -0
  195. package/examples/teams/personas/qa.yaml +4 -0
  196. package/examples/teams/small-product.yaml +20 -0
  197. package/graphs/lecture/lecture-note.yaml +28 -0
  198. package/graphs/lecture/recipes.yaml +17 -0
  199. package/graphs/orchestrator/orchestrator.yaml +59 -0
  200. package/graphs/orchestrator/recipes.yaml +18 -0
  201. package/graphs/release/recipes.yaml +3 -0
  202. package/graphs/release/release-loop.yaml +4 -1
  203. package/graphs/rhythm/daily.yaml +38 -0
  204. package/graphs/rhythm/recipes.yaml +18 -0
  205. package/graphs/rhythm/weekly.yaml +39 -0
  206. package/graphs/video/recipes.yaml +3 -0
  207. package/graphs/video/youtube-summary.yaml +17 -0
  208. package/package.json +2 -1
  209. package/scripts/audit-test-state-writes.ts +72 -17
  210. package/scripts/brand/check.ts +186 -0
  211. package/scripts/context-hooks/emit.ts +36 -14
  212. package/scripts/coord-channel-watch.sh +1 -0
  213. package/scripts/coord-post.sh +11 -1
  214. package/scripts/discord-seat-probe.ts +144 -0
  215. package/scripts/docs-cli-check.ts +23 -6
  216. package/scripts/expose-rubric-check.ts +118 -0
  217. package/scripts/expose-rubric-suggest.ts +163 -0
  218. package/scripts/heal/run-role.ts +14 -0
  219. package/scripts/hq/hq-standby.sh +65 -0
  220. package/scripts/hq/standby-promote.ts +173 -0
  221. package/scripts/hq/standby-role.ts +59 -0
  222. package/scripts/hq/standby-snapshot.ts +226 -0
  223. package/scripts/hq/standby-verify.ts +223 -0
  224. package/scripts/lecture-note/node.ts +256 -0
  225. package/scripts/lessons/handbook-page.ts +85 -0
  226. package/scripts/release-loop/gate-node.ts +62 -15
  227. package/scripts/release-loop/ops-upgrade-node.ts +17 -5
  228. package/scripts/release-loop/publish-node.ts +14 -1
  229. package/scripts/release-loop/unattended-release.ts +23 -1
  230. package/scripts/release-loop/vault-note-node.ts +196 -0
  231. package/scripts/release-story/draft.ts +172 -0
  232. package/scripts/rhythm/daily.ts +383 -0
  233. package/scripts/rhythm/weekly.ts +294 -0
  234. package/scripts/skills/public-check.ts +169 -0
  235. package/scripts/test-deterministic.ts +57 -11
  236. package/scripts/test-diet/effectiveness.ts +195 -0
  237. package/scripts/test-diet/lib.ts +16 -8
  238. package/scripts/test-diet/node.ts +77 -9
  239. package/scripts/ux/demo-rehearsal.ts +2 -2
  240. package/scripts/ux/lib/rehearsal-verdict.ts +1 -0
  241. package/scripts/video/youtube-summary.ts +133 -0
  242. package/skills/diagram-master/SKILL.md +42 -49
  243. package/skills/diagram-master/references/pyproject.toml +4 -1
  244. package/skills/diagram-master/references/routing-guide.md +4 -2
  245. package/skills/grill-me/SKILL.md +1 -0
  246. package/skills/omni-crawl/SKILL.md +16 -16
  247. package/skills/omni-crawl/package-lock.json +1 -544
  248. package/skills/omni-crawl/package.json +3 -4
  249. package/skills/omni-crawl/scripts/main.ts +13 -13
  250. package/skills/omni-crawl/scripts/monitor.ts +8 -8
  251. package/skills/omni-crawl/src/apify.ts +13 -2
  252. package/skills/omni-crawl/src/capture.ts +6 -0
  253. package/skills/omni-crawl/src/registry.ts +2 -2
  254. package/skills/omni-crawl/src/stdout-json.ts +4 -0
  255. package/skills/omni-digest/SKILL.md +10 -11
  256. package/skills/omni-digest/package-lock.json +0 -545
  257. package/skills/omni-digest/package.json +2 -5
  258. package/skills/omni-digest/scripts/main.ts +3 -3
  259. package/skills/omni-digest/src/delegate.ts +5 -5
  260. package/skills/omni-digest/src/fetch.ts +2 -2
  261. package/skills/omni-digest/src/media.ts +25 -11
  262. package/skills/omni-digest/src/visual.ts +3 -2
  263. package/skills/omni-market/scripts/main.ts +1 -1
  264. package/skills/omni-market/scripts/stdout-json.ts +4 -0
  265. package/skills/youtube-master/SKILL.md +13 -13
  266. package/skills/youtube-master/package-lock.json +0 -545
  267. package/skills/youtube-master/package.json +2 -5
  268. package/skills/youtube-master/scripts/main.ts +2 -2
  269. package/skills/youtube-master/scripts/transcribe-local.ts +1 -1
  270. package/skills/youtube-master/src/util.ts +30 -7
  271. package/src/agent/identity-env.ts +11 -0
  272. package/src/agent-mission/driver.ts +15 -10
  273. package/src/agent-mission/duo.ts +94 -0
  274. package/src/agent-mission/pty-decision.ts +1 -1
  275. package/src/autopilot/build/llm-conflict-merge.ts +45 -8
  276. package/src/autopilot/build/merge-intent.ts +53 -0
  277. package/src/autopilot/track-agent-shadow.ts +2 -1
  278. package/src/autopilot/track-agent.ts +4 -1
  279. package/src/boot/daemon-prompt-request.ts +3 -0
  280. package/src/boot/daemon-prompt-turn.ts +6 -2
  281. package/src/boot/daemon-runtime.ts +48 -16
  282. package/src/chat/index.ts +2 -0
  283. package/src/claims/claims-ledger.ts +232 -0
  284. package/src/claims/claims-render.ts +157 -0
  285. package/src/cli/claims-cli.ts +94 -0
  286. package/src/cli/decisions-cli.ts +84 -20
  287. package/src/cli/docs-cli.ts +2 -0
  288. package/src/cli/flow-cli.ts +11 -0
  289. package/src/cli/freeze-cli.ts +45 -0
  290. package/src/cli/lesson-cli.ts +108 -0
  291. package/src/cli/logs-cli.ts +104 -2
  292. package/src/cli/logs-model-input.ts +131 -0
  293. package/src/cli/outputs-cli.ts +92 -0
  294. package/src/cli/persona-cli.ts +29 -0
  295. package/src/cli/pod-cli.ts +10 -3
  296. package/src/cli/pr-cli.ts +44 -2
  297. package/src/cli/private-ledger-commands.ts +54 -4
  298. package/src/cli/pty-drive-verdict.ts +11 -2
  299. package/src/cli/release-cli.ts +248 -45
  300. package/src/cli/rfc-status.ts +91 -0
  301. package/src/connectors/linear.ts +5 -3
  302. package/src/context-bus/condense.ts +189 -0
  303. package/src/context-bus/context-day.ts +82 -0
  304. package/src/context-bus/context-now-public.ts +86 -0
  305. package/src/context-bus/context-now-surfaces.ts +70 -4
  306. package/src/context-bus/context-now.ts +4 -3
  307. package/src/context-bus/long-term-memory.ts +201 -24
  308. package/src/context-bus/nightly-condense.ts +1 -1
  309. package/src/dashboard/index.ts +75 -22
  310. package/src/dashboard/slash-runtime/chat-persona.ts +66 -0
  311. package/src/dashboard/slash-runtime/dashboard-handlers.ts +99 -0
  312. package/src/dashboard/slash-runtime/loops-table.ts +77 -0
  313. package/src/decisions/decision-cards.ts +13 -7
  314. package/src/decisions/decision-ledger.ts +83 -18
  315. package/src/discord/sprint21-runtime.ts +16 -0
  316. package/src/discord-self-message.ts +8 -0
  317. package/src/discord-slash-wire.ts +31 -2
  318. package/src/domains/outbound-alert.ts +1 -1
  319. package/src/elanous-config-dir.ts +29 -4
  320. package/src/execution-loop/launch-gate.ts +29 -14
  321. package/src/flow/cards.ts +239 -0
  322. package/src/graph-kinds/registry.ts +2 -2
  323. package/src/graph-runner/graph-approval-card.ts +2 -2
  324. package/src/graph-runner/graph-grow.ts +64 -11
  325. package/src/graph-runner/graph-tick.ts +13 -2
  326. package/src/graph-runner/runner.ts +111 -22
  327. package/src/harness/harness-cli-command.ts +178 -27
  328. package/src/harness/harness-incidents.ts +43 -12
  329. package/src/harness/harness-pod-dispatch.ts +2 -0
  330. package/src/harness/harness-queue-child.ts +37 -0
  331. package/src/harness/harness-queue.ts +494 -0
  332. package/src/harness/helper-scan.ts +139 -0
  333. package/src/harness/model-input-observation.ts +37 -0
  334. package/src/hooks/github-shadow.ts +72 -2
  335. package/src/hooks/receiver.ts +4 -2
  336. package/src/hq/hq.ts +318 -0
  337. package/src/hq/lease.ts +210 -0
  338. package/src/hq/ledger-write-target.ts +41 -0
  339. package/src/hq/promote.ts +301 -0
  340. package/src/index.ts +281 -27
  341. package/src/intake-plane/discord-seat-work.ts +13 -6
  342. package/src/intake-plane/linear-wish.ts +72 -0
  343. package/src/intake-plane/persona-answer.ts +5 -0
  344. package/src/intake-plane/telegram-seat-work.ts +13 -6
  345. package/src/intake-plane/wish-benchmark-ledger.ts +129 -0
  346. package/src/intake-plane/wish-card.ts +53 -2
  347. package/src/intake-plane/wish-folder.ts +5 -2
  348. package/src/intake-plane/wish-reply.ts +72 -0
  349. package/src/lessons/lesson-import.ts +106 -0
  350. package/src/lessons/lesson-ledger.ts +206 -0
  351. package/src/loops/activity.ts +203 -0
  352. package/src/loops/budget.ts +32 -0
  353. package/src/loops/checker.ts +180 -0
  354. package/src/loops/contract/examples/orchestrator.loop.yaml +1 -1
  355. package/src/loops/loop-cli.ts +72 -6
  356. package/src/loops/neighbors.ts +40 -0
  357. package/src/loops/observe.ts +59 -0
  358. package/src/loops/orchestrator/author-depth.ts +164 -0
  359. package/src/loops/orchestrator/author-input-check.ts +77 -0
  360. package/src/loops/orchestrator/author-ledger.ts +230 -0
  361. package/src/loops/orchestrator/finish-rate.ts +175 -0
  362. package/src/loops/orchestrator/overlap.ts +180 -0
  363. package/src/loops/orchestrator/resync-shadow.ts +227 -0
  364. package/src/loops/orchestrator/tick.ts +302 -0
  365. package/src/loops/orchestrator/traffic.ts +165 -0
  366. package/src/loops/package/verify.ts +125 -0
  367. package/src/loops/registry.ts +454 -9
  368. package/src/loops/status-rows.ts +118 -0
  369. package/src/loops/verdict.ts +34 -0
  370. package/src/maturity/bot-slash-catalog.ts +66 -0
  371. package/src/maturity/feature-maturity.ts +8 -5
  372. package/src/memory.ts +37 -4
  373. package/src/nexus/api/card-followup-route.ts +118 -0
  374. package/src/nexus/api/dashboard.ts +6 -1
  375. package/src/nexus/api/decisions-route.ts +68 -0
  376. package/src/nexus/api/http-server.ts +65 -2
  377. package/src/nexus/api/merge-approvals-gate.ts +1 -1
  378. package/src/nexus/api/personas.ts +2 -1
  379. package/src/nexus/api/schedules-read.ts +5 -1
  380. package/src/nexus/api/skill-exec-route.ts +97 -0
  381. package/src/nexus/api/task-cards-api.ts +44 -5
  382. package/src/nexus/api/workflows.ts +2 -1
  383. package/src/persona/loader.ts +18 -0
  384. package/src/persona/persona-todo.ts +14 -9
  385. package/src/persona/presets.ts +2 -1
  386. package/src/persona/team.ts +105 -0
  387. package/src/persona/types.ts +4 -0
  388. package/src/pod-lease/host-lease.ts +130 -20
  389. package/src/release-loop/auto-start.ts +48 -0
  390. package/src/release-loop/checklist.ts +13 -4
  391. package/src/release-loop/feature-store.ts +28 -16
  392. package/src/release-loop/landed-but-yellow.ts +12 -0
  393. package/src/release-loop/landing-freeze.ts +121 -0
  394. package/src/release-loop/placement.ts +242 -0
  395. package/src/release-loop/release-schedule.ts +3 -1
  396. package/src/seat-dispatch/ceo-commands.ts +2 -2
  397. package/src/seat-dispatch/seat-ask.ts +73 -15
  398. package/src/seat-dispatch/seat-questions.ts +58 -0
  399. package/src/seat-loop/seat-loop.ts +495 -46
  400. package/src/seat-loop/seat-pack.ts +127 -0
  401. package/src/self-dev/ask-launch-flow.ts +3 -0
  402. package/src/self-dev/dev-pipeline.ts +6 -2
  403. package/src/self-dev/draft-sweep.ts +76 -0
  404. package/src/self-dev/run-store.ts +73 -1
  405. package/src/self-dev/run-supervisor.ts +8 -0
  406. package/src/self-dev/self-orchestrate-runtime.ts +5 -1
  407. package/src/self-implement/budget-gate.ts +29 -0
  408. package/src/self-implement/expose-gate.ts +47 -0
  409. package/src/self-implement/frozen-merges.ts +294 -0
  410. package/src/self-implement/gate-cli.ts +10 -4
  411. package/src/self-implement/gate-scope.ts +46 -9
  412. package/src/self-implement/goal-archive.ts +222 -0
  413. package/src/self-implement/goal-lookup.ts +147 -0
  414. package/src/self-implement/heal-intake.ts +79 -0
  415. package/src/self-implement/host-regate.ts +25 -2
  416. package/src/self-implement/l8-merge-queue.ts +157 -1
  417. package/src/self-implement/orchestrator.ts +114 -10
  418. package/src/self-implement/revert-guard.ts +62 -0
  419. package/src/self-implement/run-ledger.ts +1 -1
  420. package/src/self-implement/seams.ts +28 -8
  421. package/src/session/index.ts +2 -0
  422. package/src/steward/launch.ts +5 -1
  423. package/src/steward/readiness.ts +125 -0
  424. package/src/steward/rescue.ts +75 -0
  425. package/src/steward/triage.ts +48 -9
  426. package/src/task-cards/card-cli.ts +86 -0
  427. package/src/task-cards/card-store.ts +10 -6
  428. package/src/task-orchestrator/surfaces/pod-lease.ts +23 -8
  429. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +46 -6
  430. package/src/task-orchestrator/surfaces/pod-pool.ts +27 -10
  431. package/src/task-orchestrator/surfaces/self-implement-pod.ts +112 -18
  432. package/src/telegram-commands.ts +27 -3
  433. package/src/telegram-dispatch-forward.ts +1 -1
  434. package/src/telegram-fabric-plan.ts +90 -0
  435. package/src/telegram-loops-command.ts +44 -0
  436. package/src/telegram-project-command.ts +10 -4
  437. package/src/telegram.ts +5 -0
  438. package/src/tool-runtime/elanous-control-runtimes.ts +2 -1
  439. package/src/user-config.ts +204 -16
  440. package/src/version/packed-revision.json +1 -1
  441. package/src/workflow-runtime/executor.ts +4 -0
  442. package/src/workflow-runtime/node-catalog.ts +13 -0
  443. package/src/workflow-runtime/nodes/knowledge.ts +38 -0
  444. package/src/workflow-runtime/schema.ts +17 -1
  445. package/src/workflow-runtime/storage.ts +31 -13
  446. package/src/workflow-runtime/types.ts +13 -1
  447. package/apps/pwa/out/_next/static/-6XrAoD3eU7NyMHAE4xPS/_buildManifest.js +0 -1
  448. package/apps/pwa/out/_next/static/chunks/1147-466ec15afacc1a65.js +0 -1
  449. package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +0 -1
  450. package/apps/pwa/out/_next/static/chunks/1394-ee3795b112907122.js +0 -1
  451. package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +0 -1
  452. package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +0 -1
  453. package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +0 -1
  454. package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +0 -1
  455. package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +0 -1
  456. package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +0 -1
  457. package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +0 -1
  458. package/apps/pwa/out/_next/static/chunks/3992-2e522a8ac5f2afde.js +0 -25
  459. package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +0 -1
  460. package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +0 -1
  461. package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +0 -1
  462. package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +0 -2
  463. package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +0 -1
  464. package/apps/pwa/out/_next/static/chunks/5845-e6c003ce8d5fd36a.js +0 -1
  465. package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +0 -1
  466. package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +0 -1
  467. package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +0 -1
  468. package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +0 -1
  469. package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +0 -1
  470. package/apps/pwa/out/_next/static/chunks/7277-0b2fc7c16ba2d64b.js +0 -1
  471. package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +0 -1
  472. package/apps/pwa/out/_next/static/chunks/7758-0b018a9e4fc56200.js +0 -1
  473. package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +0 -1
  474. package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +0 -1
  475. package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +0 -1
  476. package/apps/pwa/out/_next/static/chunks/8605-0259994816604c49.js +0 -1
  477. package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +0 -1
  478. package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +0 -1
  479. package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +0 -1
  480. package/apps/pwa/out/_next/static/chunks/9269.d2a4795bc6812717.js +0 -1
  481. package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +0 -1
  482. package/apps/pwa/out/_next/static/chunks/9920-5cfdc5e8fd209825.js +0 -1
  483. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d16993889b125c42.js +0 -1
  484. package/apps/pwa/out/_next/static/chunks/app/approvals/page-cb169620945d1952.js +0 -1
  485. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-cefe8169e36cf62a.js +0 -1
  486. package/apps/pwa/out/_next/static/chunks/app/board/page-d2e35228bc497119.js +0 -1
  487. package/apps/pwa/out/_next/static/chunks/app/botlab/page-5c05b0da4bc1392c.js +0 -1
  488. package/apps/pwa/out/_next/static/chunks/app/bots/page-e0ccdb576812b523.js +0 -1
  489. package/apps/pwa/out/_next/static/chunks/app/chat/page-813c4a52c9d05ca8.js +0 -1
  490. package/apps/pwa/out/_next/static/chunks/app/consult/page-4731f2c215358937.js +0 -1
  491. package/apps/pwa/out/_next/static/chunks/app/control/page-a022b257950f39e6.js +0 -1
  492. package/apps/pwa/out/_next/static/chunks/app/design-check/page-a2ffc717e04b8b0a.js +0 -1
  493. package/apps/pwa/out/_next/static/chunks/app/editor/page-b59ee4e70fc18fcf.js +0 -1
  494. package/apps/pwa/out/_next/static/chunks/app/field/page-c172dabd796eb7a3.js +0 -1
  495. package/apps/pwa/out/_next/static/chunks/app/healthz/route-d16993889b125c42.js +0 -1
  496. package/apps/pwa/out/_next/static/chunks/app/inside/page-1efd0cb9e0ed9d13.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/app/intake/page-d5dc7ff69a37b4cf.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/app/layout-e71c84189e8c683e.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/app/live/page-1d6a533ce5a6efcb.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/app/market/page-dfca9b7d2de0b135.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/app/morning/page-6dd3bfbbde2a5c2c.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/app/not-found-490653e8d4cc8852.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/app/observatory/page-77447356baad75d2.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-29e961191f6057df.js +0 -1
  505. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-c822f55b884d9104.js +0 -1
  506. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fa4ccfc432424e52.js +0 -1
  507. package/apps/pwa/out/_next/static/chunks/app/outputs/page-6d604227e1664b71.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/app/page-9c7e8f744a765daf.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/app/reflection/page-4048543b4b7ad5e0.js +0 -1
  510. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-4c3a3126abed551a.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/app/sessions/page-64b0aecb1ca75e75.js +0 -1
  512. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-4292d0149020e8a7.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/app/settings/page-db14d0be7f1c460c.js +0 -1
  514. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-e5d8072cd7cfef73.js +0 -1
  515. package/apps/pwa/out/_next/static/chunks/app/setup/layout-d16993889b125c42.js +0 -1
  516. package/apps/pwa/out/_next/static/chunks/app/setup/page-9547b5cec7cf76bf.js +0 -1
  517. package/apps/pwa/out/_next/static/chunks/app/share/page-e6dcb83265530636.js +0 -1
  518. package/apps/pwa/out/_next/static/chunks/app/showroom/page-45248f08d6aecae5.js +0 -1
  519. package/apps/pwa/out/_next/static/chunks/app/tasks/page-2cddeb9e2aeedc55.js +0 -1
  520. package/apps/pwa/out/_next/static/chunks/app/today/page-baba2e047872fb0f.js +0 -1
  521. package/apps/pwa/out/_next/static/chunks/app/trace/page-e895da4ec9cac345.js +0 -1
  522. package/apps/pwa/out/_next/static/chunks/app/vault/page-4344faf20216233b.js +0 -1
  523. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-698bb29f11864b89.js +0 -1
  524. package/apps/pwa/out/_next/static/chunks/app/workflows/page-bd6a6dc1ddc273f2.js +0 -1
  525. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2e9e2fb01c9da1c9.js +0 -1
  526. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-d62ceb13411ebba4.js +0 -1
  527. package/apps/pwa/out/_next/static/chunks/framework-172630e6aa5e2d7f.js +0 -1
  528. package/apps/pwa/out/_next/static/chunks/main-app-f0fe2d7477845181.js +0 -1
  529. package/apps/pwa/out/_next/static/chunks/main-fc98ac70b44eccb0.js +0 -1
  530. package/apps/pwa/out/_next/static/chunks/pages/_app-5bd7f10271f72413.js +0 -1
  531. package/apps/pwa/out/_next/static/chunks/pages/_error-97d0d85ea05fef9b.js +0 -1
  532. package/apps/pwa/out/_next/static/chunks/webpack-9e4d02d567b98240.js +0 -1
  533. package/apps/pwa/out/_next/static/css/128fb7fb670b2fbf.css +0 -1
  534. /package/apps/pwa/out/_next/static/{-6XrAoD3eU7NyMHAE4xPS → x6mjI6vqOkVVu0jFuDkz1}/_ssgManifest.js +0 -0
@@ -2,14 +2,16 @@ import { execFileSync } from 'node:child_process';
2
2
  import { dlopen, FFIType } from 'bun:ffi';
3
3
  import { createHash } from 'node:crypto';
4
4
  import { existsSync, readFileSync } from 'node:fs';
5
- import { constants as osConstants } from 'node:os';
6
- import { resolve, sep } from 'node:path';
5
+ import { constants as osConstants, hostname } from 'node:os';
6
+ import { basename, dirname, resolve, sep } from 'node:path';
7
7
  import { findGitDir } from '../git-fs/locate.js';
8
8
  import { LogStore, logsDbPath } from '../mss/logging/log-store.js';
9
9
  import { loadSelfDevRun } from '../self-dev/run-store.js';
10
10
  import { normalizeRunId } from './harness-space.js';
11
- import { effectiveInstanceRoot } from '../instance/resolve.js';
12
- import { detectBursts, formatIncidentBurstWarning, readRunExits, recentBurst, recordRunExit } from './harness-incidents.js';
11
+ import { effectiveInstanceRoot, prodInstanceRoot } from '../instance/resolve.js';
12
+ import { lookupGoal, type GoalLookupOptions, type GoalLookupResult } from '../self-implement/goal-lookup.js';
13
+ import { archiveGoals, type GoalArchiveOptions } from '../self-implement/goal-archive.js';
14
+ import { detectBursts, formatIncidentBurstWarning, incidentLastLines, readRunExits, recentBurst, recordRunExit } from './harness-incidents.js';
13
15
  import { Command, Option } from 'commander';
14
16
  import { runGitCommand } from '../git-fs/runner.js';
15
17
  import { GOAL_TYPES, lintGoalFile, parseGoalId, parseGoalType, tracedPathReferences, type GoalType } from '../self-implement/goal-author.js';
@@ -29,6 +31,7 @@ import { DEFAULT_FALLBACK_CHAIN } from '../oauth/fallback-chain.js';
29
31
  import { installDeliverableVerifyCliCommand, type InstallDeliverableVerifyCliDeps } from './deliverable-verify-cli.js';
30
32
  import { getUserConfig } from '../user-config.js';
31
33
  import { installHarnessCliSinkHook } from './harness-cli-sink.js';
34
+ import { addHarnessQueue, listHarnessQueue, queueSeatForCwd, reconcileHarnessQueue, removeHarnessQueue, tickHarnessQueue, type HarnessQueueDeps, type QueueSeat } from './harness-queue.js';
32
35
  import { resolveHarnessSubstrate, type ResolvedHarnessSubstrate } from './harness-substrate-default.js';
33
36
  import { runHarnessPlanRfc } from './harness-plan-rfc.js';
34
37
  import { classifyGarbage, isGarbageProcessTarget, type GarbageProcess } from './process-garbage.js';
@@ -91,6 +94,7 @@ export class HarnessCliInputError extends Error {
91
94
  }
92
95
 
93
96
  export interface HarnessAskSayOptions {
97
+ seat?: QueueSeat;
94
98
  json?: boolean;
95
99
  base?: string;
96
100
  target?: string;
@@ -252,6 +256,7 @@ function registerHarnessAskSayOptions(command: Command): Command {
252
256
  .option('--target <path>', 'self: harness가 작업할 레포 또는 디렉터리(self-mission 전용)')
253
257
  .option('--yes', 'git 아닌 프로젝트에 git init 및 첫 커밋을 만들도록 동의')
254
258
  .option('--correlation <id>', '요청과 런을 잇는 불투명 correlation 값')
259
+ .addOption(new Option('--seat <seat>', '런 귀속 자리 (OP|TC|MK|UX)').choices(['OP', 'TC', 'MK', 'UX']))
255
260
  .addOption(new Option('--goal-type <type>', `골 종류 (${GOAL_TYPES.join('|')})`).choices([...GOAL_TYPES]))
256
261
  .option('--force-preflight', '전제 검사 막힘을 명시 요청으로 우회(관측에 남음)')
257
262
  .option('--force-gate', '같은 골 실행 중 막힘을 명시 요청으로 우회(관측에 남음; 예산 막힘은 우회 불가)')
@@ -283,6 +288,7 @@ function registerHarnessMissionOptions(command: Command): Command {
283
288
  function normalizeHarnessCommonOptions(opts: HarnessAskSayOptions): HarnessAskSayOptions {
284
289
  return {
285
290
  ...(opts.json ? { json: true } : {}),
291
+ ...(opts.seat !== undefined ? { seat: opts.seat } : {}),
286
292
  ...(opts.base !== undefined ? { base: opts.base } : {}),
287
293
  ...(opts.target !== undefined ? { target: opts.target } : {}),
288
294
  ...(opts.yes === true ? { yes: true } : {}),
@@ -327,7 +333,7 @@ function assertHarnessChildLlmModel(opts: HarnessAskSayChildLlmOptions): void {
327
333
  }
328
334
 
329
335
  /** Shared post-parse gate: validate child model before any ask/say early return (including `--dry-run`). */
330
- type HarnessSubstrateOpts = { substrate?: 'local' | 'pod'; podPool?: string; podMemory?: string; after?: string; autoMerge?: boolean; base?: string; json?: boolean; target?: string; source?: string };
336
+ type HarnessSubstrateOpts = { substrate?: 'local' | 'pod'; podPool?: string; podMemory?: string; after?: string; autoMerge?: boolean; base?: string; json?: boolean; target?: string; source?: string; seat?: QueueSeat };
331
337
  function resolveLaunchSubstrate(opts: HarnessSubstrateOpts): ResolvedHarnessSubstrate {
332
338
  const resolved = resolveHarnessSubstrate({ flag: opts, config: getUserConfig(), env: process.env });
333
339
  debug.log('harness.substrate', 'resolved', resolved);
@@ -337,7 +343,17 @@ function resolveLaunchSubstrate(opts: HarnessSubstrateOpts): ResolvedHarnessSubs
337
343
  return resolved;
338
344
  }
339
345
  type PodExit = { status: number | null; signal?: string | null };
340
- type PodExitReason = 'signal' | 'human-stop' | 'supervisor' | 'no-launch' | 'unknown';
346
+ type PodExitReason = 'signal' | 'human-stop' | 'supervisor' | 'no-launch' | 'unknown'
347
+ | 'pod-error' | 'launch-gate-blocked' | 'ENOBUFS' | 'OOMKilled';
348
+
349
+ function unknownPodExitTailReason(output: string): PodExitReason | undefined {
350
+ const tail = output.slice(-16_000);
351
+ if (/\bOOMKilled\b/i.test(tail)) return 'OOMKilled';
352
+ if (/\bENOBUFS\b/i.test(tail)) return 'ENOBUFS';
353
+ if (/launch gate[^\r\n]*blocked|blocked[^\r\n]*launch gate/i.test(tail)) return 'launch-gate-blocked';
354
+ if (/\bpod-error\b/i.test(tail)) return 'pod-error';
355
+ return undefined;
356
+ }
341
357
  /** GitHub lists at most this many files for one pull request; a list that reaches it is not provably complete. */
342
358
  export const GITHUB_PR_FILES_LIST_CAP = 3000;
343
359
  const STOPPING_SUPERVISOR_VERDICTS = new Set(['UNCONVERGEABLE', 'CONTRACT-CONFLICT']);
@@ -440,34 +456,47 @@ export function classifyHarnessPodExit(
440
456
  try { applied = (deps.hasJobApplied ?? hasHarnessPodJobApplied)(runId, output); } catch { /* unknown */ }
441
457
  const noLaunch = ledgerReadable && !ledger?.some((entry) => LAUNCH_EVIDENCE_EVENTS.has(entry.event))
442
458
  && applied === false && !pr;
443
- reason = noLaunch ? 'no-launch' : 'unknown';
459
+ reason = noLaunch ? 'no-launch' : unknownPodExitTailReason(output) ?? 'unknown';
444
460
  line = noLaunch
445
461
  ? 'Pod 실행에 닿지 못했다 — 풀·컨텍스트·SSH 연결을 확인하거나 `--substrate local` 로 명시하라'
446
- : 'Pod 실행 여부 또는 종료 이유를 확인하지 못했다';
462
+ : reason === 'unknown' ? 'Pod 실행 여부 또는 종료 이유를 확인하지 못했다'
463
+ : `Pod 종료 출력에서 확인한 원인: ${reason}`;
447
464
  }
448
465
  try { debug.log('harness.pod', 'exit-classified', { runId, reason, status: exit.status, signal: signal ?? null }); } catch { /* observation is fail-soft */ }
449
466
  return { reason, lines: [line, ...(pr ? [`PR 이 남아 있다: ${pr}`] : [])] };
450
467
  }
451
468
 
452
469
  /** The classified status is the same exit handed to the launcher; ledger failure is never an exit failure. */
453
- export function recordClassifiedHarnessPodExit(runId: string | undefined, reason: string, status: number): void {
470
+ export function recordClassifiedHarnessPodExit(runId: string | undefined, reason: string, status: number,
471
+ context: { entrance?: string; seat?: string; output?: string } = {}): void {
454
472
  if (!runId) return;
455
473
  try {
456
474
  const signal = status > 128
457
475
  ? Object.entries(osConstants.signals).find(([, number]) => number === status - 128)?.[0] ?? null : null;
458
- recordRunExit({ runId, reason, status, signal, at: new Date().toISOString() }, effectiveInstanceRoot());
476
+ const cwd = process.cwd();
477
+ const seat = context.seat ?? process.env.ELANOUS_HARNESS_SEAT;
478
+ recordRunExit({ runId, reason, status, signal, at: new Date().toISOString(),
479
+ ...(context.entrance ? { entrance: context.entrance } : {}),
480
+ ...(['OP', 'TC', 'MK', 'UX'].includes(seat ?? '') ? { seat } : {}),
481
+ cwd: `${basename(dirname(cwd))}/${basename(cwd)}`, hostname: hostname().split('.')[0]!, pid: process.pid,
482
+ ...(context.output ? { lastLines: incidentLastLines(context.output) } : {}),
483
+ }, effectiveInstanceRoot());
459
484
  } catch { /* incident writes cannot change Pod output or exit */ }
460
485
  }
461
486
 
462
487
  /** Every Pod launch entrance (harness ask/say · dev ask --substrate pod) warns once after a recent burst; it never blocks the launch. */
463
488
  export function warnRecentIncidentBurst(
464
489
  root: string = effectiveInstanceRoot(), now: Date = new Date(), write: (line: string) => void = (line) => console.error(line),
490
+ context: { seat?: string; entrance?: string } = {},
465
491
  ): boolean {
466
492
  try {
467
- const burst = recentBurst(root, now);
493
+ const seat = context.seat ?? process.env.ELANOUS_HARNESS_SEAT;
494
+ const burst = recentBurst(root, now, { entrance: context.entrance,
495
+ ...(['OP', 'TC', 'MK', 'UX'].includes(seat ?? '') ? { seat } : {}) });
468
496
  if (!burst) return false;
469
497
  write(formatIncidentBurstWarning(burst));
470
- try { debug.log('harness.incident', 'burst', { reason: burst.reason, count: burst.count }); } catch { /* observation is fail-soft */ }
498
+ try { debug.log('harness.incident', 'burst', { reason: burst.reason, count: burst.count,
499
+ ...(burst.seat ? { seat: burst.seat } : {}), ...(burst.entrance ? { entrance: burst.entrance } : {}) }); } catch { /* observation is fail-soft */ }
471
500
  return true;
472
501
  } catch { return false; /* incident reads cannot block a launch */ }
473
502
  }
@@ -539,7 +568,8 @@ async function onPod(opts: unknown, entrance: 'cli-harness-ask' | 'cli-harness-s
539
568
  process.exitCode = 2;
540
569
  return;
541
570
  }
542
- warnRecentIncidentBurst();
571
+ warnRecentIncidentBurst(effectiveInstanceRoot(), new Date(), (line) => console.error(line),
572
+ { entrance, seat: o.seat });
543
573
  let dispatchRecorded = false;
544
574
  try {
545
575
  const goalText = entrance === 'cli-harness-ask' ? readFileSync(input, 'utf8') : input;
@@ -558,14 +588,15 @@ async function onPod(opts: unknown, entrance: 'cli-harness-ask' | 'cli-harness-s
558
588
  const { dispatchHarnessOnPod } = await import('./harness-pod-dispatch.js');
559
589
  let output = '';
560
590
  let runId: string | undefined;
561
- const status = await dispatchHarnessOnPod({ entrance, input, podPool: pool, ...(dispatchRecorded ? { dispatchRecorded: true } : {}), ...(o.podMemory ? { podMemory: o.podMemory } : {}), ...(o.after ? { after: o.after } : {}), ...(o.autoMerge === false ? { autoMerge: false } : {}), ...(o.base ? { base: o.base } : {}), ...(o.json ? { json: true } : {}), ...(o.source ? { source: o.source } : {}) },
591
+ const status = await dispatchHarnessOnPod({ entrance, input, podPool: pool, ...(dispatchRecorded ? { dispatchRecorded: true } : {}), ...(o.podMemory ? { podMemory: o.podMemory } : {}), ...(o.after ? { after: o.after } : {}), ...(o.autoMerge === false ? { autoMerge: false } : {}), ...(o.base ? { base: o.base } : {}), ...(o.json ? { json: true } : {}), ...(o.source ? { source: o.source } : {}), ...(o.seat ? { seat: o.seat } : {}) },
562
592
  { onOutput: (text) => {
563
593
  runId ??= harnessPodRunId(output + text);
564
594
  output = (output + text).slice(-16_000);
565
595
  } });
566
596
  if (status !== 0) {
567
597
  const classified = classifyHarnessPodExit({ status }, output, { ...(runId ? { runId } : {}) });
568
- recordClassifiedHarnessPodExit(runId ?? harnessPodRunId(output), classified.reason, status);
598
+ recordClassifiedHarnessPodExit(runId ?? harnessPodRunId(output), classified.reason, status,
599
+ { entrance, seat: o.seat, output });
569
600
  for (const line of classified.lines) console.error(line);
570
601
  process.exitCode = status;
571
602
  }
@@ -633,7 +664,7 @@ async function dispatchHarnessAskSay(
633
664
  readonly wouldStart: string;
634
665
  readonly goalPath?: string;
635
666
  },
636
- dispatch: (resolved: ResolvedHarnessSubstrate) => Promise<void>,
667
+ dispatch: (resolved: ResolvedHarnessSubstrate, stampedOpts: HarnessAskSayChildLlmOptions & HarnessDryRunOpts) => Promise<void>,
637
668
  gate: PreLaunchGateDeps & { readBudget?: () => Promise<BudgetDecision | 'unknown'> } = {},
638
669
  ): Promise<void> {
639
670
  await runInjectedHarnessHandler(async () => {
@@ -643,6 +674,25 @@ async function dispatchHarnessAskSay(
643
674
  printHarnessLaunchDryRun(dryRunPreview);
644
675
  return;
645
676
  }
677
+ // Resolve the launch directory once; descendants and the checkpoint retain the same identity.
678
+ const queued = process.env.ELANOUS_HARNESS_QUEUE_LAUNCH && process.env.ELANOUS_HARNESS_SEAT;
679
+ const queueSeat = queued === 'OP' || queued === 'TC' || queued === 'MK' || queued === 'UX' ? queued : undefined;
680
+ const assigned = opts.seat ?? queueSeat ?? queueSeatForCwd(process.cwd(), getUserConfig().loops?.orchestrator?.seatTrees);
681
+ const inheritedSeat = process.env.ELANOUS_HARNESS_SEAT;
682
+ const podSeat = resolved.substrate === 'pod' && ['OP', 'TC', 'MK', 'UX'].includes(inheritedSeat ?? '')
683
+ ? inheritedSeat as QueueSeat : undefined;
684
+ const stampedSeat = resolved.substrate === 'pod' ? opts.seat ?? queueSeat ?? podSeat ?? assigned : assigned;
685
+ const stampedOpts = stampedSeat ? { ...opts, seat: stampedSeat } : opts;
686
+ const launch = async () => {
687
+ const previous = process.env.ELANOUS_HARNESS_SEAT;
688
+ if (stampedSeat) process.env.ELANOUS_HARNESS_SEAT = stampedSeat;
689
+ else delete process.env.ELANOUS_HARNESS_SEAT;
690
+ try { await dispatch(resolved, stampedOpts); }
691
+ finally {
692
+ if (previous === undefined) delete process.env.ELANOUS_HARNESS_SEAT;
693
+ else process.env.ELANOUS_HARNESS_SEAT = previous;
694
+ }
695
+ };
646
696
  // Without a GoalId, match the request to a live run's exact verbatim original ask; still check budget.
647
697
  let identity = dryRunPreview.input;
648
698
  let goalId: string | null = null;
@@ -657,7 +707,7 @@ async function dispatchHarnessAskSay(
657
707
  // CLI wiring tests time out on them. Gate tests inject deps or opt in with ELANOUS_LAUNCH_GATE_LIVE=1.
658
708
  if (process.env.NODE_ENV === 'test' && Object.keys(gate).length === 0 && process.env.ELANOUS_LAUNCH_GATE_LIVE !== '1') {
659
709
  try { debug.log('execution-loop.launch-gate', 'skipped-test-env', { goalId }); } catch { /* observation is fail-soft */ }
660
- await dispatch(resolved);
710
+ await launch();
661
711
  return;
662
712
  }
663
713
  const budget = await (gate.readBudget ?? (() => readHarnessLaunchBudget(opts)))();
@@ -672,7 +722,7 @@ async function dispatchHarnessAskSay(
672
722
  ...(decision.budget === 'unknown' ? ['budget unknown'] : decision.budget.action === 'next-provider' ? [decision.budget.reasons.join(' · ') || 'next-provider'] : []),
673
723
  ];
674
724
  if (warnings.length > 0) console.error(`⚠️ launch gate: ${warnings.join(' · ').split(/\r?\n/, 1)[0]}`);
675
- await dispatch(resolved);
725
+ await launch();
676
726
  });
677
727
  }
678
728
 
@@ -1471,7 +1521,7 @@ export function readProcessOwnership(
1471
1521
  }
1472
1522
  }
1473
1523
 
1474
- function defaultListHarnessProcesses(): HarnessProcessListObservation {
1524
+ export function defaultListHarnessProcesses(): HarnessProcessListObservation {
1475
1525
  let out: string;
1476
1526
  try {
1477
1527
  out = execFileSync('ps', ['-axo', 'pid=,ppid=,pcpu=,etime=,command='], {
@@ -1605,6 +1655,30 @@ const executeGh: GhExecute = (args) => execFileSync('gh', args, {
1605
1655
  stdio: ['ignore', 'pipe', 'pipe'], env: process.env,
1606
1656
  });
1607
1657
 
1658
+ /** Merge-time adapter: reuse the sweep's GitHub inventory and worktree liveness without a second triage implementation. */
1659
+ export async function supersedeMergedGoalDrafts(
1660
+ number: number, cwd: string,
1661
+ execute: GhExecute = executeGh, git: DraftSweepGitExecute = runGitCommand,
1662
+ ): Promise<void> {
1663
+ const remote = git(cwd, ['config', '--get', 'remote.origin.url']);
1664
+ const repository = remote.status === 0 ? githubRemoteRepository(remote.stdout) : undefined;
1665
+ if (!repository) throw new Error('Merge repository unavailable');
1666
+ // The PR can change between open and merge; never authorize a close using locally reconstructed metadata.
1667
+ const pr = ghJson<GithubPull>(['api', `repos/${repository}/pulls/${number}`], execute);
1668
+ if (!pr || pr.number !== number || pr.state !== 'closed' || !pr.merged_at || !pr.title?.trim()
1669
+ || !pr.head?.ref?.trim() || !pr.created_at || !Array.isArray(pr.labels)
1670
+ || !Number.isFinite(Date.parse(pr.created_at)) || !Number.isFinite(Date.parse(pr.merged_at))
1671
+ || Date.parse(pr.merged_at) < Date.parse(pr.created_at)) {
1672
+ throw new Error('Merged PR metadata unavailable');
1673
+ }
1674
+ const merged: SweepMergedPr & { createdAt: string; mergedAt: string } = {
1675
+ number: pr.number, title: pr.title, body: pr.body ?? undefined, branch: pr.head.ref,
1676
+ createdAt: pr.created_at, mergedAt: pr.merged_at,
1677
+ };
1678
+ const { supersedeDraftsOnMerge } = await import('../self-dev/draft-sweep.js');
1679
+ await supersedeDraftsOnMerge({ repository, merged, adapters: githubDraftSweepAdapters(execute, git, cwd) });
1680
+ }
1681
+
1608
1682
  function ghJson<T>(args: string[], execute: GhExecute): T {
1609
1683
  return JSON.parse(execute(args)) as T;
1610
1684
  }
@@ -1669,7 +1743,7 @@ function githubRemoteRepository(url: string): string | undefined {
1669
1743
  return match?.[1];
1670
1744
  }
1671
1745
 
1672
- export function githubDraftSweepAdapters(execute: GhExecute = executeGh, git: DraftSweepGitExecute = runGitCommand): DraftSweepAdapters {
1746
+ export function githubDraftSweepAdapters(execute: GhExecute = executeGh, git: DraftSweepGitExecute = runGitCommand, cwd = process.cwd()): DraftSweepAdapters {
1673
1747
  const inventory = async (repository: string, state: 'open' | 'closed'): Promise<GithubPull[]> => {
1674
1748
  // ⛔ A large repository has tens of thousands of closed PRs (2026-09-28: 21k → «exceeded pagination limit», no sweep at all).
1675
1749
  // Merged twins only matter after the oldest open draft was opened, so the closed listing stops at that time.
@@ -1762,6 +1836,7 @@ export function githubDraftSweepAdapters(execute: GhExecute = executeGh, git: Dr
1762
1836
  return draftSweepRunStatus(ledgerMatches, running, repository, draft.number);
1763
1837
  };
1764
1838
  return {
1839
+ getPrFiles: async (repository, number) => filesFor(repository, number),
1765
1840
  listDrafts: async (page, perPage, repository): Promise<SweepDraft[]> =>
1766
1841
  (await listed(open, repository, 'open')).filter((pr) => pr.draft)
1767
1842
  .slice((page - 1) * perPage, page * perPage)
@@ -1810,7 +1885,6 @@ export function githubDraftSweepAdapters(execute: GhExecute = executeGh, git: Dr
1810
1885
  } finally { store.close(); }
1811
1886
  },
1812
1887
  listLiveBranches: async (repository) => {
1813
- const cwd = process.cwd();
1814
1888
  const remote = git(cwd, ['config', '--get', 'remote.origin.url']);
1815
1889
  if (remote.status !== 0 || githubRemoteRepository(remote.stdout)?.toLowerCase() !== repository.toLowerCase()) {
1816
1890
  return undefined;
@@ -1919,6 +1993,55 @@ function installHarnessDraftSweepCommand(harnessCmd: Command, deps: HarnessDraft
1919
1993
  });
1920
1994
  }
1921
1995
 
1996
+ function renderHarnessGoal(result: GoalLookupResult, universe: '운영' | '시험'): string {
1997
+ const matchedRun = result.runState?.results.find((entry) => entry.runId === result.runId);
1998
+ return [
1999
+ `골: ${result.goalBody?.split(/\r?\n/, 1)[0] || '골 문서에 없음'}`,
2000
+ `runId: ${result.runId ?? '원장에 없음'}`,
2001
+ `칸 id: ${result.kanId ?? '기록 없음'}`,
2002
+ `PR: ${result.record?.prNumber != null ? `#${result.record.prNumber}` : '기록 없음'}`,
2003
+ `stage/결과: ${result.record?.stage ?? '기록 없음'} / ${result.record?.outcome ?? '기록 없음'}`,
2004
+ `종결 사유: ${result.runState?.supervisorStopReason ?? matchedRun?.error?.message ?? '기록 없음'}`,
2005
+ `찾은 우주: ${universe}`,
2006
+ `원장: ${result.runState ? '찾음' : '원장에 없음'}`,
2007
+ `골 문서: ${result.goalFile ? '찾음' : '골 문서에 없음'}`,
2008
+ ].join('\n');
2009
+ }
2010
+
2011
+ function installHarnessGoalCommand(harnessCmd: Command, options?: GoalLookupOptions, archiveOptions?: GoalArchiveOptions): Command {
2012
+ const goal = harnessCmd.command('goal').usage('<reference> | archive [--before <date>] [--apply]')
2013
+ .description('골 문서와 실행 결과를 조회하거나 보관한다');
2014
+ goal.command('archive')
2015
+ .description('종료된 골 문서를 미리 보거나 상태 루트에 보관한다 — 저장소는 건드리지 않고 지울 목록(filesForPr)만 낸다')
2016
+ .option('--before <date>', '완료 시각이 이 YYYY-MM-DD 이전인 골만')
2017
+ .option('--apply', '상태 루트에 보관 복사·색인 (저장소 삭제·스테이지·커밋 없음 · 지우기는 filesForPr 로 사람이 PR)')
2018
+ .option('--trust-doc-record', '런 원장이 없을 때 문서에 적힌 종료 실행 기록을 신뢰')
2019
+ .action((opts: { before?: string; apply?: boolean; trustDocRecord?: boolean }) => {
2020
+ try {
2021
+ const result = archiveGoals({ ...archiveOptions, before: opts.before, apply: opts.apply, trustDocRecord: opts.trustDocRecord });
2022
+ console.log(JSON.stringify(result));
2023
+ } catch (error) {
2024
+ console.error(humanErrorLine(error));
2025
+ process.exitCode = 1;
2026
+ }
2027
+ });
2028
+ goal.command('show <reference>', { isDefault: true })
2029
+ .description('PR 번호 또는 runId 로 골 문서와 실행 결과를 조회한다 (읽기 전용)')
2030
+ .option('--json', 'GoalLookupResult 그대로 출력')
2031
+ .action((reference: string, opts: { json?: boolean }) => {
2032
+ try {
2033
+ const result = lookupGoal(reference, options);
2034
+ const universe = effectiveInstanceRoot() === prodInstanceRoot() ? '운영' : '시험';
2035
+ console.log(opts.json ? JSON.stringify(result) : renderHarnessGoal(result, universe));
2036
+ if (!result.goalFile || !result.runState) process.exitCode = 1;
2037
+ } catch (error) {
2038
+ console.error(humanErrorLine(error));
2039
+ process.exitCode = 1;
2040
+ }
2041
+ });
2042
+ return goal;
2043
+ }
2044
+
1922
2045
  function installHarnessBudgetCommand(harnessCmd: Command): Command {
1923
2046
  return harnessCmd
1924
2047
  .command('budget')
@@ -2104,6 +2227,9 @@ export interface HarnessCliCommandDeps {
2104
2227
  deliverableVerify?: InstallDeliverableVerifyCliDeps;
2105
2228
  processObservation?: HarnessProcessObservationDeps;
2106
2229
  draftSweep?: HarnessDraftSweepDeps;
2230
+ queue?: HarnessQueueDeps;
2231
+ goalLookup?: GoalLookupOptions;
2232
+ goalArchive?: GoalArchiveOptions;
2107
2233
  ask?: HarnessAskHandler;
2108
2234
  say?: HarnessSayHandler;
2109
2235
  /** Observe-only Pod host dispatch seam. */
@@ -2144,8 +2270,8 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
2144
2270
  const bursts = detectBursts(rows).filter((burst) => Date.parse(burst.lastAt) >= now - since * 60_000 && Date.parse(burst.lastAt) <= now);
2145
2271
  if (opts.json) console.log(JSON.stringify({ rows: selected, bursts }));
2146
2272
  else {
2147
- for (const row of selected) console.log(`${row.at} run ${row.runId} · ${row.reason} · status=${row.status} signal=${row.signal ?? '-'}`);
2148
- for (const burst of bursts) console.log(`${burst.firstAt}–${burst.lastAt} 묶음 ${burst.reason} ×${burst.count} · ${burst.runIds.join(', ')}`);
2273
+ for (const row of selected) console.log(`${row.at} run ${row.runId} · seat=${row.seat ?? '-'} entrance=${row.entrance ?? '-'} reason=${row.reason} · status=${row.status} signal=${row.signal ?? '-'}`);
2274
+ for (const burst of bursts) console.log(`${burst.firstAt}–${burst.lastAt} 묶음 ${burst.reason} ×${burst.count} · seat=${burst.seat ?? '-'} entrance=${burst.entrance ?? '-'} · ${burst.runIds.join(', ')}`);
2149
2275
  }
2150
2276
  } catch (error) {
2151
2277
  console.error(humanErrorLine(error));
@@ -2155,7 +2281,32 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
2155
2281
  installDeliverableVerifyCliCommand(harnessCmd, deps.deliverableVerify);
2156
2282
  installHarnessProcessObservationCommand(harnessCmd, deps.processObservation);
2157
2283
  installHarnessBudgetCommand(harnessCmd);
2284
+ installHarnessGoalCommand(harnessCmd, deps.goalLookup, deps.goalArchive);
2158
2285
  installHarnessDraftSweepCommand(harnessCmd, deps.draftSweep);
2286
+ const queue = harnessCmd.command('queue').description('자리별 영속 발사 대기열');
2287
+ const queueDeps = deps.queue ?? {};
2288
+ const queueAction = (action: () => Promise<void>): Promise<void> => runInjectedHarnessHandler(action);
2289
+ queue.command('add').requiredOption('--seat <seat>', 'OP|TC|MK|UX')
2290
+ .option('--say <sentence>', '원문 문장').option('--ask <goal-path>', '골 문서 경로')
2291
+ .option('--hold', '자동 병합 금지').option('--heavy', 'Pod high 메모리')
2292
+ .action((opts: { seat: string; say?: string; ask?: string; hold?: boolean; heavy?: boolean }) => queueAction(async () => {
2293
+ const row = await addHarnessQueue(opts, queueDeps);
2294
+ console.log(`${row.id} ${row.seat} ${row.kind} queued`);
2295
+ }));
2296
+ queue.command('list').description('대기·발사 원장 조회').action(() => queueAction(async () => {
2297
+ for (const row of listHarnessQueue(queueDeps)) console.log(`${row.id} ${row.seat} ${row.status} ${row.kind} ${row.input}`);
2298
+ }));
2299
+ queue.command('remove <id>').description('대기 중 또는 종료 확인된 항목 제거').action((id: string) => queueAction(async () => {
2300
+ if (!await removeHarnessQueue(id, queueDeps)) throw new Error(`queue item active or not found: ${id}`);
2301
+ console.log(`${id} removed`);
2302
+ }));
2303
+ queue.command('reconcile <id>').description('불확정 발사를 확인하고 종료 또는 미발사 증거가 있으면 예약 해소').action((id: string) => queueAction(async () => {
2304
+ console.log(`${id} ${await reconcileHarnessQueue(id, queueDeps)}`);
2305
+ }));
2306
+ queue.command('tick').description('맨 앞 한 건의 자리 몫과 Pod 풀 여유를 확인하고 발사').action(() => queueAction(async () => {
2307
+ const result = await tickHarnessQueue(queueDeps);
2308
+ console.log(`${result.outcome}${result.item ? ` ${result.item.id}` : ''}: ${result.reason}`);
2309
+ }));
2159
2310
 
2160
2311
  const ask = deps.ask;
2161
2312
  if (ask) {
@@ -2171,13 +2322,13 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
2171
2322
  ...(opts.goalType !== undefined ? { goalType: opts.goalType } : {}),
2172
2323
  ...(opts.target !== undefined ? { target: opts.target } : {}),
2173
2324
  },
2174
- (resolved) => {
2325
+ (resolved, stampedOpts) => {
2175
2326
  if (resolved.substrate !== 'pod' && (opts as { source?: string }).source !== undefined) {
2176
2327
  console.error('`--source` 는 `--substrate pod` 와 함께');
2177
2328
  process.exitCode = 2;
2178
2329
  return Promise.resolve();
2179
2330
  }
2180
- return resolved.substrate === 'pod' ? onPod(opts, 'cli-harness-ask', goalPath, resolved.pool!, deps.podDispatchTask) : ask(goalPath, normalizeHarnessAskSayOptions(opts));
2331
+ return resolved.substrate === 'pod' ? onPod(stampedOpts, 'cli-harness-ask', goalPath, resolved.pool!, deps.podDispatchTask) : ask(goalPath, normalizeHarnessAskSayOptions(stampedOpts));
2181
2332
  },
2182
2333
  deps.launchGate,
2183
2334
  );
@@ -2196,13 +2347,13 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
2196
2347
  wouldStart: '골 문서 · 워크트리 · 브랜치 · 자식 · 파이프라인',
2197
2348
  ...(opts.target !== undefined ? { target: opts.target } : {}),
2198
2349
  },
2199
- (resolved) => {
2350
+ (resolved, stampedOpts) => {
2200
2351
  if (resolved.substrate !== 'pod' && (opts as { source?: string }).source !== undefined) {
2201
2352
  console.error('`--source` 는 `--substrate pod` 와 함께');
2202
2353
  process.exitCode = 2;
2203
2354
  return Promise.resolve();
2204
2355
  }
2205
- return resolved.substrate === 'pod' ? onPod(opts, 'cli-harness-say', sentence.join(' '), resolved.pool!, deps.podDispatchTask) : say(sentence, normalizeHarnessAskSayOptions(opts));
2356
+ return resolved.substrate === 'pod' ? onPod(stampedOpts, 'cli-harness-say', sentence.join(' '), resolved.pool!, deps.podDispatchTask) : say(sentence, normalizeHarnessAskSayOptions(stampedOpts));
2206
2357
  },
2207
2358
  deps.launchGate,
2208
2359
  );
@@ -1,6 +1,7 @@
1
1
  import { Database } from 'bun:sqlite';
2
2
  import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync } from 'node:fs';
3
3
  import { join } from 'node:path';
4
+ import { redactSecretText } from '../debug/log.js';
4
5
 
5
6
  export interface RunExit {
6
7
  runId: string;
@@ -8,6 +9,12 @@ export interface RunExit {
8
9
  status: number | null;
9
10
  signal: string | null;
10
11
  at: string;
12
+ entrance?: string;
13
+ seat?: string;
14
+ cwd?: string;
15
+ hostname?: string;
16
+ pid?: number;
17
+ lastLines?: string[];
11
18
  }
12
19
 
13
20
  export interface IncidentBurst {
@@ -16,6 +23,14 @@ export interface IncidentBurst {
16
23
  firstAt: string;
17
24
  lastAt: string;
18
25
  runIds: string[];
26
+ entrance?: string;
27
+ seat?: string;
28
+ }
29
+
30
+ /** Scrub before bounding: a token crossing the 200-character boundary must not leak. */
31
+ export function incidentLastLines(output: string): string[] {
32
+ return output.split(/\r?\n/).filter((line) => line.trim().length > 0).slice(-3)
33
+ .map((line) => redactSecretText(line).slice(0, 200));
19
34
  }
20
35
 
21
36
  const KST_OFFSET_MS = 9 * 60 * 60_000;
@@ -52,7 +67,15 @@ export function readRunExits(root: string): RunExit[] {
52
67
  || !(row.status === null || typeof row.status === 'number')
53
68
  || !(row.signal === null || typeof row.signal === 'string')) continue;
54
69
  seen.add(row.runId);
55
- rows.push(row as RunExit);
70
+ rows.push({ runId: row.runId, reason: row.reason, status: row.status, signal: row.signal, at: row.at,
71
+ ...(typeof row.entrance === 'string' ? { entrance: row.entrance } : {}),
72
+ ...(typeof row.seat === 'string' ? { seat: row.seat } : {}),
73
+ ...(typeof row.cwd === 'string' ? { cwd: row.cwd } : {}),
74
+ ...(typeof row.hostname === 'string' ? { hostname: row.hostname } : {}),
75
+ ...(typeof row.pid === 'number' && Number.isSafeInteger(row.pid) ? { pid: row.pid } : {}),
76
+ ...(Array.isArray(row.lastLines) && row.lastLines.every((line) => typeof line === 'string')
77
+ ? { lastLines: row.lastLines } : {}),
78
+ });
56
79
  }
57
80
  }
58
81
  return rows.sort((a, b) => Date.parse(a.at) - Date.parse(b.at) || a.runId.localeCompare(b.runId));
@@ -71,7 +94,10 @@ export function recordRunExit(row: RunExit, root: string): void {
71
94
  lock.exec('BEGIN IMMEDIATE');
72
95
  try {
73
96
  if (!readRunExits(root).some((record) => record.runId === row.runId)) {
74
- appendFileSync(join(dir, `${kstDate(at)}.jsonl`), `${JSON.stringify(row)}\n`);
97
+ const safeRow = row.lastLines === undefined ? row : {
98
+ ...row, lastLines: row.lastLines.slice(-3).map((line) => redactSecretText(line).slice(0, 200)),
99
+ };
100
+ appendFileSync(join(dir, `${kstDate(at)}.jsonl`), `${JSON.stringify(safeRow)}\n`);
75
101
  }
76
102
  lock.exec('COMMIT');
77
103
  } catch (error) {
@@ -83,21 +109,23 @@ export function recordRunExit(row: RunExit, root: string): void {
83
109
  }
84
110
  }
85
111
 
86
- /** Non-overlapping, maximal same-reason windows; no run contributes to two bursts. */
112
+ /** Non-overlapping, maximal same-reason/seat/entrance windows; no run contributes to two bursts. */
87
113
  export function detectBursts(rows: readonly RunExit[], { windowMs = 60_000, threshold = 3 }: {
88
114
  windowMs?: number; threshold?: number;
89
115
  } = {}): IncidentBurst[] {
90
116
  const bursts: IncidentBurst[] = [];
91
- const byReason = new Map<string, RunExit[]>();
117
+ const byIdentity = new Map<string, RunExit[]>();
92
118
  const seen = new Set<string>();
93
119
  for (const row of rows) {
94
120
  if (!row.runId || seen.has(row.runId) || !Number.isFinite(Date.parse(row.at))) continue;
95
121
  seen.add(row.runId);
96
- const group = byReason.get(row.reason) ?? [];
122
+ const key = JSON.stringify([row.reason, row.seat ?? null, row.entrance ?? null]);
123
+ const group = byIdentity.get(key) ?? [];
97
124
  group.push(row);
98
- byReason.set(row.reason, group);
125
+ byIdentity.set(key, group);
99
126
  }
100
- for (const [reason, group] of byReason) {
127
+ for (const group of byIdentity.values()) {
128
+ const { reason, seat, entrance } = group[0]!;
101
129
  group.sort((a, b) => Date.parse(a.at) - Date.parse(b.at) || a.runId.localeCompare(b.runId));
102
130
  for (let start = 0; start < group.length;) {
103
131
  let end = start + 1;
@@ -105,25 +133,28 @@ export function detectBursts(rows: readonly RunExit[], { windowMs = 60_000, thre
105
133
  if (end - start < threshold) { start++; continue; }
106
134
  const members = group.slice(start, end);
107
135
  bursts.push({ reason, count: members.length, firstAt: members[0]!.at,
108
- lastAt: members[members.length - 1]!.at, runIds: members.map((row) => row.runId) });
136
+ lastAt: members[members.length - 1]!.at, runIds: members.map((row) => row.runId),
137
+ ...(seat ? { seat } : {}), ...(entrance ? { entrance } : {}) });
109
138
  start = end;
110
139
  }
111
140
  }
112
141
  return bursts.sort((a, b) => Date.parse(a.firstAt) - Date.parse(b.firstAt) || a.reason.localeCompare(b.reason));
113
142
  }
114
143
 
115
- export function recentBurst(root: string, now: Date | number, { lookbackMs = 15 * 60_000 }: {
116
- lookbackMs?: number;
144
+ export function recentBurst(root: string, now: Date | number, { lookbackMs = 15 * 60_000, seat, entrance }: {
145
+ lookbackMs?: number; seat?: string; entrance?: string;
117
146
  } = {}): IncidentBurst | null {
118
147
  const time = typeof now === 'number' ? now : now.getTime();
119
148
  const recent = detectBursts(readRunExits(root)).filter((burst) => {
120
149
  const last = Date.parse(burst.lastAt);
121
- return last <= time && last >= time - lookbackMs;
150
+ return last <= time && last >= time - lookbackMs
151
+ && burst.seat === seat && burst.entrance === entrance;
122
152
  });
123
153
  return recent.sort((a, b) => Date.parse(a.lastAt) - Date.parse(b.lastAt)).at(-1) ?? null;
124
154
  }
125
155
 
126
156
  export function formatIncidentBurstWarning(burst: IncidentBurst): string {
127
157
  const time = new Date(Date.parse(burst.lastAt) + KST_OFFSET_MS).toISOString().slice(11, 16);
128
- return `⚠ 최근 묶음 사고: ${burst.reason} ×${burst.count} (${time} KST) — 원인 확인 전 재발사는 중복·재사고 위험 · elanous harness incidents`;
158
+ const owner = burst.seat || burst.entrance ? ` [seat=${burst.seat ?? '-'} entrance=${burst.entrance ?? '-'}]` : '';
159
+ return `⚠ 최근 묶음 사고${owner}: ${burst.reason} ×${burst.count} (${time} KST) — 원인 확인 전 재발사는 중복·재사고 위험 · elanous harness incidents`;
129
160
  }
@@ -20,6 +20,7 @@ export interface HarnessPodDispatchInput {
20
20
  /** say 의 문장, 또는 ask 의 골 문서 경로. */
21
21
  readonly input: string;
22
22
  readonly podPool?: string;
23
+ readonly seat?: 'OP' | 'TC' | 'MK' | 'UX';
23
24
  /** Host already recorded the observe-only dispatch; suppress a second decision in the Pod. */
24
25
  readonly dispatchRecorded?: boolean;
25
26
  /** Pod 메모리 등급(standard|high) — 오케스트레이터 환경 `ELANOUS_POD_MEMORY_TIER` 로 Job 까지 간다. */
@@ -80,6 +81,7 @@ export function dispatchHarnessOnPod(input: HarnessPodDispatchInput, deps: PodDi
80
81
  const args = podOrchestrateArgs(input, goalFile);
81
82
  debug.log('harness.substrate', 'goal-file', { entrance: input.entrance, mode: tempDir ? 'temp' : 'path' });
82
83
  const env = { ...process.env };
84
+ if (input.seat) env.ELANOUS_HARNESS_SEAT = input.seat;
83
85
  delete env.ELANOUS_POD_GOAL_DOC;
84
86
  delete env.ELANOUS_DISPATCH_RECORDED;
85
87
  if (input.dispatchRecorded) env.ELANOUS_DISPATCH_RECORDED = '1';
@@ -0,0 +1,37 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { renameSync, writeFileSync } from 'node:fs';
4
+
5
+ /** Only a confirmed terminal observation allows reconciliation to release an uncertain launch. */
6
+ export function writeHarnessQueueReceipt(receiptPath: string, state: 'started' | 'finished' | 'not-started', exitCode?: number): void {
7
+ const temp = `${receiptPath}.${randomUUID()}.tmp`;
8
+ writeFileSync(temp, JSON.stringify({ state, at: new Date().toISOString(), ...(exitCode === undefined ? {} : { exitCode }) }), { flag: 'wx', mode: 0o600 });
9
+ renameSync(temp, receiptPath);
10
+ }
11
+
12
+ export async function runHarnessQueueChild(
13
+ receiptPath: string, command: string, args: string[],
14
+ start: typeof spawn = spawn,
15
+ ): Promise<number> {
16
+ const record = (state: 'started' | 'finished' | 'not-started') => writeHarnessQueueReceipt(receiptPath, state);
17
+ let child: ReturnType<typeof spawn>;
18
+ try { child = start(process.execPath, [command, ...args], { stdio: 'inherit', env: process.env }); }
19
+ catch { record('not-started'); return 1; }
20
+ return await new Promise<number>((done) => {
21
+ let started = false;
22
+ child.once('spawn', () => {
23
+ started = true;
24
+ record('started');
25
+ child.once('exit', (code) => { writeHarnessQueueReceipt(receiptPath, 'finished', code ?? 1); done(code ?? 1); });
26
+ });
27
+ child.once('error', () => {
28
+ if (!started) { record('not-started'); done(1); }
29
+ });
30
+ });
31
+ }
32
+
33
+ if (import.meta.main) {
34
+ const [receiptPath, command, ...args] = process.argv.slice(2);
35
+ if (!receiptPath || !command) throw new Error('queue child requires receipt path and command');
36
+ process.exitCode = await runHarnessQueueChild(receiptPath, command, args);
37
+ }