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
@@ -0,0 +1,85 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { listRunLedgers } from '../self-implement/run-ledger.js';
3
+ import { runGitCommand } from '../git-fs/runner.js';
4
+ import type { PodLeasePredecessor } from './admission.js';
5
+
6
+ /** merged = admit · waiting = keep queued (open, unknown, unreadable) · blocked = predecessor closed without merging. */
7
+ export type PredecessorState = 'merged' | 'waiting' | 'blocked';
8
+
9
+ type PrState = string | null;
10
+
11
+ type DependencyStateDeps = {
12
+ prState?: (number: number, signal?: AbortSignal) => PrState | Promise<PrState>;
13
+ ledgers?: () => ReturnType<typeof listRunLedgers>['matches'];
14
+ };
15
+
16
+ function githubRepository(): string | null {
17
+ const remote = runGitCommand(process.cwd(), ['config', '--get', 'remote.origin.url'], { encoding: 'utf8', timeout: 5_000 });
18
+ const repository = /^(?:https:\/\/github\.com\/|git@github\.com:|ssh:\/\/git@github\.com\/)([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+?)(?:\.git)?\/?$/.exec(remote.stdout.trim())?.[1];
19
+ return remote.status !== 0 || !repository || repository.includes('..') ? null : repository;
20
+ }
21
+
22
+ /** Async so a slow GitHub lookup never blocks the event loop (admission, cancellation, other launches). */
23
+ function githubPrState(number: number, signal?: AbortSignal): Promise<PrState> {
24
+ const repository = githubRepository();
25
+ if (!repository) return Promise.resolve(null);
26
+ return new Promise((resolve) => {
27
+ execFile('gh', ['pr', 'view', String(number), '--repo', repository, '--json', 'state'], {
28
+ encoding: 'utf8', timeout: 15_000, env: process.env, ...(signal ? { signal } : {}),
29
+ }, (error, stdout) => {
30
+ if (error) return resolve(null);
31
+ try {
32
+ const response: unknown = JSON.parse(stdout);
33
+ resolve(response && typeof response === 'object' && 'state' in response && typeof response.state === 'string' ? response.state : null);
34
+ } catch { resolve(null); }
35
+ });
36
+ });
37
+ }
38
+
39
+ const prNumberOf = (after: PodLeasePredecessor): number | null => {
40
+ const number = typeof after === 'number' ? after : Number(after.replace(/^#/, ''));
41
+ return Number.isSafeInteger(number) && number > 0 ? number : null;
42
+ };
43
+
44
+ async function readState(number: number, deps: DependencyStateDeps, signal?: AbortSignal): Promise<PrState> {
45
+ try { return await (deps.prState ?? githubPrState)(number, signal); } catch { return null; }
46
+ }
47
+
48
+ /** Only a verified merged predecessor grants a Pod lease; a predecessor closed without merging blocks; everything else waits. */
49
+ export async function predecessorState(after: PodLeasePredecessor, deps: DependencyStateDeps = {}, signal?: AbortSignal): Promise<PredecessorState> {
50
+ const isPr = typeof after === 'number' || (typeof after === 'string' && !/^[a-f0-9]{16}$/.test(after) && /^#?[1-9]\d*$/.test(after));
51
+ if (isPr) {
52
+ const number = prNumberOf(after);
53
+ if (number === null) return 'waiting';
54
+ const state = await readState(number, deps, signal);
55
+ return state === 'MERGED' ? 'merged' : state === 'CLOSED' ? 'blocked' : 'waiting';
56
+ }
57
+ if (typeof after !== 'string' || !after.trim()) return 'waiting';
58
+ let numbers: Set<number>;
59
+ try {
60
+ const matches = (deps.ledgers ?? (() => listRunLedgers().matches))()
61
+ .filter(({ entries }) => entries.some((entry) => entry.goalId === after));
62
+ numbers = new Set<number>();
63
+ for (const { entries } of matches) {
64
+ for (const entry of entries) {
65
+ if (entry.event !== 'pr-opened' || entry.goalId !== after) continue;
66
+ const number = entry.data.number;
67
+ if (typeof number === 'number' && Number.isSafeInteger(number) && number > 0) numbers.add(number);
68
+ }
69
+ }
70
+ } catch { return 'waiting'; }
71
+ if (numbers.size === 0) return 'waiting';
72
+ // A later attempt or manual merge can land an opened PR without an in-run merged event.
73
+ let allClosed = true;
74
+ for (const number of numbers) {
75
+ const state = await readState(number, deps, signal);
76
+ if (state === 'MERGED') return 'merged';
77
+ if (state !== 'CLOSED') allClosed = false;
78
+ }
79
+ return allClosed ? 'blocked' : 'waiting';
80
+ }
81
+
82
+ /** Back-compat boolean view: true only when merged. */
83
+ export async function predecessorMerged(after: PodLeasePredecessor, deps: DependencyStateDeps = {}, signal?: AbortSignal): Promise<boolean> {
84
+ return (await predecessorState(after, deps, signal)) === 'merged';
85
+ }
@@ -0,0 +1,127 @@
1
+ // L7b — cross-process Pod admission on one host. Each admitted launch holds a lease file (pid ⊕ process start time)
2
+ // in a per-user directory; a launch is admitted only while live leases stay under the pool's measured total.
3
+ // Without this, independent CLI processes each measured an empty pool and all launched (review round 2 of #23212).
4
+ import { spawnSync } from 'node:child_process';
5
+ import { mkdirSync, readdirSync, readFileSync, rmSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
6
+ import { tmpdir, userInfo } from 'node:os';
7
+ import { join } from 'node:path';
8
+ import { debug } from '../debug/log.js';
9
+
10
+ export interface HostLeaseRecord { pid: number; startedAt: string; at: string }
11
+
12
+ export interface HostPoolLeaseOptions {
13
+ /** Base directory — default `$TMPDIR/elanous-pod-leases-<uid>` (0700). */
14
+ dir?: string;
15
+ pid?: number;
16
+ /** `ps -o lstart=` text for a pid, or null when the process is gone. */
17
+ processStart?: (pid: number) => string | null;
18
+ now?: () => number;
19
+ /** A lock older than this is a crash leftover and is cleared. */
20
+ staleLockMs?: number;
21
+ }
22
+
23
+ export function hostLeaseBaseDir(env: NodeJS.ProcessEnv = process.env): string {
24
+ if (env.ELANOUS_POD_LEASE_DIR?.trim()) return env.ELANOUS_POD_LEASE_DIR.trim();
25
+ // Test runs get a per-process directory so parallel test files never share real host leases.
26
+ if (env.NODE_ENV === 'test') return join(tmpdir(), `elanous-pod-leases-test-${process.pid}`);
27
+ let uid: string;
28
+ try { uid = String(userInfo().uid); } catch { uid = 'unknown'; }
29
+ return join(tmpdir(), `elanous-pod-leases-${uid}`);
30
+ }
31
+
32
+ /** Process start time as `ps` prints it (C locale so the text is stable). Null = no such process. */
33
+ export function psProcessStart(pid: number): string | null {
34
+ if (!Number.isSafeInteger(pid) || pid <= 0) return null;
35
+ const result = spawnSync('ps', ['-o', 'lstart=', '-p', String(pid)], { encoding: 'utf8', env: { ...process.env, LC_ALL: 'C', LANG: 'C' } });
36
+ const text = result.status === 0 ? result.stdout.trim() : '';
37
+ return text || null;
38
+ }
39
+
40
+ export class HostPoolLease {
41
+ readonly dir: string;
42
+ private readonly pid: number;
43
+ private readonly processStart: (pid: number) => string | null;
44
+ private readonly now: () => number;
45
+ private readonly staleLockMs: number;
46
+ private ownStart: string | null | undefined;
47
+
48
+ constructor(poolKey: string, options: HostPoolLeaseOptions = {}) {
49
+ this.dir = join(options.dir ?? hostLeaseBaseDir(), poolKey.replace(/[^A-Za-z0-9._-]/g, '_').slice(0, 120) || 'pool');
50
+ this.pid = options.pid ?? process.pid;
51
+ this.processStart = options.processStart ?? psProcessStart;
52
+ this.now = options.now ?? Date.now;
53
+ this.staleLockMs = options.staleLockMs ?? 10_000;
54
+ }
55
+
56
+ /** Live leases after removing stale ones (dead pid or a recycled pid whose start time differs). */
57
+ live(): HostLeaseRecord[] {
58
+ const out: HostLeaseRecord[] = [];
59
+ let names: string[] = [];
60
+ try { names = readdirSync(this.dir).filter((name) => name.endsWith('.json')); } catch { return out; }
61
+ for (const name of names) {
62
+ const file = join(this.dir, name);
63
+ let record: HostLeaseRecord | null = null;
64
+ try { record = JSON.parse(readFileSync(file, 'utf8')) as HostLeaseRecord; } catch { record = null; }
65
+ const alive = record && this.processStart(record.pid) === record.startedAt;
66
+ if (record && alive) { out.push(record); continue; }
67
+ try { unlinkSync(file); } catch { /* another process cleaned it */ }
68
+ debug.log('pod.lease', 'host-lease-stale-cleared', { file: name, pid: record?.pid ?? null });
69
+ }
70
+ return out;
71
+ }
72
+
73
+ /** Unapplied leases held by other processes — launches admitted elsewhere whose Job the cluster can't see yet. */
74
+ othersPending(): number {
75
+ if (this.ownStart === undefined) this.ownStart = this.processStart(this.pid);
76
+ return this.live().filter((r) => !(r.pid === this.pid && r.startedAt === this.ownStart)).length;
77
+ }
78
+
79
+ /**
80
+ * Under the host lock, take a lease only if `allowed(othersPending)` holds — the check and the write are one step,
81
+ * so independent processes measuring the same empty pool cannot all pass. Null = refused or lock busy (retry later).
82
+ */
83
+ tryReserve(allowed: (othersPending: number) => boolean): (() => void) | null {
84
+ try { mkdirSync(this.dir, { recursive: true, mode: 0o700 }); } catch { return null; }
85
+ if (!this.lock()) return null;
86
+ try {
87
+ if (this.ownStart === undefined) this.ownStart = this.processStart(this.pid);
88
+ if (!this.ownStart) return null;
89
+ const others = this.live().filter((r) => !(r.pid === this.pid && r.startedAt === this.ownStart)).length;
90
+ if (!allowed(others)) {
91
+ debug.log('pod.lease', 'host-lease-refused', { othersPending: others });
92
+ return null;
93
+ }
94
+ const file = join(this.dir, `${this.pid}-${this.now()}-${Math.random().toString(36).slice(2, 8)}.json`);
95
+ const record: HostLeaseRecord = { pid: this.pid, startedAt: this.ownStart, at: new Date(this.now()).toISOString() };
96
+ writeFileSync(file, JSON.stringify(record), { flag: 'wx', mode: 0o600 });
97
+ debug.log('pod.lease', 'host-lease-acquired', { othersPending: others });
98
+ let released = false;
99
+ return () => {
100
+ if (released) return;
101
+ released = true;
102
+ try { unlinkSync(file); } catch { /* already cleaned */ }
103
+ };
104
+ } finally {
105
+ this.unlock();
106
+ }
107
+ }
108
+
109
+ private lockDir(): string { return join(this.dir, '.lock'); }
110
+
111
+ private lock(): boolean {
112
+ const dir = this.lockDir();
113
+ try { mkdirSync(dir); return true; } catch { /* held */ }
114
+ try {
115
+ if (this.now() - statSync(dir).mtimeMs > this.staleLockMs) {
116
+ rmSync(dir, { recursive: true, force: true });
117
+ mkdirSync(dir);
118
+ return true;
119
+ }
120
+ } catch { /* raced */ }
121
+ return false;
122
+ }
123
+
124
+ private unlock(): void {
125
+ try { rmSync(this.lockDir(), { recursive: true, force: true }); } catch { /* gone */ }
126
+ }
127
+ }
@@ -0,0 +1,16 @@
1
+ // 작업 폴더 → 프로젝트 제안을 «폴더가 바뀔 때마다» 다시 계산한다(IA1 리뷰 must-fix · 시작 시 process.cwd() 한 번만 보던 것).
2
+ // 같은 프로젝트를 연달아 다시 알리지 않는다 — 바뀐 경우에만 돌려준다.
3
+ import type { Project } from './project-store.js';
4
+
5
+ export function createProjectSuggester(suggest: (folder: string) => Project | null): (folder: string) => Project | null {
6
+ let lastId: string | null = null;
7
+ return (folder) => {
8
+ let project: Project | null;
9
+ try { project = suggest(folder); }
10
+ catch { return null; } // 제안은 대화를 끊지 않는다
11
+ const id = project?.id ?? null;
12
+ if (id === lastId) return null;
13
+ lastId = id;
14
+ return project;
15
+ };
16
+ }
@@ -0,0 +1,24 @@
1
+ import { readFileSync, statSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { getProject } from './project-store.js';
4
+ import { loadSession } from '../session/index.js';
5
+
6
+ /** Resolve the assigned project's folder for a persisted conversation, never from client-supplied paths. */
7
+ export function resolveSessionProjectContext(sessionId: string | undefined): { cwd: string; instructions?: string } | null {
8
+ if (!sessionId) return null;
9
+ const projectId = loadSession(sessionId)?.meta.projectId;
10
+ if (!projectId) return null;
11
+ const folder = getProject(projectId)?.primaryFolder;
12
+ if (!folder) return null;
13
+ try {
14
+ if (!statSync(folder).isDirectory()) return null;
15
+ } catch { return null; }
16
+ const file = join(folder, 'AGENTS.md');
17
+ try {
18
+ const instructions = readFileSync(file, 'utf8');
19
+ return { cwd: folder, ...(instructions.trim() ? { instructions: `Project instructions (${file}):\n${instructions}` } : {}) };
20
+ } catch (error) {
21
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
22
+ return { cwd: folder };
23
+ }
24
+ }
@@ -29,6 +29,7 @@ export interface ChecklistHistory {
29
29
  released: string;
30
30
  dev: string;
31
31
  force?: true;
32
+ reason?: string;
32
33
  }
33
34
  export interface Checklist {
34
35
  version: string;
@@ -68,8 +69,13 @@ function decodeClaimHistory(data: Checklist): Checklist {
68
69
  return { ...data, history: data.history.map(decodeClaimHistoryEntry) };
69
70
  }
70
71
 
71
- export function listChecklist(v: string): Checklist {
72
- return decodeClaimHistory(store.list(v));
72
+ export function listChecklist(v: string, root?: string): Checklist {
73
+ return decodeClaimHistory(root === undefined ? store.list(v) : store.list(v, undefined, undefined, root));
74
+ }
75
+
76
+ /** Events for this id across every release, ordered by event time (then ledger sequence). */
77
+ export function checklistHistory(id: string): Array<ChecklistHistory & { version: string; seq: number }> {
78
+ return store.history(id).map(decodeClaimHistoryEntry);
73
79
  }
74
80
 
75
81
  /** Status events in the requested window; other ledger fields never become status changes. */
@@ -78,7 +84,7 @@ export function statusChangesSince(history: readonly ChecklistHistory[], sinceIs
78
84
  return history.filter((entry) => entry.field === 'status' && Date.parse(entry.at) >= since);
79
85
  }
80
86
 
81
- function mutate(v: string, apply: (data: Checklist) => boolean): Checklist {
87
+ function mutate(v: string, apply: (data: Checklist, otherItems: (id: string) => store.ChecklistCollision[]) => boolean): Checklist {
82
88
  return decodeClaimHistory(store.mutate(v, store.releasedVersion(), devVersion(), apply));
83
89
  }
84
90
 
@@ -87,13 +93,19 @@ function change(data: Checklist, id: string, field: string, from: unknown, to: u
87
93
  debug.log('release-loop.checklist', 'change', { version: data.version, id, field, from: from ?? null, to: to ?? null, by });
88
94
  }
89
95
 
90
- export function addItem(v: string, input: { id: string; title: string; owner?: string; kind?: ChecklistKind }): Checklist {
96
+ export function addItem(v: string, input: { id: string; title: string; owner?: string; kind?: ChecklistKind }, options: { allowDuplicateId?: boolean } = {}): Checklist {
91
97
  if (!input.id.trim()) throw new CliUserError('칸 id 가 비었다');
92
98
  if (!input.title.trim()) throw new CliUserError('칸 제목이 비었다');
93
99
  if (input.kind !== undefined && input.kind !== 'screen') throw new CliUserError(`잘못된 종류: ${input.kind}`, 'screen');
94
100
  if (input.owner !== undefined) parseOwner(input.owner);
95
- return mutate(v, (data) => {
101
+ return mutate(v, (data, otherItems) => {
96
102
  if (data.items.some((item) => item.id === input.id)) throw new CliUserError(`이미 있는 칸: ${input.id}`, 'set <id> 로 고친다');
103
+ const collisions = otherItems(input.id);
104
+ if (collisions.length) {
105
+ const displayedId = JSON.stringify(input.id).slice(1, -1);
106
+ console.error(`⚠ 중복 id: ${displayedId} — ${collisions.map((item) => `${item.version} · 담당 ${JSON.stringify(item.owner ?? '-').slice(1, -1)} · ${item.title.replace(/\s+/g, ' ').slice(0, 40)}`).join(' / ')}`);
107
+ if (!options.allowDuplicateId) throw new CliUserError(`다른 판에 이미 있는 칸: ${displayedId}`, '--allow-duplicate-id 로 허용');
108
+ }
97
109
  const by = process.env.ELANOUS_TRACK || 'cli';
98
110
  const at = new Date().toISOString();
99
111
  const item: ChecklistItem = { id: input.id, title: input.title, status: 'yellow', ...(input.owner !== undefined ? { owner: input.owner } : {}), ...(input.kind !== undefined ? { kind: input.kind } : {}), updatedAt: at, updatedBy: by };
@@ -50,12 +50,13 @@ function open(root = releaseLedgerRoot()): Database {
50
50
  db.exec('PRAGMA foreign_keys = ON');
51
51
  if (!db.query("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'imported_versions'").get()) db.exec(`CREATE TABLE IF NOT EXISTS features (id TEXT PRIMARY KEY, title TEXT NOT NULL, owner TEXT, kind TEXT, created_at TEXT NOT NULL);
52
52
  CREATE TABLE IF NOT EXISTS assignments (feature_id TEXT NOT NULL REFERENCES features(id), version TEXT NOT NULL, status TEXT NOT NULL, disposition TEXT, evidence TEXT, title_override TEXT, owner TEXT, kind TEXT, updated_at TEXT NOT NULL, updated_by TEXT NOT NULL, PRIMARY KEY(feature_id, version));
53
- CREATE TABLE IF NOT EXISTS events (seq INTEGER PRIMARY KEY AUTOINCREMENT, at TEXT NOT NULL, by TEXT NOT NULL, feature_id TEXT NOT NULL, version TEXT NOT NULL, field TEXT NOT NULL, "from" TEXT, "to" TEXT, released TEXT NOT NULL, dev TEXT NOT NULL);
53
+ CREATE TABLE IF NOT EXISTS events (seq INTEGER PRIMARY KEY AUTOINCREMENT, at TEXT NOT NULL, by TEXT NOT NULL, feature_id TEXT NOT NULL, version TEXT NOT NULL, field TEXT NOT NULL, "from" TEXT, "to" TEXT, released TEXT NOT NULL, dev TEXT NOT NULL, reason TEXT);
54
54
  CREATE TABLE IF NOT EXISTS evidence (feature_id TEXT NOT NULL, version TEXT NOT NULL, ref TEXT NOT NULL, at TEXT NOT NULL, by TEXT NOT NULL);
55
55
  CREATE TABLE IF NOT EXISTS imported_versions (version TEXT PRIMARY KEY, json_hash TEXT, imported_at TEXT);`);
56
56
  const columns = db.query('PRAGMA table_info(imported_versions)').all() as Array<{ name: string }>;
57
57
  if (!columns.some((column) => column.name === 'json_hash')) db.exec('ALTER TABLE imported_versions ADD COLUMN json_hash TEXT');
58
58
  if (!columns.some((column) => column.name === 'imported_at')) db.exec('ALTER TABLE imported_versions ADD COLUMN imported_at TEXT');
59
+ if (!(db.query('PRAGMA table_info(events)').all() as Array<{ name: string }>).some((column) => column.name === 'reason')) db.exec('ALTER TABLE events ADD COLUMN reason TEXT');
59
60
  db.exec('CREATE TABLE IF NOT EXISTS release_schedules (version TEXT PRIMARY KEY, cut_at TEXT NOT NULL, land_by TEXT, updated_at TEXT NOT NULL, updated_by TEXT NOT NULL)');
60
61
  return db;
61
62
  } catch (error) { db.close(); throw error; }
@@ -119,14 +120,15 @@ function readTransaction<T>(db: Database, work: () => T): T {
119
120
  catch (error) { db.exec('ROLLBACK'); throw error; }
120
121
  }
121
122
 
122
- type EventRow = { seq: number; at: string; by: string; feature_id: string; version: string; field: string; from: string; to: string; released: string; dev: string };
123
+ type EventRow = { seq: number; at: string; by: string; feature_id: string; version: string; field: string; from: string; to: string; released: string; dev: string; reason: string | null };
123
124
  function decode(value: string | null): unknown { return value === null ? null : JSON.parse(value); }
124
125
  function historyRow(row: EventRow): ChecklistHistory {
125
- return { at: row.at, by: row.by, id: row.feature_id, field: row.field, from: decode(row.from), to: decode(row.to), released: row.released, dev: row.dev };
126
+ return { at: row.at, by: row.by, id: row.feature_id, field: row.field, from: decode(row.from), to: decode(row.to), released: row.released, dev: row.dev,
127
+ ...(row.reason !== null ? { reason: row.reason } : {}) };
126
128
  }
127
129
  function record(db: Database, version: string, entry: ChecklistHistory): void {
128
- db.query('INSERT INTO events (at, by, feature_id, version, field, "from", "to", released, dev) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)')
129
- .run(entry.at, entry.by, entry.id, version, entry.field, JSON.stringify(entry.from ?? null), JSON.stringify(entry.to ?? null), entry.released, entry.dev);
130
+ db.query('INSERT INTO events (at, by, feature_id, version, field, "from", "to", released, dev, reason) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)')
131
+ .run(entry.at, entry.by, entry.id, version, entry.field, JSON.stringify(entry.from ?? null), JSON.stringify(entry.to ?? null), entry.released, entry.dev, entry.reason ?? null);
130
132
  }
131
133
  function evidenceRefs(db: Database, version: string, id: string): string[] {
132
134
  return (db.query('SELECT ref FROM evidence WHERE feature_id = ? AND version = ? ORDER BY rowid').all(id, version) as Array<{ ref: string }>).map((row) => row.ref);
@@ -278,15 +280,19 @@ function importLegacyAll(): void {
278
280
  } finally { db.close(); }
279
281
  }
280
282
 
281
- export function list(version: string, released = releasedVersion(), dev = devVersion()): Checklist {
283
+ /** `root` reads another ledger (no legacy JSON import there); omitted = the default ledger as before. */
284
+ export function list(version: string, released?: string, dev = devVersion(), root?: string): Checklist {
282
285
  validateVersion(version);
283
- importJson(version);
284
- const db = open();
286
+ if (root === undefined) importJson(version);
287
+ released ??= releasedVersion(root);
288
+ const db = open(root);
285
289
  try { return readTransaction(db, () => snapshot(db, version, released, dev)); } finally { db.close(); }
286
290
  }
287
291
 
288
292
  /** The checklist API supplies its existing validation, field ordering and legacy history shape. */
289
- export function mutate(version: string, released: string, dev: string, apply: (data: Checklist) => boolean): Checklist {
293
+ export interface ChecklistCollision { version: string; owner?: string; title: string }
294
+
295
+ export function mutate(version: string, released: string, dev: string, apply: (data: Checklist, otherItems: (id: string) => ChecklistCollision[]) => boolean): Checklist {
290
296
  validateVersion(version);
291
297
  const db = open();
292
298
  try {
@@ -296,7 +302,16 @@ export function mutate(version: string, released: string, dev: string, apply: (d
296
302
  const data = snapshot(db, version, released, dev);
297
303
  const before = new Map(data.items.map((item) => [item.id, JSON.stringify(item)]));
298
304
  const count = data.history.length;
299
- if (apply(data)) {
305
+ if (apply(data, (id) => {
306
+ for (const other of legacyVersions()) if (other !== version) importOnDb(db, other);
307
+ return db.query(`SELECT a.version, a.owner, COALESCE(a.title_override, f.title) AS title
308
+ FROM assignments a JOIN features f ON f.id = a.feature_id
309
+ WHERE a.feature_id = ? AND a.version <> ? ORDER BY a.version`).all(id, version)
310
+ .map((row) => {
311
+ const item = row as { version: string; owner: string | null; title: string };
312
+ return { version: item.version, ...(item.owner !== null ? { owner: item.owner } : {}), title: item.title };
313
+ });
314
+ })) {
300
315
  for (const item of data.items) if (before.get(item.id) !== JSON.stringify(item)) {
301
316
  const previous = before.get(item.id);
302
317
  const evidenceChanged = previous && (JSON.parse(previous) as ChecklistItem).evidence !== item.evidence;
@@ -354,7 +369,7 @@ export function remove(version: string, id: string, by: string, released = relea
354
369
  return true;
355
370
  });
356
371
  }
357
- export function move(id: string, from: string, to: string, by: string, released = releasedVersion(), dev = devVersion()): Checklist {
372
+ export function move(id: string, from: string, to: string, by: string, released = releasedVersion(), dev = devVersion(), reason?: string): Checklist {
358
373
  validateVersion(from); validateVersion(to);
359
374
  if (from === to) throw new CliUserError('같은 판으로 옮길 수 없다');
360
375
  const db = open();
@@ -364,11 +379,12 @@ export function move(id: string, from: string, to: string, by: string, released
364
379
  importedFrom = importOnDb(db, from); importedTo = importOnDb(db, to);
365
380
  const item = items(db, from).find((i) => i.id === id);
366
381
  if (!item) throw new CliUserError(`없는 칸: ${id}`, 'list 로 칸 목록을 본다');
367
- if (db.query('SELECT 1 FROM assignments WHERE feature_id = ? AND version = ?').get(id, to)) throw new CliUserError(`이미 있는 칸: ${id}`);
382
+ const collision = items(db, to).find((i) => i.id === id);
383
+ if (collision) throw new CliUserError(`이미 있는 칸: ${JSON.stringify(id).slice(1, -1)} — ${to} · 담당 ${JSON.stringify(collision.owner ?? '-').slice(1, -1)} · ${collision.title.replace(/\s+/g, ' ').slice(0, 40)}`);
368
384
  const at = new Date().toISOString();
369
385
  db.query('UPDATE assignments SET version = ?, updated_at = ?, updated_by = ? WHERE feature_id = ? AND version = ?').run(to, at, by, id, from);
370
386
  db.query('UPDATE evidence SET version = ? WHERE feature_id = ? AND version = ?').run(to, id, from);
371
- record(db, to, { at, by, id, field: 'move', from, to, released, dev });
387
+ record(db, to, { at, by, id, field: 'move', from, to, released, dev, ...(reason !== undefined ? { reason } : {}) });
372
388
  return snapshot(db, to, released, dev);
373
389
  });
374
390
  if (importedFrom) debug.log('release.features', 'imported', { version: from });
@@ -0,0 +1,48 @@
1
+ import { ownerMatches, type ChecklistItem } from './checklist.js';
2
+
3
+ export interface MergedChecklistPr { number: number; title: string; body: string | null; mergedAt: string }
4
+ type PrMatchBasis = 'cell-line' | 'title-strong' | 'mention';
5
+ export interface LandedButYellowRow {
6
+ id: string;
7
+ owner: string | undefined;
8
+ status: 'yellow' | 'red';
9
+ prs: Array<Pick<MergedChecklistPr, 'number' | 'title' | 'mergedAt'> & { basis: PrMatchBasis }>;
10
+ alreadyInEvidence: boolean[];
11
+ newer: boolean;
12
+ }
13
+
14
+ /** Prefer a dedicated body cell line over title evidence; incidental mentions only qualify for long ids. */
15
+ export function landedButYellow(items: readonly ChecklistItem[], prs: readonly MergedChecklistPr[], { owner }: { owner?: string } = {}): LandedButYellowRow[] {
16
+ const rows: LandedButYellowRow[] = [];
17
+ for (const item of items) {
18
+ if (item.status !== 'yellow' && item.status !== 'red') continue;
19
+ if (owner !== undefined && !ownerMatches(item.owner, owner)) continue;
20
+ const id = item.id.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
21
+ const word = new RegExp(`(?<![A-Za-z0-9])${id}(?![A-Za-z0-9])`);
22
+ const shortId = /^[A-Z]{1,2}\d{1,2}[a-z]?$/.test(item.id);
23
+ const titleStart = shortId
24
+ ? new RegExp(`^${id}(?:$|\\s+(?:수확|(?:첫|둘째|셋째)\\s*조각|fixed|new|old)(?=\\s|:|$))`)
25
+ : new RegExp(`^${id}(?:\\s|$)`);
26
+ const explicitTitle = new RegExp(`(?<![A-Za-z0-9])${id}\\s+(?:수확|(?:첫|둘째|셋째)\\s*조각)(?![A-Za-z0-9가-힣])|\\(${id}\\)`);
27
+ const rank: Record<PrMatchBasis, number> = { 'cell-line': 0, 'title-strong': 1, mention: 2 };
28
+ const matched = prs.flatMap((pr) => {
29
+ const cellLine = (pr.body ?? '').split('\n').some((line) => {
30
+ const match = /^[ \t]*칸(?:[ \t]*:[ \t]*|[ \t]+)([A-Za-z0-9_-]+(?:[ \t]*,[ \t]*[A-Za-z0-9_-]+)*)[ \t]*$/.exec(line.endsWith('\r') ? line.slice(0, -1) : line);
31
+ return match?.[1]?.split(',').some((candidate) => candidate.trim() === item.id) ?? false;
32
+ });
33
+ const basis: PrMatchBasis | undefined = cellLine ? 'cell-line'
34
+ : titleStart.test(pr.title) || explicitTitle.test(pr.title) ? 'title-strong'
35
+ : !shortId && (word.test(pr.title) || word.test(pr.body ?? '')) ? 'mention' : undefined;
36
+ return basis ? [{ number: pr.number, title: pr.title, mergedAt: pr.mergedAt, basis }] : [];
37
+ }).sort((a, b) => rank[a.basis] - rank[b.basis] || b.mergedAt.localeCompare(a.mergedAt) || b.number - a.number);
38
+ if (!matched.length) continue;
39
+ const shown = matched.slice(0, 5);
40
+ rows.push({
41
+ id: item.id, owner: item.owner, status: item.status,
42
+ prs: shown,
43
+ alreadyInEvidence: shown.map(({ number }) => new RegExp(`(?<![A-Za-z0-9_#])#${number}(?![A-Za-z0-9_])`).test(item.evidence ?? '')),
44
+ newer: matched.some((pr) => pr.mergedAt > item.updatedAt),
45
+ });
46
+ }
47
+ return rows.sort((a, b) => Number(b.newer) - Number(a.newer));
48
+ }
@@ -0,0 +1,58 @@
1
+ // Deterministic merge for `release/next.md` — two landings that each append a line to the same section
2
+ // keep both lines (union per section, same sentence once). Returns null when it cannot decide (the caller
3
+ // falls back to its usual resolver): a side deleted a line, or a stage is missing.
4
+
5
+ export const NEXT_MD_PATH = 'release/next.md';
6
+
7
+ type Section = { header: string | null; lines: string[] };
8
+
9
+ function sections(text: string): Section[] {
10
+ const out: Section[] = [{ header: null, lines: [] }];
11
+ for (const line of text.replace(/\r\n/g, '\n').split('\n')) {
12
+ if (/^## /.test(line)) out.push({ header: line.trim(), lines: [] });
13
+ else out[out.length - 1]!.lines.push(line);
14
+ }
15
+ return out;
16
+ }
17
+
18
+ const key = (line: string) => line.trim();
19
+
20
+ function added(base: Section | undefined, side: Section): string[] {
21
+ const before = new Set((base?.lines ?? []).map(key));
22
+ return side.lines.filter((line) => key(line) !== '' && !before.has(key(line)));
23
+ }
24
+
25
+ function deletedAny(base: Section[], side: Section[]): boolean {
26
+ for (const b of base) {
27
+ const s = side.find((x) => x.header === b.header);
28
+ const kept = new Set((s?.lines ?? []).map(key));
29
+ if (b.lines.some((line) => key(line) !== '' && !kept.has(key(line)))) return true;
30
+ }
31
+ return false;
32
+ }
33
+
34
+ /** Union of both sides' additions over `theirs` (the branch being merged in). Null = not decidable here. */
35
+ export function resolveNextMdConflict(base: string, ours: string, theirs: string): string | null {
36
+ const b = sections(base);
37
+ const o = sections(ours);
38
+ // Our side only appends (harness landings add one line); a deletion on our side is not ours to decide.
39
+ if (deletedAny(b, o)) return null;
40
+ const result = sections(theirs);
41
+ for (const section of o) {
42
+ const extra = added(b.find((x) => x.header === section.header), section);
43
+ if (extra.length === 0) continue;
44
+ let target = result.find((x) => x.header === section.header);
45
+ if (!target) {
46
+ target = { header: section.header, lines: [''] };
47
+ result.push(target);
48
+ }
49
+ const present = new Set(target.lines.map(key));
50
+ const fresh = extra.filter((line) => !present.has(key(line)));
51
+ if (fresh.length === 0) continue;
52
+ // Insert before the section's trailing blank lines so the blank separator stays last.
53
+ let end = target.lines.length;
54
+ while (end > 0 && key(target.lines[end - 1]!) === '') end -= 1;
55
+ target.lines.splice(end, 0, ...fresh);
56
+ }
57
+ return result.map((section) => (section.header === null ? section.lines : [section.header, ...section.lines]).join('\n')).join('\n');
58
+ }
@@ -1,6 +1,7 @@
1
1
  import { closeSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
2
2
  import { randomUUID } from 'node:crypto';
3
3
  import { join } from 'node:path';
4
+ import { debug } from '../debug/log.js';
4
5
 
5
6
  export interface ReleaseNoteFragment {
6
7
  pr: number;
@@ -12,7 +13,8 @@ export interface ReleaseNoteFragment {
12
13
  mergeSha?: string;
13
14
  }
14
15
 
15
- type Section = Omit<ReleaseNoteFragment, 'pr' | 'source' | 'mergeSha'>;
16
+ export type ReleaseNoteSection = Omit<ReleaseNoteFragment, 'pr' | 'source' | 'mergeSha'>;
17
+ type Section = ReleaseNoteSection;
16
18
 
17
19
  /** Blank out fenced code blocks (``` / ~~~) line-for-line so example headings and fields inside them are never read as the real section. */
18
20
  function blankFencedCode(text: string): string {
@@ -69,10 +71,10 @@ export function renderReleaseNoteSection(fragment: Section): string {
69
71
  return `## 릴리스 노트\n- 한 줄: ${fragment.line}\n- 종류: ${fragment.kind}\n- 문서: ${'path' in fragment.docs ? fragment.docs.path : `없음(${fragment.docs.none})`}\n- 대상: ${fragment.target}\n`;
70
72
  }
71
73
 
72
- export function harnessReleaseNote(goalDocument: string, title: string): Section {
74
+ export function parseGoalReleaseNoteSection(goalDocument: string): ReturnType<typeof parseReleaseNoteSection> & { hasSection: boolean } {
73
75
  const document = blankFencedCode(goalDocument);
74
76
  const goal = /^ {0,3}## 목표[ \t]*\r?$/m.exec(document);
75
- let section = '';
77
+ let section = goal ? '' : document;
76
78
  if (goal) {
77
79
  const rest = document.slice(goal.index + goal[0].length);
78
80
  let end = rest.length;
@@ -84,7 +86,20 @@ export function harnessReleaseNote(goalDocument: string, title: string): Section
84
86
  }
85
87
  section = rest.slice(0, end);
86
88
  }
87
- return parseReleaseNoteSection(section).fragment ?? {
89
+ return {
90
+ ...parseReleaseNoteSection(section),
91
+ hasSection: /(?:^|\n)## 릴리스 노트[ \t]*\r?(?:\n|$)/.test(section),
92
+ };
93
+ }
94
+
95
+ export function harnessReleaseNote(goalDocument: string, title: string, shard = false): Section {
96
+ const parsed = parseGoalReleaseNoteSection(goalDocument);
97
+ if (parsed.fragment) return parsed.fragment;
98
+ debug.log('release-note', 'harness-fallback', {
99
+ reason: parsed.hasSection ? 'invalid' : 'missing',
100
+ shard,
101
+ });
102
+ return {
88
103
  line: title,
89
104
  kind: 'internal',
90
105
  docs: { none: '하니스 자동 생성' },
@@ -92,6 +107,46 @@ export function harnessReleaseNote(goalDocument: string, title: string): Section
92
107
  };
93
108
  }
94
109
 
110
+ /** Only the worktree's release notes enter the public cut. Keep repeats byte-for-byte idempotent. */
111
+ export function addNextMdReleaseNote(worktreePath: string, note: Section): boolean {
112
+ if (note.target !== 'next' || note.kind === 'internal') return false;
113
+ const path = join(worktreePath, 'release', 'next.md');
114
+ // Public next notes require the worktree's cut source; ENOENT must stop PR creation.
115
+ const text = readFileSync(path, 'utf8');
116
+ const sentence = note.line.trim();
117
+ const prefixed = sentence.startsWith(`${note.kind} — `) ? sentence : `${note.kind} — ${sentence}`;
118
+ const item = `- ${prefixed.replace(/\.$/, '')}. Documentation: ${'path' in note.docs ? note.docs.path : 'none'}. Target: next.`;
119
+ const lines = text.split('\n');
120
+ const sameSentence = (line: string) => line.replace(/\r$/, '')
121
+ .replace(/\. Documentation:.*$/, '').replace(/\.$/, '') === `- ${prefixed.replace(/\.$/, '')}`;
122
+ const existing = lines.findIndex(sameSentence);
123
+ if (existing >= 0) {
124
+ if (lines[existing]!.replace(/\r$/, '') === item) return false;
125
+ // Same sentence, different documentation: the PR body is the source — make next.md say the same thing.
126
+ lines[existing] = item;
127
+ writeFileSync(path, lines.join('\n'));
128
+ debug.log('release-note', 'next-md-updated', { kind: note.kind, docs: 'path' in note.docs ? 'path' : 'none' });
129
+ return true;
130
+ }
131
+ const heading = `## ${note.kind === 'feat' ? 'Feat' : note.kind === 'fix' ? 'Fix' : 'Security'}`;
132
+ let index = lines.findIndex((line) => line.trimEnd() === heading);
133
+ if (index < 0) {
134
+ const fix = lines.findIndex((line) => line.trimEnd() === '## Fix');
135
+ if (fix < 0) throw new Error(`release/next.md has no ## Fix section: ${path}`);
136
+ index = fix + 1;
137
+ while (index < lines.length && !/^##\s/.test(lines[index]!)) index++;
138
+ lines.splice(index, 0, heading, '');
139
+ index++;
140
+ }
141
+ index++;
142
+ while (index < lines.length && !/^##\s/.test(lines[index]!)) index++;
143
+ while (index > 0 && lines[index - 1] === '') index--;
144
+ if (index === lines.findIndex((line) => line.trimEnd() === heading) + 1) lines.splice(index++, 0, '');
145
+ lines.splice(index, 0, item);
146
+ writeFileSync(path, lines.join('\n'));
147
+ return true;
148
+ }
149
+
95
150
  export function releaseNotesDir(instanceRoot: string): string {
96
151
  return join(instanceRoot, 'release', 'notes');
97
152
  }