elanous 0.2.19 → 0.2.20

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 (388) 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/{J5H7BvqgSI8DUjtRRcA_S → Bam1YziK2iH9bN4K0utGk}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 1268.0840ff91e083fcbd.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/1307-77fad80cf54477c6.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1612-f60c0c93ec773f81.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1844-9ec4b1d12f6e80eb.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/1866-e7a6999f25548b30.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/1948.15f356b15a625597.js +5 -0
  11. package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/{2977-e38442e86289f1d3.js → 211-9c9090c59e2c5907.js} +1 -1
  14. package/apps/pwa/out/_next/static/chunks/2116-6429c188eb846ab8.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/224-e7be8f762fe74986.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/2489-ff55a35b202c0f94.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/2549-89c518a4c8ed4d00.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/2957-96ffab3d521fb08f.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/3857-995ea9f5ef33d71e.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/4144-fad095b48a1862ee.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/4795-f372490b178583fc.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/5360-be1f1d8f9fcc9755.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/5755-ea7ed88eb956f47b.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/5977-94ed6f78e9814b2b.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/{4847.56f5bc454fbcc827.js → 6110.1297d4093fdfbb0d.js} +2 -2
  35. package/apps/pwa/out/_next/static/chunks/6158.af2e93b7adff709b.js +2 -0
  36. package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/{1470.98a6ab61ccb4131c.js → 726.83532d69452ee24b.js} +1 -1
  38. package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 7327-2d69fb321ba73389.js} +1 -1
  40. package/apps/pwa/out/_next/static/chunks/{8870-9614a213b990b10b.js → 7748-6e51bfe927019f5c.js} +3 -3
  41. package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/8401-35e94e9091f5eb8d.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/9070.067c106528ec1ae6.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/9103-60fbef33e66f0a46.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/9247-c1753a059e7bac08.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/{7647.2fcd30d894d5ee30.js → 935.4ddaa99fc1d1126c.js} +1 -1
  51. package/apps/pwa/out/_next/static/chunks/{ae787e4c-e5b1ef00b2dbceca.js → a9f2b54d-33c0689b70ed8cf3.js} +1 -1
  52. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/approvals/page-c855c24de7db74fe.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-85b316bb4235980b.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/board/page-9b2b26a33cb2cd42.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/app/botlab/page-c9955fde67b1bcb5.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/bots/page-7a852956c74015a3.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/ceo/page-c5a5c621be0e04ae.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/chat/page-4ba4abe096b6c2a8.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/consult/page-575d4a8b4f7becb2.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/control/page-f5d56ff5f45ad164.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/decisions/page-2260e9eef0357421.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/design-check/page-7c058069c5e7fa63.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/editor/page-dc5da0b8a2edb8a4.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/exec/page-466bf0ee16ad1432.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/field/page-a23bc71713bcc08f.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/inside/{page-5b29d9b06ceadade.js → page-ed4a6f799f005d09.js} +1 -1
  69. package/apps/pwa/out/_next/static/chunks/app/intake/page-9cd565005b93aca6.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/layout-55f0f40c6db95f15.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/live/page-474cb5888ea0e8f3.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/loops/page-35bfec2e631d3eae.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/market/page-9ca21f9ad3208a61.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-f9ccb1b1adbea14d.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/morning/page-ec102c9b8b691e37.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/not-found-a9c2cfa8dda922a5.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/observatory/page-6da35144878feb6d.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-f6055495e9ed7ea8.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-25204694fa9984a5.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-a60e035f151d84d3.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/outputs/page-189c587f36874538.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/page-c56634bde705a65d.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/reflection/page-db03ae3589a81c9e.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-4a6463b97f0ad94f.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/app/sessions/page-75bffecc9aa1004e.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a8d2fe0c669e0de4.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/app/settings/page-31d5b875760bb62d.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-c26fa10aac6c21c4.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/app/setup/page-68793c6e10331d66.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/app/share/page-46d5201d2207a511.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/app/showroom/page-d81c07a7b4f618ae.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/app/tasks/page-be1c4e8697f22360.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/app/term/page-cf0a4b4abead71ea.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/app/today/page-a4717e2c4bf7a28f.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/app/trace/page-0fd895fab9b62c1b.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/app/vault/page-8b6c1454a8e8f306.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-c6e4364d9de8f717.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/app/workflows/page-0acc8e198f7b0dfb.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/app/workspace/page-34882c84f84021c5.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-0506a1e0e4bdb2f5.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → f6231284-45726550aba08fca.js} +1 -1
  103. package/apps/pwa/out/_next/static/chunks/{2ea96958.e8c7e112de50b067.js → f6b7c9ba.64a895b8b2fedbdc.js} +1 -1
  104. package/apps/pwa/out/_next/static/chunks/framework-5c169757551134ed.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +1 -0
  106. package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/webpack-c260571513bf43c2.js +1 -0
  110. package/apps/pwa/out/_next/static/css/c23d959d3ee15e57.css +1 -0
  111. package/apps/pwa/out/approvals/index.html +1 -1
  112. package/apps/pwa/out/approvals/index.txt +23 -23
  113. package/apps/pwa/out/autopilot/index.html +1 -1
  114. package/apps/pwa/out/autopilot/index.txt +23 -23
  115. package/apps/pwa/out/board/index.html +1 -1
  116. package/apps/pwa/out/board/index.txt +23 -23
  117. package/apps/pwa/out/botlab/index.html +1 -1
  118. package/apps/pwa/out/botlab/index.txt +22 -22
  119. package/apps/pwa/out/bots/index.html +1 -1
  120. package/apps/pwa/out/bots/index.txt +22 -22
  121. package/apps/pwa/out/ceo/index.html +1 -1
  122. package/apps/pwa/out/ceo/index.txt +23 -23
  123. package/apps/pwa/out/chat/index.html +1 -1
  124. package/apps/pwa/out/chat/index.txt +22 -22
  125. package/apps/pwa/out/consult/index.html +1 -1
  126. package/apps/pwa/out/consult/index.txt +23 -23
  127. package/apps/pwa/out/control/index.html +1 -1
  128. package/apps/pwa/out/control/index.txt +23 -23
  129. package/apps/pwa/out/decisions/index.html +1 -1
  130. package/apps/pwa/out/decisions/index.txt +22 -22
  131. package/apps/pwa/out/design-check/index.html +1 -1
  132. package/apps/pwa/out/design-check/index.txt +23 -23
  133. package/apps/pwa/out/editor/index.html +1 -1
  134. package/apps/pwa/out/editor/index.txt +23 -23
  135. package/apps/pwa/out/exec/index.html +1 -1
  136. package/apps/pwa/out/exec/index.txt +23 -23
  137. package/apps/pwa/out/field/index.html +1 -1
  138. package/apps/pwa/out/field/index.txt +23 -23
  139. package/apps/pwa/out/healthz +1 -1
  140. package/apps/pwa/out/index.html +1 -1
  141. package/apps/pwa/out/index.txt +22 -22
  142. package/apps/pwa/out/inside/index.html +1 -1
  143. package/apps/pwa/out/inside/index.txt +22 -22
  144. package/apps/pwa/out/intake/index.html +1 -1
  145. package/apps/pwa/out/intake/index.txt +23 -23
  146. package/apps/pwa/out/live/index.html +1 -1
  147. package/apps/pwa/out/live/index.txt +23 -23
  148. package/apps/pwa/out/loops/index.html +1 -1
  149. package/apps/pwa/out/loops/index.txt +22 -22
  150. package/apps/pwa/out/market/index.html +1 -1
  151. package/apps/pwa/out/market/index.txt +22 -22
  152. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  153. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  154. package/apps/pwa/out/morning/index.html +1 -1
  155. package/apps/pwa/out/morning/index.txt +23 -23
  156. package/apps/pwa/out/observatory/index.html +1 -1
  157. package/apps/pwa/out/observatory/index.txt +23 -23
  158. package/apps/pwa/out/ops/checklist/index.html +1 -1
  159. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  160. package/apps/pwa/out/ops/release/index.html +1 -1
  161. package/apps/pwa/out/ops/release/index.txt +22 -22
  162. package/apps/pwa/out/ops/seats/index.html +1 -1
  163. package/apps/pwa/out/ops/seats/index.txt +22 -22
  164. package/apps/pwa/out/outputs/index.html +1 -1
  165. package/apps/pwa/out/outputs/index.txt +22 -22
  166. package/apps/pwa/out/reflection/index.html +1 -1
  167. package/apps/pwa/out/reflection/index.txt +23 -23
  168. package/apps/pwa/out/scheduler/index.html +1 -1
  169. package/apps/pwa/out/scheduler/index.txt +23 -23
  170. package/apps/pwa/out/sessions/index.html +1 -1
  171. package/apps/pwa/out/sessions/index.txt +22 -22
  172. package/apps/pwa/out/settings/devices/index.html +1 -1
  173. package/apps/pwa/out/settings/devices/index.txt +23 -23
  174. package/apps/pwa/out/settings/index.html +1 -1
  175. package/apps/pwa/out/settings/index.txt +23 -23
  176. package/apps/pwa/out/setup/done/index.html +1 -1
  177. package/apps/pwa/out/setup/done/index.txt +23 -23
  178. package/apps/pwa/out/setup/index.html +1 -1
  179. package/apps/pwa/out/setup/index.txt +23 -23
  180. package/apps/pwa/out/share/index.html +1 -1
  181. package/apps/pwa/out/share/index.txt +23 -23
  182. package/apps/pwa/out/showroom/index.html +1 -1
  183. package/apps/pwa/out/showroom/index.txt +22 -22
  184. package/apps/pwa/out/tasks/index.html +1 -1
  185. package/apps/pwa/out/tasks/index.txt +23 -23
  186. package/apps/pwa/out/term/index.html +1 -1
  187. package/apps/pwa/out/term/index.txt +23 -23
  188. package/apps/pwa/out/today/index.html +1 -1
  189. package/apps/pwa/out/today/index.txt +23 -23
  190. package/apps/pwa/out/trace/index.html +1 -1
  191. package/apps/pwa/out/trace/index.txt +23 -23
  192. package/apps/pwa/out/vault/index.html +1 -1
  193. package/apps/pwa/out/vault/index.txt +23 -23
  194. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  195. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  196. package/apps/pwa/out/workflows/index.html +1 -1
  197. package/apps/pwa/out/workflows/index.txt +23 -23
  198. package/apps/pwa/out/workspace/index.html +1 -1
  199. package/apps/pwa/out/workspace/index.txt +23 -23
  200. package/apps/pwa/out/worktrees/index.html +1 -1
  201. package/apps/pwa/out/worktrees/index.txt +23 -23
  202. package/graphs/demo/editor-recipes.yaml +22 -0
  203. package/graphs/retro/daily.yaml +25 -0
  204. package/graphs/retro/recipes.yaml +24 -0
  205. package/package.json +1 -1
  206. package/scripts/ask-marker-check.ts +3 -1
  207. package/scripts/land-global-checks.ts +89 -0
  208. package/scripts/lib/pch-regress-map.ts +2 -1
  209. package/scripts/release-loop/gate-node.ts +296 -25
  210. package/scripts/release-loop/gate-timing.ts +116 -7
  211. package/scripts/release-loop/preflight.ts +6 -4
  212. package/scripts/release-loop/publish-window.ts +141 -0
  213. package/scripts/release-loop/release-docs.ts +1 -4
  214. package/scripts/release-loop/release-universe.ts +38 -0
  215. package/scripts/release-loop/resume-release.ts +4 -1
  216. package/scripts/release-loop/unattended-release.ts +10 -2
  217. package/scripts/release-story/draft.ts +5 -1
  218. package/scripts/retro/shadow-compare.ts +71 -0
  219. package/scripts/rhythm/daily.ts +6 -1
  220. package/src/agent-mission/driver.ts +34 -6
  221. package/src/autopilot/build/llm-conflict-merge.ts +132 -7
  222. package/src/autopilot/build/merge-intent.ts +135 -7
  223. package/src/away/release-watch.ts +20 -3
  224. package/src/cli/config-drift.ts +513 -0
  225. package/src/cli/log-event-names.ts +4 -0
  226. package/src/cli/pr-cli.ts +20 -0
  227. package/src/cli/release-cli.ts +20 -5
  228. package/src/cli/retro-cli.ts +78 -0
  229. package/src/cli/tasks-cli.ts +94 -8
  230. package/src/dashboard/slash-runtime/dashboard-handlers.ts +5 -4
  231. package/src/dashboard/slash-runtime/help-from-registry.ts +57 -31
  232. package/src/domains/outbound-log-probe.ts +6 -1
  233. package/src/graph-runner/graph-run-api.ts +205 -0
  234. package/src/graph-runner/runner.ts +11 -1
  235. package/src/harness/harness-cli-command.ts +419 -52
  236. package/src/harness/harness-queue.ts +315 -25
  237. package/src/harness/harness-salvage-cli.ts +62 -0
  238. package/src/hq/fence-outcomes.ts +63 -0
  239. package/src/hq/fence-wrapper.ts +33 -0
  240. package/src/hq/hq.ts +11 -1
  241. package/src/index.ts +69 -6
  242. package/src/maturity/feature-maturity.ts +2 -2
  243. package/src/nexus/api/graph-kinds.ts +10 -4
  244. package/src/nexus/api/graphs-api.ts +178 -6
  245. package/src/nexus/api/http-server.ts +38 -3
  246. package/src/nexus/api/loop-edges.ts +4 -2
  247. package/src/nexus/api/ops-api.ts +22 -7
  248. package/src/nexus/api/ops-board-route.ts +48 -0
  249. package/src/onboarding/entry-hints.ts +5 -0
  250. package/src/onboarding.ts +24 -6
  251. package/src/release-loop/feature-store.ts +14 -11
  252. package/src/release-loop/gate-shards.ts +158 -0
  253. package/src/release-loop/release-schedule.ts +6 -2
  254. package/src/retro/daily.ts +372 -0
  255. package/src/seat-loop/op-decide.ts +173 -0
  256. package/src/seat-loop/seat-loop.ts +60 -3
  257. package/src/seat-loop/seat-report.ts +58 -3
  258. package/src/self-dev/ask-launch-flow.ts +5 -4
  259. package/src/self-dev/ask-launch-io.ts +49 -1
  260. package/src/self-dev/ask-path-candidates.ts +40 -2
  261. package/src/self-dev/draft-sweep.ts +371 -16
  262. package/src/self-dev/draft-triage-rules.ts +19 -4
  263. package/src/self-dev/pr-preview.ts +84 -16
  264. package/src/self-implement/blocked-draft-policy.ts +101 -0
  265. package/src/self-implement/execution-clarification.ts +11 -0
  266. package/src/self-implement/gate-env-retry.ts +83 -0
  267. package/src/self-implement/orchestrator.ts +218 -5
  268. package/src/self-implement/run-stop.ts +123 -0
  269. package/src/self-implement/run-ttl.ts +258 -0
  270. package/src/self-implement/salvage-retention.ts +123 -0
  271. package/src/self-implement/seams.ts +4 -0
  272. package/src/self-implement/self-implement-cli.ts +2 -0
  273. package/src/self-implement/sibling-resync.ts +223 -0
  274. package/src/self-implement/stop-autoheal.ts +204 -0
  275. package/src/task-agent/board.ts +1 -1
  276. package/src/task-agent/card-evidence.ts +99 -10
  277. package/src/task-agent/judge.ts +7 -0
  278. package/src/task-agent/live-moves.ts +120 -20
  279. package/src/task-agent/mission.ts +1 -1
  280. package/src/task-agent/shadow.ts +10 -1
  281. package/src/task-agent/task-hand.ts +7 -2
  282. package/src/task-orchestrator/surfaces/pod-command-job.ts +19 -5
  283. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +4 -0
  284. package/src/task-orchestrator/surfaces/pod-install-slots.ts +89 -0
  285. package/src/task-orchestrator/surfaces/pod-lease.ts +93 -10
  286. package/src/task-orchestrator/surfaces/pod-pool.ts +147 -34
  287. package/src/task-orchestrator/surfaces/self-implement-pod.ts +19 -3
  288. package/src/telegram-commands.ts +41 -0
  289. package/src/testing/real-cli-spawn.ts +106 -0
  290. package/src/user-config.ts +56 -3
  291. package/src/version/packed-revision.json +1 -1
  292. package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
  293. package/apps/pwa/out/_next/static/chunks/1157-cba0e8553d35a980.js +0 -1
  294. package/apps/pwa/out/_next/static/chunks/171-23a877ed11720de9.js +0 -1
  295. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
  296. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
  297. package/apps/pwa/out/_next/static/chunks/2336-e830c777eb6bce04.js +0 -1
  298. package/apps/pwa/out/_next/static/chunks/2375-e061bb24cd2b5cde.js +0 -1
  299. package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +0 -1
  300. package/apps/pwa/out/_next/static/chunks/3094.67339318c7f72a3a.js +0 -2
  301. package/apps/pwa/out/_next/static/chunks/338-ceb956612dcfaa86.js +0 -1
  302. package/apps/pwa/out/_next/static/chunks/3395-835e4ceaa6335ba4.js +0 -1
  303. package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
  304. package/apps/pwa/out/_next/static/chunks/3441-be82e3595aef3a20.js +0 -1
  305. package/apps/pwa/out/_next/static/chunks/3689-89905039bf3e7257.js +0 -1
  306. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
  307. package/apps/pwa/out/_next/static/chunks/3822-6f268bc26c6a84ac.js +0 -1
  308. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
  309. package/apps/pwa/out/_next/static/chunks/4457-d7df81c90f9d0268.js +0 -1
  310. package/apps/pwa/out/_next/static/chunks/4551-77dadae76f6cbace.js +0 -1
  311. package/apps/pwa/out/_next/static/chunks/4978.6fd93b974441e87f.js +0 -1
  312. package/apps/pwa/out/_next/static/chunks/5012-f746f6549b869f91.js +0 -1
  313. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
  314. package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
  315. package/apps/pwa/out/_next/static/chunks/62-5bfed1e23790b745.js +0 -1
  316. package/apps/pwa/out/_next/static/chunks/6320-9bc8561fde416e07.js +0 -1
  317. package/apps/pwa/out/_next/static/chunks/649-ca96d1c5d09e02b8.js +0 -1
  318. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
  319. package/apps/pwa/out/_next/static/chunks/6726-85a7db59b288c53f.js +0 -1
  320. package/apps/pwa/out/_next/static/chunks/6735-33267c4630d0d963.js +0 -1
  321. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
  322. package/apps/pwa/out/_next/static/chunks/7633-141682015873beb6.js +0 -1
  323. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
  324. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
  325. package/apps/pwa/out/_next/static/chunks/8067-143791231c99bd02.js +0 -1
  326. package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
  327. package/apps/pwa/out/_next/static/chunks/8430-a10cfba8ef282d4e.js +0 -1
  328. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
  329. package/apps/pwa/out/_next/static/chunks/9454-12c2f30dea62291c.js +0 -1
  330. package/apps/pwa/out/_next/static/chunks/976-4a9b4ebdca7c739a.js +0 -1
  331. package/apps/pwa/out/_next/static/chunks/9989-15cdf568bafa9fc8.js +0 -1
  332. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
  333. package/apps/pwa/out/_next/static/chunks/app/approvals/page-deef5ee191830f14.js +0 -1
  334. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-b193da485f9a14b2.js +0 -1
  335. package/apps/pwa/out/_next/static/chunks/app/board/page-b6e1a79df0e214f4.js +0 -1
  336. package/apps/pwa/out/_next/static/chunks/app/botlab/page-e84df4d907f703c7.js +0 -1
  337. package/apps/pwa/out/_next/static/chunks/app/bots/page-a5fb9a5db89ae4ea.js +0 -1
  338. package/apps/pwa/out/_next/static/chunks/app/ceo/page-1499f481d7fc52d6.js +0 -1
  339. package/apps/pwa/out/_next/static/chunks/app/chat/page-c7cf753aeb648790.js +0 -1
  340. package/apps/pwa/out/_next/static/chunks/app/consult/page-4118469e514e10fd.js +0 -1
  341. package/apps/pwa/out/_next/static/chunks/app/control/page-e968f7630654332a.js +0 -1
  342. package/apps/pwa/out/_next/static/chunks/app/decisions/page-505915052f947022.js +0 -1
  343. package/apps/pwa/out/_next/static/chunks/app/design-check/page-c2f962fb4a3b06c6.js +0 -1
  344. package/apps/pwa/out/_next/static/chunks/app/editor/page-7b5ed0bd8a589ec4.js +0 -1
  345. package/apps/pwa/out/_next/static/chunks/app/exec/page-cf8193250dd42dd4.js +0 -1
  346. package/apps/pwa/out/_next/static/chunks/app/field/page-cf3f5dfa35dea8e9.js +0 -1
  347. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
  348. package/apps/pwa/out/_next/static/chunks/app/intake/page-a15e7fcc283e9ca7.js +0 -1
  349. package/apps/pwa/out/_next/static/chunks/app/layout-d2915e7b70ff3d0e.js +0 -1
  350. package/apps/pwa/out/_next/static/chunks/app/live/page-b70cd005d9169e25.js +0 -1
  351. package/apps/pwa/out/_next/static/chunks/app/loops/page-2595f0eba29f57e1.js +0 -1
  352. package/apps/pwa/out/_next/static/chunks/app/market/page-c776322454156e36.js +0 -1
  353. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-77bbd17f4435aa10.js +0 -1
  354. package/apps/pwa/out/_next/static/chunks/app/morning/page-049689e611ecc936.js +0 -1
  355. package/apps/pwa/out/_next/static/chunks/app/not-found-85954a4fb09c44eb.js +0 -1
  356. package/apps/pwa/out/_next/static/chunks/app/observatory/page-e773e552e1068ee6.js +0 -1
  357. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-5633c4bfa7684b4a.js +0 -1
  358. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-d829b92bd01b4994.js +0 -1
  359. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-061c317fc771becf.js +0 -1
  360. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a45ece8865ed4b6.js +0 -1
  361. package/apps/pwa/out/_next/static/chunks/app/page-0be990618c0d56bd.js +0 -1
  362. package/apps/pwa/out/_next/static/chunks/app/reflection/page-bfea4865dc244b73.js +0 -1
  363. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-0d6ba8e337741484.js +0 -1
  364. package/apps/pwa/out/_next/static/chunks/app/sessions/page-76a88b55d816482e.js +0 -1
  365. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-bba1542df59f61ce.js +0 -1
  366. package/apps/pwa/out/_next/static/chunks/app/settings/page-4185c95689bb372e.js +0 -1
  367. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-b699e1a8d5d34b8b.js +0 -1
  368. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
  369. package/apps/pwa/out/_next/static/chunks/app/setup/page-c225e338c31bf274.js +0 -1
  370. package/apps/pwa/out/_next/static/chunks/app/share/page-449353f70507ca1f.js +0 -1
  371. package/apps/pwa/out/_next/static/chunks/app/showroom/page-90b889714102476d.js +0 -1
  372. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f1ee7fbeb7f96ddc.js +0 -1
  373. package/apps/pwa/out/_next/static/chunks/app/term/page-d59a95e1434dfaa0.js +0 -1
  374. package/apps/pwa/out/_next/static/chunks/app/today/page-71491a23414d1a47.js +0 -1
  375. package/apps/pwa/out/_next/static/chunks/app/trace/page-de7112bdb65fa4fe.js +0 -1
  376. package/apps/pwa/out/_next/static/chunks/app/vault/page-feb4d0d88079660d.js +0 -1
  377. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-f17d887b40dd6b1d.js +0 -1
  378. package/apps/pwa/out/_next/static/chunks/app/workflows/page-8da77be2192bcd5d.js +0 -1
  379. package/apps/pwa/out/_next/static/chunks/app/workspace/page-72c3b9a5e9582b29.js +0 -1
  380. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3afe4c8d0c48c39.js +0 -1
  381. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
  382. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
  383. package/apps/pwa/out/_next/static/chunks/main-app-b9308f2d8a931531.js +0 -1
  384. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
  385. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
  386. package/apps/pwa/out/_next/static/chunks/webpack-6f35f9d892fa630b.js +0 -1
  387. package/apps/pwa/out/_next/static/css/85f53c6f35d11382.css +0 -1
  388. /package/apps/pwa/out/_next/static/{J5H7BvqgSI8DUjtRRcA_S → Bam1YziK2iH9bN4K0utGk}/_ssgManifest.js +0 -0
@@ -26,6 +26,7 @@ import { answerCrossCheck, answerSeat, askCrossCheck, askSeat, crossCheckAnswer,
26
26
  import { queryRunningRuns } from '../self-implement/running-runs.js';
27
27
  import { checklistHistory, listChecklist, type Checklist } from '../release-loop/checklist.js';
28
28
  import { readSchedules, releasedVersion } from '../release-loop/feature-store.js';
29
+ import { runOpDecide, type OpDecideCandidate } from './op-decide.js';
29
30
 
30
31
  // `kind`/`createdAt` are the V3 shadow-compare keys (내부 문서 `METHOD-v3-shadow-compare-2026-10-02` · MK 10-02 18:54).
31
32
  export type SeatItem = { source: 'request' | 'checklist' | 'hook' | 'seat-question'; kind?: 'request' | 'cell' | 'hook-task' | 'seat-question'; id: string; title: string; text: string; evidence?: string; status?: string; version?: string; seat?: string; evidenceHash?: string; asOf?: string; queuedAt?: string; createdAt?: string; from?: SeatId };
@@ -43,7 +44,7 @@ export type TcCandidate =
43
44
  | { kind: 'publication-pr-approval-wait'; pr: number; title: string; readyAt: string; approvalWaitAt: string; paths: string[] }
44
45
  | { kind: 'other-seat-cell-defect'; version: string; id: string; title: string; to: SeatId };
45
46
  export type TcPullRequest = { number: number; title: string; body?: string; state: 'OPEN' | 'MERGED' | 'CLOSED'; isDraft: boolean; createdAt: string; updatedAt?: string; mergedAt?: string | null; readyAt?: string; approvalWaitAt?: string; paths?: string[]; reviewDecision?: string };
46
- export type SeatEntry = { seat: string; ts?: string; at: string; modeDowngradeReason?: string; status: 'shadow' | 'attempting' | 'outcome-unknown' | 'queued' | 'refused' | 'held' | 'launched' | 'hitl' | 'wait' | 'asked' | 'answered' | 'awaiting-answer' | 'awaiting-xcheck' | 'awaiting-resolution' | 'resolved-by-neighbor' | 'resolved' | 'rejected-no-evidence' | 'skipped-budget' | 'skipped-empty'; item?: SeatItem; candidate?: OpCandidate | TcCandidate; action?: SeatAction; reason?: string; runId?: string; queueId?: string; inquiry?: SeatInquiry; answer?: string; escalated?: boolean; xcheckRequestedAt?: string; xcheckQuestionId?: number; xcheckNeighbor?: SeatId; xcheckNote?: string; xcheckJudgmentEvidence?: string; xcheckWaitMinutes?: number };
47
+ export type SeatEntry = { seat: string; ts?: string; at: string; modeDowngradeReason?: string; status: 'shadow' | 'attempting' | 'outcome-unknown' | 'queued' | 'refused' | 'held' | 'launched' | 'hitl' | 'wait' | 'asked' | 'answered' | 'awaiting-answer' | 'awaiting-xcheck' | 'awaiting-resolution' | 'resolved-by-neighbor' | 'resolved' | 'rejected-no-evidence' | 'skipped-budget' | 'skipped-empty'; item?: SeatItem; candidate?: OpCandidate | TcCandidate | OpDecideCandidate; action?: SeatAction; reason?: string; runId?: string; queueId?: string; inquiry?: SeatInquiry; answer?: string; escalated?: boolean; xcheckRequestedAt?: string; xcheckQuestionId?: number; xcheckNeighbor?: SeatId; xcheckNote?: string; xcheckJudgmentEvidence?: string; xcheckWaitMinutes?: number };
47
48
  export type SeatLoopResult = SeatEntry | { seat: string; status: 'skipped-off'; modeDowngradeReason?: string };
48
49
 
49
50
  export function seatLoopTickLine(result: SeatLoopResult): string {
@@ -99,6 +100,8 @@ export type SeatDeps = {
99
100
  /** Judge a decision draft against the receiving seat's evidence; null leaves the request pending. */
100
101
  crossCheck?: (seat: SeatId, draft: string, from: SeatId) => Promise<CrossCheckJudgment | null> | CrossCheckJudgment | null;
101
102
  decisionsConfig?: UserConfig['decisions'];
103
+ /** OP-LOOP-DECIDE: who owns a run (for run-blocking cards). Default = today's seat ledgers' launched rows. */
104
+ runOwner?: (runId: string) => SeatId | undefined;
102
105
  };
103
106
 
104
107
  const repoRoot = resolve(import.meta.dir, '../..');
@@ -291,6 +294,8 @@ function personReopened(ledger: readonly SeatEntry[], seat: string, item: SeatIt
291
294
  const ref = decisionRef(seat, item);
292
295
  return new DecisionLedger({ stateDir: root }).list({ status: 'all' }).some((card) => {
293
296
  if (!card.refs?.includes(ref) || card.status !== 'decided' || !card.decidedAt) return false;
297
+ // «멈춘다» on a repeat-stop card keeps the cell held — only a reopen (or any other card's decision) frees it (OP-LOOP-DECIDE).
298
+ if (/ 반복 착지 0$/.test(card.title) && card.options.find((option) => option.key === card.choice)?.label === '멈춘다') return false;
294
299
  const decidedAt = Date.parse(card.decidedAt);
295
300
  return Number.isFinite(decidedAt) && decidedAt > heldAt;
296
301
  });
@@ -551,6 +556,40 @@ export async function gatherSeatInputs(seat: string, deps: SeatDeps = {}, ledger
551
556
  return { requests: pending, checklist, role: read(join(deps.repo ?? repoRoot, 'docs', 'roles', `${seat}.md`)).slice(0, 4_000) };
552
557
  }
553
558
 
559
+ /** OP-LOOP-DECIDE: filter open decision cards each OP turn. Shadow records; live decides/routes. A failure never blocks the turn. */
560
+ function opDecidePass(config: SeatLoopConfig, deps: SeatDeps, now: Date, path: string, stateRoot: string, ledger: readonly SeatEntry[]): void {
561
+ const mode = config.opDecide?.mode === 'live' ? 'live' : 'shadow';
562
+ let owners: Map<string, SeatId> | undefined;
563
+ try {
564
+ const append = deps.append ?? defaultAppend;
565
+ const recorded = new Map<string, OpDecideCandidate[]>();
566
+ for (const row of ledger) {
567
+ if (row.candidate?.kind === 'decision-filter') recorded.set(row.candidate.id, [...(recorded.get(row.candidate.id) ?? []), row.candidate]);
568
+ }
569
+ runOpDecide({
570
+ mode,
571
+ ledger: cardLedger(stateRoot, deps, now),
572
+ runOwner: deps.runOwner ?? ((runId) => {
573
+ owners ??= new Map((['OP', 'UX', 'MK', 'TC'] as const).flatMap((owner) =>
574
+ readSeatLedger(owner, deps, now).filter((row) => row.runId).map((row) => [row.runId!, owner] as const)));
575
+ return owners.get(runId);
576
+ }),
577
+ routeToSeat: (to, card, reason) => {
578
+ askSeat(stateRoot, 'OP', to, `결정 카드 ${card.id} «${card.title}» — ${reason}. 런 주인이 판단해 elanous decisions decide ${card.id} 로 닫아 주세요.`,
579
+ `op-decide:${card.id}:${to}`);
580
+ return true;
581
+ },
582
+ recorded: (id) => recorded.get(id) ?? [],
583
+ record: (candidate) => {
584
+ append(path, { seat: 'OP', ts: now.toISOString(), at: now.toISOString(),
585
+ status: mode === 'shadow' ? 'shadow' : candidate.verdict === 'auto' ? 'resolved' : 'hitl',
586
+ ...(candidate.verdict === 'route' && candidate.to === 'CEO' ? { escalated: true } : {}),
587
+ candidate, reason: candidate.reason });
588
+ },
589
+ });
590
+ } catch (error) { observe('op-decide', { event: 'pass-failed', mode, error: String(error).slice(0, 200) }); }
591
+ }
592
+
554
593
  const OP_CUTOFF_WINDOW_MS = 2 * 60 * 60_000;
555
594
  const RUN_REFERENCE = /\brun-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi;
556
595
 
@@ -870,10 +909,25 @@ const SEAT_ACTION_RULES: ReadonlyArray<{ kind: 'wait' | 'harness' | 'decision';
870
909
  { kind: 'wait', signal: /실측\s*대기|(?:사람|실제)\s*기기(?!\s*(?:(?:실측|측정|확인)\s*)?완료)(?:\s*(?:실측|측정|확인|대기))?|실물\s*측정(?!\s*완료)/iu, source: 'content', resolvedBy: /(?:실측|측정|(?:사람|실제)\s*기기|실물\s*측정)\s*완료/iu },
871
910
  { kind: 'wait', signal: /\u{1F451}\s*확인\s*대기/iu, source: 'content', resolvedBy: /\u{1F451}\s*확인\s*완료/iu },
872
911
  { kind: 'decision', signal: TRACK_AGENT_FORBIDDEN_ACTION_REGEX, source: 'task' },
912
+ { kind: 'decision', signal: /토큰\s*(?:을|를)?\s*(?:출력|노출|공개|첨부|붙여)/iu, source: 'task' },
873
913
  { kind: 'decision', signal: /(?:마켓|레지스트리)(?:에|로)?\s*(?:게시|등록|발행)|사이트\s*운영\s*반영|SNS(?:에|로)?\s*(?:게시|발행|업로드)|공개\s*발행/iu, source: 'task' },
874
914
  { kind: 'harness', signal: /(?:마켓|레지스트리|사이트|SNS|공개)?(?:에|로)?\s*(?:게시|발행|배포|등록|운영\s*반영)(?:를?\s*위한)?\s*(?:준비|초안|원고|자료|구현|점검|테스트)|(?:마켓|레지스트리|사이트|SNS|공개)?(?:에|로)?\s*(?:게시|발행|배포|등록)(?:용|를?\s*위한)\s*(?:초안|원고|자료|코드|기능)/iu, source: 'task' },
875
915
  ];
876
916
 
917
+ // SEAT-FORBID-NEGATION: «비밀 0» · «비밀 제외» · «no secret» name the word to exclude it, not to act on it.
918
+ // Only a negation right next to the hit (optionally after a short noun such as 키 · 증명 and a particle) clears it;
919
+ // «비밀 키를 붙여라» keeps its imperative and stays a decision.
920
+ const FORBID_NEGATION = '(?:0(?![\\dx.,])|제외|없이|빼고|금지|없음|none\\b|no\\b|without\\b|excluding\\b)';
921
+ const FORBID_NEGATION_AFTER = new RegExp(`^(?:\\s*(?:증명|정보|키|값|토큰|keys?|tokens?)(?![A-Za-z]))?\\s*(?:은|는|이|가|을|를|:)?\\s*${FORBID_NEGATION}`, 'iu');
922
+ const FORBID_NEGATION_BEFORE = /(?:^|[\s(\[«"'·,:])(?:no|without|excluding|none|제외|금지)\s*[:·-]?\s*$/iu;
923
+
924
+ export const FORBID_JUDGMENT_LINE = (word: string | undefined): string =>
925
+ `판정 근거: «${word ?? '?'}» 바로 앞뒤에 부정 문면(0·제외·없이·빼고·금지·없음·no·without·excluding)이 없어 실행 요청으로 봤다`;
926
+
927
+ export function forbiddenHitNegated(text: string, index: number, length: number): boolean {
928
+ return FORBID_NEGATION_AFTER.test(text.slice(index + length)) || FORBID_NEGATION_BEFORE.test(text.slice(0, index));
929
+ }
930
+
877
931
  export function planAction(item: SeatItem, seat?: string): { kind: 'decision' | 'harness' | 'wait'; text: string; reason?: string } {
878
932
  const task = `${item.id}\n${item.title}\n${item.text}`;
879
933
  const content = `${task}\n${item.evidence ?? ''}`;
@@ -884,7 +938,9 @@ export function planAction(item: SeatItem, seat?: string): { kind: 'decision' |
884
938
  if (resolvedBy?.test(item.evidence ?? '')) return null;
885
939
  const hits = [...text.matchAll(new RegExp(signal.source, `${signal.flags.replace('g', '')}g`))];
886
940
  const hit = kind === 'decision' ? hits.find((match) => !prepared.some((prep) =>
887
- match.index! >= prep.index! && match.index! + match[0].length <= prep.index! + prep[0].length)) : hits[0];
941
+ match.index! >= prep.index! && match.index! + match[0].length <= prep.index! + prep[0].length)
942
+ && !(forbiddenHitNegated(text, match.index!, match[0].length)
943
+ && (observe('forbid-negated', { item: item.id, word: match[0] }), true))) : hits[0];
888
944
  return hit ? { kind, reason: hit[0] } : null;
889
945
  }).find((hit) => hit !== null);
890
946
  const kind = rule?.kind ?? 'harness';
@@ -1004,6 +1060,7 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
1004
1060
  } catch (error) { observe('heartbeat-write-failed', { seat, error: String(error).slice(0, 200) }); }
1005
1061
  const recorded = readSeatLedger(seat, deps, now);
1006
1062
  const ledger = config.mode === 'live-safe' ? reconciledQueueLedger(recorded, stateRoot, deps) : recorded;
1063
+ if (seat === 'OP') opDecidePass(config, deps, now, path, stateRoot, ledger);
1007
1064
  let turnError: unknown;
1008
1065
  try {
1009
1066
  if (config.mode === 'on' && config.questions === 'on') {
@@ -1494,7 +1551,7 @@ export async function runSeatLoopOnce(seat: string, deps: SeatDeps = {}): Promis
1494
1551
  : undefined;
1495
1552
  } catch (error) { observe('decision-source-unreadable', { seat, item: item.id, error: String(error).slice(0, 200) }); }
1496
1553
  const material = decisionCardMaterial({ what: `${seat} 배정 항목: ${item.title} 역할: ${inputs.role || '역할 근거 없음'}`, soFar: `요청 내용: ${item.text}`,
1497
- current: `밑바탕 재측: ${currentStatus ?? '상태 미확인'}; 요청에서 금지 문면 «${action.reason}» 발견 — 실행 전 사람 승인 필요`,
1554
+ current: `밑바탕 재측: ${currentStatus ?? '상태 미확인'}; 요청에서 금지 문면 «${action.reason}» 발견 — 실행 전 사람 승인 필요; ${FORBID_JUDGMENT_LINE(action.reason)}`,
1498
1555
  question: `금지 문면 «${action.reason}»이 걸린 요청을 승인할까?`, answer: '승인 전에는 집행하지 않고 보류한다',
1499
1556
  options, recommendation, confidence: '높음 — 요청에 금지 문면 직접 일치',
1500
1557
  ifUntouched: '자동 집행되지 않고 해당 칸은 보류 상태로 남는다',
@@ -6,6 +6,7 @@ import { promisify } from 'node:util';
6
6
  import { debug } from '../debug/log.js';
7
7
  import { effectiveInstanceRoot } from '../instance/resolve.js';
8
8
  import { getUserConfig, type SeatLoopConfig } from '../user-config.js';
9
+ import { collectDraftMetrics, collectOverlapMetrics, countSalvagedToday, type DraftMetrics, type OverlapMetrics } from '../self-dev/draft-sweep.js';
9
10
  import { seatDay, seatLedgerPath, type OpCandidate, type SeatEntry } from './seat-loop.js';
10
11
 
11
12
  export type SeatReportDeps = {
@@ -16,6 +17,10 @@ export type SeatReportDeps = {
16
17
  config?: SeatLoopConfig;
17
18
  post?: boolean;
18
19
  send?: (body: string, seat: string, pr: number) => Promise<void> | void;
20
+ draftMetrics?: (now: Date) => Promise<DraftMetrics>;
21
+ overlapMetrics?: (now: Date) => Promise<OverlapMetrics>;
22
+ /** DRAFT-NOT-ARCHIVE salvage branches since the KST day start; null = unreadable. */
23
+ salvagedToday?: (dayStart: Date, now: Date) => Promise<number | null>;
19
24
  };
20
25
  export type SeatReportResult = { seat: string; date: string; body: string; posted: boolean };
21
26
  const repoRoot = resolve(import.meta.dir, '../..');
@@ -36,6 +41,21 @@ function opJudgment(candidate: OpCandidate): { kind: 'card' | 'todo'; text: stri
36
41
  text: `${text} (${candidate.category ?? '범주 없음'})` };
37
42
  }
38
43
 
44
+ /** OVERLAP-METRIC line; ⛔ an unmeasured value says so instead of printing 0. */
45
+ function overlapLine(metrics: OverlapMetrics): string {
46
+ if (metrics.sourceIncomplete || metrics.launches24h === null) return ' · 겹침 발사 24h 못 잼(원천 불완전)';
47
+ const launched = metrics.launched === null ? '' : metrics.unmeasured
48
+ ? ` 이상 (발사 ${metrics.launched} 중 ${metrics.unmeasured} 못 잼)` : ` (발사 ${metrics.launched})`;
49
+ const rate = metrics.autoRate === null || metrics.linked === null || metrics.autoLanded === null
50
+ ? (metrics.linked === 0 ? '표본 없음' : '못 잼')
51
+ : `${(metrics.autoRate * 100).toFixed(1)}% (${metrics.autoLanded}/${metrics.linked})`;
52
+ const median = metrics.secondSiblingMedianHours === null ? (metrics.linked === null ? '못 잼' : '표본 없음')
53
+ : `${metrics.secondSiblingMedianHours.toFixed(1)}h`;
54
+ const salvage = (metrics.salvaged ? ` · 수확 가지 형제 ${metrics.salvaged}(미착지)` : '')
55
+ + (metrics.salvageUnmeasured ? ` · 수확 가지 ${metrics.salvageUnmeasured} 못 잼` : '');
56
+ return ` · 겹침 발사 24h ${metrics.launches24h}${launched} · 겹침 자동 착지 ${rate} · 둘째 형제 착지 중앙값 ${median}${salvage}`;
57
+ }
58
+
39
59
  async function defaultSend(body: string, seat: string, pr: number, repo: string): Promise<void> {
40
60
  const dir = mkdtempSync(join(tmpdir(), 'seat-report-'));
41
61
  try {
@@ -58,8 +78,13 @@ export async function seatReport(seat: string, deps: SeatReportDeps = {}): Promi
58
78
  // One line per item, its last state wins: an `attempting` row followed by `launched` is not «unconfirmed».
59
79
  const latest = new Map<string, SeatEntry>();
60
80
  const judgments = new Map<string, OpCandidate>();
81
+ const filtered = { auto: 0, route: 0 };
61
82
  for (const entry of entries) {
62
- if (seat === 'OP' && entry.candidate) {
83
+ if (entry.candidate?.kind === 'decision-filter') {
84
+ filtered[entry.candidate.verdict] += 1;
85
+ continue;
86
+ }
87
+ if (seat === 'OP' && entry.candidate && 'verdict' in entry.candidate) {
63
88
  if (entry.candidate.verdict === 'none') {
64
89
  if (entry.candidate.id) {
65
90
  const key = entry.candidate.kind === 'unassigned-cell'
@@ -89,8 +114,38 @@ export async function seatReport(seat: string, deps: SeatReportDeps = {}): Promi
89
114
  return `건너뜀 ${entry.status} ${label}`;
90
115
  });
91
116
  const classified = [...judgments.values()].map(opJudgment).filter((row): row is NonNullable<typeof row> => row !== null);
92
- const opDetail = classified.length ? ` · 결정 카드 후보: ${classified.filter((row) => row.kind === 'card').map((row) => row.text).join(' / ') || '없음'} · OP 가 할 일: ${classified.filter((row) => row.kind === 'todo').map((row) => row.text).join(' / ') || '없음'}` : '';
93
- const body = `**[${seat}]** {{TS}} → 보고 ${date}: ${detail.join(' · ') || (classified.length ? 'OP 그림자 판단' : '원장 기록 없음')}${opDetail}`;
117
+ const opDetail = (classified.length ? ` · 결정 카드 후보: ${classified.filter((row) => row.kind === 'card').map((row) => row.text).join(' / ') || '없음'} · OP 가 할 일: ${classified.filter((row) => row.kind === 'todo').map((row) => row.text).join(' / ') || '없음'}` : '')
118
+ + (filtered.auto + filtered.route ? ` · 카드 거르기: 자동 ${filtered.auto} · 넘김 ${filtered.route}` : '');
119
+ let draftDetail = '';
120
+ if (seat === 'OP') {
121
+ try {
122
+ const metrics = await (deps.draftMetrics ?? (async (clock: Date) => {
123
+ const [{ resolveRepositoryName }, { githubDraftSweepAdapters }] = await Promise.all([
124
+ import('../harness/repository-name.js'), import('../harness/harness-cli-command.js'),
125
+ ]);
126
+ return collectDraftMetrics(resolveRepositoryName({}), githubDraftSweepAdapters(), clock);
127
+ }))(now);
128
+ draftDetail = ` · draft 재고 ${metrics.inventory} · 최장 나이 ${metrics.oldestAgeHours === null ? '해당 없음' : `${metrics.oldestAgeHours.toFixed(1)}h`} · needs-owner ${metrics.needsOwner} · 48h 전환율 ${metrics.conversion48h === null ? '표본 없음' : `${(metrics.conversion48h * 100).toFixed(1)}% (${metrics.converted48h}/${metrics.cohort48h})`}`;
129
+ } catch { draftDetail = ' · draft 지표 못 읽음'; }
130
+ try {
131
+ const overlap = await (deps.overlapMetrics ?? (async (clock: Date) => {
132
+ const [{ resolveRepositoryName }, { githubDraftSweepAdapters }, { readAskPreflightRowsSince, readSalvagedRowsSince }] = await Promise.all([
133
+ import('../harness/repository-name.js'), import('../harness/harness-cli-command.js'), import('../self-dev/ask-launch-io.js'),
134
+ ]);
135
+ return collectOverlapMetrics(resolveRepositoryName({}), { ...githubDraftSweepAdapters(), listPreflightRows: readAskPreflightRowsSince, listSalvagedRows: readSalvagedRowsSince }, clock);
136
+ }))(now);
137
+ draftDetail += overlapLine(overlap);
138
+ } catch { draftDetail += ' · 겹침 지표 못 읽음'; }
139
+ let salvagedToday: number | null = null;
140
+ try {
141
+ salvagedToday = await (deps.salvagedToday ?? (async (dayStart: Date, clock: Date) => {
142
+ const { readSalvagedRowsSince } = await import('../self-dev/ask-launch-io.js');
143
+ return countSalvagedToday(await readSalvagedRowsSince(dayStart), dayStart, clock);
144
+ }))(new Date(`${date}T00:00:00+09:00`), now);
145
+ } catch { salvagedToday = null; }
146
+ draftDetail += ` · 수확 가지 오늘 ${salvagedToday === null ? '못 잼' : salvagedToday}`;
147
+ }
148
+ const body = `**[${seat}]** {{TS}} → 보고 ${date}: ${detail.join(' · ') || (classified.length ? 'OP 그림자 판단' : '원장 기록 없음')}${opDetail}${draftDetail}`;
94
149
  const pr = (deps.config ?? getUserConfig().loops?.seat)?.reportPr;
95
150
  const posted = deps.post === true && pr !== undefined;
96
151
  if (posted) await (deps.send ?? ((text, id, number) => defaultSend(text, id, number, deps.repo ?? repoRoot)))(body, seat, pr);
@@ -1026,11 +1026,12 @@ export async function runAskLaunchFlow(
1026
1026
  ...(candidates.error === undefined ? {} : { error: candidates.error }),
1027
1027
  }, candidates.error === undefined ? 'info' : 'warn');
1028
1028
  }
1029
- if (candidates.paths.length > 0) {
1030
- deps.print(`[ask] ⑴ 코드에서 후보 경로 ${candidates.paths.length}개를 찾아 넣고 다시 저작한다: ${candidates.paths.join(' · ')}`);
1029
+ const selectedPaths = candidates.paths.slice(0, 1);
1030
+ if (selectedPaths.length > 0) {
1031
+ deps.print(`[ask] ⑴ 코드에서 후보 경로 ${selectedPaths.length}개를 찾아 넣고 다시 저작한다: ${selectedPaths.join(' · ')}`);
1031
1032
  }
1032
- authored = await authorAndPreflight(candidates.paths.length > 0
1033
- ? `대상 경로: ${candidates.paths.join(' · ')}\n${input.askText}`
1033
+ authored = await authorAndPreflight(selectedPaths.length > 0
1034
+ ? `대상 경로: ${selectedPaths.join(' · ')}\n${input.askText}`
1034
1035
  : input.askText);
1035
1036
  deps.log('ask-auto-reauthor', {
1036
1037
  candidatePathCount: candidates.paths.length,
@@ -5,7 +5,7 @@
5
5
  // 72차 판이 복제를 금지했으므로 여기로 옮긴다. CLI 와 TUI 가 «같은 조회»를 쓴다.
6
6
  // ⛔ 판정은 여기 없다 — 여기는 「무엇을 조회하는가」뿐이고, 「그래서 막나」는 launch-preflight 가 정한다.
7
7
  import { execFileSync } from 'node:child_process';
8
- import { readFileSync } from 'node:fs';
8
+ import { readFileSync, realpathSync } from 'node:fs';
9
9
  import {
10
10
  parseAskTargetPathHints,
11
11
  parseAskTargetPathHintsResult,
@@ -300,6 +300,54 @@ export async function readAskPreflightLogRows(): Promise<AskLogRow[] | null> {
300
300
  } catch { return null; }
301
301
  }
302
302
 
303
+ /** Every `ask-preflight` row since `since`, all universes, paged past the 1000-row cap (OVERLAP-METRIC).
304
+ * ⛔ Unreadable ⇒ `null` — never an empty list that reads as «no overlap launches». */
305
+ export async function readAskPreflightRowsSince(since: Date): Promise<AskLogRow[] | null> {
306
+ return readLogRowsSince(since, 'dev-pipeline', 'ask-preflight');
307
+ }
308
+
309
+ /** Every DRAFT-NOT-ARCHIVE `salvaged` row since `since`, all universes (OVERLAP-METRIC salvage siblings).
310
+ * ⛔ Unreadable ⇒ `null` — a salvage branch we could not see is «unmeasured», not «none». */
311
+ export async function readSalvagedRowsSince(since: Date): Promise<AskLogRow[] | null> {
312
+ return readLogRowsSince(since, 'self-implement.draft-not-archive', 'salvaged');
313
+ }
314
+
315
+ async function readLogRowsSince(since: Date, category: string, event: string): Promise<AskLogRow[] | null> {
316
+ try {
317
+ const [{ resolveLogTargets }, { LogStore }] = await Promise.all([
318
+ import('../cli/logs-cli.js'),
319
+ import('../mss/logging/log-store.js'),
320
+ ]);
321
+ const { targets } = resolveLogTargets({ all: true, includeTest: true });
322
+ const rows: AskLogRow[] = [];
323
+ // ⛔ One store can be registered under two names (`~/.monad` → `~/.elanous` symlink) — read each real file once,
324
+ // or every prod row is counted twice (OVERLAP-METRIC 실측: 169 행 중 69 가 같은 prod 파일의 겹침).
325
+ const seen = new Set<string>();
326
+ for (const target of targets) {
327
+ let real = target.dbPath;
328
+ try { real = realpathSync(target.dbPath); } catch { /* missing store: LogStore below decides */ }
329
+ if (seen.has(real)) continue;
330
+ seen.add(real);
331
+ const store = new LogStore(target.dbPath, { readonly: true });
332
+ try {
333
+ let beforeId: number | undefined;
334
+ for (let page = 0; page < 100; page++) {
335
+ const batch = store.query({ exactCategories: [category], events: [event], sinceMs: since.getTime(),
336
+ limit: 1_000, ...(beforeId === undefined ? {} : { beforeId }) });
337
+ for (const row of batch) {
338
+ const parsed = typeof row.data === 'string' ? JSON.parse(row.data) : row.data;
339
+ rows.push({ ts: row.ts, data: (parsed ?? null) as Record<string, unknown> | null });
340
+ }
341
+ if (batch.length < 1_000) break;
342
+ if (page === 99) return null;
343
+ beforeId = batch.at(-1)!.id;
344
+ }
345
+ } finally { store.close?.(); }
346
+ }
347
+ return rows;
348
+ } catch { return null; }
349
+ }
350
+
303
351
  const stringList = (value: unknown): string[] =>
304
352
  Array.isArray(value) ? value.filter((entry): entry is string => typeof entry === 'string') : [];
305
353
 
@@ -14,6 +14,43 @@ export type AskPathCandidates =
14
14
  const STOP_WORDS = new Set(['the', 'and', 'test', 'src', 'scripts', 'file', 'path', 'paths', 'code', 'with', 'from', 'this', 'that', 'into', 'for', 'then', 'when', 'have', 'does', 'should', 'will', 'please', 'target', 'change']);
15
15
  const WORD = /[-/]?[A-Za-z][A-Za-z0-9_.-]*(?:\/[A-Za-z0-9_.-]+)*/g;
16
16
 
17
+ // ⛔ AUTO-TARGET (10-08 실측 · `ask-auto-target-candidates` Pod 로그): 한글 ask 에서 영문 낱말은 거의
18
+ // «포장»뿐이었다 — 디스패처 머리 `[UX · 0.2.20 칸 <ID> · TASK-AGENT(OP 디스패처)]` ⊕ Pod 샤드 꼬리
19
+ // `## Shard identity {"shardId","totalShards","position","summary","siblings"}` ⊕ `## Working-memory handoff`.
20
+ // 그 낱말들이 하니스 파일(orchestrate.ts · orchestrator.ts · self-implement-pod.ts · index.ts)을 끌어와
21
+ // 네 칸이 엉뚱한 골로 저작됐다. ⇒ 포장을 «증거»에서 걷고, 남는 것이 없으면 «후보 없음»(=근거 없음)을 낸다.
22
+ /** Machine-appended sections whose words describe the transport, not the request. */
23
+ const WRAPPER_SECTION_HEADINGS = new Set(['shard identity', 'working-memory handoff']);
24
+ /** Dispatcher wrapper lines that are identical for every card. */
25
+ const WRAPPER_LINE = /^\s*(?:보존 계약|판정)\s*:/;
26
+ /** Dispatcher header `[OWNER · <ver> 칸 <ID> · …]` — the title after it is kept. */
27
+ const WRAPPER_HEADER = /^\s*\[[^\]\n]*칸[^\]\n]*\]\s*/;
28
+ /** Dispatcher evidence label — the evidence itself is kept. */
29
+ const WRAPPER_LABEL = /^\s*칸 근거 끝\(남은 것\)\s*:\s*/;
30
+ /** Seat/role/surface words and similar labels that name the process, not the code. */
31
+ const GENERIC_TOKENS = new Set(['task-agent', 'op', 'mk', 'tc', 'ux', 'ceo', 'coo', 'cmo', 'cto', 'cxo', 'pr', 'prs', 'pod', 'tui', 'pwa', 'cli', 'hitl', 'p0', 'p1', 'p2']);
32
+
33
+ function isGenericToken(token: string): boolean {
34
+ const lower = token.toLowerCase().replace(/[.]+$/, '');
35
+ if (GENERIC_TOKENS.has(lower)) return true;
36
+ if (/^v?\d+(?:\.\d+)+(?:-[a-z0-9.]+)?$/i.test(lower)) return true; // version strings
37
+ if (/^[0-9a-f]{7,40}$/.test(lower) && /\d/.test(lower)) return true; // commit shas
38
+ return false;
39
+ }
40
+
41
+ /** Remove dispatcher/Pod wrapper text so only the request body counts as lexical evidence. */
42
+ export function stripAskWrapper(askText: string): string {
43
+ const kept: string[] = [];
44
+ let skipping = false;
45
+ for (const line of askText.split(/\r?\n/)) {
46
+ const heading = /^##\s+(.+?)\s*$/.exec(line);
47
+ if (heading) skipping = WRAPPER_SECTION_HEADINGS.has(heading[1]!.toLowerCase());
48
+ if (skipping || WRAPPER_LINE.test(line)) continue;
49
+ kept.push(line.replace(WRAPPER_HEADER, '').replace(WRAPPER_LABEL, ''));
50
+ }
51
+ return kept.join('\n');
52
+ }
53
+
17
54
  function existingFile(fragment: string, cwd: string): string | null {
18
55
  const path = fragment.replace(/^\.\//, '');
19
56
  if (!path.includes('/') && !/\.[A-Za-z][A-Za-z0-9]*$/.test(path)) return null;
@@ -29,7 +66,8 @@ export function findAskPathCandidates(askText: string, { cwd, run = execFileSync
29
66
  const scores = new Map<string, Set<string>>();
30
67
  const tokens: string[] = [];
31
68
  const seen = new Set<string>();
32
- const chunks = [...askText.matchAll(/`([^`]+)`/g)].map((match) => match[1]!).concat([askText]);
69
+ const body = stripAskWrapper(askText);
70
+ const chunks = [...body.matchAll(/`([^`]+)`/g)].map((match) => match[1]!).concat([body]);
33
71
  for (const chunk of chunks) {
34
72
  for (const match of chunk.matchAll(WORD)) {
35
73
  const raw = match[0];
@@ -40,7 +78,7 @@ export function findAskPathCandidates(askText: string, { cwd, run = execFileSync
40
78
  hits.add(token);
41
79
  scores.set(file, hits);
42
80
  }
43
- if (STOP_WORDS.has(token.toLowerCase()) || seen.has(token)) continue;
81
+ if (STOP_WORDS.has(token.toLowerCase()) || isGenericToken(token) || seen.has(token)) continue;
44
82
  if (!file && !token.includes('/') && !token.includes('.') && !token.includes('-') && !token.includes('_')
45
83
  && !/[a-z][A-Z]/.test(token) && token.length < 4) continue;
46
84
  seen.add(token);