elanous 0.2.9 → 0.2.11

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 (694) 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/Y4zKzL62sakG5momz3SoQ/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1394-556de6d71bbdb0fb.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1452.93700483b9d4fc61.js +9 -0
  7. package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 1829-b453a1b22d3999bb.js} +1 -1
  8. package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/2310-7a59f043f6db207e.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/2548-6e316138d347cefe.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/3869-a39d1d9921e10d43.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/390.c373e31b3b9d0c41.js +5 -0
  19. package/apps/pwa/out/_next/static/chunks/3992-2e522a8ac5f2afde.js +25 -0
  20. package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/4685-c3c1c625b71accd1.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +2 -0
  25. package/apps/pwa/out/_next/static/chunks/507-5acd98bd6dab2d8a.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/5445-745eb795c5963d79.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/5845-e6c003ce8d5fd36a.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → 5cd7e3a9-404fd6b8d0ecd44c.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 7334.a6679d9168f37aa8.js} +1 -1
  36. package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/79f8be9e-957d68913b590b08.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/8039-df92e08fede5de8f.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/8421-1c4acfb1395a0edf.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/8605-ea5bee9dfae512c7.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/{2415.ed6428ff334c7ce2.js → 9269.d2a4795bc6812717.js} +1 -1
  48. package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/9656-c1540088f5859c80.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/9906-482c6619b3fdda20.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d16993889b125c42.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/approvals/page-1dcdc58fc2de2454.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-6a061bd97f0b9398.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/board/page-cb95a7432c9ee744.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-f972fc1c00a62e88.js → page-b12b6d2f8d2d1aea.js} +1 -1
  56. package/apps/pwa/out/_next/static/chunks/app/bots/page-b9efe868bfd36255.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/chat/page-7c97dcdfe2e37a60.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/consult/page-8fd36984f549270a.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/control/page-5721537ca8a3598c.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/design-check/page-b654240a572e4c48.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/editor/page-b9750abc897508c3.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/exec/page-4cd1de371f8ad048.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/field/page-d99f89657828421c.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/healthz/route-d16993889b125c42.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/inside/page-50cb2a0eb15e6c83.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/intake/page-c8b1ccda6ba7ae98.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/layout-d6b1d156fb95e74b.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/live/page-2a9932ddb62d76bd.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/market/page-9688f5b4f1ff950f.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6b15dd6456c5df09.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/morning/{page-adfbd2ba9598e59a.js → page-35414e0a007b4082.js} +1 -1
  72. package/apps/pwa/out/_next/static/chunks/app/not-found-e7a529e0507ac86c.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/observatory/page-e001823c35c9dd67.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-371423d1a573d64b.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4b764b125339ac17.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cdf8e4b01067bcf1.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a24072b35470157.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/page-94c82eb0ae5abb32.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/reflection/page-0be8d4b2ced75ba0.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d37cb0d52f9b6bd5.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/sessions/page-686da0df9bf23e5c.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-4dc614f3ce043e63.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/settings/page-53f80edc6b8dc07c.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-d02c782fc792e2af.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/app/setup/layout-d16993889b125c42.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/app/setup/page-239c17b911b84e72.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/app/share/page-dc6a79fb003f746c.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/app/showroom/page-0fffb84cafdc9da6.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/app/tasks/page-1218753c82d585f6.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/app/term/page-4b5935e67531efb5.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/app/today/page-3285bc87caf1eaa5.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/app/trace/page-ad553b0e06505ecd.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/app/vault/page-fca096e3e860306f.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-9ebfa78f0d9885c3.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/app/workflows/page-3dc5f21f7e39622d.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/app/workspace/page-c6da2b468454532d.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-93bc70e36f595414.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/{2ea96958-e8c7e112de50b067.js → f48170cc-d05d28bf87a43bf4.js} +1 -1
  99. package/apps/pwa/out/_next/static/chunks/framework-172630e6aa5e2d7f.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/main-app-f0fe2d7477845181.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/main-fc98ac70b44eccb0.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/pages/_app-5bd7f10271f72413.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/pages/_error-97d0d85ea05fef9b.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/webpack-9e4d02d567b98240.js +1 -0
  105. package/apps/pwa/out/_next/static/css/543f56bcb1f79fca.css +1 -0
  106. package/apps/pwa/out/approvals/index.html +1 -1
  107. package/apps/pwa/out/approvals/index.txt +23 -23
  108. package/apps/pwa/out/autopilot/index.html +1 -1
  109. package/apps/pwa/out/autopilot/index.txt +23 -23
  110. package/apps/pwa/out/board/index.html +1 -1
  111. package/apps/pwa/out/board/index.txt +23 -23
  112. package/apps/pwa/out/botlab/index.html +1 -1
  113. package/apps/pwa/out/botlab/index.txt +22 -22
  114. package/apps/pwa/out/bots/index.html +1 -1
  115. package/apps/pwa/out/bots/index.txt +22 -22
  116. package/apps/pwa/out/chat/index.html +1 -1
  117. package/apps/pwa/out/chat/index.txt +22 -22
  118. package/apps/pwa/out/consult/index.html +1 -1
  119. package/apps/pwa/out/consult/index.txt +23 -23
  120. package/apps/pwa/out/control/index.html +1 -1
  121. package/apps/pwa/out/control/index.txt +23 -23
  122. package/apps/pwa/out/design-check/index.html +1 -1
  123. package/apps/pwa/out/design-check/index.txt +23 -23
  124. package/apps/pwa/out/editor/index.html +1 -1
  125. package/apps/pwa/out/editor/index.txt +23 -23
  126. package/apps/pwa/out/exec/index.html +1 -1
  127. package/apps/pwa/out/exec/index.txt +23 -23
  128. package/apps/pwa/out/field/index.html +1 -1
  129. package/apps/pwa/out/field/index.txt +23 -23
  130. package/apps/pwa/out/healthz +1 -1
  131. package/apps/pwa/out/index.html +1 -1
  132. package/apps/pwa/out/index.txt +22 -22
  133. package/apps/pwa/out/inside/index.html +1 -0
  134. package/apps/pwa/out/inside/index.txt +31 -0
  135. package/apps/pwa/out/intake/index.html +1 -1
  136. package/apps/pwa/out/intake/index.txt +23 -23
  137. package/apps/pwa/out/live/index.html +1 -1
  138. package/apps/pwa/out/live/index.txt +23 -23
  139. package/apps/pwa/out/market/index.html +1 -1
  140. package/apps/pwa/out/market/index.txt +22 -22
  141. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  142. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  143. package/apps/pwa/out/morning/index.html +1 -1
  144. package/apps/pwa/out/morning/index.txt +23 -23
  145. package/apps/pwa/out/observatory/index.html +1 -1
  146. package/apps/pwa/out/observatory/index.txt +23 -23
  147. package/apps/pwa/out/ops/checklist/index.html +1 -1
  148. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  149. package/apps/pwa/out/ops/release/index.html +1 -1
  150. package/apps/pwa/out/ops/release/index.txt +22 -22
  151. package/apps/pwa/out/ops/seats/index.html +1 -0
  152. package/apps/pwa/out/ops/seats/index.txt +30 -0
  153. package/apps/pwa/out/outputs/index.html +1 -0
  154. package/apps/pwa/out/outputs/index.txt +30 -0
  155. package/apps/pwa/out/reflection/index.html +1 -1
  156. package/apps/pwa/out/reflection/index.txt +23 -23
  157. package/apps/pwa/out/scheduler/index.html +1 -1
  158. package/apps/pwa/out/scheduler/index.txt +23 -23
  159. package/apps/pwa/out/sessions/index.html +1 -1
  160. package/apps/pwa/out/sessions/index.txt +22 -22
  161. package/apps/pwa/out/settings/devices/index.html +1 -1
  162. package/apps/pwa/out/settings/devices/index.txt +23 -23
  163. package/apps/pwa/out/settings/index.html +1 -1
  164. package/apps/pwa/out/settings/index.txt +23 -23
  165. package/apps/pwa/out/setup/done/index.html +1 -1
  166. package/apps/pwa/out/setup/done/index.txt +23 -23
  167. package/apps/pwa/out/setup/index.html +1 -1
  168. package/apps/pwa/out/setup/index.txt +23 -23
  169. package/apps/pwa/out/share/index.html +1 -1
  170. package/apps/pwa/out/share/index.txt +23 -23
  171. package/apps/pwa/out/showroom/index.html +1 -1
  172. package/apps/pwa/out/showroom/index.txt +22 -22
  173. package/apps/pwa/out/tasks/index.html +1 -1
  174. package/apps/pwa/out/tasks/index.txt +23 -23
  175. package/apps/pwa/out/term/index.html +1 -1
  176. package/apps/pwa/out/term/index.txt +23 -24
  177. package/apps/pwa/out/today/index.html +1 -0
  178. package/apps/pwa/out/today/index.txt +33 -0
  179. package/apps/pwa/out/trace/index.html +1 -1
  180. package/apps/pwa/out/trace/index.txt +23 -23
  181. package/apps/pwa/out/vault/index.html +1 -1
  182. package/apps/pwa/out/vault/index.txt +23 -23
  183. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  184. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  185. package/apps/pwa/out/workflows/index.html +1 -1
  186. package/apps/pwa/out/workflows/index.txt +23 -23
  187. package/apps/pwa/out/workspace/index.html +1 -1
  188. package/apps/pwa/out/workspace/index.txt +23 -24
  189. package/apps/pwa/out/worktrees/index.html +1 -1
  190. package/apps/pwa/out/worktrees/index.txt +23 -23
  191. package/graphs/drafts/draft-cleanup.yaml +19 -0
  192. package/graphs/drafts/recipes.yaml +23 -0
  193. package/graphs/field/field-feed.yaml +2 -0
  194. package/graphs/implement-loop.yaml +1 -1
  195. package/graphs/intake/intake-daily.yaml +2 -1
  196. package/graphs/ops/recipes.yaml +3 -0
  197. package/graphs/ops/secret-exposure.yaml +24 -0
  198. package/graphs/release/recipes.yaml +3 -0
  199. package/graphs/release/release-loop.yaml +4 -1
  200. package/graphs/video/explainer-line.yaml +2 -2
  201. package/package.json +2 -1
  202. package/plugins/card-followup/graphs/run-step.ts +48 -13
  203. package/plugins/doc-draft/graphs/doc-draft.yaml +3 -3
  204. package/plugins/doc-draft/graphs/run-step.ts +13 -6
  205. package/plugins/elanous-hwp/README.md +25 -0
  206. package/plugins/elanous-hwp/cli.ts +22 -0
  207. package/plugins/elanous-hwp/converter.ts +237 -0
  208. package/plugins/elanous-hwp/fixtures/fixed-controls.hwpx +0 -0
  209. package/plugins/elanous-hwp/fixtures/fixed-table-controls.hwpx +0 -0
  210. package/plugins/elanous-hwp/fixtures/fixed.hwpx +0 -0
  211. package/plugins/elanous-hwp/plugin.json +16 -0
  212. package/plugins/elanous-hwp/plugin.ts +40 -0
  213. package/scripts/ci-public-export-leak-gate.ts +75 -14
  214. package/scripts/ci-public-leak-gate.ts +44 -9
  215. package/scripts/ci-pwa-build-gate.ts +160 -0
  216. package/scripts/context-hooks/claude-stop.sh +4 -0
  217. package/scripts/context-hooks/emit.ts +24 -0
  218. package/scripts/context-hooks/post-tool-use.sh +4 -0
  219. package/scripts/coord-channel-watch.sh +54 -8
  220. package/scripts/coord-digest.ts +199 -0
  221. package/scripts/coord-post.sh +60 -3
  222. package/scripts/coord-requests-bridge.ts +143 -0
  223. package/scripts/field-feed/lib.ts +4 -0
  224. package/scripts/fleet/node-setup.sh +20 -3
  225. package/scripts/homebrew-formula-bump.ts +69 -0
  226. package/scripts/install.ps1 +68 -9
  227. package/scripts/install.sh +39 -12
  228. package/scripts/lib/pwa-scenarios/scenarios.ts +20 -0
  229. package/scripts/lib/tui-regress-checks.ts +146 -0
  230. package/scripts/marketing/measure-site-numbers.ts +169 -0
  231. package/scripts/measure-gate-shards.ts +309 -0
  232. package/scripts/memory-lifecycle-cycle.ts +2 -2
  233. package/scripts/mission-request-judge.ts +31 -1
  234. package/scripts/morning-report.ts +2 -2
  235. package/scripts/opportunity-scan.ts +1 -1
  236. package/scripts/ops-now-probe.ts +327 -0
  237. package/scripts/publish-internal-dist.ts +11 -1
  238. package/scripts/release-loop/auto-approve-node.ts +21 -4
  239. package/scripts/release-loop/cut-branch.ts +84 -0
  240. package/scripts/release-loop/cutoff.ts +19 -8
  241. package/scripts/release-loop/gate-node.ts +14 -13
  242. package/scripts/release-loop/mac-smoke-node.ts +229 -0
  243. package/scripts/release-loop/node-verdict.ts +3 -3
  244. package/scripts/release-loop/ops-upgrade-node.ts +72 -16
  245. package/scripts/release-loop/release-readiness.ts +9 -0
  246. package/scripts/release-loop/tui-node-checks.ts +42 -0
  247. package/scripts/release-loop/tui-sim-node.ts +32 -8
  248. package/scripts/release-loop/unattended-release.ts +5 -3
  249. package/scripts/release-loop/version-node.ts +112 -13
  250. package/scripts/repo-watch-cycle.ts +1 -1
  251. package/scripts/retro-cycle.ts +2 -2
  252. package/scripts/schedule-health-report.ts +1 -1
  253. package/scripts/signal-digest-cycle.ts +2 -7
  254. package/scripts/signal-digest-photo.ts +11 -0
  255. package/scripts/storyboard/storyboard.ts +5 -161
  256. package/scripts/tech1-public-measure.ts +94 -0
  257. package/scripts/test-diet/lib.ts +30 -1
  258. package/scripts/test-diet/node.ts +83 -8
  259. package/scripts/test-timeout-audit.ts +114 -0
  260. package/scripts/tree-sync-alert.ts +1 -1
  261. package/scripts/tui-regress.ts +71 -0
  262. package/scripts/ux/demo-rehearsal.ts +243 -0
  263. package/scripts/ux/lib/rehearsal-verdict.ts +73 -0
  264. package/scripts/waitlist-daily-digest.ts +1 -1
  265. package/skills/diagram-master/SKILL.md +366 -0
  266. package/skills/diagram-master/references/engines/excalidraw/color-palette.md +67 -0
  267. package/skills/diagram-master/references/engines/excalidraw/element-templates.md +182 -0
  268. package/skills/diagram-master/references/engines/excalidraw/json-schema.md +71 -0
  269. package/skills/diagram-master/references/engines/excalidraw/render_excalidraw.py +189 -0
  270. package/skills/diagram-master/references/engines/excalidraw/render_template.html +57 -0
  271. package/skills/diagram-master/references/engines/matplotlib/matplotlib-patterns.md +235 -0
  272. package/skills/diagram-master/references/engines/matplotlib/render_matplotlib.py +184 -0
  273. package/skills/diagram-master/references/engines/mermaid/domain-templates.md +435 -0
  274. package/skills/diagram-master/references/engines/mermaid/render_mermaid.py +116 -0
  275. package/skills/diagram-master/references/engines/mermaid/syntax-reference.md +2247 -0
  276. package/skills/diagram-master/references/engines/nanobannana/render_nanobannana2.py +168 -0
  277. package/skills/diagram-master/references/engines/svg/color-palette.md +188 -0
  278. package/skills/diagram-master/references/engines/svg/html-base-template.html +332 -0
  279. package/skills/diagram-master/references/engines/svg/interactivity-patterns.md +435 -0
  280. package/skills/diagram-master/references/engines/svg/render_interactive_svg.py +212 -0
  281. package/skills/diagram-master/references/engines/svg/svg-element-templates.md +349 -0
  282. package/skills/diagram-master/references/examples/README.md +69 -0
  283. package/skills/diagram-master/references/examples/ref_grid_architecture.png +0 -0
  284. package/skills/diagram-master/references/examples/ref_layered_flow.png +0 -0
  285. package/skills/diagram-master/references/pyproject.toml +16 -0
  286. package/skills/diagram-master/references/quality-checklist.md +33 -0
  287. package/skills/diagram-master/references/routing-guide.md +164 -0
  288. package/skills/diagram-master/references/uv.lock +1356 -0
  289. package/skills/explainer-video/SKILL.md +4 -0
  290. package/skills/explainer-video/engine/reel.mjs +18 -13
  291. package/skills/explainer-video/engine/reel.sh +24 -5
  292. package/skills/explainer-video/engine/stitch.mjs +145 -0
  293. package/skills/google-workspace/SKILL.md +33 -0
  294. package/skills/google-workspace/scripts/gws-safe.sh +177 -0
  295. package/skills/grill-me/SKILL.md +1 -1
  296. package/skills/lecture-note-digitizer/SKILL.md +926 -0
  297. package/skills/lecture-note-digitizer/references/convert_to_pdf.py +30 -0
  298. package/skills/lecture-note-digitizer/references/extract_slide_diagram.py +73 -0
  299. package/skills/lecture-note-digitizer/references/html-template.md +167 -0
  300. package/skills/lecture-note-digitizer/references/katex-style-guide.md +165 -0
  301. package/skills/omni-crawl/SKILL.md +21 -11
  302. package/skills/omni-crawl/scripts/main.ts +6 -6
  303. package/skills/omni-crawl/src/router.ts +3 -3
  304. package/skills/omni-digest/SKILL.md +17 -9
  305. package/skills/omni-digest/digest.md +2 -2
  306. package/skills/omni-digest/src/fetch.ts +30 -3
  307. package/skills/pitch-deck/SKILL.md +36 -0
  308. package/skills/pitch-deck/template.html +80 -0
  309. package/skills/youtube-master/SKILL.md +163 -0
  310. package/skills/youtube-master/package-lock.json +558 -0
  311. package/skills/youtube-master/package.json +13 -0
  312. package/skills/youtube-master/references/study-note-template.md +124 -0
  313. package/skills/youtube-master/scripts/main.ts +435 -0
  314. package/skills/youtube-master/scripts/transcribe-local.ts +24 -0
  315. package/skills/youtube-master/src/audio.ts +63 -0
  316. package/skills/youtube-master/src/domain-staging.ts +50 -0
  317. package/skills/youtube-master/src/env.ts +36 -0
  318. package/skills/youtube-master/src/obsidian.ts +155 -0
  319. package/skills/youtube-master/src/router.ts +110 -0
  320. package/skills/youtube-master/src/stt.ts +199 -0
  321. package/skills/youtube-master/src/study-note.ts +175 -0
  322. package/skills/youtube-master/src/summarize.ts +358 -0
  323. package/skills/youtube-master/src/transcript.ts +76 -0
  324. package/skills/youtube-master/src/types.ts +91 -0
  325. package/skills/youtube-master/src/util.ts +93 -0
  326. package/skills/youtube-master/src/youtube.ts +70 -0
  327. package/skills/youtube-master/tsconfig.json +13 -0
  328. package/src/acp/codex-approval-adapter.ts +9 -3
  329. package/src/acp/core-turn-bridge.ts +117 -18
  330. package/src/acp/fact-question.ts +11 -0
  331. package/src/acp/server.ts +53 -9
  332. package/src/acp/tool-approval.ts +126 -0
  333. package/src/acp/turn-runner.ts +36 -0
  334. package/src/agent/monad-agent-turn.ts +8 -0
  335. package/src/agent-mission/driver.ts +80 -10
  336. package/src/agent-mission/pty-decision.ts +71 -0
  337. package/src/agent-mission/review-loop.ts +30 -0
  338. package/src/ask-user-question/pending-questions.ts +25 -10
  339. package/src/ask-user-question/types.ts +9 -0
  340. package/src/auth/setup-link-tokens.ts +16 -5
  341. package/src/autopilot/build/integrity-gate.ts +12 -0
  342. package/src/autopilot/build/llm-conflict-merge.ts +16 -3
  343. package/src/autopilot/tool-registry.ts +2 -0
  344. package/src/boot/daemon-prompt-turn.ts +8 -0
  345. package/src/boot/daemon-runtime.ts +9 -3
  346. package/src/boot/daemon-tools/index.ts +8 -0
  347. package/src/boot/daemon-tools/types.ts +2 -0
  348. package/src/boot/daemon-tools/web-search.ts +4 -0
  349. package/src/card-followup/core.ts +109 -0
  350. package/src/chat/ctrl-c-in-input.ts +14 -0
  351. package/src/chat/index.ts +53 -0
  352. package/src/chat/pickers/state.ts +6 -2
  353. package/src/cli/agent-cli.ts +7 -0
  354. package/src/cli/decisions-cli.ts +25 -3
  355. package/src/cli/doctor-bundle-upload.ts +68 -0
  356. package/src/cli/doctor-cli.ts +38 -4
  357. package/src/cli/drive-input-jail.ts +101 -0
  358. package/src/cli/hooks-cli.ts +17 -3
  359. package/src/cli/make-cli.ts +43 -0
  360. package/src/cli/ops-cli.ts +20 -0
  361. package/src/cli/persona-cli.ts +90 -4
  362. package/src/cli/plugin-cli.ts +31 -22
  363. package/src/cli/pod-cli.ts +61 -2
  364. package/src/cli/pr-cli.ts +82 -17
  365. package/src/cli/pty-drive-cli.ts +41 -7
  366. package/src/cli/pty-drive-verdict.ts +40 -0
  367. package/src/cli/pwa-stop.ts +163 -13
  368. package/src/cli/pwa-test.ts +3 -0
  369. package/src/cli/release-cli.ts +80 -24
  370. package/src/cli/self-update.ts +14 -0
  371. package/src/cli/setup-cli.ts +43 -1
  372. package/src/cli/skills-cli.ts +17 -1
  373. package/src/cli/storyboard-cli.ts +37 -0
  374. package/src/cli/test-state-dir-flag.ts +11 -4
  375. package/src/connectors/linear.ts +146 -0
  376. package/src/context-bus/claude-hooks-install.ts +10 -0
  377. package/src/context-bus/context-now-surfaces.ts +48 -0
  378. package/src/context-bus/context-now.ts +104 -0
  379. package/src/context-bus/coord-events.ts +115 -0
  380. package/src/context-bus/external-events.ts +52 -0
  381. package/src/context-bus/long-term-memory.ts +108 -0
  382. package/src/dashboard/dashboard-boot-fixture.ts +23 -0
  383. package/src/dashboard/index.ts +63 -21
  384. package/src/dashboard/slash-runtime/dashboard-handlers.ts +14 -1
  385. package/src/dashboard/slash-runtime/help-from-registry.ts +43 -28
  386. package/src/decisions/decision-cards.ts +58 -3
  387. package/src/decisions/decision-ledger.ts +85 -6
  388. package/src/decisions/decision-linear-projection.ts +157 -0
  389. package/src/decisions/web-push-decision-cards.ts +50 -0
  390. package/src/directive/interpretation-card.ts +36 -0
  391. package/src/discord/sprint21-runtime.ts +5 -1
  392. package/src/discord-self-message.ts +32 -0
  393. package/src/discord-slash-wire.ts +5 -1
  394. package/src/discord.ts +19 -2
  395. package/src/domains/coo-admin-tool.ts +62 -0
  396. package/src/domains/core-tools.ts +13 -4
  397. package/src/domains/elanous-card.ts +29 -0
  398. package/src/domains/ops-facts-tool.ts +76 -0
  399. package/src/domains/outbound-alert.ts +46 -13
  400. package/src/domains/release-tool.ts +168 -0
  401. package/src/domains/telegram-kind-route.ts +65 -9
  402. package/src/exec-requests/default-outputs.ts +25 -0
  403. package/src/exec-requests/planner.ts +106 -28
  404. package/src/exec-requests/runner.ts +123 -12
  405. package/src/exec-requests/store.ts +11 -2
  406. package/src/execution-loop/launch-gate.ts +100 -0
  407. package/src/field/field-media.ts +10 -7
  408. package/src/field/field-reel.ts +19 -5
  409. package/src/field-note/index.ts +201 -0
  410. package/src/field-note/transcribe.ts +248 -0
  411. package/src/git-fs/worktree.ts +45 -6
  412. package/src/graph-kinds/registry.ts +3 -3
  413. package/src/graph-runner/graph-approval-card.ts +113 -0
  414. package/src/graph-runner/graph-cli.ts +189 -4
  415. package/src/graph-runner/graph-grow-llm.ts +37 -0
  416. package/src/graph-runner/graph-grow.ts +47 -0
  417. package/src/graph-runner/graph-notify.ts +25 -3
  418. package/src/graph-runner/runner.ts +453 -35
  419. package/src/grok/agent-search.ts +8 -2
  420. package/src/guardian/secret-exposure-scan.ts +101 -0
  421. package/src/harness/browser-verify.ts +25 -2
  422. package/src/harness/harness-cli-command.ts +478 -42
  423. package/src/harness/harness-incidents-writer.fixture.ts +24 -0
  424. package/src/harness/harness-incidents.ts +129 -0
  425. package/src/harness/harness-pod-dispatch.ts +43 -23
  426. package/src/harness/harness-seams.ts +38 -4
  427. package/src/harness/harness-stop.ts +75 -6
  428. package/src/harness/launch-registry.ts +91 -0
  429. package/src/harness/process-garbage.ts +5 -3
  430. package/src/hitl/clarification-policy.ts +3 -1
  431. package/src/hooks/dispatch.ts +82 -0
  432. package/src/hooks/expose.ts +254 -0
  433. package/src/hooks/github-shadow.ts +108 -0
  434. package/src/hooks/providers.ts +71 -5
  435. package/src/hooks/queue.ts +1 -1
  436. package/src/hooks/receiver.ts +21 -20
  437. package/src/index.ts +206 -49
  438. package/src/intake-plane/author-goals.ts +37 -4
  439. package/src/intake-plane/categorize.ts +18 -46
  440. package/src/intake-plane/check.ts +3 -0
  441. package/src/intake-plane/discord-seat-work.ts +57 -2
  442. package/src/intake-plane/front-route-classifier.ts +33 -39
  443. package/src/intake-plane/persona-answer.ts +30 -0
  444. package/src/intake-plane/runtime-callables.ts +8 -1
  445. package/src/intake-plane/seat-doc-route.ts +170 -6
  446. package/src/intake-plane/telegram-seat-work.ts +80 -4
  447. package/src/intake-plane/telegram-work.ts +1 -0
  448. package/src/intake-plane/wish-folder.ts +67 -0
  449. package/src/intent-gate/gate.ts +30 -10
  450. package/src/live/detail-switch.ts +5 -0
  451. package/src/llm/difficulty.ts +57 -0
  452. package/src/llm/judge-layer.ts +101 -0
  453. package/src/llm/mission-router.ts +33 -19
  454. package/src/loops/contract/examples/cmo-seat.loop.yaml +51 -0
  455. package/src/loops/contract/examples/orchestrator.loop.yaml +57 -0
  456. package/src/loops/contract/examples/steward.loop.yaml +50 -0
  457. package/src/loops/contract/loop-agent.schema.json +138 -0
  458. package/src/loops/contract/validate.ts +64 -0
  459. package/src/loops/registry.ts +20 -6
  460. package/src/market/signed-index.ts +4 -0
  461. package/src/maturity/bot-command-maturity.ts +45 -0
  462. package/src/maturity/cli-maturity.ts +34 -0
  463. package/src/maturity/feature-maturity.ts +120 -1
  464. package/src/maturity/tui-slash-maturity.ts +31 -0
  465. package/src/memory.ts +32 -4
  466. package/src/model-tier/index.ts +2 -2
  467. package/src/model-tier/tier-resolver.ts +20 -20
  468. package/src/model-tier/types.ts +6 -6
  469. package/src/mss/logging/log-store.ts +15 -0
  470. package/src/nexus/api/config-model-tier.ts +5 -4
  471. package/src/nexus/api/exec-requests.ts +33 -3
  472. package/src/nexus/api/fabric-plans.ts +53 -0
  473. package/src/nexus/api/harness-api.ts +5 -1
  474. package/src/nexus/api/health.ts +2 -0
  475. package/src/nexus/api/http-server.ts +89 -1
  476. package/src/nexus/api/inside-events.ts +226 -0
  477. package/src/nexus/api/meta-api.ts +32 -8
  478. package/src/nexus/api/missions.ts +6 -1
  479. package/src/nexus/api/ops-api.ts +10 -1
  480. package/src/nexus/api/ops-seats-sources.ts +37 -7
  481. package/src/nexus/api/ops-seats.ts +49 -6
  482. package/src/nexus/api/outputs-route.ts +44 -0
  483. package/src/nexus/api/seat-requests.ts +132 -10
  484. package/src/nexus/api/sessions-store.ts +42 -0
  485. package/src/nexus/api/setup-codex-login.ts +160 -0
  486. package/src/nexus/api/sub-seat-charters.ts +34 -0
  487. package/src/nexus/api/tabs-mutations.ts +3 -1
  488. package/src/nexus/api/trace.ts +18 -6
  489. package/src/nexus/api/usage-api.ts +102 -0
  490. package/src/nexus/api/workflow-run-event-bridge.ts +1 -0
  491. package/src/nexus/api/workflows.ts +80 -6
  492. package/src/nexus/index.ts +47 -4
  493. package/src/nexus/kinds/webterm.ts +2 -1
  494. package/src/nexus/webterm/pty.ts +6 -2
  495. package/src/onboarding/cli-auth-status.ts +80 -0
  496. package/src/onboarding/first-run.ts +89 -0
  497. package/src/onboarding/lan-address.ts +45 -0
  498. package/src/onboarding/voice-ai.ts +3 -3
  499. package/src/onboarding/web-first.ts +119 -24
  500. package/src/onboarding.ts +4 -0
  501. package/src/ops-now/ops-now-refresh.ts +100 -0
  502. package/src/org/seat-budget.ts +48 -0
  503. package/src/org/stall-escalation.ts +63 -0
  504. package/src/outputs/ledger.ts +120 -0
  505. package/src/persona/persona-todo.ts +79 -0
  506. package/src/persona/presets.ts +116 -2
  507. package/src/persona/team-set.ts +155 -0
  508. package/src/plugins/install/plugin-install.ts +14 -0
  509. package/src/plugins/maker/market-bundle.ts +109 -0
  510. package/src/plugins/maker/plugin-maker.ts +254 -11
  511. package/src/plugins/maker/wizard-generate.ts +103 -0
  512. package/src/pod-lease/admission.ts +150 -0
  513. package/src/pod-lease/dependency-state.ts +85 -0
  514. package/src/pod-lease/host-lease.ts +127 -0
  515. package/src/project/project-store.ts +115 -0
  516. package/src/project/project-suggestion.ts +16 -0
  517. package/src/project/session-context.ts +24 -0
  518. package/src/release-loop/checklist.ts +79 -5
  519. package/src/release-loop/feature-store.ts +159 -24
  520. package/src/release-loop/next-md-merge.ts +58 -0
  521. package/src/release-loop/release-note.ts +59 -4
  522. package/src/release-loop/release-schedule.ts +53 -0
  523. package/src/repl/index.ts +7 -0
  524. package/src/seat-dispatch/ceo-commands.ts +41 -8
  525. package/src/seat-dispatch/ceo-intent.ts +38 -0
  526. package/src/seat-dispatch/seat-ask.ts +130 -0
  527. package/src/seat-dispatch/seat-questions.ts +100 -0
  528. package/src/seat-loop/seat-loop.ts +750 -0
  529. package/src/seat-loop/seat-report.ts +99 -0
  530. package/src/self-dev/decompose.ts +66 -11
  531. package/src/self-dev/dev-cli.ts +4 -1
  532. package/src/self-dev/dev-pipeline.ts +37 -1
  533. package/src/self-dev/draft-sweep.ts +38 -33
  534. package/src/self-dev/draft-triage-rules.ts +8 -5
  535. package/src/self-dev/fabric-plan-core.ts +297 -0
  536. package/src/self-dev/orchestrate.ts +36 -4
  537. package/src/self-dev/release-path-guard.ts +62 -0
  538. package/src/self-dev/run-store.ts +10 -1
  539. package/src/self-dev/shard-aggregate.ts +230 -0
  540. package/src/self-dev/shard-placement.ts +60 -0
  541. package/src/self-dev/shard-plan.ts +48 -0
  542. package/src/self-dev/shard-pod-run.ts +77 -0
  543. package/src/self-dev/shard-run.ts +105 -0
  544. package/src/self-implement/execution-clarification.ts +115 -0
  545. package/src/self-implement/gate-baseline.ts +1 -1
  546. package/src/self-implement/gate-cli.ts +46 -4
  547. package/src/self-implement/gate-scope.ts +37 -7
  548. package/src/self-implement/graph-variant.ts +171 -0
  549. package/src/self-implement/harness-policy-gates.ts +1 -1
  550. package/src/self-implement/headless-elanous-driver.ts +22 -3
  551. package/src/self-implement/host-regate.ts +22 -1
  552. package/src/self-implement/importer-test-index.ts +2 -1
  553. package/src/self-implement/l8-merge-queue.ts +130 -0
  554. package/src/self-implement/orchestrator.ts +279 -12
  555. package/src/self-implement/pipeline-shape.ts +1 -1
  556. package/src/self-implement/route-consumer-test-index.ts +122 -0
  557. package/src/self-implement/run-status-mapping.ts +5 -3
  558. package/src/self-implement/seams.ts +72 -11
  559. package/src/self-implement/test-seams.ts +1 -0
  560. package/src/self-implement/uncommitted-work.ts +59 -0
  561. package/src/session/chat.ts +2 -0
  562. package/src/session/index.ts +45 -13
  563. package/src/shell-env-bootstrap.ts +17 -0
  564. package/src/skills/gift-install.ts +215 -0
  565. package/src/skills/tools/web-search.ts +4 -1
  566. package/src/steward/directive.ts +51 -2
  567. package/src/steward/launch.ts +266 -0
  568. package/src/steward/outcome-notify.ts +115 -0
  569. package/src/steward/steward-cards.ts +25 -11
  570. package/src/steward/triage-plan.ts +32 -4
  571. package/src/steward/triage.ts +129 -13
  572. package/src/steward/working-backwards.ts +64 -0
  573. package/src/storyboard/storyboard.ts +141 -0
  574. package/src/task-cards/card-cli.ts +25 -1
  575. package/src/task-cards/card-store.ts +2 -2
  576. package/src/task-orchestrator/mission.ts +5 -0
  577. package/src/task-orchestrator/store.ts +12 -3
  578. package/src/task-orchestrator/surfaces/pod-command-job.ts +29 -7
  579. package/src/task-orchestrator/surfaces/pod-lease.ts +397 -0
  580. package/src/task-orchestrator/surfaces/pod-pool.ts +127 -26
  581. package/src/task-orchestrator/surfaces/self-implement-pod.ts +214 -40
  582. package/src/task-orchestrator/surfaces/self-implement.ts +15 -2
  583. package/src/task-orchestrator/types.ts +2 -0
  584. package/src/telegram-card-followup.ts +153 -0
  585. package/src/telegram-commands.ts +10 -1
  586. package/src/telegram-project-command.ts +71 -0
  587. package/src/telegram-report.ts +29 -19
  588. package/src/telegram-run.ts +7 -0
  589. package/src/telegram.ts +58 -11
  590. package/src/tool-runtime/self-cognition-runtimes.ts +13 -3
  591. package/src/tool-runtime/types.ts +4 -0
  592. package/src/tui-client/dashboard-session.ts +2 -0
  593. package/src/user-config.ts +216 -27
  594. package/src/version/packed-revision.json +1 -1
  595. package/src/web-push/notify-exec-request.ts +32 -0
  596. package/src/web-search/grok.ts +7 -1
  597. package/src/web-search/index.ts +28 -1
  598. package/src/web-search/provider.ts +4 -0
  599. package/src/web-search/x-intent.ts +5 -0
  600. package/src/workflow-runtime/executor.ts +277 -47
  601. package/src/workflow-runtime/index.ts +1 -0
  602. package/src/workflow-runtime/nodes/subworkflow.ts +60 -0
  603. package/src/workflow-runtime/plugin-kind-node.ts +1 -1
  604. package/src/workflow-runtime/schema.ts +79 -4
  605. package/src/workflow-runtime/types.ts +35 -4
  606. package/apps/pwa/out/_next/static/bFI5EEwg7T6aG88ibX4Zq/_buildManifest.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/2375-cb6debea56cce4fb.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/2929-617f1e66a55be184.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
  616. package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
  617. package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
  618. package/apps/pwa/out/_next/static/chunks/3441-a1dd4acfeda14488.js +0 -1
  619. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
  620. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/5080-1613752891fcadb4.js +0 -10
  622. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/6090-85a3f05f954e5697.js +0 -1
  625. package/apps/pwa/out/_next/static/chunks/6284-69db415193d5f8bd.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
  629. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
  630. package/apps/pwa/out/_next/static/chunks/7032-f4ef698518fba61c.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
  632. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/8067-d976b441a104ab74.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/8519-5632377cce994f86.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/8630-cdfc61e82c653ffc.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/8894-e263b332208e3957.js +0 -1
  640. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
  641. package/apps/pwa/out/_next/static/chunks/9454-139040d37c79dbc9.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/9740-8f5ff96c522b5f55.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/app/approvals/page-fb7c2d458f32fcad.js +0 -1
  647. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-35e2713597dd24d1.js +0 -1
  648. package/apps/pwa/out/_next/static/chunks/app/board/page-918b656f064577ba.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/app/bots/page-5f30e00693527ebd.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/app/chat/page-7fbae173b3c103a7.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/app/consult/page-3b4edcce364062ea.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/app/control/page-c5794f484c251f44.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/app/design-check/page-1130d8eefcd87e76.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/app/editor/page-973996d584f008c1.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/app/exec/page-016e2bda04eecb1f.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/app/field/page-b4d6d9a3b56f2940.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/app/intake/page-ec49a466121233ba.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/app/layout-f72db356744ceade.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/app/live/page-a6fd9b50d1b85378.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/app/market/page-8b0402a46a749a3d.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-037f83e2a1edc8e2.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/app/not-found-46fedff57d4bec67.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/app/observatory/page-0cf318e9dcee2bbf.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-899b327cb8b6845c.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-6c1e6ba743a29dfe.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/app/page-1a8e4807292f6b17.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/app/reflection/page-28deba6bcb834916.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-de68e2205794037d.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/app/sessions/page-c71c33a573230672.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-0df30e0c7e50148a.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/app/settings/page-4fc56790fe0d2682.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-59df2aebb38a7c1e.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/app/setup/page-4647be5dbcbd0795.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/app/share/page-e025892bda74b743.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/app/showroom/page-a0955de73b89f078.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/app/tasks/page-369846d2921af584.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/app/term/page-72cbc1c8cdd0f65d.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/app/trace/page-77b28bfb0996d5ea.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/app/vault/page-3220c4934f49c6c2.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-90670589b87abea8.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/app/workflows/page-7df9243ed021a4cc.js +0 -1
  684. package/apps/pwa/out/_next/static/chunks/app/workspace/page-4498c3853f4f688d.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-35bedde6d885f08f.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/main-app-6870f30ea99f8346.js +0 -1
  689. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/webpack-e5bea36f183b25e6.js +0 -1
  692. package/apps/pwa/out/_next/static/css/f92f63a5df0ba229.css +0 -1
  693. package/src/dashboard/turn-typeahead-echo.ts +0 -23
  694. /package/apps/pwa/out/_next/static/{bFI5EEwg7T6aG88ibX4Zq → Y4zKzL62sakG5momz3SoQ}/_ssgManifest.js +0 -0
@@ -0,0 +1,115 @@
1
+ import { realpathSync, statSync } from 'node:fs';
2
+ import { isAbsolute, extname, relative, resolve } from 'node:path';
3
+ import { debug } from '../debug/log.js';
4
+ import { notifyMissionDocument, notifyMissionOrigin } from '../autopilot/mission-notify.js';
5
+ import type { MissionOrigin } from '../autopilot/mission-origin.js';
6
+ import { redactSecrets } from '../task-cards/card-store.js';
7
+ import { loadDirectiveOrigins } from './directive.js';
8
+ import { saveLaunchLedger, type LaunchEntry, type LaunchLedger } from './launch.js';
9
+
10
+ export interface OutcomeNotifyDeps {
11
+ root: string;
12
+ ledger: LaunchLedger;
13
+ shadow?: boolean;
14
+ loadOrigin?: (root: string, issue: string) => MissionOrigin | null;
15
+ loadOrigins?: typeof loadDirectiveOrigins;
16
+ document?: typeof notifyMissionDocument;
17
+ message?: typeof notifyMissionOrigin;
18
+ worktreeCommand?: (args: string[]) => { exitCode: number; stdout: string };
19
+ }
20
+
21
+ const EXTENSIONS = new Set(['.md', '.pdf', '.png', '.jpg', '.csv', '.txt', '.html', '.json']);
22
+ const MAX_BYTES = 20 * 1024 * 1024;
23
+
24
+ function localArtifact(outcome: LaunchEntry, deps: OutcomeNotifyDeps): string | null {
25
+ const ref = outcome.artifactRef;
26
+ const branch = outcome.artifactBranch;
27
+ if (!ref || !branch || isAbsolute(ref) || !EXTENSIONS.has(extname(ref).toLowerCase())) return null;
28
+ const command = deps.worktreeCommand ?? ((args: string[]) => {
29
+ const result = Bun.spawnSync(args, { cwd: resolve(import.meta.dir, '../..'), stdout: 'pipe', stderr: 'pipe' });
30
+ return { exitCode: result.exitCode, stdout: result.stdout.toString() };
31
+ });
32
+ try {
33
+ const result = command(['git', 'worktree', 'list', '--porcelain']);
34
+ if (result.exitCode !== 0) return null;
35
+ for (const block of result.stdout.split(/\n\s*\n/)) {
36
+ const path = /^worktree (.+)$/m.exec(block)?.[1];
37
+ if (!path || !block.split('\n').includes(`branch refs/heads/${branch}`) ||
38
+ (outcome.artifactWorktree && !path.endsWith(outcome.artifactWorktree))) continue;
39
+ try {
40
+ const base = realpathSync(path);
41
+ const file = realpathSync(resolve(base, ref));
42
+ const inside = relative(base, file);
43
+ if (!inside || inside === '..' || inside.startsWith('../') || isAbsolute(inside)) continue;
44
+ const stat = statSync(file);
45
+ return stat.isFile() && stat.size <= MAX_BYTES ? file : null;
46
+ } catch { /* Worktree or file is gone; try another matching worktree. */ }
47
+ }
48
+ } catch { /* No accessible worktree or artifact: use PR link instead. */ }
49
+ return null;
50
+ }
51
+
52
+ /** Persist each conversation's delivery independently; only failed recipients retry (up to three times). */
53
+ export function notifyOutcome(outcome: LaunchEntry, deps: OutcomeNotifyDeps): LaunchEntry['notified'] {
54
+ if (outcome.notified === 'shadow') return 'shadow';
55
+ let origins: MissionOrigin[] = [];
56
+ try {
57
+ origins = deps.loadOrigin
58
+ ? [deps.loadOrigin(deps.root, outcome.issue)].filter((item): item is MissionOrigin => item !== null)
59
+ : (deps.loadOrigins ?? loadDirectiveOrigins)(deps.root, outcome.issue);
60
+ } catch { /* Damaged or unavailable origin cannot route a result. */ }
61
+ const recipients = new Map<string, MissionOrigin>();
62
+ for (const origin of origins) {
63
+ if (origin.channel !== 'telegram' || !Number.isSafeInteger(origin.chatId)) continue;
64
+ const key = JSON.stringify([origin.chatId, origin.botId ?? null, origin.threadId ?? null]);
65
+ recipients.set(key, origin);
66
+ }
67
+ if (outcome.notified === 'skipped-no-origin' && recipients.size === 0 && !deps.shadow) return outcome.notified;
68
+ if (outcome.notified === 'sent' && !outcome.notifyRecipients) return 'sent';
69
+ const file = outcome.status !== 'failed' && (recipients.size > 0 || deps.shadow) ? localArtifact(outcome, deps) : null;
70
+ const kind = outcome.status === 'failed' ? 'failure' : file ? 'file' : 'link';
71
+ let sent = false;
72
+ if (deps.shadow) {
73
+ outcome.notified = 'shadow';
74
+ outcome.notifyPreview = { hasChatId: recipients.size > 0, kind: file ? 'file' : 'link' };
75
+ } else if (recipients.size === 0) {
76
+ outcome.notified = 'skipped-no-origin';
77
+ } else {
78
+ const title = redactSecrets(outcome.title).replace(/\s+/g, ' ').slice(0, 240);
79
+ // Say what happened in plain words: an open PR is not a merge, and a run that left nothing is not a failure.
80
+ const status = outcome.awaitingMerge ? 'PR 열림(병합 대기)' : outcome.status === 'merged' ? '병합됨'
81
+ : outcome.status === 'completed' ? '완료(병합 없음)' : outcome.status;
82
+ const pr = outcome.prNumber;
83
+ const link = outcome.prUrl && /^https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+$/.test(outcome.prUrl) &&
84
+ (pr === undefined || outcome.prUrl.endsWith(`/pull/${pr}`)) ? outcome.prUrl : undefined;
85
+ const noOutput = outcome.status === 'completed' && !outcome.artifactRef && pr === undefined && !outcome.prUrl;
86
+ const undeliverable = outcome.status !== 'failed' && !noOutput && !file && !link;
87
+ const text = outcome.status === 'failed'
88
+ ? `${title} · 실패 — ${redactSecrets(outcome.reason ?? '런 실패').split(/\r?\n/)[0]?.slice(0, 240) ?? '런 실패'}`
89
+ : noOutput
90
+ ? `${title} · ${status} — 남긴 파일·PR 없음`
91
+ : undeliverable
92
+ ? `${title} · ${status} · 산출물 전달 불가 — ${outcome.artifactRef ? '파일을 확인할 수 없습니다' : '검증된 PR 링크가 없습니다'}`
93
+ : `${title} · ${status} · PR ${pr ?? '없음'} · ${link}`;
94
+ const legacyAttempts = !outcome.notifyRecipients && outcome.notified === 'failed' ? outcome.notifyAttempts ?? 0 : 0;
95
+ const progress = outcome.notifyRecipients ??= {};
96
+ for (const [key, origin] of recipients) {
97
+ const previous = progress[key] ?? (legacyAttempts ? { notified: 'failed' as const, attempts: legacyAttempts } : undefined);
98
+ if (previous?.notified === 'sent' || (previous?.attempts ?? 0) >= 3) continue;
99
+ let delivered = false;
100
+ try {
101
+ if (!undeliverable || !previous) {
102
+ delivered = file ? (deps.document ?? notifyMissionDocument)(origin, file, redactSecrets(`${title} · ${status}`))
103
+ : (deps.message ?? notifyMissionOrigin)(origin, redactSecrets(text));
104
+ }
105
+ } catch { /* A failed recipient remains eligible on a later tick. */ }
106
+ progress[key] = { notified: delivered && !undeliverable ? 'sent' : 'failed', attempts: (previous?.attempts ?? 0) + 1 };
107
+ sent ||= delivered && !undeliverable;
108
+ }
109
+ outcome.notifyAttempts = Math.max(0, ...Object.values(progress).map(item => item.attempts));
110
+ outcome.notified = [...recipients.keys()].every(key => progress[key]?.notified === 'sent') ? 'sent' : 'failed';
111
+ }
112
+ saveLaunchLedger(deps.root, deps.ledger);
113
+ debug.log('steward.outcome', 'notify', { cardId: `linear:${outcome.issue}`, issue: outcome.issue, kind, sent });
114
+ return outcome.notified;
115
+ }
@@ -1,6 +1,7 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import { CardStore, redactSecrets, type AppendSectionInput, type CreateCardInput, type TaskCard } from '../task-cards/card-store.js';
3
3
  import type { ScheduledDecision, TriageIssue } from './triage.js';
4
+ import type { HitlEntry } from './launch.js';
4
5
 
5
6
  export interface StewardCardStore {
6
7
  createCard(input: CreateCardInput): TaskCard;
@@ -14,6 +15,22 @@ export interface StewardCardDeps {
14
15
  now?: () => Date;
15
16
  }
16
17
 
18
+ /** Record the first intake for each polled issue, independently of LLM triage. */
19
+ export function recordIntakeOnCards(store: StewardCardStore, issues: TriageIssue[], now: () => Date = () => new Date()): void {
20
+ for (const issue of issues) {
21
+ const card = store.createCard({ goalId: `linear:${issue.identifier}`, title: redactSecrets(issue.title) });
22
+ const intakeKey = `intake:${issue.identifier}`;
23
+ if (!card.sections.some(section => section.key === intakeKey)) {
24
+ const directiveSource = /^출처: (telegram|pwa|tui|cli)$/m.exec(issue.body)?.[1];
25
+ const directiveTime = /^시각: (\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z)$/m.exec(issue.body)?.[1];
26
+ store.appendSection(card.id, {
27
+ key: intakeKey, owner: 'steward',
28
+ content: JSON.stringify({ summary: redactSecrets(issue.title), source: directiveSource ?? `linear:${issue.identifier}`, at: directiveTime ?? now().toISOString() }),
29
+ });
30
+ }
31
+ }
32
+ }
33
+
17
34
  /** Record observe-only intake and the latest scheduled judgment without launching any work. */
18
35
  export function recordTriageOnCards(decisions: ScheduledDecision[], issues: TriageIssue[], deps: StewardCardDeps = {}): void {
19
36
  const store = deps.store ?? new CardStore(deps.root);
@@ -22,18 +39,10 @@ export function recordTriageOnCards(decisions: ScheduledDecision[], issues: Tria
22
39
  for (const decision of decisions) {
23
40
  const issue = byIssue.get(decision.issue);
24
41
  if (!issue) throw new Error(`Missing issue ${decision.issue}`);
42
+ recordIntakeOnCards(store, [issue], deps.now);
25
43
  const card = store.createCard({ goalId: `linear:${issue.identifier}`, title: redactSecrets(issue.title) });
26
- const intakeKey = `intake:${issue.identifier}`;
27
- if (!card.sections.some(section => section.key === intakeKey)) {
28
- const directiveSource = /^출처: (telegram|pwa|tui|cli)$/m.exec(issue.body)?.[1];
29
- const directiveTime = /^시각: (\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z)$/m.exec(issue.body)?.[1];
30
- store.appendSection(card.id, {
31
- key: intakeKey, owner: 'steward',
32
- content: JSON.stringify({ summary: redactSecrets(issue.title), source: directiveSource ?? `linear:${issue.identifier}`, at: directiveTime ?? (deps.now ?? (() => new Date()))().toISOString() }),
33
- });
34
- }
35
44
  const { rung, dependsOn, priority, disposition, hitlReason, why, owner } = decision;
36
- const judgment = { rung, dependsOn, priority, disposition: rung === 'hitl' || hitlReason ? 'hitl' : disposition, hitlReason: hitlReason ?? null, why: redactSecrets(why), ...(owner ? { owner } : {}) };
45
+ const judgment = { rung, dependsOn, priority, disposition: rung === 'hitl' || hitlReason ? 'hitl' : disposition, hitlReason: hitlReason ?? null, why: redactSecrets(why), ...(owner ? { owner } : {}), ...(decision.capability ? { capability: decision.capability } : {}) };
37
46
  const key = `triage:${createHash('sha256').update(JSON.stringify(judgment)).digest('hex')}`;
38
47
  if (!card.sections.some(section => section.key === key)) {
39
48
  store.appendSection(card.id, { key, owner: 'steward', content: JSON.stringify(judgment) });
@@ -44,7 +53,7 @@ export function recordTriageOnCards(decisions: ScheduledDecision[], issues: Tria
44
53
  }
45
54
  }
46
55
 
47
- function recordOnCard(section: 'launch' | 'outcome', issue: Pick<TriageIssue, 'identifier' | 'title'>, value: object, deps: StewardCardDeps): void {
56
+ function recordOnCard(section: 'launch' | 'outcome' | 'hitl', issue: Pick<TriageIssue, 'identifier' | 'title'>, value: object, deps: StewardCardDeps): void {
48
57
  const store = deps.store ?? new CardStore(deps.root);
49
58
  try {
50
59
  const content = redactSecrets(JSON.stringify(value));
@@ -67,3 +76,8 @@ export function recordLaunchOnCard(issue: Pick<TriageIssue, 'identifier' | 'titl
67
76
  export function recordOutcomeOnCard(issue: Pick<TriageIssue, 'identifier' | 'title'>, outcome: object, deps: StewardCardDeps = {}): void {
68
77
  recordOnCard('outcome', issue, outcome, deps);
69
78
  }
79
+
80
+ /** Append a HITL handoff revision (pending → raised/unavailable) to the issue card. */
81
+ export function recordHitlOnCard(issue: Pick<TriageIssue, 'identifier' | 'title'>, entry: HitlEntry, deps: StewardCardDeps = {}): void {
82
+ recordOnCard('hitl', issue, entry, deps);
83
+ }
@@ -1,8 +1,33 @@
1
+ import { readdirSync, readFileSync } from 'node:fs';
2
+ import { join, resolve } from 'node:path';
3
+ import { spawnSync } from 'node:child_process';
1
4
  import { resolveRoleModel } from '../user-config.js';
2
5
  import type { HitlReason, Rung, TriageIssue } from './triage.js';
3
6
 
4
7
  export type StewardAsk = (prompt: string, role: 'classify' | 'planning') => Promise<unknown>;
5
- export interface ClassifiedIssue { issue: string; rung: Rung; why: string; hitlReason?: HitlReason; duplicateOf?: string }
8
+ export interface ClassifiedIssue { issue: string; rung: Rung; why: string; hitlReason?: HitlReason; duplicateOf?: string; capability?: 'new-capability' | 'existing-capability' }
9
+ export interface CapabilityInventory { graphs: readonly string[]; plugins: readonly string[]; commands: readonly string[] }
10
+
11
+ /** Read installed names, not promises in documentation. The caller can supply a test-instance root. */
12
+ export function installedCapabilities(root: string = resolve(import.meta.dir, '../..')): CapabilityInventory {
13
+ const files = (dir: string): string[] => {
14
+ try { return readdirSync(dir, { withFileTypes: true }).flatMap(entry => entry.isDirectory()
15
+ ? files(join(dir, entry.name)) : entry.isFile() && /\.ya?ml$/.test(entry.name) ? [join(dir, entry.name)] : []); }
16
+ catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') return []; throw error; }
17
+ };
18
+ const graphs = files(join(root, 'graphs')).flatMap(file => {
19
+ const match = /^graph_id:\s*['"]?([^\s'"#]+)/m.exec(readFileSync(file, 'utf8'));
20
+ return match ? [match[1]!] : [];
21
+ });
22
+ let plugins: string[];
23
+ try { plugins = readdirSync(join(root, 'plugins'), { withFileTypes: true }).filter(entry => entry.isDirectory() && entry.name !== 'examples').map(entry => entry.name); }
24
+ catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; plugins = []; }
25
+ const help = spawnSync(process.execPath, [join(root, 'bin/elanous.mjs'), '--test', '--help'], { cwd: root, encoding: 'utf8', timeout: 15_000 });
26
+ // An unconfigured test instance cannot start the CLI. Unknown commands must not be treated as installed.
27
+ const commands = help.status === 0
28
+ ? [...help.stdout.matchAll(/^\s{2}(?!-)([\w:-]+)(?:\s|$)/gm)].map(match => match[1]!) : [];
29
+ return { graphs, plugins, commands };
30
+ }
6
31
  export interface IssuePlan { issue: string; priority: number; dependsOn: string[]; owner?: string }
7
32
 
8
33
  const unsafe: Array<[HitlReason, RegExp]> = [
@@ -42,14 +67,17 @@ export async function askSteward(prompt: string, role: 'classify' | 'planning'):
42
67
  return parsed(result);
43
68
  }
44
69
 
45
- export async function classifyIssue(issue: TriageIssue, ask: StewardAsk = askSteward): Promise<ClassifiedIssue> {
70
+ export async function classifyIssue(issue: TriageIssue, ask: StewardAsk = askSteward, inventory: CapabilityInventory = installedCapabilities()): Promise<ClassifiedIssue> {
46
71
  const candidate = unsafeReason(issue);
47
- const response = parsed(await ask(`스튜어드 분류. JSON 객체만: {"rung":0|1|2|3|4|5|"hitl","hitlReason":null,"why":"한 문장"}. 0=이미됨, 1=셸, 2=CLI, 3=조사, 4=하니스 골, 5=외부 에이전트. 돈·공개·보안·비가역은 hitl; 그 외 사람 확인은 other. 확인되지 않은 완료를 추측하지 마라. ${candidate ? `안전 후보 ${candidate}: 이유를 확인하라.` : ''}\n이슈: ${JSON.stringify(issue)}`, 'classify'));
72
+ const response = parsed(await ask(`스튜어드 분류. JSON 객체만: {"rung":0|1|2|3|4|5|"hitl","hitlReason":null,"why":"한 문장","capability":"new-capability"|"existing-capability"}. 0=이미됨, 1=셸, 2=CLI, 3=조사, 4=하니스 골, 5=외부 에이전트. 돈·공개·보안·비가역은 hitl; 그 외 사람 확인은 other. 새 능력인지 기존 그래프·플러그인·명령으로 되는 일인지 판정하라. 존재만으로 그 기능이 소원을 충족한다고 추측하지 마라. 확인되지 않은 완료를 추측하지 마라. ${candidate ? `안전 후보 ${candidate}: 이유를 확인하라.` : ''}\n설치된 능력: ${JSON.stringify(inventory)}\n이슈: ${JSON.stringify(issue)}`, 'classify'));
48
73
  if (![0, 1, 2, 3, 4, 5, 'hitl'].includes(response.rung as Rung) || typeof response.why !== 'string' || !response.why.trim()) throw new Error(`Invalid steward classification: ${issue.identifier}`);
49
74
  const reason = candidate ?? (unsafe.some(([name]) => name === response.hitlReason) ? response.hitlReason as HitlReason : undefined)
50
75
  ?? (response.rung === 'hitl' ? 'other' : undefined);
76
+ // A missing or unknown verdict does not block triage; working-backwards drafting is simply skipped for that issue.
77
+ const capability = response.capability === 'new-capability' || response.capability === 'existing-capability' ? response.capability : undefined;
51
78
  return { issue: issue.identifier, rung: reason ? 'hitl' : response.rung as Rung,
52
- ...(reason ? { hitlReason: reason } : {}), why: response.why.trim().replace(/\s+/g, ' ') };
79
+ ...(reason ? { hitlReason: reason } : {}), why: response.why.trim().replace(/\s+/g, ' '),
80
+ ...(capability ? { capability } : {}) };
53
81
  }
54
82
 
55
83
  /** One request for the current batch, never one request per issue. */
@@ -8,9 +8,14 @@ import { debug, redactSecretText } from '../debug/log.js';
8
8
  import { effectiveInstanceRoot } from '../instance/resolve.js';
9
9
  import { redactSecrets } from '../task-cards/card-store.js';
10
10
  import { directiveHash } from './directive.js';
11
- import { recordTriageOnCards, type StewardCardDeps } from './steward-cards.js';
11
+ import { notifyOutcome, type OutcomeNotifyDeps } from './outcome-notify.js';
12
+ import { recordIntakeOnCards, recordTriageOnCards, recordLaunchOnCard, recordOutcomeOnCard, recordHitlOnCard, type StewardCardDeps } from './steward-cards.js';
13
+ import { planLaunches, launch, collectOutcomes, raiseHitl, readLaunchLedger, saveLaunchLedger, type LaunchDeps } from './launch.js';
14
+ import { listFabricExecutionCandidates } from '../self-dev/fabric-plan-core.js';
15
+ import { InvalidWorkingBackwardsDraft, recordWorkingBackwardsOnCards } from './working-backwards.js';
16
+ import { CardStore } from '../task-cards/card-store.js';
12
17
  import { triageWithinBudget } from './triage-budget.js';
13
- import { askSteward, classifyIssue, planIssues, ruleJudgment, unsafeReason, type ClassifiedIssue, type StewardAsk } from './triage-plan.js';
18
+ import { askSteward, classifyIssue, installedCapabilities, planIssues, ruleJudgment, unsafeReason, type ClassifiedIssue, type StewardAsk } from './triage-plan.js';
14
19
  import { failureAlertText, markStageStart, markStageEnd, readFailureStreak, shouldAlert, writeFailureStreak, type FailureStreak, type StewardStage } from './failure-streak.js';
15
20
 
16
21
  export type Rung = 0 | 1 | 2 | 3 | 4 | 5 | 'hitl';
@@ -24,14 +29,18 @@ export interface TriageDecision {
24
29
  owner?: string;
25
30
  /** Carried from a previous triage without a current judgment; scheduling must not route it. */
26
31
  deferred?: true;
32
+ capability?: ClassifiedIssue['capability'];
27
33
  }
28
34
  export interface ScheduledDecision extends TriageDecision { disposition: 'now' | 'wait' | 'hitl' }
29
35
  export interface StewardSettings {
30
- mode?: 'observe' | 'act'; linearTeam?: string;
36
+ mode?: 'off' | 'shadow' | 'live'; linearTeam?: string;
31
37
  roles?: Record<string, { maxConcurrent?: number }>;
32
38
  budget?: number;
33
39
  tracks?: Record<string, string>;
34
40
  alertAfterFailures?: number;
41
+ launch?: 'off' | 'shadow' | 'live';
42
+ maxParallel?: number;
43
+ podPool?: string;
35
44
  }
36
45
  export interface StewardDeps {
37
46
  fetch?: typeof fetch;
@@ -44,14 +53,28 @@ export interface StewardDeps {
44
53
  cardStore?: StewardCardDeps['store'];
45
54
  warn?: (message: string) => void;
46
55
  ask?: StewardAsk;
56
+ launchSettings?: Pick<StewardSettings, 'mode' | 'launch' | 'maxParallel' | 'podPool'>;
57
+ launchCommand?: LaunchDeps['command'];
58
+ spawnLaunch?: LaunchDeps['spawn'];
59
+ launchGate?: LaunchDeps['gate'];
60
+ outcomeNotify?: Pick<OutcomeNotifyDeps, 'document' | 'message' | 'worktreeCommand' | 'loadOrigin' | 'loadOrigins'>;
47
61
  /** Internal command boundary: the current sync has already claimed this pid. */
48
62
  stageOwnedByCommand?: boolean;
49
63
  }
50
64
 
51
65
  export function stewardSettings(): StewardSettings {
52
- return getUserConfig().loops?.steward ?? {};
66
+ return getUserConfig().loops?.steward ?? { mode: 'shadow' };
53
67
  }
54
68
 
69
+ function effectiveStewardMode(settings: StewardSettings): 'off' | 'shadow' | 'live' {
70
+ return settings.mode ?? settings.launch ?? 'shadow';
71
+ }
72
+
73
+ function resolvedStewardSettings(override?: StewardDeps['launchSettings']): StewardSettings {
74
+ const settings = { ...stewardSettings(), ...override };
75
+ if (override?.launch !== undefined && override.mode === undefined) settings.mode = override.launch;
76
+ return { ...settings, mode: effectiveStewardMode(settings) };
77
+ }
55
78
 
56
79
  function parseJudgment(issue: TriageIssue, raw: unknown, tracks?: Record<string, string>): TriageDecision {
57
80
  const value = typeof raw === 'string' ? JSON.parse(raw) as unknown : raw;
@@ -89,6 +112,8 @@ export async function triageIssues(issues: TriageIssue[], judge?: NonNullable<St
89
112
  const classified: ClassifiedIssue[] = [];
90
113
  const classifiedIds = new Set<string>();
91
114
  const counts = { rule: 0, classify: 0, planning: 0 };
115
+ // Inventory is shared by every classification in this batch; do not spawn --help per issue.
116
+ let inventory: ReturnType<typeof installedCapabilities> | undefined;
92
117
  const signal = (result: TriageDecision): void => {
93
118
  decide({ kind: result.rung === 'hitl' ? 'ESCALATE' : 'ROUTE', what: result.issue, reason: result.why,
94
119
  purpose: 'steward triage', target: result.owner ? `${result.rung} · ${result.owner}` : String(result.rung), refs: { issue: result.issue } });
@@ -99,7 +124,7 @@ export async function triageIssues(issues: TriageIssue[], judge?: NonNullable<St
99
124
  const byRule = ruleJudgment(issue, observedIssues ?? issues);
100
125
  if (byRule) { counts.rule++; return { ...byRule, dependsOn: [], priority: 0 }; }
101
126
  counts.classify++;
102
- const row = await classifyIssue(issue, ask);
127
+ const row = await classifyIssue(issue, ask, inventory ??= installedCapabilities());
103
128
  return { ...row, dependsOn: [], priority: 0 };
104
129
  } catch (error) {
105
130
  try { debug.log('steward.stage', 'judgment-invalid', { issue: issue.identifier, reason: redactSecrets(String(error)) }); } catch { /* fail-soft */ }
@@ -111,6 +136,7 @@ export async function triageIssues(issues: TriageIssue[], judge?: NonNullable<St
111
136
  result.why !== 'triage judgment timed out — human review' &&
112
137
  result.why !== 'triage deferred — deadline') {
113
138
  classified.push({ issue: result.issue, rung: result.rung, why: result.why,
139
+ ...(result.capability ? { capability: result.capability } : {}),
114
140
  ...(result.hitlReason ? { hitlReason: result.hitlReason } : {}) });
115
141
  classifiedIds.add(result.issue);
116
142
  } else signal(result);
@@ -232,10 +258,10 @@ function readState(path: string): { issues: Record<string, string>; digestDay?:
232
258
  catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') return { issues: {} }; throw error; }
233
259
  }
234
260
 
235
- /** Each command node invokes one stage. No stage can spawn, launch, merge or approve. */
261
+ /** Each command node invokes one stage. Live launch still requires the launch gate; merge and approval remain with the harness and human. */
236
262
  export async function runStewardStage(stage: 'sync' | 'triage' | 'schedule' | 'report', deps: StewardDeps = {}): Promise<void> {
237
- const settings = stewardSettings();
238
- if ((settings.mode ?? 'observe') !== 'observe') throw new Error('steward act mode is out of scope');
263
+ const settings = resolvedStewardSettings(deps.launchSettings);
264
+ if (settings.mode === 'off') return;
239
265
  const root = deps.root ?? effectiveInstanceRoot();
240
266
  if (stage === 'sync' && !deps.stageOwnedByCommand) {
241
267
  return withStageSlot(root, async () => {
@@ -257,7 +283,25 @@ export async function runStewardStage(stage: 'sync' | 'triage' | 'schedule' | 'r
257
283
  if (!key) throw new Error('connector.linear.apiKey missing');
258
284
  if (stage === 'sync') {
259
285
  const events = await fetchLinearIssues({ apiKey: key, teamKey: settings.linearTeam ?? 'ELA', fetch: fetchFn });
260
- writeFileSync(snapshot, JSON.stringify(events.map(e => ({ identifier: e.identifier, ref: e.ref, title: e.title, body: e.body }))));
286
+ const issues = events.map(e => ({ identifier: e.identifier, ref: e.ref, title: e.title, body: e.body }));
287
+ writeFileSync(snapshot, JSON.stringify(issues));
288
+ const cardIssues = issues.filter((issue): issue is TriageIssue => typeof issue.identifier === 'string');
289
+ if (cardIssues.length) {
290
+ const observeCardFailure = (error: unknown, issue?: string): void => {
291
+ const message = `steward sync: card write failed${issue ? ` (${issue})` : ''}: ${redactSecrets(error instanceof Error ? error.message : String(error))}`;
292
+ try { debug.log('steward.cards', 'write-failed', { error: message }); } catch { /* observation must not fail sync */ }
293
+ try { (deps.warn ?? console.warn)(message); } catch { /* observation must not fail sync */ }
294
+ };
295
+ try {
296
+ const store = deps.cardStore ?? new CardStore(root);
297
+ try {
298
+ for (const issue of cardIssues) {
299
+ try { recordIntakeOnCards(store, [issue], deps.now); }
300
+ catch (error) { observeCardFailure(error, issue.identifier); }
301
+ }
302
+ } finally { if (!deps.cardStore) store.close(); }
303
+ } catch (error) { observeCardFailure(error); }
304
+ }
261
305
  } else if (stage === 'triage') {
262
306
  const issues = JSON.parse(readFileSync(snapshot, 'utf8')) as TriageIssue[];
263
307
  const judge = deps.judge;
@@ -286,7 +330,8 @@ export async function runStewardStage(stage: 'sync' | 'triage' | 'schedule' | 'r
286
330
  let cursor: string | null = null;
287
331
  do {
288
332
  const data: { issues: { nodes: Array<{ identifier: string; state?: { type: string } }>; pageInfo: { hasNextPage: boolean; endCursor: string | null } } } = await linear(key,
289
- 'query($ids:[String!]!,$after:String){issues(filter:{identifier:{in:$ids}},first:250,after:$after){nodes{identifier state{type}} pageInfo{hasNextPage endCursor}}}',
333
+ // IssueFilter has no `identifier` field (HTTP 400 GRAPHQL_VALIDATION_FAILED · 10-02 measured); `id` accepts identifiers like ELA-1.
334
+ 'query($ids:[ID!]!,$after:String){issues(filter:{id:{in:$ids}},first:250,after:$after){nodes{identifier state{type}} pageInfo{hasNextPage endCursor}}}',
290
335
  { ids: batch, after: cursor }, fetchFn);
291
336
  if (!Array.isArray(data.issues?.nodes) || !data.issues.pageInfo ||
292
337
  (data.issues.pageInfo.hasNextPage && (!data.issues.pageInfo.endCursor || data.issues.pageInfo.endCursor === cursor))) {
@@ -297,7 +342,48 @@ export async function runStewardStage(stage: 'sync' | 'triage' | 'schedule' | 'r
297
342
  } while (cursor);
298
343
  }
299
344
  }
300
- writeFileSync(schedule, JSON.stringify(scheduleTriage(decisions, settings, {}, completed)));
345
+ let rows = scheduleTriage(decisions, settings, {}, completed);
346
+ const issues = existsSync(snapshot) ? JSON.parse(readFileSync(snapshot, 'utf8')) as TriageIssue[] : [];
347
+ // L2 — a new-capability wish gets its PR/FAQ and manual drafts on the card before any launch is recorded.
348
+ // Only new-capability rows: triage sections are written first so the card reads intake → triage → prfaq → manual → launch;
349
+ // the report stage repeats both calls idempotently. A wish whose draft is invalid goes to hitl on its own —
350
+ // the other rows (and the shadow ledger) still proceed this tick.
351
+ {
352
+ const present = new Set(issues.map(issue => issue.identifier));
353
+ const wishes = rows.filter(row => present.has(row.issue) && row.capability === 'new-capability' && row.rung !== 'hitl');
354
+ const store = wishes.length ? deps.cardStore ?? new CardStore(root) : undefined;
355
+ const invalid = new Set<string>();
356
+ if (store) try {
357
+ recordTriageOnCards(wishes, issues, { root, store, now: deps.now });
358
+ for (const wish of wishes) {
359
+ try { await recordWorkingBackwardsOnCards([wish], issues, store, deps.ask ?? askSteward); }
360
+ catch (error) {
361
+ if (!(error instanceof InvalidWorkingBackwardsDraft)) throw error;
362
+ invalid.add(wish.issue);
363
+ debug.log('steward.stage', 'draft-invalid', { issue: wish.issue });
364
+ }
365
+ }
366
+ } finally { if (!deps.cardStore) store.close(); }
367
+ if (invalid.size) rows = rows.map(row => invalid.has(row.issue)
368
+ ? { ...row, rung: 'hitl' as const, disposition: 'hitl' as const, hitlReason: 'other' as const, why: 'working-backwards draft invalid' }
369
+ : row);
370
+ }
371
+ writeFileSync(schedule, JSON.stringify(rows));
372
+ const ledger = readLaunchLedger(root);
373
+ const launchDeps: LaunchDeps = { root, settings, ledger, command: deps.launchCommand, spawn: deps.spawnLaunch, gate: deps.launchGate };
374
+ const cardDeps = { root, store: deps.cardStore, now: deps.now };
375
+ collectOutcomes(ledger, launchDeps);
376
+ const plan = planLaunches(rows, issues, ledger, settings);
377
+ // Keep fabric candidates distinct from Linear launches until a fabric executor is wired.
378
+ writeFileSync(join(dir, 'fabric-candidates.json'), JSON.stringify(listFabricExecutionCandidates(root)));
379
+ for (const { issue, row } of plan.hitl) {
380
+ const entry = raiseHitl(issue, row, launchDeps);
381
+ recordHitlOnCard(issue, entry, cardDeps);
382
+ }
383
+ for (const item of plan.launches) {
384
+ const entry = launch(item, launchDeps);
385
+ recordLaunchOnCard(item.issue, entry, cardDeps);
386
+ }
301
387
  } else {
302
388
  const rows = JSON.parse(readFileSync(schedule, 'utf8')) as ScheduledDecision[];
303
389
  const path = reportPath(root);
@@ -305,15 +391,23 @@ export async function runStewardStage(stage: 'sync' | 'triage' | 'schedule' | 'r
305
391
  const at = (deps.now ?? (() => new Date()))().toISOString();
306
392
  const issues = JSON.parse(readFileSync(snapshot, 'utf8')) as TriageIssue[];
307
393
  try {
308
- recordTriageOnCards(rows, issues, { root, store: deps.cardStore, now: deps.now });
394
+ const present = new Set(issues.map(issue => issue.identifier));
395
+ const known = rows.filter(row => present.has(row.issue));
396
+ const store = deps.cardStore ?? new CardStore(root);
397
+ try {
398
+ recordTriageOnCards(known, issues, { root, store, now: deps.now });
399
+ // Rows without a capability verdict (legacy, injected or unjudged) proceed; only drafting is skipped for them.
400
+ await recordWorkingBackwardsOnCards(known, issues, store, deps.ask ?? askSteward);
401
+ } finally { if (!deps.cardStore) store.close(); }
309
402
  } catch (error) {
310
403
  const message = `steward report: card write failed: ${redactSecrets(error instanceof Error ? error.message : String(error))}`;
311
404
  debug.log('steward.cards', 'write-failed', { error: message });
312
405
  (deps.warn ?? console.warn)(message);
406
+ if (error instanceof InvalidWorkingBackwardsDraft) throw error;
313
407
  }
314
408
  for (const row of rows) {
315
409
  const issue = issues.find(item => item.identifier === row.issue);
316
- if (!issue) throw new Error(`Missing issue ${row.issue}`);
410
+ if (!issue) continue;
317
411
  const body = `스튜어드 observe · ${row.disposition} · rung ${row.rung} · 우선순위 ${row.priority} · 의존 ${row.dependsOn.join(', ') || '없음'}${row.duplicateOf ? ` · 중복 ${row.duplicateOf}` : ''} · ${row.why}${row.hitlReason ? ` · HITL ${row.hitlReason}` : ''}`;
318
412
  const hash = directiveHash(body);
319
413
  if (state.issues[row.issue] === hash) continue;
@@ -322,6 +416,27 @@ export async function runStewardStage(stage: 'sync' | 'triage' | 'schedule' | 'r
322
416
  state.issues[row.issue] = hash;
323
417
  writeFileSync(path, JSON.stringify(state));
324
418
  }
419
+ const ledger = readLaunchLedger(root);
420
+ const outcomes = collectOutcomes(ledger, { root, settings, ledger, command: deps.launchCommand, spawn: deps.spawnLaunch });
421
+ for (const outcome of outcomes) {
422
+ const issue = issues.find(item => item.identifier === outcome.issue) ??
423
+ { identifier: outcome.issue, title: outcome.title, ref: '', body: '' };
424
+ recordOutcomeOnCard(issue, outcome, { root, store: deps.cardStore, now: deps.now });
425
+ notifyOutcome(outcome, { root, ledger, shadow: settings.mode !== 'live', ...deps.outcomeNotify });
426
+ const prefix = ['telegram', 'pwa', 'tui', 'cli'].includes(outcome.source) ? `${outcome.source} · ` : '';
427
+ await (deps.sendDigest ?? sendStewardDigest)(`${prefix}${issue.title} · PR ${outcome.prNumber ?? '없음'} · ${outcome.status}`);
428
+ outcome.reported = true;
429
+ saveLaunchLedger(root, ledger);
430
+ }
431
+ const justCollected = new Set(outcomes.map(outcome => outcome.issue));
432
+ for (const outcome of Object.values(ledger.launches)) {
433
+ if ((settings.mode !== 'live' && outcome.status === 'shadow' && !outcome.notified) ||
434
+ (settings.mode === 'live' && !justCollected.has(outcome.issue) && outcome.reported && outcome.notified !== 'shadow') ||
435
+ // A finished run whose PR waits for merge still owes the wish's chat its PR link (once; failures retry).
436
+ (settings.mode === 'live' && outcome.awaitingMerge === true && !outcome.reported && outcome.notified !== 'sent')) {
437
+ notifyOutcome(outcome, { root, ledger, shadow: settings.mode !== 'live', ...deps.outcomeNotify });
438
+ }
439
+ }
325
440
  const day = at.slice(0, 10);
326
441
  if (state.digestDay !== day) {
327
442
  const send = deps.sendDigest ?? sendStewardDigest;
@@ -371,6 +486,7 @@ async function withStageSlot<T>(root: string, run: () => Promise<T>, onBusy?: ()
371
486
 
372
487
  /** The command-node boundary records both normal exits and an unclosed stage from a killed process. */
373
488
  export async function runStewardStageCommand(stage: StewardStage, deps: StewardCommandDeps = {}): Promise<0 | 1> {
489
+ if (resolvedStewardSettings(deps.launchSettings).mode === 'off') return 0;
374
490
  const root = deps.root ?? effectiveInstanceRoot();
375
491
  if (stage !== 'sync' && existsSync(skippedPath(root))) return 0;
376
492
  return withStageSlot(root, async () => {
@@ -0,0 +1,64 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { debug } from '../debug/log.js';
3
+ import { redactSecrets } from '../task-cards/card-store.js';
4
+ import type { ScheduledDecision, TriageIssue } from './triage.js';
5
+ import type { StewardAsk } from './triage-plan.js';
6
+ import type { StewardCardStore } from './steward-cards.js';
7
+
8
+ export interface WorkingBackwardsDraft {
9
+ prfaq: string;
10
+ manual: string;
11
+ }
12
+
13
+ export class InvalidWorkingBackwardsDraft extends Error {
14
+ constructor() { super('Invalid working-backwards draft'); }
15
+ }
16
+
17
+ /** Draft promises before implementation; no document, site, or channel is written here. */
18
+ export async function draftWorkingBackwards(issue: TriageIssue, ask: StewardAsk): Promise<WorkingBackwardsDraft> {
19
+ const raw = await ask(`새 능력 소원의 구현 전 워킹 백워드 초안을 JSON 객체 {"prfaq":"...","manual":"...","signals":["판정 신호: 조건 = ...; 관측 = ...; 기대 = ..."]} 로만 써라. PR/FAQ 는 docs/marketing/TEMPLATE-prfaq-2026-09-29.md 모양의 ① 한 줄, 부제(누가), ② 문제(왜), ③ 해결(무엇), ④ 대표의 말(없는 인용은 확인 대기), ⑤ 시작하기, ⑥ 고객 FAQ(질문·답 셋 이상), ⑦ 내부 FAQ, ⑧ 판정 기준을 포함한다. 매뉴얼은 docs/marketing/TEMPLATE-manual-2026-10-02.md 모양으로 사용자가 칠 명령과 볼 화면을 제안하되 구현 전 가설이라고 명시한다. 아직 구현된 명령·화면이라고 주장하지 마라. 판정 신호 한두 문장은 PR/FAQ ⑧ 에 그대로 포함시켜라.\n소원: ${JSON.stringify(issue)}`, 'planning');
20
+ let value: unknown = raw;
21
+ if (typeof raw === 'string') {
22
+ // A draft that is not JSON is as invalid as one with missing sections — the report must stop on it too.
23
+ try { value = JSON.parse(raw.replace(/^```(?:json)?\s*|\s*```$/g, '').trim()); }
24
+ catch { throw new InvalidWorkingBackwardsDraft(); }
25
+ }
26
+ if (!value || typeof value !== 'object' || Array.isArray(value)) throw new InvalidWorkingBackwardsDraft();
27
+ const row = value as Record<string, unknown>;
28
+ const prfaq = row.prfaq;
29
+ const manual = row.manual;
30
+ const faq = typeof prfaq === 'string' ? prfaq.split('⑥ 고객 FAQ')[1]?.split('⑦ 내부 FAQ')[0] ?? '' : '';
31
+ const criteria = typeof prfaq === 'string' ? prfaq.split('⑧ 판정 기준')[1] ?? '' : '';
32
+ const answered = faq.split('\n').filter(line => /^\|\s*[^|?]+\?\s*\|\s*[^|\s][^|]*\|/.test(line));
33
+ if (typeof prfaq !== 'string' || typeof manual !== 'string' ||
34
+ !Array.isArray(row.signals) || row.signals.length < 1 || row.signals.length > 2 ||
35
+ !row.signals.every(signal => typeof signal === 'string' && /^판정 신호: 조건 = .+; 관측 = .+; 기대 = .+\.$/.test(signal) && criteria.includes(signal)) ||
36
+ !['①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧'].every(section => prfaq.includes(section)) ||
37
+ answered.length < 3 ||
38
+ !/elanous\s+\S+/.test(manual) || !/화면/.test(manual)) throw new InvalidWorkingBackwardsDraft();
39
+ return { prfaq: redactSecrets(prfaq), manual: redactSecrets(manual) };
40
+ }
41
+
42
+ /** Called during the steward report, after triage and before a launch can be recorded. */
43
+ export async function recordWorkingBackwardsOnCards(
44
+ decisions: ScheduledDecision[], issues: TriageIssue[], store: StewardCardStore, ask: StewardAsk,
45
+ ): Promise<void> {
46
+ const byId = new Map(issues.map(issue => [issue.identifier, issue]));
47
+ for (const decision of decisions) {
48
+ const issue = byId.get(decision.issue);
49
+ if (!issue) throw new Error(`Missing issue ${decision.issue}`);
50
+ if (decision.capability !== 'new-capability' || decision.deferred || decision.duplicateOf || decision.rung === 'hitl') {
51
+ debug.log('steward.working-backwards', 'skipped-existing', { issueId: issue.identifier, verdict: decision.capability ?? 'unknown' });
52
+ continue;
53
+ }
54
+ const card = store.createCard({ goalId: `linear:${issue.identifier}`, title: redactSecrets(issue.title) });
55
+ if (card.sections.some(section => section.key.startsWith('prfaq:')) && card.sections.some(section => section.key.startsWith('manual:'))) continue;
56
+ if (card.sections.some(section => section.key.startsWith('launch:'))) throw new Error(`Working-backwards draft follows launch: ${issue.identifier}`);
57
+ const draft = await draftWorkingBackwards(issue, ask);
58
+ for (const [section, content] of [['prfaq', draft.prfaq], ['manual', draft.manual]] as const) {
59
+ const key = `${section}:${createHash('sha256').update(content).digest('hex')}`;
60
+ if (!card.sections.some(item => item.key === key)) store.appendSection(card.id, { key, owner: 'steward', content });
61
+ }
62
+ debug.log('steward.working-backwards', 'drafted', { issueId: issue.identifier, verdict: decision.capability });
63
+ }
64
+ }