elanous 0.2.4 → 0.2.6

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 (455) hide show
  1. package/README.md +7 -6
  2. package/apps/pwa/out/404/index.html +1 -1
  3. package/apps/pwa/out/404.html +1 -1
  4. package/apps/pwa/out/_next/static/chunks/1169-696263ac7af8c4d4.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1883.c91a2648af4842fd.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1947-9d64872eeb9cfdec.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/2808-dc06482994b40f70.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/{5080-1613752891fcadb4.js → 2984-977cfa339fcd65a5.js} +3 -3
  9. package/apps/pwa/out/_next/static/chunks/3165-95515ca5f299a120.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/{8142-011e1f474efb8b33.js → 3190-e9282753e7ebde85.js} +1 -1
  11. package/apps/pwa/out/_next/static/chunks/3995-bee512a7063036ce.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 4240-7153881b0b377306.js} +1 -1
  13. package/apps/pwa/out/_next/static/chunks/4259-99614149577e77cb.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/4283-329eabeaf233e6cd.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/4328-cb5058b1acc2de5e.js +25 -0
  16. package/apps/pwa/out/_next/static/chunks/4801-ecee6ef62b349b18.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/5110-8058791dd25a2e14.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/{3403.4bc97b4066945a94.js → 5329.8b7e4bca684b6eec.js} +2 -2
  19. package/apps/pwa/out/_next/static/chunks/5330-c41e9670f594c38f.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/5423-e95fd125d1794349.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/5478-b5eae07dc79ea619.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/5787-96ee6642b9a661e5.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/6448-79d72368589f98bc.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/6522-9866c002b5531836.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/6729-b4a558b7aa891e0c.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/6969-cb03f9410b7d2e03.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/7007-3b9861091bb8dd21.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/{5791-bb82743fa6299562.js → 763-7d0e1913b5074e98.js} +1 -1
  29. package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 7633.0654fe6ea259c1ee.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/7930-8dc7e20fc6fc19ca.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/8071-187edbc22a86741f.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/8198-d4101ad70cb9a579.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/8250.4a2c5553a07cdf47.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/8293-123b91e7b0330dc0.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/8296-d6d5d87dfa911d41.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/8305-603963c33ee8eec3.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/8612-4a0066f20951b8fd.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/{2ea96958-e8c7e112de50b067.js → 8d0d9faf-97887ff99713b84e.js} +1 -1
  39. package/apps/pwa/out/_next/static/chunks/9181-cf4e1b61c4783f92.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → 91ff6dda-3b33d42d20657429.js} +1 -1
  41. package/apps/pwa/out/_next/static/chunks/9262-26c2ecbd5a67e4a2.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/9328-45abdebce4fbecb7.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/9355-dbc67205adf8745f.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/9763.255b90dd69875689.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/9933-66d31ec25c0231af.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-bd06fb55f4da0381.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/app/approvals/page-4e586142da455a25.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-889ad5cddcf45b0d.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/app/board/page-47ba401d56572c27.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/botlab/page-0b54f9c83458e038.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/bots/page-4fcc296ef6c8ed90.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/chat/page-fe9fda494a87f4e5.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/control/page-f9642e25ce129590.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/design-check/page-e1063e8ba5f3e2e7.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/editor/page-9d0df30027d8aa9d.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/app/healthz/route-bd06fb55f4da0381.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/intake/page-d97adf2c9455b8d0.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/layout-236b80d202110362.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/live/page-21a30f35f1a276ad.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/market/page-b1a5be357c4fa25d.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1705a60303f7f573.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/morning/page-76b337c11ad0e237.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/not-found-7c0f85fbb56f64c6.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/observatory/page-65ca16b20617b44d.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/page-e7f9b8fc29411c1d.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/reflection/page-92ea6213d1aeec04.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-a913a005aacb5476.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/sessions/page-2d78804a40dfdef6.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-08a99148e780ac00.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/settings/page-deb674e7ed36f815.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-49f8de0534d192d7.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/setup/layout-bd06fb55f4da0381.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/setup/page-ba162a28d2176e18.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/share/page-1ff1a37b550ab41a.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/showroom/page-ccaa698f56c0fbdb.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/tasks/page-53b944082de1d7db.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/term/page-9b70adf1552789bf.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/trace/page-30d482aa63241fe8.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/vault/page-aecf786f54651d8a.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6ab9b62c7b6e08f2.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/workflows/page-08949735455e0c9a.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/workspace/page-a1479005234a46c2.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3890e90bd232732.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/beb5cf10-9adcc10b83930090.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/framework-d68d41c88e05c801.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/main-app-11c63971b7a9593b.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/main-d09452353bd0d9cc.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/pages/_app-184d727cebeca767.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/pages/_error-21bbbbac99b720b1.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/{webpack-e5bea36f183b25e6.js → webpack-37b8bf78a3c6eec5.js} +1 -1
  91. package/apps/pwa/out/_next/static/css/eaa74e8bb2f62269.css +1 -0
  92. package/apps/pwa/out/_next/static/{zoQX5iTvXOsIvCjxCe1Ji → qMI0jnlZaBjjm4aIDYNGM}/_buildManifest.js +1 -1
  93. package/apps/pwa/out/approvals/index.html +1 -1
  94. package/apps/pwa/out/approvals/index.txt +29 -27
  95. package/apps/pwa/out/autopilot/index.html +1 -1
  96. package/apps/pwa/out/autopilot/index.txt +29 -27
  97. package/apps/pwa/out/board/index.html +1 -1
  98. package/apps/pwa/out/board/index.txt +29 -27
  99. package/apps/pwa/out/botlab/index.html +1 -1
  100. package/apps/pwa/out/botlab/index.txt +26 -24
  101. package/apps/pwa/out/bots/index.html +1 -1
  102. package/apps/pwa/out/bots/index.txt +26 -24
  103. package/apps/pwa/out/chat/index.html +1 -1
  104. package/apps/pwa/out/chat/index.txt +26 -24
  105. package/apps/pwa/out/control/index.html +1 -1
  106. package/apps/pwa/out/control/index.txt +29 -27
  107. package/apps/pwa/out/design-check/index.html +1 -1
  108. package/apps/pwa/out/design-check/index.txt +29 -27
  109. package/apps/pwa/out/editor/index.html +1 -1
  110. package/apps/pwa/out/editor/index.txt +29 -27
  111. package/apps/pwa/out/healthz +1 -1
  112. package/apps/pwa/out/icons/elanous-16.png +0 -0
  113. package/apps/pwa/out/icons/elanous-180.png +0 -0
  114. package/apps/pwa/out/icons/elanous-192.png +0 -0
  115. package/apps/pwa/out/icons/elanous-32.png +0 -0
  116. package/apps/pwa/out/icons/elanous-512.png +0 -0
  117. package/apps/pwa/out/index.html +1 -1
  118. package/apps/pwa/out/index.txt +26 -24
  119. package/apps/pwa/out/intake/index.html +1 -1
  120. package/apps/pwa/out/intake/index.txt +29 -27
  121. package/apps/pwa/out/live/index.html +1 -1
  122. package/apps/pwa/out/live/index.txt +29 -27
  123. package/apps/pwa/out/manifest.webmanifest +5 -2
  124. package/apps/pwa/out/market/index.html +1 -1
  125. package/apps/pwa/out/market/index.txt +26 -24
  126. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  127. package/apps/pwa/out/missions/placeholder/index.txt +26 -24
  128. package/apps/pwa/out/morning/index.html +1 -1
  129. package/apps/pwa/out/morning/index.txt +29 -27
  130. package/apps/pwa/out/observatory/index.html +1 -1
  131. package/apps/pwa/out/observatory/index.txt +29 -27
  132. package/apps/pwa/out/reflection/index.html +1 -1
  133. package/apps/pwa/out/reflection/index.txt +27 -25
  134. package/apps/pwa/out/scheduler/index.html +1 -1
  135. package/apps/pwa/out/scheduler/index.txt +29 -27
  136. package/apps/pwa/out/sessions/index.html +1 -1
  137. package/apps/pwa/out/sessions/index.txt +26 -24
  138. package/apps/pwa/out/settings/devices/index.html +1 -1
  139. package/apps/pwa/out/settings/devices/index.txt +29 -27
  140. package/apps/pwa/out/settings/index.html +1 -1
  141. package/apps/pwa/out/settings/index.txt +29 -27
  142. package/apps/pwa/out/setup/done/index.html +1 -1
  143. package/apps/pwa/out/setup/done/index.txt +29 -27
  144. package/apps/pwa/out/setup/index.html +1 -1
  145. package/apps/pwa/out/setup/index.txt +29 -27
  146. package/apps/pwa/out/share/index.html +1 -1
  147. package/apps/pwa/out/share/index.txt +29 -27
  148. package/apps/pwa/out/showroom/index.html +1 -1
  149. package/apps/pwa/out/showroom/index.txt +26 -24
  150. package/apps/pwa/out/sw-strategies.js +44 -0
  151. package/apps/pwa/out/sw.js +6 -44
  152. package/apps/pwa/out/tasks/index.html +1 -1
  153. package/apps/pwa/out/tasks/index.txt +29 -27
  154. package/apps/pwa/out/term/index.html +1 -1
  155. package/apps/pwa/out/term/index.txt +29 -27
  156. package/apps/pwa/out/trace/index.html +1 -1
  157. package/apps/pwa/out/trace/index.txt +29 -27
  158. package/apps/pwa/out/vault/index.html +1 -1
  159. package/apps/pwa/out/vault/index.txt +29 -27
  160. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  161. package/apps/pwa/out/workflows/chat-ui/index.txt +29 -27
  162. package/apps/pwa/out/workflows/index.html +1 -1
  163. package/apps/pwa/out/workflows/index.txt +29 -27
  164. package/apps/pwa/out/workspace/index.html +1 -1
  165. package/apps/pwa/out/workspace/index.txt +29 -27
  166. package/apps/pwa/out/worktrees/index.html +1 -1
  167. package/apps/pwa/out/worktrees/index.txt +29 -27
  168. package/catalog/resources.yaml +6 -6
  169. package/docs/goal-context/steward-directives-2026-09-28.md +35 -37
  170. package/graphs/landing/landing-heal.yaml +1 -1
  171. package/graphs/release/recipes.yaml +11 -1
  172. package/graphs/release/release-loop.yaml +9 -2
  173. package/graphs/video/README.md +1 -0
  174. package/graphs/video/explainer-line.yaml +58 -0
  175. package/package.json +3 -1
  176. package/plugins/card-followup/README.md +33 -0
  177. package/plugins/card-followup/examples/card-sample.json +7 -0
  178. package/plugins/card-followup/graphs/card-followup.yaml +16 -0
  179. package/plugins/card-followup/graphs/recipes.yaml +12 -0
  180. package/plugins/card-followup/graphs/run-step.ts +172 -0
  181. package/plugins/card-followup/plugin.json +25 -0
  182. package/plugins/geo-check/README.md +13 -0
  183. package/plugins/geo-check/examples/brand-sample.json +8 -0
  184. package/plugins/geo-check/graphs/geo-check.yaml +20 -0
  185. package/plugins/geo-check/graphs/recipes.yaml +18 -0
  186. package/plugins/geo-check/graphs/run-step.ts +177 -0
  187. package/plugins/geo-check/plugin.json +24 -0
  188. package/plugins/job-coach/.codex-plugin/plugin.json +18 -1
  189. package/plugins/job-coach/README.md +42 -0
  190. package/plugins/job-coach/connectors/ncs/server.ts +4 -3
  191. package/plugins/job-coach/graphs/gap-judge.ts +44 -0
  192. package/plugins/job-coach/graphs/recipes.yaml +27 -0
  193. package/plugins/job-coach/graphs/report-enterprise.yaml +28 -0
  194. package/plugins/job-coach/graphs/run-step.ts +211 -17
  195. package/plugins/job-coach/plugin.json +16 -3
  196. package/plugins/job-coach/skills/enterprise-ai-fit/SKILL.md +8 -0
  197. package/plugins/job-coach/skills/enterprise-competency/SKILL.md +8 -0
  198. package/plugins/job-coach/skills/enterprise-needs/SKILL.md +8 -0
  199. package/plugins/job-coach/skills/enterprise-roadmap/SKILL.md +8 -0
  200. package/plugins/job-coach/skills/enterprise-task-analysis/SKILL.md +8 -0
  201. package/plugins/job-coach/skills/interview-to-profile/SKILL.md +18 -1
  202. package/scripts/backup/elanous-backup.sh +4 -1
  203. package/scripts/build-docs-site.ts +4 -3
  204. package/scripts/coord-channel-watch.sh +23 -4
  205. package/scripts/coord-post.sh +49 -12
  206. package/scripts/coord-tracks.json +5 -5
  207. package/scripts/daemon-port-baseline.txt +1 -2
  208. package/scripts/eval-bot-vs-grok.ts +79 -0
  209. package/scripts/eval-grok-live-baseline.ts +36 -0
  210. package/scripts/fixtures/front-route-corpus.sample.json +17 -0
  211. package/scripts/fleet/node-setup.sh +46 -7
  212. package/scripts/landing-heal/must-fix-match.ts +26 -0
  213. package/scripts/landing-heal/run-mission.ts +114 -20
  214. package/scripts/lib/android-test-device.ts +32 -0
  215. package/scripts/lib/front-route-measurement.ts +131 -0
  216. package/scripts/lib/tui-sim-keys.ts +42 -0
  217. package/scripts/measure-front-route-classifier.ts +56 -0
  218. package/scripts/media/ocr-leak-scan.sh +21 -0
  219. package/scripts/public-export.ts +2 -3
  220. package/scripts/public-import-pr.ts +80 -6
  221. package/scripts/publish-internal-dist.ts +2 -3
  222. package/scripts/red-sweep-diff.ts +1 -1
  223. package/scripts/release-loop/auto-approve-node.ts +72 -0
  224. package/scripts/release-loop/checklist-gate-node.ts +35 -0
  225. package/scripts/release-loop/gate-diff.ts +29 -0
  226. package/scripts/release-loop/gate-node.ts +366 -45
  227. package/scripts/release-loop/publish-node.ts +7 -3
  228. package/scripts/release-loop/release-docs.ts +20 -13
  229. package/scripts/release-loop/shard-plan.ts +47 -0
  230. package/scripts/release-loop/test-durations.ts +63 -0
  231. package/scripts/release-loop/upgrade-node.ts +3 -2
  232. package/scripts/test-audit-coverage-per-file.sh +16 -0
  233. package/scripts/test-audit-meaningless.ts +88 -0
  234. package/scripts/test-audit-overlap-and-weak.ts +73 -0
  235. package/scripts/test-deterministic.ts +27 -5
  236. package/scripts/tui-sim.ts +59 -14
  237. package/scripts/ux/bump-installed.sh +20 -0
  238. package/scripts/ux/lib/cdp.ts +72 -0
  239. package/scripts/ux/record-page.ts +53 -0
  240. package/skills/explainer-video/SKILL.md +117 -0
  241. package/skills/explainer-video/engine/brand/elanous-mark-on-dark.svg +1 -0
  242. package/skills/explainer-video/engine/build.mjs +431 -0
  243. package/skills/explainer-video/engine/reel.mjs +128 -0
  244. package/skills/explainer-video/engine/reel.sh +8 -0
  245. package/skills/explainer-video/engine/vo.ts +79 -0
  246. package/skills/explainer-video/examples/elanous-0.2.5/script.json +155 -0
  247. package/src/agent/nested-agent-env.ts +1 -0
  248. package/src/agent-mission/driver.ts +95 -16
  249. package/src/agent-mission/mission-cli.ts +6 -0
  250. package/src/agent-mission/resource-ladder.ts +73 -25
  251. package/src/auth/acp-token.ts +16 -0
  252. package/src/auth/github-app-token.ts +112 -0
  253. package/src/autopilot/gh-rest-fallback.ts +177 -0
  254. package/src/autopilot/mission-origin.ts +20 -4
  255. package/src/autopilot/mission-phase-track.ts +11 -1
  256. package/src/autopilot/pr-manager.ts +51 -4
  257. package/src/autopilot/track-agent.ts +3 -3
  258. package/src/autopilot/track-courier.ts +2 -2
  259. package/src/boot/acp-server.ts +2 -16
  260. package/src/boot/daemon-tools/web-search.ts +2 -1
  261. package/src/boot/ws-bridge.ts +11 -0
  262. package/src/chat/index.ts +37 -8
  263. package/src/chat/log-scroll-key.ts +12 -0
  264. package/src/chat/submit-trace.ts +30 -0
  265. package/src/cli/agent-cli.ts +7 -2
  266. package/src/cli/decisions-cli.ts +94 -0
  267. package/src/cli/doctor-cli.ts +40 -54
  268. package/src/cli/doctor-readiness.ts +16 -24
  269. package/src/cli/intake-cli.ts +1 -1
  270. package/src/cli/mcp-call.ts +9 -0
  271. package/src/cli/mcp-gateway-cli.ts +7 -3
  272. package/src/cli/nexus-ios-bind.ts +25 -8
  273. package/src/cli/phone-link.ts +117 -0
  274. package/src/cli/plugin-cli.ts +136 -11
  275. package/src/cli/pr-lineage.ts +2 -1
  276. package/src/cli/private-ledger-commands.ts +31 -0
  277. package/src/cli/pwa-start.ts +3 -0
  278. package/src/cli/release-cli.ts +97 -7
  279. package/src/cli/self-update.ts +7 -4
  280. package/src/cli/start-cli.ts +33 -14
  281. package/src/dashboard/acp-boot.ts +3 -0
  282. package/src/dashboard/index.ts +62 -13
  283. package/src/dashboard/input/no-provider-reply.ts +13 -0
  284. package/src/dashboard/slash-runtime/unknown-slash-reply.ts +78 -0
  285. package/src/dashboard/stale-install-check.ts +77 -0
  286. package/src/decisions/decision-ledger.ts +404 -0
  287. package/src/domains/outbound-alert.ts +13 -7
  288. package/src/domains/telegram-kind-route.ts +21 -0
  289. package/src/execution-loop/dispatch-task.ts +139 -0
  290. package/src/execution-loop/gate-decision-summary.ts +29 -0
  291. package/src/execution-loop/gates.ts +197 -0
  292. package/src/field/field-media.ts +245 -0
  293. package/src/graph-kinds/installed-plugin-nodes.ts +95 -0
  294. package/src/graph-kinds/plugin-nodes.ts +10 -2
  295. package/src/graph-runner/runner.ts +35 -5
  296. package/src/grounding/token.ts +12 -6
  297. package/src/harness/harness-cli-command.ts +68 -9
  298. package/src/harness/harness-pod-dispatch.ts +8 -2
  299. package/src/harness/harness-stop.ts +147 -18
  300. package/src/harness/self-send-decide.ts +26 -17
  301. package/src/harness/self-send-target.ts +22 -1
  302. package/src/harness/worktree-branch-prefix.ts +4 -3
  303. package/src/index.ts +50 -13
  304. package/src/instance/resolve.ts +6 -0
  305. package/src/intake-plane/collect-telegram-saved.ts +5 -0
  306. package/src/intake-plane/digest.ts +11 -2
  307. package/src/live/detail-switch.ts +8 -3
  308. package/src/llm/usable-llm.ts +32 -0
  309. package/src/loops/registry.ts +27 -5
  310. package/src/market/publish.ts +59 -3
  311. package/src/native-tool-catalog.ts +2 -2
  312. package/src/nexus/api/field-uploads.ts +143 -0
  313. package/src/nexus/api/graph-approvals.ts +174 -0
  314. package/src/nexus/api/harness-api.ts +219 -5
  315. package/src/nexus/api/http-server.ts +69 -6
  316. package/src/nexus/api/log-fabric.ts +10 -1
  317. package/src/nexus/api/mcp-oauth-hint.ts +10 -9
  318. package/src/nexus/api/merge-approvals.ts +2 -1
  319. package/src/nexus/api/meta-api.ts +14 -0
  320. package/src/nexus/api/plugins-credentials-api.ts +47 -0
  321. package/src/nexus/api/plugins-market.ts +87 -3
  322. package/src/nexus/api/pod-credential-api.ts +68 -14
  323. package/src/nexus/api/public-routes.ts +7 -0
  324. package/src/nexus/api/trace.ts +57 -6
  325. package/src/nexus/boot/register-mcp-clients.ts +99 -14
  326. package/src/nexus/index.ts +33 -32
  327. package/src/nexus/outbound/router.ts +1 -1
  328. package/src/notify/llm-budget-alert.ts +119 -0
  329. package/src/onboarding.ts +25 -2
  330. package/src/plugins/core/host.ts +14 -0
  331. package/src/plugins/install/market-fetch.ts +4 -3
  332. package/src/plugins/install/plugin-credentials.ts +106 -0
  333. package/src/plugins/install/plugin-install.ts +163 -32
  334. package/src/plugins/maker/plugin-maker.ts +208 -0
  335. package/src/prompt-library/universal-preamble.ts +8 -2
  336. package/src/relay/channel.ts +184 -0
  337. package/src/relay/mux.ts +182 -0
  338. package/src/relay/test-vectors.json +29 -0
  339. package/src/release-loop/checklist.ts +217 -0
  340. package/src/release-loop/release-note.ts +6 -3
  341. package/src/seat-address/seat-address.ts +34 -0
  342. package/src/self-dev/dev-cli.ts +40 -20
  343. package/src/self-dev/dev-pipeline.ts +77 -1
  344. package/src/self-implement/gate-baseline.ts +3 -1
  345. package/src/self-implement/goal-author.ts +18 -1
  346. package/src/self-implement/host-regate.ts +6 -2
  347. package/src/self-implement/orchestrator.ts +7 -2
  348. package/src/self-implement/run-ledger-compact.ts +1 -1
  349. package/src/self-implement/seams.ts +4 -2
  350. package/src/session/index.ts +23 -10
  351. package/src/skills/tools/dev-harness.ts +42 -0
  352. package/src/steward/steward-cards.ts +45 -0
  353. package/src/steward/triage.ts +50 -14
  354. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +61 -3
  355. package/src/task-orchestrator/surfaces/pod-bun-cache.ts +18 -0
  356. package/src/task-orchestrator/surfaces/pod-command-job.ts +36 -6
  357. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +39 -9
  358. package/src/task-orchestrator/surfaces/pod-source-bundle.ts +16 -1
  359. package/src/task-orchestrator/surfaces/pod-source-receive.ts +51 -13
  360. package/src/task-orchestrator/surfaces/self-implement-pod.ts +172 -28
  361. package/src/telegram-report.ts +12 -6
  362. package/src/telegram.ts +108 -1
  363. package/src/tui-client/acp-transport-ws-client.ts +72 -5
  364. package/src/tui.ts +3 -2
  365. package/src/user-config.ts +32 -2
  366. package/src/util/spawn-sync-output.ts +15 -0
  367. package/src/version/code-revision.ts +1 -1
  368. package/src/version/packed-revision.json +1 -1
  369. package/src/video-pipeline/recipes/explainer.ts +213 -0
  370. package/src/video-pipeline/walk-line.ts +3 -2
  371. package/src/web-search/free.ts +77 -0
  372. package/src/web-search/index.ts +2 -0
  373. package/src/workflow-runtime/deps-bridge.ts +4 -3
  374. package/src/workflow-runtime/plugin-kind-node.ts +32 -4
  375. package/src/workflow-runtime/types.ts +2 -1
  376. package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
  377. package/apps/pwa/out/_next/static/chunks/1191-7b13a24117a689fa.js +0 -1
  378. package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.js +0 -1
  379. package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
  380. package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
  381. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
  382. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
  383. package/apps/pwa/out/_next/static/chunks/2375-3fcae5186c64fe6b.js +0 -1
  384. package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +0 -1
  385. package/apps/pwa/out/_next/static/chunks/2929-617f1e66a55be184.js +0 -1
  386. package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
  387. package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
  388. package/apps/pwa/out/_next/static/chunks/319-d37df1b6e0cbeff4.js +0 -1
  389. package/apps/pwa/out/_next/static/chunks/3441-a1dd4acfeda14488.js +0 -1
  390. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
  391. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
  392. package/apps/pwa/out/_next/static/chunks/4836-3c93795ab834381e.js +0 -1
  393. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
  394. package/apps/pwa/out/_next/static/chunks/6090-1bd6077591603947.js +0 -1
  395. package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
  396. package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.js +0 -1
  397. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
  398. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
  399. package/apps/pwa/out/_next/static/chunks/7457-f5cb1cc8db9f7453.js +0 -1
  400. package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
  401. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
  402. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
  403. package/apps/pwa/out/_next/static/chunks/8067-f74bf14d67ad98f1.js +0 -1
  404. package/apps/pwa/out/_next/static/chunks/8519-0059d82bdb4812bf.js +0 -1
  405. package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
  406. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
  407. package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
  408. package/apps/pwa/out/_next/static/chunks/9740-52467f244755917f.js +0 -1
  409. package/apps/pwa/out/_next/static/chunks/9804-64c02c3c127d5014.js +0 -1
  410. package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
  411. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
  412. package/apps/pwa/out/_next/static/chunks/app/approvals/page-21c3261af6ab377c.js +0 -1
  413. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-1991445b8ea86274.js +0 -1
  414. package/apps/pwa/out/_next/static/chunks/app/board/page-4ebfaee2369fe248.js +0 -1
  415. package/apps/pwa/out/_next/static/chunks/app/botlab/page-9fa879ebf5959096.js +0 -1
  416. package/apps/pwa/out/_next/static/chunks/app/bots/page-ca7c3cc305c3285a.js +0 -1
  417. package/apps/pwa/out/_next/static/chunks/app/chat/page-9951930add3d2051.js +0 -1
  418. package/apps/pwa/out/_next/static/chunks/app/control/page-4472ddf1e2458e69.js +0 -1
  419. package/apps/pwa/out/_next/static/chunks/app/design-check/page-4de08d4b766f134b.js +0 -1
  420. package/apps/pwa/out/_next/static/chunks/app/editor/page-7a975e2b9db1dfb7.js +0 -1
  421. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
  422. package/apps/pwa/out/_next/static/chunks/app/intake/page-49270714ef35bb96.js +0 -1
  423. package/apps/pwa/out/_next/static/chunks/app/layout-33fbdf353e2a5982.js +0 -1
  424. package/apps/pwa/out/_next/static/chunks/app/live/page-259f122e2dd64317.js +0 -1
  425. package/apps/pwa/out/_next/static/chunks/app/market/page-65b82726753658e1.js +0 -1
  426. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-4b8df3748dc06285.js +0 -1
  427. package/apps/pwa/out/_next/static/chunks/app/morning/page-112f7e477bda77fd.js +0 -1
  428. package/apps/pwa/out/_next/static/chunks/app/not-found-542a5c0006da637c.js +0 -1
  429. package/apps/pwa/out/_next/static/chunks/app/observatory/page-63ffb347831262ea.js +0 -1
  430. package/apps/pwa/out/_next/static/chunks/app/page-98e4e359d97263b4.js +0 -1
  431. package/apps/pwa/out/_next/static/chunks/app/reflection/page-97e24ac675ce576e.js +0 -1
  432. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-ac6391f26391d89d.js +0 -1
  433. package/apps/pwa/out/_next/static/chunks/app/sessions/page-9f45c9d45ffc43bd.js +0 -1
  434. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-36995b9cb5d98218.js +0 -1
  435. package/apps/pwa/out/_next/static/chunks/app/settings/page-0c795b577988d63f.js +0 -1
  436. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-b4311e2f71c6735a.js +0 -1
  437. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
  438. package/apps/pwa/out/_next/static/chunks/app/setup/page-eb2381074df20dba.js +0 -1
  439. package/apps/pwa/out/_next/static/chunks/app/share/page-b01336e64eba01ae.js +0 -1
  440. package/apps/pwa/out/_next/static/chunks/app/showroom/page-7af7f21c1f2858ef.js +0 -1
  441. package/apps/pwa/out/_next/static/chunks/app/tasks/page-a98ecd364586c89b.js +0 -1
  442. package/apps/pwa/out/_next/static/chunks/app/term/page-57f0502dfbfa089b.js +0 -1
  443. package/apps/pwa/out/_next/static/chunks/app/trace/page-3467d2e1abd8a14a.js +0 -1
  444. package/apps/pwa/out/_next/static/chunks/app/vault/page-7842ca23d53ba682.js +0 -1
  445. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-ae9ac882912b0198.js +0 -1
  446. package/apps/pwa/out/_next/static/chunks/app/workflows/page-3a0d72e3f783bcef.js +0 -1
  447. package/apps/pwa/out/_next/static/chunks/app/workspace/page-f3b39fe8358c2a97.js +0 -1
  448. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-5a22af3181ac2afb.js +0 -1
  449. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
  450. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
  451. package/apps/pwa/out/_next/static/chunks/main-app-1457627bb302a22c.js +0 -1
  452. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
  453. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
  454. package/apps/pwa/out/_next/static/css/e66b7f5efc8df3f3.css +0 -1
  455. /package/apps/pwa/out/_next/static/{zoQX5iTvXOsIvCjxCe1Ji → qMI0jnlZaBjjm4aIDYNGM}/_ssgManifest.js +0 -0
@@ -0,0 +1,217 @@
1
+ import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { Database } from 'bun:sqlite';
3
+ import { dirname, join } from 'node:path';
4
+ import { randomUUID } from 'node:crypto';
5
+ import { debug } from '../debug/log.js';
6
+ import { releaseLedgerRoot } from '../instance/resolve.js';
7
+
8
+ export type ChecklistStatus = 'green' | 'yellow' | 'red' | 'done';
9
+ export type ChecklistDisposition = 'move' | 'known-issue' | 'block';
10
+ export interface ChecklistItem {
11
+ id: string;
12
+ title: string;
13
+ status: ChecklistStatus;
14
+ owner?: string;
15
+ evidence?: string;
16
+ disposition?: ChecklistDisposition;
17
+ updatedAt: string;
18
+ updatedBy: string;
19
+ }
20
+ export interface ChecklistHistory {
21
+ at: string;
22
+ by: string;
23
+ id: string;
24
+ field: string;
25
+ from: unknown;
26
+ to: unknown;
27
+ released: string;
28
+ dev: string;
29
+ }
30
+ export interface Checklist {
31
+ version: string;
32
+ released: string;
33
+ dev: string;
34
+ items: ChecklistItem[];
35
+ history: ChecklistHistory[];
36
+ }
37
+
38
+ export function devVersion(): string {
39
+ return (JSON.parse(readFileSync(join(import.meta.dir, '..', '..', 'package.json'), 'utf8')) as { version: string }).version;
40
+ }
41
+
42
+ function releasedVersion(): string {
43
+ const dir = join(releaseLedgerRoot(), 'release');
44
+ if (!existsSync(dir)) return '';
45
+ return readdirSync(dir).filter((v) => /^\d+\.\d+\.\d+$/.test(v) && existsSync(join(dir, v, 'release.json')))
46
+ .filter((v) => {
47
+ try { const record = JSON.parse(readFileSync(join(dir, v, 'release.json'), 'utf8')) as { version?: string; publishedAt?: string }; return record.version === v && typeof record.publishedAt === 'string'; }
48
+ catch { return false; }
49
+ })
50
+ .sort((a, b) => {
51
+ const aa = a.split('.').map(Number), bb = b.split('.').map(Number);
52
+ return (bb[0]! - aa[0]!) || (bb[1]! - aa[1]!) || (bb[2]! - aa[2]!);
53
+ })[0] ?? '';
54
+ }
55
+
56
+ function pathFor(v: string): string {
57
+ if (!/^\d+\.\d+\.\d+(?:-(?:rc|alpha|beta)\.\d+)?$/.test(v)) throw new Error(`체크리스트 판이 아니다: ${v}`);
58
+ return join(releaseLedgerRoot(), 'release', v, 'checklist.json');
59
+ }
60
+
61
+ export function listChecklist(v: string): Checklist {
62
+ const path = pathFor(v);
63
+ if (existsSync(path)) {
64
+ const data = JSON.parse(readFileSync(path, 'utf8')) as Checklist;
65
+ refresh(data);
66
+ return data;
67
+ }
68
+ return { version: v, released: releasedVersion(), dev: devVersion(), items: [], history: [] };
69
+ }
70
+
71
+ // SQLite's OS-backed write lock is released when the owning process exits, even without a finally block.
72
+ // Hold it across the JSON read/modify/atomic-rename sequence so writers never use stale snapshots.
73
+ function mutate(v: string, apply: (data: Checklist) => boolean): Checklist {
74
+ const path = pathFor(v);
75
+ mkdirSync(dirname(path), { recursive: true });
76
+ const lock = `${path}.mutex.sqlite`;
77
+ const db = new Database(lock, { create: true, strict: true });
78
+ try {
79
+ chmodSync(lock, 0o600);
80
+ db.exec('PRAGMA busy_timeout = 10000');
81
+ db.exec('BEGIN IMMEDIATE');
82
+ try {
83
+ const data = listChecklist(v);
84
+ if (apply(data)) save(v, data);
85
+ db.exec('COMMIT');
86
+ return data;
87
+ } catch (error) {
88
+ db.exec('ROLLBACK');
89
+ throw error;
90
+ }
91
+ } finally { db.close(); }
92
+ }
93
+
94
+ function save(v: string, data: Checklist): void {
95
+ const path = pathFor(v);
96
+ mkdirSync(dirname(path), { recursive: true });
97
+ const temp = `${path}.${process.pid}.${randomUUID()}.tmp`;
98
+ try {
99
+ writeFileSync(temp, `${JSON.stringify(data, null, 2)}\n`, { mode: 0o600 });
100
+ chmodSync(temp, 0o600);
101
+ renameSync(temp, path);
102
+ } finally { if (existsSync(temp)) rmSync(temp); }
103
+ }
104
+
105
+ function change(data: Checklist, id: string, field: string, from: unknown, to: unknown, by: string, at: string): void {
106
+ data.history.push({ at, by, id, field, from: from ?? null, to: to ?? null, released: data.released, dev: data.dev });
107
+ debug.log('release-loop.checklist', 'change', { version: data.version, id, field, from: from ?? null, to: to ?? null, by });
108
+ }
109
+
110
+ function refresh(data: Checklist): void { data.released = releasedVersion(); data.dev = devVersion(); }
111
+
112
+ export function addItem(v: string, input: { id: string; title: string; owner?: string }): Checklist {
113
+ if (!input.id.trim()) throw new Error('칸 id 가 비었다');
114
+ if (!input.title.trim()) throw new Error('칸 제목이 비었다');
115
+ return mutate(v, (data) => {
116
+ if (data.items.some((item) => item.id === input.id)) throw new Error(`이미 있는 칸: ${input.id}`);
117
+ const by = process.env.ELANOUS_TRACK || 'cli';
118
+ const at = new Date().toISOString();
119
+ const item: ChecklistItem = { id: input.id, title: input.title, status: 'yellow', ...(input.owner !== undefined ? { owner: input.owner } : {}), updatedAt: at, updatedBy: by };
120
+ data.items.push(item);
121
+ change(data, item.id, 'add', null, item, by, at);
122
+ return true;
123
+ });
124
+ }
125
+
126
+ export function setItem(v: string, id: string, patch: { status?: ChecklistStatus; evidence?: string; owner?: string; disposition?: ChecklistDisposition }, by: string): Checklist {
127
+ return mutate(v, (data) => {
128
+ const item = data.items.find((i) => i.id === id);
129
+ if (!item) throw new Error(`없는 칸: ${id}`);
130
+ if (patch.status !== undefined && !['green', 'yellow', 'red', 'done'].includes(patch.status)) throw new Error(`잘못된 상태: ${patch.status}`);
131
+ if (patch.disposition !== undefined && !['move', 'known-issue', 'block'].includes(patch.disposition)) throw new Error(`잘못된 처분: ${patch.disposition}`);
132
+ const fields = (['evidence', 'owner', 'status', 'disposition'] as const).filter((field) => patch[field] !== undefined && patch[field] !== item[field]);
133
+ if (fields.length === 0) return false;
134
+ const at = new Date().toISOString();
135
+ for (const field of fields) {
136
+ const from = item[field];
137
+ const to = patch[field]!;
138
+ (item as unknown as Record<string, unknown>)[field] = to;
139
+ item.updatedAt = at;
140
+ item.updatedBy = by;
141
+ change(data, id, field, from, to, by, at);
142
+ }
143
+ return true;
144
+ });
145
+ }
146
+
147
+ export function removeItem(v: string, id: string, by: string): Checklist {
148
+ return mutate(v, (data) => {
149
+ const index = data.items.findIndex((i) => i.id === id);
150
+ if (index < 0) throw new Error(`없는 칸: ${id}`);
151
+ const at = new Date().toISOString();
152
+ const [item] = data.items.splice(index, 1);
153
+ change(data, id, 'remove', item, null, by, at);
154
+ return true;
155
+ });
156
+ }
157
+
158
+ export function summarize(v: string): ChecklistSummary {
159
+ return summarizeChecklist(listChecklist(v));
160
+ }
161
+
162
+ export interface ChecklistSummary { green: number; yellow: number; red: number; done: number; blocked: string[]; byOwner: Record<string, number> }
163
+
164
+ export function summarizeChecklist(data: Checklist): ChecklistSummary {
165
+ const summary = { green: 0, yellow: 0, red: 0, done: 0, blocked: [] as string[], byOwner: Object.create(null) as Record<string, number> };
166
+ for (const item of data.items) {
167
+ summary[item.status]++;
168
+ if (item.status === 'red') summary.blocked.push(item.id);
169
+ const owner = item.owner ?? 'unassigned';
170
+ summary.byOwner[owner] = (summary.byOwner[owner] ?? 0) + 1;
171
+ }
172
+ return summary;
173
+ }
174
+
175
+ export interface ChecklistGate {
176
+ ok: boolean;
177
+ red: string[];
178
+ undecided: string[];
179
+ blocked: string[];
180
+ moved: string[];
181
+ knownIssues: Array<{ id: string; title: string; evidence: string }>;
182
+ }
183
+
184
+ export function checklistGate(v: string): ChecklistGate {
185
+ const result: ChecklistGate = { ok: true, red: [], undecided: [], blocked: [], moved: [], knownIssues: [] };
186
+ for (const item of listChecklist(v).items) {
187
+ if (item.status === 'red') result.red.push(item.id);
188
+ if (item.status !== 'yellow') continue;
189
+ if (item.disposition === 'block') result.blocked.push(item.id);
190
+ else if (item.disposition === 'move') result.moved.push(item.id);
191
+ else if (item.disposition === 'known-issue') result.knownIssues.push({ id: item.id, title: item.title, evidence: item.evidence ?? '' });
192
+ else result.undecided.push(item.id);
193
+ }
194
+ result.ok = result.red.length === 0 && result.undecided.length === 0 && result.blocked.length === 0;
195
+ return result;
196
+ }
197
+
198
+ export function seedFromRoadmap(v: string, markdown: string): Checklist {
199
+ return mutate(v, (data) => {
200
+ let added = false;
201
+ for (const line of markdown.split('\n')) {
202
+ const match = /^\|\s*(K\d+[a-z]?[′']?)\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|/.exec(line);
203
+ if (!match || data.items.some((i) => i.id === match[1])) continue;
204
+ const id = match[1]!;
205
+ const title = match[2]!;
206
+ const icon = match[3]!.trim();
207
+ const status: ChecklistStatus = icon.startsWith('🟢') ? 'green' : icon.startsWith('🔴') ? 'red' : icon.startsWith('✅') ? 'done' : 'yellow';
208
+ const at = new Date().toISOString();
209
+ const by = process.env.ELANOUS_TRACK || 'cli';
210
+ const item: ChecklistItem = { id, title, status, updatedAt: at, updatedBy: by };
211
+ data.items.push(item);
212
+ change(data, id, 'add', null, item, by, at);
213
+ added = true;
214
+ }
215
+ return added;
216
+ });
217
+ }
@@ -30,9 +30,10 @@ function blankFencedCode(text: string): string {
30
30
 
31
31
  export function parseReleaseNoteSection(rawPrBody: string): { fragment?: Section; problems: string[] } {
32
32
  const prBody = blankFencedCode(rawPrBody);
33
- const start = /^## 릴리스 노트[ \t]*\r?$/m.exec(prBody);
33
+ const sections = [...prBody.matchAll(/^## 릴리스 노트[ \t]*\r?$/gm)];
34
+ const start = sections.at(-1);
34
35
  if (!start) return { problems: [] };
35
- const rest = prBody.slice(start.index + start[0].length).replace(/^\r?\n/, '');
36
+ const rest = prBody.slice(start.index! + start[0].length).replace(/^\r?\n/, '');
36
37
  const nextHeading = /^ {0,3}#{1,6}(?:[ \t]+|$)/m.exec(rest);
37
38
  const body = nextHeading ? rest.slice(0, nextHeading.index) : rest;
38
39
  const fields = new Map<string, string>();
@@ -51,7 +52,9 @@ export function parseReleaseNoteSection(rawPrBody: string): { fragment?: Section
51
52
  if (duplicates.has('종류') || !kind || !['fix', 'feat', 'security', 'internal'].includes(kind)) problems.push('종류');
52
53
  if (duplicates.has('문서') || !doc || (doc.startsWith('없음') ? !/^없음\([^\s()]+(?:[^()]*)\)$/.test(doc) : !/^\S+$/.test(doc))) problems.push('문서');
53
54
  if (duplicates.has('대상') || (target !== 'next' && target !== 'later')) problems.push('대상');
54
- if (problems.length) return { problems };
55
+ const invalid = problems.length > 0;
56
+ if (sections.length > 1) problems.push(`multiple release-note sections (${sections.length}); using the last`);
57
+ if (invalid) return { problems };
55
58
  return {
56
59
  fragment: {
57
60
  line: line!, kind: kind as Section['kind'],
@@ -0,0 +1,34 @@
1
+ import trackData from '../../scripts/coord-tracks.json';
2
+
3
+ export type SeatEntry = {
4
+ id: string;
5
+ title?: string;
6
+ alias?: string;
7
+ };
8
+
9
+ export type SeatAddress = { seats: string[]; body: string };
10
+
11
+ const registry: readonly SeatEntry[] = trackData.tracks.filter((entry) => 'title' in entry);
12
+ const addressLine = /^@([A-Za-z][A-Za-z0-9_-]*(?:,[A-Za-z][A-Za-z0-9_-]*)*)(?:[ \t]+|\r?\n|(?![\s\S]))/gm;
13
+
14
+ /** Parse an address only when its @ begins a line; leave the instruction text intact. */
15
+ export function parseSeatAddress(text: string): SeatAddress | null {
16
+ const match = addressLine.exec(text);
17
+ addressLine.lastIndex = 0;
18
+ if (!match) return null;
19
+
20
+ const seats = match[1]!.split(',');
21
+ const body = `${text.slice(0, match.index)}${text.slice(match.index + match[0].length)}`;
22
+ return { seats, body };
23
+ }
24
+
25
+ /** Resolve against the seat registry, preferring titles to ids to legacy aliases. */
26
+ export function resolveSeat(address: string, seats: readonly SeatEntry[] = registry): SeatEntry | undefined {
27
+ const needle = address.replace(/^@/, '').toLocaleLowerCase();
28
+ if (!needle) return undefined;
29
+ for (const field of ['title', 'id', 'alias'] as const) {
30
+ const found = seats.find((seat) => seat[field]?.toLocaleLowerCase() === needle);
31
+ if (found) return found;
32
+ }
33
+ return undefined;
34
+ }
@@ -45,6 +45,7 @@ import { getUserConfig, type UserConfig } from '../user-config.js';
45
45
  import { resolveChildLlmPreference, type ChildLlmPreferenceInput, type ResolvedChildLlmPreference } from '../self-implement/child-llm-preference.js';
46
46
  import { readCachedGrokQuota } from '../oauth/codex-account-store.js';
47
47
  import { queryAbandonedDraftPrs, type AbandonedDraftPr } from '../cli/logs-abandoned-draft-prs.js';
48
+ import { branchLineageSlug } from '../cli/pr-lineage.js';
48
49
  import { decideDraft, type DraftTriagePr } from './draft-triage-rules.js';
49
50
  import { resolveLogTargets } from '../cli/logs-cli.js';
50
51
  import { LogStore } from '../mss/logging/log-store.js';
@@ -196,12 +197,12 @@ export interface DevSelfRunExecution {
196
197
 
197
198
  interface DraftTriage {
198
199
  closed: number[];
199
- kept: number | null;
200
+ kept: number[] | null;
200
201
  closeFailed: number[];
201
202
  skippedNotDraft: number[];
202
203
  }
203
204
 
204
- export type DraftPrViewState = { isDraft: boolean; state: string } | null;
205
+ export type DraftPrViewState = { isDraft: boolean; state: string; headRefName?: string } | null;
205
206
 
206
207
  export interface DraftPrInventory {
207
208
  readonly open: readonly (DraftTriagePr & { url: string; openedAtMs: number; runId?: string | null })[];
@@ -294,11 +295,13 @@ function defaultViewDraftPr(prUrl: string, timeoutMs?: number): DraftPrViewState
294
295
  if (IN_TEST_PROCESS()) return null;
295
296
  const target = draftPrTargetFromUrl(prUrl);
296
297
  if (!target) return null;
297
- const view = spawnSync('gh', ['pr', 'view', String(target.number), '--repo', target.repository, '--json', 'isDraft,state'], { encoding: 'utf8', ...(timeoutMs === undefined ? {} : { timeout: timeoutMs }) });
298
+ const view = spawnSync('gh', ['pr', 'view', String(target.number), '--repo', target.repository, '--json', 'isDraft,state,headRefName'], { encoding: 'utf8', ...(timeoutMs === undefined ? {} : { timeout: timeoutMs }) });
298
299
  if (view.status !== 0) return null;
299
300
  try {
300
- const parsed = JSON.parse(view.stdout) as { isDraft?: unknown; state?: unknown };
301
- return typeof parsed.isDraft === 'boolean' && typeof parsed.state === 'string' ? { isDraft: parsed.isDraft, state: parsed.state } : null;
301
+ const parsed = JSON.parse(view.stdout) as { isDraft?: unknown; state?: unknown; headRefName?: unknown };
302
+ return typeof parsed.isDraft === 'boolean' && typeof parsed.state === 'string'
303
+ ? { isDraft: parsed.isDraft, state: parsed.state, ...(typeof parsed.headRefName === 'string' ? { headRefName: parsed.headRefName } : {}) }
304
+ : null;
302
305
  } catch {
303
306
  return null;
304
307
  }
@@ -946,41 +949,58 @@ export async function executeDevSelfRun(
946
949
  const view = supervise?.viewDraftPr ?? defaultViewDraftPr;
947
950
  const closeDraft = supervise?.closeDraftPr ?? defaultCloseDraftPr;
948
951
  const commentDraft = supervise?.commentDraftPr ?? defaultCommentDraftPr;
949
- const openDrafts: string[] = [];
952
+ const openDrafts: Array<{ url: string; lineage: string | null }> = [];
950
953
  const skippedNotDraft: number[] = [];
951
954
  for (const prUrl of drafts) {
952
955
  let live: DraftPrViewState = null;
953
956
  try { live = view(prUrl); } catch { live = null; }
954
- if (live?.isDraft === true && live.state === 'OPEN') openDrafts.push(prUrl);
955
- else {
957
+ if (live?.isDraft === true && live.state === 'OPEN') {
958
+ openDrafts.push({ url: prUrl, lineage: live.headRefName ? branchLineageSlug(live.headRefName) : null });
959
+ } else {
956
960
  const number = prNumberFromUrl(prUrl);
957
961
  if (number !== null) skippedNotDraft.push(number);
958
962
  }
959
963
  }
960
- const keptUrl = converged ? undefined : openDrafts.at(-1);
961
- const kept = keptUrl === undefined ? null : prNumberFromUrl(keptUrl);
964
+ const keptByLineage = new Map<string, string>();
965
+ if (!converged) {
966
+ for (const draft of openDrafts) {
967
+ if (draft.lineage !== null) keptByLineage.set(draft.lineage, draft.url);
968
+ }
969
+ }
970
+ const keptDrafts = converged ? [] : openDrafts.filter(({ url, lineage }) => lineage === null || keptByLineage.get(lineage) === url);
971
+ const kept = keptDrafts.length === 0 ? null : keptDrafts.flatMap(({ url }) => {
972
+ const number = prNumberFromUrl(url);
973
+ return number === null ? [] : [number];
974
+ });
962
975
  const closed: number[] = [];
963
976
  const closeFailed: number[] = [];
964
977
  const landed = mergedNumbers.map((n) => `#${n}`).join(' ') || '없음';
965
- for (const prUrl of openDrafts) {
966
- if (prUrl === keptUrl) continue;
967
- const number = prNumberFromUrl(prUrl);
978
+ for (const { url, lineage } of openDrafts) {
979
+ if (!converged && (lineage === null || keptByLineage.get(lineage) === url)) continue;
980
+ const number = prNumberFromUrl(url);
981
+ const keptInLineage = lineage === null ? null : prNumberFromUrl(keptByLineage.get(lineage)!);
968
982
  const comment = converged
969
983
  ? `하니스 종결 트리아지: 이 런이 수렴해 대체됨 — 착지 ${landed}`
970
- : `하니스 종결 트리아지: 최신 산출 #${kept ?? 'unknown'} 로 대체됨`;
984
+ : `하니스 종결 트리아지: 최신 산출 #${keptInLineage ?? 'unknown'} 로 대체됨`;
971
985
  let ok = false;
972
- try { ok = closeDraft(prUrl, comment); } catch { ok = false; }
986
+ try { ok = closeDraft(url, comment); } catch { ok = false; }
973
987
  if (number === null) continue;
974
988
  if (ok) closed.push(number);
975
989
  else closeFailed.push(number);
976
990
  }
977
- if (keptUrl !== undefined) {
991
+ for (const { url, lineage } of keptDrafts) {
992
+ const keptLineage = [...keptByLineage.keys()].find((other) => other !== lineage);
993
+ if (lineage !== null && keptLineage !== undefined) {
994
+ try { debug.log('self-dev.triage', 'kept-distinct-piece', { pr: prNumberFromUrl(url), lineage, keptLineage }); } catch { /* fail-open */ }
995
+ }
978
996
  const rounds = Math.max(1, executedRounds);
979
- const handoff = closeFailed.length === 0
980
- ? '이 draft 가 이 골의 유일한 사람 판단 대상'
981
- : `닫지 못한 draft 도 남아 있다: ${closeFailed.map((n) => `#${n}`).join(' ')}`;
997
+ const handoff = closeFailed.length > 0
998
+ ? `닫지 못한 draft 도 남아 있다: ${closeFailed.map((n) => `#${n}`).join(' ')}`
999
+ : lineage === null ? '브랜치 계보 미확인 — 사람 판단 대상'
1000
+ : keptDrafts.length === 1 ? '이 draft 가 이 골의 유일한 사람 판단 대상'
1001
+ : '이 draft 가 이 계보의 사람 판단 대상';
982
1002
  const comment = `하니스 종결 트리아지: 멈춘 사유 ${supervisorStopReason ?? 'unsupervised'} · 라운드 ${rounds} · 착지한 조각 PR ${landed} · ${handoff}`;
983
- try { commentDraft(keptUrl, comment); } catch { /* fail-open */ }
1003
+ try { commentDraft(url, comment); } catch { /* fail-open */ }
984
1004
  }
985
1005
  draftTriage = { closed, kept, closeFailed, skippedNotDraft };
986
1006
  };
@@ -15,8 +15,12 @@
15
15
  // self 는 backend 리스트 항목이 아니라 transport 축을 붕괴시키는 특수 갈래. 감독 축도 self=turn 레벨.
16
16
 
17
17
  import { execFileSync } from 'node:child_process';
18
+ import { createHash } from 'node:crypto';
18
19
  import { readFileSync } from 'node:fs';
19
- import { appendEvidenceLocationRequirement, extractVerbatimOriginalAsk, formatGoalFileLintFinding, inspectAskDecisionSignalMarker, leadingGoalMetadata, lintGoalFile, parseGoalId, parseGoalType, SUPERSEDED_BY_LINE, type GoalFileLintFinding, type GoalFileLintResult } from '../self-implement/goal-author.js';
20
+ import { dispatchTask, type DispatchTaskDeps } from '../execution-loop/dispatch-task.js';
21
+ import { getNestDepth } from '../agent/nest-depth.js';
22
+ import { appendRunLedgerEntry } from '../self-implement/run-ledger.js';
23
+ import { appendEvidenceLocationRequirement, extractVerbatimOriginalAsk, formatGoalFileLintFinding, inspectAskDecisionSignalMarker, leadingGoalMetadata, lintGoalFile, parseGoalId, parseGoalType, tracedPathReferences, SUPERSEDED_BY_LINE, type GoalFileLintFinding, type GoalFileLintResult } from '../self-implement/goal-author.js';
20
24
  import { createRepositoryReferencedFileReader, type ReferencedFileReader } from '../self-implement/goal-file-reader.js';
21
25
  import { parseGoalDocumentClarifications } from '../self-implement/goal-author-clarification.js';
22
26
  import { requiredEvidenceFromGoal } from '../self-implement/off-diff-evidence.js';
@@ -55,6 +59,8 @@ export type OrchestrateRuntime = Omit<OrchestrateSelfDevOptions, 'goals' | 'conc
55
59
  export interface DevMissionOpts {
56
60
  chain?: readonly ('codex' | 'claude' | 'elanous')[];
57
61
  plugin?: { plugin: string; marketplace: string };
62
+ resources?: 'on' | 'off';
63
+ backendExplicit?: string;
58
64
  headless?: boolean;
59
65
  evidence?: EvidenceMode;
60
66
  maxRounds?: number;
@@ -619,6 +625,8 @@ export function toAgentMissionSpec(
619
625
  ...(m.headless === true ? { headless: true } : {}),
620
626
  ...(m.chain ? { chain: m.chain } : {}),
621
627
  ...(m.plugin ? { plugin: m.plugin } : {}),
628
+ ...(m.resources !== undefined ? { resources: m.resources } : {}),
629
+ ...(m.backendExplicit !== undefined ? { backendExplicit: m.backendExplicit } : {}),
622
630
  ...(plan.base ? { base: plan.base } : {}),
623
631
  ...(plan.enhance !== undefined ? { enhance: plan.enhance } : {}),
624
632
  ...(m.maxRounds !== undefined ? { maxRounds: m.maxRounds } : {}),
@@ -644,6 +652,8 @@ export function buildAgentMissionDevSpec(o: {
644
652
  headless?: boolean;
645
653
  chain?: readonly ('codex' | 'claude' | 'elanous')[];
646
654
  plugin?: { plugin: string; marketplace: string };
655
+ resources?: 'on' | 'off';
656
+ backendExplicit?: string;
647
657
  /** --no-enhance 시 false(그 외 미지정 → driver 가 entry 로 구동). */
648
658
  enhanceOff?: boolean;
649
659
  evidence: EvidenceMode;
@@ -663,6 +673,8 @@ export function buildAgentMissionDevSpec(o: {
663
673
  ...(o.headless === true ? { headless: true } : {}),
664
674
  ...(o.chain ? { chain: o.chain } : {}),
665
675
  ...(o.plugin ? { plugin: o.plugin } : {}),
676
+ ...(o.resources !== undefined ? { resources: o.resources } : {}),
677
+ ...(o.backendExplicit !== undefined ? { backendExplicit: o.backendExplicit } : {}),
666
678
  maxRounds: o.maxRounds,
667
679
  commit: o.commit,
668
680
  entry: 'elanous-apparatus',
@@ -918,6 +930,10 @@ export interface DevHarnessDispatchArgs extends Record<string, unknown> {
918
930
  }
919
931
 
920
932
  export interface DevPipelineDeps {
933
+ dispatchTask?: typeof dispatchTask;
934
+ dispatchTaskDeps?: DispatchTaskDeps;
935
+ /** Caller has already recorded this dispatch decision at an outer entrance. */
936
+ skipDispatchTask?: boolean;
921
937
  /** Caller-managed human progress sink (for example SurfaceUx.progress). Absent callers retain byte-for-byte CLI stdout. */
922
938
  progress?: SurfaceUx['progress'];
923
939
  runSelfImplement?: (o: SelfImplementOptions) => Promise<SelfImplementResult>;
@@ -1777,6 +1793,65 @@ export async function runDevPipeline(spec: DevPipelineSpec, deps: DevPipelineDep
1777
1793
  }
1778
1794
  }
1779
1795
 
1796
+ async function observeDevTaskDispatch(spec: DevPipelineSpec, plan: ResolvedDevPlan, deps: DevPipelineDeps): Promise<void> {
1797
+ const skipped = plan.dispatch === 'interactive' && getNestDepth() > 0 ? 'nested-interactive'
1798
+ : plan.relaunch === true ? 'relaunch'
1799
+ : plan.dispatch === 'parallel' ? 'parallel-parent'
1800
+ : deps.skipDispatchTask === true ? 'skipDispatchTask'
1801
+ : process.env.ELANOUS_DISPATCH_RECORDED === '1' ? 'dispatch-recorded'
1802
+ // `bun test` preload sets this so pipeline tests do not run the real gates (usage/recall
1803
+ // lookups) on every launch; tests that inject a dispatcher or gates still observe.
1804
+ : process.env.ELANOUS_EXECUTION_LOOP_DISPATCH === 'off' && !deps.dispatchTask && !deps.dispatchTaskDeps ? 'disabled'
1805
+ : undefined;
1806
+ if (skipped) {
1807
+ try { debug.log('execution-loop.gate', 'dispatch-skipped', { reason: skipped, ...(spec.runId ? { runId: spec.runId } : {}) }); } catch { /* fail-soft */ }
1808
+ return;
1809
+ }
1810
+ try {
1811
+ const goalText = 'text' in plan.input ? plan.input.text : (deps.readFile ?? ((path: string) => readFileSync(path, 'utf8')))(plan.input.file);
1812
+ const goalId = 'file' in plan.input ? parseGoalId(goalText) : null;
1813
+ let recordedCardId: string | undefined;
1814
+ const recordCard = (cardId: string): void => {
1815
+ if (!spec.runId || recordedCardId) return;
1816
+ recordedCardId = cardId;
1817
+ try {
1818
+ appendRunLedgerEntry({ timestamp: new Date().toISOString(), runId: spec.runId,
1819
+ event: 'execution-loop-card', data: { cardId } });
1820
+ } catch (error) {
1821
+ try { debug.log('execution-loop.gate', 'ledger-unavailable', { reason: String(error) }); } catch { /* fail-soft */ }
1822
+ }
1823
+ };
1824
+ const dispatchDeps: DispatchTaskDeps = {
1825
+ ...deps.dispatchTaskDeps,
1826
+ onCardCreated: (cardId) => {
1827
+ recordCard(cardId);
1828
+ deps.dispatchTaskDeps?.onCardCreated?.(cardId);
1829
+ },
1830
+ };
1831
+ const work = Promise.resolve().then(() => (deps.dispatchTask ?? dispatchTask)({
1832
+ goalId: goalId ?? `request-${createHash('sha256').update(goalText).digest('hex').slice(0, 32)}`,
1833
+ title: goalText.split(/\r?\n/, 1)[0]?.trim() || 'Untitled request',
1834
+ goalText,
1835
+ targetPaths: 'file' in plan.input ? tracedPathReferences(goalText).map((reference) => reference.path) : [],
1836
+ spec,
1837
+ ...(spec.runId ? { runKey: spec.runId } : {}),
1838
+ }, dispatchDeps));
1839
+ // The default dispatcher bounds its gates to five seconds. Await its completed
1840
+ // observation so short-lived callers cannot exit before decisions reach the card.
1841
+ // Injected dispatchers retain an outer timeout to protect the original launch.
1842
+ let timeout: ReturnType<typeof setTimeout> | undefined;
1843
+ const result = await (deps.dispatchTask
1844
+ ? Promise.race([
1845
+ work,
1846
+ new Promise<never>((_, reject) => { timeout = setTimeout(() => reject(new Error('dispatch timeout after 5000ms')), 5_000); }),
1847
+ ]).finally(() => { if (timeout) clearTimeout(timeout); })
1848
+ : work);
1849
+ recordCard(result.cardId);
1850
+ } catch (error) {
1851
+ try { debug.log('execution-loop.gate', 'dispatch-unavailable', { reason: String(error) }); } catch { /* fail-soft */ }
1852
+ }
1853
+ }
1854
+
1780
1855
  async function runDevPipelineDispatch(
1781
1856
  spec: DevPipelineSpec,
1782
1857
  deps: DevPipelineDeps,
@@ -1785,6 +1860,7 @@ async function runDevPipelineDispatch(
1785
1860
  ): Promise<DevPipelineRunResult> {
1786
1861
  const plan = planDevPipeline(spec);
1787
1862
  seen.plan = plan;
1863
+ await observeDevTaskDispatch(spec, plan, deps);
1788
1864
  if (!plan.wired) {
1789
1865
  throw new DevPipelineError(
1790
1866
  `runDevPipeline: '${plan.dispatch}' 경로는 U4 골격에서 미배선(NotYetUnified). ` +
@@ -682,7 +682,9 @@ function runIsolatedBunTest(cwd: string, args: string[], timeout: number): Proce
682
682
  return { status: null, stdout: '', stderr: message, error: error instanceof Error ? error : new Error(message) };
683
683
  }
684
684
  try {
685
- return spawnSync('bun', ['test', ...args], { cwd, env: isolated.env, encoding: 'utf8', timeout, maxBuffer: 32 * 1024 * 1024 });
685
+ // SIGKILL — a test that traps SIGTERM while blocked in a native wait never runs its handler, so spawnSync
686
+ // waited forever and its drive grandchildren lived on as ppid=1 orphans (09-30 O1: 19 processes, up to 11h).
687
+ return spawnSync('bun', ['test', ...args], { cwd, env: isolated.env, encoding: 'utf8', timeout, killSignal: 'SIGKILL', maxBuffer: 32 * 1024 * 1024 });
686
688
  } finally {
687
689
  isolated.cleanup();
688
690
  }
@@ -32,6 +32,7 @@ import { quoteShellArg } from '../cli/logs-cli.js';
32
32
  import type { HarnessGroundingProvenance } from './context-capsule.js';
33
33
  import { hasGoalStepCodeName } from './goal-step-code-name.js';
34
34
  import { targetScopedGoalText } from './goal-text-path-scope.js';
35
+ import { parseReleaseNoteSection } from '../release-loop/release-note.js';
35
36
 
36
37
  // ⭐ `situation`/`complication` 은 **선택**이다 — 안 오면 `scqaNarrative` 가 종전 문면을 쓴다.
37
38
  // ⛔ 필수로 만들지 않는다: 그러면 인핸싱이 폴백으로 떨어진 저작(LLM 실패·`disabled`)이 전부 죽는다.
@@ -334,7 +335,7 @@ function endGoalAuthorPhase(phase: GoalAuthorTimedPhase, startedAt: number, auth
334
335
 
335
336
  export type GoalFileLintLevel = 'ERROR' | 'WARN';
336
337
 
337
- export type GoalFileLintTag = 'canonical-structure' | 'evidence-section' | 'boundary-size' | 'launch-branch' | 'unanswered-clarification' | 'shell-damage' | 'traced-path' | 'grounding-evidence' | 'empty-result-population' | 'decision-signal-numeric-source' | 'decision-signal-numeric-coverage' | 'decision-signal-proxy-expectation' | 'out-of-target-requirement' | 'heading-form-marker' | 'blanket-invariant' | 'self-question-subject' | 'artifact-launch-declaration' | 'all-negative-signals' | 'unreadable-signals' | 'alternative-signals' | 'count-observation' | 'identifier-name-observation' | 'self-reported-observation' | 'default-invocation-observation';
338
+ export type GoalFileLintTag = 'canonical-structure' | 'evidence-section' | 'boundary-size' | 'launch-branch' | 'unanswered-clarification' | 'shell-damage' | 'traced-path' | 'grounding-evidence' | 'empty-result-population' | 'decision-signal-numeric-source' | 'decision-signal-numeric-coverage' | 'decision-signal-proxy-expectation' | 'out-of-target-requirement' | 'heading-form-marker' | 'blanket-invariant' | 'self-question-subject' | 'artifact-launch-declaration' | 'all-negative-signals' | 'unreadable-signals' | 'alternative-signals' | 'count-observation' | 'identifier-name-observation' | 'self-reported-observation' | 'default-invocation-observation' | 'release-note';
338
339
 
339
340
  type GoalFileLintOrigin =
340
341
  | { readonly kind: 'known-incident'; readonly incident: string; readonly reference: string }
@@ -369,6 +370,7 @@ export const GOAL_FILE_LINT_ORIGINS: Record<GoalFileLintTag, GoalFileLintOrigin>
369
370
  'identifier-name-observation': { kind: 'known-incident', incident: "an authored observation of 'the list of test names this file registers' produced a test that greps its own source for test-name strings, which passes with every named test body emptied; 59 of 3164 goal documents carried the same wording", reference: 'PR #15791 (closed; superseded by #15802); docs/harness/observability/ISSUES.md OBS-T415' },
370
371
  'self-reported-observation': { kind: 'known-incident', incident: "an authored observation of 'the captureScope field of the emitted JSON' stayed green across two failed implementations: a Chrome CLI flag Chrome silently ignores (capture stayed 1280x900 against a true 4651) and a CDP call that timed out leaving no file at all, both of which still emitted captureScope:'full-page'", reference: 'docs/manual/MANUAL-web-clone-to-reproducible-resource-2026-09-08.md section 6b; PR #16201; measured 2026-09-08: 190 of 2867 goal documents that carry a decision signal match this shape, three times the 63 that match identifier-name-observation' },
371
372
  'default-invocation-observation': { kind: 'known-incident', incident: 'a scripts/*.ts checker landed green from fixture tests and, run once with no arguments from the repository root, reported covered 1 and uncovered 22 because its default catalog path never opened the map the goal named', reference: 'PR #19126; measured 2026-09-20 against the same-day controls #19123 and #19127' },
373
+ 'release-note': { kind: 'unknown-origin', label: 'ORIGIN-UNKNOWN' },
372
374
  } as const satisfies Record<GoalFileLintTag, GoalFileLintOrigin>;
373
375
 
374
376
  /** Machine-countable reason for a finding that retains the canonical-structure tag. */
@@ -1452,6 +1454,21 @@ export function lintGoalFile(document: string, branch: string, deps: GoalFileLin
1452
1454
  for (const finding of askSectionCountLintFindings(document)) findings.push(finding);
1453
1455
  for (const finding of headingFormMarkerLintFindings(document)) findings.push(finding);
1454
1456
  for (const finding of blanketInvariantLintFindings(document)) findings.push(finding);
1457
+ const releaseNote = parseReleaseNoteSection(document);
1458
+ for (const problem of releaseNote.problems) {
1459
+ findings.push({
1460
+ level: 'WARN', tag: 'release-note',
1461
+ message: `## 릴리스 노트 ${problem} invalid or missing; use - 한 줄: English summary, - 종류: feat|fix|security|internal (feature → feat), - 문서: path or 없음(이유), - 대상: next|later`,
1462
+ });
1463
+ }
1464
+ if (releaseNote.fragment || releaseNote.problems.length) {
1465
+ const section = markdownSection(document, '릴리스 노트') ?? '';
1466
+ const kind = releaseNote.fragment?.kind ?? /^[ \t]*- 종류:[ \t]*(.*?)[ \t]*$/m.exec(section)?.[1];
1467
+ const line = releaseNote.fragment?.line ?? /^[ \t]*- 한 줄:[ \t]*(.*?)[ \t]*$/m.exec(section)?.[1];
1468
+ if (kind && ['feat', 'fix', 'security'].includes(kind) && line && /[가-힣ㄱ-ㅎㅏ-ㅣ]/u.test(line)) {
1469
+ findings.push({ level: 'WARN', tag: 'release-note', message: '## 릴리스 노트 한 줄: 공개 노트에 실리는 문장은 영어로(internal 은 한국어 무방)' });
1470
+ }
1471
+ }
1455
1472
  for (const violation of selfQuestionSubjectViolations) {
1456
1473
  findings.push({
1457
1474
  level: 'WARN',
@@ -27,9 +27,13 @@ export type HostRegateDeps = {
27
27
  log?: (event: 'passed' | 'failed' | 'unmeasured' | 'base-raced', data: Record<string, unknown>) => void;
28
28
  };
29
29
 
30
+ const SPAWN_MAX_BUFFER = 256 * 1024 * 1024;
31
+
30
32
  const defaultCommand: NonNullable<HostRegateDeps['command']> = (bin, args, cwd, env) => {
31
- const r = spawnSync(bin, [...args], { cwd, env: env ?? process.env, encoding: 'utf8', timeout: 600_000 });
32
- return { status: r.status, stdout: r.stdout ?? '', stderr: (r.stderr ?? '') + (r.error ? `${r.error.message} (command unavailable)` : '') };
33
+ // bun ≥1.4 applies Node's 1MB default maxBuffer and silently truncates — test output here is often larger.
34
+ const r = spawnSync(bin, [...args], { cwd, env: env ?? process.env, encoding: 'utf8', timeout: 600_000, maxBuffer: SPAWN_MAX_BUFFER });
35
+ const overflow = (r.error as NodeJS.ErrnoException | undefined)?.code === 'ENOBUFS';
36
+ return { status: overflow ? null : r.status, stdout: r.stdout ?? '', stderr: (r.stderr ?? '') + (r.error ? `${r.error.message} (${overflow ? `output exceeded ${SPAWN_MAX_BUFFER} bytes` : 'command unavailable'})` : '') };
33
37
  };
34
38
 
35
39
  /** The informational pr-land gate always returns zero; enforce its measured report and the combined test result instead. */
@@ -3307,9 +3307,14 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
3307
3307
  if (opts.childLlm && (typeof opts.childLlm.provider !== 'string' || !opts.childLlm.provider.trim() || typeof opts.childLlm.model !== 'string' || !opts.childLlm.model.trim())) {
3308
3308
  throw new Error('childLlm.provider and childLlm.model must both be non-empty');
3309
3309
  }
3310
- const branch = opts.branchName ?? plannedSelfImplBranch(opts.feature, opts.goalId);
3311
3310
  const identity = resolveRunIdentity({ explicit: opts.runId });
3312
3311
  const { runId } = identity;
3312
+ const branch = opts.branchName ?? plannedSelfImplBranch(opts.feature, opts.goalId, runId);
3313
+ try {
3314
+ debug.log('self-implement.branch', 'planned', {
3315
+ runId, goalId: opts.goalId, branch, runSuffix: opts.branchName === undefined ? branch.match(/-r[a-z0-9]{6}$/)?.[0] ?? null : null,
3316
+ });
3317
+ } catch { /* observation must not block the run */ }
3313
3318
  const attemptOrdinal = incrementRunAttemptOrdinal(runId);
3314
3319
  const shardIdentity = parseRunShardIdentity(opts.feature);
3315
3320
  const observeOuter = makeRunObserver(runId, opts.goalId, debug.log.bind(debug), opts.seams.writeRunLedger, shardIdentity);
@@ -4517,8 +4522,8 @@ async function runSelfImplementInner(
4517
4522
  roundClassifications.push(classification);
4518
4523
  onRoundClassification(classification);
4519
4524
  };
4520
- const branch = opts.branchName ?? plannedSelfImplBranch(opts.feature, opts.goalId);
4521
4525
  const { runId, source: runIdSource } = identity ?? resolveRunIdentity({ explicit: opts.runId });
4526
+ const branch = opts.branchName ?? plannedSelfImplBranch(opts.feature, opts.goalId, runId);
4522
4527
  try {
4523
4528
  debug.log('run-identity', 'own', {
4524
4529
  runId, branch, source: runIdSource, nestDepth: nestInfo().depth, ...originObservationFields(),
@@ -280,7 +280,7 @@ function recordedString(entries: readonly RunLedgerEntry[], field: string): stri
280
280
  return undefined;
281
281
  }
282
282
 
283
- function summarizeTerminalLedger(runId: string, entries: readonly RunLedgerEntry[]): RunLedgerSummary {
283
+ export function summarizeTerminalLedger(runId: string, entries: readonly RunLedgerEntry[]): RunLedgerSummary {
284
284
  const last = entries.at(-1)?.timestamp;
285
285
  if (!last || !Number.isFinite(Date.parse(last)) || !hasTerminalRunStatus(entries)) throw new Error('ledger changed since plan');
286
286
  const terminator = [...entries].reverse().find((entry) => entry.event === 'human-stop' || entry.event === 'terminal'