elanous 0.2.17 → 0.2.18

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 (309) 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/P6YTVgzuZz5oTZhY4Din_/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/{1307-aac7332576c4d6fc.js → 1307-30dff22c9e1f3073.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/3857-4f936cdb30322aa6.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/4554-5761612531d0146b.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/5977-a11f37254196b659.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/{6158.9c0449a9a8d0064b.js → 6158.af2e93b7adff709b.js} +1 -1
  14. package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/8401-8615a660c84cdb03.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/app/approvals/{page-3143b07a817d1da9.js → page-d92f7ee3994e84c7.js} +1 -1
  19. package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-17ba086ef82d339b.js → page-4733c967b56a4277.js} +1 -1
  20. package/apps/pwa/out/_next/static/chunks/app/board/{page-7eb661ff23af7da2.js → page-d22fd849cc22f104.js} +1 -1
  21. package/apps/pwa/out/_next/static/chunks/app/botlab/page-bb742f995c6769e7.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/app/bots/{page-6e5c3a867557298b.js → page-02b073a2ac793b83.js} +1 -1
  23. package/apps/pwa/out/_next/static/chunks/app/ceo/page-60c3da708f9a0ccc.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/app/chat/page-8a9b3594bc23289b.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/app/consult/{page-5e77cd1802567c89.js → page-a0eb02638b1e6f31.js} +1 -1
  26. package/apps/pwa/out/_next/static/chunks/app/control/page-308111abadf35670.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/app/decisions/page-6eaf7bac37830c21.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/app/design-check/{page-4187666132fc98c4.js → page-5d97b2901e0d1ea6.js} +1 -1
  29. package/apps/pwa/out/_next/static/chunks/app/editor/{page-09328d499162bae4.js → page-c39554c321576982.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/app/exec/{page-29ff3a8f84f03b6a.js → page-8c3d8e63ae2e1c98.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/app/field/{page-80ec7401bc83f511.js → page-9b72a07d424701c6.js} +1 -1
  32. package/apps/pwa/out/_next/static/chunks/app/inside/page-bd5d586429167fea.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/app/intake/{page-fc7d86467291ae38.js → page-a572f58cd3ca5864.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/app/layout-59b7834d497c2255.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/app/live/page-be5ea965a07fbc41.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/app/loops/{page-0e2d6985fb42cd2f.js → page-c130b63862437814.js} +1 -1
  37. package/apps/pwa/out/_next/static/chunks/app/market/{page-13e4108999f58d13.js → page-fa244bd146a00ae8.js} +1 -1
  38. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-e8296ee284aa1293.js → page-44d56d6391965a6a.js} +1 -1
  39. package/apps/pwa/out/_next/static/chunks/app/morning/{page-043a9111e74f6b24.js → page-83a700d4c754d802.js} +1 -1
  40. package/apps/pwa/out/_next/static/chunks/app/{not-found-23a4601ceb7f452e.js → not-found-7b3170f374f77b9d.js} +1 -1
  41. package/apps/pwa/out/_next/static/chunks/app/observatory/page-5f23d0c66a3b4793.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-10d958605a77b3bd.js → page-a3c58f5c72a5f3f9.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-a2c4625596f8d69c.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-0e9099531d239f11.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/app/outputs/{page-7a1d85e121065cc8.js → page-e8e1ecaea7cd71d2.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/app/page-a1b7ffed710a93e0.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/app/reflection/{page-12916918988ac97e.js → page-1467047104141297.js} +1 -1
  48. package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-d8a3347a9d0c3f4a.js → page-2da6f9b8a38c6978.js} +1 -1
  49. package/apps/pwa/out/_next/static/chunks/app/sessions/page-102310930791197a.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-f331e38fefc00393.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/settings/page-6ed0b73bae8351d8.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-1d824510df025d18.js → page-44d5f767491692bf.js} +1 -1
  53. package/apps/pwa/out/_next/static/chunks/app/setup/page-43336055451f6e37.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/share/{page-7f49fe645d5132f7.js → page-3cc47dca0922ab9a.js} +1 -1
  55. package/apps/pwa/out/_next/static/chunks/app/showroom/page-a77ee355a2304ff0.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/app/tasks/{page-27d71f131acd9320.js → page-b17cd46cf0e9b8cf.js} +1 -1
  57. package/apps/pwa/out/_next/static/chunks/app/term/page-61553851aa606146.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/today/page-a1f585e154e61c59.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/trace/{page-424659bacfb8cdd7.js → page-7bf54c9bb9274af8.js} +1 -1
  60. package/apps/pwa/out/_next/static/chunks/app/vault/page-5150ef17b5383a57.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b9d8ce70792c1672.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/workflows/page-a5a3de231f2cc7d5.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/workspace/{page-5d4fef9ccd7d406b.js → page-134da468538baf48.js} +1 -1
  64. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31ac4afa2dcd915c.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/{webpack-539cde99554be20f.js → webpack-c2782b330447ffb6.js} +1 -1
  66. package/apps/pwa/out/_next/static/css/7fa358fec7da7c47.css +1 -0
  67. package/apps/pwa/out/approvals/index.html +1 -1
  68. package/apps/pwa/out/approvals/index.txt +14 -14
  69. package/apps/pwa/out/autopilot/index.html +1 -1
  70. package/apps/pwa/out/autopilot/index.txt +14 -14
  71. package/apps/pwa/out/board/index.html +1 -1
  72. package/apps/pwa/out/board/index.txt +14 -14
  73. package/apps/pwa/out/botlab/index.html +1 -1
  74. package/apps/pwa/out/botlab/index.txt +14 -14
  75. package/apps/pwa/out/bots/index.html +1 -1
  76. package/apps/pwa/out/bots/index.txt +14 -14
  77. package/apps/pwa/out/ceo/index.html +1 -1
  78. package/apps/pwa/out/ceo/index.txt +14 -14
  79. package/apps/pwa/out/chat/index.html +1 -1
  80. package/apps/pwa/out/chat/index.txt +14 -14
  81. package/apps/pwa/out/consult/index.html +1 -1
  82. package/apps/pwa/out/consult/index.txt +14 -14
  83. package/apps/pwa/out/control/index.html +1 -1
  84. package/apps/pwa/out/control/index.txt +14 -14
  85. package/apps/pwa/out/decisions/index.html +1 -0
  86. package/apps/pwa/out/decisions/index.txt +30 -0
  87. package/apps/pwa/out/design-check/index.html +1 -1
  88. package/apps/pwa/out/design-check/index.txt +14 -14
  89. package/apps/pwa/out/editor/index.html +1 -1
  90. package/apps/pwa/out/editor/index.txt +14 -14
  91. package/apps/pwa/out/exec/index.html +1 -1
  92. package/apps/pwa/out/exec/index.txt +14 -14
  93. package/apps/pwa/out/field/index.html +1 -1
  94. package/apps/pwa/out/field/index.txt +14 -14
  95. package/apps/pwa/out/healthz +1 -1
  96. package/apps/pwa/out/index.html +1 -1
  97. package/apps/pwa/out/index.txt +14 -14
  98. package/apps/pwa/out/inside/index.html +1 -1
  99. package/apps/pwa/out/inside/index.txt +14 -14
  100. package/apps/pwa/out/intake/index.html +1 -1
  101. package/apps/pwa/out/intake/index.txt +14 -14
  102. package/apps/pwa/out/live/index.html +1 -1
  103. package/apps/pwa/out/live/index.txt +14 -14
  104. package/apps/pwa/out/loops/index.html +1 -1
  105. package/apps/pwa/out/loops/index.txt +14 -14
  106. package/apps/pwa/out/market/index.html +1 -1
  107. package/apps/pwa/out/market/index.txt +14 -14
  108. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  109. package/apps/pwa/out/missions/placeholder/index.txt +14 -14
  110. package/apps/pwa/out/morning/index.html +1 -1
  111. package/apps/pwa/out/morning/index.txt +14 -14
  112. package/apps/pwa/out/observatory/index.html +1 -1
  113. package/apps/pwa/out/observatory/index.txt +14 -14
  114. package/apps/pwa/out/ops/checklist/index.html +1 -1
  115. package/apps/pwa/out/ops/checklist/index.txt +14 -14
  116. package/apps/pwa/out/ops/release/index.html +1 -1
  117. package/apps/pwa/out/ops/release/index.txt +14 -14
  118. package/apps/pwa/out/ops/seats/index.html +1 -1
  119. package/apps/pwa/out/ops/seats/index.txt +14 -14
  120. package/apps/pwa/out/outputs/index.html +1 -1
  121. package/apps/pwa/out/outputs/index.txt +14 -14
  122. package/apps/pwa/out/reflection/index.html +1 -1
  123. package/apps/pwa/out/reflection/index.txt +15 -15
  124. package/apps/pwa/out/scheduler/index.html +1 -1
  125. package/apps/pwa/out/scheduler/index.txt +14 -14
  126. package/apps/pwa/out/sessions/index.html +1 -1
  127. package/apps/pwa/out/sessions/index.txt +14 -14
  128. package/apps/pwa/out/settings/devices/index.html +1 -1
  129. package/apps/pwa/out/settings/devices/index.txt +14 -14
  130. package/apps/pwa/out/settings/index.html +1 -1
  131. package/apps/pwa/out/settings/index.txt +14 -14
  132. package/apps/pwa/out/setup/done/index.html +1 -1
  133. package/apps/pwa/out/setup/done/index.txt +14 -14
  134. package/apps/pwa/out/setup/index.html +1 -1
  135. package/apps/pwa/out/setup/index.txt +14 -14
  136. package/apps/pwa/out/share/index.html +1 -1
  137. package/apps/pwa/out/share/index.txt +14 -14
  138. package/apps/pwa/out/showroom/index.html +1 -1
  139. package/apps/pwa/out/showroom/index.txt +14 -14
  140. package/apps/pwa/out/tasks/index.html +1 -1
  141. package/apps/pwa/out/tasks/index.txt +14 -14
  142. package/apps/pwa/out/term/index.html +1 -1
  143. package/apps/pwa/out/term/index.txt +14 -14
  144. package/apps/pwa/out/today/index.html +1 -1
  145. package/apps/pwa/out/today/index.txt +14 -14
  146. package/apps/pwa/out/trace/index.html +1 -1
  147. package/apps/pwa/out/trace/index.txt +14 -14
  148. package/apps/pwa/out/vault/index.html +1 -1
  149. package/apps/pwa/out/vault/index.txt +14 -14
  150. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  151. package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
  152. package/apps/pwa/out/workflows/index.html +1 -1
  153. package/apps/pwa/out/workflows/index.txt +14 -14
  154. package/apps/pwa/out/workspace/index.html +1 -1
  155. package/apps/pwa/out/workspace/index.txt +14 -14
  156. package/apps/pwa/out/worktrees/index.html +1 -1
  157. package/apps/pwa/out/worktrees/index.txt +14 -14
  158. package/package.json +2 -1
  159. package/samples/workflows/templates/approval-gated-deploy.yaml +30 -0
  160. package/samples/workflows/templates/chat-driven-research.yaml +35 -0
  161. package/samples/workflows/templates/discord-listen-and-respond.yaml +29 -0
  162. package/samples/workflows/templates/http-poll-then-alert.yaml +30 -0
  163. package/samples/workflows/templates/manual-quickstart.yaml +21 -0
  164. package/samples/workflows/templates/scheduled-daily-digest.yaml +30 -0
  165. package/samples/workflows/templates/webhook-deploy-hook.yaml +27 -0
  166. package/scripts/ci-public-export-import-gate.ts +7 -2
  167. package/scripts/ci-typecheck-changed.ts +21 -6
  168. package/scripts/discord-seat-probe.ts +3 -2
  169. package/scripts/docs/generate-cli-anatomy.ts +162 -0
  170. package/scripts/goals/authoring-metrics.ts +73 -0
  171. package/scripts/landing-heal/must-fix-match.ts +7 -7
  172. package/scripts/landing-heal/run-mission.ts +3 -2
  173. package/scripts/public-export-test-run.ts +37 -2
  174. package/scripts/release-loop/checklist-gate-node.ts +13 -11
  175. package/scripts/release-loop/cut-branch.ts +2 -1
  176. package/scripts/release-loop/cutoff.ts +4 -2
  177. package/scripts/release-loop/docs-land-node.ts +8 -1
  178. package/scripts/release-loop/docs-node.ts +1 -1
  179. package/scripts/release-loop/gate-node.ts +99 -14
  180. package/scripts/release-loop/node-verdict.ts +2 -1
  181. package/scripts/release-loop/npm-publish-node.ts +11 -7
  182. package/scripts/release-loop/ops-upgrade-node.ts +8 -1
  183. package/scripts/release-loop/preflight.ts +246 -0
  184. package/scripts/release-loop/publish-node.ts +8 -1
  185. package/scripts/release-loop/release-readiness.ts +11 -6
  186. package/scripts/release-loop/release-version.ts +48 -0
  187. package/scripts/release-loop/resume-release.ts +115 -0
  188. package/scripts/release-loop/unattended-release.ts +43 -7
  189. package/scripts/release-loop/version-node.ts +145 -40
  190. package/scripts/rhythm/daily.ts +25 -4
  191. package/scripts/skills/public-check.ts +62 -12
  192. package/scripts/test-deterministic.ts +51 -33
  193. package/scripts/test-diet/effectiveness.ts +62 -1
  194. package/scripts/test-diet/lib.ts +1 -1
  195. package/scripts/test-diet/node.ts +8 -1
  196. package/src/agent-mission/review-loop.ts +4 -3
  197. package/src/agent-substrate/pr-comment-meta.ts +20 -0
  198. package/src/autopilot/build/llm-conflict-merge.ts +28 -8
  199. package/src/briefing/brief-items.ts +86 -17
  200. package/src/cli/autopilot-cli.ts +30 -11
  201. package/src/cli/brief-cli.ts +36 -2
  202. package/src/cli/freeze-cli.ts +3 -2
  203. package/src/cli/intake-cli.ts +73 -1
  204. package/src/cli/notify-cli.ts +75 -0
  205. package/src/cli/provider-cli.ts +12 -2
  206. package/src/cli/release-cli.ts +101 -17
  207. package/src/cli/tasks-cli.ts +309 -3
  208. package/src/context-bus/condense.ts +19 -3
  209. package/src/context-bus/context-day.ts +77 -24
  210. package/src/context-bus/context-first.ts +33 -0
  211. package/src/discord-self-message.ts +20 -2
  212. package/src/discord-slash-wire.ts +1 -0
  213. package/src/domains/outbound-alert.ts +200 -27
  214. package/src/domains/outbound-log-probe.ts +16 -1
  215. package/src/domains/release-tool.ts +2 -2
  216. package/src/execution-loop/gates.ts +11 -2
  217. package/src/flow-loop/split.ts +8 -4
  218. package/src/git-fs/worktree.ts +23 -3
  219. package/src/harness/harness-cli-command.ts +10 -2
  220. package/src/harness/harness-queue.ts +388 -33
  221. package/src/harness/helper-scan.ts +10 -3
  222. package/src/hq/seats.ts +369 -0
  223. package/src/index.ts +133 -15
  224. package/src/intake-plane/adopt-gate.ts +49 -0
  225. package/src/intake-plane/check.ts +3 -0
  226. package/src/intake-plane/collect-github.ts +57 -25
  227. package/src/intake-plane/discord-seat-work.ts +8 -2
  228. package/src/intake-plane/requirement-funnel.ts +316 -0
  229. package/src/knowledge/know-find.ts +10 -1
  230. package/src/loops/budget.ts +36 -0
  231. package/src/loops/orchestrator/finish-rate.ts +73 -1
  232. package/src/loops/orchestrator/grid-place.ts +11 -0
  233. package/src/loops/orchestrator/tick.ts +295 -20
  234. package/src/maturity/feature-maturity.ts +3 -2
  235. package/src/nexus/api/decisions-route.ts +11 -1
  236. package/src/nexus/api/loop-edges.ts +69 -13
  237. package/src/nexus/api/ops-seats-sources.ts +2 -2
  238. package/src/nexus/api/workflow-templates.ts +3 -0
  239. package/src/oauth/codex.ts +38 -0
  240. package/src/onboarding/first-chat-gate.ts +29 -0
  241. package/src/release-loop/checklist.ts +104 -6
  242. package/src/release-loop/feature-store.ts +10 -9
  243. package/src/release-loop/landing-freeze.ts +6 -4
  244. package/src/release-loop/placement.ts +13 -3
  245. package/src/release-loop/rubric.ts +80 -0
  246. package/src/seat-dispatch/seat-ask.ts +79 -16
  247. package/src/seat-loop/seat-loop.ts +12 -6
  248. package/src/self-dev/ask-launch-flow.ts +24 -3
  249. package/src/self-dev/ask-path-candidates.ts +68 -0
  250. package/src/self-dev/dev-pipeline.ts +4 -0
  251. package/src/self-dev/draft-triage-rules.ts +1 -1
  252. package/src/self-dev/orchestrate-cli.ts +22 -0
  253. package/src/self-dev/round-pr-supersede.ts +244 -0
  254. package/src/self-dev/run-supervisor.ts +14 -0
  255. package/src/self-implement/follow-up-goals.ts +2 -2
  256. package/src/self-implement/gate-remote.ts +427 -0
  257. package/src/self-implement/goal-author.ts +8 -3
  258. package/src/self-implement/l8-merge-queue.ts +29 -5
  259. package/src/self-implement/orchestrator.ts +59 -12
  260. package/src/self-implement/prior-draft-findings.ts +4 -3
  261. package/src/skills/skill-feedback.ts +76 -0
  262. package/src/task-agent/actions.ts +39 -6
  263. package/src/task-agent/judge.ts +31 -5
  264. package/src/task-agent/mission.ts +423 -0
  265. package/src/task-agent/shadow.ts +90 -0
  266. package/src/task-agent/task-hand.ts +235 -0
  267. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +10 -0
  268. package/src/task-orchestrator/surfaces/self-implement-pod.ts +86 -18
  269. package/src/telegram-loops-command.ts +1 -1
  270. package/src/telegram.ts +5 -20
  271. package/src/user-config.ts +63 -3
  272. package/src/version/packed-revision.json +1 -1
  273. package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.js +0 -1
  274. package/apps/pwa/out/_next/static/chunks/4554-3565eea1f1495ba7.js +0 -1
  275. package/apps/pwa/out/_next/static/chunks/4614-21403aac730d4586.js +0 -1
  276. package/apps/pwa/out/_next/static/chunks/5135-44008fe8b1f2aa41.js +0 -1
  277. package/apps/pwa/out/_next/static/chunks/5233-bf28bd6bd7a32099.js +0 -1
  278. package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +0 -1
  279. package/apps/pwa/out/_next/static/chunks/6877-fe94e76a5841ee9b.js +0 -1
  280. package/apps/pwa/out/_next/static/chunks/7395-164e238f471b5d7b.js +0 -1
  281. package/apps/pwa/out/_next/static/chunks/7787-02a1c0b04e25e811.js +0 -1
  282. package/apps/pwa/out/_next/static/chunks/8401-4ce1c42c8d89903f.js +0 -1
  283. package/apps/pwa/out/_next/static/chunks/8949-ecb4128a0858b911.js +0 -1
  284. package/apps/pwa/out/_next/static/chunks/899-f41be85e8a680651.js +0 -1
  285. package/apps/pwa/out/_next/static/chunks/app/botlab/page-6b8444914da74ca6.js +0 -1
  286. package/apps/pwa/out/_next/static/chunks/app/ceo/page-7110f732f45bf944.js +0 -1
  287. package/apps/pwa/out/_next/static/chunks/app/chat/page-9a41d65355fc25b9.js +0 -1
  288. package/apps/pwa/out/_next/static/chunks/app/control/page-41afec0b255ed2bb.js +0 -1
  289. package/apps/pwa/out/_next/static/chunks/app/inside/page-ab93b240f687d69a.js +0 -1
  290. package/apps/pwa/out/_next/static/chunks/app/layout-de43a186e5ba5683.js +0 -1
  291. package/apps/pwa/out/_next/static/chunks/app/live/page-8b08cafad27759c9.js +0 -1
  292. package/apps/pwa/out/_next/static/chunks/app/observatory/page-1ed1324b1dc36754.js +0 -1
  293. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-166745e51ac0c7b0.js +0 -1
  294. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-a04260b8ccc968d4.js +0 -1
  295. package/apps/pwa/out/_next/static/chunks/app/page-1f50e3d4e13435f4.js +0 -1
  296. package/apps/pwa/out/_next/static/chunks/app/sessions/page-a9117d2cd3093d36.js +0 -1
  297. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-5a64cb5208c6a8fd.js +0 -1
  298. package/apps/pwa/out/_next/static/chunks/app/settings/page-1407ec83ea64c1f9.js +0 -1
  299. package/apps/pwa/out/_next/static/chunks/app/setup/page-6e878ebbe1737d00.js +0 -1
  300. package/apps/pwa/out/_next/static/chunks/app/showroom/page-81a4e7f8dbfe74cb.js +0 -1
  301. package/apps/pwa/out/_next/static/chunks/app/term/page-3ab59d19913b200e.js +0 -1
  302. package/apps/pwa/out/_next/static/chunks/app/today/page-bf85b2f5bf077925.js +0 -1
  303. package/apps/pwa/out/_next/static/chunks/app/vault/page-ab32a8f522a60371.js +0 -1
  304. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-fc2df261f8a2c7df.js +0 -1
  305. package/apps/pwa/out/_next/static/chunks/app/workflows/page-09461366af82ef13.js +0 -1
  306. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-ea90a8ac73ce2749.js +0 -1
  307. package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +0 -1
  308. package/apps/pwa/out/_next/static/yRA5ur6DcPl0YHSRheH2O/_buildManifest.js +0 -1
  309. /package/apps/pwa/out/_next/static/{yRA5ur6DcPl0YHSRheH2O → P6YTVgzuZz5oTZhY4Din_}/_ssgManifest.js +0 -0
@@ -0,0 +1,49 @@
1
+ import { debug } from '../debug/log.js';
2
+
3
+ export interface AdoptionCandidate {
4
+ readonly id: string;
5
+ readonly grade: 'P1' | 'P2' | 'P3' | 'P4';
6
+ readonly risk: number;
7
+ readonly money: number;
8
+ readonly security: number;
9
+ readonly license: string | null;
10
+ readonly touchesPatentCandidate: boolean;
11
+ }
12
+
13
+ export type AdoptShadowReason = 'grade' | 'risk' | 'money' | 'security' | 'license' | 'patent' | 'daily-cap';
14
+
15
+ export interface AdoptShadowVerdict {
16
+ readonly id: string;
17
+ readonly adopt: boolean;
18
+ readonly reasons: readonly AdoptShadowReason[];
19
+ }
20
+
21
+ const ALLOWED_LICENSES: ReadonlySet<string> = new Set([
22
+ 'MIT', 'Apache-2.0', 'BSD-2-Clause', 'BSD-3-Clause', 'ISC', 'MPL-2.0',
23
+ ]);
24
+
25
+ export function judgeAdoption(candidate: AdoptionCandidate, adoptedToday: number): AdoptShadowVerdict {
26
+ const reasons: AdoptShadowReason[] = [];
27
+ if (candidate.grade !== 'P1' && candidate.grade !== 'P2') reasons.push('grade');
28
+ if (!(candidate.risk <= 1)) reasons.push('risk');
29
+ if (candidate.money !== 0) reasons.push('money');
30
+ if (candidate.security !== 0) reasons.push('security');
31
+ if (candidate.license === null || !ALLOWED_LICENSES.has(candidate.license)) reasons.push('license');
32
+ if (candidate.touchesPatentCandidate !== false) reasons.push('patent');
33
+ if (!(adoptedToday < 5)) reasons.push('daily-cap');
34
+ return { id: candidate.id, adopt: reasons.length === 0, reasons };
35
+ }
36
+
37
+ export function shadowAdopt(candidates: readonly AdoptionCandidate[]): AdoptShadowVerdict[] {
38
+ const verdicts: AdoptShadowVerdict[] = [];
39
+ let adoptedToday = 0;
40
+ for (const candidate of candidates) {
41
+ const verdict = judgeAdoption(candidate, adoptedToday);
42
+ if (verdict.adopt) adoptedToday++;
43
+ verdicts.push(verdict);
44
+ debug.log('intake.adopt', 'shadow-verdict', {
45
+ id: verdict.id, adopt: verdict.adopt, reasons: verdict.reasons,
46
+ });
47
+ }
48
+ return verdicts;
49
+ }
@@ -21,6 +21,7 @@ import { parse as parseYaml } from 'yaml';
21
21
  import { debug } from '../debug/log.js';
22
22
  import { openSurfaceEventsDb, recallEvents } from '../domains/surface-events.js';
23
23
  import { extractJsonBlock, skeletonFallback } from './decompose.js';
24
+ import type { AdoptShadowVerdict } from './adopt-gate.js';
24
25
 
25
26
  export const INTAKE_CHECK_MODE = 'check' as const;
26
27
 
@@ -110,6 +111,7 @@ export interface IntakeCheckReport {
110
111
  /** 비교·시너지 제안 수(근거 없음 포함). */
111
112
  readonly proposalCount?: number;
112
113
  readonly proposals?: readonly IntakeCompareProposal[];
114
+ readonly adoptShadow?: readonly AdoptShadowVerdict[];
113
115
  }
114
116
 
115
117
  /** 선가공 렌즈 여섯. 🅞 원장 · PR #20009. */
@@ -1826,6 +1828,7 @@ export function intakeCheckReportJson(report: IntakeCheckReport): Record<string,
1826
1828
  proposalCount: report.proposalCount,
1827
1829
  proposals: report.proposals,
1828
1830
  } : {}),
1831
+ ...(report.adoptShadow !== undefined ? { adoptShadow: report.adoptShadow } : {}),
1829
1832
  };
1830
1833
  }
1831
1834
 
@@ -6,14 +6,18 @@ import { execFileSync } from 'node:child_process';
6
6
  import { debug } from '../debug/log.js';
7
7
  import { ingestIntakeItems, intakeLedgerDir, type IngestResult, type RawIntakeItem } from './items.js';
8
8
 
9
- /** 한 판에 열 번 안쪽 — 검색 API 비율 제한(인증 시 분당 30회)을 시험 밖에서 상수로 묶는다. */
9
+ /** 한 판에 열 번 이하 — 검색 API 비율 제한(인증 시 분당 30회)을 시험 밖에서 상수로 묶는다. */
10
10
  export const GITHUB_STAR_QUERIES = [
11
11
  'topic:ai-agents',
12
- 'topic:llm-agent',
13
12
  'topic:coding-agent',
14
13
  'topic:mcp',
15
14
  'topic:video-generation',
16
- '"ai agent" in:name,description',
15
+ 'topic:agent-framework',
16
+ 'topic:computer-use',
17
+ 'topic:local-llm',
18
+ 'topic:claude-code',
19
+ 'topic:codex',
20
+ 'topic:agent-memory',
17
21
  ] as const;
18
22
 
19
23
  /** `--days` 기본. 최근 이 일수 안에 만들어졌거나 푸시된 저장소만 받는다. */
@@ -32,6 +36,7 @@ export interface GithubStarRepo {
32
36
  stars: number;
33
37
  createdAt: string;
34
38
  pushedAt: string;
39
+ license?: string;
35
40
  }
36
41
 
37
42
  /** 질의 하나 → 저장소 목록. 실제 구현은 `gh api` · 시험은 가짜. */
@@ -43,6 +48,7 @@ export interface GithubStarSignals {
43
48
  stars: number;
44
49
  starsDelta?: number;
45
50
  starsPerDay?: number;
51
+ baselineDays?: number;
46
52
  }
47
53
 
48
54
  export interface CollectGithubQueryCount { query: string; received: number }
@@ -55,6 +61,8 @@ export interface CollectGithubRepoRow {
55
61
  stars: number;
56
62
  starsDelta?: number;
57
63
  starsPerDay?: number;
64
+ baselineDays?: number;
65
+ license?: string;
58
66
  }
59
67
 
60
68
  export interface CollectGithubResult {
@@ -99,6 +107,17 @@ export function precedingSnapshot(snaps: readonly StarSnapshot[], repo: string,
99
107
  return best;
100
108
  }
101
109
 
110
+ /** 7일 전 또는 그 이전 중 가장 가까운 스냅숏; 없으면 직전 스냅숏. */
111
+ export function surgeBaseline(snaps: readonly StarSnapshot[], repo: string, day: string): StarSnapshot | undefined {
112
+ const cutoff = new Date(Date.parse(`${day}T00:00:00.000Z`) - 7 * 86_400_000).toISOString().slice(0, 10);
113
+ let best: StarSnapshot | undefined;
114
+ for (const s of snaps) {
115
+ if (s.repo !== repo || s.day > cutoff) continue;
116
+ if (!best || s.day > best.day) best = s;
117
+ }
118
+ return best ?? precedingSnapshot(snaps, repo, day);
119
+ }
120
+
102
121
  function utcDay(iso: string): string {
103
122
  const t = Date.parse(iso);
104
123
  if (!Number.isFinite(t)) return '';
@@ -128,7 +147,7 @@ export function signalsFromSnapshot(stars: number, prev: StarSnapshot | undefine
128
147
  if (!prev) return { stars };
129
148
  const span = Math.max(1, daysBetween(prev.day, day));
130
149
  const starsDelta = stars - prev.stars;
131
- return { stars, starsDelta, starsPerDay: starsDelta / span };
150
+ return { stars, starsDelta, starsPerDay: starsDelta / span, baselineDays: span };
132
151
  }
133
152
 
134
153
  export function appendGithubStarSnapshots(root: string, rows: StarSnapshot[]): void {
@@ -154,13 +173,13 @@ export interface CollectGithubOpts {
154
173
  /** 스냅숏·비교의 «오늘». 생략하면 UTC 날짜. */
155
174
  day?: string;
156
175
  now?: () => Date;
157
- /** 등록 원천이 질의 하나만 수집할 때; 생략 시 기존 관심 질의 전부. */
176
+ /** 등록 원천이 질의 하나만 수집할 때; 생략 시 관심 질의 전부 (최대 10개). */
158
177
  queries?: readonly string[];
159
178
  }
160
179
 
161
180
  /**
162
181
  * 관심 주제 질의마다 별 순 검색을 부르고, 최근 창 안의 저장소만 남긴 뒤 질의 사이에서 중복을 없앤다.
163
- * 저장소마다 오늘 별 수 스냅숏을 덧붙이고, 직전 스냅숏과 비교한 신호를 원장에 넣는다.
182
+ * 저장소마다 오늘 별 수 스냅숏을 덧붙이고, 7일 기준(없으면 직전) 스냅숏과 비교한 신호를 원장에 넣는다.
164
183
  * dryRun 이면 스냅숏·원장을 바꾸지 않는다.
165
184
  */
166
185
  export async function collectGithubStars(
@@ -175,7 +194,7 @@ export async function collectGithubStars(
175
194
  const found: GithubStarRepo[] = [];
176
195
  // «급상승»은 최근에 생긴 저장소 중 별 순이다 — 전체 기간 별 순이면 활발한 거대 저장소가 매일 같은 목록으로 나온다(2026-09-26 검토).
177
196
  const windowStart = new Date(Date.parse(`${day}T00:00:00.000Z`) - days * 86_400_000).toISOString().slice(0, 10);
178
- for (const query of opts.queries ?? GITHUB_STAR_QUERIES) {
197
+ for (const query of (opts.queries ?? GITHUB_STAR_QUERIES).slice(0, 10)) {
179
198
  const rows = await search(`${query} created:>=${windowStart}`, perQuery);
180
199
  const recent = rows.filter((r) => isRecentRepo(r, day, days));
181
200
  queries.push({ query, received: recent.length });
@@ -186,20 +205,24 @@ export async function collectGithubStars(
186
205
  const repos: CollectGithubRepoRow[] = unique.map((repo) => {
187
206
  const prev = precedingSnapshot(prior, repo.fullName, day);
188
207
  const signals = signalsFromSnapshot(repo.stars, prev, day);
208
+ const surge = signalsFromSnapshot(repo.stars, surgeBaseline(prior, repo.fullName, day), day);
189
209
  return {
190
210
  repo: repo.fullName,
191
211
  url: repo.url,
192
212
  title: repo.fullName,
193
213
  text: repo.description,
194
214
  stars: signals.stars,
215
+ ...(repo.license !== undefined ? { license: repo.license } : {}),
195
216
  ...(signals.starsDelta !== undefined ? { starsDelta: signals.starsDelta } : {}),
196
- ...(signals.starsPerDay !== undefined ? { starsPerDay: signals.starsPerDay } : {}),
217
+ ...(surge.starsPerDay !== undefined ? { starsPerDay: surge.starsPerDay } : {}),
218
+ ...(surge.baselineDays !== undefined ? { baselineDays: surge.baselineDays } : {}),
197
219
  };
198
- });
220
+ }).sort((a, b) => (b.starsPerDay ?? -Infinity) - (a.starsPerDay ?? -Infinity) || b.stars - a.stars);
199
221
  const raws: RawIntakeItem[] = repos.map((row) => ({
200
222
  url: row.url,
201
223
  title: row.title,
202
- text: row.text,
224
+ // 모든 행 첫 줄에 라이선스: SPDX · 확인된 부재는 «없음» · 응답이 알려 주지 않으면 «미상»(없음으로 읽지 않는다).
225
+ text: `라이선스: ${row.license ?? '미상'}\n${row.text}`,
203
226
  signals: {
204
227
  stars: row.stars,
205
228
  ...(row.starsDelta !== undefined ? { starsDelta: row.starsDelta } : {}),
@@ -226,23 +249,10 @@ interface GhSearchItem {
226
249
  stargazers_count?: number;
227
250
  created_at?: string;
228
251
  pushed_at?: string;
252
+ license?: { spdx_id?: string | null } | null;
229
253
  }
230
254
 
231
- /** `gh api search/repositories` 를 `sort=stars` 로 한 번 부른다. */
232
- export const ghApiSearchRepos: SearchGithubRepos = async (query, perQuery) => {
233
- const perPage = Math.min(100, Math.max(1, perQuery));
234
- const q = `${query} sort:stars`;
235
- const out = execFileSync('gh', [
236
- 'api',
237
- '-X', 'GET',
238
- 'search/repositories',
239
- '-f', `q=${q}`,
240
- '-f', 'sort=stars',
241
- '-f', 'order=desc',
242
- '-f', `per_page=${perPage}`,
243
- '--jq', '.items',
244
- ], { encoding: 'utf8', timeout: 30_000 });
245
- const items = JSON.parse(out) as GhSearchItem[];
255
+ export function reposFromGhSearchItems(items: GhSearchItem[], perQuery: number): GithubStarRepo[] {
246
256
  if (!Array.isArray(items)) return [];
247
257
  const repos: GithubStarRepo[] = [];
248
258
  for (const item of items) {
@@ -254,7 +264,29 @@ export const ghApiSearchRepos: SearchGithubRepos = async (query, perQuery) => {
254
264
  stars: Number(item.stargazers_count ?? 0),
255
265
  createdAt: item.created_at ?? '',
256
266
  pushedAt: item.pushed_at ?? '',
267
+ ...(item.license === null
268
+ ? { license: '없음' }
269
+ : typeof item.license?.spdx_id === 'string' && item.license.spdx_id
270
+ ? { license: item.license.spdx_id }
271
+ : {}),
257
272
  });
258
273
  }
259
274
  return repos.slice(0, perQuery);
275
+ }
276
+
277
+ /** `gh api search/repositories` 를 `sort=stars` 로 한 번 부른다. */
278
+ export const ghApiSearchRepos: SearchGithubRepos = async (query, perQuery) => {
279
+ const perPage = Math.min(100, Math.max(1, perQuery));
280
+ const q = `${query} sort:stars`;
281
+ const out = execFileSync('gh', [
282
+ 'api',
283
+ '-X', 'GET',
284
+ 'search/repositories',
285
+ '-f', `q=${q}`,
286
+ '-f', 'sort=stars',
287
+ '-f', 'order=desc',
288
+ '-f', `per_page=${perPage}`,
289
+ '--jq', '.items',
290
+ ], { encoding: 'utf8', timeout: 30_000 });
291
+ return reposFromGhSearchItems(JSON.parse(out) as GhSearchItem[], perQuery);
260
292
  };
@@ -5,7 +5,7 @@ import { awaitGlobalPersonaLoad, getGlobalPersonaRegistry } from '../persona/glo
5
5
  import type { PersonaSource } from '../persona/mention-parser.js';
6
6
  import { dispatchCeoTask, type CeoCommandDeps } from '../seat-dispatch/ceo-commands.js';
7
7
  import { ceoTaskDeps, classifyCeoIntent } from '../seat-dispatch/ceo-intent.js';
8
- import { askSeat, parseSeatAsk, type SeatAskDeps } from '../seat-dispatch/seat-ask.js';
8
+ import { askSeat, askSeatAs, parseSeatAsk, type SeatAskDeps } from '../seat-dispatch/seat-ask.js';
9
9
  import { parseSeatAddress, resolveSeat } from '../seat-address/seat-address.js';
10
10
  import { getUserConfig, type UserConfig } from '../user-config.js';
11
11
  import { answerAsSeat } from './seat-answer.js';
@@ -110,7 +110,13 @@ export async function handleDiscordSeatWork(
110
110
  const seatId = canonicalSeatId(seats[0].seat.id);
111
111
  if (auth && ['OP', 'TC', 'MK', 'UX'].includes(seatId)) {
112
112
  const intent = classifyCeoIntent(body);
113
- if (intent === 'task') return sendTask(seatId, body, auth.cfg, auth.id);
113
+ if (intent === 'task') {
114
+ if (deps.askDeps) return askSeatAs(seatId as 'OP' | 'TC' | 'MK' | 'UX', body,
115
+ { channel: 'discord', channelId: msg.channelId, messageId: msg.messageId,
116
+ ...(msg.threadId ? { threadId: msg.threadId } : {}) },
117
+ deps.commandDeps ?? ceoTaskDeps(auth.cfg, auth.id), deps.askDeps);
118
+ return sendTask(seatId, body, auth.cfg, auth.id);
119
+ }
114
120
  const answer = await (deps.answer ?? answerAsSeat)(seats[0].name, body);
115
121
  debug.log('seat.dispatch', 'intent', { seat: seatId, intent, via: 'discord', outcome: answer ? 'answered' : 'intake' });
116
122
  if (answer) return answer.text;
@@ -0,0 +1,316 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { elanousStateRoot } from '../autopilot/state-paths.js';
5
+ import { normalizeClaim, type AddBriefItem } from '../briefing/brief-items.js';
6
+ import { createRequire } from 'node:module';
7
+ import { debug } from '../debug/log.js';
8
+ import { listChecklist, setItem, type ChecklistItem } from '../release-loop/checklist.js';
9
+ import { placeCell, type PlacementDeps } from '../release-loop/placement.js';
10
+ import { listSchedules } from '../release-loop/release-schedule.js';
11
+ import { getUserConfig } from '../user-config.js';
12
+ import { dateKey } from '../time/format.js';
13
+ import { cardIndexPath, CardStore, type TaskCard } from '../task-cards/card-store.js';
14
+ import { loadIntakeLedger, type IntakeItem } from './items.js';
15
+ import { parseRubric, rubricGrade, rubricScore } from '../release-loop/rubric.js';
16
+
17
+ export type RequirementSource = 'directive' | 'intake' | 'grounding' | 'coordination' | 'linear';
18
+ export interface RequirementCandidate {
19
+ id: string;
20
+ source: RequirementSource;
21
+ text: string;
22
+ link: string;
23
+ receivedAt: string;
24
+ priority?: 'P0' | 'P1' | 'P2';
25
+ risk?: number;
26
+ cost?: number;
27
+ security?: number;
28
+ license?: string;
29
+ patentPrivatePath?: boolean;
30
+ owner?: string;
31
+ deadlineVersion?: string;
32
+ }
33
+ export type IncomingRequirement = Omit<RequirementCandidate, 'source'>;
34
+ export interface RequirementSources {
35
+ directives?: readonly DirectiveRow[];
36
+ intake?: readonly IntakeItem[];
37
+ grounding?: readonly IncomingRequirement[];
38
+ coordination?: readonly IncomingRequirement[];
39
+ linear?: readonly IncomingRequirement[];
40
+ }
41
+ export interface RequirementDecision {
42
+ candidate: RequirementCandidate;
43
+ sources: RequirementCandidate[];
44
+ status: 'adopted' | 'would-adopt' | 'proposal';
45
+ cellId?: string;
46
+ version?: string;
47
+ rubric: string;
48
+ reason?: string;
49
+ }
50
+ export interface RequirementFunnelResult {
51
+ counts: Record<RequirementSource, number>;
52
+ candidates: number;
53
+ decisions: RequirementDecision[];
54
+ unlinkedDirectives: RequirementCandidate[];
55
+ briefItems: AddBriefItem[];
56
+ }
57
+ export interface FunnelOptions {
58
+ sources: RequirementSources;
59
+ /** Older directives are only checked for unlinked P0 backlog, not counted as today's arrivals. */
60
+ historicalDirectives?: readonly DirectiveRow[];
61
+ cells: readonly { version: string; item: ChecklistItem }[];
62
+ now: Date;
63
+ mode?: 'shadow' | 'live';
64
+ /** Required for live external adoption; the unresolved daily limit has no implicit value. */
65
+ dailyCap?: number;
66
+ adoptedToday?: number;
67
+ allowedLicenses?: readonly string[];
68
+ placement?: PlacementDeps;
69
+ adopt?: (candidate: RequirementCandidate, version: string, cellId: string, evidence: string) => void;
70
+ }
71
+
72
+ /** How many of the last 24 hours' unlinked directives the single P0 brief line names (the full backlog is one count). */
73
+ export const UNLINKED_DIRECTIVES_SHOWN = 5;
74
+ /** ABSORB-ADOPT daily limit when config sets none (OP 10-06 22:59 · the cell's own number). */
75
+ export const DEFAULT_DAILY_CAP = 5;
76
+ /** Directive row fields the funnel reads — kept local because `src/directives/directive-index` is excluded from the public export. */
77
+ export interface DirectiveRow { ts: string; track: string | null; text: string; source_file: string; line_no: number }
78
+
79
+ /** Loads the private directive index at run time only (same pattern as decisions/proact-meter), so public builds bundle cleanly. */
80
+ function openDirectiveIndex(stateDir: string): { dailyRequirements(since: string, until: string): DirectiveRow[]; close(): void } {
81
+ const spec = ['..', 'directives', 'directive-index.js'].join('/');
82
+ const mod = createRequire(import.meta.url)(spec) as { DirectiveIndex: new (o: { stateDir?: string }) => ReturnType<typeof openDirectiveIndex> };
83
+ return new mod.DirectiveIndex({ stateDir });
84
+ }
85
+
86
+ const oneLine = (value: string) => value.replace(/\s+/g, ' ').trim();
87
+ const idFor = (source: RequirementSource, key: string) => createHash('sha256').update(`${source}:${key}`).digest('hex').slice(0, 16);
88
+
89
+ export function directiveCandidates(rows: readonly DirectiveRow[]): RequirementCandidate[] {
90
+ return rows.map(row => ({ id: idFor('directive', `${row.source_file}:${row.line_no}`), source: 'directive',
91
+ text: oneLine(row.text), link: `${row.source_file}#L${row.line_no}`, receivedAt: row.ts,
92
+ priority: 'P2', owner: row.track && /^[A-Z]{2,8}$/.test(row.track) ? row.track : 'OP' }));
93
+ }
94
+ export function intakeCandidates(rows: readonly IntakeItem[]): RequirementCandidate[] {
95
+ return rows.map(row => ({ id: row.id, source: 'intake', text: oneLine(row.text || row.title || ''),
96
+ link: row.url || `intake:${row.id}`, receivedAt: row.observedAt }));
97
+ }
98
+ export function suppliedCandidates(source: 'grounding' | 'coordination' | 'linear', rows: readonly IncomingRequirement[]): RequirementCandidate[] {
99
+ return rows.map(row => ({ ...row, source, text: oneLine(row.text) }));
100
+ }
101
+
102
+ function linkFor(candidate: RequirementCandidate, cells: FunnelOptions['cells']): FunnelOptions['cells'][number] | undefined {
103
+ return cells.find(({ item }) => !!candidate.link && !!item.evidence && item.evidence.includes(candidate.link));
104
+ }
105
+ function gate(candidate: RequirementCandidate, options: FunnelOptions, used: number): string | null {
106
+ if (candidate.priority !== 'P1' && candidate.priority !== 'P2') return 'P1/P2 판정 없음';
107
+ if (candidate.priority === 'P1' && !candidate.deadlineVersion) return 'P1 마감 판 미확인';
108
+ if (candidate.risk === undefined || !Number.isFinite(candidate.risk) || candidate.risk > 1 || candidate.risk < 0) return '위험≤1 미확인';
109
+ if (candidate.cost !== 0) return '돈 0 미확인';
110
+ if (candidate.security !== 0) return '보안 0 미확인';
111
+ if (!candidate.license || !options.allowedLicenses?.includes(candidate.license)) return '허용 라이선스 미확인';
112
+ if (candidate.patentPrivatePath !== false) return '특허 비공개 경로 무접촉 미확인';
113
+ if (options.dailyCap === undefined || !Number.isSafeInteger(options.dailyCap) || options.dailyCap < 0) return '하루 상한 미설정';
114
+ if (used >= options.dailyCap) return '하루 상한 도달';
115
+ return null;
116
+ }
117
+
118
+ /** RUBRIC-PLACE: use the release placement engine's priority, deadline, capacity and predecessor checks, not an invented score. */
119
+ export function runRequirementFunnel(options: FunnelOptions): RequirementFunnelResult {
120
+ const { sources, cells, now } = options;
121
+ const arrivals = [
122
+ ...directiveCandidates(sources.directives ?? []), ...intakeCandidates(sources.intake ?? []),
123
+ ...suppliedCandidates('grounding', sources.grounding ?? []),
124
+ ...suppliedCandidates('coordination', sources.coordination ?? []),
125
+ ...suppliedCandidates('linear', sources.linear ?? []),
126
+ ].filter(candidate => candidate.text && Number.isFinite(Date.parse(candidate.receivedAt)) && Date.parse(candidate.receivedAt) <= now.getTime());
127
+ const counts: RequirementFunnelResult['counts'] = { directive: 0, intake: 0, grounding: 0, coordination: 0, linear: 0 };
128
+ for (const candidate of arrivals) counts[candidate.source]++;
129
+ const groups = new Map<string, RequirementCandidate[]>();
130
+ for (const candidate of arrivals) {
131
+ const key = normalizeClaim(candidate.text);
132
+ groups.set(key, [...(groups.get(key) ?? []), candidate]);
133
+ }
134
+ const decisions: RequirementDecision[] = [];
135
+ let used = options.adoptedToday ?? 0;
136
+ for (const members of groups.values()) {
137
+ members.sort((a, b) => a.receivedAt.localeCompare(b.receivedAt));
138
+ const candidate = members.find(member => member.source === 'directive') ?? members[0]!;
139
+ const linked = members.map(member => linkFor(member, cells)).find(Boolean);
140
+ const evidence = members.map(member => `${member.link} (${member.receivedAt})`).join(' · ');
141
+ if (linked) {
142
+ decisions.push({ candidate, sources: members, status: 'adopted', cellId: linked.item.id, version: linked.version,
143
+ rubric: `RUBRIC-PLACE ${linked.item.priority ?? '미지정'} · ${linked.version}` });
144
+ continue;
145
+ }
146
+ const isDirective = members.some(member => member.source === 'directive');
147
+ const reason = isDirective ? null : gate(candidate, options, used);
148
+ if (reason) {
149
+ decisions.push({ candidate, sources: members, status: 'proposal', reason, rubric: `ABSORB-ADOPT · ${reason}` });
150
+ continue;
151
+ }
152
+ const cellId = candidate.id;
153
+ const priority = candidate.priority ?? 'P1';
154
+ const rubricInput = { id: cellId, title: candidate.text, owner: candidate.owner ?? 'OP', priority,
155
+ predecessors: [], ...(priority === 'P1' && candidate.deadlineVersion ? { deadlineVersion: candidate.deadlineVersion } : {}) };
156
+ let placement: ReturnType<typeof placeCell>;
157
+ try { placement = placeCell(rubricInput, { ...options.placement, now, dryRun: true }); }
158
+ catch (error) {
159
+ const reason = error instanceof Error ? error.message : String(error);
160
+ decisions.push({ candidate, sources: members, status: isDirective ? 'would-adopt' : 'proposal', reason,
161
+ rubric: `RUBRIC-PLACE · 판 배치 보류: ${reason}` });
162
+ continue;
163
+ }
164
+ const rubric = `RUBRIC-PLACE ${priority} · ${placement.version}: ${placement.reason}`;
165
+ if (options.mode === 'live') {
166
+ if (!options.adopt) throw new Error('편입 기록자가 설정되지 않았다');
167
+ options.adopt(candidate, placement.version, cellId, evidence);
168
+ }
169
+ decisions.push({ candidate, sources: members, status: options.mode === 'live' ? 'adopted' : 'would-adopt',
170
+ cellId, version: placement.version, rubric });
171
+ if (!isDirective) used++;
172
+ }
173
+ const unlinkedDirectives = [...directiveCandidates(options.historicalDirectives ?? []).filter(candidate =>
174
+ candidate.text && Number.isFinite(Date.parse(candidate.receivedAt)) && Date.parse(candidate.receivedAt) <= now.getTime()
175
+ && !linkFor(candidate, cells) && !arrivals.some(arrival => arrival.id === candidate.id)),
176
+ ...decisions.filter(decision => decision.status !== 'adopted')
177
+ .flatMap(decision => decision.sources.filter(source => source.source === 'directive'))]
178
+ .sort((a, b) => a.receivedAt.localeCompare(b.receivedAt));
179
+ const briefItems: AddBriefItem[] = [];
180
+ const summary = `들어온 요구 ${groups.size}(문별 지시 ${counts.directive} · 흡수 ${counts.intake} · 그라운딩 ${counts.grounding} · 조율 ${counts.coordination} · Linear ${counts.linear}) · 편입 ${decisions.filter(d => d.status === 'adopted').length} · 했을 것 ${decisions.filter(d => d.status === 'would-adopt').length} · 제안 ${decisions.filter(d => d.status === 'proposal').length}`;
181
+ if (arrivals.length || unlinkedDirectives.length) briefItems.push({ text: summary, domain: '흡수', priority: 'P1', source: 'requirement-funnel' });
182
+ for (const decision of decisions) {
183
+ const { candidate } = decision;
184
+ const status = decision.status === 'adopted' ? `편입 ${decision.cellId} · 판 ${decision.version}`
185
+ : decision.status === 'would-adopt' ? decision.version ? `했을 것 ${decision.cellId} · 판 ${decision.version}` : '편입 대상 · 판 배치 대기'
186
+ : `제안: ${decision.reason}`;
187
+ briefItems.push({ text: `${status} · ${candidate.text} · ${decision.rubric}`, domain: decision.status === 'proposal' ? '흡수' : '판',
188
+ priority: 'P1', source: 'requirement-funnel', evidence: decision.sources.map(member => `${member.link} (${member.receivedAt})`).join(' · ') });
189
+ }
190
+ // OP 10-06 22:59: the backlog can hold the whole directive history, so only the last 24 hours get named (one P0 line);
191
+ // the full backlog is a single count. Nothing recent → the count alone, at P1, so history cannot flood the brief.
192
+ if (unlinkedDirectives.length) {
193
+ const recent = unlinkedDirectives.filter(directive => now.getTime() - Date.parse(directive.receivedAt) <= 24 * 3_600_000);
194
+ const named = recent.slice(0, UNLINKED_DIRECTIVES_SHOWN);
195
+ const rest = recent.length - named.length;
196
+ const total = `전체 누계 ${unlinkedDirectives.length}`;
197
+ briefItems.push(recent.length
198
+ ? { text: `대표 지시 중 아직 칸 없는 것 — 최근 24시간 ${recent.length}: ${named.map(directive => `${directive.text} (${directive.receivedAt})`).join(' · ')}${rest > 0 ? ` · 외 ${rest}` : ''} · ${total}`,
199
+ domain: '판', priority: 'P0', source: 'requirement-funnel', evidence: named.map(directive => directive.link).join(' · ') }
200
+ : { text: `대표 지시 중 아직 칸 없는 것 — 최근 24시간 0 · ${total}`, domain: '판', priority: 'P1', source: 'requirement-funnel' });
201
+ }
202
+ return { counts, candidates: groups.size, decisions, unlinkedDirectives, briefItems };
203
+ }
204
+
205
+ /** Existing ledgers only; no source collector or sender is started here. */
206
+ export function readRequirementFunnel(options: { stateDir?: string; now?: Date; mode?: 'shadow' | 'live'; dailyCap?: number;
207
+ allowedLicenses?: readonly string[]; grounding?: readonly IncomingRequirement[]; coordination?: readonly IncomingRequirement[];
208
+ linear?: readonly IncomingRequirement[]; placement?: PlacementDeps } = {}): RequirementFunnelResult {
209
+ const stateDir = options.stateDir ?? elanousStateRoot();
210
+ const now = options.now ?? new Date();
211
+ const config = (getUserConfig().raw.intake as { requirementFunnel?: { mode?: 'shadow' | 'live'; dailyCap?: number; allowedLicenses?: string[] } } | undefined)?.requirementFunnel;
212
+ const mode = options.mode ?? config?.mode ?? 'shadow';
213
+ if (mode !== 'shadow' && mode !== 'live') throw new Error(`알 수 없는 요구 깔때기 모드: ${mode}`);
214
+ const dailyCap = options.dailyCap ?? config?.dailyCap ?? DEFAULT_DAILY_CAP;
215
+ const allowedLicenses = options.allowedLicenses ?? config?.allowedLicenses;
216
+ let directives: DirectiveRow[] = [];
217
+ let historicalDirectives: DirectiveRow[] = [];
218
+ const since = new Date(Date.parse(`${dateKey(now, { timeZone: 'Asia/Seoul' })}T00:00:00+09:00`)).toISOString();
219
+ try {
220
+ const index = openDirectiveIndex(stateDir);
221
+ try {
222
+ directives = index.dailyRequirements(since, now.toISOString());
223
+ historicalDirectives = index.dailyRequirements('1970-01-01T00:00:00.000Z', since);
224
+ } finally { index.close(); }
225
+ } catch (error) {
226
+ // Public builds have no directive index; the other doors still report.
227
+ debug.log('intake.requirement-funnel', 'directives-unavailable', { error: error instanceof Error ? error.message : String(error) });
228
+ }
229
+ const schedules = listSchedules();
230
+ const cells = schedules.flatMap(schedule => listChecklist(schedule.version).items.map(item => ({ version: schedule.version, item })));
231
+ const ledger = loadIntakeLedger(stateDir);
232
+ const day = dateKey(now, { timeZone: 'Asia/Seoul' });
233
+ const todayCount = cells.filter(({ item }) => {
234
+ const adoptedAt = item.evidence?.match(/ABSORB-ADOPT@(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z)/)?.[1];
235
+ return adoptedAt && dateKey(adoptedAt, { timeZone: 'Asia/Seoul' }) === day;
236
+ }).length;
237
+ const cardRequests: Record<'coordination' | 'linear', IncomingRequirement[]> = { coordination: [], linear: [] };
238
+ if (existsSync(cardIndexPath(stateDir))) {
239
+ const cards = new CardStore(stateDir, true);
240
+ try {
241
+ for (const card of cards.listCards()) {
242
+ const section = card.sections.find(entry => entry.key === 'intake');
243
+ if (!section) continue;
244
+ let raw: unknown;
245
+ try { raw = JSON.parse(section.content); } catch { continue; }
246
+ if (!raw || typeof raw !== 'object') continue;
247
+ const input = raw as { source?: string; text?: string; ref?: string; replyTo?: { surface?: string } };
248
+ const source = input.source === 'linear' ? 'linear' : input.source === 'coordination' ? 'coordination' : null;
249
+ if (!source || !input.text) continue;
250
+ cardRequests[source].push({ id: card.goalId, text: oneLine(input.text.split('\n')[0] ?? ''),
251
+ link: input.ref || card.goalId, receivedAt: card.createdAt });
252
+ }
253
+ } finally { cards.close(); }
254
+ }
255
+ const fromQueue = (name: string): IncomingRequirement[] => {
256
+ const path = join(stateDir, 'intake', name);
257
+ if (!existsSync(path)) return [];
258
+ return readFileSync(path, 'utf8').split('\n').flatMap(line => {
259
+ if (!line.trim()) return [];
260
+ try {
261
+ const value: unknown = JSON.parse(line);
262
+ if (!value || typeof value !== 'object') return [];
263
+ const row = value as Partial<IncomingRequirement> & { path?: string };
264
+ const item = row.id ? ledger.items.get(row.id) : undefined;
265
+ const text = row.text ?? item?.text ?? item?.title;
266
+ const link = row.link ?? item?.url ?? row.path;
267
+ const receivedAt = row.receivedAt ?? (row as { at?: string }).at ?? item?.observedAt;
268
+ return typeof row.id === 'string' && typeof text === 'string' && typeof link === 'string' && typeof receivedAt === 'string'
269
+ ? [{ ...row, id: row.id, text, link, receivedAt }] : [];
270
+ } catch { return []; }
271
+ });
272
+ };
273
+ // The grounding queue and the card store are cumulative; only today's rows are today's arrivals.
274
+ const today = (rows: readonly IncomingRequirement[]) => rows.filter(row => Date.parse(row.receivedAt) >= Date.parse(since));
275
+ return runRequirementFunnel({ sources: { directives, intake: [...ledger.items.values()].filter(item => Date.parse(item.observedAt) >= Date.parse(since)),
276
+ grounding: today(options.grounding ?? fromQueue('outbox/grounding.jsonl')),
277
+ coordination: today(options.coordination ?? cardRequests.coordination), linear: today(options.linear ?? cardRequests.linear) }, historicalDirectives, cells, now, mode,
278
+ dailyCap, adoptedToday: todayCount, allowedLicenses, placement: { ...options.placement, schedules },
279
+ adopt: (candidate, version, cellId, evidence) => {
280
+ placeCell({ id: cellId, title: candidate.text, owner: candidate.owner ?? 'OP', priority: candidate.priority ?? 'P1',
281
+ predecessors: [], ...(candidate.deadlineVersion ? { deadlineVersion: candidate.deadlineVersion } : {}) }, { ...options.placement, now, schedules });
282
+ setItem(version, cellId, { evidence: `${evidence} · ABSORB-ADOPT@${now.toISOString()}` }, 'OP');
283
+ } });
284
+ }
285
+
286
+ // ── `intake candidates` — 읽기 전용 요구 후보 목록(지시·흡수·소원 · 명시 루브릭만 점수) ──
287
+ export interface FunnelCandidate {
288
+ source: 'directive' | 'intake' | 'wish';
289
+ id: string;
290
+ title: string;
291
+ score: number | null;
292
+ grade: 'P1' | 'P2' | 'P3' | 'P4' | null;
293
+ }
294
+ /** The candidate list keys directives by session — the funnel's local `DirectiveRow` plus the index's `session_id`. */
295
+ export type FunnelDirectiveRow = DirectiveRow & { session_id: string };
296
+
297
+ function funnelCandidate(source: FunnelCandidate['source'], id: string, title: string, original: string): FunnelCandidate {
298
+ const rubric = parseRubric(original);
299
+ const score = rubric === null ? null : rubricScore(rubric);
300
+ return { source, id, title: title.split(/\r?\n/, 1)[0]!.slice(0, 80), score, grade: score === null ? null : rubricGrade(score) };
301
+ }
302
+
303
+ export function collectFunnelCandidates({ directives, intakeItems, cards }: {
304
+ directives: readonly FunnelDirectiveRow[];
305
+ intakeItems: readonly IntakeItem[];
306
+ cards: readonly TaskCard[];
307
+ }): FunnelCandidate[] {
308
+ return [
309
+ ...directives.map((row) => funnelCandidate('directive', `${row.session_id}:${row.line_no}`, row.text, row.text)),
310
+ ...intakeItems.filter((item) => item.status !== 'discarded')
311
+ .map((item) => funnelCandidate('intake', item.id, item.title ?? item.text ?? item.url ?? '', [item.title, item.text, item.url].filter(Boolean).join('\n'))),
312
+ ...cards.filter((card) => card.status === 'open' && card.goalId.startsWith('wish:'))
313
+ .map((card) => funnelCandidate('wish', card.goalId, card.title, card.title)),
314
+ ].sort((a, b) => a.score === null && b.score === null ? 0
315
+ : a.score === null ? 1 : b.score === null ? -1 : b.score - a.score);
316
+ }
@@ -1,6 +1,6 @@
1
1
  import { createRequire } from 'node:module';
2
2
  import { LessonLedger, type LessonRow } from '../lessons/lesson-ledger.js';
3
- import { DecisionLedger, type DecisionEntry } from '../decisions/decision-ledger.js';
3
+ import { DecisionLedger, type DecisionEntry, type SeatDecisionRecord } from '../decisions/decision-ledger.js';
4
4
  import { devVersion, listChecklist, type Checklist } from '../release-loop/checklist.js';
5
5
  import { debug } from '../debug/log.js';
6
6
 
@@ -31,6 +31,7 @@ export interface KnowResult {
31
31
  export interface KnowFindDeps {
32
32
  directives: (query: string) => DirectiveRow[];
33
33
  decisions: () => DecisionEntry[];
34
+ seatDecisions: () => SeatDecisionRecord[];
34
35
  checklist: (version: string) => Checklist;
35
36
  lessons: (query: string) => LessonRow[];
36
37
  versions: () => string[];
@@ -49,6 +50,7 @@ const defaults: KnowFindDeps = {
49
50
  try { return index.search(query); } finally { index.close(); }
50
51
  },
51
52
  decisions: () => new DecisionLedger().list({ status: 'all' }),
53
+ seatDecisions: () => new DecisionLedger().seatReport(),
52
54
  checklist: (version) => listChecklist(version),
53
55
  lessons: (query) => new LessonLedger().find(query),
54
56
  versions,
@@ -84,6 +86,13 @@ export function knowFind(query: string, deps: Partial<KnowFindDeps> = {}): KnowR
84
86
  ref: row.refs?.[0] ?? `decisions show ${row.id}` });
85
87
  }
86
88
  });
89
+ attempt('decision', () => {
90
+ for (const row of read.seatDecisions()) {
91
+ if (!matches([row.title, row.decision, row.delegation].join(' '), words)) continue;
92
+ rows.push({ source: 'decision', id: row.id, title: row.title, status: 'seat-posthoc',
93
+ at: row.decidedAt ?? row.recordedAt, current: false, ref: `decisions seat-report --seat ${row.seat}` });
94
+ }
95
+ });
87
96
  attempt('checklist', () => {
88
97
  for (const version of new Set(read.versions())) {
89
98
  for (const row of read.checklist(version).items) {