elanous 0.2.10 → 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 (584) 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/{8067-87ce253ec2f23697.js → 5845-e6c003ce8d5fd36a.js} +1 -1
  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-54b32bd27726588d.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-35414e0a007b4082.js +1 -0
  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 -1
  152. package/apps/pwa/out/ops/seats/index.txt +22 -22
  153. package/apps/pwa/out/outputs/index.html +1 -0
  154. package/apps/pwa/out/outputs/index.txt +30 -0
  155. package/apps/pwa/out/reflection/index.html +1 -1
  156. package/apps/pwa/out/reflection/index.txt +23 -23
  157. package/apps/pwa/out/scheduler/index.html +1 -1
  158. package/apps/pwa/out/scheduler/index.txt +23 -23
  159. package/apps/pwa/out/sessions/index.html +1 -1
  160. package/apps/pwa/out/sessions/index.txt +22 -22
  161. package/apps/pwa/out/settings/devices/index.html +1 -1
  162. package/apps/pwa/out/settings/devices/index.txt +23 -23
  163. package/apps/pwa/out/settings/index.html +1 -1
  164. package/apps/pwa/out/settings/index.txt +23 -23
  165. package/apps/pwa/out/setup/done/index.html +1 -1
  166. package/apps/pwa/out/setup/done/index.txt +23 -23
  167. package/apps/pwa/out/setup/index.html +1 -1
  168. package/apps/pwa/out/setup/index.txt +23 -23
  169. package/apps/pwa/out/share/index.html +1 -1
  170. package/apps/pwa/out/share/index.txt +23 -23
  171. package/apps/pwa/out/showroom/index.html +1 -1
  172. package/apps/pwa/out/showroom/index.txt +22 -22
  173. package/apps/pwa/out/tasks/index.html +1 -1
  174. package/apps/pwa/out/tasks/index.txt +23 -23
  175. package/apps/pwa/out/term/index.html +1 -1
  176. package/apps/pwa/out/term/index.txt +23 -24
  177. package/apps/pwa/out/today/index.html +1 -1
  178. package/apps/pwa/out/today/index.txt +23 -23
  179. package/apps/pwa/out/trace/index.html +1 -1
  180. package/apps/pwa/out/trace/index.txt +23 -23
  181. package/apps/pwa/out/vault/index.html +1 -1
  182. package/apps/pwa/out/vault/index.txt +23 -23
  183. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  184. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  185. package/apps/pwa/out/workflows/index.html +1 -1
  186. package/apps/pwa/out/workflows/index.txt +23 -23
  187. package/apps/pwa/out/workspace/index.html +1 -1
  188. package/apps/pwa/out/workspace/index.txt +23 -24
  189. package/apps/pwa/out/worktrees/index.html +1 -1
  190. package/apps/pwa/out/worktrees/index.txt +23 -23
  191. package/graphs/drafts/draft-cleanup.yaml +19 -0
  192. package/graphs/drafts/recipes.yaml +23 -0
  193. package/graphs/intake/intake-daily.yaml +2 -1
  194. package/graphs/ops/recipes.yaml +3 -0
  195. package/graphs/ops/secret-exposure.yaml +24 -0
  196. package/graphs/release/recipes.yaml +3 -0
  197. package/graphs/release/release-loop.yaml +4 -1
  198. package/graphs/video/explainer-line.yaml +2 -2
  199. package/package.json +2 -1
  200. package/plugins/elanous-hwp/README.md +25 -0
  201. package/plugins/elanous-hwp/cli.ts +22 -0
  202. package/plugins/elanous-hwp/converter.ts +237 -0
  203. package/plugins/elanous-hwp/fixtures/fixed-controls.hwpx +0 -0
  204. package/plugins/elanous-hwp/fixtures/fixed-table-controls.hwpx +0 -0
  205. package/plugins/elanous-hwp/fixtures/fixed.hwpx +0 -0
  206. package/plugins/elanous-hwp/plugin.json +16 -0
  207. package/plugins/elanous-hwp/plugin.ts +40 -0
  208. package/scripts/ci-public-export-leak-gate.ts +75 -14
  209. package/scripts/ci-public-leak-gate.ts +44 -9
  210. package/scripts/context-hooks/claude-stop.sh +4 -0
  211. package/scripts/context-hooks/emit.ts +24 -0
  212. package/scripts/context-hooks/post-tool-use.sh +4 -0
  213. package/scripts/coord-channel-watch.sh +12 -0
  214. package/scripts/coord-requests-bridge.ts +143 -0
  215. package/scripts/field-feed/lib.ts +4 -0
  216. package/scripts/fleet/node-setup.sh +20 -3
  217. package/scripts/homebrew-formula-bump.ts +69 -0
  218. package/scripts/marketing/measure-site-numbers.ts +169 -0
  219. package/scripts/measure-gate-shards.ts +309 -0
  220. package/scripts/mission-request-judge.ts +31 -1
  221. package/scripts/morning-report.ts +2 -2
  222. package/scripts/opportunity-scan.ts +1 -1
  223. package/scripts/release-loop/auto-approve-node.ts +21 -4
  224. package/scripts/release-loop/cut-branch.ts +84 -0
  225. package/scripts/release-loop/gate-node.ts +14 -13
  226. package/scripts/release-loop/mac-smoke-node.ts +229 -0
  227. package/scripts/release-loop/node-verdict.ts +3 -3
  228. package/scripts/release-loop/tui-node-checks.ts +42 -0
  229. package/scripts/release-loop/tui-sim-node.ts +32 -8
  230. package/scripts/release-loop/unattended-release.ts +5 -3
  231. package/scripts/release-loop/version-node.ts +61 -10
  232. package/scripts/signal-digest-cycle.ts +2 -7
  233. package/scripts/signal-digest-photo.ts +11 -0
  234. package/scripts/tech1-public-measure.ts +94 -0
  235. package/scripts/test-diet/node.ts +1 -1
  236. package/scripts/tui-regress.ts +2 -2
  237. package/scripts/ux/demo-rehearsal.ts +243 -0
  238. package/scripts/ux/lib/rehearsal-verdict.ts +73 -0
  239. package/skills/diagram-master/SKILL.md +366 -0
  240. package/skills/diagram-master/references/engines/excalidraw/color-palette.md +67 -0
  241. package/skills/diagram-master/references/engines/excalidraw/element-templates.md +182 -0
  242. package/skills/diagram-master/references/engines/excalidraw/json-schema.md +71 -0
  243. package/skills/diagram-master/references/engines/excalidraw/render_excalidraw.py +189 -0
  244. package/skills/diagram-master/references/engines/excalidraw/render_template.html +57 -0
  245. package/skills/diagram-master/references/engines/matplotlib/matplotlib-patterns.md +235 -0
  246. package/skills/diagram-master/references/engines/matplotlib/render_matplotlib.py +184 -0
  247. package/skills/diagram-master/references/engines/mermaid/domain-templates.md +435 -0
  248. package/skills/diagram-master/references/engines/mermaid/render_mermaid.py +116 -0
  249. package/skills/diagram-master/references/engines/mermaid/syntax-reference.md +2247 -0
  250. package/skills/diagram-master/references/engines/nanobannana/render_nanobannana2.py +168 -0
  251. package/skills/diagram-master/references/engines/svg/color-palette.md +188 -0
  252. package/skills/diagram-master/references/engines/svg/html-base-template.html +332 -0
  253. package/skills/diagram-master/references/engines/svg/interactivity-patterns.md +435 -0
  254. package/skills/diagram-master/references/engines/svg/render_interactive_svg.py +212 -0
  255. package/skills/diagram-master/references/engines/svg/svg-element-templates.md +349 -0
  256. package/skills/diagram-master/references/examples/README.md +69 -0
  257. package/skills/diagram-master/references/examples/ref_grid_architecture.png +0 -0
  258. package/skills/diagram-master/references/examples/ref_layered_flow.png +0 -0
  259. package/skills/diagram-master/references/pyproject.toml +16 -0
  260. package/skills/diagram-master/references/quality-checklist.md +33 -0
  261. package/skills/diagram-master/references/routing-guide.md +164 -0
  262. package/skills/diagram-master/references/uv.lock +1356 -0
  263. package/skills/google-workspace/SKILL.md +33 -0
  264. package/skills/google-workspace/scripts/gws-safe.sh +177 -0
  265. package/skills/grill-me/SKILL.md +1 -1
  266. package/skills/lecture-note-digitizer/SKILL.md +926 -0
  267. package/skills/lecture-note-digitizer/references/convert_to_pdf.py +30 -0
  268. package/skills/lecture-note-digitizer/references/extract_slide_diagram.py +73 -0
  269. package/skills/lecture-note-digitizer/references/html-template.md +167 -0
  270. package/skills/lecture-note-digitizer/references/katex-style-guide.md +165 -0
  271. package/skills/omni-crawl/SKILL.md +21 -11
  272. package/skills/omni-crawl/scripts/main.ts +6 -6
  273. package/skills/omni-crawl/src/router.ts +3 -3
  274. package/skills/omni-digest/SKILL.md +17 -9
  275. package/skills/omni-digest/digest.md +2 -2
  276. package/skills/omni-digest/src/fetch.ts +30 -3
  277. package/skills/pitch-deck/SKILL.md +2 -2
  278. package/skills/pitch-deck/template.html +2 -2
  279. package/skills/youtube-master/SKILL.md +163 -0
  280. package/skills/youtube-master/package-lock.json +558 -0
  281. package/skills/youtube-master/package.json +13 -0
  282. package/skills/youtube-master/references/study-note-template.md +124 -0
  283. package/skills/youtube-master/scripts/main.ts +435 -0
  284. package/skills/youtube-master/scripts/transcribe-local.ts +24 -0
  285. package/skills/youtube-master/src/audio.ts +63 -0
  286. package/skills/youtube-master/src/domain-staging.ts +50 -0
  287. package/skills/youtube-master/src/env.ts +36 -0
  288. package/skills/youtube-master/src/obsidian.ts +155 -0
  289. package/skills/youtube-master/src/router.ts +110 -0
  290. package/skills/youtube-master/src/stt.ts +199 -0
  291. package/skills/youtube-master/src/study-note.ts +175 -0
  292. package/skills/youtube-master/src/summarize.ts +358 -0
  293. package/skills/youtube-master/src/transcript.ts +76 -0
  294. package/skills/youtube-master/src/types.ts +91 -0
  295. package/skills/youtube-master/src/util.ts +93 -0
  296. package/skills/youtube-master/src/youtube.ts +70 -0
  297. package/skills/youtube-master/tsconfig.json +13 -0
  298. package/src/acp/server.ts +5 -2
  299. package/src/agent-mission/driver.ts +80 -10
  300. package/src/agent-mission/pty-decision.ts +71 -0
  301. package/src/agent-mission/review-loop.ts +30 -0
  302. package/src/autopilot/build/llm-conflict-merge.ts +16 -3
  303. package/src/autopilot/tool-registry.ts +2 -0
  304. package/src/boot/daemon-prompt-turn.ts +5 -0
  305. package/src/boot/daemon-runtime.ts +9 -3
  306. package/src/boot/daemon-tools/web-search.ts +3 -0
  307. package/src/card-followup/core.ts +109 -0
  308. package/src/chat/index.ts +1 -0
  309. package/src/cli/drive-input-jail.ts +101 -0
  310. package/src/cli/hooks-cli.ts +17 -3
  311. package/src/cli/make-cli.ts +43 -0
  312. package/src/cli/persona-cli.ts +90 -4
  313. package/src/cli/plugin-cli.ts +31 -22
  314. package/src/cli/pod-cli.ts +19 -9
  315. package/src/cli/pr-cli.ts +51 -15
  316. package/src/cli/pty-drive-cli.ts +41 -7
  317. package/src/cli/pty-drive-verdict.ts +40 -0
  318. package/src/cli/release-cli.ts +17 -4
  319. package/src/cli/setup-cli.ts +43 -1
  320. package/src/cli/skills-cli.ts +17 -1
  321. package/src/context-bus/claude-hooks-install.ts +10 -0
  322. package/src/context-bus/context-now-surfaces.ts +48 -0
  323. package/src/context-bus/context-now.ts +104 -0
  324. package/src/context-bus/coord-events.ts +2 -2
  325. package/src/context-bus/external-events.ts +52 -0
  326. package/src/context-bus/long-term-memory.ts +108 -0
  327. package/src/dashboard/index.ts +9 -4
  328. package/src/dashboard/slash-runtime/dashboard-handlers.ts +7 -1
  329. package/src/decisions/decision-cards.ts +10 -3
  330. package/src/decisions/decision-ledger.ts +13 -5
  331. package/src/discord/sprint21-runtime.ts +5 -1
  332. package/src/discord-self-message.ts +32 -0
  333. package/src/discord-slash-wire.ts +5 -1
  334. package/src/discord.ts +17 -1
  335. package/src/domains/core-tools.ts +3 -2
  336. package/src/domains/outbound-alert.ts +6 -1
  337. package/src/domains/telegram-kind-route.ts +51 -13
  338. package/src/exec-requests/default-outputs.ts +25 -0
  339. package/src/exec-requests/planner.ts +10 -2
  340. package/src/exec-requests/runner.ts +20 -6
  341. package/src/exec-requests/store.ts +3 -0
  342. package/src/execution-loop/launch-gate.ts +100 -0
  343. package/src/field/field-reel.ts +5 -1
  344. package/src/field-note/index.ts +201 -0
  345. package/src/field-note/transcribe.ts +248 -0
  346. package/src/graph-kinds/registry.ts +3 -3
  347. package/src/graph-runner/graph-cli.ts +61 -1
  348. package/src/graph-runner/graph-grow-llm.ts +37 -0
  349. package/src/graph-runner/graph-grow.ts +47 -0
  350. package/src/graph-runner/runner.ts +317 -25
  351. package/src/grok/agent-search.ts +8 -2
  352. package/src/guardian/secret-exposure-scan.ts +101 -0
  353. package/src/harness/harness-cli-command.ts +247 -7
  354. package/src/harness/harness-pod-dispatch.ts +43 -23
  355. package/src/harness/harness-seams.ts +38 -4
  356. package/src/harness/harness-stop.ts +23 -4
  357. package/src/harness/launch-registry.ts +91 -0
  358. package/src/harness/process-garbage.ts +5 -3
  359. package/src/hooks/dispatch.ts +82 -0
  360. package/src/hooks/expose.ts +254 -0
  361. package/src/hooks/github-shadow.ts +108 -0
  362. package/src/hooks/providers.ts +71 -5
  363. package/src/hooks/queue.ts +1 -1
  364. package/src/hooks/receiver.ts +21 -20
  365. package/src/index.ts +52 -14
  366. package/src/intake-plane/categorize.ts +18 -46
  367. package/src/intake-plane/discord-seat-work.ts +10 -0
  368. package/src/intake-plane/front-route-classifier.ts +33 -39
  369. package/src/intake-plane/persona-answer.ts +30 -0
  370. package/src/intake-plane/runtime-callables.ts +8 -1
  371. package/src/intake-plane/telegram-seat-work.ts +36 -2
  372. package/src/intake-plane/wish-folder.ts +67 -0
  373. package/src/intent-gate/gate.ts +30 -10
  374. package/src/live/detail-switch.ts +5 -0
  375. package/src/llm/difficulty.ts +57 -0
  376. package/src/llm/judge-layer.ts +101 -0
  377. package/src/llm/mission-router.ts +33 -19
  378. package/src/loops/contract/examples/cmo-seat.loop.yaml +51 -0
  379. package/src/loops/contract/examples/orchestrator.loop.yaml +57 -0
  380. package/src/loops/contract/examples/steward.loop.yaml +50 -0
  381. package/src/loops/contract/loop-agent.schema.json +138 -0
  382. package/src/loops/contract/validate.ts +64 -0
  383. package/src/loops/registry.ts +20 -6
  384. package/src/market/signed-index.ts +4 -0
  385. package/src/maturity/bot-command-maturity.ts +45 -0
  386. package/src/maturity/feature-maturity.ts +26 -4
  387. package/src/model-tier/index.ts +2 -2
  388. package/src/model-tier/tier-resolver.ts +20 -20
  389. package/src/model-tier/types.ts +6 -6
  390. package/src/mss/logging/log-store.ts +15 -0
  391. package/src/nexus/api/config-model-tier.ts +5 -4
  392. package/src/nexus/api/fabric-plans.ts +53 -0
  393. package/src/nexus/api/harness-api.ts +5 -1
  394. package/src/nexus/api/health.ts +2 -0
  395. package/src/nexus/api/http-server.ts +36 -0
  396. package/src/nexus/api/inside-events.ts +226 -0
  397. package/src/nexus/api/meta-api.ts +21 -8
  398. package/src/nexus/api/missions.ts +6 -1
  399. package/src/nexus/api/ops-seats.ts +33 -0
  400. package/src/nexus/api/outputs-route.ts +44 -0
  401. package/src/nexus/api/seat-requests.ts +44 -2
  402. package/src/nexus/api/sessions-store.ts +17 -38
  403. package/src/nexus/api/sub-seat-charters.ts +34 -0
  404. package/src/nexus/api/tabs-mutations.ts +3 -1
  405. package/src/nexus/api/trace.ts +18 -6
  406. package/src/nexus/api/workflow-run-event-bridge.ts +1 -0
  407. package/src/nexus/api/workflows.ts +80 -6
  408. package/src/nexus/index.ts +2 -0
  409. package/src/nexus/kinds/webterm.ts +2 -1
  410. package/src/nexus/webterm/pty.ts +6 -2
  411. package/src/onboarding/cli-auth-status.ts +80 -0
  412. package/src/onboarding/voice-ai.ts +3 -3
  413. package/src/org/seat-budget.ts +48 -0
  414. package/src/org/stall-escalation.ts +63 -0
  415. package/src/outputs/ledger.ts +120 -0
  416. package/src/persona/persona-todo.ts +79 -0
  417. package/src/persona/presets.ts +116 -2
  418. package/src/persona/team-set.ts +155 -0
  419. package/src/plugins/install/plugin-install.ts +14 -0
  420. package/src/plugins/maker/market-bundle.ts +109 -0
  421. package/src/plugins/maker/plugin-maker.ts +254 -11
  422. package/src/plugins/maker/wizard-generate.ts +103 -0
  423. package/src/pod-lease/admission.ts +150 -0
  424. package/src/pod-lease/dependency-state.ts +85 -0
  425. package/src/pod-lease/host-lease.ts +127 -0
  426. package/src/project/project-suggestion.ts +16 -0
  427. package/src/project/session-context.ts +24 -0
  428. package/src/release-loop/checklist.ts +2 -2
  429. package/src/release-loop/feature-store.ts +5 -3
  430. package/src/release-loop/next-md-merge.ts +58 -0
  431. package/src/release-loop/release-note.ts +59 -4
  432. package/src/seat-dispatch/seat-ask.ts +130 -0
  433. package/src/seat-dispatch/seat-questions.ts +100 -0
  434. package/src/seat-loop/seat-loop.ts +548 -29
  435. package/src/seat-loop/seat-report.ts +37 -2
  436. package/src/self-dev/dev-cli.ts +4 -1
  437. package/src/self-dev/dev-pipeline.ts +37 -1
  438. package/src/self-dev/draft-sweep.ts +38 -33
  439. package/src/self-dev/draft-triage-rules.ts +8 -5
  440. package/src/self-dev/fabric-plan-core.ts +297 -0
  441. package/src/self-dev/orchestrate.ts +36 -4
  442. package/src/self-dev/release-path-guard.ts +62 -0
  443. package/src/self-dev/shard-aggregate.ts +230 -0
  444. package/src/self-dev/shard-placement.ts +60 -0
  445. package/src/self-dev/shard-plan.ts +48 -0
  446. package/src/self-dev/shard-pod-run.ts +77 -0
  447. package/src/self-dev/shard-run.ts +105 -0
  448. package/src/self-implement/gate-baseline.ts +1 -1
  449. package/src/self-implement/gate-cli.ts +40 -1
  450. package/src/self-implement/gate-scope.ts +37 -7
  451. package/src/self-implement/graph-variant.ts +171 -0
  452. package/src/self-implement/harness-policy-gates.ts +1 -1
  453. package/src/self-implement/headless-elanous-driver.ts +22 -3
  454. package/src/self-implement/host-regate.ts +22 -1
  455. package/src/self-implement/importer-test-index.ts +2 -1
  456. package/src/self-implement/l8-merge-queue.ts +130 -0
  457. package/src/self-implement/orchestrator.ts +242 -9
  458. package/src/self-implement/route-consumer-test-index.ts +122 -0
  459. package/src/self-implement/seams.ts +72 -11
  460. package/src/self-implement/test-seams.ts +1 -0
  461. package/src/self-implement/uncommitted-work.ts +59 -0
  462. package/src/session/index.ts +36 -13
  463. package/src/skills/gift-install.ts +215 -0
  464. package/src/skills/tools/web-search.ts +4 -1
  465. package/src/steward/launch.ts +42 -26
  466. package/src/steward/steward-cards.ts +17 -9
  467. package/src/steward/triage.ts +46 -14
  468. package/src/task-cards/card-cli.ts +25 -1
  469. package/src/task-orchestrator/mission.ts +5 -0
  470. package/src/task-orchestrator/store.ts +12 -3
  471. package/src/task-orchestrator/surfaces/pod-command-job.ts +29 -7
  472. package/src/task-orchestrator/surfaces/pod-lease.ts +156 -19
  473. package/src/task-orchestrator/surfaces/pod-pool.ts +127 -26
  474. package/src/task-orchestrator/surfaces/self-implement-pod.ts +214 -40
  475. package/src/task-orchestrator/surfaces/self-implement.ts +15 -2
  476. package/src/task-orchestrator/types.ts +2 -0
  477. package/src/telegram-card-followup.ts +16 -96
  478. package/src/telegram-commands.ts +10 -1
  479. package/src/telegram-project-command.ts +71 -0
  480. package/src/telegram-report.ts +21 -17
  481. package/src/telegram.ts +29 -3
  482. package/src/tool-runtime/self-cognition-runtimes.ts +13 -3
  483. package/src/user-config.ts +102 -20
  484. package/src/version/packed-revision.json +1 -1
  485. package/src/web-search/grok.ts +7 -1
  486. package/src/web-search/index.ts +28 -1
  487. package/src/web-search/provider.ts +4 -0
  488. package/src/web-search/x-intent.ts +5 -0
  489. package/src/workflow-runtime/executor.ts +277 -47
  490. package/src/workflow-runtime/index.ts +1 -0
  491. package/src/workflow-runtime/nodes/subworkflow.ts +60 -0
  492. package/src/workflow-runtime/plugin-kind-node.ts +1 -1
  493. package/src/workflow-runtime/schema.ts +79 -4
  494. package/src/workflow-runtime/types.ts +35 -4
  495. package/apps/pwa/out/_next/static/2DsiDfDCks44g1cPUMhiW/_buildManifest.js +0 -1
  496. package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/2375-cb6debea56cce4fb.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/24-e6d28380efab7faf.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
  505. package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
  506. package/apps/pwa/out/_next/static/chunks/3615-740dd270764ffa24.js +0 -1
  507. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/3734-1015fe991eb9b7e7.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
  510. package/apps/pwa/out/_next/static/chunks/5012-a7fe6f3c8b6fe45a.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
  512. package/apps/pwa/out/_next/static/chunks/5783-ef40ebfc91d55230.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
  514. package/apps/pwa/out/_next/static/chunks/62-ff227c3edb8b248b.js +0 -1
  515. package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
  516. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
  517. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
  518. package/apps/pwa/out/_next/static/chunks/7633-1b3709b0854c9412.js +0 -1
  519. package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
  520. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
  521. package/apps/pwa/out/_next/static/chunks/7951-8b03dbb10b24cd06.js +0 -1
  522. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
  523. package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
  524. package/apps/pwa/out/_next/static/chunks/8195-6fde3d7a39859206.js +0 -10
  525. package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
  526. package/apps/pwa/out/_next/static/chunks/8894-b5fb36bab837cd29.js +0 -1
  527. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
  528. package/apps/pwa/out/_next/static/chunks/9454-84d0d5d77446bd92.js +0 -1
  529. package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
  530. package/apps/pwa/out/_next/static/chunks/988-5f92e7ab7ccea3dd.js +0 -1
  531. package/apps/pwa/out/_next/static/chunks/9989-5582582f06429840.js +0 -1
  532. package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
  533. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
  534. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d2253d7e58ef1890.js +0 -1
  535. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-5441d4a9ab35e518.js +0 -1
  536. package/apps/pwa/out/_next/static/chunks/app/board/page-d9b12d680922d980.js +0 -1
  537. package/apps/pwa/out/_next/static/chunks/app/bots/page-e268d6d145827a22.js +0 -1
  538. package/apps/pwa/out/_next/static/chunks/app/chat/page-f8116559c7424da7.js +0 -1
  539. package/apps/pwa/out/_next/static/chunks/app/consult/page-1085399fd0f32112.js +0 -1
  540. package/apps/pwa/out/_next/static/chunks/app/control/page-1df686a14f65895a.js +0 -1
  541. package/apps/pwa/out/_next/static/chunks/app/design-check/page-54e1f966ab60a4dc.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/app/editor/page-7ed95f296f5ee8e9.js +0 -1
  543. package/apps/pwa/out/_next/static/chunks/app/exec/page-11edac668d77e5c3.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/app/field/page-59e2b9a22cd5a6d5.js +0 -1
  545. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/app/intake/page-2976df21ad74ab48.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/app/layout-34d211a67f16d510.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/app/live/page-8ff652d5d950639b.js +0 -1
  549. package/apps/pwa/out/_next/static/chunks/app/market/page-b31610deda76337b.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6cb0e1bbefe5fb3d.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/app/morning/page-a286a152cf584576.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/app/not-found-d9237581e7003bcb.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/app/observatory/page-83274e761643fdd2.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-ceee6cfb1d4e6d68.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-9c9f8bb3e4a64091.js +0 -1
  556. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c17217603ea3426a.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/app/page-efb6177cbf1e8a51.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/app/reflection/page-d307523703bc4df3.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-462f4e4bab45ce7d.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/app/sessions/page-8990e96f33af3837.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e168fa1030af0548.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/app/settings/page-1233e05a4068bc9c.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-13a1840b1fac337a.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/app/setup/page-64a29fc80b11ab55.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/app/share/page-68e787339a16b3dc.js +0 -1
  567. package/apps/pwa/out/_next/static/chunks/app/showroom/page-4fddc90b23feb215.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/app/tasks/page-886ba8260dc79c79.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/app/term/page-0f26507a1722e40b.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/app/today/page-109d4ccd42259c9d.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/app/trace/page-f5c2a4e510726774.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/app/vault/page-b2e0039416f42522.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-26102fd7350f171e.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/app/workflows/page-708948bc8a802794.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/app/workspace/page-5e5611620f78fb0b.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-139ba5c367bbf60a.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/main-app-202b4e81f33895ea.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/webpack-e5bea36f183b25e6.js +0 -1
  583. package/apps/pwa/out/_next/static/css/43feca5fd387dcbd.css +0 -1
  584. /package/apps/pwa/out/_next/static/{2DsiDfDCks44g1cPUMhiW → Y4zKzL62sakG5momz3SoQ}/_ssgManifest.js +0 -0
@@ -0,0 +1,309 @@
1
+ #!/usr/bin/env bun
2
+ /** On node-b, from a clean main checkout: bun scripts/measure-gate-shards.ts --shards 4 --measurements docs/measurements/td1-whole-gate-mechanical-2026-10-01.tsv
3
+ * Runs the same tracked gate test files once together and once in GT2 memory-budgeted bundles.
4
+ * Missing TSV planning rows are measured individually on this machine before either comparison run;
5
+ * allow time for that calibration when the historical TSV is stale. No guessed RSS is used.
6
+ * Each mode uses the same machine, Bun executable and deterministic test environment. stdout is one JSON line;
7
+ * stderr contains diagnostics. RSS is the maximum sampled sum of process-group RSS (50 ms resolution).
8
+ * Peak RSS is sampling-dependent and excludes memory after a process leaves its group.
9
+ */
10
+ import { spawnSync } from 'node:child_process';
11
+ import { mkdirSync, mkdtempSync, readFileSync, rmSync, rmdirSync, writeFileSync } from 'node:fs';
12
+ import { tmpdir } from 'node:os';
13
+ import { join, resolve } from 'node:path';
14
+ import { planGateTestShards } from '../src/self-dev/shard-plan.js';
15
+ import { prepareIsolatedTestEnv, deriveCdpTestPatterns } from './test-deterministic.js';
16
+
17
+ export interface GateMeasurement {
18
+ mode: 'unsharded' | 'sharded';
19
+ durationMs: number;
20
+ peakRssMb: number;
21
+ // Confirmed OOMs only; an unknown termination is counted separately.
22
+ oomCount: number;
23
+ unknownTerminations: number;
24
+ oomCountStatus: 'confirmed' | 'partial';
25
+ verdict: 'passed' | 'failed' | 'unmeasured';
26
+ cases: Record<string, 'passed' | 'failed'>;
27
+ runs: number;
28
+ }
29
+
30
+ export interface GateComparison {
31
+ commit: string;
32
+ machine: string;
33
+ files: number;
34
+ shards: number;
35
+ unsharded: GateMeasurement;
36
+ sharded: GateMeasurement;
37
+ verdictAgrees: boolean;
38
+ }
39
+
40
+ export function readGateMeasurements(path: string): { rss: Map<string, number>; seconds: Map<string, number> } {
41
+ const lines = readFileSync(path, 'utf8').trimEnd().split(/\r?\n/);
42
+ const header = lines.shift()?.split('\t') ?? [];
43
+ const fileAt = header.indexOf('file'), rssAt = header.indexOf('rss_mb'), secondsAt = header.indexOf('secs');
44
+ if (fileAt < 0 || rssAt < 0 || secondsAt < 0) throw new Error('measurement TSV needs file, rss_mb and secs columns');
45
+ const rss = new Map<string, number>(), seconds = new Map<string, number>();
46
+ for (const line of lines) {
47
+ const cells = line.split('\t');
48
+ const file = cells[fileAt], mb = Number(cells[rssAt]), sec = Number(cells[secondsAt]);
49
+ if (!file || rss.has(file) || !cells[rssAt] || !cells[secondsAt] || !Number.isFinite(mb) || mb <= 0 || !Number.isFinite(sec) || sec < 0) {
50
+ throw new Error(`invalid measurement row: ${line}`);
51
+ }
52
+ rss.set(file, mb);
53
+ seconds.set(file, sec);
54
+ }
55
+ return { rss, seconds };
56
+ }
57
+
58
+ function git(repo: string, args: string[]): string {
59
+ const result = spawnSync('git', args, { cwd: repo, encoding: 'utf8' });
60
+ if (result.status !== 0) throw new Error(`git ${args[0]} failed: ${result.stderr.trim()}`);
61
+ return result.stdout.trim();
62
+ }
63
+
64
+ export function trackedGateFiles(repo: string): { commit: string; files: string[] } {
65
+ if (git(repo, ['rev-parse', '--show-toplevel']) !== resolve(repo)) throw new Error('run from the repository root');
66
+ if (git(repo, ['symbolic-ref', '--short', 'HEAD']) !== 'main') throw new Error('measurement requires the main checkout');
67
+ if (git(repo, ['status', '--porcelain'])) throw new Error('measurement requires a clean main checkout');
68
+ const commit = git(repo, ['rev-parse', 'HEAD']);
69
+ const files = git(repo, ['ls-files', '*.test.*']).split('\n').filter((file) => /\.test\.(?:tsx?|jsx?|mts|cts)$/.test(file))
70
+ .filter((file) => !file.split('/').some((part) => part.startsWith('.'))).sort();
71
+ if (!files.length) throw new Error('no gate tests found');
72
+ return { commit, files };
73
+ }
74
+
75
+ /** Fill missing in-memory planning rows from actual single-file runs, never an assumed RSS. */
76
+ export async function calibrateMissingGateMeasurements(options: {
77
+ repo: string; files: readonly string[]; rss: Map<string, number>; seconds: Map<string, number>;
78
+ }): Promise<void> {
79
+ const { repo, files, rss, seconds } = options;
80
+ const missing = files.filter((file) => !rss.has(file) || !seconds.has(file));
81
+ if (!missing.length) return;
82
+ console.error(`calibrating ${missing.length} gate files missing from the historical TSV on this machine`);
83
+ const root = mkdtempSync(join(tmpdir(), 'gate-shards-calibrate-'));
84
+ try {
85
+ for (const [index, file] of missing.entries()) {
86
+ const env = prepareIsolatedTestEnv(process.env, join(root, `env-${index}`));
87
+ const report = join(root, `case-${index}.xml`);
88
+ const started = performance.now();
89
+ const child = Bun.spawn(['bun', 'test', '--reporter=junit', `--reporter-outfile=${report}`, `./${file}`], {
90
+ cwd: repo, env, stdin: 'ignore', stdout: 'ignore', stderr: 'pipe', detached: true,
91
+ });
92
+ let peakKb = 0;
93
+ const sample = () => {
94
+ const ps = spawnSync('ps', ['-e', '-o', 'pgid=', '-o', 'rss='], { encoding: 'utf8' });
95
+ if (ps.status !== 0) throw new Error(`RSS calibration sampling failed: ${file}`);
96
+ const kb = ps.stdout.split('\n').reduce((sum, line) => {
97
+ const [group, value] = line.trim().split(/\s+/).map(Number);
98
+ return group === child.pid && Number.isFinite(value) ? sum + value! : sum;
99
+ }, 0);
100
+ peakKb = Math.max(peakKb, kb);
101
+ };
102
+ const interval = setInterval(sample, 50);
103
+ let stderr: string, code: number;
104
+ try {
105
+ sample();
106
+ stderr = await new Response(child.stderr).text();
107
+ code = await child.exited;
108
+ sample();
109
+ } finally { clearInterval(interval); }
110
+ let cases: Record<string, 'passed' | 'failed'>;
111
+ try { cases = readCases(readFileSync(report, 'utf8'), [file]); }
112
+ catch (error) { throw new Error(`calibration JUnit incomplete for ${file}: ${error}`); }
113
+ const failed = Object.values(cases).includes('failed');
114
+ if (child.signalCode || peakKb === 0 || (code !== 0 && code !== 1) || (code === 0 && failed) || (code === 1 && !failed)) {
115
+ throw new Error(`calibration incomplete for ${file}: code=${code} signal=${child.signalCode ?? 'none'} ${stderr.slice(-500)}`);
116
+ }
117
+ rss.set(file, Math.ceil(peakKb / 1024));
118
+ seconds.set(file, (performance.now() - started) / 1000);
119
+ }
120
+ } finally { rmSync(root, { recursive: true, force: true }); }
121
+ }
122
+
123
+ // Bun JUnit writes one testsuite per file. Require every expected file and every declared case,
124
+ // including failures/errors; missing or truncated output is unmeasured, never a passing verdict.
125
+ export function readCases(xml: string, files: readonly string[]): Record<string, 'passed' | 'failed'> {
126
+ const expected = new Set(files), seen = new Set<string>();
127
+ const cases: Record<string, 'passed' | 'failed'> = Object.create(null);
128
+ const suites = [...xml.matchAll(/<testsuite\b([^>]*)>([\s\S]*?)<\/testsuite>/g)];
129
+ if (!/<testsuites\b/.test(xml) || !/<\/testsuites>\s*$/.test(xml)) throw new Error('incomplete JUnit document');
130
+ const attr = (text: string, key: string) => new RegExp(`\\b${key}="([^"]*)"`).exec(text)?.[1];
131
+ for (const [, attrs, body] of suites) {
132
+ const file = attr(attrs!, 'file') ?? attr(attrs!, 'name');
133
+ if (!file || !expected.has(file) || seen.has(file)) throw new Error(`unexpected or repeated JUnit suite: ${file}`);
134
+ seen.add(file);
135
+ const tests = Number(attr(attrs!, 'tests'));
136
+ const entries = [...body!.matchAll(/<testcase\b([^>]*?)(?:\/>|>([\s\S]*?)<\/testcase>)/g)];
137
+ if (!Number.isSafeInteger(tests) || tests < 1 || entries.length !== tests) throw new Error(`incomplete JUnit suite: ${file}`);
138
+ for (const [, properties, contents] of entries) {
139
+ const name = attr(properties!, 'name'), caseFile: string = attr(properties!, 'file') ?? file;
140
+ const className = attr(properties!, 'classname');
141
+ if (!name || caseFile !== file || /<skipped\b/.test(contents ?? '')) throw new Error(`incomplete JUnit case: ${file}`);
142
+ const id = `${file} > ${className && className !== file ? `${className} > ` : ''}${name}`;
143
+ if (id in cases) throw new Error(`duplicate JUnit case: ${id}`);
144
+ cases[id] = /<(?:failure|error)\b/.test(contents ?? '') ? 'failed' : 'passed';
145
+ }
146
+ }
147
+ if (seen.size !== expected.size) throw new Error(`missing JUnit suites: ${[...expected].filter((file) => !seen.has(file)).join(', ')}`);
148
+ return cases;
149
+ }
150
+
151
+ // A private cgroup records kernel-attributed OOM kills for exactly one test bundle.
152
+ // A host without delegated cgroup v2 write access cannot classify abnormal exits as OOM.
153
+ function oomKills(path: string): number {
154
+ const line = readFileSync(join(path, 'memory.events'), 'utf8').split('\n').find((entry) => entry.startsWith('oom_kill '));
155
+ const value = Number(line?.slice('oom_kill '.length));
156
+ if (!line || !Number.isSafeInteger(value) || value < 0) throw new Error(`invalid cgroup oom_kill counter: ${path}`);
157
+ return value;
158
+ }
159
+
160
+ interface Run { code: number | null; signal: string | null; xml: string; stderr: string; oomKills: number | null }
161
+
162
+ export async function measureGateComparison(options: {
163
+ repo: string; files: string[]; commit: string; shards: number; rss: ReadonlyMap<string, number>;
164
+ seconds: ReadonlyMap<string, number>; budgetGiB?: number;
165
+ /** Fixture-only cgroup filesystem; production always uses the calling process's cgroup v2. */
166
+ cgroupRoot?: string;
167
+ /** Test-only creation hook for a synthetic cgroup v2 fixture. */
168
+ onCgroupCreated?: (path: string) => void;
169
+ }): Promise<GateComparison> {
170
+ const { repo, files, commit, shards, rss, seconds } = options;
171
+ if (!Number.isSafeInteger(shards) || shards < 1 || files.length === 0 || new Set(files).size !== files.length) throw new Error('invalid shard count or files');
172
+ const bundles = planGateTestShards(files, rss, seconds, options.budgetGiB ?? 8, shards);
173
+ const root = mkdtempSync(join(tmpdir(), 'gate-shards-measure-'));
174
+ const cgroup = options.cgroupRoot ?? (() => {
175
+ try {
176
+ const entry = readFileSync('/proc/self/cgroup', 'utf8').split('\n').find((line) => line.startsWith('0::'));
177
+ if (!entry) return null;
178
+ const path = resolve('/sys/fs/cgroup', `.${entry.slice(3)}`);
179
+ return path.startsWith('/sys/fs/cgroup/') || path === '/sys/fs/cgroup' ? path : null;
180
+ } catch { return null; } // macOS and hosts without cgroup v2 report partial OOM counts.
181
+ })();
182
+ const measure = async (mode: GateMeasurement['mode'], groups: string[][]): Promise<GateMeasurement> => {
183
+ const started = performance.now();
184
+ const testRoot = join(root, mode);
185
+ mkdirSync(testRoot);
186
+ const env = prepareIsolatedTestEnv(process.env, testRoot);
187
+ const reports = groups.map((_, index) => join(root, `${mode}-${index}.xml`));
188
+ // Record total RSS of the concurrently running test process groups, not the wrapper's RSS.
189
+ const running = new Set<number>();
190
+ let peakKb = 0;
191
+ const sample = () => {
192
+ if (!running.size) return;
193
+ const ps = spawnSync('ps', ['-e', '-o', 'pgid=', '-o', 'rss='], { encoding: 'utf8' });
194
+ if (ps.status !== 0) throw new Error('RSS sampling failed');
195
+ const kb = ps.stdout.split('\n').reduce((sum, line) => {
196
+ const [group, rssKb] = line.trim().split(/\s+/).map(Number);
197
+ return running.has(group!) && Number.isFinite(rssKb) ? sum + rssKb! : sum;
198
+ }, 0);
199
+ peakKb = Math.max(peakKb, kb);
200
+ };
201
+ const runs: Run[] = [];
202
+ let interval: ReturnType<typeof setInterval> | undefined;
203
+ try {
204
+ await Promise.all(groups.map(async (group, index) => {
205
+ // Bundles share the mode's isolated environment but have separate Bun process groups.
206
+ const cmd = ['bun', 'test', '--reporter=junit', `--reporter-outfile=${reports[index]}`, ...group.map((file) => `./${file}`)];
207
+ let groupPath: string | null = null;
208
+ if (cgroup) {
209
+ try {
210
+ groupPath = join(cgroup, `gate-shards-${process.pid}-${index}-${mode}-${root.split('/').at(-1)}`);
211
+ mkdirSync(groupPath);
212
+ options.onCgroupCreated?.(groupPath);
213
+ oomKills(groupPath);
214
+ } catch {
215
+ if (groupPath) { try { rmdirSync(groupPath); } catch { /* No delegated cleanup access. */ } }
216
+ groupPath = null;
217
+ }
218
+ }
219
+ // The shell blocks before exec: assign its PID to the private cgroup first.
220
+ // exec retains the PID; descendants inherit the same group and memory.events.
221
+ const child = Bun.spawn(groupPath ? ['bash', '-c', 'read -r _ || exit 125; exec "$@"', 'gate-shards', ...cmd] : cmd, {
222
+ cwd: repo, env: options.cgroupRoot && groupPath ? { ...env, ELANOUS_GATE_MEASURE_CGROUP_DIR: groupPath } : env,
223
+ stdin: groupPath ? 'pipe' : 'ignore', stdout: 'ignore', stderr: 'pipe', detached: true,
224
+ });
225
+ let attached = false;
226
+ if (groupPath) {
227
+ try {
228
+ writeFileSync(join(groupPath, 'cgroup.procs'), String(child.pid));
229
+ attached = true;
230
+ } catch { /* Without attachment a cgroup OOM counter is not attributable. */ }
231
+ if (attached) child.stdin!.write('\n');
232
+ else child.kill(); // Fail closed rather than run an untracked wrapper.
233
+ child.stdin!.end();
234
+ }
235
+ running.add(child.pid);
236
+ if (!interval) interval = setInterval(sample, 50);
237
+ sample();
238
+ const stderr = await new Response(child.stderr).text();
239
+ const code = await child.exited;
240
+ sample();
241
+ running.delete(child.pid);
242
+ let count: number | null = null;
243
+ if (groupPath) {
244
+ try {
245
+ if (attached) count = oomKills(groupPath);
246
+ } catch { /* Lost attribution: do not report a definitive zero. */ }
247
+ try { rmdirSync(groupPath); } catch { /* Another live process may still occupy it. */ }
248
+ }
249
+ let xml = '';
250
+ try { xml = readFileSync(reports[index]!, 'utf8'); } catch { /* An absent JUnit is unmeasured. */ }
251
+ runs[index] = { code, signal: child.signalCode, xml, stderr, oomKills: count };
252
+ }));
253
+ } finally { if (interval) clearInterval(interval); }
254
+ const cases: Record<string, 'passed' | 'failed'> = Object.create(null);
255
+ // Only kernel OOM kills attributed to the bundle's private cgroup count.
256
+ // Neither test stderr nor SIGKILL/137 establishes an OOM cause.
257
+ let oomCount = 0;
258
+ let unknownTerminations = 0, complete = true;
259
+ for (const [index, run] of runs.entries()) {
260
+ if (run.oomKills !== null) oomCount += run.oomKills;
261
+ if ((run.signal || (run.code !== 0 && run.code !== 1)) && !run.oomKills) unknownTerminations++;
262
+ if (run.oomKills) complete = false;
263
+ try {
264
+ const batch = readCases(run.xml, groups[index]!);
265
+ const hasFailure = Object.values(batch).includes('failed');
266
+ if (![0, 1].includes(run.code ?? -1) || run.signal || (run.code === 0 && hasFailure) || (run.code === 1 && !hasFailure)) complete = false;
267
+ Object.assign(cases, batch);
268
+ } catch { complete = false; }
269
+ if (run.signal || (run.code !== 0 && run.code !== 1)) console.error(`${mode} bundle ${index}: signal=${run.signal ?? 'none'} code=${run.code ?? 'null'} ${run.stderr.slice(-500)}`);
270
+ }
271
+ if (peakKb === 0) complete = false;
272
+ return { mode, durationMs: Math.round(performance.now() - started), peakRssMb: Math.ceil(peakKb / 1024), oomCount, unknownTerminations, oomCountStatus: runs.some((run) => run.oomKills === null) ? 'partial' : 'confirmed',
273
+ verdict: !complete ? 'unmeasured' : Object.values(cases).includes('failed') ? 'failed' : 'passed', cases, runs: groups.length };
274
+ };
275
+ try {
276
+ const unsharded = await measure('unsharded', [files]);
277
+ const sharded = await measure('sharded', bundles.map((bundle) => bundle.files));
278
+ const verdictAgrees = unsharded.verdict !== 'unmeasured' && sharded.verdict !== 'unmeasured'
279
+ && unsharded.verdict === sharded.verdict
280
+ && JSON.stringify(Object.entries(unsharded.cases).sort()) === JSON.stringify(Object.entries(sharded.cases).sort());
281
+ return { commit, machine: (await import('node:os')).hostname(), files: files.length, shards: bundles.length, unsharded, sharded, verdictAgrees };
282
+ } finally { rmSync(root, { recursive: true, force: true }); }
283
+ }
284
+
285
+ export async function main(argv: string[] = process.argv.slice(2), repo = process.cwd()): Promise<number> {
286
+ const take = (flag: string): string | undefined => { const index = argv.indexOf(flag); return index < 0 ? undefined : argv[index + 1]; };
287
+ if (argv.includes('--help')) {
288
+ console.error('bun scripts/measure-gate-shards.ts --shards N --measurements <TSV> [--budget-gib 8] (clean main checkout on node-b)');
289
+ return 0;
290
+ }
291
+ const count = Number(take('--shards'));
292
+ const source = take('--measurements');
293
+ if (!source || !Number.isSafeInteger(count) || count < 1) throw new Error('--shards N and --measurements TSV are required');
294
+ const budgetGiB = take('--budget-gib') === undefined ? 8 : Number(take('--budget-gib'));
295
+ const { commit, files: tracked } = trackedGateFiles(repo);
296
+ const cdp = new Set(deriveCdpTestPatterns({ cwd: repo }));
297
+ // Match the deterministic whole-gate CDP exclusion; keep every other tracked test, including install.
298
+ const files = tracked.filter((file) => !cdp.has(file));
299
+ const { rss, seconds } = readGateMeasurements(resolve(repo, source));
300
+ await calibrateMissingGateMeasurements({ repo, files, rss, seconds });
301
+ const result = await measureGateComparison({ repo, files, commit, shards: count, rss, seconds, budgetGiB });
302
+ process.stdout.write(JSON.stringify(result) + '\n');
303
+ return result.verdictAgrees ? 0 : 1;
304
+ }
305
+
306
+ if (import.meta.main) {
307
+ try { process.exitCode = await main(); }
308
+ catch (error) { console.error(error); process.exitCode = 2; }
309
+ }
@@ -10,9 +10,14 @@ import { judgeMissionRequests } from '../src/mission-loop/judge.js';
10
10
  import { unknownCronFlag } from '../src/domains/cron-flag-contract.js';
11
11
  import { ensureCronNodePath } from '../src/domains/cron-path.js';
12
12
  import { registerStandaloneLogSink } from '../src/domains/standalone-log-sink.js';
13
+ import { debug } from '../src/debug/log.js';
13
14
 
14
15
  export type CreateHarnessGoal = (goal: HarnessGoal) => Promise<void> | void;
15
16
 
17
+ function logJudge(event: string, data: Record<string, unknown>): void {
18
+ try { debug.log('mission-fabric.judge', event, data); } catch { /* observation must not change a judgment */ }
19
+ }
20
+
16
21
  /**
17
22
  * ⛔ 실패해도 «여기까지 본 것」을 잃지 않는다.
18
23
  * 📏 회귀(리뷰 2R must-fix): 카탈로그가 없을 때 옛 CLI 는 「📍 권위 트리 / 📍 요청 카탈로그」 두 줄을
@@ -73,8 +78,23 @@ export async function runMissionRequestJudge(argv: readonly string[], dependenci
73
78
  };
74
79
  const authorityRoot = resolve(flag('--root') ?? join(dirname(new URL(import.meta.url).pathname), '..'));
75
80
  const result = judgeMissionRequests(authorityRoot);
81
+ logJudge('requests-judged', {
82
+ authorityRoot: result.authorityRoot, requestCatalog: result.requestCatalog,
83
+ catalogStatus: result.catalogStatus, requestsScanned: result.requestsScanned,
84
+ candidateCount: result.judgments.filter(judgment => judgment.status === 'blueprint-candidate').length,
85
+ invalidCount: result.invalidCount,
86
+ });
87
+ for (const judgment of result.judgments) {
88
+ logJudge('request-decision', {
89
+ file: judgment.file, status: judgment.status,
90
+ ...(judgment.status === 'invalid-request' ? { reasons: judgment.reasons } : {}),
91
+ ...(judgment.status === 'missing-capability' ? { missingCapabilities: judgment.missingCapabilities.map(capability => capability.id) } : {}),
92
+ ...(judgment.status === 'missing-blueprint' ? { reason: 'missing-blueprint' } : {}),
93
+ });
94
+ }
76
95
  const lines = [`📍 권위 트리: ${result.authorityRoot}`, `📍 요청 카탈로그: ${result.requestCatalog}`];
77
96
  if (result.catalogStatus === 'missing') {
97
+ logJudge('skipped', { reason: 'request-catalog-missing', requestCatalog: result.requestCatalog });
78
98
  const message = '⛔ 요청 카탈로그가 «없다» — 「요청 0건」이 아니라 ***디렉토리 부재***다. root 를 확인하라.';
79
99
  throw new MissionRequestJudgeError(message, [...lines, message]);
80
100
  }
@@ -91,13 +111,23 @@ export async function runMissionRequestJudge(argv: readonly string[], dependenci
91
111
  else lines.push(`✅ ${judgment.file} ⇒ 스키마 통과 · 능력 ${judgment.capabilityCount}개`, ' 🟢 블루프린트 «후보» 있음 → 검증 ⑴~⑷');
92
112
  }
93
113
  lines.push(`📏 훑은 ${result.requestsScanned} · invalid ${result.invalidCount}`);
94
- if (!argv.includes('--tick')) return lines;
114
+ if (!argv.includes('--tick')) {
115
+ logJudge('skipped', { reason: 'tick-not-requested' });
116
+ return lines;
117
+ }
95
118
 
96
119
  const cycle = dependencies.runCompositeCycle
97
120
  ? await dependencies.runCompositeCycle(authorityRoot)
98
121
  : await runCompositeCycle(authorityRoot, { createHarnessGoal: dependencies.createHarnessGoal ?? createHarnessGoal });
99
122
  lines.push(`🔁 복합 회차 ${cycle.actions.length}건`);
123
+ if (cycle.actions.length === 0) logJudge('skipped', { reason: 'no-cycle-actions' });
100
124
  for (const action of cycle.actions) {
125
+ logJudge('cycle-decision', {
126
+ requestId: action.requestId, action: action.action,
127
+ ...(action.action === 'escalated' || action.action === 'ignored' ? { reason: action.reason } : {}),
128
+ ...(action.action === 'goal-created' ? { paths: action.goal.paths } : {}),
129
+ ...(action.action === 'executed' ? { fileDelivery: action.fileDelivery.status } : {}),
130
+ });
101
131
  if (action.action === 'executed') {
102
132
  const delivery = action.fileDelivery.status === 'persisted'
103
133
  ? `file-delivery persisted · ${action.fileDelivery.path} · ${action.fileDelivery.bytes} bytes`
@@ -31,7 +31,7 @@ const report = await composeMorningReport(new Date(), { narrate, heatmapImageUrl
31
31
  if (dry) {
32
32
  console.log(report);
33
33
  } else {
34
- const sent = await sendTelegramReport(cfg, report, { markdown: true });
34
+ const sent = await sendTelegramReport(cfg, report, { markdown: true, kind: 'report' });
35
35
  console.error(sent
36
36
  ? '[morning-report] sent to report channel ✓'
37
37
  : '[morning-report] no report channel configured — skipped');
@@ -39,7 +39,7 @@ if (dry) {
39
39
  // 히트맵 이미지 첨부(위에서 캡처한 URL 재사용). fail-soft.
40
40
  if (heatmapImageUrl) {
41
41
  try {
42
- const ok = await sendReportPhoto(cfg, heatmapImageUrl, { caption: '🗺️ S&P 500 히트맵 (finviz)' });
42
+ const ok = await sendReportPhoto(cfg, heatmapImageUrl, { caption: '🗺️ S&P 500 히트맵 (finviz)', kind: 'report' });
43
43
  console.error(ok ? '[morning-report] heatmap image sent ✓' : '[morning-report] heatmap: no report channel');
44
44
  } catch (e) { console.error(`[morning-report] heatmap image error: ${e instanceof Error ? e.message : String(e)}`); }
45
45
  }
@@ -54,7 +54,7 @@ const report = parts.join('\n');
54
54
  if (dry) {
55
55
  console.log(report);
56
56
  } else {
57
- const sent = await sendTelegramReport(cfg, report, { markdown: true });
57
+ const sent = await sendTelegramReport(cfg, report, { markdown: true, kind: 'report' });
58
58
  console.error(sent
59
59
  ? '[opportunity-scan] sent to report channel ✓'
60
60
  : '[opportunity-scan] no report channel — printing:\n' + report);
@@ -4,15 +4,26 @@ import { dirname, isAbsolute, join, resolve } from 'node:path';
4
4
  import { debug } from '../../src/debug/log.js';
5
5
  import { effectiveInstanceRoot } from '../../src/instance/resolve.js';
6
6
  import { emitNodeResult, readGraphContext, type GraphContext } from './node-verdict.js';
7
+ import { regressWarning, validatedTuiRegress } from './tui-node-checks.js';
7
8
 
8
9
  export type MetricVerdict = 'pass' | 'fail' | 'unmeasured';
9
- export interface ApprovalMetric { name: string; value: string; verdict: MetricVerdict }
10
+ export interface ApprovalMetric { name: string; value: string; verdict: MetricVerdict; reason?: string }
10
11
 
11
12
  const outcomeMetric = (name: string, node: Record<string, unknown> | undefined): ApprovalMetric => ({
12
13
  name, value: node?.outcome === undefined ? 'missing' : String(node.outcome),
13
14
  verdict: node?.outcome === undefined ? 'unmeasured' : node.outcome === 'ok' ? 'pass' : 'fail',
14
15
  });
15
16
 
17
+ /** Rows shown in auto-approval.md but never counted toward the 8 blocking metrics. */
18
+ const WARNING_ONLY = new Set(['tui-regress', 'mac-smoke']);
19
+
20
+ /** `ok` · `warn` (a failure) · `unmeasured` (error or no output) — the macOS smoke never blocks publishing. */
21
+ function macSmokeLevel(node: Record<string, unknown> | undefined): 'ok' | 'warn' | 'unmeasured' {
22
+ if (node?.outcome === 'ok') return 'ok';
23
+ if (node?.outcome === 'fail') return 'warn';
24
+ return 'unmeasured';
25
+ }
26
+
16
27
  export function runAutoApprove(context: GraphContext = readGraphContext(), deps: { instanceRoot?: string; repo?: string; bunVersion?: string } = {}) {
17
28
  const { version } = context.input;
18
29
  const root = deps.instanceRoot ?? effectiveInstanceRoot();
@@ -30,6 +41,8 @@ export function runAutoApprove(context: GraphContext = readGraphContext(), deps:
30
41
  catch { distCount = undefined; }
31
42
  }
32
43
  const gateErrorAbsent = gate && !Object.hasOwn(gate, 'error');
44
+ const regressStatus = regressWarning(validatedTuiRegress(context.outputs.tui?.regress));
45
+ const macSmoke = context.outputs['mac-smoke'];
33
46
  const metrics: ApprovalMetric[] = [
34
47
  { name: 'gate regressions and measurement', value: JSON.stringify({ outcome: gate?.outcome ?? null, introduced: gate?.introduced ?? null, error: gate?.error ?? null }),
35
48
  verdict: !gate || gate.outcome === undefined || !Array.isArray(gate.introduced) ? 'unmeasured'
@@ -47,16 +60,20 @@ export function runAutoApprove(context: GraphContext = readGraphContext(), deps:
47
60
  { name: 'installation files', value: `dist files=${distCount ?? 'missing'}; prepare.out=${String(out ?? 'missing')}`,
48
61
  verdict: prepare?.outcome !== 'ok' ? prepare?.outcome === undefined ? 'unmeasured' : 'fail'
49
62
  : distCount === undefined ? 'unmeasured' : distCount === 5 ? 'pass' : 'fail' },
63
+ // Warning-only and last, so the numbered rows 1–8 keep their places in auto-approval.md (V1g follow-up).
64
+ { name: 'tui-regress', value: regressStatus.level, reason: regressStatus.line, verdict: 'pass' },
65
+ // MAC1: the macOS smoke is warning-only too (ship with known issues) — shown, never counted.
66
+ { name: 'mac-smoke', value: macSmokeLevel(macSmoke), reason: typeof macSmoke?.summary === 'string' ? macSmoke.summary : undefined, verdict: 'pass' },
50
67
  ];
51
- const outcome = metrics.every((metric) => metric.verdict === 'pass') ? 'ok' as const : 'fail' as const;
68
+ const outcome = metrics.filter((metric) => !WARNING_ONLY.has(metric.name)).every((metric) => metric.verdict === 'pass') ? 'ok' as const : 'fail' as const;
52
69
  const report = join(root, 'release', version, 'auto-approval.md');
53
70
  const lines = [`# v${version} auto-approval`, '', `Outcome: ${outcome}`, '', '| # | Metric | Value | Verdict |', '|---|---|---|---|',
54
- ...metrics.map((metric, i) => `| ${i + 1} | ${metric.name} | ${metric.value.replaceAll('|', '\\|').replace(/[\r\n]/g, ' ')} | ${metric.verdict} |`),
71
+ ...metrics.map((metric, i) => `| ${i + 1} | ${metric.name} | ${[metric.value, metric.reason].filter(Boolean).join(' — ').replaceAll('|', '\\|').replace(/[\r\n]/g, ' ')} | ${metric.verdict} |`),
55
72
  '', '## 되돌리는 법', `gh release edit v${version} --draft`, `git tag -d v${version} && git push origin :refs/tags/v${version}`, '해당 docs PR revert', ''];
56
73
  mkdirSync(dirname(report), { recursive: true });
57
74
  writeFileSync(report, lines.join('\n'));
58
75
  debug.log('release-loop.approve', 'auto-decision', { version, outcome, metrics });
59
- return { outcome, verdict: outcome === 'ok' ? 'pass' as const : 'fail' as const, summary: `auto approval ${outcome}: ${metrics.filter((metric) => metric.verdict === 'pass').length}/8`,
76
+ return { outcome, verdict: outcome === 'ok' ? 'pass' as const : 'fail' as const, summary: `auto approval ${outcome}: ${metrics.filter((metric) => !WARNING_ONLY.has(metric.name) && metric.verdict === 'pass').length}/8`,
60
77
  ...(outcome === 'ok' ? { decidedBy: 'release-loop metrics' as const } : {}), metrics, report };
61
78
  }
62
79
 
@@ -0,0 +1,84 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { mkdtempSync, rmSync } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { join, resolve } from 'node:path';
5
+ import { debug } from '../../src/debug/log.js';
6
+
7
+ export interface CutBranchOptions { version: string; base: string; pick: string[]; dryRun?: boolean; repoRoot?: string; log?: (line: string) => void }
8
+
9
+ // git-spawn-allow: release branch operations happen only in an isolated temporary worktree; main is never checked out or changed.
10
+ function git(repo: string, args: string[]): string {
11
+ const result = spawnSync('git', args, { cwd: repo, encoding: 'utf8' });
12
+ if (result.status !== 0 || result.error) throw new Error(`git ${args[0]} failed: ${(result.stderr || result.error || result.stdout || 'no output').toString().trim()}`);
13
+ return result.stdout.trim();
14
+ }
15
+
16
+ function ancestor(repo: string, sha: string, ref: string): boolean {
17
+ const result = spawnSync('git', ['merge-base', '--is-ancestor', sha, ref], { cwd: repo });
18
+ if (result.status !== 0 && result.status !== 1) throw new Error(`git merge-base failed: ${result.error || result.stderr || 'unknown'}`);
19
+ return result.status === 0;
20
+ }
21
+
22
+ export function cutReleaseBranch(opts: CutBranchOptions): { branch: string; commit: string | null; dryRun: boolean } {
23
+ const repo = resolve(opts.repoRoot ?? process.cwd());
24
+ const branch = `release/${opts.version}`;
25
+ const log = opts.log ?? console.log;
26
+ const report = (event: 'created' | 'refused' | 'conflict', reason?: string, commit?: string) =>
27
+ debug.log('release-loop.cut-branch', event, { version: opts.version, branch, ...(commit ? { commit } : {}), ...(reason ? { reason } : {}) });
28
+ try {
29
+ if (!/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.test(opts.version)) throw new Error(`invalid release version: ${opts.version}`);
30
+ if (!opts.pick.length) throw new Error('at least one --pick commit is required');
31
+ for (const sha of [opts.base, ...opts.pick]) if (!/^[0-9a-f]{7,40}$/i.test(sha)) throw new Error(`invalid commit SHA: ${sha}`);
32
+ git(repo, ['fetch', 'origin', 'main']);
33
+ const base = git(repo, ['rev-parse', '--verify', `${opts.base}^{commit}`]);
34
+ if (!ancestor(repo, base, 'origin/main')) throw new Error(`base ${base} is not an ancestor of origin/main`);
35
+ const picks = opts.pick.map((sha) => git(repo, ['rev-parse', '--verify', `${sha}^{commit}`]));
36
+ for (const sha of picks) if (!ancestor(repo, sha, 'origin/main')) throw new Error(`pick ${sha} is not an ancestor of origin/main`);
37
+ for (const sha of picks) if (ancestor(repo, sha, base)) throw new Error(`pick ${sha} is already in base ${base}`);
38
+ if (new Set(picks).size !== picks.length) throw new Error('duplicate --pick commit');
39
+ for (const sha of picks) if (git(repo, ['rev-list', '--parents', '-n', '1', sha]).split(' ').length !== 2) throw new Error(`pick ${sha} must have exactly one parent`);
40
+ if (git(repo, ['ls-remote', '--heads', 'origin', `refs/heads/${branch}`])) throw new Error(`${branch} already exists on origin`);
41
+ if (spawnSync('git', ['show-ref', '--verify', '--quiet', `refs/heads/${branch}`], { cwd: repo }).status === 0) throw new Error(`${branch} already exists locally`);
42
+ const pkg = JSON.parse(git(repo, ['show', `${base}:package.json`])) as { version?: unknown };
43
+ if (pkg.version !== opts.version) throw new Error(`base package.json version ${pkg.version} is not ${opts.version}`);
44
+ log(`${branch}: ${base} + ${picks.join(' + ')}${opts.dryRun ? ' (dry-run)' : ''}`);
45
+ if (opts.dryRun) return { branch, commit: null, dryRun: true };
46
+ const temp = mkdtempSync(join(tmpdir(), 'release-cut-branch-'));
47
+ const tree = join(temp, 'tree');
48
+ let added = false;
49
+ let commit: string | undefined;
50
+ try {
51
+ git(repo, ['worktree', 'add', '--detach', tree, base]);
52
+ added = true;
53
+ for (const sha of picks) {
54
+ try { git(tree, ['cherry-pick', sha]); }
55
+ catch (error) {
56
+ const files = git(tree, ['diff', '--name-only', '--diff-filter=U']);
57
+ if (!files) throw error;
58
+ report('conflict', `cherry-pick ${sha}`);
59
+ throw new Error(`cherry-pick ${sha} 충돌 — 손으로 풀 곳: ${files.split('\n').join(', ')} (release/${opts.version} 가지는 만들지 않음)`);
60
+ }
61
+ }
62
+ commit = git(tree, ['rev-parse', 'HEAD']);
63
+ const finalPackage = JSON.parse(git(tree, ['show', 'HEAD:package.json'])) as { version?: unknown };
64
+ if (finalPackage.version !== opts.version) throw new Error(`picked branch package.json version ${finalPackage.version} is not ${opts.version}`);
65
+ } finally {
66
+ try {
67
+ if (added) git(repo, ['worktree', 'remove', '--force', tree]);
68
+ } finally { rmSync(temp, { recursive: true, force: true }); }
69
+ }
70
+ if (!commit) throw new Error('cherry-pick produced no commit');
71
+ const push = git(repo, ['push', '--porcelain', `--force-with-lease=refs/heads/${branch}:`, 'origin', `${commit}:refs/heads/${branch}`]);
72
+ const newBranch = push.split('\n').some((line) => {
73
+ const [flag, ref, summary] = line.split('\t');
74
+ return flag === '*' && ref?.endsWith(`:refs/heads/${branch}`) && summary === '[new branch]';
75
+ });
76
+ if (!newBranch) throw new Error(`${branch} already exists on origin (push did not create a new branch)`);
77
+ report('created', undefined, commit);
78
+ log(`${branch} → ${commit}`);
79
+ return { branch, commit, dryRun: false };
80
+ } catch (error) {
81
+ if (!(error instanceof Error && error.message.includes('충돌'))) report('refused', error instanceof Error ? error.message : String(error));
82
+ throw error;
83
+ }
84
+ }
@@ -125,13 +125,14 @@ export const POD_MEMORY_SOURCE = 'docs/measurements/td1-whole-gate-mechanical-20
125
125
  export const POD_SHARD_FILE_CAP = 27;
126
126
 
127
127
  export const POD_SWEEP_INTEGRATION_ONLY: readonly string[] = ['scripts/install.test.ts'];
128
- /** Whole-repository audits run alone in the nightly test-diet graph, not in release-gate shards. */
128
+ /** Nightly-only whole-repository cases; gate runs the fixture cases in these same files. */
129
129
  export const GATE_NIGHTLY_AUDITS: readonly string[] = [
130
- 'test/f12-sweep.test.ts', // Sweeps the whole repository; 7.8 GB even after TD1a.
131
- 'scripts/unwired-exports.test.ts', // Compiles and scans all exports; 9.9 GB.
132
- 'test/guardian/dispatch-surface-contract.test.ts', // Compiles repository-wide dispatch contracts; 8.3 GB.
133
- 'test/pwa-build-typecheck.test.ts', // Builds and typechecks the whole PWA; 4.2 GB.
134
- 'test/user-config-mcp.test.ts', // 2.1 GB peak; keep the gate below the 2 GB audit budget.
130
+ // Replaces F12's real-repo bucket B apps/pwa scope check and its whole-repo export partition check.
131
+ 'test/f12-sweep.test.ts',
132
+ // Replaces unwired-exports' real src/oauth caller check and its src/ad-pipeline CLI scan.
133
+ 'scripts/unwired-exports.test.ts',
134
+ // Replaces pwa-build-typecheck's full root tsc and PWA tsc sweeps (PWA build also runs in the release loop).
135
+ 'test/pwa-build-typecheck.test.ts',
135
136
  ];
136
137
  const tail40 = (output: string) => output.trimEnd().split(/\r?\n/).slice(-40).join('\n');
137
138
  const lastStartedTestFile = (output: string, paths: string[]): string | undefined => {
@@ -219,15 +220,15 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
219
220
  // CDP 시험은 `test:deterministic` 이 스스로 뺀다 — 조각에 그것만 남으면 bun «시험 없음» exit 1 을 «불완전»으로 읽었다(09-30 G1d 실측 둘).
220
221
  // bun 의 탐색은 숨은 디렉터리(`.x/`)를 안 연다 — 사람의 `bun test` 가 한 번도 안 돌리는 빈 픽스처가 조각에 혼자 남아 «Ran 0 tests» 를 «불완전»으로 읽었다(09-30 G1e 5번 조각).
221
222
  const assignable = files.filter((file) => !cdpPatterns.includes(file) && !POD_SWEEP_INTEGRATION_ONLY.includes(file)
222
- && !GATE_NIGHTLY_AUDITS.includes(file) && !file.split('/').some((part) => part.startsWith('.')));
223
+ && !file.split('/').some((part) => part.startsWith('.')));
223
224
  const integrationOnly = files.filter((file) => POD_SWEEP_INTEGRATION_ONLY.includes(file));
224
225
  if (integrationOnly.length) debug.log('release-loop.gate', 'pod-sweep-integration-only', { files: integrationOnly });
225
- const nightlyAudits = files.filter((file) => GATE_NIGHTLY_AUDITS.includes(file));
226
- if (nightlyAudits.length) debug.log('release-loop.gate', 'pod-sweep-nightly-audit', { files: nightlyAudits });
227
226
  if (!assignable.length) throw new Error('sweep incomplete: no tests ran');
228
227
  const durations = pod.durationSource ? readFileDurations(pod.durationSource) : new Map<string, number>();
229
228
  const memory = readFileMemory(join(tree, POD_MEMORY_SOURCE));
230
- const heavy = new Set(assignable.filter((file) => (memory.get(file) ?? 0) >= POD_HEAVY_FILE_MB));
229
+ // The TD1 measurements predate the fixture-only gate; do not isolate those five files at their old 2–13 GB peaks.
230
+ const fixtureOnly = new Set([...GATE_NIGHTLY_AUDITS, 'test/guardian/dispatch-surface-contract.test.ts', 'test/user-config-mcp.test.ts']);
231
+ const heavy = new Set(assignable.filter((file) => !fixtureOnly.has(file) && (memory.get(file) ?? 0) >= POD_HEAVY_FILE_MB));
231
232
  const light = assignable.filter((file) => !heavy.has(file));
232
233
  const shards = [
233
234
  ...(light.length ? planShards(light, durations, shardCount) : []),
@@ -437,9 +438,9 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
437
438
  const outputs: string[] = [];
438
439
  let total = { pass: 0, fail: 0, errors: 0, ran: 0, files: 0 };
439
440
  for (const group of groups) {
440
- if (!group.paths.length || !files.some((f) => !GATE_NIGHTLY_AUDITS.includes(f) && group.paths.some((p) => f === p || f.startsWith(`${p}/`)))) continue;
441
+ if (!group.paths.length || !files.some((f) => group.paths.some((p) => f === p || f.startsWith(`${p}/`)))) continue;
441
442
  const start = Date.now();
442
- const groupIgnores = [...new Set([...cdpPatterns, ...GATE_NIGHTLY_AUDITS])]
443
+ const groupIgnores = [...new Set(cdpPatterns)]
443
444
  .filter((pattern) => files.includes(pattern) && group.paths.some((p) => pattern === p || pattern.startsWith(`${p}/`)))
444
445
  .flatMap((pattern) => ['--path-ignore-patterns', pattern]);
445
446
  const run = await command('bun', ['run', 'test:deterministic', ...groupIgnores, ...group.paths.map(asPath)], tree);
@@ -508,7 +509,7 @@ function readBaseline(root: string, version: string): { commit: string; failures
508
509
  return record;
509
510
  }
510
511
 
511
- function baselineCommit(root: string, version: string): string {
512
+ export function baselineCommit(root: string, version: string): string {
512
513
  const path = join(root, 'release', version, 'release.json');
513
514
  const manifest: unknown = JSON.parse(readFileSync(path, 'utf8'));
514
515
  const commit = manifest && typeof manifest === 'object' && 'sourceCommit' in manifest ? manifest.sourceCommit : undefined;