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
@@ -35,14 +35,15 @@ import type { DocumentReferenceStatus } from '../self-implement/self-implement-r
35
35
  import type { SelfDevGoal, OrchestrateSelfDevOptions, SelfDevJobResult } from './orchestrate.js';
36
36
  import { launchCapabilityObservation, resolveLaunchCapabilities } from './launch-capabilities.js';
37
37
  import { lookupEntrance, type EntranceId } from './entrance-registry.js';
38
+ import { resolveLaunchStamp } from '../harness/launch-stamp.js';
38
39
  import type { IngestionEntry } from '../agent-substrate/execution/ingestion-policy.js';
39
40
  import type { ReviewerContextItem } from '../agent-substrate/pr-reviewer.js';
40
41
  import { resolveRunIdentity, type RunIdSource } from '../harness/harness-space.js';
41
- import { addSelfDevRunParticipant, saveSelfDevRun } from './run-store.js';
42
+ import { addSelfDevRunParticipant, processBirthId, saveSelfDevRun } from './run-store.js';
42
43
  import { getUserConfig, resolveRoleLlm } from '../user-config.js';
43
44
  import { reviewReasoningEffort } from '../model-tier/review-effort.js';
44
45
  import { getProvider, inferProviderFromModel } from '../llm.js';
45
- import { buildReviewProviderAttempts, runReviewWithFallback, reviewFallbackModelsFromConfig } from './review-provider-fallback.js';
46
+ import { buildReviewProviderAttempts, defaultReviewFallbackModel, runReviewWithFallback, reviewFallbackModelsFromConfig } from './review-provider-fallback.js';
46
47
  import type { DefaultSeamsOptions } from '../self-implement/seams.js';
47
48
  import { harnessTargetOptions, resolveHarnessTarget, revalidateHarnessTarget, type HarnessTargetResolution } from '../self-implement/harness-target-options.js';
48
49
  import { provisionRepository, type RepoProvisionResult } from '../self-implement/repo-provision.js';
@@ -609,6 +610,7 @@ export function toSelfImplementOptions(text: string, plan: ResolvedDevPlan, seam
609
610
  ...(s.correlationId !== undefined ? { correlationId: s.correlationId } : {}),
610
611
  ...(s.branchName ? { branchName: s.branchName } : {}),
611
612
  ...(s.maxReworkRounds !== undefined ? { maxReworkRounds: s.maxReworkRounds } : {}),
613
+ launch: resolveLaunchStamp({ entrance: plan.entrance, queueId: process.env.ELANOUS_HARNESS_QUEUE_LAUNCH }),
612
614
  };
613
615
  }
614
616
 
@@ -1093,10 +1095,12 @@ export async function buildDefaultSelfImplementSeams(
1093
1095
  provider: inferProviderFromModel(model) ? getProvider(model) : undefined,
1094
1096
  label,
1095
1097
  });
1098
+ const primaryReviewAttempt = reviewAttemptForModel(reviewModel, 'primary');
1096
1099
  const reviewAttempts = buildReviewProviderAttempts(
1097
- reviewAttemptForModel(reviewModel, 'primary'),
1098
- reviewFallbackModels.map((model) => reviewAttemptForModel(model, model)),
1099
- );
1100
+ primaryReviewAttempt,
1101
+ [...reviewFallbackModels, defaultReviewFallbackModel(primaryReviewAttempt.provider?.name)]
1102
+ .map((model) => reviewAttemptForModel(model, model)),
1103
+ ).slice(0, 2);
1100
1104
  const defaultApiReview = async (prompt: string): Promise<string> => {
1101
1105
  const r = await runReviewWithFallback(
1102
1106
  reviewAttempts,
@@ -1718,7 +1722,11 @@ export async function runDevPipeline(spec: DevPipelineSpec, deps: DevPipelineDep
1718
1722
  if (spec.runId) {
1719
1723
  const runIdSource = spec.runIdSource ?? resolveRunIdentity({ explicit: spec.runId }).source;
1720
1724
  const now = Date.now();
1721
- saveSelfDevRun({ runId: spec.runId, createdAt: now, updatedAt: now, results: [], pid: process.pid });
1725
+ const seat = process.env.ELANOUS_HARNESS_SEAT;
1726
+ const pidStart = processBirthId(process.pid);
1727
+ saveSelfDevRun({ runId: spec.runId, createdAt: now, updatedAt: now, results: [], pid: process.pid,
1728
+ ...(pidStart ? { pidStart } : {}),
1729
+ ...(seat === 'OP' || seat === 'TC' || seat === 'MK' || seat === 'UX' ? { seat } : {}) });
1722
1730
  addSelfDevRunParticipant(spec.runId, {
1723
1731
  id: `process:${process.pid}`,
1724
1732
  kind: 'process',
@@ -1,6 +1,6 @@
1
1
  import { CLAIM_IDLE_HOURS, PR_LABELS, STALLED_DRAFT_HOURS } from '../github/pr-labels.js';
2
2
  import { debug } from '../debug/log.js';
3
- import { decideDraft, type DraftTriagePr } from './draft-triage-rules.js';
3
+ import { decideDraft, sameGoalPr, type DraftTriagePr } from './draft-triage-rules.js';
4
4
 
5
5
  export interface SweepDraft extends DraftTriagePr {
6
6
  readonly title: string;
@@ -31,6 +31,20 @@ export interface DraftSweepAdapters {
31
31
  hasFinalRunResult?(draft: SweepDraft, repository: string): Promise<boolean | undefined>;
32
32
  /** Per-file last change on the draft branch; undefined means file coverage is unverified. */
33
33
  getLatestFileChanges?(draft: SweepDraft, repository: string): Promise<Readonly<Record<string, string>> | undefined>;
34
+ /** Optional merge-time file inventory for the existing all-files-landed decision. */
35
+ getPrFiles?(repository: string, number: number): Promise<readonly string[] | undefined>;
36
+ /**
37
+ * Review and gate posture for the daily count. Omitted means unknown — not harvestable and not blocked.
38
+ * `review: 'pass'` is a review PASS; `mustFix` means must-fix remains; `gate: 'pass'` is a green gate.
39
+ */
40
+ getReviewGate?(draft: SweepDraft, repository: string): Promise<SweepReviewGate | undefined>;
41
+ }
42
+
43
+ /** Review PASS and gate posture used only by the daily old-draft count. */
44
+ export interface SweepReviewGate {
45
+ readonly review?: 'pass' | 'fail';
46
+ readonly mustFix?: boolean;
47
+ readonly gate?: 'pass' | 'fail';
34
48
  }
35
49
 
36
50
  export interface DraftSweepOptions {
@@ -65,9 +79,23 @@ export interface DraftSweepResult {
65
79
  claimed?: number;
66
80
  /** Running claims older than the idle window, including those kept by liveness. */
67
81
  claimExpired?: number;
82
+ /**
83
+ * One line per sweep: harness drafts older than 24h, split into closable (a same-goal landing exists),
84
+ * harvestable (review PASS and gate pass), and blocked (must-fix remains).
85
+ */
86
+ daily?: DraftSweepDaily;
68
87
  error?: string;
69
88
  }
70
89
 
90
+ /** Daily old-draft census. `date` is the sweep clock's UTC calendar day. */
91
+ export interface DraftSweepDaily {
92
+ readonly date: string;
93
+ readonly over24h: number;
94
+ readonly closable: number;
95
+ readonly harvestable: number;
96
+ readonly blocked: number;
97
+ }
98
+
71
99
  const stateLabels = PR_LABELS.filter((label) => label.axis === 'state');
72
100
  const label = (action: 'mark-stalled' | 'close', name?: string): string => {
73
101
  const match = PR_LABELS.find((entry) => entry.axis === 'state' && entry.sweep.action === action && (name === undefined || entry.name.endsWith(name)));
@@ -84,6 +112,80 @@ const PAGE_SIZE = 100;
84
112
  /** At most this many closes per tick, so a wrong rule cannot close everything at once. */
85
113
  export const DRAFT_SWEEP_CLOSE_CAP = 10;
86
114
 
115
+ /** Post-merge shortcut: only the existing superseded-by decision can authorize a close. */
116
+ export async function supersedeDraftsOnMerge(input: {
117
+ repository: string;
118
+ merged: SweepMergedPr & { createdAt: string; mergedAt: string };
119
+ adapters: Pick<DraftSweepAdapters, 'listDrafts' | 'listLiveBranches' | 'getRunStatus' | 'setLabels' | 'closeDraft'>
120
+ & Pick<Partial<DraftSweepAdapters>, 'getLatestFileChanges' | 'getPrFiles'>;
121
+ }): Promise<{ closed: number[]; kept: number[] }> {
122
+ const { repository, merged, adapters } = input;
123
+ const closed: number[] = [];
124
+ const kept: number[] = [];
125
+ let drafts: SweepDraft[] = [];
126
+ let error: string | undefined;
127
+ const failed: number[] = [];
128
+ let closeAttempts = 0;
129
+ try {
130
+ const [listedDrafts, liveBranches] = await Promise.all([
131
+ collectPages((page, size) => adapters.listDrafts(page, size, repository)),
132
+ adapters.listLiveBranches(repository),
133
+ ]);
134
+ drafts = listedDrafts;
135
+ if (!liveBranches || !Number.isInteger(merged.number) || !merged.branch || !merged.title
136
+ || !Number.isFinite(Date.parse(merged.createdAt)) || !Number.isFinite(Date.parse(merged.mergedAt))
137
+ || drafts.some((pr) => !Number.isInteger(pr.number) || !pr.branch || !Array.isArray(pr.labels)
138
+ || !Number.isFinite(Date.parse(pr.createdAt)))) throw new Error('Incomplete merge draft inventory');
139
+ const statuses = new Map<number, string | undefined>();
140
+ const latestChanges = new Map<number, Readonly<Record<string, string>> | undefined>();
141
+ const mergedFiles = await adapters.getPrFiles?.(repository, merged.number);
142
+ const landed = { ...merged, changedFiles: mergedFiles };
143
+ for (const draft of drafts) {
144
+ if (draft.number !== merged.number && draft.branch.startsWith('self-impl/') && !liveBranches.has(draft.branch)
145
+ && Date.parse(draft.createdAt) < Date.parse(merged.mergedAt)) {
146
+ statuses.set(draft.number, await adapters.getRunStatus(draft, repository));
147
+ if (draft.changedFiles?.length && adapters.getLatestFileChanges) {
148
+ latestChanges.set(draft.number, await adapters.getLatestFileChanges(draft, repository));
149
+ }
150
+ }
151
+ }
152
+ for (const listedDraft of drafts) {
153
+ const draft = { ...listedDraft, latestFileChanges: latestChanges.get(listedDraft.number) };
154
+ if (draft.number === merged.number || !draft.branch.startsWith('self-impl/') || liveBranches.has(draft.branch)
155
+ || Date.parse(draft.createdAt) >= Date.parse(merged.mergedAt)) {
156
+ kept.push(draft.number);
157
+ continue;
158
+ }
159
+ const decision = decideDraft({ draft, runStatus: statuses.get(draft.number),
160
+ mergedTwins: [landed], liveBranches, ageHours: NaN });
161
+ if (decision.action !== 'close' || !decision.reason.startsWith(`superseded-by #${merged.number}`)
162
+ || PR_LABELS.filter((entry) => entry.axis === 'state' && draft.labels.includes(entry.name)).length > 1
163
+ || closeAttempts >= DRAFT_SWEEP_CLOSE_CAP) {
164
+ kept.push(draft.number);
165
+ continue;
166
+ }
167
+ closeAttempts += 1;
168
+ const remove = stateLabels.filter((entry) => draft.labels.includes(entry.name) && entry.name !== supersededLabel).map((entry) => entry.name);
169
+ try {
170
+ // Label first: a failed label write must never strand a closed, unlabelled PR outside open-draft sweeps.
171
+ if (!draft.labels.includes(supersededLabel)) await adapters.setLabels(repository, draft.number, { add: supersededLabel, remove });
172
+ await adapters.closeDraft(repository, draft.number,
173
+ `Draft sweep: superseded-by #${merged.number} (https://github.com/${repository}/pull/${merged.number}). Branch preserved.`);
174
+ closed.push(draft.number);
175
+ } catch (failure) {
176
+ kept.push(draft.number);
177
+ failed.push(draft.number);
178
+ error = String(failure);
179
+ }
180
+ }
181
+ } catch (failure) {
182
+ error = String(failure);
183
+ kept.push(...drafts.map((draft) => draft.number).filter((number) => !closed.includes(number) && !kept.includes(number)));
184
+ }
185
+ debug.log('drafts.cleanup', 'superseded-on-merge', { merged: merged.number, closed, kept, ...(failed.length ? { failed } : {}), ...(error ? { error } : {}) });
186
+ return { closed, kept };
187
+ }
188
+
87
189
  async function collectPages<T>(fetch: (page: number, perPage: number) => Promise<readonly T[]>): Promise<T[]> {
88
190
  const rows: T[] = [];
89
191
  for (let page = 1; page <= 100; page++) {
@@ -95,6 +197,46 @@ async function collectPages<T>(fetch: (page: number, perPage: number) => Promise
95
197
  throw new Error('PR listing exceeded pagination limit');
96
198
  }
97
199
 
200
+ /** First line of a failure, with no stack — the draft-cleanup loop log shows this line as-is. */
201
+ export function sweepFailureReason(error: unknown): string {
202
+ const message = error instanceof Error ? error.message : String(error);
203
+ const line = message.split(/\r?\n/).map((part) => part.trim()).find((part) => part.length > 0 && !/^\s*at\s/.test(part));
204
+ return line && line.length > 0 ? line : 'sweep failed';
205
+ }
206
+
207
+ function harnessDraft(draft: SweepDraft): boolean {
208
+ return draft.branch.startsWith('self-impl/') || draft.labels.some((name) => name.startsWith('elanous:'));
209
+ }
210
+
211
+ /**
212
+ * Census of harness drafts older than 24h. Same-goal landing uses `sameGoalPr` unchanged.
213
+ * A draft counts as closable when a same-goal merged PR exists, harvestable when review PASS and the gate passed,
214
+ * and blocked when must-fix remains. Unknown review/gate is neither harvestable nor blocked.
215
+ */
216
+ export function draftSweepDaily(
217
+ drafts: readonly SweepDraft[],
218
+ merged: readonly SweepMergedPr[],
219
+ now: Date,
220
+ reviewGate: ReadonlyMap<number, SweepReviewGate | undefined>,
221
+ ): DraftSweepDaily {
222
+ const cutoff = now.getTime() - STALLED_DRAFT_HOURS * 3_600_000;
223
+ let over24h = 0;
224
+ let closable = 0;
225
+ let harvestable = 0;
226
+ let blocked = 0;
227
+ for (const draft of drafts) {
228
+ if (!harnessDraft(draft)) continue;
229
+ const created = Date.parse(draft.createdAt);
230
+ if (!Number.isFinite(created) || created > cutoff) continue;
231
+ over24h += 1;
232
+ if (merged.some((pr) => pr.number !== draft.number && sameGoalPr(draft, pr))) closable += 1;
233
+ const posture = reviewGate.get(draft.number);
234
+ if (posture?.mustFix === true) blocked += 1;
235
+ else if (posture?.review === 'pass' && posture.gate === 'pass') harvestable += 1;
236
+ }
237
+ return { date: now.toISOString().slice(0, 10), over24h, closable, harvestable, blocked };
238
+ }
239
+
98
240
  /** A failed inventory or liveness lookup is never treated as proof that a draft can be closed. */
99
241
  export async function runDraftSweep({ repository, adapters, apply = false, now = new Date() }: DraftSweepOptions): Promise<DraftSweepResult> {
100
242
  const result: DraftSweepResult = { repository, apply, complete: false, entries: [], counts: {} };
@@ -105,10 +247,12 @@ export async function runDraftSweep({ repository, adapters, apply = false, now =
105
247
  };
106
248
  const finish = (): DraftSweepResult => {
107
249
  debug.log('drafts.cleanup', 'summary', result.counts);
250
+ if (result.daily) debug.log('harness.drafts', 'daily', result.daily);
251
+ if (!result.complete && result.error) debug.log('drafts.cleanup', 'failed', { reason: sweepFailureReason(result.error) });
108
252
  return result;
109
253
  };
110
- let drafts: SweepDraft[];
111
- let merged: SweepMergedPr[];
254
+ let drafts: SweepDraft[] = [];
255
+ let merged: SweepMergedPr[] = [];
112
256
  let liveBranches: ReadonlySet<string> | undefined;
113
257
  try {
114
258
  [drafts, merged, liveBranches] = await Promise.all([
@@ -120,7 +264,8 @@ export async function runDraftSweep({ repository, adapters, apply = false, now =
120
264
  if (drafts.some((pr) => !Number.isInteger(pr.number) || !pr.branch || !Array.isArray(pr.labels) || !Number.isFinite(Date.parse(pr.createdAt)))
121
265
  || merged.some((pr) => !Number.isInteger(pr.number) || !pr.branch)) throw new Error('Invalid PR inventory');
122
266
  } catch (error) {
123
- result.error = String(error);
267
+ result.error = sweepFailureReason(error);
268
+ if (drafts.length > 0) result.daily = draftSweepDaily(drafts, merged, now, new Map());
124
269
  return finish();
125
270
  }
126
271
  let statuses: Map<number, string | undefined>;
@@ -142,9 +287,19 @@ export async function runDraftSweep({ repository, adapters, apply = false, now =
142
287
  }
143
288
  }
144
289
  } catch (error) {
145
- result.error = String(error);
290
+ result.error = sweepFailureReason(error);
146
291
  return finish();
147
292
  }
293
+ let reviewGate = new Map<number, SweepReviewGate | undefined>();
294
+ if (adapters.getReviewGate) {
295
+ try {
296
+ for (const draft of drafts) reviewGate.set(draft.number, await adapters.getReviewGate(draft, repository));
297
+ } catch (error) {
298
+ result.error = sweepFailureReason(error);
299
+ return finish();
300
+ }
301
+ }
302
+ result.daily = draftSweepDaily(drafts, merged, now, reviewGate);
148
303
  result.complete = true;
149
304
  let closes = 0;
150
305
  let unobserved = 0;
@@ -254,7 +409,8 @@ export async function runDraftSweep({ repository, adapters, apply = false, now =
254
409
  if (action === 'close') {
255
410
  await adapters.closeDraft(repository, draft.number, decision.reason === 'stale-unobserved'
256
411
  ? `Draft sweep: run unobserved — no run record reachable here, no host worktree for this branch, idle ≥${STALLED_DRAFT_HOURS}h. Closed as stalled. Branch preserved; reopen to restore.`
257
- : `Draft sweep: ${decision.reason}. Branch preserved.`);
412
+ : `Draft sweep: ${decision.reason}${decision.reason.startsWith('superseded-by #')
413
+ ? ` (https://github.com/${repository}/pull/${decision.reason.slice('superseded-by #'.length).match(/^\d+/)![0]})` : ''}. Branch preserved.`);
258
414
  }
259
415
  entry.applied = labelsChanged || action === 'close';
260
416
  } catch (error) {
@@ -38,6 +38,21 @@ const PROTECTED_LABELS: ReadonlySet<string> = new Set<string>([...PR_LABELS
38
38
  const goalId = (branch: string): string | undefined => /(?:^|[-/])goalid-([a-f0-9]+)(?=-|\/|$)/i.exec(branch)?.[1]?.toLowerCase();
39
39
  export const branchStem = (branch: string): string | undefined => /^self-impl\/(.+)-[a-f0-9]{8}-r[a-f0-9]+$/i.exec(branch)?.[1];
40
40
  const slot = (body?: string): string | undefined => /^칸:\s*(.+?)\s*$/m.exec(body ?? '')?.[1];
41
+ const goalText = (body?: string): string | undefined => /^골:\s*(\S.*?)\s*$/m.exec(body ?? '')?.[1];
42
+ // Auto titles name only files («src/harness: harness-queue.ts») — different goals share them, so they never identify a goal.
43
+ export const isAutoTitle = (title: string): boolean =>
44
+ /^[\w./-]+:\s*[^\s,]+\.[a-z0-9]+(?:\s*,\s*[^\s,]+\.[a-z0-9]+)*$/i.test(title.trim());
45
+
46
+ /** Same goal (TC 10-05): ① both carry a goal identifier → compare it ② otherwise exact title ⊕ both harness drafts ⊕ same branch stem, never an auto title ③ else false. */
47
+ export function sameGoalPr(a: DraftTriagePr, b: DraftTriagePr): boolean {
48
+ const idA = goalId(a.branch), idB = goalId(b.branch);
49
+ if (idA && idB) return idA === idB;
50
+ const textA = goalText(a.body), textB = goalText(b.body);
51
+ if (textA && textB) return textA === textB;
52
+ if (!a.title || a.title !== b.title || isAutoTitle(a.title)) return false;
53
+ const stemA = branchStem(a.branch);
54
+ return Boolean(stemA && a.branch.startsWith('self-impl/') && b.branch.startsWith('self-impl/') && stemA === branchStem(b.branch));
55
+ }
41
56
  const referencesDraft = (text: string, number: number): boolean =>
42
57
  new RegExp(`(?:\\(수확\\s*#${number}\\s*\\)|\\bsuperseded\\s*#${number}(?!\\d))`, 'i').test(text);
43
58
  const landingCommentReferencesDraft = (text: string, number: number): boolean =>
@@ -55,28 +70,30 @@ export function decideDraft({ draft, runStatus, mergedTwins, openDrafts, ageHour
55
70
  return { action: 'keep', reason: 'live' };
56
71
  }
57
72
  const lineage = goalId(draft.branch);
58
- const twin = mergedTwins.find((pr) => pr.number !== draft.number && (
59
- (draft.title && pr.title === draft.title) || (lineage && goalId(pr.branch) === lineage)
60
- ));
61
- if (twin) return { action: 'close', reason: `superseded-by #${twin.number}` };
73
+ const draftSlot = slot(draft.body);
62
74
  const stem = branchStem(draft.branch);
75
+ const compatible = (pr: DraftTriagePr): boolean =>
76
+ !(lineage && goalId(pr.branch) && goalId(pr.branch) !== lineage)
77
+ && !(draftSlot && slot(pr.body) && slot(pr.body) !== draftSlot);
78
+ const twin = mergedTwins.find((pr) => pr.number !== draft.number && compatible(pr) && sameGoalPr(draft, pr));
79
+ if (twin) return { action: 'close', reason: `superseded-by #${twin.number}` };
63
80
  const createdAt = Date.parse(draft.createdAt ?? '');
64
81
  const newer = stem && draft.title && Number.isFinite(createdAt) ? openDrafts
65
- ?.filter((pr) => pr.number !== draft.number && pr.title === draft.title && branchStem(pr.branch) === stem
82
+ ?.filter((pr) => pr.number !== draft.number && pr.title === draft.title && branchStem(pr.branch) === stem && sameGoalPr(draft, pr)
66
83
  && Number.isFinite(Date.parse(pr.createdAt ?? '')) && Date.parse(pr.createdAt!) > createdAt)
67
84
  .sort((a, b) => Date.parse(b.createdAt!) - Date.parse(a.createdAt!) || b.number - a.number)[0] : undefined;
68
85
  if (newer) return { action: 'close', reason: `duplicate-of-open #${newer.number}` };
69
- const harvest = mergedTwins.find((pr) => pr.number !== draft.number && (
86
+ const harvest = mergedTwins.find((pr) => pr.number !== draft.number && compatible(pr) && (
70
87
  [pr.mergeCommitMessage, pr.body].some((text) => text && referencesDraft(text, draft.number))
71
88
  || pr.landingVerifiedComments?.some((text) => landingCommentReferencesDraft(text, draft.number))
72
89
  ));
73
90
  if (harvest) return { action: 'close', reason: `superseded-by #${harvest.number} (harvest #${draft.number})` };
74
91
  const created = Date.parse(draft.createdAt ?? '');
75
92
  const files = draft.changedFiles;
76
- const draftSlot = slot(draft.body);
77
93
  const later = mergedTwins.filter((pr) => pr.number !== draft.number && Number.isFinite(created)
78
94
  && Number.isFinite(Date.parse(pr.mergedAt ?? '')) && Date.parse(pr.mergedAt!) > created
79
- && ((lineage && goalId(pr.branch) === lineage) || (draftSlot && slot(pr.body) === draftSlot)));
95
+ && compatible(pr) && ((lineage && goalId(pr.branch) === lineage)
96
+ || (draftSlot && slot(pr.body) === draftSlot) || (stem && branchStem(pr.branch) === stem)));
80
97
  if (files?.length && files.every((file) => {
81
98
  const changed = Date.parse(draft.latestFileChanges?.[file] ?? '');
82
99
  return Number.isFinite(changed) && changed >= created && later.some((pr) =>
@@ -260,12 +260,23 @@ export interface SelfDevJobResult {
260
260
  * (merged / pr-opened / gate-failed / review-blocked / pr-declined). */
261
261
  stage?: string;
262
262
  branch?: string;
263
+ /** Child confirmed the finished implementation was pushed for manual harvest. */
264
+ harvestable?: true;
263
265
  worktreePath?: string;
264
266
  prUrl?: string;
265
267
  prNumber?: number;
266
268
  merged?: boolean;
267
269
  /** Child commit SHA, when observed by the Pod/host. */
268
270
  checkedHeadCommit?: string;
271
+ /**
272
+ * This run's own `--base` (not main). A non-main completion mode
273
+ * (worktree-only, draft, `--base` other than main) lands commits here.
274
+ */
275
+ base?: string;
276
+ /** Child finished with GOAL-COMPLETE (`ok=true`). Distinct from a main merge. */
277
+ ok?: boolean;
278
+ /** Commits this child added against its own `--base`. 0 is not completion progress. */
279
+ commitsAheadOfBase?: number;
269
280
  /** A dependency is done but its result is not on main; no child was launched. */
270
281
  blockReason?: 'parent-unlanded';
271
282
  parentPrNumber?: number;
@@ -405,6 +416,7 @@ export type ResumeDisposition = 'skip' | 'rerun' | 'rerun-duplicate-risk';
405
416
  */
406
417
  export function classifyResumeDisposition(result: SelfDevJobResult): ResumeDisposition {
407
418
  if (hasDelivered(result)) return 'skip';
419
+ if (result.harvestable === true && result.branch) return 'skip';
408
420
  if (result.stage === 'pr-opened') return 'rerun-duplicate-risk';
409
421
  if (result.merged === false) return 'rerun';
410
422
  if (result.status !== 'done') return 'rerun';
@@ -621,6 +633,7 @@ export function classifyFailure(
621
633
  ) {
622
634
  return 'awaiting-human';
623
635
  }
636
+ if (result.harvestable === true && result.branch) return 'main-sync-blocked';
624
637
  if (result.status === 'done' && !hasDelivered(result)) return convergedFailureKind();
625
638
  // ⭐ 상류가 죽어 «취소»된 조각 — 종전엔 status 가 failed 도 done 도 아니라 null 로 사라졌다.
626
639
  // ⛔ 이것을 못 보면 「무엇이 상류를 기다리는가」를 슈퍼바이저가 영영 모른다.
@@ -1479,6 +1492,7 @@ export function orchestrateSelfDev(opts: OrchestrateSelfDevOptions): Promise<Sel
1479
1492
  ...(resumeDisposition ? { resumeDisposition } : {}),
1480
1493
  ...(disp?.stage ? { stage: disp.stage } : {}),
1481
1494
  ...(disp?.branch ? { branch: disp.branch } : {}),
1495
+ ...(disp?.harvestable === true && disp.branch ? { harvestable: true } : {}),
1482
1496
  ...(disp?.worktreePath ? { worktreePath: disp.worktreePath } : {}),
1483
1497
  ...(disp?.prUrl ? { prUrl: disp.prUrl } : {}),
1484
1498
  ...(disp?.prNumber !== undefined ? { prNumber: disp.prNumber } : {}),
@@ -1526,7 +1540,7 @@ export function orchestrateSelfDev(opts: OrchestrateSelfDevOptions): Promise<Sel
1526
1540
  const rm = opts.removeWorktree ?? defaultRemoveWorktree;
1527
1541
  let removed = 0;
1528
1542
  for (const r of out) {
1529
- if (!r.worktreePath || r.prUrl) continue;
1543
+ if (!r.worktreePath || r.prUrl || r.harvestable) continue;
1530
1544
  try { rm(r.worktreePath); removed++; } catch { /* fail-soft */ }
1531
1545
  }
1532
1546
  if (removed > 0) debug.log('self-dev.orchestrate', 'teardown', { removed });
@@ -73,13 +73,18 @@ export function buildReviewProviderAttempts(
73
73
  const out: ReviewProviderAttempt[] = [primary];
74
74
  const seen = new Set([primary.model]);
75
75
  for (const f of fallbacks) {
76
- if (!f.model.trim() || seen.has(f.model)) continue;
76
+ if (!f.model.trim() || seen.has(f.model) || (primary.provider && f.provider?.name === primary.provider.name)) continue;
77
77
  seen.add(f.model);
78
78
  out.push(f);
79
79
  }
80
80
  return out;
81
81
  }
82
82
 
83
+ /** A distinct provider is available even when the optional config list is empty. */
84
+ export function defaultReviewFallbackModel(primaryProvider?: string): string {
85
+ return resolveModelAlias(primaryProvider === 'grok' ? 'claude' : 'grok')!;
86
+ }
87
+
83
88
  export interface ReviewFallbackObservation {
84
89
  readonly attempt: number;
85
90
  readonly total: number;
@@ -16,8 +16,9 @@
16
16
  * Cf. PLAN-parallel-self-dev-orchestrator-2026-07-21 §5 (S3).
17
17
  */
18
18
  import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
19
+ import { dlopen, FFIType } from 'bun:ffi';
19
20
  import { randomUUID } from 'node:crypto';
20
- import { debug } from '../debug/log.js';
21
+ import { debug, redactSecretText } from '../debug/log.js';
21
22
  import { elanousStateRoot } from '../autopilot/state-paths.js';
22
23
  import { loadRunLedger, queryInterruptedRunLedgers, runLedgerDir, type RunLedgerEntry } from '../self-implement/run-ledger.js';
23
24
  import { join, resolve, sep } from 'node:path';
@@ -68,6 +69,8 @@ export interface SelfDevRunState {
68
69
  createdAt: number;
69
70
  updatedAt: number;
70
71
  results: SelfDevJobResult[];
72
+ /** Redacted, bounded one-line projection for loop readers; legacy checkpoints omit it. */
73
+ summaryLine?: string;
71
74
  /** Undefined means a legacy checkpoint has no dependency information; each key is a shard ID and its array lists predecessor shard IDs. */
72
75
  dependencies?: Record<string, string[]>;
73
76
  /**
@@ -93,6 +96,10 @@ export interface SelfDevRunState {
93
96
  * non-terminal goal 은 **실행중(running)** 이지 막힌(interrupted) 게 아니다. 죽었으면(오케스트레이터
94
97
  * killed) 진짜 interrupted. isPidAlive 로 구분해 라이브 잡을 parked/repair-signals 에서 제외. */
95
98
  pid?: number;
99
+ /** OS process birth identity, paired with pid to reject a recycled pid. */
100
+ pidStart?: string;
101
+ /** Seat fixed at launch; survives removal of the launch worktree. */
102
+ seat?: 'OP' | 'TC' | 'MK' | 'UX';
96
103
  }
97
104
 
98
105
  /** `<ELANOUS_STATE_DIR or ~/.elanous>/self-dev-runs`. */
@@ -103,6 +110,34 @@ export function selfDevRunsDir(stateDir?: string): string {
103
110
 
104
111
  const RUN_ID = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
105
112
 
113
+ /** Kernel process birth identity: Linux boot ID + start ticks or Darwin microseconds since epoch. */
114
+ export function processBirthId(pid: number, platform: NodeJS.Platform = process.platform): string | undefined {
115
+ if (!Number.isSafeInteger(pid) || pid < 1) return undefined;
116
+ try {
117
+ if (platform === 'linux') {
118
+ const stat = readFileSync(`/proc/${pid}/stat`, 'utf8');
119
+ const tail = stat.slice(stat.lastIndexOf(')') + 2).trim().split(/\s+/);
120
+ const ticks = tail[19]; // field 22 (starttime), after pid and parenthesized comm
121
+ const bootId = readFileSync('/proc/sys/kernel/random/boot_id', 'utf8').trim();
122
+ return ticks && /^\d+$/.test(ticks) && /^[0-9a-f-]{36}$/i.test(bootId)
123
+ ? `linux:${bootId}:${ticks}` : undefined;
124
+ }
125
+ if (platform === 'darwin') {
126
+ const libproc = dlopen('/usr/lib/libproc.dylib', {
127
+ proc_pidinfo: { args: [FFIType.i32, FFIType.i32, FFIType.u64, FFIType.ptr, FFIType.i32], returns: FFIType.i32 },
128
+ });
129
+ try {
130
+ const buf = new Uint8Array(136);
131
+ if (libproc.symbols.proc_pidinfo(pid, 3, 0n, buf, buf.length) !== buf.length) return undefined;
132
+ const view = new DataView(buf.buffer);
133
+ if (view.getUint32(12, true) !== pid) return undefined;
134
+ return `darwin:${view.getBigUint64(120, true) * 1_000_000n + view.getBigUint64(128, true)}`;
135
+ } finally { libproc.close(); }
136
+ }
137
+ } catch { /* Unreadable birth identity is not evidence of a match. */ }
138
+ return undefined;
139
+ }
140
+
106
141
  function runPath(runId: string, dir: string): string {
107
142
  if (!RUN_ID.test(runId)) throw new Error(`invalid self-dev run ID: ${runId}`);
108
143
  const root = resolve(dir);
@@ -154,6 +189,40 @@ function mergeParticipants(
154
189
  return [...byId.values()];
155
190
  }
156
191
 
192
+ const SUMMARY_LINE_LIMIT = 120;
193
+ const FINISHED_RESULT_STATUSES = new Set(['done', 'failed', 'cancelled', 'superseded']);
194
+
195
+ function boundedSummaryLine(text: string): string {
196
+ const redacted = redactSecretText(text.replace(/\s+/g, ' ').trim());
197
+ if (redacted.length <= SUMMARY_LINE_LIMIT) return redacted;
198
+ const head = redacted.slice(0, SUMMARY_LINE_LIMIT - 1).replace(/[\uD800-\uDBFF]$/, '');
199
+ return `${head}…`;
200
+ }
201
+
202
+ /** Read-only projection for loop readers; missing legacy fields are derived in memory. */
203
+ export function runSummaryLine(state: SelfDevRunState): string {
204
+ if (state.summaryLine !== undefined) return boundedSummaryLine(state.summaryLine);
205
+ const feature = state.goals?.[0]?.feature ?? state.results[0]?.feature ?? '';
206
+ const head = feature.split(/\r?\n/, 1)[0]?.trim() ?? '';
207
+ const header = /^「하니스로 구현」\s+(\S+?)\s+—\s*(.*)$/.exec(head);
208
+ const id = state.goals?.[0]?.goalId ?? header?.[1] ?? state.goals?.[0]?.id ?? state.runId;
209
+ const phrase = (header?.[2] ?? head).split(/[.!?。;;]/, 1)[0]?.trim() ?? '';
210
+ const finished = state.results.length > 0
211
+ && state.results.every((result) => FINISHED_RESULT_STATUSES.has(result.status));
212
+ if (finished) {
213
+ // A completed shard cannot hide a failed one merely by settling later.
214
+ const result = state.results.find(({ status }) => status === 'failed')
215
+ ?? state.results.find(({ status }) => status === 'cancelled')
216
+ ?? state.results.find(({ status }) => status === 'superseded')
217
+ ?? state.results.at(-1)!;
218
+ const stage = result.stage ?? result.status;
219
+ const prNumber = result.prNumber ?? Number(/\/pull\/(\d+)(?:[/?#]|$)/.exec(result.prUrl ?? '')?.[1]);
220
+ const pr = Number.isSafeInteger(prNumber) && prNumber > 0 ? ` · PR #${prNumber}` : '';
221
+ return boundedSummaryLine(`${id} · ${stage}${pr}`);
222
+ }
223
+ return boundedSummaryLine(`「하니스로 구현」 ${id}${phrase ? ` — ${phrase}` : ''}`);
224
+ }
225
+
157
226
  /** Persist a run checkpoint (fail-soft — never throws into the caller). */
158
227
  export function saveSelfDevRun(state: SelfDevRunState, dir = selfDevRunsDir()): void {
159
228
  try {
@@ -163,7 +232,10 @@ export function saveSelfDevRun(state: SelfDevRunState, dir = selfDevRunsDir()):
163
232
  const participants = mergeParticipants(current?.participants, state.participants);
164
233
  const saved = {
165
234
  ...state,
235
+ summaryLine: runSummaryLine({ ...state, goals: state.goals ?? current?.goals, summaryLine: undefined }),
166
236
  ...(participants === undefined ? {} : { participants }),
237
+ ...(current?.seat && !state.seat ? { seat: current.seat } : {}),
238
+ ...(current?.pidStart && current.pid === state.pid && !state.pidStart ? { pidStart: current.pidStart } : {}),
167
239
  ...(current?.parkedResolution && !state.parkedResolution ? { parkedResolution: current.parkedResolution } : {}),
168
240
  };
169
241
  writeSelfDevRun(saved, dir);