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
@@ -1,8 +1,9 @@
1
1
  import { spawnSync } from 'node:child_process';
2
- import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
3
3
  import { tmpdir } from 'node:os';
4
4
  import { join } from 'node:path';
5
5
  import { NEXT_MD_PATH, resolveNextMdConflict } from '../release-loop/next-md-merge.js';
6
+ import { getDefaultLogStore } from '../mss/logging/log-store.js';
6
7
 
7
8
  type Command = (bin: string, args: readonly string[], cwd: string) => { status: number | null; stdout: string; stderr: string };
8
9
  export type L8MergeQueueInput = { number: number; cwd: string; matchHeadCommit: string };
@@ -13,6 +14,161 @@ export type L8MergeQueueDeps = {
13
14
  acquire?: (cwd: string) => Promise<() => void>;
14
15
  };
15
16
 
17
+ export type L8ShadowVerdict = { number: number; head: string; base?: string; verdict: 'pass' | 'conflict' | 'fail'; detail?: string; tests: string[]; at: string };
18
+ export type L8ShadowQueueState = { pending: { number: number; head: string }[]; verdicts: L8ShadowVerdict[] };
19
+ export type L8ShadowQueueDeps = L8MergeQueueDeps & { observe?: (verdict: L8ShadowVerdict) => void | Promise<void> };
20
+
21
+ function shadowCommand(execute: Command, bin: string, args: string[], cwd: string): string {
22
+ const result = execute(bin, args, cwd);
23
+ if (result.status !== 0) throw new Error(`${bin} ${args.join(' ')}: ${(result.stderr || result.stdout || `exit ${result.status}`).trim().slice(0, 500)}`);
24
+ return args.includes('-z') ? result.stdout : result.stdout.trim();
25
+ }
26
+
27
+ function shadowQueuePath(cwd: string, execute: Command): string {
28
+ const dir = shadowCommand(execute, 'git', ['rev-parse', '--path-format=absolute', '--git-common-dir'], cwd);
29
+ if (!dir) throw new Error('merge queue common git directory unavailable');
30
+ return join(dir, 'elanous-l8-shadow-queue.json');
31
+ }
32
+
33
+ function readShadowQueue(path: string): L8ShadowQueueState {
34
+ if (!existsSync(path)) return { pending: [], verdicts: [] };
35
+ const value: unknown = JSON.parse(readFileSync(path, 'utf8'));
36
+ if (!value || typeof value !== 'object' || !('pending' in value) || !Array.isArray(value.pending)
37
+ || !value.pending.every((p: { number?: number; head?: string }) => Number.isSafeInteger(p?.number) && p.number! > 0 && typeof p.head === 'string' && /^[0-9a-f]{40}$/i.test(p.head))
38
+ || !('verdicts' in value) || !Array.isArray(value.verdicts)) throw new Error('invalid shadow queue ledger');
39
+ return value as L8ShadowQueueState;
40
+ }
41
+
42
+ function writeShadowQueue(path: string, state: L8ShadowQueueState): void {
43
+ const temp = `${path}.${process.pid}.${Date.now()}.tmp`;
44
+ try {
45
+ writeFileSync(temp, JSON.stringify(state, null, 2) + '\n', { flag: 'wx' });
46
+ renameSync(temp, path);
47
+ } finally { rmSync(temp, { force: true }); }
48
+ }
49
+
50
+ async function withShadowQueue<T>(cwd: string, deps: L8ShadowQueueDeps, fn: (state: L8ShadowQueueState, path: string) => Promise<T>): Promise<T> {
51
+ const release = await (deps.acquire ?? acquireL8MergeQueue)(cwd);
52
+ try {
53
+ const path = shadowQueuePath(cwd, deps.command ?? command);
54
+ return await fn(readShadowQueue(path), path);
55
+ } finally { release(); }
56
+ }
57
+
58
+ export function shadowPrNumber(raw: string): number {
59
+ const text = raw.replace(/^#/, '');
60
+ const number = Number(text);
61
+ if (!/^[1-9]\d*$/.test(text) || !Number.isSafeInteger(number)) throw new Error('expected a positive PR number');
62
+ return number;
63
+ }
64
+
65
+ export async function statusL8ShadowQueue(cwd: string, deps: L8ShadowQueueDeps = {}): Promise<L8ShadowQueueState> {
66
+ return withShadowQueue(cwd, deps, async (state) => state);
67
+ }
68
+
69
+ export async function enqueueL8ShadowQueue(cwd: string, number: number, deps: L8ShadowQueueDeps = {}): Promise<L8ShadowQueueState> {
70
+ if (!Number.isSafeInteger(number) || number <= 0) throw new Error('expected a positive PR number');
71
+ return withShadowQueue(cwd, deps, async (state, path) => {
72
+ const execute = deps.command ?? command;
73
+ const view = JSON.parse(shadowCommand(execute, 'gh', ['pr', 'view', String(number), '--json', 'headRefOid,baseRefName,state,isDraft,isCrossRepository'], cwd)) as {
74
+ headRefOid?: string; baseRefName?: string; state?: string; isDraft?: boolean; isCrossRepository?: boolean;
75
+ };
76
+ if (!view.headRefOid || !/^[0-9a-f]{40}$/i.test(view.headRefOid) || view.baseRefName !== 'main'
77
+ || view.state !== 'OPEN' || view.isDraft !== false || view.isCrossRepository !== false) throw new Error('PR is not an open, ready, same-repository main PR');
78
+ if (state.pending.some((p) => p.number === number)) throw new Error(`PR #${number} already queued`);
79
+ state.pending.push({ number, head: view.headRefOid });
80
+ writeShadowQueue(path, state);
81
+ return state;
82
+ });
83
+ }
84
+
85
+ /** Evaluate one pinned queue head against freshly fetched main. Never push or call a merge API. */
86
+ export async function runL8ShadowQueue(cwd: string, deps: L8ShadowQueueDeps = {}): Promise<L8ShadowVerdict | null> {
87
+ return withShadowQueue(cwd, deps, async (state, path) => {
88
+ const item = state.pending[0];
89
+ if (!item) return null;
90
+ const execute = deps.command ?? command;
91
+ const run = (bin: string, args: string[], where = cwd) => shadowCommand(execute, bin, args, where);
92
+ const verdict: L8ShadowVerdict = { number: item.number, head: item.head, verdict: 'fail', tests: [], at: new Date().toISOString() };
93
+ const view = JSON.parse(run('gh', ['pr', 'view', String(item.number), '--json', 'headRefOid,baseRefName,state,isDraft,isCrossRepository'])) as {
94
+ headRefOid?: string; baseRefName?: string; state?: string; isDraft?: boolean; isCrossRepository?: boolean;
95
+ };
96
+ if (view.headRefOid !== item.head || view.baseRefName !== 'main' || view.state !== 'OPEN'
97
+ || view.isDraft !== false || view.isCrossRepository !== false) throw new Error('queued PR changed or is not ready');
98
+ run('git', ['fetch', 'origin', 'refs/heads/main']);
99
+ const base = run('git', ['rev-parse', 'FETCH_HEAD']);
100
+ if (!/^[0-9a-f]{40}$/i.test(base)) throw new Error('main commit unavailable');
101
+ verdict.base = base;
102
+ run('git', ['fetch', 'origin', `refs/pull/${item.number}/head`]);
103
+ if (run('git', ['rev-parse', 'FETCH_HEAD']) !== item.head) throw new Error('fetched PR head changed');
104
+ let temp: string | undefined;
105
+ let attached = false;
106
+ try {
107
+ temp = mkdtempSync(join(tmpdir(), 'elanous-l8-shadow-'));
108
+ run('git', ['worktree', 'add', '--detach', temp, base]);
109
+ attached = true;
110
+ const fork = run('git', ['merge-base', base, item.head], temp);
111
+ const changed = run('git', ['diff', '--name-only', '-z', fork, item.head], temp).split('\0').filter(Boolean);
112
+ if (!changed.length) throw new Error('PR changed-file paths unavailable');
113
+ const diff = run('git', ['diff', '--name-only', '-z', '--diff-filter=ACMRT', fork, item.head], temp);
114
+ const files = diff.split('\0').filter(Boolean);
115
+ if (changed.some((file) => file.startsWith('-') || file.startsWith('/') || file.split('/').includes('..') || file.includes('\\'))) throw new Error('unsafe changed-file path');
116
+ verdict.tests = files.filter((file) => /(?:^|\/)[^/]+\.(?:test|spec)\.(?:[cm]?[jt]sx?)$/.test(file));
117
+ const merge = execute('git', ['-c', 'user.name=elanous shadow', '-c', 'user.email=shadow@localhost', 'merge', '--no-ff', '--no-edit', item.head], temp);
118
+ if (merge.status !== 0) {
119
+ const unresolved = run('git', ['diff', '--name-only', '--diff-filter=U'], temp);
120
+ if (!unresolved) throw new Error(`git merge failed: ${merge.stderr.trim() || `exit ${merge.status}`}`);
121
+ verdict.verdict = 'conflict';
122
+ verdict.detail = unresolved;
123
+ } else {
124
+ if (verdict.tests.length) {
125
+ if (verdict.tests.some((file) => !existsSync(join(temp!, file)))) throw new Error('changed test path unavailable');
126
+ run('bun', ['install', '--frozen-lockfile'], temp);
127
+ const result = execute('bun', ['run', 'test:deterministic', ...verdict.tests], temp);
128
+ const output = `${result.stdout}\n${result.stderr}`;
129
+ const counts = /\b(\d+) pass\b[\s\S]*?\b(\d+) fail\b[\s\S]*?Ran ([1-9]\d*) tests? across ([1-9]\d*) files?/.exec(output);
130
+ if (!counts || Number(counts[1]) + Number(counts[2]) !== Number(counts[3])
131
+ || Number(counts[4]) !== verdict.tests.length || (result.status !== 0 && Number(counts[2]) === 0)) {
132
+ throw new Error(`changed tests unmeasured: ${output.slice(-400)}`);
133
+ }
134
+ if (Number(counts[2]) > 0 || Number(counts[1]) === 0) {
135
+ verdict.detail = `changed tests failed: ${output.slice(-400)}`;
136
+ } else {
137
+ verdict.verdict = 'pass';
138
+ }
139
+ } else {
140
+ verdict.verdict = 'pass';
141
+ }
142
+ }
143
+ } finally {
144
+ try {
145
+ if (attached && temp) run('git', ['worktree', 'remove', '--force', temp]);
146
+ } finally {
147
+ if (temp) rmSync(temp, { recursive: true, force: true });
148
+ }
149
+ }
150
+ if (verdict.verdict === 'pass') {
151
+ run('git', ['fetch', 'origin', 'refs/heads/main']);
152
+ if (run('git', ['rev-parse', 'FETCH_HEAD']) !== verdict.base) throw new Error('main advanced during shadow verification');
153
+ const current = JSON.parse(run('gh', ['pr', 'view', String(item.number), '--json', 'headRefOid,baseRefName,state,isDraft,isCrossRepository'])) as {
154
+ headRefOid?: string; baseRefName?: string; state?: string; isDraft?: boolean; isCrossRepository?: boolean;
155
+ };
156
+ if (current.headRefOid !== item.head || current.baseRefName !== 'main' || current.state !== 'OPEN'
157
+ || current.isDraft !== false || current.isCrossRepository !== false) throw new Error('PR changed during shadow verification');
158
+ }
159
+ // A rejected insert leaves the pinned item queued for a later evaluation.
160
+ await (deps.observe ?? ((entry) => {
161
+ const store = getDefaultLogStore();
162
+ if (!store) throw new Error('merge-queue observation store unavailable');
163
+ store.insertBatch([{ rec: { ts: new Date().toISOString(), category: 'merge-queue', event: 'shadow-verdict', data: entry }, surface: 'merge-queue' }]);
164
+ }))(verdict);
165
+ state.pending.shift();
166
+ state.verdicts.push(verdict);
167
+ writeShadowQueue(path, state);
168
+ return verdict;
169
+ });
170
+ }
171
+
16
172
  const command: Command = (bin, args, cwd) => {
17
173
  const r = spawnSync(bin, [...args], { cwd, encoding: 'utf8', timeout: 600_000, maxBuffer: 32 * 1024 * 1024 });
18
174
  return { status: r.status, stdout: r.stdout ?? '', stderr: (r.stderr ?? '') + (r.error?.message ?? '') };
@@ -68,6 +68,7 @@ import { decideLineageSupersede, lineageSupersedeCloseComment, type LineageSuper
68
68
  import { basename, isAbsolute, join, relative, resolve, sep } from 'node:path';
69
69
  import { enqueueControlMemo, readSoftStopRequestStatus, type SoftStopRequestRead } from '../harness/control-inbox.js';
70
70
  import { debug } from '../debug/log.js';
71
+ import { recordFailureEvent } from './heal-intake.js';
71
72
  import { releasePathHold, releasePathHoldComment, RELEASE_PATH_LABEL } from '../self-dev/release-path-guard.js';
72
73
  import { emitDecision } from '../live/detail-switch.js';
73
74
  import { reworkDecision } from './decision-events.js';
@@ -83,6 +84,7 @@ import { decomposeSelfDevGoal, inferHotPaths, type SelfDevDecomposeOptions } fro
83
84
  import { boundaryGlobs, boundaryViolations } from './goal-boundary.js';
84
85
  import { askAtExecution, type ExecutionClarificationResult } from './execution-clarification.js';
85
86
  import { targetScopedGoalText } from './goal-text-path-scope.js';
87
+ import { dropStaleReverts, type RevertGuardResult } from './revert-guard.js';
86
88
  import type { SelfDevGoalType } from '../self-dev/orchestrate.js';
87
89
  import { DEFAULT_BRANCH_WORKTREE_BASE, gateGitResidue, observeGitResidue } from '../git-fs/worktree.js';
88
90
  import { runGitCommand } from '../git-fs/runner.js';
@@ -96,6 +98,8 @@ import { getHarnessSpace, normalizeSpaceId, resolveRunIdentity } from '../harnes
96
98
  import { getElanousConfigDir } from '../elanous-config-dir.js';
97
99
  import { elanousStateRoot } from '../autopilot/state-paths.js';
98
100
  import { addNextMdReleaseNote, harnessReleaseNote, parseReleaseNoteSection, releaseNotesDir, renderReleaseNoteSection, writeReleaseNote, type ReleaseNoteFragment, type ReleaseNoteSection } from '../release-loop/release-note.js';
101
+ import { landingFreezeMessage } from '../release-loop/landing-freeze.js';
102
+ import { admitLandingMerge, owningRepoRoot } from './frozen-merges.js';
99
103
  import { plannedSelfImplBranch } from '../harness/worktree-branch-prefix.js';
100
104
  export { slugifyFeature } from '../harness/worktree-branch-prefix.js';
101
105
  import { AUTO_REVIEW_LABEL, resolveAutoReviewLabels } from './context-capsule.js';
@@ -757,6 +761,33 @@ function declaredPathsFromGoalDocumentText(document: string): { readable: boolea
757
761
  return parseDeclaredTargetPathsFromAsk(originalAsk);
758
762
  }
759
763
 
764
+ /** Only paths explicitly designated as targets in the ask or piece text protect an intentional revert. */
765
+ function declaredRevertGuardPaths(text: string): string[] {
766
+ const lines = targetScopedGoalText(text).split(/\r?\n/);
767
+ const pieceTargets: string[] = [];
768
+ for (let index = 0; index < lines.length; index++) {
769
+ const line = lines[index]!.replace(/^\s*(?:[-*+]\s+|>\s*)+/, '');
770
+ const declaration = /^(?:조각\s+)?(?:대상\s*경로|target\s*paths?)\s*[::]\s*(.*)$/i.exec(line);
771
+ if (!declaration) continue;
772
+ const fragments = [declaration[1]!];
773
+ while (index + 1 < lines.length) {
774
+ const next = lines[index + 1]!.trim().replace(/^(?:[-*+]\s+|>\s*)+/, '');
775
+ const paths = parseDeclaredTargetPathsFromAsk(`대상 경로: ${next}`).paths;
776
+ const tokens = next.split(/[·,]/).map((part) => part.trim().replace(/^`+|`+$/g, ''));
777
+ if (!tokens.length || !tokens.every((token) => token && paths.includes(normalizeDeclaredScopePath(token)))) break;
778
+ fragments.push(next);
779
+ index++;
780
+ }
781
+ pieceTargets.push(...parseDeclaredTargetPathsFromAsk(`대상 경로: ${fragments.join(',')}`).paths);
782
+ }
783
+ return pieceTargets;
784
+ }
785
+
786
+ function revertGuardTargetPaths(feature: string, goalDocument?: string): string[] {
787
+ const originalAsk = goalDocument ? extractVerbatimOriginalAsk(goalDocument)?.ask : undefined;
788
+ return [...new Set([...declaredRevertGuardPaths(feature), ...(originalAsk ? declaredRevertGuardPaths(originalAsk) : [])])];
789
+ }
790
+
760
791
  export function detectDeclaredScopeDiff(input: {
761
792
  goalDocument?: string | null;
762
793
  changedFiles?: readonly string[] | null;
@@ -972,7 +1003,7 @@ export interface SelfImplementSeams {
972
1003
  /** 변경 전 ref의 runtime source 전체. 생략 시 git에서 읽으며, 읽기 실패는 판정 미지로 남긴다. */
973
1004
  reviewBaselineObservationSource?: (cwd: string, base: string) => Promise<string | undefined>;
974
1005
  /** ④ gate — cwd 에서 bun test/build. postsync=정합 후 재게이트(merge-base 실행 범위). */
975
- gate: (cwd: string, ctx?: { runId?: string; mode?: 'postsync' }) => Promise<SelfImplementGateResult>;
1006
+ gate: (cwd: string, ctx?: { runId?: string; mode?: 'postsync'; round?: number }) => Promise<SelfImplementGateResult>;
976
1007
  /** Gate가 실행된 worktree HEAD가 local origin/main보다 뒤처진 커밋 수. 실패는 undefined(unknown)로 정직하게 남긴다. */
977
1008
  gateWorktreeBehindMain?: (cwd: string) => Promise<number | undefined>;
978
1009
  /** 변경 파일 목록 — 게이트 «라우터»가 「코드가 바뀌었나」를 판정하는 입력.
@@ -1029,6 +1060,8 @@ export interface SelfImplementSeams {
1029
1060
  /** ★ ⑦ 병합 seam(2026-07-21·auto-merge) — 열린 PR 을 실제 병합(gh pr merge --squash). `matchHeadCommit`이 있으면
1030
1061
  * 검사한 고정 head 뒤 변경을 gh가 거부한다. autoMerge + 리뷰 clean 일 때만 호출(outward-facing·main 자율병합). */
1031
1062
  mergePr?: (opts: { number: number; cwd: string; matchHeadCommit?: string }) => Promise<{ merged: boolean; baseRefName?: string; mergeCommit?: string; detail?: string }>;
1063
+ /** Only successful merges trigger this injected, fail-closed draft inventory and mutation seam. */
1064
+ supersedeDraftsOnMerge?: (number: number, cwd: string) => Promise<void>;
1032
1065
  /** Post-merge cleanup is opt-in and injectable so tests never touch the filesystem. */
1033
1066
  postMergeCleanup?: {
1034
1067
  enabled: boolean;
@@ -2551,12 +2584,16 @@ function releaseNoteForRun(goalFile: string | undefined, feature: string): Relea
2551
2584
  }
2552
2585
 
2553
2586
  /** GATE-CALLERS (OP 10-03): caller tests that exceeded the gate cap must stay visible in the PR body even when the gate log is cut. */
2587
+ export function exposeGateLines(gateLog?: string): string[] {
2588
+ return (gateLog ?? '').split('\n').filter((line) => line.startsWith('[expose]'));
2589
+ }
2590
+
2554
2591
  export function gateNotRunSection(gateLog?: string): string[] {
2555
2592
  const lines = (gateLog ?? '').split('\n').filter((line) => line.startsWith('⚠️ caller test cap exceeded'));
2556
2593
  return lines.length ? ['', '## Gate · 안 돈 소비자 시험(상한 초과)', ...lines.map((line) => `- ${line.replace(/^⚠️ caller test cap exceeded — not run: /, '')}`)] : [];
2557
2594
  }
2558
2595
 
2559
- function prBody(feature: string, implSummary: string, gateLog?: string, review?: SelfImplementReview, reviewIntent?: string, autoReviewDeclineReasons?: readonly string[], evidence?: string, goalFile?: string, planRevision?: ReworkPlanRevision, releaseNote?: ReturnType<typeof harnessReleaseNote>): string {
2596
+ function prBody(feature: string, implSummary: string, gateLog?: string, review?: SelfImplementReview, reviewIntent?: string, autoReviewDeclineReasons?: readonly string[], evidence?: string, goalFile?: string, planRevision?: ReworkPlanRevision, releaseNote?: ReturnType<typeof harnessReleaseNote>, revertGuard?: RevertGuardResult): string {
2560
2597
  const note = releaseNote ?? releaseNoteForRun(goalFile, feature);
2561
2598
  const relaxation = planRevision?.relaxation
2562
2599
  ? ['', '## 감독 수용 기준 완화', `- 대상: ${planRevision.relaxation.target}`, `- 이전: ${planRevision.relaxation.expected}`, `- 완화: ${planRevision.relaxation.replacement}`, `- 이유: ${planRevision.reason}`, `- 적용: ${planRevision.application?.status ?? 'failed'}${planRevision.application?.detail ? ` (${planRevision.application.detail})` : ''}`, ...(planRevision.disposition ? [`- 충돌 처분: ${planRevision.disposition}`] : [])]
@@ -2577,7 +2614,14 @@ function prBody(feature: string, implSummary: string, gateLog?: string, review?:
2577
2614
  ...(review.shouldFix.length ? ['', '**should-fix (비블로킹):**', ...review.shouldFix.slice(0, 8).map((f) => `- ${f}`)] : [])] : []),
2578
2615
  ...(autoReviewDeclineReasons?.length ? ['', '## Auto-review label not applied', 'The requested auto-review label was declined by the autonomy gate:', ...autoReviewDeclineReasons.map((reason) => `- ${reason}`)] : []),
2579
2616
  ...gateNotRunSection(gateLog),
2580
- ...(gateLog ? ['', '## Gate', '```', gateLog.trim().slice(0, 3000), '```'] : []),
2617
+ ...(gateLog || revertGuard ? ['', '## Gate',
2618
+ // 경고는 잘린 gate 로그의 코드블록 밖에서도 한눈에 보인다.
2619
+ ...((gateLog ?? '').split('\n').filter((line) => line.startsWith('⚠️ 동작 검증 0:'))),
2620
+ ...exposeGateLines(gateLog),
2621
+ ...(gateLog ? ['```', gateLog.trim().slice(0, 3000), '```'] : []),
2622
+ ...(revertGuard?.reverted.length ? [`- 되돌림 방지: ${revertGuard.reverted.length}파일 제외`] : []),
2623
+ ...(revertGuard?.protected.length ? [`- 되돌림 경고: 대상 경로 ${revertGuard.protected.length}파일 유지 (${revertGuard.protected.join(', ')})`] : []),
2624
+ ...(revertGuard?.warning ? [`- 되돌림 검사 실패(커밋 계속): ${revertGuard.warning}`] : [])] : []),
2581
2625
  ...relaxation,
2582
2626
  '',
2583
2627
  '🤖 self-implement 오케스트레이터 (elanous 자율 구현·리뷰-게이트 병합)',
@@ -2807,7 +2851,7 @@ function blockedDraftPrBody(feature: string, implSummary: string, state: Blocked
2807
2851
  ...(state.gate === undefined
2808
2852
  ? ['(게이트 미실행)']
2809
2853
  : state.gate.log
2810
- ? ['```', state.gate.log.trim().slice(0, 3000), '```']
2854
+ ? [...state.gate.log.split('\n').filter((line) => line.startsWith('⚠️ 동작 검증 0:')), ...exposeGateLines(state.gate.log), '```', state.gate.log.trim().slice(0, 3000), '```'] // 경고는 절단된 로그 밖에도 보존한다.
2811
2855
  : ['(게이트 실행됨 · 로그 없음)']),
2812
2856
  '',
2813
2857
  '## Main-sync typecheck',
@@ -4087,6 +4131,19 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
4087
4131
  throw e;
4088
4132
  } finally {
4089
4133
  if (loopRegistered) registerRunLoop('ended');
4134
+ const terminalStage = terminalResult?.stage ?? 'crashed';
4135
+ try {
4136
+ const mappedStatus = mapStageToRunStatus(terminalStage)?.runStatus;
4137
+ if (!terminalResult || mappedStatus === 'failed' || (mappedStatus === undefined && terminalResult.ok === false) || signalIncompleteState(roundClassifications).latestSignalIncomplete) {
4138
+ recordFailureEvent({
4139
+ source: 'harness-run', kind: terminalStage, ref: runId,
4140
+ summary: opts.feature.split(/\r?\n/, 1)[0]?.trim() || `Harness run ${runId} failed`,
4141
+ at: new Date().toISOString(),
4142
+ });
4143
+ }
4144
+ } catch (error) {
4145
+ logRunAwareFailSoft(runId, 'heal-intake-record-failed', { stage: terminalStage, error: safeErrorDescription(error) });
4146
+ }
4090
4147
  if (terminalResult) await recordGoalExecution(terminalResult);
4091
4148
  // ⛔⭐⭐⭐ 종결을 «못 적고» 죽는 길을 막는다 (2026-08-11 · 🅣 71차 · 대표 「종료 때 정리가 안 되면 설계 문제」).
4092
4149
  // `terminalResult` 는 «두 자리»에서만 선다 — 정상 종료 ⊕ `StepTimeoutError` 분기.
@@ -5279,6 +5336,13 @@ async function runSelfImplementInner(
5279
5336
  let finishedChildCommitMissing = false;
5280
5337
  let finishedChildCommitFailure: string | undefined;
5281
5338
  let finishedChildCommitted = false;
5339
+ const revertGuard: RevertGuardResult = { reverted: [], protected: [] };
5340
+ const inspectStaleReverts = (targets: readonly string[]): void => {
5341
+ const inspected = dropStaleReverts(wt.path, targets, runId, getUserConfig().harness?.revertGuard?.depth);
5342
+ for (const file of inspected.reverted) if (!revertGuard.reverted.includes(file)) revertGuard.reverted.push(file);
5343
+ for (const file of inspected.protected) if (!revertGuard.protected.includes(file)) revertGuard.protected.push(file);
5344
+ if (inspected.warning) revertGuard.warning = inspected.warning;
5345
+ };
5282
5346
  const classifyUnfinishedRun = (stage: SelfImplementStage, verdict?: ReworkBudgetVerdict, decomposition?: TerminalDecomposition): AbandonedClassificationResult => {
5283
5347
  const quotaExhaustionAssessment = quotaExhaustionAssessmentForRun();
5284
5348
  const worktree = unfinishedWorktreeObservation(wt.path);
@@ -6306,6 +6370,7 @@ async function runSelfImplementInner(
6306
6370
  .filter((path) => !harnessSeededPaths.includes(path)
6307
6371
  && targets.some((target) => changedFileIsInsideDeclaredTarget(path, target)));
6308
6372
  if (eligible.length) {
6373
+ inspectStaleReverts(revertGuardTargetPaths(opts.feature, goalDocument));
6309
6374
  const message = `self-implement: ${prTitle(opts.feature)}`;
6310
6375
  let outcome: { ok: boolean; out: string };
6311
6376
  try {
@@ -6514,7 +6579,7 @@ async function runSelfImplementInner(
6514
6579
  return { ...stoppedBeforeGate, ...resolveRunOutcome({ termination: 'abandoned' }), sessionId, quotaExhaustionAssessment: quotaExhaustionAssessmentForRun(), ...(review ? { review } : {}) };
6515
6580
  }
6516
6581
  onNodeEntry(node('gate'), round);
6517
- gate = await stepTimeout(s.gate(wt.path, { runId }), T.gate, 'gate');
6582
+ gate = await stepTimeout(s.gate(wt.path, { runId, round }), T.gate, 'gate');
6518
6583
  } else {
6519
6584
  observe('gate-skipped-by-graph', { ...graphAuthorityFields(graphAuthority, graphTemplate), round, reason: gateRoute.reason, changedFileCount: gateRouteFiles?.length ?? null });
6520
6585
  gate = {
@@ -7106,6 +7171,11 @@ async function runSelfImplementInner(
7106
7171
 
7107
7172
  if (s.mergeMain && s.commitWork) {
7108
7173
  onNodeEntry('main-sync', round);
7174
+ let goalDocument: string | undefined;
7175
+ if (opts.goalFile) {
7176
+ try { goalDocument = readFileSync(opts.goalFile, 'utf8'); } catch { /* no documented targets */ }
7177
+ }
7178
+ inspectStaleReverts(revertGuardTargetPaths(opts.feature, goalDocument));
7109
7179
  s.commitWork(wt.path, prTitle(opts.feature));
7110
7180
  // ⛔⭐ resume 경로와 «같은» 해석기를 쓴다 — 두 자리가 갈리면 같은 원인이 다른 얼굴로 나온다.
7111
7181
  const resolvedTarget = resolveDefaultBranchTarget(s.defaultBranchRef ?? defaultBranchRef, wt.path);
@@ -7139,7 +7209,7 @@ async function runSelfImplementInner(
7139
7209
  ? `${mergeTarget} 충돌해결됨 — ${formatLlmMergeOutcome(sync)} — 통합 결과 full 재-gate…`
7140
7210
  : `${mergeTarget} 정합됨 — 통합 결과 full 재-gate…`);
7141
7211
  onNodeEntry('regate', round);
7142
- const regate = await withStepTimeout(s.gate(wt.path, { runId, mode: 'postsync' }), T.gate, 'gate');
7212
+ const regate = await withStepTimeout(s.gate(wt.path, { runId, mode: 'postsync', round }), T.gate, 'gate');
7143
7213
  if (!regate.passed) await onFailureVerdict('gate');
7144
7214
  // `mode` name/meaning stay: `full` = existing gate including tests. Both
7145
7215
  // conflict-resolved and clean-merge reuse this same `s.gate` invocation.
@@ -7269,7 +7339,7 @@ async function runSelfImplementInner(
7269
7339
  addNextMdReleaseNote(wt.path, releaseNote);
7270
7340
  const preparedPrBody = preparePrBody(
7271
7341
  [
7272
- prBody(opts.feature, impl.summary, gate.log, review, reviewIntent, declineReasons, harvestedForPr(), opts.goalFile, lastPlanRevision, releaseNote),
7342
+ prBody(opts.feature, impl.summary, gate.log, review, reviewIntent, declineReasons, harvestedForPr(), opts.goalFile, lastPlanRevision, releaseNote, revertGuard),
7273
7343
  ...(prEvidenceDecision.body ? ['', '---', '', prEvidenceDecision.body] : []),
7274
7344
  ].join('\n'),
7275
7345
  s.persistPrBodyArtifact,
@@ -7418,11 +7488,45 @@ async function runSelfImplementInner(
7418
7488
  progress('pr-opened', `호스트 재게이트 대기 (#${pr.number})`);
7419
7489
  return { ok: true, stage: 'merge-ready', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), checkedHeadCommit: checkedHeadCommit!, prUrl: pr.url, prNumber: pr.number };
7420
7490
  }
7421
- onNodeEntry('merge', round);
7422
- progress('merging', '자동 병합 (리뷰 clean·squash)…');
7423
- const m = await withStepTimeout(s.mergePr!({ number: pr.number, cwd: wt.path, matchHeadCommit: checkedHeadCommit! }), T.pr, 'pr');
7491
+ // Landing freeze: the in-flight marker is written before the freeze is read, so a `freeze on` racing this merge waits.
7492
+ // The resume sweep merges from the repository that owns the run worktree (the worktree itself may be cleaned up).
7493
+ const heldEntry = () => {
7494
+ if (!checkedHeadCommit || !/^[0-9a-f]{40}$/i.test(checkedHeadCommit)) return null;
7495
+ const owner = owningRepoRoot(wt.path);
7496
+ // Without a known owning repository the held PR stays queued for a person; no sweep resumes it from a worktree.
7497
+ return owner ? { prNumber: pr.number, headCommit: checkedHeadCommit, repoRoot: owner } : { prNumber: pr.number, headCommit: checkedHeadCommit, repoRoot: wt.path, manual: true as const };
7498
+ };
7499
+ const landing = admitLandingMerge(heldEntry, undefined, {}, { prNumber: pr.number, repoRoot: owningRepoRoot(wt.path) ?? wt.path, ...(checkedHeadCommit ? { headCommit: checkedHeadCommit } : {}) });
7500
+ if (landing.kind === 'held' && !(checkedHeadCommit && /^[0-9a-f]{40}$/i.test(checkedHeadCommit))) {
7501
+ return { ok: true, stage: 'pr-opened', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), mergeReason: 'merge-guard-unevaluated', prUrl: pr.url, prNumber: pr.number, detail: 'freeze: checked head SHA unavailable; PR left ready for review' };
7502
+ }
7503
+ if (landing.kind !== 'merge') {
7504
+ const detail = landing.kind === 'held' ? landingFreezeMessage(landing.freeze) : 'freeze lifted; a resume sweep is merging this PR';
7505
+ debug.log('harness.merge', 'frozen', { pr: pr.number, ...(landing.kind === 'held' ? { reason: landing.freeze.reason, until: landing.freeze.until } : { resumedElsewhere: true }) });
7506
+ progress('pr-opened', `#${pr.number} ready · ${detail}`);
7507
+ return { ok: true, stage: 'pr-opened', node: 'open-pr', ...resolveRunOutcome({ termination: 'completed' }), ...decisionSignalResult, ...preservedCompletionResult, ...(completionStatus ? { completionStatus } : {}), sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), mergeReason: landing.kind === 'held' ? 'frozen' : 'resumed-elsewhere', prUrl: pr.url, prNumber: pr.number, detail };
7508
+ }
7509
+ // The marker and claim live until the merge call itself settles — a step timeout does not stop a merge already
7510
+ // sent — and anything that throws before the merge call is made releases them.
7511
+ let merging: ReturnType<NonNullable<typeof s.mergePr>>;
7512
+ try {
7513
+ onNodeEntry('merge', round);
7514
+ progress('merging', '자동 병합 (리뷰 clean·squash)…');
7515
+ merging = s.mergePr!({ number: pr.number, cwd: wt.path, matchHeadCommit: checkedHeadCommit! });
7516
+ } catch (error) { landing.end(); throw error; }
7517
+ // The default merge seam resolves only after `gh pr view` reads MERGED, so settling is the confirmation.
7518
+ const release = (merged: boolean) => {
7519
+ try { landing.end(merged); } catch (error) { observe('landing-release-failed', { number: pr.number, error: safeErrorDescription(error) }, { level: 'error' }); }
7520
+ };
7521
+ void merging.then((outcome) => release(outcome.merged === true), () => release(false));
7522
+ const m = await withStepTimeout(merging, T.pr, 'pr');
7424
7523
  observe('merged', { number: pr.number, merged: m.merged, detail: m.detail ?? null });
7425
7524
  if (m.merged) {
7525
+ try {
7526
+ await s.supersedeDraftsOnMerge?.(pr.number, wt.path);
7527
+ } catch (error) {
7528
+ observe('draft-supersede-on-merge-failed', { number: pr.number, error: safeErrorDescription(error) }, { level: 'warn' });
7529
+ }
7426
7530
  const directory = releaseNotesDir(elanousStateRoot());
7427
7531
  const fragment: ReleaseNoteFragment = {
7428
7532
  pr: pr.number,
@@ -0,0 +1,62 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { lstatSync, readFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { debug } from '../debug/log.js';
5
+
6
+ export type RevertGuardResult = { reverted: string[]; protected: string[]; warning?: string };
7
+
8
+ /** Compare the worktree's exact bytes with blobs from recent HEAD ancestry, before add -A. */
9
+ export function dropStaleReverts(cwd: string, targets: readonly string[], runId: string, depth = 50): RevertGuardResult {
10
+ const result: RevertGuardResult = { reverted: [], protected: [] };
11
+ const git = (...args: string[]): Buffer => execFileSync('git', args, { cwd, timeout: 20_000, maxBuffer: 16 * 1024 * 1024, stdio: ['pipe', 'pipe', 'pipe'] });
12
+ const reason = (error: unknown): string => (error instanceof Error ? error.message : String(error)).replace(/\s+/g, ' ').slice(0, 240);
13
+ try {
14
+ const limit = Number.isSafeInteger(depth) && depth > 0 ? depth : 50;
15
+ const commits = git('rev-list', `--max-count=${limit + 1}`, 'HEAD').toString('utf8').trim().split('\n');
16
+ const changed = git('diff', '--name-only', '--diff-filter=M', '-z', 'HEAD', '--').toString('utf8').split('\0').filter(Boolean);
17
+ const staged = git('diff', '--cached', '--name-only', '--diff-filter=M', '-z', 'HEAD', '--').toString('utf8').split('\0').filter(Boolean);
18
+ for (const file of new Set([...changed, ...staged])) {
19
+ try {
20
+ if (!lstatSync(join(cwd, file)).isFile()) continue;
21
+ const current = readFileSync(join(cwd, file));
22
+ const rawOid = execFileSync('git', ['hash-object', '--stdin'], { cwd, input: current, timeout: 20_000 }).toString('utf8').trim();
23
+ const headEntry = git('ls-tree', 'HEAD', '--', file).toString('utf8').trim();
24
+ const head = /^\d+ blob ([0-9a-f]+)\t/.exec(headEntry)?.[1];
25
+ if (!head) continue;
26
+ const sameBytes = (oid: string): boolean => oid === rawOid || git('cat-file', 'blob', oid).equals(current);
27
+ if (sameBytes(head)) continue;
28
+ let matchedCommit: string | undefined;
29
+ const inspectedOids = new Set<string>();
30
+ for (const commit of commits.slice(1)) {
31
+ // A path may not have existed in an ancestor; that is not a failed inspection.
32
+ const blob = git('ls-tree', commit, '--', file).toString('utf8').trim();
33
+ const oid = /^\d+ blob ([0-9a-f]+)\t/.exec(blob)?.[1];
34
+ if (oid && !inspectedOids.has(oid)) {
35
+ if (sameBytes(oid)) { matchedCommit = commit; break; }
36
+ inspectedOids.add(oid);
37
+ }
38
+ }
39
+ if (!matchedCommit) continue;
40
+ const scoped = targets.some((target) => {
41
+ const path = target.replace(/^\.\//, '').replace(/\/+$/, '');
42
+ return path !== '' && (file === path || file.startsWith(`${path}/`));
43
+ });
44
+ if (scoped) {
45
+ result.protected.push(file);
46
+ debug.log('self-implement.revert-guard', 'protected-target', { runId, files: [file], matchedCommit }, { level: 'warn' });
47
+ } else {
48
+ git('checkout', 'HEAD', '--', file);
49
+ result.reverted.push(file);
50
+ debug.log('self-implement.revert-guard', 'reverted-stale', { runId, files: [file], matchedCommit });
51
+ }
52
+ } catch (error) {
53
+ result.warning = `git inspection/checkout failed for ${file}: ${reason(error)}`;
54
+ debug.log('self-implement.revert-guard', 'fail-open', { runId, reason: result.warning }, { level: 'warn' });
55
+ }
56
+ }
57
+ } catch (error) {
58
+ result.warning = `git inspection failed: ${reason(error)}`;
59
+ debug.log('self-implement.revert-guard', 'fail-open', { runId, reason: result.warning }, { level: 'warn' });
60
+ }
61
+ return result;
62
+ }
@@ -276,7 +276,7 @@ interface RunLedgerLookupTarget {
276
276
  targetName: string | null;
277
277
  }
278
278
 
279
- function resolveFederatedRunLedgerTargets(options: Pick<FederatedRunLedgerLookupOptions, 'includeTest' | 'targets'>): RunLedgerLookupTarget[] {
279
+ export function resolveFederatedRunLedgerTargets(options: Pick<FederatedRunLedgerLookupOptions, 'includeTest' | 'targets'>): RunLedgerLookupTarget[] {
280
280
  const resolved = options.targets ? { targets: [...options.targets] } : resolveLogTargets({ all: true, includeTest: options.includeTest });
281
281
  if (resolved.error) throw new Error(resolved.error);
282
282
  const targets = new Map<string, string>();
@@ -37,7 +37,8 @@ import { iosFilesIn, runIosUnitTestGate } from '../../scripts/ci-ios-unit-tests.
37
37
  import { detectGateCommand } from './detect-gate.js';
38
38
  import { writeScopedTypecheckConfig } from './scoped-typecheck-config.js';
39
39
  // ⭐ 게이트 스코프 근본 수리(2026-07-26) — 풀 `bun test` 폴백 제거 + 연관 테스트 유도(순수).
40
- import { resolveGateScope } from './gate-scope.js';
40
+ import { formatNoRelatedTestsWarning, resolveGateScope } from './gate-scope.js';
41
+ import { publicExposureFiles, runExposeGate } from './expose-gate.js';
41
42
  import { buildImporterTestIndex, isTestPath } from './importer-test-index.js';
42
43
  import { buildRouteConsumerTestIndex } from './route-consumer-test-index.js';
43
44
  import { countTestDeclarations } from './test-declarations.js';
@@ -976,6 +977,7 @@ export interface DefaultSeamsOptions {
976
977
  runIosUnitTestGate?: (io: { args: readonly string[]; cwd: string }) => number;
977
978
  /** Source-policy gate adapter for focused seam tests. */
978
979
  runHarnessPolicyGates?: typeof runHarnessPolicyGates;
980
+ runExposeGate?: typeof runExposeGate;
979
981
  /** Dependency-wide validation adapter for focused seam tests. */
980
982
  runDependencyChangeGate?: typeof runDependencyChangeGate;
981
983
  /** 워크트리 시험 묶음(없으면 실패 파일)을 base에서 재실행하는 seam. 실패 경로에서만 호출된다. */
@@ -1409,6 +1411,10 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
1409
1411
  },
1410
1412
  removeMatchingGoalCopy,
1411
1413
  },
1414
+ supersedeDraftsOnMerge: async (number, cwd) => {
1415
+ const { supersedeMergedGoalDrafts } = await import('../harness/harness-cli-command.js');
1416
+ await supersedeMergedGoalDrafts(number, cwd);
1417
+ },
1412
1418
  lineageSupersede: {
1413
1419
  listOpenDrafts: () => listOpenDraftsForLineage(),
1414
1420
  readRunLedger: (runId) => {
@@ -1810,8 +1816,13 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
1810
1816
  const observationChanges = gateChangedFiles(cwd, comparisonBase);
1811
1817
  const observationChangedFiles = observationChanges.files;
1812
1818
  const postsync = ctx?.mode === 'postsync';
1813
- // 정합 전 일반 게이트는 worktree-only. 정합 후 재게이트만 이미 계산된 merge-base 목록을 실행 범위로 쓴다.
1814
- const changedAll = postsync ? observationChangedFiles : gitChangedFiles(cwd);
1819
+ // 일반 게이트는 기존 worktree 범위를 유지하되 fork 이후 커밋된 시험을 더한다.
1820
+ // HEAD 대비 미커밋 목록만 쓰면 릴리스 노트만 남은 재작업 라운드에서
1821
+ // 커밋된 시험을 놓쳐 docs-only 로 건너뛴다. postsync는 종전처럼 전 변경을 잰다.
1822
+ const changedAll = postsync ? observationChangedFiles : [...new Set([
1823
+ ...gitChangedFiles(cwd),
1824
+ ...observationChangedFiles.filter((file) => isTestPath(file)),
1825
+ ])];
1815
1826
  // ⚠️ **regular-file 판정**(리뷰 should-fix 5R) — `existsSync` 는 **디렉터리도 true** 다.
1816
1827
  // 계약은 "실존 테스트 **파일**"이므로 파일 여부까지 확인한다(디렉터리를 testArgs 로 넘기면
1817
1828
  // 필터 무매치 → "0 files ran" 부당 실패).
@@ -1824,11 +1835,19 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
1824
1835
  trackedFiles.filter(isTestPath),
1825
1836
  [...new Set([...trackedFiles, ...changedAll, ...observationChangedFiles])].filter((file) => /\.(?:[cm]?[jt]sx?)$/.test(file)),
1826
1837
  );
1827
- const routeConsumerTestIndex = buildRouteConsumerTestIndex(cwd, trackedFiles.filter(isTestPath), changedAll, comparisonBase ?? 'HEAD');
1838
+ // 런 전체 재탐색 시 앞 라운드의 변경·삭제 서버 경로도 비교-base 소비자 색인에 있어야 한다.
1839
+ const routeConsumerTestIndex = buildRouteConsumerTestIndex(cwd, trackedFiles.filter(isTestPath),
1840
+ [...new Set([...changedAll, ...observationChangedFiles])], comparisonBase ?? 'HEAD');
1828
1841
  // 워크트리에 없는 변경 경로 = 지운 파일 — 미검증으로 세지 않는다(gate-scope `isDeleted` · 2026-09-26 run-7dd4cce6).
1829
1842
  const isDeleted = (path: string): boolean => !isFile(path);
1830
- const scopeOpts = { isDeleted, routeConsumerTestIndex: routeConsumerTestIndex ?? undefined };
1843
+ const scopeOpts = { isDeleted, routeConsumerTestIndex: routeConsumerTestIndex ?? undefined,
1844
+ runChanged: observationChangedFiles, round: ctx?.round };
1831
1845
  const scope = resolveGateScope(changedAll, isFile, importerTestIndex ?? undefined, postsync ? { ...scopeOpts, mode: 'postsync' } : scopeOpts);
1846
+ // The landing scope is the PR diff, not only this round's uncommitted test files.
1847
+ const expose = publicExposureFiles(observationChangedFiles).length > 0
1848
+ ? (o.runExposeGate ?? runExposeGate)(cwd, observationChangedFiles,
1849
+ getUserConfig().harness?.exposeGate === 'strict' ? 'strict' : 'warn')
1850
+ : { passed: true, log: '' };
1832
1851
  const comparisonFailureReason = observationChanges.comparisonBaseStatus === 'unavailable'
1833
1852
  ? 'comparison-base-unavailable'
1834
1853
  : observationChanges.comparisonBaseStatus === 'comparison-failed'
@@ -1865,6 +1884,7 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
1865
1884
  debug.log('self-implement', 'gate.scope', {
1866
1885
  ...(ctx?.runId ? { runId: ctx.runId } : {}),
1867
1886
  targeted: gateOpts.testArgs?.length ?? 0, files: (gateOpts.testArgs ?? []).slice(0, 8),
1887
+ 'evaluated-changes': changedAll.slice(0, 8), evaluatedChangeCount: changedAll.length,
1868
1888
  changedTests: observedChangedTestFiles.slice(0, 8), changedTestCount: observedChangedTestFiles.length,
1869
1889
  srcChanged: scope.sourceFiles.length, testStepSkipped,
1870
1890
  scopeReason, derived: scope.derived.length, ignoredOutsideSrc: scope.ignoredOutsideSrc,
@@ -2053,7 +2073,7 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
2053
2073
  // `tc`(변경파일 tsc) **전에** 실행해서, tsc 가 깨져 **최종 게이트가 실패한 런에도**
2054
2074
  // base 를 다시 돌리고 `gate.log` 를 바꿨다 ⇒ *"게이트 실패 경로 무변경"* 경계 위반.
2055
2075
  // ⇒ 조건을 최종 판정과 같은 식으로 묶는다.
2056
- const gatePassed = !comparisonFailureReason && !unmeasuredPostsync && integrityPassed && tc.passed && policy.passed && (dependencyGate?.passed ?? true);
2076
+ const gatePassed = !comparisonFailureReason && !unmeasuredPostsync && integrityPassed && tc.passed && policy.passed && expose.passed && (dependencyGate?.passed ?? true);
2057
2077
  const verifyIdentity = gateVerifyIdentity(ctx?.runId);
2058
2078
  if (gatePassed && scope.skipTestStep) {
2059
2079
  verifyByBreaking = { ran: false, distinguishes: 0, 'does-not-distinguish': 0, unknown: 0, skippedReason: scope.reason };
@@ -2204,7 +2224,7 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
2204
2224
  + (scope.reason === 'no-related-tests'
2205
2225
  // ⭐ **미검증 파일 목록**(2026-07-27 사후 리뷰 수리) — 종전엔 `sourceFiles` 를 실어
2206
2226
  // `package.json`·설정·스크립트 변경 시 **빈 목록**이 나갔다(그 파일들은 소스 확장자가 아니다).
2207
- ? `동작 검증을 못 한 변경: ${scope.unverified.slice(0, 8).join(', ')}`
2227
+ ? `\n\n${formatNoRelatedTestsWarning(scope.unverified)}\n\n동작 검증을 못 한 변경: ${scope.unverified.slice(0, 8).join(', ')}`
2208
2228
  + `\n⚠️ 이 라운드는 **동작 검증이 되지 않았다**(변경파일 tsc 만 통과) — "게이트 통과=검증됨"으로 결론내지 말 것.`
2209
2229
  // ⚠️ 안내를 일반화(리뷰 should-fix) — `package.json`·lockfile·설정·스크립트에는
2210
2230
  // "해당 소스의 테스트"라는 표현이 부정확하다(그 변경엔 대응 테스트 관례가 없다).
@@ -2229,7 +2249,7 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
2229
2249
  : '';
2230
2250
  return {
2231
2251
  passed: gatePassed,
2232
- log: `${baseLog}${policyFailureLog}${dependencyFailureLog}${dependencyUnmeasuredNote}${comparisonFailureLog}${baselineNote}${verifyByBreakingNote}${scopeNote}${uncoveredNote}${pulledInRelatedTestsNote}${callerOverflowNote}${routeLookupNote}`,
2252
+ log: `${baseLog}${policyFailureLog}${dependencyFailureLog}${dependencyUnmeasuredNote}${comparisonFailureLog}${baselineNote}${verifyByBreakingNote}${scopeNote}${uncoveredNote}${pulledInRelatedTestsNote}${callerOverflowNote}${routeLookupNote}${expose.log ? `\n\n${expose.log}` : ''}`,
2233
2253
  testStepExecuted: !testStepSkipped && testStep !== undefined && !testStep.skipped,
2234
2254
  scopeReason,
2235
2255
  comparisonBaseStatus: observationChanges.comparisonBaseStatus,
@@ -213,6 +213,8 @@ export interface SessionMeta {
213
213
  tgBotId?: string;
214
214
  /** Stable identity for a persona's resident conversation. */
215
215
  personaId?: string;
216
+ /** Optional persona selected for this conversation's replies (not resident identity). */
217
+ chatPersonaId?: string;
216
218
  /** Telegram session-key used for lookup by chat, e.g.
217
219
  * `telegram:8799226199:dm:123:0` (bot-scoped) or legacy
218
220
  * `telegram:dm:123:0`. Constructed once at create time. */