elanous 0.2.13 → 0.2.15

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 (524) 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/LZcstaO9_NXypPFmujolv/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/{7334.a6679d9168f37aa8.js → 1268.0840ff91e083fcbd.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/1307-33ffcc49ab45123b.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/{9656-c1540088f5859c80.js → 1844-9ec4b1d12f6e80eb.js} +1 -1
  8. package/apps/pwa/out/_next/static/chunks/1866-e7a6999f25548b30.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/{390.c373e31b3b9d0c41.js → 1948.15f356b15a625597.js} +2 -2
  10. package/apps/pwa/out/_next/static/chunks/211-9c9090c59e2c5907.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/224-798346143007ff65.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/2957-96ffab3d521fb08f.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/4144-a0041c8dc3be57ac.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/4554-3565eea1f1495ba7.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-c952712c87a1f757.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/8060-cc008cc6fedfa818.js +1 -0
  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/899-46946fe8363e1262.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/9070.067c106528ec1ae6.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/9103-7a3ae7f60af3b99f.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/{3869-3cd46526879f454c.js → 9247-29d889b809288e65.js} +1 -1
  47. package/apps/pwa/out/_next/static/chunks/935.4ddaa99fc1d1126c.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/{79f8be9e-957d68913b590b08.js → a9f2b54d-7865aba0f4d2ee49.js} +1 -1
  49. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/approvals/page-0e2c57fe6add4b2a.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-e05a77df7e005b04.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/board/page-f544338193a9a762.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/botlab/page-5491c115b1f9a923.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/bots/page-b8c49f6b4fa2df91.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/ceo/page-96398e364fbd4548.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/app/chat/page-976813c91d7394ea.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/consult/page-28b9448091776c06.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/control/page-1d4901eb7c2d8822.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/design-check/page-72d3e7ff35a8c954.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/editor/page-60c7a8e1a4fe62ce.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/exec/page-2a52b316cb143d9c.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/field/page-c3ce483c8d5db370.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/inside/page-13c7b53e0c2519a9.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/intake/page-96f074f6538e8a2c.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/layout-e97be5334d83a9f5.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/live/page-65e484302921a932.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/loops/page-34d08facaf08f556.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/market/page-955ec2da3be4c560.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-9f88fbc15d9058d1.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/morning/page-c1343f9499d86f3f.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/not-found-8739a2e0ae793130.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/observatory/page-c9fc5706b8abaa11.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-d23b12bb92242558.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8d47f45a0dc044ac.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c7fea475488201a8.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/outputs/page-e2ee2896811e98d0.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/page-83b19dae8619de05.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/reflection/page-5efbf81124c474ae.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f1a1fc0a82738c.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/sessions/page-da6c08aae0f92768.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-06ee266953d9c879.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/settings/page-153c2b3bcc89e8de.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-87a11e6a05f15199.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/app/setup/page-7d4182964407c09c.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/app/share/page-0d6d23b40263a7ea.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/app/showroom/page-7e8c39241f8eb6d0.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/app/tasks/page-008c1a96618a4887.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/app/term/{page-63d14a4d8fe1e1c1.js → page-f5597e894c02cd33.js} +1 -1
  91. package/apps/pwa/out/_next/static/chunks/app/today/page-0337fdf7fb5e37b6.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/app/trace/page-73735b64bfe1ae67.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/app/vault/page-8fcec4c9ea2e7f70.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-f7e631e9b4daf66e.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/app/workflows/page-4881b8f9f63694b4.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/app/workspace/page-9817c7f9007466cc.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-97f9f43172a3a90e.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/{5cd7e3a9-404fd6b8d0ecd44c.js → f6231284-45726550aba08fca.js} +1 -1
  99. package/apps/pwa/out/_next/static/chunks/{f48170cc-d05d28bf87a43bf4.js → f6b7c9ba.64a895b8b2fedbdc.js} +1 -1
  100. package/apps/pwa/out/_next/static/chunks/framework-5c169757551134ed.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/webpack-539cde99554be20f.js +1 -0
  106. package/apps/pwa/out/_next/static/css/1f7c958c41ffa3b9.css +1 -0
  107. package/apps/pwa/out/approvals/index.html +1 -1
  108. package/apps/pwa/out/approvals/index.txt +23 -23
  109. package/apps/pwa/out/autopilot/index.html +1 -1
  110. package/apps/pwa/out/autopilot/index.txt +23 -23
  111. package/apps/pwa/out/board/index.html +1 -1
  112. package/apps/pwa/out/board/index.txt +23 -23
  113. package/apps/pwa/out/botlab/index.html +1 -1
  114. package/apps/pwa/out/botlab/index.txt +22 -22
  115. package/apps/pwa/out/bots/index.html +1 -1
  116. package/apps/pwa/out/bots/index.txt +22 -22
  117. package/apps/pwa/out/ceo/index.html +1 -0
  118. package/apps/pwa/out/ceo/index.txt +33 -0
  119. package/apps/pwa/out/chat/index.html +1 -1
  120. package/apps/pwa/out/chat/index.txt +22 -22
  121. package/apps/pwa/out/consult/index.html +1 -1
  122. package/apps/pwa/out/consult/index.txt +23 -23
  123. package/apps/pwa/out/control/index.html +1 -1
  124. package/apps/pwa/out/control/index.txt +23 -23
  125. package/apps/pwa/out/design-check/index.html +1 -1
  126. package/apps/pwa/out/design-check/index.txt +23 -23
  127. package/apps/pwa/out/editor/index.html +1 -1
  128. package/apps/pwa/out/editor/index.txt +23 -23
  129. package/apps/pwa/out/exec/index.html +1 -1
  130. package/apps/pwa/out/exec/index.txt +23 -23
  131. package/apps/pwa/out/field/index.html +1 -1
  132. package/apps/pwa/out/field/index.txt +23 -23
  133. package/apps/pwa/out/healthz +1 -1
  134. package/apps/pwa/out/index.html +1 -1
  135. package/apps/pwa/out/index.txt +22 -22
  136. package/apps/pwa/out/inside/index.html +1 -1
  137. package/apps/pwa/out/inside/index.txt +22 -22
  138. package/apps/pwa/out/intake/index.html +1 -1
  139. package/apps/pwa/out/intake/index.txt +23 -23
  140. package/apps/pwa/out/live/index.html +1 -1
  141. package/apps/pwa/out/live/index.txt +23 -23
  142. package/apps/pwa/out/loops/index.html +1 -0
  143. package/apps/pwa/out/loops/index.txt +30 -0
  144. package/apps/pwa/out/market/index.html +1 -1
  145. package/apps/pwa/out/market/index.txt +22 -22
  146. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  147. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  148. package/apps/pwa/out/morning/index.html +1 -1
  149. package/apps/pwa/out/morning/index.txt +23 -23
  150. package/apps/pwa/out/observatory/index.html +1 -1
  151. package/apps/pwa/out/observatory/index.txt +23 -23
  152. package/apps/pwa/out/ops/checklist/index.html +1 -1
  153. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  154. package/apps/pwa/out/ops/release/index.html +1 -1
  155. package/apps/pwa/out/ops/release/index.txt +22 -22
  156. package/apps/pwa/out/ops/seats/index.html +1 -1
  157. package/apps/pwa/out/ops/seats/index.txt +22 -22
  158. package/apps/pwa/out/outputs/index.html +1 -1
  159. package/apps/pwa/out/outputs/index.txt +22 -22
  160. package/apps/pwa/out/reflection/index.html +1 -1
  161. package/apps/pwa/out/reflection/index.txt +23 -23
  162. package/apps/pwa/out/scheduler/index.html +1 -1
  163. package/apps/pwa/out/scheduler/index.txt +23 -23
  164. package/apps/pwa/out/sessions/index.html +1 -1
  165. package/apps/pwa/out/sessions/index.txt +22 -22
  166. package/apps/pwa/out/settings/devices/index.html +1 -1
  167. package/apps/pwa/out/settings/devices/index.txt +23 -23
  168. package/apps/pwa/out/settings/index.html +1 -1
  169. package/apps/pwa/out/settings/index.txt +23 -23
  170. package/apps/pwa/out/setup/done/index.html +1 -1
  171. package/apps/pwa/out/setup/done/index.txt +23 -23
  172. package/apps/pwa/out/setup/index.html +1 -1
  173. package/apps/pwa/out/setup/index.txt +23 -23
  174. package/apps/pwa/out/share/index.html +1 -1
  175. package/apps/pwa/out/share/index.txt +23 -23
  176. package/apps/pwa/out/showroom/index.html +1 -1
  177. package/apps/pwa/out/showroom/index.txt +22 -22
  178. package/apps/pwa/out/tasks/index.html +1 -1
  179. package/apps/pwa/out/tasks/index.txt +23 -23
  180. package/apps/pwa/out/term/index.html +1 -1
  181. package/apps/pwa/out/term/index.txt +23 -23
  182. package/apps/pwa/out/today/index.html +1 -1
  183. package/apps/pwa/out/today/index.txt +23 -23
  184. package/apps/pwa/out/trace/index.html +1 -1
  185. package/apps/pwa/out/trace/index.txt +23 -23
  186. package/apps/pwa/out/vault/index.html +1 -1
  187. package/apps/pwa/out/vault/index.txt +23 -23
  188. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  189. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  190. package/apps/pwa/out/workflows/index.html +1 -1
  191. package/apps/pwa/out/workflows/index.txt +23 -23
  192. package/apps/pwa/out/workspace/index.html +1 -1
  193. package/apps/pwa/out/workspace/index.txt +23 -23
  194. package/apps/pwa/out/worktrees/index.html +1 -1
  195. package/apps/pwa/out/worktrees/index.txt +23 -23
  196. package/graphs/heal/heal-loop.yaml +1 -1
  197. package/graphs/implement-loop.yaml +1 -1
  198. package/graphs/orchestrator/orchestrator.yaml +59 -0
  199. package/graphs/orchestrator/recipes.yaml +18 -0
  200. package/graphs/release/recipes.yaml +6 -0
  201. package/graphs/release/release-loop.yaml +6 -1
  202. package/graphs/rhythm/daily.yaml +38 -0
  203. package/graphs/rhythm/recipes.yaml +18 -0
  204. package/graphs/rhythm/weekly.yaml +39 -0
  205. package/graphs/video/recipes.yaml +3 -0
  206. package/graphs/video/youtube-summary.yaml +17 -0
  207. package/package.json +1 -1
  208. package/scripts/audit-test-state-writes.ts +72 -17
  209. package/scripts/brand/check.ts +188 -0
  210. package/scripts/ci-typecheck-changed.ts +50 -18
  211. package/scripts/coord-post.sh +11 -1
  212. package/scripts/docs/generate-graph-anatomy.ts +315 -0
  213. package/scripts/docs-cli-check.ts +23 -6
  214. package/scripts/expose-rubric-check.ts +118 -0
  215. package/scripts/expose-rubric-suggest.ts +169 -0
  216. package/scripts/heal/run-role.ts +14 -0
  217. package/scripts/hq/standby-verify.ts +11 -7
  218. package/scripts/lecture-note/node.ts +31 -11
  219. package/scripts/lessons/handbook-page.ts +112 -0
  220. package/scripts/marketing/generate-ax-brief.ts +140 -0
  221. package/scripts/marketing/generate-feature-map.ts +118 -0
  222. package/scripts/release-loop/checklist-gate-node.ts +53 -14
  223. package/scripts/release-loop/gate-node.ts +89 -14
  224. package/scripts/release-loop/publish-node.ts +14 -1
  225. package/scripts/release-loop/unattended-release.ts +23 -1
  226. package/scripts/release-loop/vault-note-node.ts +428 -0
  227. package/scripts/release-story/draft.ts +220 -0
  228. package/scripts/rhythm/daily.ts +570 -0
  229. package/scripts/rhythm/weekly.ts +315 -0
  230. package/scripts/skills/public-check.ts +169 -0
  231. package/scripts/smart-merge/resync-shadow.ts +147 -0
  232. package/scripts/test-deterministic.ts +4 -8
  233. package/scripts/test-diet/effectiveness.ts +195 -0
  234. package/scripts/test-diet/lib.ts +16 -8
  235. package/scripts/test-diet/node.ts +77 -9
  236. package/scripts/ux/demo-rehearsal.ts +2 -2
  237. package/scripts/ux/lib/rehearsal-verdict.ts +1 -0
  238. package/scripts/video/youtube-summary.ts +133 -0
  239. package/skills/diagram-master/SKILL.md +42 -49
  240. package/skills/diagram-master/references/pyproject.toml +4 -1
  241. package/skills/diagram-master/references/routing-guide.md +4 -2
  242. package/skills/grill-me/SKILL.md +1 -0
  243. package/skills/youtube-master/SKILL.md +13 -13
  244. package/skills/youtube-master/package-lock.json +0 -545
  245. package/skills/youtube-master/package.json +2 -5
  246. package/skills/youtube-master/scripts/main.ts +2 -2
  247. package/skills/youtube-master/scripts/transcribe-local.ts +1 -1
  248. package/skills/youtube-master/src/util.ts +30 -7
  249. package/src/agent-env/profile.ts +161 -0
  250. package/src/agent-mission/driver.ts +3 -3
  251. package/src/agent-substrate/pr-reviewer.ts +2 -0
  252. package/src/ask-user-question/pending-questions.ts +5 -0
  253. package/src/auth/github-app-token.ts +112 -13
  254. package/src/autopilot/build/llm-conflict-merge.ts +73 -10
  255. package/src/autopilot/build/merge-intent.ts +53 -0
  256. package/src/briefing/brief-items.ts +121 -0
  257. package/src/chat/index.ts +2 -0
  258. package/src/claims/claims-ledger.ts +232 -0
  259. package/src/claims/claims-render.ts +166 -0
  260. package/src/cli/agent-env-cli.ts +21 -0
  261. package/src/cli/claims-cli.ts +94 -0
  262. package/src/cli/decisions-cli.ts +44 -3
  263. package/src/cli/docs-cli.ts +2 -0
  264. package/src/cli/doctor-cli.ts +23 -1
  265. package/src/cli/doctor-node-modules.ts +147 -0
  266. package/src/cli/flow-cli.ts +11 -0
  267. package/src/cli/freeze-cli.ts +45 -0
  268. package/src/cli/intake-cli.ts +46 -17
  269. package/src/cli/lesson-cli.ts +108 -0
  270. package/src/cli/logs-cli.ts +104 -2
  271. package/src/cli/logs-model-input.ts +131 -0
  272. package/src/cli/pod-cli.ts +2 -0
  273. package/src/cli/pr-cli.ts +29 -1
  274. package/src/cli/private-ledger-commands.ts +54 -4
  275. package/src/cli/pty-drive-verdict.ts +11 -2
  276. package/src/cli/release-cli.ts +190 -33
  277. package/src/cli/rfc-status.ts +91 -0
  278. package/src/cli/seat-requests-cli.ts +52 -0
  279. package/src/connectors/linear.ts +5 -3
  280. package/src/context-bus/condense.ts +15 -20
  281. package/src/context-bus/context-now-public.ts +96 -0
  282. package/src/context-bus/context-now-surfaces.ts +55 -2
  283. package/src/context-bus/context-now.ts +6 -3
  284. package/src/context-bus/coord-events.ts +6 -4
  285. package/src/context-bus/long-term-memory.ts +92 -11
  286. package/src/dashboard/index.ts +75 -22
  287. package/src/dashboard/slash-runtime/chat-persona.ts +66 -0
  288. package/src/dashboard/slash-runtime/dashboard-handlers.ts +99 -0
  289. package/src/dashboard/slash-runtime/loops-table.ts +77 -0
  290. package/src/decisions/decision-cards.ts +33 -7
  291. package/src/decisions/decision-ledger.ts +40 -1
  292. package/src/decisions/proact-meter.ts +286 -0
  293. package/src/decisions/proact1-lite.ts +86 -0
  294. package/src/decisions/web-push-decision-cards.ts +1 -1
  295. package/src/discord/sprint21-runtime.ts +16 -0
  296. package/src/discord-self-message.ts +8 -0
  297. package/src/discord-slash-wire.ts +31 -2
  298. package/src/domains/alert-audit.ts +271 -0
  299. package/src/domains/core-tools.ts +4 -3
  300. package/src/domains/ops-facts-tool.ts +31 -1
  301. package/src/domains/outbound-alert.ts +1 -1
  302. package/src/elanous-config-dir.ts +29 -4
  303. package/src/execution-loop/launch-gate.ts +29 -14
  304. package/src/flow/cards.ts +239 -0
  305. package/src/graph-runner/graph-tick.ts +4 -0
  306. package/src/graph-runner/runner.ts +30 -5
  307. package/src/harness/harness-cli-command.ts +259 -38
  308. package/src/harness/harness-incidents.ts +45 -12
  309. package/src/harness/harness-pod-dispatch.ts +102 -11
  310. package/src/harness/harness-queue-child.ts +3 -3
  311. package/src/harness/harness-queue.ts +583 -65
  312. package/src/harness/helper-repair.ts +87 -0
  313. package/src/harness/helper-scan.ts +151 -0
  314. package/src/harness/launch-stamp.ts +236 -0
  315. package/src/harness/ledger-write-guard-child.fixture.ts +12 -0
  316. package/src/harness/ledger-write-guard.ts +35 -0
  317. package/src/harness/model-input-observation.ts +37 -0
  318. package/src/harness/pod-memory-policy.ts +15 -0
  319. package/src/harness/repository-name.ts +55 -0
  320. package/src/hq/fence-audit.ts +90 -0
  321. package/src/hq/hq.ts +63 -5
  322. package/src/hq/lease.ts +4 -4
  323. package/src/hq/move-plan.ts +126 -0
  324. package/src/hq/promote.ts +332 -0
  325. package/src/index.ts +159 -27
  326. package/src/intake-plane/check.ts +63 -0
  327. package/src/intake-plane/digest.ts +37 -15
  328. package/src/intake-plane/front-classifier.ts +6 -0
  329. package/src/intake-plane/items.ts +3 -2
  330. package/src/intake-plane/lens.ts +177 -0
  331. package/src/intake-plane/linear-wish.ts +39 -11
  332. package/src/intake-plane/route.ts +145 -1
  333. package/src/intake-plane/wish-benchmark-ledger.ts +129 -0
  334. package/src/intake-plane/wish-card.ts +53 -2
  335. package/src/intake-plane/wish-folder.ts +5 -2
  336. package/src/intake-plane/wish-reply.ts +72 -0
  337. package/src/launch-head/seat-goal-queue-lock.fixture.ts +15 -0
  338. package/src/launch-head/seat-goal-queue.ts +198 -0
  339. package/src/lessons/lesson-import.ts +106 -0
  340. package/src/lessons/lesson-ledger.ts +206 -0
  341. package/src/loops/activity.ts +203 -0
  342. package/src/loops/budget.ts +63 -0
  343. package/src/loops/checker.ts +5 -3
  344. package/src/loops/contract/examples/orchestrator.loop.yaml +1 -1
  345. package/src/loops/loop-cli.ts +36 -4
  346. package/src/loops/neighbors.ts +40 -0
  347. package/src/loops/observe.ts +10 -0
  348. package/src/loops/orchestrator/author-depth.ts +220 -0
  349. package/src/loops/orchestrator/author-input-check.ts +82 -0
  350. package/src/loops/orchestrator/author-ledger.ts +232 -0
  351. package/src/loops/orchestrator/finish-rate.ts +175 -0
  352. package/src/loops/orchestrator/overlap.ts +51 -12
  353. package/src/loops/orchestrator/resync-shadow.ts +227 -0
  354. package/src/loops/orchestrator/seat-share.ts +56 -0
  355. package/src/loops/orchestrator/tick.ts +309 -0
  356. package/src/loops/orchestrator/traffic.ts +134 -13
  357. package/src/loops/registry.ts +535 -9
  358. package/src/loops/status-rows.ts +118 -0
  359. package/src/loops/verdict.ts +17 -5
  360. package/src/maturity/bot-slash-catalog.ts +66 -0
  361. package/src/maturity/feature-maturity.ts +9 -5
  362. package/src/maturity/tui-slash-availability.ts +31 -0
  363. package/src/mss/logging/log-store.ts +35 -4
  364. package/src/nexus/api/dashboard.ts +6 -1
  365. package/src/nexus/api/http-server.ts +33 -3
  366. package/src/nexus/api/loop-edges.ts +173 -0
  367. package/src/nexus/api/merge-approvals-gate.ts +1 -1
  368. package/src/nexus/api/schedules-read.ts +18 -1
  369. package/src/nexus/api/seat-requests.ts +79 -56
  370. package/src/nexus/api/task-cards-api.ts +44 -5
  371. package/src/nexus/api/workflows.ts +2 -1
  372. package/src/prompt-enhance/enhance.ts +6 -1
  373. package/src/release-loop/auto-start.ts +51 -0
  374. package/src/release-loop/checklist.ts +22 -4
  375. package/src/release-loop/feature-store.ts +71 -32
  376. package/src/release-loop/landing-freeze.ts +121 -0
  377. package/src/release-loop/merged-pr-checklist.ts +125 -0
  378. package/src/release-loop/placement.ts +302 -0
  379. package/src/release-loop/release-schedule.ts +3 -1
  380. package/src/seat-dispatch/ceo-commands.ts +2 -2
  381. package/src/seat-dispatch/seat-ask.ts +16 -3
  382. package/src/seat-dispatch/seat-questions.ts +58 -0
  383. package/src/seat-dispatch/seat-request-eligibility.ts +19 -0
  384. package/src/seat-dispatch/seat-request-ledger.ts +133 -0
  385. package/src/seat-loop/seat-loop.ts +531 -40
  386. package/src/self-dev/dev-pipeline.ts +14 -6
  387. package/src/self-dev/draft-sweep.ts +162 -6
  388. package/src/self-dev/draft-triage-rules.ts +25 -8
  389. package/src/self-dev/orchestrate.ts +15 -1
  390. package/src/self-dev/review-provider-fallback.ts +6 -1
  391. package/src/self-dev/run-store.ts +73 -1
  392. package/src/self-dev/run-supervisor.ts +57 -2
  393. package/src/self-dev/self-orchestrate-runtime.ts +5 -1
  394. package/src/self-implement/budget-gate.ts +29 -0
  395. package/src/self-implement/expose-gate.ts +47 -0
  396. package/src/self-implement/frozen-merges.ts +295 -0
  397. package/src/self-implement/gate-cli.ts +10 -4
  398. package/src/self-implement/gate-scope.ts +46 -9
  399. package/src/self-implement/goal-archive.ts +222 -0
  400. package/src/self-implement/goal-author.ts +43 -5
  401. package/src/self-implement/goal-clarification-escalation.ts +5 -0
  402. package/src/self-implement/goal-lookup.ts +147 -0
  403. package/src/self-implement/heal-intake.ts +197 -0
  404. package/src/self-implement/host-regate.ts +32 -2
  405. package/src/self-implement/l8-merge-queue.ts +132 -0
  406. package/src/self-implement/orchestrator.ts +299 -48
  407. package/src/self-implement/pipeline-shape.ts +1 -1
  408. package/src/self-implement/revert-guard.ts +97 -0
  409. package/src/self-implement/run-ledger.ts +3 -1
  410. package/src/self-implement/run-status-mapping.ts +2 -0
  411. package/src/self-implement/seams.ts +29 -9
  412. package/src/self-implement/self-implement-cli.ts +1 -0
  413. package/src/self-implement/speed-claims.ts +338 -0
  414. package/src/session/index.ts +2 -0
  415. package/src/steward/triage.ts +74 -3
  416. package/src/steward/working-backwards.ts +4 -1
  417. package/src/task-cards/card-cli.ts +10 -2
  418. package/src/task-cards/card-store.ts +10 -6
  419. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +63 -48
  420. package/src/task-orchestrator/surfaces/pod-command-job.ts +84 -9
  421. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +48 -0
  422. package/src/task-orchestrator/surfaces/pod-lease.ts +5 -2
  423. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +46 -6
  424. package/src/task-orchestrator/surfaces/pod-pool.ts +60 -8
  425. package/src/task-orchestrator/surfaces/self-implement-pod.ts +224 -49
  426. package/src/task-orchestrator/surfaces/self-implement.ts +5 -1
  427. package/src/telegram-commands.ts +27 -3
  428. package/src/telegram-loops-command.ts +44 -0
  429. package/src/telegram.ts +25 -4
  430. package/src/user-config.ts +146 -18
  431. package/src/version/packed-revision.json +1 -1
  432. package/src/workflow-runtime/nodes/bash.ts +9 -1
  433. package/src/workflow-runtime/storage.ts +31 -13
  434. package/apps/pwa/out/_next/static/D51l8jUBplJrIpWvENsDK/_buildManifest.js +0 -1
  435. package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +0 -1
  436. package/apps/pwa/out/_next/static/chunks/1394-931c9827067b2276.js +0 -1
  437. package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +0 -1
  438. package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +0 -1
  439. package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +0 -1
  440. package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +0 -1
  441. package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +0 -1
  442. package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +0 -1
  443. package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +0 -1
  444. package/apps/pwa/out/_next/static/chunks/3992-fe5c06d6b31f8b25.js +0 -25
  445. package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +0 -1
  446. package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +0 -1
  447. package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +0 -1
  448. package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +0 -2
  449. package/apps/pwa/out/_next/static/chunks/5250-c78ca295585bbf95.js +0 -1
  450. package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +0 -1
  451. package/apps/pwa/out/_next/static/chunks/5845-e6c003ce8d5fd36a.js +0 -1
  452. package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +0 -1
  453. package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +0 -1
  454. package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +0 -1
  455. package/apps/pwa/out/_next/static/chunks/6511-ff8360be3d65aaa4.js +0 -1
  456. package/apps/pwa/out/_next/static/chunks/6588-ba380869b67c4fa7.js +0 -1
  457. package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +0 -1
  458. package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +0 -1
  459. package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +0 -1
  460. package/apps/pwa/out/_next/static/chunks/7758-85c2b53f731da024.js +0 -1
  461. package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +0 -1
  462. package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +0 -1
  463. package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +0 -1
  464. package/apps/pwa/out/_next/static/chunks/8605-9da4678f8a310d18.js +0 -1
  465. package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +0 -1
  466. package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +0 -1
  467. package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +0 -1
  468. package/apps/pwa/out/_next/static/chunks/9269.d2a4795bc6812717.js +0 -1
  469. package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +0 -1
  470. package/apps/pwa/out/_next/static/chunks/9906-e7b8b5310a169dfe.js +0 -1
  471. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d16993889b125c42.js +0 -1
  472. package/apps/pwa/out/_next/static/chunks/app/approvals/page-e5b4e1b256f41076.js +0 -1
  473. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-e49b9b76c565a0b1.js +0 -1
  474. package/apps/pwa/out/_next/static/chunks/app/board/page-b4f032c893b21cc9.js +0 -1
  475. package/apps/pwa/out/_next/static/chunks/app/botlab/page-f14987d5850a31e0.js +0 -1
  476. package/apps/pwa/out/_next/static/chunks/app/bots/page-b433308aad13ceb6.js +0 -1
  477. package/apps/pwa/out/_next/static/chunks/app/chat/page-4c1dbb21861beab2.js +0 -1
  478. package/apps/pwa/out/_next/static/chunks/app/consult/page-c85b816e3b269dbb.js +0 -1
  479. package/apps/pwa/out/_next/static/chunks/app/control/page-f6bc66b919b2ea7d.js +0 -1
  480. package/apps/pwa/out/_next/static/chunks/app/design-check/page-60603c1e056c9b25.js +0 -1
  481. package/apps/pwa/out/_next/static/chunks/app/editor/page-a0ea8f5a2341ee6b.js +0 -1
  482. package/apps/pwa/out/_next/static/chunks/app/exec/page-45c5bf04b4a605e6.js +0 -1
  483. package/apps/pwa/out/_next/static/chunks/app/field/page-36c6c4183353ac9e.js +0 -1
  484. package/apps/pwa/out/_next/static/chunks/app/healthz/route-d16993889b125c42.js +0 -1
  485. package/apps/pwa/out/_next/static/chunks/app/inside/page-cb2eb0fd809980da.js +0 -1
  486. package/apps/pwa/out/_next/static/chunks/app/intake/page-31012264da8a341f.js +0 -1
  487. package/apps/pwa/out/_next/static/chunks/app/layout-50309aeb0bdd554a.js +0 -1
  488. package/apps/pwa/out/_next/static/chunks/app/live/page-bc00e964e855f4df.js +0 -1
  489. package/apps/pwa/out/_next/static/chunks/app/market/page-0696ee3fac9fe56c.js +0 -1
  490. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66c27608f651b866.js +0 -1
  491. package/apps/pwa/out/_next/static/chunks/app/morning/page-6ddd2109b7fc3a80.js +0 -1
  492. package/apps/pwa/out/_next/static/chunks/app/not-found-4e50b7298c1f69a5.js +0 -1
  493. package/apps/pwa/out/_next/static/chunks/app/observatory/page-7a2bf830038a2264.js +0 -1
  494. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1f327f18694fabca.js +0 -1
  495. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-e5cef2fe6e4028df.js +0 -1
  496. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cf596128d2b420ba.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/app/outputs/page-f672536f14f0969f.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/app/page-b52365662232db89.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/app/reflection/page-e7a6c80f288b1948.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-29a709616df9b050.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/app/sessions/page-fee842d7c2151129.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-78b40a344ed22f24.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/app/settings/page-75d199f448171014.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-a011dd5b0bff29c5.js +0 -1
  505. package/apps/pwa/out/_next/static/chunks/app/setup/layout-d16993889b125c42.js +0 -1
  506. package/apps/pwa/out/_next/static/chunks/app/setup/page-d92e32d51ab5157e.js +0 -1
  507. package/apps/pwa/out/_next/static/chunks/app/share/page-5f21aed6932ef375.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/app/showroom/page-1e1a53946804debf.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/app/tasks/page-a1833cd43d832601.js +0 -1
  510. package/apps/pwa/out/_next/static/chunks/app/today/page-2a511d320f762e06.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/app/trace/page-1c3e03f88dac1e21.js +0 -1
  512. package/apps/pwa/out/_next/static/chunks/app/vault/page-c4d5289c2ea97a89.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b91f8952deaeaf27.js +0 -1
  514. package/apps/pwa/out/_next/static/chunks/app/workflows/page-71b43a2588489987.js +0 -1
  515. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2b256feed331546f.js +0 -1
  516. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-505bb96d69a8622c.js +0 -1
  517. package/apps/pwa/out/_next/static/chunks/framework-172630e6aa5e2d7f.js +0 -1
  518. package/apps/pwa/out/_next/static/chunks/main-app-f0fe2d7477845181.js +0 -1
  519. package/apps/pwa/out/_next/static/chunks/main-fc98ac70b44eccb0.js +0 -1
  520. package/apps/pwa/out/_next/static/chunks/pages/_app-5bd7f10271f72413.js +0 -1
  521. package/apps/pwa/out/_next/static/chunks/pages/_error-97d0d85ea05fef9b.js +0 -1
  522. package/apps/pwa/out/_next/static/chunks/webpack-9e4d02d567b98240.js +0 -1
  523. package/apps/pwa/out/_next/static/css/128fb7fb670b2fbf.css +0 -1
  524. /package/apps/pwa/out/_next/static/{D51l8jUBplJrIpWvENsDK → LZcstaO9_NXypPFmujolv}/_ssgManifest.js +0 -0
@@ -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';
@@ -92,10 +94,13 @@ import type { DocumentReferenceStatus } from './self-implement-runtime.js';
92
94
  import { DEV_PIPELINE_SINK_SURFACE } from './self-cli-sink-surface.js';
93
95
  import { registerLoopAgentSafe, type LoopAgentInput } from '../domains/loop-agent-registry.js';
94
96
  import { originObservationFields } from '../agent/origin-observation.js';
97
+ import { resolveLaunchStamp, type LaunchStamp } from '../harness/launch-stamp.js';
95
98
  import { getHarnessSpace, normalizeSpaceId, resolveRunIdentity } from '../harness/harness-space.js';
96
99
  import { getElanousConfigDir } from '../elanous-config-dir.js';
97
100
  import { elanousStateRoot } from '../autopilot/state-paths.js';
98
101
  import { addNextMdReleaseNote, harnessReleaseNote, parseReleaseNoteSection, releaseNotesDir, renderReleaseNoteSection, writeReleaseNote, type ReleaseNoteFragment, type ReleaseNoteSection } from '../release-loop/release-note.js';
102
+ import { landingFreezeMessage } from '../release-loop/landing-freeze.js';
103
+ import { admitLandingMerge, owningRepoRoot } from './frozen-merges.js';
99
104
  import { plannedSelfImplBranch } from '../harness/worktree-branch-prefix.js';
100
105
  export { slugifyFeature } from '../harness/worktree-branch-prefix.js';
101
106
  import { AUTO_REVIEW_LABEL, resolveAutoReviewLabels } from './context-capsule.js';
@@ -179,7 +184,7 @@ import type { RunOutcome, ReworkBudgetVerdict } from './run-outcome.js';
179
184
  import type { EscalateTier } from './rework-policy.js';
180
185
  import type { ClassifyShadowCallLLM } from './classify-shadow.js';
181
186
  import { hasModuleLoadFailure, type GateTestFailure } from './gate-baseline.js';
182
- import { commitWorktree, defaultBranchRef } from './seams.js';
187
+ import { commitWorktree, defaultBranchRef, preservationHasChanges } from './seams.js';
183
188
  import { detectUncommittedWork } from './uncommitted-work.js';
184
189
  import { spawnSync } from 'node:child_process';
185
190
  import { blockedDraftDisposition } from './blocked-draft-policy.js';
@@ -757,6 +762,33 @@ function declaredPathsFromGoalDocumentText(document: string): { readable: boolea
757
762
  return parseDeclaredTargetPathsFromAsk(originalAsk);
758
763
  }
759
764
 
765
+ /** Only paths explicitly designated as targets in the ask or piece text protect an intentional revert. */
766
+ function declaredRevertGuardPaths(text: string): string[] {
767
+ const lines = targetScopedGoalText(text).split(/\r?\n/);
768
+ const pieceTargets: string[] = [];
769
+ for (let index = 0; index < lines.length; index++) {
770
+ const line = lines[index]!.replace(/^\s*(?:[-*+]\s+|>\s*)+/, '');
771
+ const declaration = /^(?:조각\s+)?(?:대상\s*경로|target\s*paths?)\s*[::]\s*(.*)$/i.exec(line);
772
+ if (!declaration) continue;
773
+ const fragments = [declaration[1]!];
774
+ while (index + 1 < lines.length) {
775
+ const next = lines[index + 1]!.trim().replace(/^(?:[-*+]\s+|>\s*)+/, '');
776
+ const paths = parseDeclaredTargetPathsFromAsk(`대상 경로: ${next}`).paths;
777
+ const tokens = next.split(/[·,]/).map((part) => part.trim().replace(/^`+|`+$/g, ''));
778
+ if (!tokens.length || !tokens.every((token) => token && paths.includes(normalizeDeclaredScopePath(token)))) break;
779
+ fragments.push(next);
780
+ index++;
781
+ }
782
+ pieceTargets.push(...parseDeclaredTargetPathsFromAsk(`대상 경로: ${fragments.join(',')}`).paths);
783
+ }
784
+ return pieceTargets;
785
+ }
786
+
787
+ function revertGuardTargetPaths(feature: string, goalDocument?: string): string[] {
788
+ const originalAsk = goalDocument ? extractVerbatimOriginalAsk(goalDocument)?.ask : undefined;
789
+ return [...new Set([...declaredRevertGuardPaths(feature), ...(originalAsk ? declaredRevertGuardPaths(originalAsk) : [])])];
790
+ }
791
+
760
792
  export function detectDeclaredScopeDiff(input: {
761
793
  goalDocument?: string | null;
762
794
  changedFiles?: readonly string[] | null;
@@ -972,7 +1004,7 @@ export interface SelfImplementSeams {
972
1004
  /** 변경 전 ref의 runtime source 전체. 생략 시 git에서 읽으며, 읽기 실패는 판정 미지로 남긴다. */
973
1005
  reviewBaselineObservationSource?: (cwd: string, base: string) => Promise<string | undefined>;
974
1006
  /** ④ gate — cwd 에서 bun test/build. postsync=정합 후 재게이트(merge-base 실행 범위). */
975
- gate: (cwd: string, ctx?: { runId?: string; mode?: 'postsync' }) => Promise<SelfImplementGateResult>;
1007
+ gate: (cwd: string, ctx?: { runId?: string; mode?: 'postsync'; round?: number }) => Promise<SelfImplementGateResult>;
976
1008
  /** Gate가 실행된 worktree HEAD가 local origin/main보다 뒤처진 커밋 수. 실패는 undefined(unknown)로 정직하게 남긴다. */
977
1009
  gateWorktreeBehindMain?: (cwd: string) => Promise<number | undefined>;
978
1010
  /** 변경 파일 목록 — 게이트 «라우터»가 「코드가 바뀌었나」를 판정하는 입력.
@@ -1006,10 +1038,13 @@ export interface SelfImplementSeams {
1006
1038
  /** Read-only cited-path observer. Omission uses the deterministic target-worktree filesystem observation. */
1007
1039
  observeCitedPathFacts?: (mustFix: readonly string[], cwd: string, round: number) => MustFixCitedPathFact[] | Promise<MustFixCitedPathFact[]>;
1008
1040
  /** PR을 열기 전에 보존할 산출물이 있는지 판별한다. `base` 대비 branch 고유 변경과 미커밋 변경을 함께 본다.
1009
- * 생략 시 기존 주입 seam 호환을 위해 산출물이 있다고 본다. */
1041
+ * Pod에서 생략 시 실제 git 상태로 판정한다. */
1010
1042
  preservationHasChanges?: (ctx: { cwd: string; base?: string }) => boolean | Promise<boolean>;
1011
1043
  /** Test seam for blocked environment branch persistence; return whether the branch was pushed. */
1012
- preserveBlockedBranch?: (ctx: { cwd: string; branch: string; title: string }) => boolean | Promise<boolean>;
1044
+ /** Test seam — the worktree commit to pin before main sync (default `git rev-parse HEAD`). */
1045
+ headCommit?: (cwd: string) => string | undefined;
1046
+ /** `commit` pins the pushed state; without it the worktree HEAD is pushed. */
1047
+ preserveBlockedBranch?: (ctx: { cwd: string; branch: string; title: string; commit?: string }) => boolean | Promise<boolean>;
1013
1048
  /** Hard-cap salvage evidence: a clean worktree and commits ahead of origin/main. */
1014
1049
  readReworkSalvageEvidence?: (cwd: string) => Promise<ReworkSalvageEvidence>;
1015
1050
  /** Starts exactly one detached `elanous dev --file <goal> --base <branch>` follow-up. */
@@ -1029,6 +1064,8 @@ export interface SelfImplementSeams {
1029
1064
  /** ★ ⑦ 병합 seam(2026-07-21·auto-merge) — 열린 PR 을 실제 병합(gh pr merge --squash). `matchHeadCommit`이 있으면
1030
1065
  * 검사한 고정 head 뒤 변경을 gh가 거부한다. autoMerge + 리뷰 clean 일 때만 호출(outward-facing·main 자율병합). */
1031
1066
  mergePr?: (opts: { number: number; cwd: string; matchHeadCommit?: string }) => Promise<{ merged: boolean; baseRefName?: string; mergeCommit?: string; detail?: string }>;
1067
+ /** Only successful merges trigger this injected, fail-closed draft inventory and mutation seam. */
1068
+ supersedeDraftsOnMerge?: (number: number, cwd: string) => Promise<void>;
1032
1069
  /** Post-merge cleanup is opt-in and injectable so tests never touch the filesystem. */
1033
1070
  postMergeCleanup?: {
1034
1071
  enabled: boolean;
@@ -1962,6 +1999,8 @@ export interface SelfImplementOptions {
1962
1999
  graphOverlays?: readonly GraphOverlaySpec[];
1963
2000
  /** Validated, run-local graph variant injected at launch; rejected plans never start the pipeline. */
1964
2001
  graphVariantPlan?: import('./graph-variant.js').GraphVariantPlan;
2002
+ /** Door this run was launched through. Absent means the ledger stamp reads `unknown`. */
2003
+ launch?: LaunchStamp;
1965
2004
  /** ★ K run-identity — 이 self-implement 호출의 per-run join anchor. 미지정 시 상속(env)→canonical mint.
1966
2005
  * 리워크 라운드 전부가 이 값을 공유한다(`elanous self run <runId>` 가 호출 전체를 조인). */
1967
2006
  runId?: string;
@@ -2130,6 +2169,8 @@ export interface SelfImplementResult {
2130
2169
  runId: string;
2131
2170
  ok: boolean;
2132
2171
  stage: SelfImplementStage;
2172
+ /** Verbatim must-fix findings retained on a review-blocked terminal result. */
2173
+ mustFix?: readonly string[];
2133
2174
  /** Pipeline node that produced this terminal result. */
2134
2175
  node: PipelineNodeId;
2135
2176
  /** Structured terminal disposition for every normal run exit. */
@@ -2179,6 +2220,8 @@ export interface SelfImplementResult {
2179
2220
  quotaExhaustionAssessment?: QuotaExhaustionAssessment;
2180
2221
  /** Automatic merge was authorized before this run reached its terminal result. */
2181
2222
  mergeApprovalReceived?: true;
2223
+ /** Completed implementation survived a failed main sync on the pushed branch. */
2224
+ harvestable?: true;
2182
2225
  merged?: boolean;
2183
2226
  /** Observed PR base for a confirmed merge; never inferred from the requested launch base. */
2184
2227
  mergedBase?: string;
@@ -2551,12 +2594,16 @@ function releaseNoteForRun(goalFile: string | undefined, feature: string): Relea
2551
2594
  }
2552
2595
 
2553
2596
  /** 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. */
2597
+ export function exposeGateLines(gateLog?: string): string[] {
2598
+ return (gateLog ?? '').split('\n').filter((line) => line.startsWith('[expose]'));
2599
+ }
2600
+
2554
2601
  export function gateNotRunSection(gateLog?: string): string[] {
2555
2602
  const lines = (gateLog ?? '').split('\n').filter((line) => line.startsWith('⚠️ caller test cap exceeded'));
2556
2603
  return lines.length ? ['', '## Gate · 안 돈 소비자 시험(상한 초과)', ...lines.map((line) => `- ${line.replace(/^⚠️ caller test cap exceeded — not run: /, '')}`)] : [];
2557
2604
  }
2558
2605
 
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 {
2606
+ 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
2607
  const note = releaseNote ?? releaseNoteForRun(goalFile, feature);
2561
2608
  const relaxation = planRevision?.relaxation
2562
2609
  ? ['', '## 감독 수용 기준 완화', `- 대상: ${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 +2624,16 @@ function prBody(feature: string, implSummary: string, gateLog?: string, review?:
2577
2624
  ...(review.shouldFix.length ? ['', '**should-fix (비블로킹):**', ...review.shouldFix.slice(0, 8).map((f) => `- ${f}`)] : [])] : []),
2578
2625
  ...(autoReviewDeclineReasons?.length ? ['', '## Auto-review label not applied', 'The requested auto-review label was declined by the autonomy gate:', ...autoReviewDeclineReasons.map((reason) => `- ${reason}`)] : []),
2579
2626
  ...gateNotRunSection(gateLog),
2580
- ...(gateLog ? ['', '## Gate', '```', gateLog.trim().slice(0, 3000), '```'] : []),
2627
+ ...(gateLog || revertGuard ? ['', '## Gate',
2628
+ // 경고는 잘린 gate 로그의 코드블록 밖에서도 한눈에 보인다.
2629
+ ...((gateLog ?? '').split('\n').filter((line) => line.startsWith('⚠️ 동작 검증 0:'))),
2630
+ ...exposeGateLines(gateLog),
2631
+ ...(gateLog ? ['```', gateLog.trim().slice(0, 3000), '```'] : []),
2632
+ ...(revertGuard?.reverted.length && revertGuard.reverted.length > (revertGuard.staleDeleted?.length ?? 0) ? [`- 되돌림 방지: ${revertGuard.reverted.length - (revertGuard.staleDeleted?.length ?? 0)}파일 제외`] : []),
2633
+ ...(revertGuard?.staleDeleted?.length ? [`- 되돌림 방지: 삭제 ${revertGuard.staleDeleted.length}파일 제외`] : []),
2634
+ ...(revertGuard?.outsideDeleted?.length ? [`- 삭제 경고: 대상 밖 ${revertGuard.outsideDeleted.length}파일 유지 (${revertGuard.outsideDeleted.join(', ')})`] : []),
2635
+ ...(revertGuard?.protected.length ? [`- 되돌림 경고: 대상 경로 ${revertGuard.protected.length}파일 유지 (${revertGuard.protected.join(', ')})`] : []),
2636
+ ...(revertGuard?.warning ? [`- 되돌림 검사 실패(커밋 계속): ${revertGuard.warning}`] : [])] : []),
2581
2637
  ...relaxation,
2582
2638
  '',
2583
2639
  '🤖 self-implement 오케스트레이터 (elanous 자율 구현·리뷰-게이트 병합)',
@@ -2807,7 +2863,7 @@ function blockedDraftPrBody(feature: string, implSummary: string, state: Blocked
2807
2863
  ...(state.gate === undefined
2808
2864
  ? ['(게이트 미실행)']
2809
2865
  : state.gate.log
2810
- ? ['```', state.gate.log.trim().slice(0, 3000), '```']
2866
+ ? [...state.gate.log.split('\n').filter((line) => line.startsWith('⚠️ 동작 검증 0:')), ...exposeGateLines(state.gate.log), '```', state.gate.log.trim().slice(0, 3000), '```'] // 경고는 절단된 로그 밖에도 보존한다.
2811
2867
  : ['(게이트 실행됨 · 로그 없음)']),
2812
2868
  '',
2813
2869
  '## Main-sync typecheck',
@@ -2924,6 +2980,7 @@ export function assembleBlockedDraftPrBody(
2924
2980
  JSON.stringify(classificationRecord),
2925
2981
  ...(state.reasonTruncated ? ['- reason truncated: true'] : []),
2926
2982
  ...(state.terminalStatus ? [`- child terminal status: reached=${state.terminalStatus.reached}; changed=${state.terminalStatus.changed}; toolCalls=${state.terminalStatus.toolCalls}; timedOut=${state.terminalStatus.timedOut}`] : []),
2983
+ ...(state.review?.mustFix.length ? ['## 마지막 리뷰 must-fix', ...state.review.mustFix.map((item) => `- ${item}`)] : []),
2927
2984
  ].join('\n');
2928
2985
  return preparePrBody(raw, persist, 'self-implement-blocked-draft-pr', essentialSuffix);
2929
2986
  }
@@ -3369,7 +3426,9 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
3369
3426
  const shardIdentity = parseRunShardIdentity(opts.feature);
3370
3427
  const observeOuter = makeRunObserver(runId, opts.goalId, debug.log.bind(debug), opts.seams.writeRunLedger, shardIdentity);
3371
3428
  try {
3372
- const origin = runOriginData();
3429
+ // 10-05 PODPROVIDER: record which child provider this run uses — «default» when the launch named none.
3430
+ const launch = opts.launch ?? resolveLaunchStamp();
3431
+ const origin = { ...runOriginData(), childProvider: opts.childLlm?.provider ?? 'default', ...(opts.childLlm?.model ? { childModel: opts.childLlm.model } : {}), launch };
3373
3432
  (opts.seams.writeRunLedger ?? appendRunLedgerEntry)({
3374
3433
  timestamp: new Date().toISOString(), runId, event: 'run-origin', data: origin,
3375
3434
  ...(opts.goalId ? { goalId: opts.goalId } : {}), ...shardIdentity,
@@ -3447,6 +3506,30 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
3447
3506
  let loopRegistered = false;
3448
3507
  let mergeApprovalReceived = false;
3449
3508
  let autoMergeAuthorizedBeforeSync = false;
3509
+ let finishedImplementationAtMainSync = false;
3510
+ // The finished implementation's commit, pinned before main sync starts. A timed-out mergeMain is not
3511
+ // cancellable and may keep moving HEAD in the same worktree, so the harvest pushes this SHA, never HEAD.
3512
+ let preSyncCommit: string | undefined;
3513
+ const preserveMergeHarvest = async (reason: string, inputChars?: number): Promise<boolean> => {
3514
+ if (!finishedImplementationAtMainSync || !worktreePath || !branch) return false;
3515
+ if (!preSyncCommit) {
3516
+ observeOuter('merge-harvest-push-failed', { branch, reason, error: 'pre-sync commit was not pinned' }, { level: 'warn' });
3517
+ return false;
3518
+ }
3519
+ try {
3520
+ const pushed = opts.seams.preserveBlockedBranch
3521
+ ? await opts.seams.preserveBlockedBranch({ cwd: worktreePath, branch, title: prTitle(opts.feature), commit: preSyncCommit })
3522
+ : runGitCommand(worktreePath, ['push', '-u', 'origin', `${preSyncCommit}:refs/heads/${branch}`]).status === 0;
3523
+ if (!pushed) throw new Error('branch push failed');
3524
+ // The worktree is left untouched: a late merge may still be writing it. The harvest source is the pinned commit.
3525
+ try { observeOuter('merge-abandoned-harvestable', { branch, reason, inputChars: inputChars ?? null, commit: preSyncCommit, worktree: 'left-as-is' }); }
3526
+ catch { /* observation cannot undo a confirmed push */ }
3527
+ return true;
3528
+ } catch (error) {
3529
+ observeOuter('merge-harvest-push-failed', { branch, reason, error: safeErrorDescription(error) }, { level: 'warn' });
3530
+ return false;
3531
+ }
3532
+ };
3450
3533
  let lastParsedSupervisorVerdict: ReworkBudgetVerdict | undefined;
3451
3534
  let lastParsedSupervisorReason: string | undefined;
3452
3535
  let providerErrorCount = 0;
@@ -3926,6 +4009,8 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
3926
4009
  () => credentialFailureCount > 0,
3927
4010
  hasCurrentProviderError,
3928
4011
  hasCurrentCredentialFailure,
4012
+ (commit: string | undefined) => { finishedImplementationAtMainSync = true; preSyncCommit = commit; },
4013
+ preserveMergeHarvest,
3929
4014
  );
3930
4015
  terminalNode = result.node;
3931
4016
  await mergeChildProviderErrors();
@@ -3998,9 +4083,11 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
3998
4083
  } catch (e) {
3999
4084
  if (e instanceof StepTimeoutError) {
4000
4085
  observeOuter('step-timeout', { step: e.step, ms: e.ms, branch, feature: opts.feature.slice(0, 80) }, { level: 'warn' });
4086
+ const harvestable = e.step === 'merge' && await preserveMergeHarvest('step-timeout');
4001
4087
  const result: SelfImplementResult = {
4002
4088
  runId,
4003
4089
  ok: false,
4090
+ ...(harvestable ? { harvestable: true } : {}),
4004
4091
  stage: 'timed-out',
4005
4092
  node: 'implement',
4006
4093
  ...resolveRunOutcome({ termination: 'abandoned' }),
@@ -4008,7 +4095,9 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
4008
4095
  ...(worktreePath ? { worktreePath } : {}),
4009
4096
  ...(lastParsedSupervisorReason ? { supervisorReason: lastParsedSupervisorReason } : {}),
4010
4097
  ...(mergeApprovalReceived || (e.step === 'merge' && autoMergeAuthorizedBeforeSync) ? { mergeApprovalReceived: true } : {}),
4011
- detail: worktreePath
4098
+ detail: harvestable
4099
+ ? `자율 단계 '${e.step}' wall-clock ${e.ms}ms 초과 — 구현 완료 · 수확 대기 (${branch}, worktree: ${worktreePath})`
4100
+ : worktreePath
4012
4101
  ? `자율 단계 '${e.step}' wall-clock ${e.ms}ms 초과 — hang 방지 종결(worktree 회수 경로: ${worktreePath})`
4013
4102
  : `자율 단계 '${e.step}' wall-clock ${e.ms}ms 초과 — hang 방지 종결(worktree 생성 전)`,
4014
4103
  };
@@ -4087,6 +4176,19 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
4087
4176
  throw e;
4088
4177
  } finally {
4089
4178
  if (loopRegistered) registerRunLoop('ended');
4179
+ const terminalStage = terminalResult?.stage ?? 'crashed';
4180
+ try {
4181
+ const mappedStatus = mapStageToRunStatus(terminalStage)?.runStatus;
4182
+ if (!terminalResult || mappedStatus === 'failed' || (mappedStatus === undefined && terminalResult.ok === false) || signalIncompleteState(roundClassifications).latestSignalIncomplete) {
4183
+ recordFailureEvent({
4184
+ source: 'harness-run', kind: terminalStage, ref: runId,
4185
+ summary: opts.feature.split(/\r?\n/, 1)[0]?.trim() || `Harness run ${runId} failed`,
4186
+ at: new Date().toISOString(),
4187
+ });
4188
+ }
4189
+ } catch (error) {
4190
+ logRunAwareFailSoft(runId, 'heal-intake-record-failed', { stage: terminalStage, error: safeErrorDescription(error) });
4191
+ }
4090
4192
  if (terminalResult) await recordGoalExecution(terminalResult);
4091
4193
  // ⛔⭐⭐⭐ 종결을 «못 적고» 죽는 길을 막는다 (2026-08-11 · 🅣 71차 · 대표 「종료 때 정리가 안 되면 설계 문제」).
4092
4194
  // `terminalResult` 는 «두 자리»에서만 선다 — 정상 종료 ⊕ `StepTimeoutError` 분기.
@@ -4488,6 +4590,8 @@ async function runSelfImplementInner(
4488
4590
  hasObservedCredentialFailure: () => boolean = hasCredentialFailure,
4489
4591
  hasCurrentProviderError: () => boolean = hasProviderError,
4490
4592
  hasCurrentCredentialFailure: () => boolean = hasCredentialFailure,
4593
+ onMainSyncStarted: (commit: string | undefined) => void = () => {},
4594
+ preserveMergeHarvest: (reason: string, inputChars?: number) => Promise<boolean> = async () => false,
4491
4595
  ): Promise<Omit<SelfImplementResult, 'runId'>> {
4492
4596
  const s = opts.seams;
4493
4597
  const pipelineStartedAtMs = Date.now();
@@ -5279,6 +5383,15 @@ async function runSelfImplementInner(
5279
5383
  let finishedChildCommitMissing = false;
5280
5384
  let finishedChildCommitFailure: string | undefined;
5281
5385
  let finishedChildCommitted = false;
5386
+ const revertGuard: RevertGuardResult = { reverted: [], protected: [] };
5387
+ const inspectStaleReverts = (targets: readonly string[]): void => {
5388
+ const inspected = dropStaleReverts(wt.path, targets, runId, getUserConfig().harness?.revertGuard?.depth, wt.resolvedBase);
5389
+ for (const file of inspected.reverted) if (!revertGuard.reverted.includes(file)) revertGuard.reverted.push(file);
5390
+ for (const file of inspected.protected) if (!revertGuard.protected.includes(file)) revertGuard.protected.push(file);
5391
+ for (const file of inspected.staleDeleted ?? []) if (!revertGuard.staleDeleted?.includes(file)) (revertGuard.staleDeleted ??= []).push(file);
5392
+ for (const file of inspected.outsideDeleted ?? []) if (!revertGuard.outsideDeleted?.includes(file)) (revertGuard.outsideDeleted ??= []).push(file);
5393
+ if (inspected.warning) revertGuard.warning = inspected.warning;
5394
+ };
5282
5395
  const classifyUnfinishedRun = (stage: SelfImplementStage, verdict?: ReworkBudgetVerdict, decomposition?: TerminalDecomposition): AbandonedClassificationResult => {
5283
5396
  const quotaExhaustionAssessment = quotaExhaustionAssessmentForRun();
5284
5397
  const worktree = unfinishedWorktreeObservation(wt.path);
@@ -5299,6 +5412,14 @@ async function runSelfImplementInner(
5299
5412
  mustFixReported: (review?.mustFix.length ?? 0) > 0,
5300
5413
  });
5301
5414
  };
5415
+ const podReviewBlockedDraft = process.env.ELANOUS_SUBSTRATE === 'pod';
5416
+ let blockedDraftNoChanges = false;
5417
+ let blockedDraftPrFailure: string | undefined;
5418
+ const reviewBlockedResult = (pr: { url: string; number: number } | undefined) => ({
5419
+ stage: blockedDraftNoChanges && podReviewBlockedDraft ? 'no-changes' as const : podReviewBlockedDraft && !pr ? 'aborted' as const : 'review-blocked' as const,
5420
+ mustFix: [...(review?.mustFix ?? [])],
5421
+ ...(pr ? { prUrl: pr.url, prNumber: pr.number } : {}),
5422
+ });
5302
5423
  const preserveBlockedArtifacts = async ({ stage, reason, verdict, gate: preservedGate, typecheck, decomposition, childSummary, reasonTruncated, failureKind, terminalStatus, salvageStatusExpected }: { stage: 'review-blocked' | 'gate-failed' | 'aborted'; reason: string; verdict?: ReworkBudgetVerdict; gate?: SelfImplementGateResult; typecheck?: { passed: boolean; log?: string }; decomposition?: TerminalDecomposition; childSummary?: string; reasonTruncated?: boolean; failureKind?: ReturnType<typeof classifyReviewProviderFailure> | null; terminalStatus?: ImplementTerminalStatus; salvageStatusExpected: boolean }): Promise<{ url: string; number: number } | undefined> => {
5303
5424
  let childSummaryArtifact: ChildSummaryArtifactRef | undefined;
5304
5425
  if (childSummary !== undefined) {
@@ -5391,6 +5512,7 @@ async function runSelfImplementInner(
5391
5512
  ...(childSummaryArtifact ? { childSummaryArtifactPath: childSummaryArtifact.path } : {}),
5392
5513
  };
5393
5514
  if (s.preservationHasChanges && !await s.preservationHasChanges({ cwd: wt.path, base: preservationBase })) {
5515
+ if (stage === 'review-blocked' && podReviewBlockedDraft) blockedDraftNoChanges = true;
5394
5516
  observe('rework-blocked-draft-pr', { ...observation, skipped: 'no-changes' });
5395
5517
  progress(stage, '보존할 변경이 없어 draft PR을 열지 않음');
5396
5518
  return undefined;
@@ -5424,8 +5546,9 @@ async function runSelfImplementInner(
5424
5546
  s.persistPrBodyArtifact,
5425
5547
  );
5426
5548
  // no-changes wins; worktree-only remains local even for environment stops.
5427
- const environmentStop = blockedDraftDisposition(classificationRecord.classification) === 'preserve-branch';
5428
- const worktreeOnly = opts.completion === 'worktree-only';
5549
+ const environmentStop = blockedDraftDisposition(classificationRecord.classification) === 'preserve-branch'
5550
+ && !(stage === 'review-blocked' && podReviewBlockedDraft);
5551
+ const worktreeOnly = opts.completion === 'worktree-only' && !(stage === 'review-blocked' && podReviewBlockedDraft);
5429
5552
  if (worktreeOnly || environmentStop) {
5430
5553
  let artifactPath: string | undefined;
5431
5554
  let artifactError: string | undefined;
@@ -5495,7 +5618,54 @@ async function runSelfImplementInner(
5495
5618
  }
5496
5619
  return undefined;
5497
5620
  }
5621
+ if (stage === 'review-blocked' && podReviewBlockedDraft && !s.preservationHasChanges
5622
+ && !preservationHasChanges(wt.path, preservationBase)) {
5623
+ blockedDraftNoChanges = true;
5624
+ observe('rework-blocked-draft-pr', { ...observation, skipped: 'no-changes' });
5625
+ progress(stage, '보존할 변경이 없어 draft PR을 열지 않음');
5626
+ return undefined;
5627
+ }
5628
+ // Pod review-blocked: a failed commit or PR still pushes the branch, and the result detail says what survived.
5629
+ const preserveReviewBlockedBranch = async (cause: string, commitFirst: boolean, suffix = ''): Promise<undefined> => {
5630
+ let pushed = false;
5631
+ try {
5632
+ if (s.preserveBlockedBranch) {
5633
+ pushed = await s.preserveBlockedBranch({ cwd: wt.path, branch: wt.branch, title: prTitle(opts.feature) });
5634
+ } else {
5635
+ if (commitFirst) {
5636
+ const commit = commitWorktree(wt.path, prTitle(opts.feature));
5637
+ if (!commit.ok && !/nothing to commit/.test(commit.out)) throw new Error(`로컬 브랜치 커밋 실패: ${commit.out}`);
5638
+ }
5639
+ const push = runGitCommand(wt.path, ['push', '-u', 'origin', `HEAD:refs/heads/${wt.branch}`]);
5640
+ if (push.status !== 0) throw new Error(push.stderr || push.stdout || 'git push failed');
5641
+ pushed = true;
5642
+ }
5643
+ if (!pushed) throw new Error('브랜치 push가 성공하지 않았음');
5644
+ } catch (pushError) {
5645
+ const pushReason = safeErrorDescription(pushError);
5646
+ blockedDraftPrFailure = `${cause}; 원격 브랜치 보존 실패: ${pushReason}; worktree: ${wt.path}`;
5647
+ observe('rework-blocked-draft-pr', { ...observation, error: cause, pushed: false, pushError: pushReason, branch: wt.branch }, { level: 'warn' });
5648
+ progress('aborted', blockedDraftPrFailure);
5649
+ return undefined;
5650
+ }
5651
+ blockedDraftPrFailure = `${cause}; 원격 브랜치 보존: ${wt.branch}${suffix}`;
5652
+ observe('rework-blocked-draft-pr', { ...observation, error: cause, pushed: true, branch: wt.branch }, { level: 'warn' });
5653
+ progress('aborted', blockedDraftPrFailure);
5654
+ return undefined;
5655
+ };
5498
5656
  progress('pr-opening', '중단 산출물을 draft PR로 보존…');
5657
+ if (stage === 'review-blocked' && podReviewBlockedDraft) {
5658
+ let commitError: string | undefined;
5659
+ try {
5660
+ const commit = commitWorktree(wt.path, prTitle(opts.feature));
5661
+ if (!commit.ok && !/nothing to commit/.test(commit.out)) commitError = commit.out.trim() || 'git commit failed';
5662
+ } catch (error) {
5663
+ commitError = safeErrorDescription(error);
5664
+ }
5665
+ if (commitError !== undefined) {
5666
+ return preserveReviewBlockedBranch(`review-blocked 작업 커밋 실패: ${commitError}`, false, `; 미커밋 작업은 worktree 에 남음: ${wt.path}`);
5667
+ }
5668
+ }
5499
5669
  let pr: { url: string; number: number };
5500
5670
  try {
5501
5671
  pr = await withStepTimeout(s.openPr({
@@ -5504,6 +5674,7 @@ async function runSelfImplementInner(
5504
5674
  head: wt.branch,
5505
5675
  ...(opts.base ? { base: opts.base } : {}),
5506
5676
  draft: true,
5677
+ ...(stage === 'review-blocked' ? { labels: ['review-blocked'] } : {}),
5507
5678
  cwd: wt.path,
5508
5679
  }), T.pr, 'pr');
5509
5680
  } catch (error) {
@@ -5525,6 +5696,10 @@ async function runSelfImplementInner(
5525
5696
  ...(artifactPath ? { prBodyArtifactPath: artifactPath } : {}),
5526
5697
  ...(artifactError ? { prBodyArtifactError: artifactError } : {}),
5527
5698
  }, { level: 'warn' });
5699
+ if (stage === 'review-blocked' && podReviewBlockedDraft) {
5700
+ return preserveReviewBlockedBranch(`draft PR 생성 실패: ${prError}`, true,
5701
+ `; ${artifactPath ? `판정 본문: ${artifactPath}` : `판정 본문 보존 실패: ${artifactError ?? 'unknown error'}`}`);
5702
+ }
5528
5703
  progress('aborted', artifactPath
5529
5704
  ? `중단 산출물의 draft PR 생성 실패(worktree·branch 보존 · 판정 본문 보존: ${artifactPath})`
5530
5705
  : `중단 산출물의 draft PR 생성 실패(worktree·branch 보존 · 판정 본문 보존 실패: ${artifactError ?? 'unknown error'})`);
@@ -5537,6 +5712,9 @@ async function runSelfImplementInner(
5537
5712
  ...(assembled.truncated ? { prBodyTruncated: true, prBodyOriginalChars: assembled.originalChars } : {}),
5538
5713
  });
5539
5714
  await flushPrComments(pr);
5715
+ if (stage === 'review-blocked') {
5716
+ try { debug.log('self-implement.terminal', 'review-blocked-pr', { runId, prNumber: pr.number, round: Math.max(0, round - 1) }); } catch { /* observation is fail-soft */ }
5717
+ }
5540
5718
  progress('pr-opened', `중단 산출물 draft PR 개설 (#${pr.number}·사람 판단 대기)`);
5541
5719
  return pr;
5542
5720
  };
@@ -6049,7 +6227,7 @@ async function runSelfImplementInner(
6049
6227
  if (reflectRejectedAudit.length && review) review.shouldFix = [...review.shouldFix, ...reflectRejectedAudit];
6050
6228
  const pr = await preserveBlockedArtifacts({ stage: 'review-blocked', verdict: parsedDecision.verdict, reason: parsedDecision.reason, gate, decomposition: terminalDecomposition, salvageStatusExpected: false });
6051
6229
  const quotaExhaustionAssessment = quotaExhaustionAssessmentForRun();
6052
- return { ok: false, stage: 'review-blocked', node: 'rework', ...terminal, ...supervisorOutcome, sessionId, worktreePath: wt.path, branch: wt.branch, gate, review, quotaExhaustionAssessment, ...(lastSupervisorReason ? { supervisorReason: lastSupervisorReason } : {}), ...(pr ? { prUrl: pr.url, prNumber: pr.number } : {}), detail: `rework judged unconvergeable after ${round} rework round(s): ${parsedDecision.reason}` };
6230
+ return { ok: false, node: 'rework', ...terminal, ...supervisorOutcome, sessionId, worktreePath: wt.path, branch: wt.branch, gate, review, quotaExhaustionAssessment, ...(lastSupervisorReason ? { supervisorReason: lastSupervisorReason } : {}), ...reviewBlockedResult(pr), detail: `${blockedDraftPrFailure ? `${blockedDraftPrFailure}; ` : ''}rework judged unconvergeable after ${round} rework round(s): ${parsedDecision.reason}` };
6053
6231
  }
6054
6232
  const baselineStop = noRetryBaseline();
6055
6233
  const reason = baselineStop.terminationReason ?? parsedDecision.reason;
@@ -6077,7 +6255,7 @@ async function runSelfImplementInner(
6077
6255
  if (isReviewRework(reworkKind)) {
6078
6256
  if (reflectRejectedAudit.length && review) review.shouldFix = [...review.shouldFix, ...reflectRejectedAudit];
6079
6257
  const pr = await preserveBlockedArtifacts({ stage: 'review-blocked', verdict: lastReworkVerdict, reason, gate, salvageStatusExpected: false });
6080
- return { ok: false, stage: 'review-blocked', node: 'rework', ...terminal, sessionId, worktreePath: wt.path, branch: wt.branch, gate, review, quotaExhaustionAssessment: quotaExhaustionAssessmentForRun(), ...(lastSupervisorVerdict === 'CONTRACT-CONFLICT' ? { supervisorVerdict: lastSupervisorVerdict } : {}), ...(pr ? { prUrl: pr.url, prNumber: pr.number } : {}), detail: reason };
6258
+ return { ok: false, node: 'rework', ...terminal, sessionId, worktreePath: wt.path, branch: wt.branch, gate, review, quotaExhaustionAssessment: quotaExhaustionAssessmentForRun(), ...(lastSupervisorVerdict === 'CONTRACT-CONFLICT' ? { supervisorVerdict: lastSupervisorVerdict } : {}), ...reviewBlockedResult(pr), detail: blockedDraftPrFailure ?? reason };
6081
6259
  }
6082
6260
  const baselineStop = noRetryBaseline();
6083
6261
  const pr = await preserveBlockedArtifacts({ stage: 'gate-failed', verdict: lastReworkVerdict, reason: baselineStop.terminationReason ?? reason, gate, salvageStatusExpected: false });
@@ -6093,32 +6271,34 @@ async function runSelfImplementInner(
6093
6271
  if (isReviewRework(reworkKind)) {
6094
6272
  if (reflectRejectedAudit.length && review) review.shouldFix = [...review.shouldFix, ...reflectRejectedAudit];
6095
6273
  const reason = `review must-fix unresolved after ${round - 1} rework round(s)`;
6096
- const acceptance = await acceptReviewBudget(
6097
- review?.mustFix ?? [],
6098
- reworkParts.some((part) => part.source === 'supervisor'),
6099
- );
6100
- if (acceptance.decision.action === 'accept') {
6101
- return {
6102
- ok: true,
6103
- stage: 'pr-opened',
6104
- node: 'open-pr',
6105
- ...terminal,
6106
- sessionId,
6107
- worktreePath: wt.path,
6108
- branch: wt.branch,
6109
- gate,
6110
- review,
6111
- mergeReason: 'review-budget-follow-up-required',
6112
- followUpMustFix: acceptance.decision.unresolvedMustFix,
6113
- followUpMustFixCount: acceptance.decision.unresolvedMustFix.length,
6114
- ...(acceptance.pr ? { prUrl: acceptance.pr.url, prNumber: acceptance.pr.number } : {}),
6115
- detail: `${reason}; accepted with ${acceptance.decision.unresolvedMustFix.length} follow-up must-fix item(s)`,
6116
- };
6274
+ if (!podReviewBlockedDraft) {
6275
+ const acceptance = await acceptReviewBudget(
6276
+ review?.mustFix ?? [],
6277
+ reworkParts.some((part) => part.source === 'supervisor'),
6278
+ );
6279
+ if (acceptance.decision.action === 'accept') {
6280
+ return {
6281
+ ok: true,
6282
+ stage: 'pr-opened',
6283
+ node: 'open-pr',
6284
+ ...terminal,
6285
+ sessionId,
6286
+ worktreePath: wt.path,
6287
+ branch: wt.branch,
6288
+ gate,
6289
+ review,
6290
+ mergeReason: 'review-budget-follow-up-required',
6291
+ followUpMustFix: acceptance.decision.unresolvedMustFix,
6292
+ followUpMustFixCount: acceptance.decision.unresolvedMustFix.length,
6293
+ ...(acceptance.pr ? { prUrl: acceptance.pr.url, prNumber: acceptance.pr.number } : {}),
6294
+ detail: `${reason}; accepted with ${acceptance.decision.unresolvedMustFix.length} follow-up must-fix item(s)`,
6295
+ };
6296
+ }
6117
6297
  }
6118
6298
  const pr = await preserveBlockedArtifacts({ stage: 'review-blocked', reason, gate, salvageStatusExpected: true });
6119
- const salvage = await salvageHardCap(canSalvage, pr);
6299
+ const salvage = blockedDraftNoChanges || blockedDraftPrFailure ? 'parked' : await salvageHardCap(canSalvage, pr);
6120
6300
  const disposition = { ...terminal, ...(salvage ? { salvage } : {}) };
6121
- return { ok: false, stage: 'review-blocked', node: 'rework', ...disposition, sessionId, worktreePath: wt.path, branch: wt.branch, gate, review, quotaExhaustionAssessment: quotaExhaustionAssessmentForRun(), ...(lastSupervisorVerdict === 'CONTRACT-CONFLICT' ? { supervisorVerdict: lastSupervisorVerdict } : {}), ...(pr ? { prUrl: pr.url, prNumber: pr.number } : {}), detail: reason };
6301
+ return { ok: false, node: 'rework', ...disposition, sessionId, worktreePath: wt.path, branch: wt.branch, gate, review, quotaExhaustionAssessment: quotaExhaustionAssessmentForRun(), ...(lastSupervisorVerdict === 'CONTRACT-CONFLICT' ? { supervisorVerdict: lastSupervisorVerdict } : {}), ...reviewBlockedResult(pr), detail: blockedDraftPrFailure ?? reason };
6122
6302
  }
6123
6303
  const baselineStop = noRetryBaseline();
6124
6304
  const reason = baselineStop.terminationReason ?? `gate failed after ${round - 1} rework round(s)`;
@@ -6306,6 +6486,7 @@ async function runSelfImplementInner(
6306
6486
  .filter((path) => !harnessSeededPaths.includes(path)
6307
6487
  && targets.some((target) => changedFileIsInsideDeclaredTarget(path, target)));
6308
6488
  if (eligible.length) {
6489
+ inspectStaleReverts(revertGuardTargetPaths(opts.feature, goalDocument));
6309
6490
  const message = `self-implement: ${prTitle(opts.feature)}`;
6310
6491
  let outcome: { ok: boolean; out: string };
6311
6492
  try {
@@ -6514,7 +6695,7 @@ async function runSelfImplementInner(
6514
6695
  return { ...stoppedBeforeGate, ...resolveRunOutcome({ termination: 'abandoned' }), sessionId, quotaExhaustionAssessment: quotaExhaustionAssessmentForRun(), ...(review ? { review } : {}) };
6515
6696
  }
6516
6697
  onNodeEntry(node('gate'), round);
6517
- gate = await stepTimeout(s.gate(wt.path, { runId }), T.gate, 'gate');
6698
+ gate = await stepTimeout(s.gate(wt.path, { runId, round }), T.gate, 'gate');
6518
6699
  } else {
6519
6700
  observe('gate-skipped-by-graph', { ...graphAuthorityFields(graphAuthority, graphTemplate), round, reason: gateRoute.reason, changedFileCount: gateRouteFiles?.length ?? null });
6520
6701
  gate = {
@@ -7008,9 +7189,13 @@ async function runSelfImplementInner(
7008
7189
  `Child completion: ${impl.summary.trim() || '(summary unavailable)'}`,
7009
7190
  ]);
7010
7191
  if (review) {
7011
- bufferPrComment(round, 'reviewer', `Round ${round}: review completed (${review.verdict}).`, [
7012
- `Review summary: ${review.summary.trim() || '(summary unavailable)'}`,
7013
- ]);
7192
+ if (review.reviewed) {
7193
+ bufferPrComment(round, 'reviewer', `Round ${round}: review completed (${review.verdict}).`, [
7194
+ `Review summary: ${review.summary.trim() || '(summary unavailable)'}`,
7195
+ ]);
7196
+ } else {
7197
+ bufferPrComment(round, 'reviewer', `Round ${round}: 리뷰 못 함(사유: ${review.failureReason?.trim() || '사유 미상'}) — 통과 판정 아님`, []);
7198
+ }
7014
7199
  }
7015
7200
  finalizeSupervisorDeliveries('no-next-rework-round');
7016
7201
  break;
@@ -7106,6 +7291,11 @@ async function runSelfImplementInner(
7106
7291
 
7107
7292
  if (s.mergeMain && s.commitWork) {
7108
7293
  onNodeEntry('main-sync', round);
7294
+ let goalDocument: string | undefined;
7295
+ if (opts.goalFile) {
7296
+ try { goalDocument = readFileSync(opts.goalFile, 'utf8'); } catch { /* no documented targets */ }
7297
+ }
7298
+ inspectStaleReverts(revertGuardTargetPaths(opts.feature, goalDocument));
7109
7299
  s.commitWork(wt.path, prTitle(opts.feature));
7110
7300
  // ⛔⭐ resume 경로와 «같은» 해석기를 쓴다 — 두 자리가 갈리면 같은 원인이 다른 얼굴로 나온다.
7111
7301
  const resolvedTarget = resolveDefaultBranchTarget(s.defaultBranchRef ?? defaultBranchRef, wt.path);
@@ -7123,7 +7313,23 @@ async function runSelfImplementInner(
7123
7313
  return { ok: false, stage: 'merge-conflict', node: 'main-sync', ...resolveRunOutcome({ termination: 'abandoned' }), ...decisionSignalResult, sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), detail: 'pre-PR main sync default-branch-unresolved' };
7124
7314
  }
7125
7315
  progress('gating', `최신 ${mergeTarget} 정합 (병렬 드리프트·LLM 충돌해결)…`);
7126
- const sync = await withStepTimeout(s.mergeMain(wt.path, mergeTarget), T.merge, 'merge');
7316
+ onMainSyncStarted((s.headCommit ?? ((cwd: string) => {
7317
+ const head = runGitCommand(cwd, ['rev-parse', '--verify', 'HEAD'], { encoding: 'utf8' });
7318
+ return head.status === 0 ? String(head.stdout).trim() || undefined : undefined;
7319
+ }))(wt.path));
7320
+ let sync: Awaited<ReturnType<NonNullable<SelfImplementSeams['mergeMain']>>>;
7321
+ try {
7322
+ sync = await withStepTimeout(s.mergeMain(wt.path, mergeTarget), T.merge, 'merge');
7323
+ } catch (error) {
7324
+ if (error instanceof StepTimeoutError) throw error;
7325
+ const harvestable = await preserveMergeHarvest('merge-interrupted');
7326
+ if (!harvestable) throw error;
7327
+ canAuto = false;
7328
+ mergeReason = 'main-sync-interrupted';
7329
+ recordMergeDecision();
7330
+ progress('merge-conflict', `⚠️ ${mergeTarget} 정합 중단 — 사람 수확 (${wt.branch})`);
7331
+ return { ok: false, stage: 'merge-conflict', node: 'main-sync', ...resolveRunOutcome({ termination: 'abandoned' }), ...decisionSignalResult, sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), harvestable: true, detail: `pre-PR main sync interrupted: ${safeErrorDescription(error)}` };
7332
+ }
7127
7333
  // ⛔⭐ `OBS-T96` 🅐 — 「무엇을 봤는지」를 남긴다. 수만 남기면 정합 실패가 «재현 불가»가 된다.
7128
7334
  observe('pre-pr-sync', mainSyncObservation(sync, mergeTarget, opts.base, wt.branch));
7129
7335
  if (sync.status === 'conflict-unresolved' || sync.status === 'error') {
@@ -7131,7 +7337,9 @@ async function runSelfImplementInner(
7131
7337
  canAuto = false;
7132
7338
  mergeReason = `main-sync-${sync.status}`;
7133
7339
  recordMergeDecision();
7134
- return { ok: false, stage: 'merge-conflict', node: 'main-sync', ...resolveRunOutcome({ termination: 'abandoned' }), ...decisionSignalResult, sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), detail: `pre-PR main sync ${sync.status}` };
7340
+ const harvestable = (sync.reason === 'conflict-input-too-large' || sync.reason === 'conflict-resolver-interrupted')
7341
+ && await preserveMergeHarvest(sync.reason, sync.inputChars);
7342
+ return { ok: false, stage: 'merge-conflict', node: 'main-sync', ...resolveRunOutcome({ termination: 'abandoned' }), ...decisionSignalResult, sessionId, worktreePath: wt.path, branch: wt.branch, gate, ...(review ? { review } : {}), ...(harvestable ? { harvestable: true } : {}), detail: harvestable ? sync.reason === 'conflict-input-too-large' ? `충돌 큼 — 사람 수확 (${wt.branch}, 입력 ${sync.inputChars}자)` : `충돌 해결 중단 — 사람 수확 (${wt.branch})` : `pre-PR main sync ${sync.status}` };
7135
7343
  }
7136
7344
  if (sync.status === 'llm-resolved' || sync.status === 'merged') {
7137
7345
  preservationBase = opts.base ?? mergeTarget;
@@ -7139,7 +7347,7 @@ async function runSelfImplementInner(
7139
7347
  ? `${mergeTarget} 충돌해결됨 — ${formatLlmMergeOutcome(sync)} — 통합 결과 full 재-gate…`
7140
7348
  : `${mergeTarget} 정합됨 — 통합 결과 full 재-gate…`);
7141
7349
  onNodeEntry('regate', round);
7142
- const regate = await withStepTimeout(s.gate(wt.path, { runId, mode: 'postsync' }), T.gate, 'gate');
7350
+ const regate = await withStepTimeout(s.gate(wt.path, { runId, mode: 'postsync', round }), T.gate, 'gate');
7143
7351
  if (!regate.passed) await onFailureVerdict('gate');
7144
7352
  // `mode` name/meaning stay: `full` = existing gate including tests. Both
7145
7353
  // conflict-resolved and clean-merge reuse this same `s.gate` invocation.
@@ -7269,7 +7477,7 @@ async function runSelfImplementInner(
7269
7477
  addNextMdReleaseNote(wt.path, releaseNote);
7270
7478
  const preparedPrBody = preparePrBody(
7271
7479
  [
7272
- prBody(opts.feature, impl.summary, gate.log, review, reviewIntent, declineReasons, harvestedForPr(), opts.goalFile, lastPlanRevision, releaseNote),
7480
+ prBody(opts.feature, impl.summary, gate.log, review, reviewIntent, declineReasons, harvestedForPr(), opts.goalFile, lastPlanRevision, releaseNote, revertGuard),
7273
7481
  ...(prEvidenceDecision.body ? ['', '---', '', prEvidenceDecision.body] : []),
7274
7482
  ].join('\n'),
7275
7483
  s.persistPrBodyArtifact,
@@ -7418,11 +7626,55 @@ async function runSelfImplementInner(
7418
7626
  progress('pr-opened', `호스트 재게이트 대기 (#${pr.number})`);
7419
7627
  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
7628
  }
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');
7629
+ // Landing freeze: the in-flight marker is written before the freeze is read, so a `freeze on` racing this merge waits.
7630
+ // The resume sweep merges from the repository that owns the run worktree (the worktree itself may be cleaned up).
7631
+ const heldEntry = () => {
7632
+ if (!checkedHeadCommit || !/^[0-9a-f]{40}$/i.test(checkedHeadCommit)) return null;
7633
+ const owner = owningRepoRoot(wt.path);
7634
+ // Without a known owning repository the held PR stays queued for a person; no sweep resumes it from a worktree.
7635
+ return owner ? { prNumber: pr.number, headCommit: checkedHeadCommit, repoRoot: owner } : { prNumber: pr.number, headCommit: checkedHeadCommit, repoRoot: wt.path, manual: true as const };
7636
+ };
7637
+ const landing = admitLandingMerge(heldEntry, undefined, {}, { prNumber: pr.number, repoRoot: owningRepoRoot(wt.path) ?? wt.path, ...(checkedHeadCommit ? { headCommit: checkedHeadCommit } : {}) });
7638
+ if (landing.kind === 'held' && !(checkedHeadCommit && /^[0-9a-f]{40}$/i.test(checkedHeadCommit))) {
7639
+ 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' };
7640
+ }
7641
+ if (landing.kind !== 'merge') {
7642
+ const detail = landing.kind === 'held' ? landingFreezeMessage(landing.freeze) : 'freeze lifted; a resume sweep is merging this PR';
7643
+ debug.log('harness.merge', 'frozen', { pr: pr.number, ...(landing.kind === 'held' ? { reason: landing.freeze.reason, until: landing.freeze.until } : { resumedElsewhere: true }) });
7644
+ progress('pr-opened', `#${pr.number} ready · ${detail}`);
7645
+ 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 };
7646
+ }
7647
+ // The marker and claim live until the merge call itself settles — a step timeout does not stop a merge already
7648
+ // sent — and anything that throws before the merge call is made releases them.
7649
+ let merging: ReturnType<NonNullable<typeof s.mergePr>>;
7650
+ try {
7651
+ onNodeEntry('merge', round);
7652
+ progress('merging', '자동 병합 (리뷰 clean·squash)…');
7653
+ merging = s.mergePr!({ number: pr.number, cwd: wt.path, matchHeadCommit: checkedHeadCommit! });
7654
+ } catch (error) { landing.end(); throw error; }
7655
+ // The default merge seam resolves only after `gh pr view` reads MERGED, so settling is the confirmation.
7656
+ const release = (merged: boolean) => {
7657
+ try { landing.end(merged); } catch (error) { observe('landing-release-failed', { number: pr.number, error: safeErrorDescription(error) }, { level: 'error' }); }
7658
+ };
7659
+ void merging.then((outcome) => release(outcome.merged === true), () => release(false));
7660
+ const m = await withStepTimeout(merging, T.pr, 'pr');
7424
7661
  observe('merged', { number: pr.number, merged: m.merged, detail: m.detail ?? null });
7425
7662
  if (m.merged) {
7663
+ const mergedBase = m.baseRefName?.trim() || observedPrBase;
7664
+ if (mergedBase && displayBranchName(mergedBase) === 'main') {
7665
+ try {
7666
+ const { syncMergedPrChecklist } = await import('../release-loop/merged-pr-checklist.js');
7667
+ const goalFile = opts.goalFile ?? (opts.goalId ? s.goalDocumentPathByGoalId?.(opts.goalId) : undefined);
7668
+ syncMergedPrChecklist(pr.number, wt.path, goalFile);
7669
+ } catch (error) {
7670
+ observe('merged-pr-checklist-sync-failed', { number: pr.number, error: safeErrorDescription(error) }, { level: 'warn' });
7671
+ }
7672
+ }
7673
+ try {
7674
+ await s.supersedeDraftsOnMerge?.(pr.number, wt.path);
7675
+ } catch (error) {
7676
+ observe('draft-supersede-on-merge-failed', { number: pr.number, error: safeErrorDescription(error) }, { level: 'warn' });
7677
+ }
7426
7678
  const directory = releaseNotesDir(elanousStateRoot());
7427
7679
  const fragment: ReleaseNoteFragment = {
7428
7680
  pr: pr.number,
@@ -7436,7 +7688,6 @@ async function runSelfImplementInner(
7436
7688
  observe('release-note-write-failed', { number: pr.number, prUrl: pr.url, directory, fragment, error: safeErrorDescription(error) }, { level: 'error' });
7437
7689
  throw new MergedReleaseNoteWriteError(pr.url, directory, fragment, error);
7438
7690
  }
7439
- const mergedBase = m.baseRefName?.trim() || observedPrBase;
7440
7691
  const defaultBranch = resolveDefaultBranchTarget(s.defaultBranchRef ?? defaultBranchRef, wt.path).target ?? undefined;
7441
7692
  progress('merged', formatAutoMergeSuccessMessage({ prNumber: pr.number, mergedBase, defaultBranch }));
7442
7693
  await runPostMergeCleanup(s.postMergeCleanup, wt.path, wt.branch, opts.goalFile, mergedBase && defaultBranch && displayBranchName(mergedBase) === displayBranchName(defaultBranch) ? defaultBranch : undefined, observe);