elanous 0.2.13 → 0.2.15

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 (524) 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/LZcstaO9_NXypPFmujolv/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/{7334.a6679d9168f37aa8.js → 1268.0840ff91e083fcbd.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/1307-33ffcc49ab45123b.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/{9656-c1540088f5859c80.js → 1844-9ec4b1d12f6e80eb.js} +1 -1
  8. package/apps/pwa/out/_next/static/chunks/1866-e7a6999f25548b30.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/{390.c373e31b3b9d0c41.js → 1948.15f356b15a625597.js} +2 -2
  10. package/apps/pwa/out/_next/static/chunks/211-9c9090c59e2c5907.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/224-798346143007ff65.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/2957-96ffab3d521fb08f.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/4144-a0041c8dc3be57ac.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/4554-3565eea1f1495ba7.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/{4685-c3c1c625b71accd1.js → 4614-21403aac730d4586.js} +1 -1
  23. package/apps/pwa/out/_next/static/chunks/{8039-df92e08fede5de8f.js → 4795-f372490b178583fc.js} +1 -1
  24. package/apps/pwa/out/_next/static/chunks/5135-44008fe8b1f2aa41.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/5233-bf28bd6bd7a32099.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/5360-be1f1d8f9fcc9755.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/5994-ae92c0de046ab9ba.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/{1452.93700483b9d4fc61.js → 6110.1297d4093fdfbb0d.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/6158.9c0449a9a8d0064b.js +2 -0
  31. package/apps/pwa/out/_next/static/chunks/6877-7c6208705c284124.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/726.83532d69452ee24b.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/{1829-b453a1b22d3999bb.js → 7327-2d69fb321ba73389.js} +1 -1
  35. package/apps/pwa/out/_next/static/chunks/7395-164e238f471b5d7b.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/7787-02a1c0b04e25e811.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/8827-e2f5b09a8d37e83b.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/8868-10a3a4440fb0ccd8.js +25 -0
  42. package/apps/pwa/out/_next/static/chunks/{8421-1c4acfb1395a0edf.js → 8949-ecb4128a0858b911.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/9070.067c106528ec1ae6.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/9103-7a3ae7f60af3b99f.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/{3869-3cd46526879f454c.js → 9247-29d889b809288e65.js} +1 -1
  47. package/apps/pwa/out/_next/static/chunks/935.4ddaa99fc1d1126c.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/{79f8be9e-957d68913b590b08.js → a9f2b54d-7865aba0f4d2ee49.js} +1 -1
  49. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/approvals/page-0e2c57fe6add4b2a.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-e05a77df7e005b04.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/board/page-f544338193a9a762.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/botlab/page-5491c115b1f9a923.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/bots/page-b8c49f6b4fa2df91.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/ceo/page-96398e364fbd4548.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/app/chat/page-976813c91d7394ea.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/consult/page-28b9448091776c06.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/control/page-1d4901eb7c2d8822.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/design-check/page-72d3e7ff35a8c954.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/editor/page-60c7a8e1a4fe62ce.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/exec/page-2a52b316cb143d9c.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/field/page-c3ce483c8d5db370.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/inside/page-13c7b53e0c2519a9.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/intake/page-96f074f6538e8a2c.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/layout-e97be5334d83a9f5.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/live/page-65e484302921a932.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/loops/page-34d08facaf08f556.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/market/page-955ec2da3be4c560.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-9f88fbc15d9058d1.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/morning/page-c1343f9499d86f3f.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/not-found-8739a2e0ae793130.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/observatory/page-c9fc5706b8abaa11.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-d23b12bb92242558.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8d47f45a0dc044ac.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c7fea475488201a8.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/outputs/page-e2ee2896811e98d0.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/page-83b19dae8619de05.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/reflection/page-5efbf81124c474ae.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f1a1fc0a82738c.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/sessions/page-da6c08aae0f92768.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-06ee266953d9c879.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/settings/page-153c2b3bcc89e8de.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-87a11e6a05f15199.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/app/setup/page-7d4182964407c09c.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/app/share/page-0d6d23b40263a7ea.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/app/showroom/page-7e8c39241f8eb6d0.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/app/tasks/page-008c1a96618a4887.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/app/term/{page-63d14a4d8fe1e1c1.js → page-f5597e894c02cd33.js} +1 -1
  91. package/apps/pwa/out/_next/static/chunks/app/today/page-0337fdf7fb5e37b6.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/app/trace/page-73735b64bfe1ae67.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/app/vault/page-8fcec4c9ea2e7f70.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-f7e631e9b4daf66e.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/app/workflows/page-4881b8f9f63694b4.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/app/workspace/page-9817c7f9007466cc.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-97f9f43172a3a90e.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/{5cd7e3a9-404fd6b8d0ecd44c.js → f6231284-45726550aba08fca.js} +1 -1
  99. package/apps/pwa/out/_next/static/chunks/{f48170cc-d05d28bf87a43bf4.js → f6b7c9ba.64a895b8b2fedbdc.js} +1 -1
  100. package/apps/pwa/out/_next/static/chunks/framework-5c169757551134ed.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/webpack-539cde99554be20f.js +1 -0
  106. package/apps/pwa/out/_next/static/css/1f7c958c41ffa3b9.css +1 -0
  107. package/apps/pwa/out/approvals/index.html +1 -1
  108. package/apps/pwa/out/approvals/index.txt +23 -23
  109. package/apps/pwa/out/autopilot/index.html +1 -1
  110. package/apps/pwa/out/autopilot/index.txt +23 -23
  111. package/apps/pwa/out/board/index.html +1 -1
  112. package/apps/pwa/out/board/index.txt +23 -23
  113. package/apps/pwa/out/botlab/index.html +1 -1
  114. package/apps/pwa/out/botlab/index.txt +22 -22
  115. package/apps/pwa/out/bots/index.html +1 -1
  116. package/apps/pwa/out/bots/index.txt +22 -22
  117. package/apps/pwa/out/ceo/index.html +1 -0
  118. package/apps/pwa/out/ceo/index.txt +33 -0
  119. package/apps/pwa/out/chat/index.html +1 -1
  120. package/apps/pwa/out/chat/index.txt +22 -22
  121. package/apps/pwa/out/consult/index.html +1 -1
  122. package/apps/pwa/out/consult/index.txt +23 -23
  123. package/apps/pwa/out/control/index.html +1 -1
  124. package/apps/pwa/out/control/index.txt +23 -23
  125. package/apps/pwa/out/design-check/index.html +1 -1
  126. package/apps/pwa/out/design-check/index.txt +23 -23
  127. package/apps/pwa/out/editor/index.html +1 -1
  128. package/apps/pwa/out/editor/index.txt +23 -23
  129. package/apps/pwa/out/exec/index.html +1 -1
  130. package/apps/pwa/out/exec/index.txt +23 -23
  131. package/apps/pwa/out/field/index.html +1 -1
  132. package/apps/pwa/out/field/index.txt +23 -23
  133. package/apps/pwa/out/healthz +1 -1
  134. package/apps/pwa/out/index.html +1 -1
  135. package/apps/pwa/out/index.txt +22 -22
  136. package/apps/pwa/out/inside/index.html +1 -1
  137. package/apps/pwa/out/inside/index.txt +22 -22
  138. package/apps/pwa/out/intake/index.html +1 -1
  139. package/apps/pwa/out/intake/index.txt +23 -23
  140. package/apps/pwa/out/live/index.html +1 -1
  141. package/apps/pwa/out/live/index.txt +23 -23
  142. package/apps/pwa/out/loops/index.html +1 -0
  143. package/apps/pwa/out/loops/index.txt +30 -0
  144. package/apps/pwa/out/market/index.html +1 -1
  145. package/apps/pwa/out/market/index.txt +22 -22
  146. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  147. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  148. package/apps/pwa/out/morning/index.html +1 -1
  149. package/apps/pwa/out/morning/index.txt +23 -23
  150. package/apps/pwa/out/observatory/index.html +1 -1
  151. package/apps/pwa/out/observatory/index.txt +23 -23
  152. package/apps/pwa/out/ops/checklist/index.html +1 -1
  153. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  154. package/apps/pwa/out/ops/release/index.html +1 -1
  155. package/apps/pwa/out/ops/release/index.txt +22 -22
  156. package/apps/pwa/out/ops/seats/index.html +1 -1
  157. package/apps/pwa/out/ops/seats/index.txt +22 -22
  158. package/apps/pwa/out/outputs/index.html +1 -1
  159. package/apps/pwa/out/outputs/index.txt +22 -22
  160. package/apps/pwa/out/reflection/index.html +1 -1
  161. package/apps/pwa/out/reflection/index.txt +23 -23
  162. package/apps/pwa/out/scheduler/index.html +1 -1
  163. package/apps/pwa/out/scheduler/index.txt +23 -23
  164. package/apps/pwa/out/sessions/index.html +1 -1
  165. package/apps/pwa/out/sessions/index.txt +22 -22
  166. package/apps/pwa/out/settings/devices/index.html +1 -1
  167. package/apps/pwa/out/settings/devices/index.txt +23 -23
  168. package/apps/pwa/out/settings/index.html +1 -1
  169. package/apps/pwa/out/settings/index.txt +23 -23
  170. package/apps/pwa/out/setup/done/index.html +1 -1
  171. package/apps/pwa/out/setup/done/index.txt +23 -23
  172. package/apps/pwa/out/setup/index.html +1 -1
  173. package/apps/pwa/out/setup/index.txt +23 -23
  174. package/apps/pwa/out/share/index.html +1 -1
  175. package/apps/pwa/out/share/index.txt +23 -23
  176. package/apps/pwa/out/showroom/index.html +1 -1
  177. package/apps/pwa/out/showroom/index.txt +22 -22
  178. package/apps/pwa/out/tasks/index.html +1 -1
  179. package/apps/pwa/out/tasks/index.txt +23 -23
  180. package/apps/pwa/out/term/index.html +1 -1
  181. package/apps/pwa/out/term/index.txt +23 -23
  182. package/apps/pwa/out/today/index.html +1 -1
  183. package/apps/pwa/out/today/index.txt +23 -23
  184. package/apps/pwa/out/trace/index.html +1 -1
  185. package/apps/pwa/out/trace/index.txt +23 -23
  186. package/apps/pwa/out/vault/index.html +1 -1
  187. package/apps/pwa/out/vault/index.txt +23 -23
  188. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  189. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  190. package/apps/pwa/out/workflows/index.html +1 -1
  191. package/apps/pwa/out/workflows/index.txt +23 -23
  192. package/apps/pwa/out/workspace/index.html +1 -1
  193. package/apps/pwa/out/workspace/index.txt +23 -23
  194. package/apps/pwa/out/worktrees/index.html +1 -1
  195. package/apps/pwa/out/worktrees/index.txt +23 -23
  196. package/graphs/heal/heal-loop.yaml +1 -1
  197. package/graphs/implement-loop.yaml +1 -1
  198. package/graphs/orchestrator/orchestrator.yaml +59 -0
  199. package/graphs/orchestrator/recipes.yaml +18 -0
  200. package/graphs/release/recipes.yaml +6 -0
  201. package/graphs/release/release-loop.yaml +6 -1
  202. package/graphs/rhythm/daily.yaml +38 -0
  203. package/graphs/rhythm/recipes.yaml +18 -0
  204. package/graphs/rhythm/weekly.yaml +39 -0
  205. package/graphs/video/recipes.yaml +3 -0
  206. package/graphs/video/youtube-summary.yaml +17 -0
  207. package/package.json +1 -1
  208. package/scripts/audit-test-state-writes.ts +72 -17
  209. package/scripts/brand/check.ts +188 -0
  210. package/scripts/ci-typecheck-changed.ts +50 -18
  211. package/scripts/coord-post.sh +11 -1
  212. package/scripts/docs/generate-graph-anatomy.ts +315 -0
  213. package/scripts/docs-cli-check.ts +23 -6
  214. package/scripts/expose-rubric-check.ts +118 -0
  215. package/scripts/expose-rubric-suggest.ts +169 -0
  216. package/scripts/heal/run-role.ts +14 -0
  217. package/scripts/hq/standby-verify.ts +11 -7
  218. package/scripts/lecture-note/node.ts +31 -11
  219. package/scripts/lessons/handbook-page.ts +112 -0
  220. package/scripts/marketing/generate-ax-brief.ts +140 -0
  221. package/scripts/marketing/generate-feature-map.ts +118 -0
  222. package/scripts/release-loop/checklist-gate-node.ts +53 -14
  223. package/scripts/release-loop/gate-node.ts +89 -14
  224. package/scripts/release-loop/publish-node.ts +14 -1
  225. package/scripts/release-loop/unattended-release.ts +23 -1
  226. package/scripts/release-loop/vault-note-node.ts +428 -0
  227. package/scripts/release-story/draft.ts +220 -0
  228. package/scripts/rhythm/daily.ts +570 -0
  229. package/scripts/rhythm/weekly.ts +315 -0
  230. package/scripts/skills/public-check.ts +169 -0
  231. package/scripts/smart-merge/resync-shadow.ts +147 -0
  232. package/scripts/test-deterministic.ts +4 -8
  233. package/scripts/test-diet/effectiveness.ts +195 -0
  234. package/scripts/test-diet/lib.ts +16 -8
  235. package/scripts/test-diet/node.ts +77 -9
  236. package/scripts/ux/demo-rehearsal.ts +2 -2
  237. package/scripts/ux/lib/rehearsal-verdict.ts +1 -0
  238. package/scripts/video/youtube-summary.ts +133 -0
  239. package/skills/diagram-master/SKILL.md +42 -49
  240. package/skills/diagram-master/references/pyproject.toml +4 -1
  241. package/skills/diagram-master/references/routing-guide.md +4 -2
  242. package/skills/grill-me/SKILL.md +1 -0
  243. package/skills/youtube-master/SKILL.md +13 -13
  244. package/skills/youtube-master/package-lock.json +0 -545
  245. package/skills/youtube-master/package.json +2 -5
  246. package/skills/youtube-master/scripts/main.ts +2 -2
  247. package/skills/youtube-master/scripts/transcribe-local.ts +1 -1
  248. package/skills/youtube-master/src/util.ts +30 -7
  249. package/src/agent-env/profile.ts +161 -0
  250. package/src/agent-mission/driver.ts +3 -3
  251. package/src/agent-substrate/pr-reviewer.ts +2 -0
  252. package/src/ask-user-question/pending-questions.ts +5 -0
  253. package/src/auth/github-app-token.ts +112 -13
  254. package/src/autopilot/build/llm-conflict-merge.ts +73 -10
  255. package/src/autopilot/build/merge-intent.ts +53 -0
  256. package/src/briefing/brief-items.ts +121 -0
  257. package/src/chat/index.ts +2 -0
  258. package/src/claims/claims-ledger.ts +232 -0
  259. package/src/claims/claims-render.ts +166 -0
  260. package/src/cli/agent-env-cli.ts +21 -0
  261. package/src/cli/claims-cli.ts +94 -0
  262. package/src/cli/decisions-cli.ts +44 -3
  263. package/src/cli/docs-cli.ts +2 -0
  264. package/src/cli/doctor-cli.ts +23 -1
  265. package/src/cli/doctor-node-modules.ts +147 -0
  266. package/src/cli/flow-cli.ts +11 -0
  267. package/src/cli/freeze-cli.ts +45 -0
  268. package/src/cli/intake-cli.ts +46 -17
  269. package/src/cli/lesson-cli.ts +108 -0
  270. package/src/cli/logs-cli.ts +104 -2
  271. package/src/cli/logs-model-input.ts +131 -0
  272. package/src/cli/pod-cli.ts +2 -0
  273. package/src/cli/pr-cli.ts +29 -1
  274. package/src/cli/private-ledger-commands.ts +54 -4
  275. package/src/cli/pty-drive-verdict.ts +11 -2
  276. package/src/cli/release-cli.ts +190 -33
  277. package/src/cli/rfc-status.ts +91 -0
  278. package/src/cli/seat-requests-cli.ts +52 -0
  279. package/src/connectors/linear.ts +5 -3
  280. package/src/context-bus/condense.ts +15 -20
  281. package/src/context-bus/context-now-public.ts +96 -0
  282. package/src/context-bus/context-now-surfaces.ts +55 -2
  283. package/src/context-bus/context-now.ts +6 -3
  284. package/src/context-bus/coord-events.ts +6 -4
  285. package/src/context-bus/long-term-memory.ts +92 -11
  286. package/src/dashboard/index.ts +75 -22
  287. package/src/dashboard/slash-runtime/chat-persona.ts +66 -0
  288. package/src/dashboard/slash-runtime/dashboard-handlers.ts +99 -0
  289. package/src/dashboard/slash-runtime/loops-table.ts +77 -0
  290. package/src/decisions/decision-cards.ts +33 -7
  291. package/src/decisions/decision-ledger.ts +40 -1
  292. package/src/decisions/proact-meter.ts +286 -0
  293. package/src/decisions/proact1-lite.ts +86 -0
  294. package/src/decisions/web-push-decision-cards.ts +1 -1
  295. package/src/discord/sprint21-runtime.ts +16 -0
  296. package/src/discord-self-message.ts +8 -0
  297. package/src/discord-slash-wire.ts +31 -2
  298. package/src/domains/alert-audit.ts +271 -0
  299. package/src/domains/core-tools.ts +4 -3
  300. package/src/domains/ops-facts-tool.ts +31 -1
  301. package/src/domains/outbound-alert.ts +1 -1
  302. package/src/elanous-config-dir.ts +29 -4
  303. package/src/execution-loop/launch-gate.ts +29 -14
  304. package/src/flow/cards.ts +239 -0
  305. package/src/graph-runner/graph-tick.ts +4 -0
  306. package/src/graph-runner/runner.ts +30 -5
  307. package/src/harness/harness-cli-command.ts +259 -38
  308. package/src/harness/harness-incidents.ts +45 -12
  309. package/src/harness/harness-pod-dispatch.ts +102 -11
  310. package/src/harness/harness-queue-child.ts +3 -3
  311. package/src/harness/harness-queue.ts +583 -65
  312. package/src/harness/helper-repair.ts +87 -0
  313. package/src/harness/helper-scan.ts +151 -0
  314. package/src/harness/launch-stamp.ts +236 -0
  315. package/src/harness/ledger-write-guard-child.fixture.ts +12 -0
  316. package/src/harness/ledger-write-guard.ts +35 -0
  317. package/src/harness/model-input-observation.ts +37 -0
  318. package/src/harness/pod-memory-policy.ts +15 -0
  319. package/src/harness/repository-name.ts +55 -0
  320. package/src/hq/fence-audit.ts +90 -0
  321. package/src/hq/hq.ts +63 -5
  322. package/src/hq/lease.ts +4 -4
  323. package/src/hq/move-plan.ts +126 -0
  324. package/src/hq/promote.ts +332 -0
  325. package/src/index.ts +159 -27
  326. package/src/intake-plane/check.ts +63 -0
  327. package/src/intake-plane/digest.ts +37 -15
  328. package/src/intake-plane/front-classifier.ts +6 -0
  329. package/src/intake-plane/items.ts +3 -2
  330. package/src/intake-plane/lens.ts +177 -0
  331. package/src/intake-plane/linear-wish.ts +39 -11
  332. package/src/intake-plane/route.ts +145 -1
  333. package/src/intake-plane/wish-benchmark-ledger.ts +129 -0
  334. package/src/intake-plane/wish-card.ts +53 -2
  335. package/src/intake-plane/wish-folder.ts +5 -2
  336. package/src/intake-plane/wish-reply.ts +72 -0
  337. package/src/launch-head/seat-goal-queue-lock.fixture.ts +15 -0
  338. package/src/launch-head/seat-goal-queue.ts +198 -0
  339. package/src/lessons/lesson-import.ts +106 -0
  340. package/src/lessons/lesson-ledger.ts +206 -0
  341. package/src/loops/activity.ts +203 -0
  342. package/src/loops/budget.ts +63 -0
  343. package/src/loops/checker.ts +5 -3
  344. package/src/loops/contract/examples/orchestrator.loop.yaml +1 -1
  345. package/src/loops/loop-cli.ts +36 -4
  346. package/src/loops/neighbors.ts +40 -0
  347. package/src/loops/observe.ts +10 -0
  348. package/src/loops/orchestrator/author-depth.ts +220 -0
  349. package/src/loops/orchestrator/author-input-check.ts +82 -0
  350. package/src/loops/orchestrator/author-ledger.ts +232 -0
  351. package/src/loops/orchestrator/finish-rate.ts +175 -0
  352. package/src/loops/orchestrator/overlap.ts +51 -12
  353. package/src/loops/orchestrator/resync-shadow.ts +227 -0
  354. package/src/loops/orchestrator/seat-share.ts +56 -0
  355. package/src/loops/orchestrator/tick.ts +309 -0
  356. package/src/loops/orchestrator/traffic.ts +134 -13
  357. package/src/loops/registry.ts +535 -9
  358. package/src/loops/status-rows.ts +118 -0
  359. package/src/loops/verdict.ts +17 -5
  360. package/src/maturity/bot-slash-catalog.ts +66 -0
  361. package/src/maturity/feature-maturity.ts +9 -5
  362. package/src/maturity/tui-slash-availability.ts +31 -0
  363. package/src/mss/logging/log-store.ts +35 -4
  364. package/src/nexus/api/dashboard.ts +6 -1
  365. package/src/nexus/api/http-server.ts +33 -3
  366. package/src/nexus/api/loop-edges.ts +173 -0
  367. package/src/nexus/api/merge-approvals-gate.ts +1 -1
  368. package/src/nexus/api/schedules-read.ts +18 -1
  369. package/src/nexus/api/seat-requests.ts +79 -56
  370. package/src/nexus/api/task-cards-api.ts +44 -5
  371. package/src/nexus/api/workflows.ts +2 -1
  372. package/src/prompt-enhance/enhance.ts +6 -1
  373. package/src/release-loop/auto-start.ts +51 -0
  374. package/src/release-loop/checklist.ts +22 -4
  375. package/src/release-loop/feature-store.ts +71 -32
  376. package/src/release-loop/landing-freeze.ts +121 -0
  377. package/src/release-loop/merged-pr-checklist.ts +125 -0
  378. package/src/release-loop/placement.ts +302 -0
  379. package/src/release-loop/release-schedule.ts +3 -1
  380. package/src/seat-dispatch/ceo-commands.ts +2 -2
  381. package/src/seat-dispatch/seat-ask.ts +16 -3
  382. package/src/seat-dispatch/seat-questions.ts +58 -0
  383. package/src/seat-dispatch/seat-request-eligibility.ts +19 -0
  384. package/src/seat-dispatch/seat-request-ledger.ts +133 -0
  385. package/src/seat-loop/seat-loop.ts +531 -40
  386. package/src/self-dev/dev-pipeline.ts +14 -6
  387. package/src/self-dev/draft-sweep.ts +162 -6
  388. package/src/self-dev/draft-triage-rules.ts +25 -8
  389. package/src/self-dev/orchestrate.ts +15 -1
  390. package/src/self-dev/review-provider-fallback.ts +6 -1
  391. package/src/self-dev/run-store.ts +73 -1
  392. package/src/self-dev/run-supervisor.ts +57 -2
  393. package/src/self-dev/self-orchestrate-runtime.ts +5 -1
  394. package/src/self-implement/budget-gate.ts +29 -0
  395. package/src/self-implement/expose-gate.ts +47 -0
  396. package/src/self-implement/frozen-merges.ts +295 -0
  397. package/src/self-implement/gate-cli.ts +10 -4
  398. package/src/self-implement/gate-scope.ts +46 -9
  399. package/src/self-implement/goal-archive.ts +222 -0
  400. package/src/self-implement/goal-author.ts +43 -5
  401. package/src/self-implement/goal-clarification-escalation.ts +5 -0
  402. package/src/self-implement/goal-lookup.ts +147 -0
  403. package/src/self-implement/heal-intake.ts +197 -0
  404. package/src/self-implement/host-regate.ts +32 -2
  405. package/src/self-implement/l8-merge-queue.ts +132 -0
  406. package/src/self-implement/orchestrator.ts +299 -48
  407. package/src/self-implement/pipeline-shape.ts +1 -1
  408. package/src/self-implement/revert-guard.ts +97 -0
  409. package/src/self-implement/run-ledger.ts +3 -1
  410. package/src/self-implement/run-status-mapping.ts +2 -0
  411. package/src/self-implement/seams.ts +29 -9
  412. package/src/self-implement/self-implement-cli.ts +1 -0
  413. package/src/self-implement/speed-claims.ts +338 -0
  414. package/src/session/index.ts +2 -0
  415. package/src/steward/triage.ts +74 -3
  416. package/src/steward/working-backwards.ts +4 -1
  417. package/src/task-cards/card-cli.ts +10 -2
  418. package/src/task-cards/card-store.ts +10 -6
  419. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +63 -48
  420. package/src/task-orchestrator/surfaces/pod-command-job.ts +84 -9
  421. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +48 -0
  422. package/src/task-orchestrator/surfaces/pod-lease.ts +5 -2
  423. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +46 -6
  424. package/src/task-orchestrator/surfaces/pod-pool.ts +60 -8
  425. package/src/task-orchestrator/surfaces/self-implement-pod.ts +224 -49
  426. package/src/task-orchestrator/surfaces/self-implement.ts +5 -1
  427. package/src/telegram-commands.ts +27 -3
  428. package/src/telegram-loops-command.ts +44 -0
  429. package/src/telegram.ts +25 -4
  430. package/src/user-config.ts +146 -18
  431. package/src/version/packed-revision.json +1 -1
  432. package/src/workflow-runtime/nodes/bash.ts +9 -1
  433. package/src/workflow-runtime/storage.ts +31 -13
  434. package/apps/pwa/out/_next/static/D51l8jUBplJrIpWvENsDK/_buildManifest.js +0 -1
  435. package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +0 -1
  436. package/apps/pwa/out/_next/static/chunks/1394-931c9827067b2276.js +0 -1
  437. package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +0 -1
  438. package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +0 -1
  439. package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +0 -1
  440. package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +0 -1
  441. package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +0 -1
  442. package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +0 -1
  443. package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +0 -1
  444. package/apps/pwa/out/_next/static/chunks/3992-fe5c06d6b31f8b25.js +0 -25
  445. package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +0 -1
  446. package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +0 -1
  447. package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +0 -1
  448. package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +0 -2
  449. package/apps/pwa/out/_next/static/chunks/5250-c78ca295585bbf95.js +0 -1
  450. package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +0 -1
  451. package/apps/pwa/out/_next/static/chunks/5845-e6c003ce8d5fd36a.js +0 -1
  452. package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +0 -1
  453. package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +0 -1
  454. package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +0 -1
  455. package/apps/pwa/out/_next/static/chunks/6511-ff8360be3d65aaa4.js +0 -1
  456. package/apps/pwa/out/_next/static/chunks/6588-ba380869b67c4fa7.js +0 -1
  457. package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +0 -1
  458. package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +0 -1
  459. package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +0 -1
  460. package/apps/pwa/out/_next/static/chunks/7758-85c2b53f731da024.js +0 -1
  461. package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +0 -1
  462. package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +0 -1
  463. package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +0 -1
  464. package/apps/pwa/out/_next/static/chunks/8605-9da4678f8a310d18.js +0 -1
  465. package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +0 -1
  466. package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +0 -1
  467. package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +0 -1
  468. package/apps/pwa/out/_next/static/chunks/9269.d2a4795bc6812717.js +0 -1
  469. package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +0 -1
  470. package/apps/pwa/out/_next/static/chunks/9906-e7b8b5310a169dfe.js +0 -1
  471. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d16993889b125c42.js +0 -1
  472. package/apps/pwa/out/_next/static/chunks/app/approvals/page-e5b4e1b256f41076.js +0 -1
  473. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-e49b9b76c565a0b1.js +0 -1
  474. package/apps/pwa/out/_next/static/chunks/app/board/page-b4f032c893b21cc9.js +0 -1
  475. package/apps/pwa/out/_next/static/chunks/app/botlab/page-f14987d5850a31e0.js +0 -1
  476. package/apps/pwa/out/_next/static/chunks/app/bots/page-b433308aad13ceb6.js +0 -1
  477. package/apps/pwa/out/_next/static/chunks/app/chat/page-4c1dbb21861beab2.js +0 -1
  478. package/apps/pwa/out/_next/static/chunks/app/consult/page-c85b816e3b269dbb.js +0 -1
  479. package/apps/pwa/out/_next/static/chunks/app/control/page-f6bc66b919b2ea7d.js +0 -1
  480. package/apps/pwa/out/_next/static/chunks/app/design-check/page-60603c1e056c9b25.js +0 -1
  481. package/apps/pwa/out/_next/static/chunks/app/editor/page-a0ea8f5a2341ee6b.js +0 -1
  482. package/apps/pwa/out/_next/static/chunks/app/exec/page-45c5bf04b4a605e6.js +0 -1
  483. package/apps/pwa/out/_next/static/chunks/app/field/page-36c6c4183353ac9e.js +0 -1
  484. package/apps/pwa/out/_next/static/chunks/app/healthz/route-d16993889b125c42.js +0 -1
  485. package/apps/pwa/out/_next/static/chunks/app/inside/page-cb2eb0fd809980da.js +0 -1
  486. package/apps/pwa/out/_next/static/chunks/app/intake/page-31012264da8a341f.js +0 -1
  487. package/apps/pwa/out/_next/static/chunks/app/layout-50309aeb0bdd554a.js +0 -1
  488. package/apps/pwa/out/_next/static/chunks/app/live/page-bc00e964e855f4df.js +0 -1
  489. package/apps/pwa/out/_next/static/chunks/app/market/page-0696ee3fac9fe56c.js +0 -1
  490. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66c27608f651b866.js +0 -1
  491. package/apps/pwa/out/_next/static/chunks/app/morning/page-6ddd2109b7fc3a80.js +0 -1
  492. package/apps/pwa/out/_next/static/chunks/app/not-found-4e50b7298c1f69a5.js +0 -1
  493. package/apps/pwa/out/_next/static/chunks/app/observatory/page-7a2bf830038a2264.js +0 -1
  494. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1f327f18694fabca.js +0 -1
  495. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-e5cef2fe6e4028df.js +0 -1
  496. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cf596128d2b420ba.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/app/outputs/page-f672536f14f0969f.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/app/page-b52365662232db89.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/app/reflection/page-e7a6c80f288b1948.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-29a709616df9b050.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/app/sessions/page-fee842d7c2151129.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-78b40a344ed22f24.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/app/settings/page-75d199f448171014.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-a011dd5b0bff29c5.js +0 -1
  505. package/apps/pwa/out/_next/static/chunks/app/setup/layout-d16993889b125c42.js +0 -1
  506. package/apps/pwa/out/_next/static/chunks/app/setup/page-d92e32d51ab5157e.js +0 -1
  507. package/apps/pwa/out/_next/static/chunks/app/share/page-5f21aed6932ef375.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/app/showroom/page-1e1a53946804debf.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/app/tasks/page-a1833cd43d832601.js +0 -1
  510. package/apps/pwa/out/_next/static/chunks/app/today/page-2a511d320f762e06.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/app/trace/page-1c3e03f88dac1e21.js +0 -1
  512. package/apps/pwa/out/_next/static/chunks/app/vault/page-c4d5289c2ea97a89.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b91f8952deaeaf27.js +0 -1
  514. package/apps/pwa/out/_next/static/chunks/app/workflows/page-71b43a2588489987.js +0 -1
  515. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2b256feed331546f.js +0 -1
  516. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-505bb96d69a8622c.js +0 -1
  517. package/apps/pwa/out/_next/static/chunks/framework-172630e6aa5e2d7f.js +0 -1
  518. package/apps/pwa/out/_next/static/chunks/main-app-f0fe2d7477845181.js +0 -1
  519. package/apps/pwa/out/_next/static/chunks/main-fc98ac70b44eccb0.js +0 -1
  520. package/apps/pwa/out/_next/static/chunks/pages/_app-5bd7f10271f72413.js +0 -1
  521. package/apps/pwa/out/_next/static/chunks/pages/_error-97d0d85ea05fef9b.js +0 -1
  522. package/apps/pwa/out/_next/static/chunks/webpack-9e4d02d567b98240.js +0 -1
  523. package/apps/pwa/out/_next/static/css/128fb7fb670b2fbf.css +0 -1
  524. /package/apps/pwa/out/_next/static/{D51l8jUBplJrIpWvENsDK → LZcstaO9_NXypPFmujolv}/_ssgManifest.js +0 -0
@@ -0,0 +1,121 @@
1
+ import { Database } from 'bun:sqlite';
2
+ import { chmodSync, mkdirSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { elanousStateRoot } from '../autopilot/state-paths.js';
5
+
6
+ export type BriefPriority = 'high' | 'medium' | 'low';
7
+
8
+ export interface BriefItem {
9
+ id: number;
10
+ text: string;
11
+ domain: string;
12
+ priority: BriefPriority;
13
+ deadline: string | null;
14
+ evidence: string | null;
15
+ source: string;
16
+ created_at: string;
17
+ }
18
+
19
+ export interface AddBriefItem {
20
+ text: string;
21
+ domain: string;
22
+ priority: BriefPriority;
23
+ deadline?: string | null;
24
+ evidence?: string | null;
25
+ source: string;
26
+ }
27
+
28
+ export interface BriefItemsOptions {
29
+ stateDir?: string;
30
+ now?: () => Date;
31
+ }
32
+
33
+ export class BriefItemsInputError extends Error {
34
+ override name = 'BriefItemsInputError';
35
+ }
36
+
37
+ function required(value: string, field: string): string {
38
+ if (typeof value !== 'string' || !value.trim()) throw new BriefItemsInputError(`${field} is required`);
39
+ return value;
40
+ }
41
+
42
+ function optional(value: string | null | undefined, field: string): string | null {
43
+ return value == null ? null : required(value, field);
44
+ }
45
+
46
+ /** Append-only item ledger. All three operations open the same instance-scoped SQLite file. */
47
+ export class BriefItemsLedger {
48
+ readonly path: string;
49
+ private readonly now: () => Date;
50
+
51
+ constructor(options: BriefItemsOptions = {}) {
52
+ this.path = join(options.stateDir ?? elanousStateRoot(), 'briefing', 'items.sqlite');
53
+ this.now = options.now ?? (() => new Date());
54
+ }
55
+
56
+ private using<T>(work: (db: Database) => T): T {
57
+ mkdirSync(dirname(this.path), { recursive: true });
58
+ const db = new Database(this.path, { create: true, strict: true });
59
+ try {
60
+ chmodSync(this.path, 0o600);
61
+ db.exec('PRAGMA busy_timeout = 10000');
62
+ db.exec(`CREATE TABLE IF NOT EXISTS items (
63
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
64
+ text TEXT NOT NULL, domain TEXT NOT NULL,
65
+ priority TEXT NOT NULL CHECK(priority IN ('high', 'medium', 'low')),
66
+ deadline TEXT, evidence TEXT, source TEXT NOT NULL, created_at TEXT NOT NULL
67
+ );
68
+ CREATE TRIGGER IF NOT EXISTS items_no_update BEFORE UPDATE ON items
69
+ BEGIN SELECT RAISE(ABORT, 'brief items are immutable'); END;
70
+ CREATE TRIGGER IF NOT EXISTS items_no_delete BEFORE DELETE ON items
71
+ BEGIN SELECT RAISE(ABORT, 'brief items are immutable'); END;`);
72
+ return work(db);
73
+ } finally {
74
+ db.close();
75
+ }
76
+ }
77
+
78
+ add(input: AddBriefItem): BriefItem {
79
+ const text = required(input.text, 'text');
80
+ const domain = required(input.domain, 'domain');
81
+ const source = required(input.source, 'source');
82
+ if (!['high', 'medium', 'low'].includes(input.priority)) throw new BriefItemsInputError('invalid priority');
83
+ const deadline = optional(input.deadline, 'deadline');
84
+ if (deadline !== null) {
85
+ const format = /^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2}))?$/;
86
+ if (!format.test(deadline) || !Number.isFinite(Date.parse(deadline))) throw new BriefItemsInputError('invalid deadline');
87
+ }
88
+ const evidence = optional(input.evidence, 'evidence');
89
+ return this.using(db => {
90
+ const createdAt = this.now().toISOString();
91
+ const result = db.query('INSERT INTO items (text, domain, priority, deadline, evidence, source, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)')
92
+ .run(text, domain, input.priority, deadline, evidence, source, createdAt);
93
+ return db.query('SELECT id, text, domain, priority, deadline, evidence, source, created_at FROM items WHERE id = ?')
94
+ .get(result.lastInsertRowid) as BriefItem;
95
+ });
96
+ }
97
+
98
+ /** Higher priorities first, then nearest deadline by instant, then insertion order. */
99
+ list(filters: { domain?: string } = {}): BriefItem[] {
100
+ const items = this.using(db => (filters.domain === undefined
101
+ ? db.query('SELECT id, text, domain, priority, deadline, evidence, source, created_at FROM items').all()
102
+ : db.query('SELECT id, text, domain, priority, deadline, evidence, source, created_at FROM items WHERE domain = ?').all(filters.domain)) as BriefItem[]);
103
+ const rank: Record<BriefPriority, number> = { high: 0, medium: 1, low: 2 };
104
+ return items.sort((a, b) => rank[a.priority] - rank[b.priority]
105
+ || Number(a.deadline === null) - Number(b.deadline === null)
106
+ || (a.deadline !== null && b.deadline !== null ? Date.parse(a.deadline) - Date.parse(b.deadline) : 0)
107
+ || a.id - b.id);
108
+ }
109
+
110
+ /** Pure Markdown output: composing never marks, alters, or sends stored items. */
111
+ compose(slot: string, filters: { domain?: string } = {}): string {
112
+ required(slot, 'slot');
113
+ const items = this.list(filters);
114
+ const lines = [`# Briefing — ${slot}`, ''];
115
+ for (const item of items) {
116
+ lines.push(`- [${item.priority}] ${item.text} (${item.domain})`);
117
+ lines.push(` - Source: ${item.source}${item.deadline ? ` · Deadline: ${item.deadline}` : ''}${item.evidence ? ` · Evidence: ${item.evidence}` : ''}`);
118
+ }
119
+ return `${lines.join('\n').trimEnd()}\n`;
120
+ }
121
+ }
package/src/chat/index.ts CHANGED
@@ -452,6 +452,7 @@ export const SLASH_COMMANDS: SlashCommand[] = [
452
452
  { name: 'reasoning', aliases: ['r', 'think'], description: 'Reasoning level (codex effort + summary, anthropic extended-thinking budget) — /reasoning [off|low|medium|high|xhigh] (cycle when no arg · xhigh 는 모델 상한이 xhigh 이상일 때만 wire 에 실린다, 아니면 high 로 깎인다)', subcommands: ['off', 'low', 'medium', 'high', 'xhigh'] },
453
453
  { name: 'local', aliases: ['ll'], description: 'Local OpenAI-compatible LLM — ping / models / test / use', subcommands: ['ping', 'models', 'test', 'use', 'status'] },
454
454
  { name: 'session', aliases: ['sess'], description: 'Session resume — list / load / sync / new (handoff from mobile)', subcommands: ['list', 'load', 'sync', 'new'] },
455
+ { name: 'persona', aliases: [], description: '이 대화의 페르소나 보기·고르기·빼기 — /persona [이름|-]', subcommands: ['-'] },
455
456
  { name: 'fork', aliases: [], description: 'Copy this session into a new one' },
456
457
  { name: 'rewind', aliases: [], description: 'Rewind — 과거 user 턴 픽커로 되감기 (원본 보존 · 새 세션 분기 · /rewind <n> 숏컷)' },
457
458
  // TUI 부활 C-a (2026-07-12): autopilot 미션 TUI 표면 — CLI 동등 단일 창구 소비.
@@ -463,6 +464,7 @@ export const SLASH_COMMANDS: SlashCommand[] = [
463
464
  { name: 'plugin', aliases: ['plugins'], description: 'Manage plugins', subcommands: ['list', 'activate', 'deactivate', 'reload'] },
464
465
  { name: 'widget', aliases: ['widgets'], description: 'Manage widgets', subcommands: ['list', 'reload', 'instances'] },
465
466
  { name: 'log', aliases: [], description: 'Chat Log controls — size / clear / filter / search / freeze / solo / turn / fold / help', subcommands: ['size', 'clear', 'filter', 'search', 'freeze', 'solo', 'turn', 'fold', 'help'] },
467
+ { name: 'loops', aliases: [], description: '루프와 크론 상태 표 — /loops [owner]' },
466
468
  { name: 'memory', aliases: ['mem'], description: 'Memory ops — list / show / search / add / delete (see `elanous memory --help`)', subcommands: ['list', 'show', 'search', 'add', 'delete', 'index'] },
467
469
  { name: 'export', aliases: [], description: 'Export this conversation transcript to a markdown file — /export [path] (default ~/temp/elanous-transcript-<stamp>.md)' },
468
470
  { name: 'delta', aliases: ['diffs'], description: 'Source delta browser — open the latest turn-scoped file diff popup', subcommands: ['open', 'help'] },
@@ -0,0 +1,232 @@
1
+ import { Database } from 'bun:sqlite';
2
+ import { chmodSync, mkdirSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { elanousStateRoot } from '../autopilot/state-paths.js';
5
+ import { debug } from '../debug/log.js';
6
+
7
+ export type ClaimStatus = 'draft' | 'verified' | 'public' | 'stale' | 'retracted';
8
+
9
+ export class ClaimsInputError extends Error {
10
+ override name = 'ClaimsInputError';
11
+ }
12
+
13
+ const claimsTable = (name: 'claims' | 'claims_new') => `CREATE TABLE ${name} (
14
+ id TEXT PRIMARY KEY, claim TEXT NOT NULL, audience TEXT NOT NULL,
15
+ status TEXT NOT NULL CHECK(status IN ('draft','verified','public','stale','retracted')),
16
+ owner TEXT NOT NULL, contrast TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL)`;
17
+ export interface ClaimRow {
18
+ id: string;
19
+ claim: string;
20
+ audience: string;
21
+ status: ClaimStatus;
22
+ owner: string;
23
+ contrast: string | null;
24
+ created_at: string;
25
+ updated_at: string;
26
+ }
27
+ export interface ClaimEvidence { claim_id: string; value: string; command: string; source: string | null; measured_at: string; valid_until: string }
28
+ export interface ClaimLink { claim_id: string; cell: string; version: string | null }
29
+ export interface ClaimHistory { claim_id: string; at: string; by: string; event: 'add' | 'verify' | 'publish' | 'link' | 'stale' | 'retract'; detail: string }
30
+ export interface ClaimDetail extends ClaimRow { evidence: ClaimEvidence[]; links: ClaimLink[]; history: ClaimHistory[] }
31
+ export interface ClaimsLedgerOptions { stateDir?: string; now?: () => Date }
32
+
33
+ function required(value: string, field: string): string {
34
+ if (typeof value !== 'string' || !value.trim()) throw new ClaimsInputError(`${field} is required`);
35
+ return value.trim();
36
+ }
37
+ function iso(value: string, field: string): string {
38
+ if (typeof value !== 'string' || !/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.\d+)?(?:Z|[+-]\d\d:\d\d)$/.test(value)) throw new ClaimsInputError(`invalid ${field}: ${value}`);
39
+ const date = new Date(value);
40
+ if (!Number.isFinite(date.getTime())) throw new ClaimsInputError(`invalid ${field}: ${value}`);
41
+ return date.toISOString();
42
+ }
43
+
44
+ export class ClaimsLedger {
45
+ readonly path: string;
46
+ private readonly now: () => Date;
47
+
48
+ constructor(options: ClaimsLedgerOptions = {}) {
49
+ this.path = join(options.stateDir ?? elanousStateRoot(), 'claims', 'claims.sqlite');
50
+ this.now = options.now ?? (() => new Date());
51
+ }
52
+
53
+ private open(): Database {
54
+ mkdirSync(dirname(this.path), { recursive: true });
55
+ const db = new Database(this.path, { create: true, strict: true });
56
+ try {
57
+ chmodSync(this.path, 0o600);
58
+ db.exec('PRAGMA busy_timeout = 10000');
59
+ db.exec('PRAGMA journal_mode = WAL');
60
+ db.exec('PRAGMA foreign_keys = ON');
61
+ db.exec(`${claimsTable('claims').replace('CREATE TABLE', 'CREATE TABLE IF NOT EXISTS')};
62
+ CREATE TABLE IF NOT EXISTS evidence (
63
+ claim_id TEXT NOT NULL REFERENCES claims(id), value TEXT NOT NULL, command TEXT NOT NULL,
64
+ source TEXT, measured_at TEXT NOT NULL, valid_until TEXT NOT NULL);
65
+ CREATE TABLE IF NOT EXISTS links (
66
+ claim_id TEXT NOT NULL REFERENCES claims(id), cell TEXT NOT NULL, version TEXT);
67
+ CREATE TABLE IF NOT EXISTS history (
68
+ claim_id TEXT NOT NULL REFERENCES claims(id), at TEXT NOT NULL, by TEXT NOT NULL,
69
+ event TEXT NOT NULL, detail TEXT NOT NULL);`);
70
+ const schema = (db.query("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'claims'").get() as { sql: string }).sql;
71
+ if (!schema.includes("'retracted'")) {
72
+ db.exec('PRAGMA foreign_keys = OFF');
73
+ try {
74
+ db.exec('BEGIN IMMEDIATE');
75
+ try {
76
+ // Another opener may have migrated while this connection waited for the write lock.
77
+ const current = (db.query("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'claims'").get() as { sql: string }).sql;
78
+ if (!current.includes("'retracted'")) {
79
+ db.exec(claimsTable('claims_new'));
80
+ db.exec('INSERT INTO claims_new SELECT * FROM claims');
81
+ db.exec('DROP TABLE claims');
82
+ db.exec('ALTER TABLE claims_new RENAME TO claims');
83
+ if ((db.query('PRAGMA foreign_key_check').all() as unknown[]).length) throw new Error('claims migration foreign key check failed');
84
+ }
85
+ db.exec('COMMIT');
86
+ } catch (error) { db.exec('ROLLBACK'); throw error; }
87
+ } finally { db.exec('PRAGMA foreign_keys = ON'); }
88
+ }
89
+ return db;
90
+ } catch (error) { db.close(); throw error; }
91
+ }
92
+
93
+ private using<T>(work: (db: Database) => T): T {
94
+ const db = this.open();
95
+ try { return work(db); } finally { db.close(); }
96
+ }
97
+
98
+ private transaction<T>(db: Database, work: () => T): T {
99
+ db.exec('BEGIN IMMEDIATE');
100
+ try { const result = work(); db.exec('COMMIT'); return result; }
101
+ catch (error) { db.exec('ROLLBACK'); throw error; }
102
+ }
103
+
104
+ private row(db: Database, id: string): ClaimRow {
105
+ const row = db.query('SELECT * FROM claims WHERE id = ?').get(id) as ClaimRow | null;
106
+ if (!row) throw new ClaimsInputError(`claim not found: ${id}`);
107
+ return row;
108
+ }
109
+
110
+ private latest(db: Database, id: string): ClaimEvidence | null {
111
+ return db.query('SELECT claim_id, value, command, source, measured_at, valid_until FROM evidence WHERE claim_id = ? ORDER BY measured_at DESC, rowid DESC LIMIT 1').get(id) as ClaimEvidence | null;
112
+ }
113
+
114
+ private record(db: Database, id: string, at: string, by: string, event: ClaimHistory['event'], detail: string): void {
115
+ db.query('INSERT INTO history (claim_id, at, by, event, detail) VALUES (?, ?, ?, ?, ?)').run(id, at, by, event, detail);
116
+ }
117
+
118
+ private refresh(db: Database, row: ClaimRow, at: string): ClaimRow {
119
+ if (row.status === 'stale' || row.status === 'draft' || row.status === 'retracted') return row;
120
+ const evidence = this.latest(db, row.id);
121
+ if (!evidence || evidence.valid_until > at) return row;
122
+ db.query("UPDATE claims SET status = 'stale', updated_at = ? WHERE id = ?").run(at, row.id);
123
+ this.record(db, row.id, at, 'system', 'stale', `evidence expired: ${evidence.valid_until}`);
124
+ debug.log('claims.ledger', 'stale', { id: row.id, status: 'stale' });
125
+ return { ...row, status: 'stale', updated_at: at };
126
+ }
127
+
128
+ add(input: { id: string; claim: string; audience: string; owner: string; contrast?: string }): ClaimRow {
129
+ const id = required(input.id, 'id');
130
+ if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(id)) throw new ClaimsInputError('invalid claim id');
131
+ const claim = required(input.claim, 'claim');
132
+ const audience = required(input.audience, 'audience').split(',').map(part => required(part, 'audience')).join(',');
133
+ const owner = required(input.owner, 'owner');
134
+ const contrast = input.contrast === undefined ? null : required(input.contrast, 'contrast');
135
+ return this.using(db => this.transaction(db, () => {
136
+ if (db.query('SELECT 1 FROM claims WHERE id = ?').get(id)) throw new ClaimsInputError(`claim already exists: ${id}`);
137
+ const at = this.now().toISOString();
138
+ db.query('INSERT INTO claims (id, claim, audience, status, owner, contrast, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)')
139
+ .run(id, claim, audience, 'draft', owner, contrast, at, at);
140
+ this.record(db, id, at, owner, 'add', claim);
141
+ debug.log('claims.ledger', 'add', { id, status: 'draft' });
142
+ return this.row(db, id);
143
+ }));
144
+ }
145
+
146
+ verify(id: string, input: { value: string; command: string; source?: string; measuredAt: string; validUntil: string; by: string }): ClaimRow {
147
+ const value = required(input.value, 'value');
148
+ const command = required(input.command, 'command');
149
+ const by = required(input.by, 'by');
150
+ const measuredAt = iso(input.measuredAt, 'measuredAt');
151
+ const validUntil = iso(input.validUntil, 'validUntil');
152
+ if (validUntil <= measuredAt) throw new ClaimsInputError('validUntil must be after measuredAt');
153
+ const source = input.source === undefined ? null : required(input.source, 'source');
154
+ return this.using(db => this.transaction(db, () => {
155
+ if (this.row(db, id).status === 'retracted') throw new ClaimsInputError(`claim retracted: ${id}`);
156
+ const at = this.now().toISOString();
157
+ db.query('INSERT INTO evidence (claim_id, value, command, source, measured_at, valid_until) VALUES (?, ?, ?, ?, ?, ?)')
158
+ .run(id, value, command, source, measuredAt, validUntil);
159
+ db.query("UPDATE claims SET status = 'verified', updated_at = ? WHERE id = ?").run(at, id);
160
+ this.record(db, id, at, by, 'verify', value);
161
+ debug.log('claims.ledger', 'verify', { id, status: 'verified' });
162
+ return this.row(db, id);
163
+ }));
164
+ }
165
+
166
+ publish(id: string, by: string): ClaimRow {
167
+ const actor = required(by, 'by');
168
+ return this.using(db => {
169
+ const published = this.transaction(db, () => {
170
+ const at = this.now().toISOString();
171
+ const row = this.refresh(db, this.row(db, id), at);
172
+ const evidence = this.latest(db, id);
173
+ if (row.status !== 'verified' || !evidence || evidence.valid_until <= at) return null;
174
+ db.query("UPDATE claims SET status = 'public', updated_at = ? WHERE id = ?").run(at, id);
175
+ this.record(db, id, at, actor, 'publish', evidence.value);
176
+ return this.row(db, id);
177
+ });
178
+ if (!published) throw new ClaimsInputError(`claim requires verified, valid evidence: ${id}`);
179
+ debug.log('claims.ledger', 'publish', { id, status: 'public' });
180
+ return published;
181
+ });
182
+ }
183
+
184
+ link(id: string, input: { cell: string; version?: string }): ClaimRow {
185
+ const cell = required(input.cell, 'cell');
186
+ const version = input.version === undefined ? null : required(input.version, 'version');
187
+ return this.using(db => this.transaction(db, () => {
188
+ const row = this.row(db, id);
189
+ const at = this.now().toISOString();
190
+ db.query('INSERT INTO links (claim_id, cell, version) VALUES (?, ?, ?)').run(id, cell, version);
191
+ db.query('UPDATE claims SET updated_at = ? WHERE id = ?').run(at, id);
192
+ this.record(db, id, at, 'system', 'link', version ? `${cell} (${version})` : cell);
193
+ debug.log('claims.ledger', 'link', { id, status: row.status });
194
+ return this.row(db, id);
195
+ }));
196
+ }
197
+
198
+ retract(id: string, input: { reason: string; by: string }): ClaimRow {
199
+ const reason = required(input.reason, 'reason');
200
+ const by = required(input.by, 'by');
201
+ return this.using(db => this.transaction(db, () => {
202
+ if (this.row(db, id).status === 'retracted') throw new ClaimsInputError(`claim already retracted: ${id}`);
203
+ const at = this.now().toISOString();
204
+ db.query("UPDATE claims SET status = 'retracted', updated_at = ? WHERE id = ?").run(at, id);
205
+ this.record(db, id, at, by, 'retract', reason);
206
+ debug.log('claims.ledger', 'retract', { id, status: 'retracted' });
207
+ return this.row(db, id);
208
+ }));
209
+ }
210
+
211
+ list(filters: { status?: ClaimStatus; audience?: string } = {}): ClaimRow[] {
212
+ if (filters.status && !['draft', 'verified', 'public', 'stale', 'retracted'].includes(filters.status)) throw new ClaimsInputError('invalid status');
213
+ return this.using(db => this.transaction(db, () => {
214
+ const at = this.now().toISOString();
215
+ return (db.query('SELECT * FROM claims ORDER BY created_at, id').all() as ClaimRow[])
216
+ .map(row => this.refresh(db, row, at))
217
+ .filter(row => (!filters.status || row.status === filters.status)
218
+ && (!filters.audience || row.audience.split(',').includes(filters.audience)));
219
+ }));
220
+ }
221
+
222
+ get(id: string): ClaimDetail {
223
+ return this.using(db => this.transaction(db, () => {
224
+ const row = this.refresh(db, this.row(db, id), this.now().toISOString());
225
+ return { ...row,
226
+ evidence: db.query('SELECT claim_id, value, command, source, measured_at, valid_until FROM evidence WHERE claim_id = ? ORDER BY measured_at, rowid').all(id) as ClaimEvidence[],
227
+ links: db.query('SELECT claim_id, cell, version FROM links WHERE claim_id = ? ORDER BY rowid').all(id) as ClaimLink[],
228
+ history: db.query('SELECT claim_id, at, by, event, detail FROM history WHERE claim_id = ? ORDER BY rowid').all(id) as ClaimHistory[],
229
+ };
230
+ }));
231
+ }
232
+ }
@@ -0,0 +1,166 @@
1
+ import { Database } from 'bun:sqlite';
2
+ import { appendFileSync, existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { dirname, join } from 'node:path';
5
+ import { BRAND_RULES_UNMEASURED, checkBrand } from '../../scripts/brand/check.js';
6
+ import { debug } from '../debug/log.js';
7
+ import { effectiveInstanceRoot } from '../instance/resolve.js';
8
+ import type { ClaimsLedger, ClaimEvidence } from './claims-ledger.js';
9
+
10
+ export type ClaimSurface = 'deck' | 'site' | 'notice';
11
+ export interface RenderedClaims {
12
+ markdown: string;
13
+ included: string[];
14
+ excluded: Array<{ id: string; reason: 'stale' | 'not-public' | 'retracted' | 'expired' | 'brand' | 'ambiguous-value' | 'rules-missing' }>;
15
+ unmeasured?: string;
16
+ }
17
+
18
+ const headings: Record<ClaimSurface, string> = {
19
+ deck: '## 왜 엘라누스인가',
20
+ site: '## 엘라누스가 하는 일',
21
+ notice: '### 이번 판에서 확인된 것',
22
+ };
23
+ const scopes: Record<ClaimSurface, 'deck' | 'site' | 'release-notes'> = {
24
+ deck: 'deck', site: 'site', notice: 'release-notes',
25
+ };
26
+
27
+ function latest(evidence: ClaimEvidence[]): ClaimEvidence | undefined {
28
+ return evidence.reduce<ClaimEvidence | undefined>((best, item) =>
29
+ !best || item.measured_at >= best.measured_at ? item : best, undefined);
30
+ }
31
+
32
+ function commentValue(value: string): string {
33
+ return value.replace(/<!--|-->|\r|\n/g, ' ').replace(/\s+/g, ' ');
34
+ }
35
+
36
+ function firstSentence(claim: string): string {
37
+ const sentence = claim.split(/\r?\n|(?<=[.!?。!?])\s+/u)[0];
38
+ return sentence?.trim() ?? '';
39
+ }
40
+
41
+ /**
42
+ * The big number to show is the one measured value that carries a unit (420만+ 줄 · 37% · 12배 …).
43
+ * Dates, times and bare numbers are not values; the first unit-bearing number wins, none is ambiguous — exclude
44
+ * rather than publish the wrong one (review round 3: «측정일 2026-10-03, 결과 420만+ 줄» used to print 2026).
45
+ */
46
+ export function representativeNumber(value: string): string | undefined {
47
+ const withoutDates = value.replace(/\b\d{4}-\d{2}-\d{2}(?:[T ][\d:.]+Z?)?\b/g, ' ').replace(/\b\d{1,2}:\d{2}(?::\d{2})?\b/g, ' ');
48
+ const matches = [...withoutDates.matchAll(/\d[\d,.]*\s*(?:만\+?\s*줄|만\+|만|억|%|배|명|건|줄|개월|lines?\b|PRs?\b|x\b)\+?/gi)].map(match => match[0].trim());
49
+ return matches[0];
50
+ }
51
+
52
+ export function renderClaims(ledger: ClaimsLedger, options: { surface: ClaimSurface; audience?: string; rulesPath?: string }): RenderedClaims {
53
+ const { surface, audience } = options;
54
+ if (!(surface in headings)) throw new Error(`invalid claims surface: ${surface}`);
55
+ const excluded: RenderedClaims['excluded'] = [];
56
+ const candidates: Array<{ id: string; line: string }> = [];
57
+ for (const row of ledger.list()) {
58
+ if (audience && !row.audience.split(',').includes(audience)) continue;
59
+ if (row.status !== 'public') {
60
+ excluded.push({ id: row.id, reason: row.status === 'stale' ? 'stale' : row.status === 'retracted' ? 'retracted' : 'not-public' });
61
+ continue;
62
+ }
63
+ const detail = ledger.get(row.id);
64
+ if (detail.status !== 'public') {
65
+ excluded.push({ id: row.id, reason: 'stale' });
66
+ continue;
67
+ }
68
+ const evidence = latest(detail.evidence);
69
+ if (!evidence) {
70
+ excluded.push({ id: row.id, reason: 'expired' });
71
+ continue;
72
+ }
73
+ const claim = firstSentence(row.claim);
74
+ const number = representativeNumber(evidence.value);
75
+ if (!number) {
76
+ excluded.push({ id: row.id, reason: 'ambiguous-value' });
77
+ continue;
78
+ }
79
+ if (!claim) {
80
+ excluded.push({ id: row.id, reason: 'brand' });
81
+ continue;
82
+ }
83
+ candidates.push({ id: row.id, line: `- ${claim} — ${number} <!-- claim:${row.id} measured:${evidence.measured_at} cmd:${commentValue(evidence.command)}${evidence.source ? ` source:${commentValue(evidence.source)}` : ''} --> <!-- src:${commentValue(evidence.source ?? evidence.command)} -->` });
84
+ }
85
+
86
+ const rulesMissing = checkBrand(scopes[surface], [], options.rulesPath).missing;
87
+ if (rulesMissing) {
88
+ excluded.push(...candidates.map(({ id }) => ({ id, reason: 'rules-missing' as const })));
89
+ const result: RenderedClaims = { markdown: `${headings[surface]}\n\n> ⚠ ${BRAND_RULES_UNMEASURED}\n`, included: [], excluded, unmeasured: BRAND_RULES_UNMEASURED };
90
+ debug.log('claims.render', 'rendered', { surface, included: result.included, excluded, unmeasured: result.unmeasured });
91
+ return result;
92
+ }
93
+
94
+ const temp = mkdtempSync(join(tmpdir(), 'claims-render-'));
95
+ const file = join(temp, 'claims.md');
96
+ try {
97
+ const probe = (items: typeof candidates): Set<string> => {
98
+ writeFileSync(file, [headings[surface], '', ...items.map(item => item.line)].join('\n').trimEnd() + '\n');
99
+ const result = checkBrand(scopes[surface], [file], options.rulesPath);
100
+ if (result.missing) throw new Error(BRAND_RULES_UNMEASURED);
101
+ const bad = new Set<string>();
102
+ for (const finding of result.findings) {
103
+ const owner = items[finding.line - 3];
104
+ if (!owner) throw new Error('brand finding outside claim lines');
105
+ bad.add(owner.id);
106
+ }
107
+ return bad;
108
+ };
109
+ let remaining = candidates;
110
+ while (remaining.length) {
111
+ const bad = probe(remaining);
112
+ if (!bad.size) break;
113
+ const next = remaining.filter(item => {
114
+ if (!bad.has(item.id)) return true;
115
+ excluded.push({ id: item.id, reason: 'brand' });
116
+ return false;
117
+ });
118
+ remaining = next;
119
+ }
120
+ const included = remaining.map(item => item.id);
121
+ const markdown = [headings[surface], '', ...remaining.map(item => item.line)].join('\n').trimEnd() + '\n';
122
+ debug.log('claims.render', 'rendered', { surface, included, excluded });
123
+ return { markdown, included, excluded };
124
+ } finally { rmSync(temp, { recursive: true, force: true }); }
125
+ }
126
+
127
+ export function queueStaleRechecks(ledger: ClaimsLedger, options: { root?: string; now?: Date } = {}): number {
128
+ const stale = ledger.list({ status: 'stale' });
129
+ if (!stale.length) {
130
+ debug.log('claims.recheck', 'queued', { count: 0 });
131
+ return 0;
132
+ }
133
+ const now = options.now ?? new Date();
134
+ const parts = new Intl.DateTimeFormat('en-US', { timeZone: 'Asia/Seoul', year: 'numeric', month: '2-digit', day: '2-digit' }).formatToParts(now);
135
+ const part = (type: string) => parts.find(p => p.type === type)!.value;
136
+ const day = `${part('year')}-${part('month')}-${part('day')}`;
137
+ const path = join(options.root ?? effectiveInstanceRoot(), 'seat-requests', 'requests.jsonl');
138
+ mkdirSync(dirname(path), { recursive: true });
139
+ const lock = new Database(`${path}.loop-checker.lock.sqlite`);
140
+ try {
141
+ lock.exec('PRAGMA busy_timeout = 10000');
142
+ lock.exec('BEGIN EXCLUSIVE');
143
+ try {
144
+ const existing = new Set<string>();
145
+ if (existsSync(path)) for (const line of readFileSync(path, 'utf8').split('\n')) {
146
+ if (line.trim()) {
147
+ const row = JSON.parse(line) as { key?: string };
148
+ if (row.key) existing.add(row.key);
149
+ }
150
+ }
151
+ let count = 0;
152
+ for (const claim of stale) {
153
+ const key = `claims-recheck:${claim.id}:${day}`;
154
+ if (existing.has(key)) continue;
155
+ const command = latest(ledger.get(claim.id).evidence)?.command ?? '없음';
156
+ appendFileSync(path, JSON.stringify({ key, seat: claim.owner, text: `${claim.id} 근거 낡음 — 재측: ${command}`,
157
+ source: 'claims', status: 'queued', queuedAt: now.toISOString() }) + '\n');
158
+ existing.add(key);
159
+ count++;
160
+ }
161
+ lock.exec('COMMIT');
162
+ debug.log('claims.recheck', 'queued', { count });
163
+ return count;
164
+ } catch (error) { lock.exec('ROLLBACK'); throw error; }
165
+ } finally { lock.close(); }
166
+ }
@@ -0,0 +1,21 @@
1
+ import type { Command } from 'commander';
2
+ import { writeFileSync } from 'node:fs';
3
+ import { captureAgentEnv } from '../agent-env/profile.js';
4
+
5
+ export function registerAgentEnvCommands(program: Command): void {
6
+ const agentEnv = program.command('agent-env').description('에이전트 CLI 환경의 비밀 없는 프로필');
7
+ agentEnv.command('capture').description('로컬 Claude · Codex · Grok 설정을 읽기 전용으로 캡처')
8
+ .option('--agent <agent>', 'claude|codex|grok')
9
+ .option('--out <file>', '프로필을 저장할 파일')
10
+ .option('--json', 'JSON 출력')
11
+ .action((opts: { agent?: string; out?: string; json?: boolean }) => {
12
+ if (opts.agent && !['claude', 'codex', 'grok'].includes(opts.agent)) {
13
+ throw new Error('--agent must be claude, codex or grok');
14
+ }
15
+ const profile = captureAgentEnv({ agent: opts.agent as 'claude' | 'codex' | 'grok' | undefined });
16
+ const text = JSON.stringify(profile, null, 2) + '\n';
17
+ if (opts.out) writeFileSync(opts.out, text, { encoding: 'utf8', mode: 0o600 });
18
+ if (opts.json || !opts.out) process.stdout.write(text);
19
+ else console.log(`agent-env capture · ${opts.out}`);
20
+ });
21
+ }
@@ -0,0 +1,94 @@
1
+ import type { Command } from 'commander';
2
+ import { ClaimsInputError, ClaimsLedger, type ClaimsLedgerOptions, type ClaimRow, type ClaimDetail } from '../claims/claims-ledger.js';
3
+ import { renderClaims, queueStaleRechecks, type ClaimSurface } from '../claims/claims-render.js';
4
+
5
+ function actor(explicit?: string): string {
6
+ const by = explicit?.trim() || process.env.AI_AGENT?.trim();
7
+ if (!by) throw new ClaimsInputError('--by or AI_AGENT is required');
8
+ return by;
9
+ }
10
+
11
+ function validUntil(raw: string, now: Date): string {
12
+ const days = /^\+(\d+)d$/.exec(raw);
13
+ const parsed = days ? new Date(now.getTime() + Number(days[1]) * 86400000) : new Date(raw);
14
+ if (!Number.isFinite(parsed.getTime()) || (!days && !/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.\d+)?(?:Z|[+-]\d\d:\d\d)$/.test(raw))) throw new ClaimsInputError(`invalid --valid-until: ${raw}`);
15
+ return parsed.toISOString();
16
+ }
17
+
18
+ function handle<Args extends unknown[]>(command: string, action: (...args: Args) => void): (...args: Args) => void {
19
+ return (...args) => {
20
+ try { action(...args); }
21
+ catch (error) {
22
+ if (!(error instanceof ClaimsInputError)) throw error;
23
+ const message = error.message.replace(/\r/g, '\\r').replace(/\n/g, '\\n')
24
+ .replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029');
25
+ process.stderr.write(`claims ${command}: ${message}\n`);
26
+ process.exitCode = 1;
27
+ }
28
+ };
29
+ }
30
+
31
+ function format(row: ClaimRow): string {
32
+ return `${row.id} · ${row.status} · ${row.claim} · ${row.audience} · ${row.owner}`;
33
+ }
34
+ function detail(row: ClaimDetail): string {
35
+ return [format(row), ...(row.contrast ? [`대비: ${row.contrast}`] : []),
36
+ '근거:', ...row.evidence.map(e => ` ${e.value} · ${e.command} · 측정 ${e.measured_at} · 유효 ${e.valid_until}${e.source ? ` · ${e.source}` : ''}`),
37
+ '연결:', ...row.links.map(l => ` ${l.cell}${l.version ? ` · ${l.version}` : ''}`),
38
+ '이력:', ...row.history.map(h => ` ${h.at} · ${h.event} · ${h.by} · ${h.detail}`)].join('\n');
39
+ }
40
+
41
+ export function registerClaimsCommands(program: Command, options: ClaimsLedgerOptions & { instanceRoot?: string } = {}, out: Pick<Console, 'log'> = console): void {
42
+ const root = program.command('claims').description('소구점 · 실측 근거 원장');
43
+ const ledger = () => new ClaimsLedger(options);
44
+ const emit = (value: unknown, json: boolean | undefined, text: string) => out.log(json ? JSON.stringify(value) : text);
45
+ root.command('add <id>').requiredOption('--claim <text>').requiredOption('--audience <list>').requiredOption('--owner <seat>')
46
+ .option('--contrast <text>').action(handle('add', (id: string, o: { claim: string; audience: string; owner: string; contrast?: string }) => {
47
+ const row = ledger().add({ id, claim: o.claim, audience: o.audience, owner: o.owner, ...(o.contrast ? { contrast: o.contrast } : {}) });
48
+ emit(row, false, format(row));
49
+ }));
50
+ root.command('verify <id>').requiredOption('--value <value>').requiredOption('--command <command>')
51
+ .requiredOption('--valid-until <ISO|+Nd>').option('--source <url>').option('--by <name>')
52
+ .action(handle('verify', (id: string, o: { value: string; command: string; validUntil: string; source?: string; by?: string }) => {
53
+ const by = actor(o.by);
54
+ const now = options.now?.() ?? new Date();
55
+ const row = ledger().verify(id, { value: o.value, command: o.command, measuredAt: now.toISOString(), validUntil: validUntil(o.validUntil, now), by, ...(o.source ? { source: o.source } : {}) });
56
+ emit(row, false, format(row));
57
+ }));
58
+ root.command('publish <id>').option('--by <name>').action(handle('publish', (id: string, o: { by?: string }) => {
59
+ const row = ledger().publish(id, actor(o.by));
60
+ emit(row, false, format(row));
61
+ }));
62
+ root.command('link <id>').requiredOption('--cell <cell>').option('--version <version>')
63
+ .action(handle('link', (id: string, o: { cell: string; version?: string }) => {
64
+ const row = ledger().link(id, { cell: o.cell, ...(o.version ? { version: o.version } : {}) });
65
+ emit(row, false, format(row));
66
+ }));
67
+ root.command('retract <id>').requiredOption('--reason <text>').option('--by <name>')
68
+ .action(handle('retract', (id: string, o: { reason: string; by?: string }) => {
69
+ const row = ledger().retract(id, { reason: o.reason, by: actor(o.by) });
70
+ emit(row, false, format(row));
71
+ }));
72
+ root.command('list').option('--status <status>').option('--audience <audience>').option('--json')
73
+ .action(handle('list', (o: { status?: ClaimRow['status']; audience?: string; json?: boolean }) => {
74
+ const rows = ledger().list({ status: o.status, audience: o.audience });
75
+ emit(rows, o.json, rows.length ? rows.map(format).join('\n') : '소구점 0건');
76
+ }));
77
+ root.command('show <id>').option('--json').action(handle('show', (id: string, o: { json?: boolean }) => {
78
+ const row = ledger().get(id);
79
+ emit(row, o.json, detail(row));
80
+ }));
81
+ root.command('render').requiredOption('--surface <surface>', 'deck | site | notice')
82
+ .option('--audience <audience>').option('--json')
83
+ .action(handle('render', (o: { surface: ClaimSurface; audience?: string; json?: boolean }) => {
84
+ const result = renderClaims(ledger(), { surface: o.surface, ...(o.audience ? { audience: o.audience } : {}) });
85
+ // Review follow-up: rendering is where stale claims surface — queue their re-measurement in the same act.
86
+ const rechecks = result.excluded.some(item => item.reason === 'stale')
87
+ ? queueStaleRechecks(ledger(), { root: options.instanceRoot, now: options.now?.() }) : 0;
88
+ emit({ ...result, rechecks }, o.json, rechecks ? `${result.markdown}\n<!-- 재측 요청 ${rechecks}건 -->` : result.markdown);
89
+ }));
90
+ root.command('recheck').option('--json').action(handle('recheck', (o: { json?: boolean }) => {
91
+ const count = queueStaleRechecks(ledger(), { root: options.instanceRoot, now: options.now?.() });
92
+ emit({ count }, o.json, `재측 요청 ${count}건`);
93
+ }));
94
+ }