elanous 0.2.10 → 0.2.12

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 (615) 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/-6XrAoD3eU7NyMHAE4xPS/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/1147-466ec15afacc1a65.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1394-ee3795b112907122.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1452.93700483b9d4fc61.js +9 -0
  8. package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 1829-b453a1b22d3999bb.js} +1 -1
  9. package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/3869-3cd46526879f454c.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/390.c373e31b3b9d0c41.js +5 -0
  18. package/apps/pwa/out/_next/static/chunks/3992-2e522a8ac5f2afde.js +25 -0
  19. package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/4685-c3c1c625b71accd1.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +2 -0
  24. package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/{8067-87ce253ec2f23697.js → 5845-e6c003ce8d5fd36a.js} +1 -1
  26. package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → 5cd7e3a9-404fd6b8d0ecd44c.js} +1 -1
  28. package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/7277-0b2fc7c16ba2d64b.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 7334.a6679d9168f37aa8.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/7758-0b018a9e4fc56200.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/79f8be9e-957d68913b590b08.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/8039-df92e08fede5de8f.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/8421-1c4acfb1395a0edf.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/8605-0259994816604c49.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/{2415.ed6428ff334c7ce2.js → 9269.d2a4795bc6812717.js} +1 -1
  47. package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/9656-c1540088f5859c80.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/9906-482c6619b3fdda20.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/9920-5cfdc5e8fd209825.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d16993889b125c42.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/approvals/page-cb169620945d1952.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-cefe8169e36cf62a.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/board/page-d2e35228bc497119.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-54b32bd27726588d.js → page-5c05b0da4bc1392c.js} +1 -1
  56. package/apps/pwa/out/_next/static/chunks/app/bots/page-e0ccdb576812b523.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/chat/page-813c4a52c9d05ca8.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/consult/page-4731f2c215358937.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/control/page-a022b257950f39e6.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/design-check/page-a2ffc717e04b8b0a.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/editor/page-b59ee4e70fc18fcf.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/exec/page-a67e11ba962337b5.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/field/page-c172dabd796eb7a3.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/healthz/route-d16993889b125c42.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/inside/page-1efd0cb9e0ed9d13.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/intake/page-d5dc7ff69a37b4cf.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/layout-e71c84189e8c683e.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/live/page-1d6a533ce5a6efcb.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/market/page-dfca9b7d2de0b135.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-793fa536b3a6ef37.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/morning/page-6dd3bfbbde2a5c2c.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/not-found-490653e8d4cc8852.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/observatory/page-77447356baad75d2.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-29e961191f6057df.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-c822f55b884d9104.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fa4ccfc432424e52.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/outputs/page-6d604227e1664b71.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/page-9c7e8f744a765daf.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/reflection/page-4048543b4b7ad5e0.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-4c3a3126abed551a.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/sessions/page-64b0aecb1ca75e75.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-4292d0149020e8a7.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/settings/page-db14d0be7f1c460c.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-e5d8072cd7cfef73.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/app/setup/layout-d16993889b125c42.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/app/setup/page-9547b5cec7cf76bf.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/app/share/page-e6dcb83265530636.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/app/showroom/page-45248f08d6aecae5.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/app/tasks/page-2cddeb9e2aeedc55.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/app/term/page-e297866051595d81.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/app/today/page-baba2e047872fb0f.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/app/trace/page-e895da4ec9cac345.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/app/vault/page-4344faf20216233b.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-698bb29f11864b89.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/app/workflows/page-bd6a6dc1ddc273f2.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2e9e2fb01c9da1c9.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-d62ceb13411ebba4.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/{2ea96958-e8c7e112de50b067.js → f48170cc-d05d28bf87a43bf4.js} +1 -1
  99. package/apps/pwa/out/_next/static/chunks/framework-172630e6aa5e2d7f.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/main-app-f0fe2d7477845181.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/main-fc98ac70b44eccb0.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/pages/_app-5bd7f10271f72413.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/pages/_error-97d0d85ea05fef9b.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/webpack-9e4d02d567b98240.js +1 -0
  105. package/apps/pwa/out/_next/static/css/128fb7fb670b2fbf.css +1 -0
  106. package/apps/pwa/out/approvals/index.html +1 -1
  107. package/apps/pwa/out/approvals/index.txt +23 -23
  108. package/apps/pwa/out/autopilot/index.html +1 -1
  109. package/apps/pwa/out/autopilot/index.txt +23 -23
  110. package/apps/pwa/out/board/index.html +1 -1
  111. package/apps/pwa/out/board/index.txt +23 -23
  112. package/apps/pwa/out/botlab/index.html +1 -1
  113. package/apps/pwa/out/botlab/index.txt +22 -22
  114. package/apps/pwa/out/bots/index.html +1 -1
  115. package/apps/pwa/out/bots/index.txt +22 -22
  116. package/apps/pwa/out/chat/index.html +1 -1
  117. package/apps/pwa/out/chat/index.txt +22 -22
  118. package/apps/pwa/out/consult/index.html +1 -1
  119. package/apps/pwa/out/consult/index.txt +23 -23
  120. package/apps/pwa/out/control/index.html +1 -1
  121. package/apps/pwa/out/control/index.txt +23 -23
  122. package/apps/pwa/out/design-check/index.html +1 -1
  123. package/apps/pwa/out/design-check/index.txt +23 -23
  124. package/apps/pwa/out/editor/index.html +1 -1
  125. package/apps/pwa/out/editor/index.txt +23 -23
  126. package/apps/pwa/out/exec/index.html +1 -1
  127. package/apps/pwa/out/exec/index.txt +23 -23
  128. package/apps/pwa/out/field/index.html +1 -1
  129. package/apps/pwa/out/field/index.txt +23 -23
  130. package/apps/pwa/out/healthz +1 -1
  131. package/apps/pwa/out/index.html +1 -1
  132. package/apps/pwa/out/index.txt +22 -22
  133. package/apps/pwa/out/inside/index.html +1 -0
  134. package/apps/pwa/out/inside/index.txt +31 -0
  135. package/apps/pwa/out/intake/index.html +1 -1
  136. package/apps/pwa/out/intake/index.txt +23 -23
  137. package/apps/pwa/out/live/index.html +1 -1
  138. package/apps/pwa/out/live/index.txt +23 -23
  139. package/apps/pwa/out/market/index.html +1 -1
  140. package/apps/pwa/out/market/index.txt +22 -22
  141. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  142. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  143. package/apps/pwa/out/morning/index.html +1 -1
  144. package/apps/pwa/out/morning/index.txt +23 -23
  145. package/apps/pwa/out/observatory/index.html +1 -1
  146. package/apps/pwa/out/observatory/index.txt +23 -23
  147. package/apps/pwa/out/ops/checklist/index.html +1 -1
  148. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  149. package/apps/pwa/out/ops/release/index.html +1 -1
  150. package/apps/pwa/out/ops/release/index.txt +22 -22
  151. package/apps/pwa/out/ops/seats/index.html +1 -1
  152. package/apps/pwa/out/ops/seats/index.txt +22 -22
  153. package/apps/pwa/out/outputs/index.html +1 -0
  154. package/apps/pwa/out/outputs/index.txt +30 -0
  155. package/apps/pwa/out/reflection/index.html +1 -1
  156. package/apps/pwa/out/reflection/index.txt +23 -23
  157. package/apps/pwa/out/scheduler/index.html +1 -1
  158. package/apps/pwa/out/scheduler/index.txt +23 -23
  159. package/apps/pwa/out/sessions/index.html +1 -1
  160. package/apps/pwa/out/sessions/index.txt +22 -22
  161. package/apps/pwa/out/settings/devices/index.html +1 -1
  162. package/apps/pwa/out/settings/devices/index.txt +23 -23
  163. package/apps/pwa/out/settings/index.html +1 -1
  164. package/apps/pwa/out/settings/index.txt +23 -23
  165. package/apps/pwa/out/setup/done/index.html +1 -1
  166. package/apps/pwa/out/setup/done/index.txt +23 -23
  167. package/apps/pwa/out/setup/index.html +1 -1
  168. package/apps/pwa/out/setup/index.txt +23 -23
  169. package/apps/pwa/out/share/index.html +1 -1
  170. package/apps/pwa/out/share/index.txt +23 -23
  171. package/apps/pwa/out/showroom/index.html +1 -1
  172. package/apps/pwa/out/showroom/index.txt +22 -22
  173. package/apps/pwa/out/tasks/index.html +1 -1
  174. package/apps/pwa/out/tasks/index.txt +23 -23
  175. package/apps/pwa/out/term/index.html +1 -1
  176. package/apps/pwa/out/term/index.txt +23 -24
  177. package/apps/pwa/out/today/index.html +1 -1
  178. package/apps/pwa/out/today/index.txt +23 -23
  179. package/apps/pwa/out/trace/index.html +1 -1
  180. package/apps/pwa/out/trace/index.txt +23 -23
  181. package/apps/pwa/out/vault/index.html +1 -1
  182. package/apps/pwa/out/vault/index.txt +23 -23
  183. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  184. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  185. package/apps/pwa/out/workflows/index.html +1 -1
  186. package/apps/pwa/out/workflows/index.txt +23 -23
  187. package/apps/pwa/out/workspace/index.html +1 -1
  188. package/apps/pwa/out/workspace/index.txt +23 -24
  189. package/apps/pwa/out/worktrees/index.html +1 -1
  190. package/apps/pwa/out/worktrees/index.txt +23 -23
  191. package/graphs/context/nightly-condense.yaml +21 -0
  192. package/graphs/context/recipes.yaml +12 -0
  193. package/graphs/demo/inside-seed.yaml +20 -0
  194. package/graphs/demo/recipes.yaml +12 -0
  195. package/graphs/drafts/draft-cleanup.yaml +19 -0
  196. package/graphs/drafts/recipes.yaml +23 -0
  197. package/graphs/intake/intake-daily.yaml +2 -1
  198. package/graphs/ops/recipes.yaml +3 -0
  199. package/graphs/ops/secret-exposure.yaml +24 -0
  200. package/graphs/release/recipes.yaml +3 -0
  201. package/graphs/release/release-loop.yaml +4 -1
  202. package/graphs/video/explainer-line.yaml +2 -2
  203. package/package.json +2 -1
  204. package/plugins/card-followup/examples/sample-card.png +0 -0
  205. package/plugins/elanous-hwp/README.md +25 -0
  206. package/plugins/elanous-hwp/cli.ts +22 -0
  207. package/plugins/elanous-hwp/converter.ts +237 -0
  208. package/plugins/elanous-hwp/fixtures/fixed-controls.hwpx +0 -0
  209. package/plugins/elanous-hwp/fixtures/fixed-table-controls.hwpx +0 -0
  210. package/plugins/elanous-hwp/fixtures/fixed.hwpx +0 -0
  211. package/plugins/elanous-hwp/plugin.json +16 -0
  212. package/plugins/elanous-hwp/plugin.ts +40 -0
  213. package/scripts/ci-public-export-leak-gate.ts +75 -14
  214. package/scripts/ci-public-leak-gate.ts +44 -9
  215. package/scripts/context-hooks/claude-stop.sh +4 -0
  216. package/scripts/context-hooks/emit.ts +24 -0
  217. package/scripts/context-hooks/post-tool-use.sh +4 -0
  218. package/scripts/coord-channel-watch.sh +12 -0
  219. package/scripts/coord-requests-bridge.ts +143 -0
  220. package/scripts/field-feed/lib.ts +4 -0
  221. package/scripts/fleet/node-setup.sh +20 -3
  222. package/scripts/homebrew-formula-bump.ts +69 -0
  223. package/scripts/lib/telegram-inject-args.ts +31 -0
  224. package/scripts/marketing/measure-site-numbers.ts +169 -0
  225. package/scripts/measure-gate-shards.ts +309 -0
  226. package/scripts/measure-pod-memory-by-goal.ts +130 -0
  227. package/scripts/mission-request-judge.ts +31 -1
  228. package/scripts/morning-report.ts +2 -2
  229. package/scripts/opportunity-scan.ts +1 -1
  230. package/scripts/release-loop/auto-approve-node.ts +21 -4
  231. package/scripts/release-loop/checklist-gate-node.ts +2 -1
  232. package/scripts/release-loop/cut-branch.ts +84 -0
  233. package/scripts/release-loop/gate-node.ts +22 -15
  234. package/scripts/release-loop/mac-smoke-node.ts +229 -0
  235. package/scripts/release-loop/node-verdict.ts +3 -3
  236. package/scripts/release-loop/ops-upgrade-node.ts +47 -6
  237. package/scripts/release-loop/publish-node.ts +4 -1
  238. package/scripts/release-loop/tui-node-checks.ts +42 -0
  239. package/scripts/release-loop/tui-sim-node.ts +32 -8
  240. package/scripts/release-loop/unattended-release.ts +5 -3
  241. package/scripts/release-loop/version-node.ts +74 -13
  242. package/scripts/signal-digest-cycle.ts +2 -7
  243. package/scripts/signal-digest-photo.ts +11 -0
  244. package/scripts/tech1-public-measure.ts +94 -0
  245. package/scripts/telegram-inject.ts +14 -6
  246. package/scripts/test-diet/node.ts +1 -1
  247. package/scripts/test-diet/remeasure-list.ts +112 -0
  248. package/scripts/tui-regress.ts +2 -2
  249. package/scripts/ux/demo-rehearsal.ts +247 -0
  250. package/scripts/ux/demo-seed.ts +108 -0
  251. package/scripts/ux/lib/rehearsal-verdict.ts +80 -0
  252. package/skills/diagram-master/SKILL.md +366 -0
  253. package/skills/diagram-master/references/engines/excalidraw/color-palette.md +67 -0
  254. package/skills/diagram-master/references/engines/excalidraw/element-templates.md +182 -0
  255. package/skills/diagram-master/references/engines/excalidraw/json-schema.md +71 -0
  256. package/skills/diagram-master/references/engines/excalidraw/render_excalidraw.py +189 -0
  257. package/skills/diagram-master/references/engines/excalidraw/render_template.html +57 -0
  258. package/skills/diagram-master/references/engines/matplotlib/matplotlib-patterns.md +235 -0
  259. package/skills/diagram-master/references/engines/matplotlib/render_matplotlib.py +184 -0
  260. package/skills/diagram-master/references/engines/mermaid/domain-templates.md +435 -0
  261. package/skills/diagram-master/references/engines/mermaid/render_mermaid.py +116 -0
  262. package/skills/diagram-master/references/engines/mermaid/syntax-reference.md +2247 -0
  263. package/skills/diagram-master/references/engines/nanobannana/render_nanobannana2.py +168 -0
  264. package/skills/diagram-master/references/engines/svg/color-palette.md +188 -0
  265. package/skills/diagram-master/references/engines/svg/html-base-template.html +332 -0
  266. package/skills/diagram-master/references/engines/svg/interactivity-patterns.md +435 -0
  267. package/skills/diagram-master/references/engines/svg/render_interactive_svg.py +212 -0
  268. package/skills/diagram-master/references/engines/svg/svg-element-templates.md +349 -0
  269. package/skills/diagram-master/references/examples/README.md +69 -0
  270. package/skills/diagram-master/references/examples/ref_grid_architecture.png +0 -0
  271. package/skills/diagram-master/references/examples/ref_layered_flow.png +0 -0
  272. package/skills/diagram-master/references/pyproject.toml +16 -0
  273. package/skills/diagram-master/references/quality-checklist.md +33 -0
  274. package/skills/diagram-master/references/routing-guide.md +164 -0
  275. package/skills/diagram-master/references/uv.lock +1356 -0
  276. package/skills/google-workspace/SKILL.md +33 -0
  277. package/skills/google-workspace/scripts/gws-safe.sh +177 -0
  278. package/skills/grill-me/SKILL.md +1 -1
  279. package/skills/lecture-note-digitizer/SKILL.md +926 -0
  280. package/skills/lecture-note-digitizer/references/convert_to_pdf.py +30 -0
  281. package/skills/lecture-note-digitizer/references/extract_slide_diagram.py +73 -0
  282. package/skills/lecture-note-digitizer/references/html-template.md +167 -0
  283. package/skills/lecture-note-digitizer/references/katex-style-guide.md +165 -0
  284. package/skills/omni-crawl/SKILL.md +21 -11
  285. package/skills/omni-crawl/scripts/main.ts +6 -6
  286. package/skills/omni-crawl/src/router.ts +3 -3
  287. package/skills/omni-digest/SKILL.md +17 -9
  288. package/skills/omni-digest/digest.md +2 -2
  289. package/skills/omni-digest/src/fetch.ts +30 -3
  290. package/skills/pitch-deck/SKILL.md +2 -2
  291. package/skills/pitch-deck/template.html +2 -2
  292. package/skills/youtube-master/SKILL.md +163 -0
  293. package/skills/youtube-master/package-lock.json +558 -0
  294. package/skills/youtube-master/package.json +13 -0
  295. package/skills/youtube-master/references/study-note-template.md +124 -0
  296. package/skills/youtube-master/scripts/main.ts +435 -0
  297. package/skills/youtube-master/scripts/transcribe-local.ts +24 -0
  298. package/skills/youtube-master/src/audio.ts +63 -0
  299. package/skills/youtube-master/src/domain-staging.ts +50 -0
  300. package/skills/youtube-master/src/env.ts +36 -0
  301. package/skills/youtube-master/src/obsidian.ts +155 -0
  302. package/skills/youtube-master/src/router.ts +110 -0
  303. package/skills/youtube-master/src/stt.ts +199 -0
  304. package/skills/youtube-master/src/study-note.ts +175 -0
  305. package/skills/youtube-master/src/summarize.ts +358 -0
  306. package/skills/youtube-master/src/transcript.ts +76 -0
  307. package/skills/youtube-master/src/types.ts +91 -0
  308. package/skills/youtube-master/src/util.ts +93 -0
  309. package/skills/youtube-master/src/youtube.ts +70 -0
  310. package/skills/youtube-master/tsconfig.json +13 -0
  311. package/src/acp/server.ts +5 -2
  312. package/src/agent-mission/driver.ts +215 -23
  313. package/src/agent-mission/pty-decision.ts +71 -0
  314. package/src/agent-mission/review-loop.ts +30 -0
  315. package/src/autopilot/build/llm-conflict-merge.ts +16 -3
  316. package/src/autopilot/pty-control-loop.ts +4 -1
  317. package/src/autopilot/tool-registry.ts +2 -0
  318. package/src/boot/daemon-prompt-turn.ts +5 -0
  319. package/src/boot/daemon-runtime.ts +9 -3
  320. package/src/boot/daemon-tools/web-search.ts +3 -0
  321. package/src/card-followup/core.ts +109 -0
  322. package/src/chat/index.ts +2 -0
  323. package/src/cli/drive-input-jail.ts +101 -0
  324. package/src/cli/hooks-cli.ts +17 -3
  325. package/src/cli/make-cli.ts +43 -0
  326. package/src/cli/persona-cli.ts +98 -9
  327. package/src/cli/plugin-cli.ts +31 -22
  328. package/src/cli/pod-cli.ts +36 -9
  329. package/src/cli/pod-memory-advice.ts +35 -0
  330. package/src/cli/pr-cli.ts +51 -15
  331. package/src/cli/pty-drive-cli.ts +41 -7
  332. package/src/cli/pty-drive-verdict.ts +40 -0
  333. package/src/cli/release-cli.ts +63 -10
  334. package/src/cli/setup-cli.ts +43 -1
  335. package/src/cli/skills-cli.ts +17 -1
  336. package/src/context-bus/claude-hooks-install.ts +10 -0
  337. package/src/context-bus/context-now-surfaces.ts +65 -0
  338. package/src/context-bus/context-now.ts +104 -0
  339. package/src/context-bus/coord-events.ts +2 -2
  340. package/src/context-bus/external-events.ts +52 -0
  341. package/src/context-bus/long-term-memory.ts +108 -0
  342. package/src/context-bus/nightly-condense.ts +153 -0
  343. package/src/context-bus/session-events.ts +35 -0
  344. package/src/dashboard/dashboard-status-lines.ts +2 -0
  345. package/src/dashboard/index.ts +17 -4
  346. package/src/dashboard/slash-runtime/dashboard-handlers.ts +31 -1
  347. package/src/decisions/decision-cards.ts +10 -3
  348. package/src/decisions/decision-ledger.ts +13 -5
  349. package/src/discord/sprint21-runtime.ts +5 -1
  350. package/src/discord-self-message.ts +32 -0
  351. package/src/discord-slash-wire.ts +5 -1
  352. package/src/discord.ts +17 -1
  353. package/src/domains/core-tools.ts +3 -2
  354. package/src/domains/outbound-alert.ts +105 -32
  355. package/src/domains/outbound-log-probe.ts +8 -0
  356. package/src/domains/telegram-kind-route.ts +51 -13
  357. package/src/exec-requests/default-outputs.ts +25 -0
  358. package/src/exec-requests/planner.ts +10 -2
  359. package/src/exec-requests/runner.ts +20 -6
  360. package/src/exec-requests/store.ts +3 -0
  361. package/src/execution-loop/launch-gate.ts +109 -0
  362. package/src/field/field-reel.ts +5 -1
  363. package/src/field-note/index.ts +201 -0
  364. package/src/field-note/telegram.ts +142 -0
  365. package/src/field-note/transcribe.ts +248 -0
  366. package/src/graph-kinds/registry.ts +3 -3
  367. package/src/graph-runner/graph-cli.ts +61 -1
  368. package/src/graph-runner/graph-grow-llm.ts +47 -0
  369. package/src/graph-runner/graph-grow.ts +50 -0
  370. package/src/graph-runner/runner.ts +329 -33
  371. package/src/grok/agent-search.ts +8 -2
  372. package/src/guardian/secret-exposure-scan.ts +101 -0
  373. package/src/harness/harness-cli-command.ts +375 -13
  374. package/src/harness/harness-pod-dispatch.ts +43 -23
  375. package/src/harness/harness-seams.ts +38 -4
  376. package/src/harness/harness-stop.ts +23 -4
  377. package/src/harness/launch-registry.ts +91 -0
  378. package/src/harness/process-garbage.ts +5 -3
  379. package/src/hooks/consult-intake.ts +153 -0
  380. package/src/hooks/dispatch.ts +82 -0
  381. package/src/hooks/expose.ts +254 -0
  382. package/src/hooks/github-shadow.ts +108 -0
  383. package/src/hooks/providers.ts +71 -5
  384. package/src/hooks/queue.ts +1 -1
  385. package/src/hooks/receiver.ts +94 -16
  386. package/src/index.ts +76 -14
  387. package/src/intake-plane/categorize.ts +18 -46
  388. package/src/intake-plane/discord-seat-work.ts +38 -2
  389. package/src/intake-plane/front-route-classifier.ts +33 -39
  390. package/src/intake-plane/persona-answer.ts +30 -0
  391. package/src/intake-plane/runtime-callables.ts +8 -1
  392. package/src/intake-plane/telegram-seat-work.ts +36 -2
  393. package/src/intake-plane/wish-card.ts +33 -0
  394. package/src/intake-plane/wish-folder.ts +67 -0
  395. package/src/intent-gate/gate.ts +30 -10
  396. package/src/live/detail-switch.ts +5 -0
  397. package/src/llm/difficulty.ts +57 -0
  398. package/src/llm/judge-layer.ts +101 -0
  399. package/src/llm/mission-router.ts +33 -19
  400. package/src/loops/contract/examples/cmo-seat.loop.yaml +51 -0
  401. package/src/loops/contract/examples/orchestrator.loop.yaml +57 -0
  402. package/src/loops/contract/examples/steward.loop.yaml +50 -0
  403. package/src/loops/contract/loop-agent.schema.json +138 -0
  404. package/src/loops/contract/validate.ts +64 -0
  405. package/src/loops/registry.ts +20 -6
  406. package/src/market/signed-index.ts +4 -0
  407. package/src/maturity/bot-command-maturity.ts +45 -0
  408. package/src/maturity/feature-maturity.ts +27 -4
  409. package/src/model-tier/index.ts +2 -2
  410. package/src/model-tier/tier-resolver.ts +20 -20
  411. package/src/model-tier/types.ts +6 -6
  412. package/src/mss/logging/log-store.ts +15 -0
  413. package/src/nexus/api/config-model-tier.ts +5 -4
  414. package/src/nexus/api/fabric-plans.ts +53 -0
  415. package/src/nexus/api/harness-api.ts +5 -1
  416. package/src/nexus/api/health.ts +2 -0
  417. package/src/nexus/api/http-server.ts +67 -8
  418. package/src/nexus/api/inside-events.ts +226 -0
  419. package/src/nexus/api/meta-api.ts +21 -8
  420. package/src/nexus/api/missions.ts +6 -1
  421. package/src/nexus/api/ops-seats.ts +33 -0
  422. package/src/nexus/api/outbound-report.ts +40 -7
  423. package/src/nexus/api/outputs-route.ts +44 -0
  424. package/src/nexus/api/personas.ts +95 -53
  425. package/src/nexus/api/seat-requests.ts +44 -2
  426. package/src/nexus/api/sessions-store.ts +17 -38
  427. package/src/nexus/api/sub-seat-charters.ts +34 -0
  428. package/src/nexus/api/tabs-mutations.ts +3 -1
  429. package/src/nexus/api/task-cards-api.ts +19 -0
  430. package/src/nexus/api/trace.ts +18 -6
  431. package/src/nexus/api/workflow-history.ts +88 -0
  432. package/src/nexus/api/workflow-run-event-bridge.ts +1 -0
  433. package/src/nexus/api/workflows.ts +132 -6
  434. package/src/nexus/index.ts +2 -0
  435. package/src/nexus/kinds/webterm.ts +2 -1
  436. package/src/nexus/outbound/delivery-ledger.ts +17 -8
  437. package/src/nexus/outbound/router.ts +8 -3
  438. package/src/nexus/webterm/pty.ts +6 -2
  439. package/src/onboarding/cli-auth-status.ts +80 -0
  440. package/src/onboarding/voice-ai.ts +3 -3
  441. package/src/org/seat-budget.ts +48 -0
  442. package/src/org/stall-escalation.ts +63 -0
  443. package/src/outputs/ledger.ts +120 -0
  444. package/src/persona/persona-todo.ts +79 -0
  445. package/src/persona/presets.ts +123 -2
  446. package/src/persona/team-set.ts +155 -0
  447. package/src/plugins/install/plugin-install.ts +14 -0
  448. package/src/plugins/maker/market-bundle.ts +110 -0
  449. package/src/plugins/maker/plugin-maker.ts +254 -11
  450. package/src/plugins/maker/wizard-generate.ts +103 -0
  451. package/src/pod-lease/admission.ts +150 -0
  452. package/src/pod-lease/dependency-state.ts +85 -0
  453. package/src/pod-lease/host-lease.ts +127 -0
  454. package/src/project/project-suggestion.ts +16 -0
  455. package/src/project/session-context.ts +24 -0
  456. package/src/release-loop/checklist.ts +17 -5
  457. package/src/release-loop/feature-store.ts +29 -13
  458. package/src/release-loop/landed-but-yellow.ts +48 -0
  459. package/src/release-loop/next-md-merge.ts +58 -0
  460. package/src/release-loop/release-note.ts +59 -4
  461. package/src/seat-dispatch/seat-ask.ts +130 -0
  462. package/src/seat-dispatch/seat-questions.ts +100 -0
  463. package/src/seat-loop/seat-loop.ts +709 -29
  464. package/src/seat-loop/seat-report.ts +37 -2
  465. package/src/self-dev/dev-cli.ts +4 -1
  466. package/src/self-dev/dev-pipeline.ts +37 -1
  467. package/src/self-dev/draft-sweep.ts +79 -40
  468. package/src/self-dev/draft-triage-rules.ts +62 -7
  469. package/src/self-dev/fabric-plan-core.ts +297 -0
  470. package/src/self-dev/orchestrate.ts +36 -4
  471. package/src/self-dev/release-path-guard.ts +62 -0
  472. package/src/self-dev/shard-aggregate.ts +230 -0
  473. package/src/self-dev/shard-placement.ts +60 -0
  474. package/src/self-dev/shard-plan.ts +48 -0
  475. package/src/self-dev/shard-pod-run.ts +77 -0
  476. package/src/self-dev/shard-run.ts +105 -0
  477. package/src/self-implement/budget-gate.ts +29 -12
  478. package/src/self-implement/gate-baseline.ts +1 -1
  479. package/src/self-implement/gate-candidate-ranking.ts +60 -0
  480. package/src/self-implement/gate-cli.ts +40 -1
  481. package/src/self-implement/gate-scope.ts +44 -13
  482. package/src/self-implement/graph-variant.ts +171 -0
  483. package/src/self-implement/harness-policy-gates.ts +1 -1
  484. package/src/self-implement/headless-elanous-driver.ts +27 -3
  485. package/src/self-implement/host-regate.ts +22 -1
  486. package/src/self-implement/importer-test-index.ts +2 -1
  487. package/src/self-implement/l8-merge-queue.ts +130 -0
  488. package/src/self-implement/orchestrator.ts +257 -9
  489. package/src/self-implement/route-consumer-test-index.ts +122 -0
  490. package/src/self-implement/seams.ts +72 -11
  491. package/src/self-implement/test-seams.ts +2 -0
  492. package/src/self-implement/uncommitted-work.ts +59 -0
  493. package/src/session/index.ts +36 -13
  494. package/src/skills/gift-install.ts +215 -0
  495. package/src/skills/tools/web-search.ts +4 -1
  496. package/src/steward/launch.ts +42 -26
  497. package/src/steward/steward-cards.ts +17 -9
  498. package/src/steward/triage.ts +46 -14
  499. package/src/task-cards/card-cli.ts +25 -1
  500. package/src/task-orchestrator/mission.ts +5 -0
  501. package/src/task-orchestrator/store.ts +12 -3
  502. package/src/task-orchestrator/surfaces/pod-command-job.ts +43 -17
  503. package/src/task-orchestrator/surfaces/pod-lease.ts +156 -19
  504. package/src/task-orchestrator/surfaces/pod-pool.ts +127 -26
  505. package/src/task-orchestrator/surfaces/self-implement-pod.ts +222 -40
  506. package/src/task-orchestrator/surfaces/self-implement.ts +15 -2
  507. package/src/task-orchestrator/types.ts +2 -0
  508. package/src/telegram-card-followup.ts +16 -96
  509. package/src/telegram-commands.ts +31 -1
  510. package/src/telegram-project-command.ts +145 -0
  511. package/src/telegram-report.ts +21 -17
  512. package/src/telegram.ts +61 -8
  513. package/src/tool-runtime/self-cognition-runtimes.ts +13 -3
  514. package/src/user-config.ts +110 -24
  515. package/src/version/packed-revision.json +1 -1
  516. package/src/web-search/grok.ts +7 -1
  517. package/src/web-search/index.ts +28 -1
  518. package/src/web-search/provider.ts +4 -0
  519. package/src/web-search/x-intent.ts +5 -0
  520. package/src/workflow-runtime/executor.ts +277 -47
  521. package/src/workflow-runtime/index.ts +1 -0
  522. package/src/workflow-runtime/nodes/subworkflow.ts +60 -0
  523. package/src/workflow-runtime/plugin-kind-node.ts +1 -1
  524. package/src/workflow-runtime/schema.ts +79 -4
  525. package/src/workflow-runtime/types.ts +35 -4
  526. package/apps/pwa/out/_next/static/2DsiDfDCks44g1cPUMhiW/_buildManifest.js +0 -1
  527. package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
  528. package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
  529. package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
  530. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
  531. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
  532. package/apps/pwa/out/_next/static/chunks/2375-cb6debea56cce4fb.js +0 -1
  533. package/apps/pwa/out/_next/static/chunks/24-e6d28380efab7faf.js +0 -1
  534. package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
  535. package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
  536. package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
  537. package/apps/pwa/out/_next/static/chunks/3615-740dd270764ffa24.js +0 -1
  538. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
  539. package/apps/pwa/out/_next/static/chunks/3734-1015fe991eb9b7e7.js +0 -1
  540. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
  541. package/apps/pwa/out/_next/static/chunks/5012-a7fe6f3c8b6fe45a.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
  543. package/apps/pwa/out/_next/static/chunks/5783-ef40ebfc91d55230.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
  545. package/apps/pwa/out/_next/static/chunks/62-ff227c3edb8b248b.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
  549. package/apps/pwa/out/_next/static/chunks/7633-1b3709b0854c9412.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/7951-8b03dbb10b24cd06.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/8195-6fde3d7a39859206.js +0 -10
  556. package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/8894-b5fb36bab837cd29.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/9454-84d0d5d77446bd92.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/988-5f92e7ab7ccea3dd.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/9989-5582582f06429840.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d2253d7e58ef1890.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-5441d4a9ab35e518.js +0 -1
  567. package/apps/pwa/out/_next/static/chunks/app/board/page-d9b12d680922d980.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/app/bots/page-e268d6d145827a22.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/app/chat/page-f8116559c7424da7.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/app/consult/page-1085399fd0f32112.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/app/control/page-1df686a14f65895a.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/app/design-check/page-54e1f966ab60a4dc.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/app/editor/page-7ed95f296f5ee8e9.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/app/exec/page-11edac668d77e5c3.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/app/field/page-59e2b9a22cd5a6d5.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/app/intake/page-2976df21ad74ab48.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/app/layout-34d211a67f16d510.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/app/live/page-8ff652d5d950639b.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/app/market/page-b31610deda76337b.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6cb0e1bbefe5fb3d.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/app/morning/page-a286a152cf584576.js +0 -1
  583. package/apps/pwa/out/_next/static/chunks/app/not-found-d9237581e7003bcb.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/app/observatory/page-83274e761643fdd2.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-ceee6cfb1d4e6d68.js +0 -1
  586. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-9c9f8bb3e4a64091.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c17217603ea3426a.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/app/page-efb6177cbf1e8a51.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/app/reflection/page-d307523703bc4df3.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-462f4e4bab45ce7d.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/app/sessions/page-8990e96f33af3837.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e168fa1030af0548.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/app/settings/page-1233e05a4068bc9c.js +0 -1
  594. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-13a1840b1fac337a.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/app/setup/page-64a29fc80b11ab55.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/app/share/page-68e787339a16b3dc.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/app/showroom/page-4fddc90b23feb215.js +0 -1
  599. package/apps/pwa/out/_next/static/chunks/app/tasks/page-886ba8260dc79c79.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/app/term/page-0f26507a1722e40b.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/app/today/page-109d4ccd42259c9d.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/app/trace/page-f5c2a4e510726774.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/app/vault/page-b2e0039416f42522.js +0 -1
  604. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-26102fd7350f171e.js +0 -1
  605. package/apps/pwa/out/_next/static/chunks/app/workflows/page-708948bc8a802794.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/app/workspace/page-5e5611620f78fb0b.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-139ba5c367bbf60a.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/main-app-202b4e81f33895ea.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/webpack-e5bea36f183b25e6.js +0 -1
  614. package/apps/pwa/out/_next/static/css/43feca5fd387dcbd.css +0 -1
  615. /package/apps/pwa/out/_next/static/{2DsiDfDCks44g1cPUMhiW → -6XrAoD3eU7NyMHAE4xPS}/_ssgManifest.js +0 -0
@@ -7,8 +7,9 @@
7
7
  * 매니페스트가 실은 런 계약(`ELANOUS_RUN_CONTRACT`)으로 자기가 Pod 인 줄 안다(graph-run-contract.ts).
8
8
  */
9
9
  import { spawn } from 'node:child_process';
10
- import { readFileSync, realpathSync } from 'node:fs';
11
- import { isAbsolute, relative, resolve, sep } from 'node:path';
10
+ import { mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
11
+ import { isAbsolute, join, relative, resolve, sep } from 'node:path';
12
+ import { selfDevRunsDir } from '../self-dev/run-store.js';
12
13
  import { findGitDir } from '../git-fs/locate.js';
13
14
  import { debug } from '../debug/log.js';
14
15
  import { ELANOUS_ENTRY_SCRIPT } from '../self-implement/seams.js';
@@ -23,6 +24,8 @@ export interface HarnessPodDispatchInput {
23
24
  readonly dispatchRecorded?: boolean;
24
25
  /** Pod 메모리 등급(standard|high) — 오케스트레이터 환경 `ELANOUS_POD_MEMORY_TIER` 로 Job 까지 간다. */
25
26
  readonly podMemory?: string;
27
+ /** L7c — Pod queue predecessor (goal ID or PR) — `ELANOUS_POD_AFTER` carries it to every Pod Job admission. */
28
+ readonly after?: string;
26
29
  readonly autoMerge?: boolean;
27
30
  readonly base?: string;
28
31
  readonly json?: boolean;
@@ -30,16 +33,9 @@ export interface HarnessPodDispatchInput {
30
33
  readonly source?: string;
31
34
  }
32
35
 
33
- /** 오케스트레이터가 `;;` 로 골을 나누므로 한 골 안의 `;;` 는 풀어 둔다(한 발 = 한 골). */
34
- export function podGoalText(input: HarnessPodDispatchInput, readFile: (path: string) => string = (p) => readFileSync(p, 'utf8')): string {
35
- // 오케스트레이터에는 내용도 보내고, 저장소 안의 ask 는 별도로 Secret 을 통해 Pod 클론에 심는다.
36
- const text = input.entrance === 'cli-harness-ask' ? readFile(input.input) : input.input;
37
- return text.replace(/;;/gu, '; ;');
38
- }
39
-
40
- export function podOrchestrateArgs(input: HarnessPodDispatchInput, goal: string): string[] {
36
+ export function podOrchestrateArgs(input: HarnessPodDispatchInput, goalFile: string): string[] {
41
37
  return [
42
- ELANOUS_ENTRY_SCRIPT, 'self', 'orchestrate', goal,
38
+ ELANOUS_ENTRY_SCRIPT, 'self', 'orchestrate', '--goal-file', goalFile,
43
39
  '--substrate', 'pod',
44
40
  ...(input.podPool ? ['--pod-pool', input.podPool] : []),
45
41
  // 하니스 기본(자동 병합)을 따른다 · 끄면 PR 까지 — 어느 쪽이든 Pod 가 사라져도 결과가 남는다.
@@ -66,13 +62,30 @@ export function dispatchHarnessOnPod(input: HarnessPodDispatchInput, deps: PodDi
66
62
  return 2;
67
63
  }
68
64
  }
69
- const goal = podGoalText(input, deps.readFile ?? ((p) => readFileSync(resolve(cwd, p), 'utf8')));
70
- const args = podOrchestrateArgs(input, goal);
65
+ let tempDir: string | undefined;
66
+ let goalFile: string;
67
+ let goalChars: number;
68
+ if (input.entrance === 'cli-harness-ask') {
69
+ goalFile = resolve(cwd, input.input);
70
+ goalChars = (deps.readFile ?? ((p) => readFileSync(p, 'utf8')))(goalFile).length;
71
+ } else {
72
+ const goal = input.input;
73
+ const runsDir = selfDevRunsDir();
74
+ mkdirSync(runsDir, { recursive: true });
75
+ tempDir = mkdtempSync(join(runsDir, 'pod-goal-'));
76
+ goalFile = join(tempDir, 'goal.txt');
77
+ writeFileSync(goalFile, goal, { mode: 0o600 });
78
+ goalChars = goal.length;
79
+ }
80
+ const args = podOrchestrateArgs(input, goalFile);
81
+ debug.log('harness.substrate', 'goal-file', { entrance: input.entrance, mode: tempDir ? 'temp' : 'path' });
71
82
  const env = { ...process.env };
72
83
  delete env.ELANOUS_POD_GOAL_DOC;
73
84
  delete env.ELANOUS_DISPATCH_RECORDED;
74
85
  if (input.dispatchRecorded) env.ELANOUS_DISPATCH_RECORDED = '1';
75
86
  if (input.podMemory) env.ELANOUS_POD_MEMORY_TIER = input.podMemory;
87
+ delete env.ELANOUS_POD_AFTER;
88
+ if (input.after) env.ELANOUS_POD_AFTER = input.after;
76
89
  if (input.entrance === 'cli-harness-ask') {
77
90
  const root = findGitDir(cwd)?.root;
78
91
  const path = resolve(cwd, input.input);
@@ -84,8 +97,8 @@ export function dispatchHarnessOnPod(input: HarnessPodDispatchInput, deps: PodDi
84
97
  }
85
98
  }
86
99
  debug.log('harness.substrate', 'dispatch-pod', {
87
- entrance: input.entrance, podPool: input.podPool ?? null, podMemory: input.podMemory ?? null, autoMerge: input.autoMerge !== false,
88
- goalChars: goal.length, ...(input.entrance === 'cli-harness-ask' ? { goalPath: input.input } : {}),
100
+ entrance: input.entrance, podPool: input.podPool ?? null, podMemory: input.podMemory ?? null, after: input.after ?? null, autoMerge: input.autoMerge !== false,
101
+ goalChars, ...(input.entrance === 'cli-harness-ask' ? { goalPath: input.input } : {}),
89
102
  });
90
103
  const run = deps.run ?? ((cmd: string, a: readonly string[], childEnv: NodeJS.ProcessEnv) => new Promise<number>((resolveStatus) => {
91
104
  const child = (deps.spawnChild ?? spawn)(cmd, [...a], { stdio: deps.onOutput ? ['inherit', 'pipe', 'inherit'] : 'inherit', env: childEnv });
@@ -104,13 +117,20 @@ export function dispatchHarnessOnPod(input: HarnessPodDispatchInput, deps: PodDi
104
117
  child.once('error', () => { cleanup(); resolveStatus(1); });
105
118
  child.once('close', (code, signal) => { cleanup(); resolveStatus(code ?? ((signal ?? forwarded) === 'SIGINT' ? 130 : 143)); });
106
119
  }));
107
- const status = run(process.execPath, args, env);
108
- if (typeof status === 'number' || status === null) {
109
- debug.log('harness.substrate', 'dispatch-pod-exit', { entrance: input.entrance, status });
110
- return status ?? 1;
120
+ const cleanup = () => { if (tempDir) rmSync(tempDir, { recursive: true, force: true }); };
121
+ try {
122
+ const status = run(process.execPath, args, env);
123
+ if (typeof status === 'number' || status === null) {
124
+ cleanup();
125
+ debug.log('harness.substrate', 'dispatch-pod-exit', { entrance: input.entrance, status });
126
+ return status ?? 1;
127
+ }
128
+ return status.then((code) => {
129
+ debug.log('harness.substrate', 'dispatch-pod-exit', { entrance: input.entrance, status: code });
130
+ return code;
131
+ }).finally(cleanup);
132
+ } catch (error) {
133
+ cleanup();
134
+ throw error;
111
135
  }
112
- return status.then((code) => {
113
- debug.log('harness.substrate', 'dispatch-pod-exit', { entrance: input.entrance, status: code });
114
- return code;
115
- });
116
136
  }
@@ -23,6 +23,7 @@ import { buildReviewSeam, gateEvidenceNote, reviewResultToCritiqueLike, type Gat
23
23
  import { buildExecuteProgressJudge } from './execute-progress-judge.js';
24
24
  import type { StagedHarnessSeams } from './staged-harness.js';
25
25
  import { debug } from '../debug/log.js';
26
+ import { releasePathHold, releasePathHoldComment, RELEASE_PATH_LABEL } from '../self-dev/release-path-guard.js';
26
27
  import { setEventLoopActivity } from '../debug/event-loop-watchdog.js';
27
28
  import { nestInfo } from '../agent/nest-depth.js';
28
29
  import { originObservationFields } from '../agent/origin-observation.js';
@@ -721,10 +722,43 @@ export function buildHarnessSeams(deps: HarnessSeamsDeps): StagedHarnessSeams {
721
722
  suppressedRiskHits: eligibility?.suppressedRiskHits.length ?? 0,
722
723
  ...(declineReasons ? { reasons: declineReasons } : {}),
723
724
  });
724
- const pr = await deps.seams.openPr({ title, body, head: branch, ...(deps.base ? { base: deps.base } : {}), draft: true, ...(labels ? { labels } : {}), cwd });
725
- // ★ 리뷰 반영(#5338) — 라벨 관측은 openPr 성공 후 PR 번호와 함께(생성 실패 시 거짓 운영 증거 방지).
726
- if (labels) observe('auto-review-labeled', { branch, number: pr.number, label: AUTO_REVIEW_LABEL });
727
- observe('deployed-pr', { number: pr.number, url: pr.url, autoReview: !!labels });
725
+ const pr = await deps.seams.openPr({ title, body, head: branch, ...(deps.base ? { base: deps.base } : {}), draft: true, cwd });
726
+ let holdPath: string | undefined;
727
+ let inspectionError: string | undefined;
728
+ try {
729
+ if (!deps.seams.readPrFiles) throw new Error('readPrFiles seam unavailable');
730
+ const prFiles = await deps.seams.readPrFiles({ number: pr.number, cwd });
731
+ if (!Array.isArray(prFiles) || !prFiles.every((path) => typeof path === 'string')) throw new Error('invalid PR file list');
732
+ holdPath = releasePathHold(prFiles);
733
+ } catch (error) { inspectionError = String(error); }
734
+ if (holdPath) {
735
+ const protectedPath = holdPath;
736
+ const prCwd = cwd;
737
+ debug.log('self-dev.merge', 'release-path-hold', { number: pr.number, path: protectedPath, label: RELEASE_PATH_LABEL });
738
+ for (const [action, annotate] of [
739
+ ['add-label', deps.seams.addPrLabel && (() => deps.seams.addPrLabel!({ number: pr.number, label: RELEASE_PATH_LABEL, cwd: prCwd }))],
740
+ ['comment', deps.seams.postPrComment && (() => deps.seams.postPrComment!({ number: pr.number, body: releasePathHoldComment(protectedPath), cwd: prCwd }))],
741
+ ] as const) {
742
+ if (!annotate) { observe('release-path-annotation-failed', { number: pr.number, action, error: 'annotation seam unavailable' }); continue; }
743
+ try { await annotate(); } catch (error) { observe('release-path-annotation-failed', { number: pr.number, action, error: String(error) }); }
744
+ }
745
+ observe('deployed-pr', { number: pr.number, url: pr.url, autoReview: false, mergeReason: 'release-path-hold' });
746
+ } else if (inspectionError) {
747
+ observe('release-path-inspection-failed', { number: pr.number, error: inspectionError });
748
+ observe('deployed-pr', { number: pr.number, url: pr.url, autoReview: false, mergeReason: 'release-path-inspection-failed' });
749
+ } else {
750
+ let autoReviewLabeled = false;
751
+ if (labels?.includes(AUTO_REVIEW_LABEL)) {
752
+ if (deps.seams.addPrLabel) {
753
+ try {
754
+ await deps.seams.addPrLabel({ number: pr.number, label: AUTO_REVIEW_LABEL, cwd });
755
+ autoReviewLabeled = true;
756
+ observe('auto-review-labeled', { branch, number: pr.number, label: AUTO_REVIEW_LABEL });
757
+ } catch (error) { observe('auto-review-label-failed', { number: pr.number, error: String(error) }); }
758
+ } else observe('auto-review-label-failed', { number: pr.number, error: 'addPrLabel seam unavailable' });
759
+ }
760
+ observe('deployed-pr', { number: pr.number, url: pr.url, autoReview: autoReviewLabeled });
761
+ }
728
762
  // B(대표 2026-07-21): post-PR 자율 리뷰 — 리뷰기가 PR 에 커멘트(fail-soft). 게이트가 놓친 품질 블로커 표면화.
729
763
  await postPrReview(cwd, pr.number, objective, deps.llmReview, deps.spawnSyncFn ?? spawnSync, deps.goalDocument);
730
764
  return { ok: true, ref: pr.url, kind: 'pr' };
@@ -14,6 +14,7 @@ import { isAbsolute, join, resolve } from 'node:path';
14
14
  import { spawnSync } from 'node:child_process';
15
15
  import { debug } from '../debug/log.js';
16
16
  import { selfDevRunsDir } from '../self-dev/run-store.js';
17
+ import { launchBindingRunIds, launchRegistryDir, readLaunchBinding } from './launch-registry.js';
17
18
  import { loadRunLedger, resolveFederatedRunLedgerDirectories, runLedgerDir } from '../self-implement/run-ledger.js';
18
19
 
19
20
  const RUN_ID = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
@@ -173,6 +174,16 @@ export function readPidRecordFrom(runsDir: string, runId: string): PidRecord | n
173
174
  }
174
175
  }
175
176
 
177
+ /** The launching universe's pid.json when the binding names it, else the binding's own pid — both still pass the start-time check. */
178
+ export function pidRecordFromLaunchBinding(runId: string, registryDir = launchRegistryDir()): PidRecord | null {
179
+ const binding = readLaunchBinding(runId, registryDir);
180
+ if (!binding) return null;
181
+ const fromRunsDir = binding.runsDir ? readPidRecordFrom(binding.runsDir, runId) : null;
182
+ if (fromRunsDir) return fromRunsDir;
183
+ debug.log('harness.stop', 'pid-from-launch-binding', { runId, pid: binding.pid });
184
+ return { pid: binding.pid, startedAt: binding.startedAt, ...(binding.argv0 ? { argv0: binding.argv0 } : {}) };
185
+ }
186
+
176
187
  /** macOS·Linux 공통 — `ps -o lstart=` 는 로컬 시각 문자열(초 단위)이다. */
177
188
  export function psProcessStartMs(pid: number): number | null {
178
189
  const r = spawnSync('ps', ['-o', 'lstart=', '-p', String(pid)], { encoding: 'utf8', timeout: 5_000 });
@@ -183,7 +194,8 @@ export function psProcessStartMs(pid: number): number | null {
183
194
  return Number.isFinite(ms) ? ms : null;
184
195
  }
185
196
 
186
- export function defaultHarnessStopDeps(overrides: Partial<HarnessStopDeps> = {}): HarnessStopDeps {
197
+ export function defaultHarnessStopDeps(overrides: Partial<HarnessStopDeps> = {}, opts: { launchRegistryDir?: string } = {}): HarnessStopDeps {
198
+ const registry = opts.launchRegistryDir ?? launchRegistryDir();
187
199
  const env = { ...process.env };
188
200
  for (const key of ['HTTPS_PROXY', 'https_proxy', 'HTTP_PROXY', 'http_proxy', 'ALL_PROXY', 'all_proxy']) delete env[key];
189
201
  const kubectl = (args: readonly string[]) => {
@@ -191,12 +203,19 @@ export function defaultHarnessStopDeps(overrides: Partial<HarnessStopDeps> = {})
191
203
  return { status: r.status, stdout: r.stdout ?? '', stderr: r.stderr ?? String(r.error ?? '') };
192
204
  };
193
205
  return {
194
- readPidRecord: (runId) => readPidRecordFrom(selfDevRunsDir(), runId),
206
+ // This universe first; then the host-wide launch binding, so a run launched from another worktree is still found.
207
+ readPidRecord: (runId) => readPidRecordFrom(selfDevRunsDir(), runId) ?? pidRecordFromLaunchBinding(runId, registry),
195
208
  runIdCandidates: (given) => {
196
209
  const ledgerDirs = new Set([runLedgerDir(), ...resolveFederatedRunLedgerDirectories({})]);
197
- return stopRunIdCandidates(given, [...ledgerDirs], selfDevRunsDir());
210
+ const found = new Set(stopRunIdCandidates(given, [...ledgerDirs], selfDevRunsDir()));
211
+ for (const id of launchBindingRunIds(registry)) if (id.startsWith(given) && FULL_RUN_ID.test(id)) found.add(id);
212
+ return [...found].sort();
213
+ },
214
+ readGoalPath: (runId) => {
215
+ let fromLedger: string | null = null;
216
+ try { fromLedger = stopGoalPathFromLedger(runId); } catch { fromLedger = null; }
217
+ return fromLedger ?? readLaunchBinding(runId, registry)?.goalPath ?? null;
198
218
  },
199
- readGoalPath: stopGoalPathFromLedger,
200
219
  scanProcesses: scanStopProcessTable,
201
220
  processStartMs: psProcessStartMs,
202
221
  kill: (pid, signal) => { process.kill(pid, signal); },
@@ -0,0 +1,91 @@
1
+ /** Host-wide run → process binding for `harness stop`, independent of the launching worktree and its universe.
2
+ * A run launched from another worktree writes `pid.json` and its ledger under that tree's state dir, so a stop from
3
+ * any other tree found «no pid.json · goal path 0» while the launcher was alive (10-03 · 09-29). The binding below lives
4
+ * in a per-user directory under the OS temp dir — it only has to outlive the process it names, not a reboot. */
5
+ import { closeSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
6
+ import { tmpdir, userInfo } from 'node:os';
7
+ import { isAbsolute, join, resolve } from 'node:path';
8
+ import { randomUUID } from 'node:crypto';
9
+ import { debug } from '../debug/log.js';
10
+
11
+ export interface LaunchBinding {
12
+ readonly pid: number;
13
+ /** Same approximation as `pid.json` (`Date.now() − uptime`) — compared with `ps` start time before any signal. */
14
+ readonly startedAt: number;
15
+ readonly argv0?: string;
16
+ /** The launching process's working directory (absolute). */
17
+ readonly cwd: string;
18
+ /** Absolute goal document path when the launcher was given one (`--goal-file`/`--ask`/`--goal`). */
19
+ readonly goalPath?: string;
20
+ /** The `self-dev-runs` directory that holds this run's `pid.json` — tells a stop which universe launched it. */
21
+ readonly runsDir?: string;
22
+ }
23
+
24
+ const RUN_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
25
+
26
+ /** One directory per OS user: `$TMPDIR` is per-user on macOS, `/tmp` is shared on Linux. */
27
+ export function launchRegistryDir(): string {
28
+ let uid: string;
29
+ try { uid = String(userInfo().uid); } catch { uid = 'unknown'; }
30
+ return join(tmpdir(), `elanous-harness-launches-${uid}`);
31
+ }
32
+
33
+ /** The goal document named in an argv, resolved against `cwd` — the same option set `harness stop` already matches. */
34
+ export function goalPathFromArgv(argv: readonly string[], cwd: string): string | undefined {
35
+ for (let i = 0; i < argv.length; i++) {
36
+ const arg = argv[i]!;
37
+ let value: string | undefined;
38
+ for (const flag of ['--goal-file', '--ask', '--goal']) {
39
+ if (arg === flag) value = argv[i + 1];
40
+ else if (arg.startsWith(`${flag}=`)) value = arg.slice(flag.length + 1);
41
+ }
42
+ if (value?.trim()) return isAbsolute(value) ? resolve(value) : resolve(cwd, value);
43
+ }
44
+ return undefined;
45
+ }
46
+
47
+ export function writeLaunchBinding(runId: string, binding: LaunchBinding, dir = launchRegistryDir()): string | null {
48
+ if (!RUN_ID.test(runId)) return null;
49
+ try {
50
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
51
+ const path = join(dir, `${runId}.json`);
52
+ const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
53
+ const fd = openSync(temporary, 'wx', 0o600);
54
+ try { writeFileSync(fd, JSON.stringify(binding)); } finally { closeSync(fd); }
55
+ renameSync(temporary, path);
56
+ debug.log('harness.launch-registry', 'bound', { runId, pid: binding.pid, goalPath: binding.goalPath ?? null });
57
+ return path;
58
+ } catch (error) {
59
+ debug.log('harness.launch-registry', 'bind-failed', { runId, error: String(error) }, { level: 'warn' });
60
+ return null;
61
+ }
62
+ }
63
+
64
+ export function readLaunchBinding(runId: string, dir = launchRegistryDir()): LaunchBinding | null {
65
+ if (!RUN_ID.test(runId)) return null;
66
+ try {
67
+ const value = JSON.parse(readFileSync(join(dir, `${runId}.json`), 'utf8')) as Partial<LaunchBinding>;
68
+ if (!Number.isSafeInteger(value.pid) || (value.pid as number) <= 0 || !Number.isFinite(value.startedAt)) return null;
69
+ if (typeof value.cwd !== 'string' || !isAbsolute(value.cwd)) return null;
70
+ return {
71
+ pid: value.pid as number, startedAt: value.startedAt as number, cwd: value.cwd,
72
+ ...(typeof value.argv0 === 'string' ? { argv0: value.argv0 } : {}),
73
+ ...(typeof value.goalPath === 'string' && isAbsolute(value.goalPath) ? { goalPath: value.goalPath } : {}),
74
+ ...(typeof value.runsDir === 'string' && isAbsolute(value.runsDir) ? { runsDir: value.runsDir } : {}),
75
+ };
76
+ } catch { return null; }
77
+ }
78
+
79
+ export function removeLaunchBinding(runId: string, dir = launchRegistryDir()): void {
80
+ if (!RUN_ID.test(runId)) return;
81
+ try { unlinkSync(join(dir, `${runId}.json`)); }
82
+ catch (error) {
83
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') debug.log('harness.launch-registry', 'unbind-failed', { runId, error: String(error) }, { level: 'warn' });
84
+ }
85
+ }
86
+
87
+ /** Run IDs with a live-looking binding — used for stop's prefix resolution next to ledgers and `pid.json` names. */
88
+ export function launchBindingRunIds(dir = launchRegistryDir()): string[] {
89
+ try { return readdirSync(dir).filter((name) => name.endsWith('.json')).map((name) => name.slice(0, -5)).filter((id) => RUN_ID.test(id)); }
90
+ catch { return []; }
91
+ }
@@ -12,7 +12,9 @@ export interface GarbageProcess extends Pick<GarbageProcessRow, 'pid' | 'ppid' |
12
12
 
13
13
  const TEST_PATH = /elanous|repo\.worktrees|self-impl|\.elanous-test|gate-baseline/i;
14
14
  const TEST_DAEMON = /(?:^|[\s/])daemon\.ts(?:\s|$)/;
15
- const TEST_RUNNER = /\bbun\s+test\b|\bgate(?:[-\s]baseline|\b)/i;
15
+ const TEST_RUNNER = /^(?:\S*\/)?bun\s+test(?:\s|$)/i;
16
+ // Only the command itself counts — `bun [run|x] <…gate…>`; goal text later in argv must not (PROC1).
17
+ const GATE_RUNNER = /^(?:\S*\/)?bun\s+(?:(?:run|x)\s+)?\S*gate(?:[-\s]baseline|\b)/i;
16
18
  const NEXUS_RUN = /\bnexus\s+run\b/;
17
19
  const TEST_MARKER = /\.elanous-test|(?:^|\s)--test(?:\s|$)/;
18
20
 
@@ -20,7 +22,7 @@ const TEST_MARKER = /\.elanous-test|(?:^|\s)--test(?:\s|$)/;
20
22
  export function isGarbageProcessTarget(command: string): boolean {
21
23
  return command.includes('elanous.mjs')
22
24
  || (TEST_DAEMON.test(command) && /elanous-nexus-|\.elanous-test/.test(command))
23
- || (TEST_RUNNER.test(command) && TEST_PATH.test(command));
25
+ || ((TEST_RUNNER.test(command) || GATE_RUNNER.test(command)) && TEST_PATH.test(command));
24
26
  }
25
27
 
26
28
  export function classifyGarbage(
@@ -36,7 +38,7 @@ export function classifyGarbage(
36
38
  const reason: GarbageProcess['reason'] = TEST_DAEMON.test(row.command)
37
39
  && /elanous-nexus-|\.elanous-test/.test(row.command)
38
40
  ? 'orphan-test-daemon'
39
- : TEST_RUNNER.test(row.command) && TEST_PATH.test(row.command)
41
+ : (TEST_RUNNER.test(row.command) || GATE_RUNNER.test(row.command)) && TEST_PATH.test(row.command)
40
42
  ? 'orphan-test-runner'
41
43
  : 'orphan-elanous';
42
44
  garbage.push({ pid: row.pid, ppid: row.ppid, elapsedSeconds: row.elapsedSeconds, command: row.command, reason });
@@ -0,0 +1,153 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, writeSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+
5
+ export const CONSULT_MAX_BYTES = 8 * 1024;
6
+ export const CONSULT_QUEUE_MAX_AGE_MS = 7 * 24 * 3600_000;
7
+
8
+ export interface ConsultInput {
9
+ name: string;
10
+ org?: string;
11
+ kind: 'company' | 'personal';
12
+ interest: 'A' | 'B';
13
+ contact: string;
14
+ consent: true;
15
+ }
16
+
17
+ export interface QueuedConsult { id: string; receivedAt: string; consult: ConsultInput }
18
+
19
+ const singleLine = (value: string): string => value.trim().replace(/[\r\n\u2028\u2029]+/g, ' ');
20
+
21
+ /** The Primary accepts optional org and requires nonblank name/contact, kind, interest and explicit consent. */
22
+ export function validateConsult(body: unknown): { ok: true; consult: ConsultInput } | { ok: false; field: string } {
23
+ if (!body || typeof body !== 'object' || Array.isArray(body)) return { ok: false, field: 'body' };
24
+ const b = body as Record<string, unknown>;
25
+ if (typeof b.name !== 'string' || !b.name.trim() || b.name.length > 256) return { ok: false, field: 'name' };
26
+ if (b.org !== undefined && (typeof b.org !== 'string' || !b.org.trim() || b.org.length > 256)) return { ok: false, field: 'org' };
27
+ if (b.kind !== 'company' && b.kind !== 'personal') return { ok: false, field: 'kind' };
28
+ if (b.interest !== 'A' && b.interest !== 'B') return { ok: false, field: 'interest' };
29
+ if (typeof b.contact !== 'string' || !b.contact.trim() || b.contact.length > 512) return { ok: false, field: 'contact' };
30
+ if (b.consent !== true) return { ok: false, field: 'consent' };
31
+ return { ok: true, consult: { name: singleLine(b.name), ...(typeof b.org === 'string' ? { org: singleLine(b.org) } : {}),
32
+ kind: b.kind, interest: b.interest, contact: b.contact.trim(), consent: true } };
33
+ }
34
+
35
+ /** Sliding windows; neither rejected hits nor invalid submissions consume quota. */
36
+ export class ConsultLimiter {
37
+ private readonly ips = new Map<string, number[]>();
38
+ private daily: number[] = [];
39
+ constructor(private readonly now: () => number = Date.now) {}
40
+
41
+ admit(ip: string): number | null {
42
+ const t = this.now();
43
+ const hits = (this.ips.get(ip) ?? []).filter(x => t - x < 600_000);
44
+ this.daily = this.daily.filter(x => t - x < 86_400_000);
45
+ const wait = hits.length >= 3 ? 600_000 - (t - hits[0]!) :
46
+ this.daily.length >= 200 ? 86_400_000 - (t - this.daily[0]!) : null;
47
+ if (wait !== null) { this.ips.set(ip, hits); return Math.max(1, Math.ceil(wait / 1000)); }
48
+ hits.push(t);
49
+ this.ips.set(ip, hits);
50
+ this.daily.push(t);
51
+ return null;
52
+ }
53
+ }
54
+
55
+ /** Atomic JSONL rewrite on every mutation, with a private file and directory. */
56
+ export class ConsultQueue {
57
+ readonly path: string;
58
+ constructor(root: string, private readonly log: (event: string, data: Record<string, unknown>) => void = () => {}) {
59
+ this.path = join(root, 'hooks', 'consult-queue.jsonl');
60
+ mkdirSync(dirname(this.path), { recursive: true, mode: 0o700 });
61
+ }
62
+
63
+ private read(): QueuedConsult[] {
64
+ try { return readFileSync(this.path, 'utf8').split('\n').filter(Boolean).map(line => JSON.parse(line) as QueuedConsult); }
65
+ catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') return []; throw error; }
66
+ }
67
+
68
+ private write(items: QueuedConsult[]): void {
69
+ const temp = `${this.path}.${randomUUID()}.tmp`;
70
+ try {
71
+ const fd = openSync(temp, 'wx', 0o600);
72
+ try { writeSync(fd, items.map(item => JSON.stringify(item)).join('\n') + (items.length ? '\n' : '')); fsyncSync(fd); }
73
+ finally { closeSync(fd); }
74
+ renameSync(temp, this.path);
75
+ } finally { rmSync(temp, { force: true }); }
76
+ }
77
+
78
+ enqueue(item: QueuedConsult): void { this.write([...this.read(), item]); }
79
+ entries(now: number = Date.now()): QueuedConsult[] {
80
+ const items = this.read();
81
+ const kept = items.filter(item => Number.isFinite(Date.parse(item.receivedAt)) && now - Date.parse(item.receivedAt) <= CONSULT_QUEUE_MAX_AGE_MS);
82
+ if (kept.length !== items.length) { this.write(kept); this.log('dropped', { expired: items.length - kept.length }); }
83
+ return kept;
84
+ }
85
+ count(): number { return existsSync(this.path) ? this.read().length : 0; }
86
+ delivered(id: string): void { this.write(this.read().filter(item => item.id !== id)); }
87
+ }
88
+
89
+ /** Accept only a validated body; responses and logs never contain user-provided values. */
90
+ export async function handleConsultPost(request: Request, deps: {
91
+ limiter: ConsultLimiter; queue: ConsultQueue; now?: () => number; ip?: string;
92
+ log?: (event: string, data: Record<string, unknown>) => void; wake?: () => void;
93
+ }): Promise<Response> {
94
+ const log = deps.log ?? (() => {});
95
+ const declared = Number(request.headers.get('content-length') ?? '0');
96
+ if (declared > CONSULT_MAX_BYTES) { log('rejected', { reason: 'too-large' }); return Response.json({ error: 'too-large' }, { status: 413 }); }
97
+ const chunks: Uint8Array[] = [];
98
+ let size = 0;
99
+ const reader = request.body?.getReader();
100
+ try {
101
+ if (reader) {
102
+ while (true) {
103
+ const { done, value } = await reader.read();
104
+ if (done) break;
105
+ size += value.byteLength;
106
+ if (size > CONSULT_MAX_BYTES) {
107
+ await reader.cancel().catch(() => {});
108
+ log('rejected', { reason: 'too-large' });
109
+ return Response.json({ error: 'too-large' }, { status: 413 });
110
+ }
111
+ chunks.push(value);
112
+ }
113
+ }
114
+ } catch {
115
+ log('rejected', { reason: 'body' });
116
+ return Response.json({ error: 'invalid', field: 'body' }, { status: 400 });
117
+ } finally { reader?.releaseLock(); }
118
+ let body: unknown;
119
+ try {
120
+ const raw = new Uint8Array(size);
121
+ let offset = 0;
122
+ for (const chunk of chunks) { raw.set(chunk, offset); offset += chunk.byteLength; }
123
+ body = JSON.parse(new TextDecoder().decode(raw));
124
+ }
125
+ catch { log('rejected', { reason: 'body' }); return Response.json({ error: 'invalid', field: 'body' }, { status: 400 }); }
126
+ const checked = validateConsult(body);
127
+ if (!checked.ok) { log('rejected', { reason: checked.field }); return Response.json({ error: 'invalid', field: checked.field }, { status: 400 }); }
128
+ const ip = deps.ip ?? 'unknown';
129
+ const wait = deps.limiter.admit(ip);
130
+ if (wait !== null) {
131
+ log('rejected', { reason: 'rate-limited' });
132
+ return Response.json({ error: 'rate-limited', retryAfterSec: wait }, { status: 429, headers: { 'Retry-After': String(wait) } });
133
+ }
134
+ const id = randomUUID();
135
+ try { deps.queue.enqueue({ id, receivedAt: new Date((deps.now ?? Date.now)()).toISOString(), consult: checked.consult }); }
136
+ catch { log('rejected', { reason: 'queue-unavailable' }); return Response.json({ error: 'queue-unavailable' }, { status: 503 }); }
137
+ log('received', { queued: true, kind: checked.consult.kind, interest: checked.consult.interest,
138
+ nameLength: checked.consult.name.length, contactLength: checked.consult.contact.length, orgLength: checked.consult.org?.length ?? 0 });
139
+ deps.wake?.();
140
+ return Response.json({ id }, { status: 202 });
141
+ }
142
+
143
+ /** Forward in arrival order; a failure leaves that item and all following items queued. */
144
+ export async function drainConsults(queue: ConsultQueue, forward: (item: QueuedConsult) => Promise<boolean>, now: number = Date.now()): Promise<{ delivered: number; failed: number }> {
145
+ let delivered = 0;
146
+ let failed = 0;
147
+ for (const item of queue.entries(now)) {
148
+ let ok = false;
149
+ try { ok = await forward(item); } catch { ok = false; }
150
+ if (ok) { queue.delivered(item.id); delivered++; } else { failed++; break; }
151
+ }
152
+ return { delivered, failed };
153
+ }
@@ -0,0 +1,82 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { join } from 'node:path';
3
+ import { debug } from '../debug/log.js';
4
+ import { runSeatLoopOnce } from '../seat-loop/seat-loop.js';
5
+ import { getUserConfig } from '../user-config.js';
6
+ import { openMsgStore } from '../msg/msg-store.js';
7
+ import type { EventsConfig } from '../user-config.js';
8
+ import type { QueuedHook } from './queue.js';
9
+
10
+ export type HookWake = (seat: string) => Promise<unknown>;
11
+
12
+ // A crashed wake can be reclaimed after the lease expires; a live wake renews it until completion.
13
+ const WAKE_LEASE_MS = 120_000;
14
+ const DEFAULT_SEAT_ROUTES = [
15
+ { source: 'linear', kind: 'Issue:update', seat: (event: QueuedHook, config: EventsConfig) =>
16
+ event.task.assigneeId ? config.linearAssignees[event.task.assigneeId] : undefined },
17
+ { source: 'github', kind: 'pull_request:review_requested', seat: 'TC' },
18
+ { source: 'github', kind: 'check_run:completed', seat: 'TC' },
19
+ { source: 'github', kind: 'check_suite:completed', seat: 'TC' },
20
+ ] as const;
21
+
22
+ /** Record a work card and inbox delivery atomically. Wake state: 0 = retryable, 2 = leased, 1 = done, 3 = seat loop off (terminal).
23
+ * `events.mode` decides only whether the card is delivered; the wake runs the seat loop under its own `loops.seat` mode.
24
+ * So a delivered card with the seat loop off is not retried (the card waits in the inbox), and a shadow loop's wake is
25
+ * recorded as `wake_status = 'shadow'` — visible as «not executed», not mistaken for a real run. */
26
+ export async function dispatchHook(event: QueuedHook, root: string, config: EventsConfig,
27
+ wake: HookWake = seat => runSeatLoopOnce(seat, { root, config: getUserConfig().loops?.seat ?? { mode: 'off' } }),
28
+ now: () => number = Date.now): Promise<void> {
29
+ const key = `${event.provider}:${event.eventId}`;
30
+ const route = config.routes.find(candidate => candidate.source === event.provider && candidate.kind === event.kind);
31
+ const defaultSeat = DEFAULT_SEAT_ROUTES.find(candidate => candidate.source === event.provider && candidate.kind === event.kind)?.seat;
32
+ const defaultResolvedSeat = typeof defaultSeat === 'function' ? defaultSeat(event, config) : defaultSeat;
33
+ if (event.provider === 'linear' && event.kind === 'Issue:update' && !route && !defaultResolvedSeat)
34
+ throw new Error('Linear issue update assignee is not mapped to a seat');
35
+ const seat = route?.seat ?? defaultResolvedSeat ?? 'OP';
36
+ const store = openMsgStore(join(root, 'msg', 'messages.db'));
37
+ const claim = randomUUID();
38
+ try {
39
+ store.db.exec(`CREATE TABLE IF NOT EXISTS hook_work_cards (
40
+ event_key TEXT PRIMARY KEY, source TEXT NOT NULL, kind TEXT NOT NULL,
41
+ seat TEXT NOT NULL, loop TEXT NOT NULL, mode TEXT NOT NULL, title TEXT NOT NULL,
42
+ message_id INTEGER, woken INTEGER NOT NULL DEFAULT 0,
43
+ wake_claim TEXT, wake_lease_until INTEGER
44
+ )`);
45
+ const columns = store.db.query('PRAGMA table_info(hook_work_cards)').all() as Array<{ name: string }>;
46
+ if (!columns.some(column => column.name === 'wake_claim')) store.db.exec('ALTER TABLE hook_work_cards ADD COLUMN wake_claim TEXT');
47
+ if (!columns.some(column => column.name === 'wake_lease_until')) store.db.exec('ALTER TABLE hook_work_cards ADD COLUMN wake_lease_until INTEGER');
48
+ if (!columns.some(column => column.name === 'wake_status')) store.db.exec('ALTER TABLE hook_work_cards ADD COLUMN wake_status TEXT');
49
+ store.db.transaction(() => {
50
+ const existing = store.db.query('SELECT event_key FROM hook_work_cards WHERE event_key = ?').get(key);
51
+ if (existing) return;
52
+ const live = config.mode === 'on';
53
+ let messageId: number | null = null;
54
+ if (live) {
55
+ const body = `[작업 카드 · ${key} · ${event.kind ?? 'unknown'} · loop: seat] ${event.task.title}\n${event.task.external.url ?? event.task.external.ref}`;
56
+ messageId = store.append({ from: 'HOOKS', to: seat, kind: 'hook-task', body }).id;
57
+ }
58
+ store.db.query(`INSERT INTO hook_work_cards(event_key, source, kind, seat, loop, mode, title, message_id)
59
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(key, event.provider, event.kind ?? 'unknown', seat, route?.loop ?? 'seat', live ? 'on' : 'shadow', event.task.title, messageId);
60
+ })();
61
+ const leaseAt = now();
62
+ const acquired = store.db.query(`UPDATE hook_work_cards SET woken = 2, wake_claim = ?, wake_lease_until = ?
63
+ WHERE event_key = ? AND message_id IS NOT NULL
64
+ AND (woken = 0 OR (woken = 2 AND (wake_lease_until IS NULL OR wake_lease_until <= ?)))`)
65
+ .run(claim, leaseAt + WAKE_LEASE_MS, key, leaseAt).changes === 1;
66
+ if (!acquired) return;
67
+ const renew = () => store.db.query(`UPDATE hook_work_cards SET wake_lease_until = ?
68
+ WHERE event_key = ? AND woken = 2 AND wake_claim = ?`).run(now() + WAKE_LEASE_MS, key, claim);
69
+ const heartbeat = setInterval(renew, WAKE_LEASE_MS / 4);
70
+ try {
71
+ const result = await wake(seat);
72
+ const status = result && typeof result === 'object' && 'status' in result && typeof result.status === 'string' ? result.status : 'woken';
73
+ // Seat loop off is a policy, not a transient failure: keep the card in the inbox and stop retrying.
74
+ const final = status === 'skipped-off' ? 3 : 1;
75
+ store.db.query('UPDATE hook_work_cards SET woken = ?, wake_status = ?, wake_claim = NULL, wake_lease_until = NULL WHERE event_key = ? AND woken = 2 AND wake_claim = ?').run(final, status, key, claim);
76
+ debug.log('hooks.dispatch', 'wake', { key, seat, status, retry: false });
77
+ } catch (error) {
78
+ store.db.query('UPDATE hook_work_cards SET woken = 0, wake_claim = NULL, wake_lease_until = NULL WHERE event_key = ? AND woken = 2 AND wake_claim = ?').run(key, claim);
79
+ throw error;
80
+ } finally { clearInterval(heartbeat); }
81
+ } finally { store.close(); }
82
+ }