stream_weaver 0.3.0

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 (381) hide show
  1. checksums.yaml +7 -0
  2. data/.beads/.gitignore +77 -0
  3. data/.beads/README.md +81 -0
  4. data/.beads/config.yaml +54 -0
  5. data/.beads/metadata.json +7 -0
  6. data/.rspec +3 -0
  7. data/AGENTS.md +91 -0
  8. data/CHANGELOG.md +294 -0
  9. data/CLAUDE.md +67 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +565 -0
  12. data/Rakefile +13 -0
  13. data/assets/streamweaver-hero.jpg +0 -0
  14. data/bench/baselines/ledger.rb +58 -0
  15. data/bench/baselines/warroom.rb +51 -0
  16. data/bench/fixtures/ledger.rb +76 -0
  17. data/bench/fixtures/warroom.rb +60 -0
  18. data/bench/results/6daf8b8.md +102 -0
  19. data/bench/results/c8d749b.md +102 -0
  20. data/bench/results/eb06ab9.md +102 -0
  21. data/bench/run.rb +151 -0
  22. data/bench/support.rb +84 -0
  23. data/copy_this/dto_run_sample_run_7631.html +220 -0
  24. data/docs/SERVICE_MODE.md +302 -0
  25. data/docs/architecture/how_streamweaver_works.md +772 -0
  26. data/docs/blog/2026-01-16-streamweaver-introduction.md +515 -0
  27. data/docs/bug-2026-08-31-canvas-bridge-socket-collision.md +83 -0
  28. data/docs/canvas-ipc-session-summary.md +127 -0
  29. data/docs/canvas-panel-workflow.md +172 -0
  30. data/docs/canvas-read.md +147 -0
  31. data/docs/canvas-roadmap.md +275 -0
  32. data/docs/case-studies/2026-09-03-didx-canvas-read-shelf.md +147 -0
  33. data/docs/claude-code-companion-skill-spec.md +567 -0
  34. data/docs/components_reference.md +1223 -0
  35. data/docs/crud-patterns.md +265 -0
  36. data/docs/endpoints.md +122 -0
  37. data/docs/for_llms.md +1 -0
  38. data/docs/form-patterns.md +165 -0
  39. data/docs/frontend-only.md +169 -0
  40. data/docs/html-artifact-audit.md +238 -0
  41. data/docs/ideas/2025-01-01-as-a-service.md +203 -0
  42. data/docs/ideas/2026-01-16-charm-tui-exploration.md +542 -0
  43. data/docs/ideas/2026-08-27-static-doc-shelf-export.md +153 -0
  44. data/docs/ideas/dhh-review-future-refactors.md +142 -0
  45. data/docs/opal-jamstack.md +47 -0
  46. data/docs/opal-npm-direction.md +184 -0
  47. data/docs/opal-spike-findings.md +194 -0
  48. data/docs/plans/2025-12-16-form-blocks-design.md +124 -0
  49. data/docs/plans/2025-12-30-examples-browser-design.md +152 -0
  50. data/docs/plans/2026-01-02-charts-design.md +288 -0
  51. data/docs/plans/2026-01-10-canvas-ipc-design.md +247 -0
  52. data/docs/plans/2026-01-19-cabinet-control-components.md +176 -0
  53. data/docs/plans/2026-01-26-gem-release-and-panel.md +140 -0
  54. data/docs/plans/canvas-claude-project-design.md +238 -0
  55. data/docs/plans/canvas-doc-location-and-discovery.md +32 -0
  56. data/docs/plans/org-doc-preview-surfaces.md +293 -0
  57. data/docs/plans/shadcn-polish-plan.md +417 -0
  58. data/docs/porting-artifacts.md +252 -0
  59. data/docs/reference/agent-skills-comparison.md +369 -0
  60. data/docs/reference/travel-state-prd.artifact.html +780 -0
  61. data/docs/research/2026-08-17-hotwire-alike-landscape.md +268 -0
  62. data/docs/research/2026-08-17-hotwire-concept-map.md +404 -0
  63. data/docs/research/2026-08-22-lazy-fragments-trigger-decision.md +246 -0
  64. data/docs/research/2026-08-22-learnhotwire-syllabus-coverage.md +239 -0
  65. data/docs/research/frontend-only-matrix.md +278 -0
  66. data/docs/research/streamweaver-way-spike-findings.md +529 -0
  67. data/docs/resource-dsl.md +342 -0
  68. data/docs/routing.md +302 -0
  69. data/docs/ruby-ui-comparison.md +319 -0
  70. data/docs/shared-dsl-fragments.md +356 -0
  71. data/docs/streamweaver-for-ai-agents.md +401 -0
  72. data/docs/streamweaver-frontend-vision.md +123 -0
  73. data/docs/streamweaver_canvas/glimmer_initial_final_layer.rb +77 -0
  74. data/docs/streamweaver_canvas/save_example.rb +9 -0
  75. data/docs/templates.md +213 -0
  76. data/docs/testing.md +155 -0
  77. data/docs/theming-hooks.md +281 -0
  78. data/docs/tutorials/the-streamweaver-way.md +882 -0
  79. data/docs/university/capability-inventory.md +137 -0
  80. data/docs/university/dependency-survey.md +114 -0
  81. data/docs/university/design-spec.md +217 -0
  82. data/docs/university/mockups/code-block-doc-theme-dark.png +0 -0
  83. data/docs/university/mockups/code-block-doc-theme-light.png +0 -0
  84. data/docs/university/mockups/course-list-dark.png +0 -0
  85. data/docs/university/mockups/course-list-light.png +0 -0
  86. data/docs/university/mockups/course_canvas_mockup.rb +741 -0
  87. data/docs/university/mockups/step-screen-dark.png +0 -0
  88. data/docs/university/mockups/step-screen-light.png +0 -0
  89. data/docs/university/roadmap.md +72 -0
  90. data/docs/university/send-to-coworker.md +77 -0
  91. data/docs/university/worker-session-mining.md +109 -0
  92. data/docs/visual/browser-rendering-spike.md +173 -0
  93. data/docs/visual/builder-visual-plan-analysis.md +378 -0
  94. data/docs/visual/sw-plan-format-exploration.md +511 -0
  95. data/docs/visual/sw-plan-rendering-deep-dive.md +453 -0
  96. data/docs/visual-skills/PROGRESS.md +60 -0
  97. data/docs/visual-skills/SESSION-CONTEXT.md +207 -0
  98. data/docs/visual-skills/analysis/components.md +317 -0
  99. data/docs/visual-skills/analysis/overlap.md +360 -0
  100. data/docs/visual-skills/analysis/pi-design-deck.md +1102 -0
  101. data/docs/visual-skills/analysis/streamweaver-inventory.md +93 -0
  102. data/docs/visual-skills/analysis/unified-specs.feature +1003 -0
  103. data/docs/visual-skills/analysis/visual-explainer.md +964 -0
  104. data/docs/visual-skills/blog/blog-series-outline.md +39 -0
  105. data/docs/visual-skills/blog/token-efficiency.md +73 -0
  106. data/docs/visual-skills/design/architecture.md +1725 -0
  107. data/docs/visual-skills/design/codex-review.md +340 -0
  108. data/docs/visual-skills/design/dhh-review.md +209 -0
  109. data/docs/visual-skills/design/evolution.md +100 -0
  110. data/docs/visual-skills/design/gemini-review.md +55 -0
  111. data/docs/visual-skills/design/review-synthesis.md +149 -0
  112. data/docs/visual-skills/implementation/STATE.md +22 -0
  113. data/docs/visual-skills/implementation/plan.md +347 -0
  114. data/docs/visual-skills/implementation/spike-findings.md +237 -0
  115. data/docs/visual-skills/implementation/tasks.md +702 -0
  116. data/docs/visual-skills/lessons-learned/process.md +145 -0
  117. data/examples/README.md +96 -0
  118. data/examples/advanced/all_components.rb +177 -0
  119. data/examples/advanced/examples_browser.rb +394 -0
  120. data/examples/advanced/teachables_browser.rb +261 -0
  121. data/examples/advanced/theme_tweaker.rb +366 -0
  122. data/examples/advanced/tutorial.rb +1528 -0
  123. data/examples/agentic/agentic_form.rb +30 -0
  124. data/examples/agentic/agentic_form_autoclose.rb +30 -0
  125. data/examples/agentic/cultivation_tracker.rb +69 -0
  126. data/examples/basic/hello_world.rb +22 -0
  127. data/examples/basic/opal_tabs_table.rb +44 -0
  128. data/examples/basic/todo_list.rb +39 -0
  129. data/examples/button_loading_test.rb +36 -0
  130. data/examples/canvas/mermaid_canvas_demo.sh +151 -0
  131. data/examples/charts/bar_chart_demo.rb +57 -0
  132. data/examples/charts/line_chart_demo.rb +73 -0
  133. data/examples/charts/pie_area_demo.rb +87 -0
  134. data/examples/charts/stacked_bar_chart_demo.rb +69 -0
  135. data/examples/claude_code/README.md +85 -0
  136. data/examples/claude_code/codebreaker/.claude/commands/infiltrate.md +632 -0
  137. data/examples/claude_code/codebreaker/.claude/settings.json +11 -0
  138. data/examples/claude_code/codebreaker/.claude/settings.local.json +17 -0
  139. data/examples/claude_code/codebreaker/.claude/skills/infiltrate.md +0 -0
  140. data/examples/claude_code/codebreaker/README.md +213 -0
  141. data/examples/claude_code/tutorial/.claude/commands/learn.md +453 -0
  142. data/examples/claude_code/tutorial/README.md +102 -0
  143. data/examples/claude_code/verification_flow/.claude/commands/verify.md +171 -0
  144. data/examples/claude_code/verification_flow/README.md +126 -0
  145. data/examples/components/annotated_code_demo.rb +54 -0
  146. data/examples/components/callout_demo.rb +33 -0
  147. data/examples/components/checkbox_group_demo.rb +46 -0
  148. data/examples/components/design_review.css +468 -0
  149. data/examples/components/design_review_demo.rb +44 -0
  150. data/examples/components/design_review_dsl.rb +300 -0
  151. data/examples/components/diff_block_demo.rb +112 -0
  152. data/examples/components/events_demo.rb +184 -0
  153. data/examples/components/form_demo.rb +62 -0
  154. data/examples/components/lesson_demo.rb +53 -0
  155. data/examples/components/markdown_demo.rb +110 -0
  156. data/examples/components/mermaid_demo.rb +166 -0
  157. data/examples/components/pareto_set.rb +64 -0
  158. data/examples/components/prd_demo.rb +21 -0
  159. data/examples/components/prd_dsl.rb +385 -0
  160. data/examples/components/quiz_demo.rb +47 -0
  161. data/examples/components/run_viewer_demo.rb +100 -0
  162. data/examples/components/score_and_collapsible_demo.rb +82 -0
  163. data/examples/components/select_stale_value_smoke_test.rb +39 -0
  164. data/examples/components/table_demo.rb +247 -0
  165. data/examples/components/timer_with_state_demo.rb +58 -0
  166. data/examples/components/todo_due_dates.rb +63 -0
  167. data/examples/components/uat_gaps_demo.rb +112 -0
  168. data/examples/dashboard/feed_simulator.rb +60 -0
  169. data/examples/dashboard/live_dashboard.rb +80 -0
  170. data/examples/dashboard_components.rb +108 -0
  171. data/examples/deferred_fragments_demo.rb +62 -0
  172. data/examples/generate_more_spike/README.md +63 -0
  173. data/examples/generate_more_spike/app.rb +441 -0
  174. data/examples/git_health.sh +401 -0
  175. data/examples/layout/layout_components_demo.rb +174 -0
  176. data/examples/layout/modal_demo.rb +215 -0
  177. data/examples/layout/navigation_demo.rb +227 -0
  178. data/examples/layout/route_tabs_demo.rb +52 -0
  179. data/examples/layout/routing_demo.rb +52 -0
  180. data/examples/layout/scroll_box_demo.rb +154 -0
  181. data/examples/lazy_fragments_demo.rb +151 -0
  182. data/examples/my_todos/README.md +29 -0
  183. data/examples/my_todos/my_todos.rb +312 -0
  184. data/examples/my_todos/store.rb +86 -0
  185. data/examples/opal/reactive_demo.rb +62 -0
  186. data/examples/opal/scenarios/s1_counter.rb +15 -0
  187. data/examples/opal/scenarios/s2_search_filter.rb +17 -0
  188. data/examples/opal/scenarios/s3_sibling_tabs.rb +25 -0
  189. data/examples/opal/scenarios/s4_shopping_cart.rb +34 -0
  190. data/examples/opal/scenarios/s5_watch.rb +39 -0
  191. data/examples/opal/scenarios/s6_on_start.rb +20 -0
  192. data/examples/opal/scenarios/s7_wizard.rb +33 -0
  193. data/examples/opal/scenarios/s8_loan_calculator.rb +21 -0
  194. data/examples/opal/scenarios/s9_dashboard.rb +27 -0
  195. data/examples/operations_dashboard_demo.rb +167 -0
  196. data/examples/panel_demo.sh +447 -0
  197. data/examples/parity/assets/tyrion_slice.css +86 -0
  198. data/examples/parity/rivet_people_slice.rb +182 -0
  199. data/examples/parity/tyrion_components.css +777 -0
  200. data/examples/parity/tyrion_warroom_components.rb +414 -0
  201. data/examples/parity/tyrion_warroom_slice.rb +549 -0
  202. data/examples/puma_dev/README.md +124 -0
  203. data/examples/puma_dev/config.ru +34 -0
  204. data/examples/puma_dev/standalone_app.rb +34 -0
  205. data/examples/scaffolding/blog.rb +49 -0
  206. data/examples/scaffolding/utf_lite.rb +105 -0
  207. data/examples/styling/feedback_demo.rb +229 -0
  208. data/examples/styling/style_showcase.rb +172 -0
  209. data/examples/styling/theme_demo.rb +508 -0
  210. data/examples/timer_health_checker.rb +111 -0
  211. data/examples/timer_showcase.rb +131 -0
  212. data/examples/tutorials/render_markdown.rb +112 -0
  213. data/examples/tutorials/streamweaver_way_tutorial.rb +44 -0
  214. data/examples/tutorials/tutorial_content.rb +1082 -0
  215. data/examples/visual_skills/design_deck_demo.rb +314 -0
  216. data/examples/visual_skills/explainer_demo.rb +499 -0
  217. data/exe/streamweaver +9 -0
  218. data/gsd/ROADMAP-1.0.md +167 -0
  219. data/gsd/STATE.md +13 -0
  220. data/gsd/research/market-positioning-research.md +139 -0
  221. data/gsd/research/production-patterns-research.md +270 -0
  222. data/gsd/research/repo-audit-1.0.md +274 -0
  223. data/lib/stream_weaver/action_token.rb +58 -0
  224. data/lib/stream_weaver/adapter/alpinejs.rb +8249 -0
  225. data/lib/stream_weaver/adapter/base.rb +591 -0
  226. data/lib/stream_weaver/adapter/opal.rb +334 -0
  227. data/lib/stream_weaver/adapter/static.rb +1118 -0
  228. data/lib/stream_weaver/admin.rb +176 -0
  229. data/lib/stream_weaver/app.rb +1821 -0
  230. data/lib/stream_weaver/assets/js/sw-copy.js +50 -0
  231. data/lib/stream_weaver/assets/js/sw-heredoc-rewrite.js +68 -0
  232. data/lib/stream_weaver/assets/js/sw-keyboard.js +165 -0
  233. data/lib/stream_weaver/assets/js/sw-mermaid-zoom.js +621 -0
  234. data/lib/stream_weaver/assets/js/sw-route-tabs.js +53 -0
  235. data/lib/stream_weaver/assets/js/sw-sidebar-toc.js +116 -0
  236. data/lib/stream_weaver/assets/js/sw-slide-nav.js +103 -0
  237. data/lib/stream_weaver/canvas/bridge.rb +239 -0
  238. data/lib/stream_weaver/canvas/bridge_server.rb +646 -0
  239. data/lib/stream_weaver/canvas/client.rb +298 -0
  240. data/lib/stream_weaver/canvas/doc_roots.rb +233 -0
  241. data/lib/stream_weaver/canvas/doc_store.rb +252 -0
  242. data/lib/stream_weaver/canvas/gist_publisher.rb +264 -0
  243. data/lib/stream_weaver/canvas/gist_save_handler.rb +89 -0
  244. data/lib/stream_weaver/canvas/gist_store.rb +135 -0
  245. data/lib/stream_weaver/canvas/helpers.rb +109 -0
  246. data/lib/stream_weaver/canvas/history.rb +90 -0
  247. data/lib/stream_weaver/canvas/protocol.rb +86 -0
  248. data/lib/stream_weaver/canvas/reader.rb +853 -0
  249. data/lib/stream_weaver/canvas/save_doc_widget.rb +457 -0
  250. data/lib/stream_weaver/canvas/scroll_top_hint.rb +21 -0
  251. data/lib/stream_weaver/canvas/session.rb +132 -0
  252. data/lib/stream_weaver/cli.rb +3235 -0
  253. data/lib/stream_weaver/component_assets.rb +70 -0
  254. data/lib/stream_weaver/component_registry.rb +67 -0
  255. data/lib/stream_weaver/component_renderer.rb +48 -0
  256. data/lib/stream_weaver/components/annotated_code.rb +53 -0
  257. data/lib/stream_weaver/components/api_endpoint.rb +42 -0
  258. data/lib/stream_weaver/components/callout.rb +59 -0
  259. data/lib/stream_weaver/components/chart.rb +84 -0
  260. data/lib/stream_weaver/components/code_block.rb +77 -0
  261. data/lib/stream_weaver/components/comparison.rb +39 -0
  262. data/lib/stream_weaver/components/decision.rb +38 -0
  263. data/lib/stream_weaver/components/deck/close_overlay.rb +84 -0
  264. data/lib/stream_weaver/components/deck/confirmation_bar.rb +60 -0
  265. data/lib/stream_weaver/components/deck/deck_option.rb +61 -0
  266. data/lib/stream_weaver/components/deck/deck_slide.rb +77 -0
  267. data/lib/stream_weaver/components/deck/deck_state.rb +469 -0
  268. data/lib/stream_weaver/components/deck/deck_summary.rb +73 -0
  269. data/lib/stream_weaver/components/deck/design_deck.rb +60 -0
  270. data/lib/stream_weaver/components/deck/generate_more_controls.rb +84 -0
  271. data/lib/stream_weaver/components/deck/model_selector.rb +84 -0
  272. data/lib/stream_weaver/components/deck/skeleton_placeholder.rb +36 -0
  273. data/lib/stream_weaver/components/diff_block.rb +125 -0
  274. data/lib/stream_weaver/components/doc_header.rb +57 -0
  275. data/lib/stream_weaver/components/image_block.rb +70 -0
  276. data/lib/stream_weaver/components/implementation_map.rb +33 -0
  277. data/lib/stream_weaver/components/keyboard_shortcuts.rb +94 -0
  278. data/lib/stream_weaver/components/kpi_dashboard.rb +78 -0
  279. data/lib/stream_weaver/components/mermaid.rb +79 -0
  280. data/lib/stream_weaver/components/pipeline.rb +63 -0
  281. data/lib/stream_weaver/components/sidebar_toc.rb +46 -0
  282. data/lib/stream_weaver/components/slide_container.rb +141 -0
  283. data/lib/stream_weaver/components/timeline_event.rb +58 -0
  284. data/lib/stream_weaver/components/wireframe.rb +29 -0
  285. data/lib/stream_weaver/components/wireframe_block.rb +34 -0
  286. data/lib/stream_weaver/components.rb +2711 -0
  287. data/lib/stream_weaver/css.rb +230 -0
  288. data/lib/stream_weaver/dev_fallback_overlay.rb +61 -0
  289. data/lib/stream_weaver/display_dsl.rb +1004 -0
  290. data/lib/stream_weaver/export/html_exporter.rb +478 -0
  291. data/lib/stream_weaver/feed.rb +34 -0
  292. data/lib/stream_weaver/feed_builder.rb +31 -0
  293. data/lib/stream_weaver/fonts.rb +33 -0
  294. data/lib/stream_weaver/interaction_runner.rb +487 -0
  295. data/lib/stream_weaver/iterm.rb +460 -0
  296. data/lib/stream_weaver/layout_registry.rb +92 -0
  297. data/lib/stream_weaver/opal/bridge.rb +52 -0
  298. data/lib/stream_weaver/opal/builder.rb +193 -0
  299. data/lib/stream_weaver/opal/env.rb +29 -0
  300. data/lib/stream_weaver/opal/reactive_state.rb +75 -0
  301. data/lib/stream_weaver/opal/regexp_anchor_patch.rb +62 -0
  302. data/lib/stream_weaver/opal/renderer.rb +77 -0
  303. data/lib/stream_weaver/opal/runtime.rb +250 -0
  304. data/lib/stream_weaver/opal/shell.rb +145 -0
  305. data/lib/stream_weaver/opal/string_bridge.rb +50 -0
  306. data/lib/stream_weaver/opal/stubs/diff.min.js +1 -0
  307. data/lib/stream_weaver/opal/stubs/digest.rb +15 -0
  308. data/lib/stream_weaver/opal/stubs/marked.umd.js +79 -0
  309. data/lib/stream_weaver/opal/stubs/md5.rb +3 -0
  310. data/lib/stream_weaver/opal/stubs/morphdom.min.js +775 -0
  311. data/lib/stream_weaver/opal/stubs/prism-tomorrow.min.css +1 -0
  312. data/lib/stream_weaver/opal/stubs/prism.min.js +1967 -0
  313. data/lib/stream_weaver/opal_entry.rb +136 -0
  314. data/lib/stream_weaver/org/inline.rb +95 -0
  315. data/lib/stream_weaver/org/reader.rb +561 -0
  316. data/lib/stream_weaver/org/recording_context.rb +86 -0
  317. data/lib/stream_weaver/org/source_splitter.rb +63 -0
  318. data/lib/stream_weaver/org/writer.rb +300 -0
  319. data/lib/stream_weaver/page_shell.rb +530 -0
  320. data/lib/stream_weaver/portfile.rb +79 -0
  321. data/lib/stream_weaver/pushable.rb +42 -0
  322. data/lib/stream_weaver/resource/default_views.rb +111 -0
  323. data/lib/stream_weaver/resource/field_input.rb +25 -0
  324. data/lib/stream_weaver/resource/state_keys.rb +16 -0
  325. data/lib/stream_weaver/resource/store.rb +18 -0
  326. data/lib/stream_weaver/resource.rb +104 -0
  327. data/lib/stream_weaver/server.rb +1471 -0
  328. data/lib/stream_weaver/service.rb +1240 -0
  329. data/lib/stream_weaver/service_client.rb +104 -0
  330. data/lib/stream_weaver/session_store.rb +186 -0
  331. data/lib/stream_weaver/skills/streamweaver-canvas-safe/SKILL.md +66 -0
  332. data/lib/stream_weaver/skills/streamweaver-canvas-safe/examples/canvas-safe-showcase.rb +98 -0
  333. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/actions-and-buttons.md +47 -0
  334. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/charts-and-diagrams.md +38 -0
  335. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/deck.md +32 -0
  336. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/inputs-and-forms.md +65 -0
  337. data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/tabs-and-navigation.md +53 -0
  338. data/lib/stream_weaver/skills/streamweaver-doc-builder/SKILL.md +298 -0
  339. data/lib/stream_weaver/skills/streamweaver-visual-companion/SKILL.md +130 -0
  340. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example.css +468 -0
  341. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example.rb +39 -0
  342. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example_dsl.rb +300 -0
  343. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/doc-parity-example.rb +24 -0
  344. data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/doc-parity-example_dsl.rb +385 -0
  345. data/lib/stream_weaver/skills/streamweaver-visual-companion/references/checkpoints-and-forms.md +28 -0
  346. data/lib/stream_weaver/skills/streamweaver-visual-companion/references/cleanup-and-panel.md +44 -0
  347. data/lib/stream_weaver/skills/streamweaver-visual-companion/references/example-gallery.md +8 -0
  348. data/lib/stream_weaver/skills/streamweaver-visual-companion/references/persistence.md +58 -0
  349. data/lib/stream_weaver/skills/streamweaver-way/SKILL.md +396 -0
  350. data/lib/stream_weaver/skills/visual-plan/SKILL.md +201 -0
  351. data/lib/stream_weaver/skills/visual-recap/SKILL.md +244 -0
  352. data/lib/stream_weaver/streamer.rb +63 -0
  353. data/lib/stream_weaver/templates/choices.rb +149 -0
  354. data/lib/stream_weaver/templates/code.rb +157 -0
  355. data/lib/stream_weaver/templates/confirm.rb +110 -0
  356. data/lib/stream_weaver/templates/diff.rb +195 -0
  357. data/lib/stream_weaver/templates/info.rb +142 -0
  358. data/lib/stream_weaver/templates/table.rb +169 -0
  359. data/lib/stream_weaver/templates/wizard.rb +271 -0
  360. data/lib/stream_weaver/theme/auto_mode.rb +116 -0
  361. data/lib/stream_weaver/theme/presets.rb +555 -0
  362. data/lib/stream_weaver/theme.rb +639 -0
  363. data/lib/stream_weaver/university/canvas.rb +924 -0
  364. data/lib/stream_weaver/university/course.rb +699 -0
  365. data/lib/stream_weaver/university/demos/counter.rb +33 -0
  366. data/lib/stream_weaver/university/demos/dashboard.rb +147 -0
  367. data/lib/stream_weaver/university/demos/decision_form.rb +143 -0
  368. data/lib/stream_weaver/university/demos.rb +50 -0
  369. data/lib/stream_weaver/university/listener.rb +467 -0
  370. data/lib/stream_weaver/university/progress.rb +201 -0
  371. data/lib/stream_weaver/university/runner.rb +138 -0
  372. data/lib/stream_weaver/university/scripts/growing_doc.rb +609 -0
  373. data/lib/stream_weaver/university/scripts/growing_doc_state.rb +91 -0
  374. data/lib/stream_weaver/utils.rb +31 -0
  375. data/lib/stream_weaver/version.rb +5 -0
  376. data/lib/stream_weaver/views/canvas/reader_layout.erb +800 -0
  377. data/lib/stream_weaver/views.rb +3940 -0
  378. data/lib/stream_weaver.rb +122 -0
  379. data/llms.txt +1269 -0
  380. data/sig/stream_weaver.rbs +4 -0
  381. metadata +598 -0
@@ -0,0 +1,72 @@
1
+ # StreamWeaver University — Roadmap of Epics
2
+
3
+ Draft 2026-08-28. Input for `/tyrion-shape`. Companion: `capability-inventory.md`.
4
+
5
+ ## Why
6
+
7
+ A coworker saw the docs + canvas demo and wants to try it. Two audiences from day one:
8
+
9
+ - **Developer** — installs the gem, runs the tutorial in their own Claude Code session.
10
+ - **Non-developer** — never installs StreamWeaver; reads org docs on gists via the Chrome extension.
11
+
12
+ Real-life forcing function: this week's discussions will run on gists + org mode with both kinds of coworker.
13
+
14
+ ## Architecture (what changes vs what stays)
15
+
16
+ | Layer | Stays the same | Varies over time |
17
+ |---|---|---|
18
+ | Curriculum | Courses, steps, scripts, expected canvas state, progress ledger | Number of courses |
19
+ | Driver | "Teacher session steps the user; worker session demonstrates" | Claude Code first; Codex (needs herdr) later |
20
+ | Surface | "Canvas beside the terminal" | iTerm split pane (`streamweaver panel`) first; herdr chromium panel; cmux tab; plain browser fallback |
21
+
22
+ Rule: the University app only knows the curriculum layer. Driver and surface are two shell-outs kept in one file, swapped later without touching courses.
23
+
24
+ ## Trilaws constraints (hard)
25
+
26
+ - **Forrest's Law** — one door: `streamweaver get-started`. It installs skills, starts the bridge, opens the surface, lands on the course list. No second command to learn.
27
+ - **Matt's Law** — course list is the table of contents. Only Getting Started enabled; future courses listed, disabled, one line each.
28
+ - **Gloria's Law** — progress persists (`~/.streamweaver/university/progress.yml`); resume lands on the next unfinished step; every step is re-runnable and ends in a visible canvas payoff.
29
+
30
+ ## Epic 1 — `university-getting-started` (this week)
31
+
32
+ Stories (draft, rigor in brackets):
33
+
34
+ 1. **readme-extension-webstore** [trivial] — README Browser Extension section links the Chrome Web Store listing (`https://chromewebstore.google.com/detail/streamweaver-doc-viewer/odjjednfpfiagefgpcfdlelldphmpcgj`) as the primary path (extension ID is stable across updates); "load unpacked" becomes the dev path. Add a "Share a doc" recipe with the two-level framing: gist = quick collab on one doc with anyone (send gist link + extension link, no install); the level-up is committing the same `.org` to the team repo, where it renders identically in the repo browser — docs graduate from shared-once to living with the code, no format change.
35
+ 2. **install-skill-covers-all-skills** [trivial] — `gem_skills` hash in `CLI.install_skill` (cli.rb ~2056) includes `visual-plan` and `visual-recap`; printed summary matches.
36
+ 3. **get-started-door-command** [loose] — `streamweaver get-started` extends existing `setup` (cli.rb ~2095, already installs skills to the Claude and `.agents` skill roots). Premier experience is opt-OUT: checks iTerm2 + `iterm2_ruby` + Python API and, if any is missing, prints exact install steps and a loud "full experience needs iTerm2" warning; only an explicit `--degraded` / "continue anyway" proceeds without it. Also checks core (Ruby, gem, canvas bridge) and agent skills (Claude and Codex roots, warn if no agent). Premier path: opens canvas split pane, opens a worker tab running `claude` (or `codex`), pushes the course-list canvas. Degraded path (Windows/Linux/no-iTerm): browser tab + instructions to arrange a second terminal beside it.
37
+ 4. **course-list-canvas** [loose] — course listing with Getting Started enabled, others disabled with one-line blurbs; points to existing `streamweaver tutorial` as "the classic component tour (older, being refreshed)".
38
+ 5. **progress-ledger** [loose] — per-step done state in `~/.streamweaver/university/progress.yml`, resume-to-next-unfinished, "repeat step" always available.
39
+ 5b. **driver-worker-runner** [strict] — the course canvas is the driver: each step has a Run button; clicking sends the step's prompt to the worker tab via `iterm2ctl send-text` (driver adapter lives in `iterm.rb` next to the surface adapter). Deterministic, no teacher LLM (IoC phase 3). Degraded mode: canvas shows the prompt with a copy button and "paste into your second terminal". Strict because tab targeting can silently hit the wrong tab.
40
+ 6. **step-1-canvas-push** [loose] — `panel` + `canvas-push` hello card. Payoff: appears in the pane, no HTML.
41
+ 7. **step-2-dsl-reexec** [loose] — 6-line `ruby app.rb`; mental model: block re-executes per interaction.
42
+ 8. **step-3-form-modes** [loose] — same form as stateful vs blocking (`canvas-wait`/`pick`/`confirm`). Payoff: "a TUI can't do this."
43
+ 9. **step-4-growing-doc** [loose] — worker runs a script that appends sections; save-as-doc; review versions in `canvas-read`.
44
+ 10. **step-5-org-portability** [loose] — `export`/`org-export` the doc, push to a gist, open with the extension; contrast native GitHub org rendering vs extension rendering.
45
+ 11. **coworker-install-blurb** [trivial] — one paragraph + 3 commands to send to a developer coworker; one link pair (gist + extension) to send to a non-developer.
46
+ 12. **clean-room-walkthrough** [strict, gate] — fresh machine/user runs the blurb end-to-end; every step reaches its payoff; evidence in ledger.
47
+
48
+ Out of scope for Epic 1: LLM teacher session (`/worker-session`), Codex as worker beyond a pickup check, cmux, herdr, `template`/`pick`/`confirm` docs beyond what step 3 needs.
49
+
50
+ ## Earmarked epics (titles only, flesh out when reached)
51
+
52
+ - **university-teacher-worker** — orchestrator session drives a `/worker-session`; course canvas becomes the teacher's control panel (jump to step N, repeat).
53
+ - **tutorial-refresh** — bring `streamweaver tutorial` (Glimmer-derived code/edit/render format, keep it) up to current components and capabilities; register it as a University course.
54
+ - **university-codex-driver** — Codex as the worker; depends on a herdr chromium-panel spike (personal payoff too).
55
+ - **university-surfaces** — cmux tab and herdr panel adapters.
56
+ - **course-docs-deep-dive**, **course-canvas-modes**, **course-skills-and-panels** — one epic each.
57
+ - **cli-discoverability** — `template`, `pick`, `confirm` documented or hidden from help.
58
+
59
+ ## Open items
60
+
61
+ - Meeting transcript → `demo-ask.md`; cross-check the five steps against what actually landed with the coworker.
62
+ - Confirm the Web Store listing is current with `extension/` on main before pointing coworkers at it.
63
+
64
+ ## Additions after dependency survey (2026-08-28)
65
+
66
+ Source: `dependency-survey.md`.
67
+
68
+ - `iterm2_ruby` already degrades gracefully (`iterm.rb:85` LoadError → `open`), so the surface tier is a report, not a gate. Only real setup step is iTerm2 → Preferences → General → Magic → Enable Python API; get-started must say so.
69
+ - Codex skill install already exists via `~/.agents/skills` (spec-tested). Follow-up story: also mirror `~/.codex/skills/` (legacy path) or verify Codex picks up `.agents/skills` on Brett's version — **verify-codex-skill-pickup** [strict].
70
+ - `/worker-session` chain (skill → `~/work/claude_code_history/bin/worker-session` → herdr or `iterm2ctl` → `claude`) is personal tooling, not the gem. Stays out of Epic 1; in `university-teacher-worker` it is published as a world-tier skill and get-started installs it like any other skill. Gem owns canvas/docs; skill package owns session orchestration; get-started is the installer that knows both.
71
+ - Browser-open logic duplicated 3× (service_client.rb, cli.rb, server.rb) — earmark **dedupe-browser-open** under `cli-discoverability`.
72
+ - No private/team/world skill packaging scheme exists yet — earmark epic **skill-packaging-tiers**.
@@ -0,0 +1,77 @@
1
+ # Send this to a coworker
2
+
3
+ The pitch, verbatim:
4
+
5
+ ```bash
6
+ gem install stream_weaver
7
+ streamweaver install
8
+ streamweaver get-started
9
+ ```
10
+
11
+ 3 steps to awesome. Full experience is macOS + iTerm2; anywhere else, `get-started`
12
+ falls back to a browser tab automatically -- same course, no surprises.
13
+
14
+ Two versions below, depending on who's getting it. Pick one and paste it as-is.
15
+
16
+ ## Developer version
17
+
18
+ > StreamWeaver is a Ruby DSL for building interactive UIs with almost no code, and it comes with a
19
+ > five-step "Getting Started" course that teaches it by actually running it next to your terminal.
20
+ > The full experience puts the course in an iTerm2 window of its own and your agent in a fresh tab
21
+ > beside your work -- set iTerm2 up first, then install the gem and run the door command.
22
+
23
+ **1. Prerequisites** (macOS + iTerm2 only -- skip if you're on another OS or terminal, you'll just
24
+ get a browser tab instead):
25
+
26
+ ```bash
27
+ gem install iterm2_ruby
28
+ ```
29
+
30
+ Then in iTerm2: **Settings → General → Magic → Enable Python API**.
31
+
32
+ **2. Install the gem:**
33
+
34
+ ```bash
35
+ gem install stream_weaver
36
+ ```
37
+
38
+ **3. Run the door command:**
39
+
40
+ ```bash
41
+ streamweaver get-started
42
+ ```
43
+
44
+ You'll see a dependency checklist (✅/❌ per item), then, if everything's green, `=== Opening
45
+ premier experience ===` followed by two things: a new iTerm2 tab in your current window running
46
+ your agent CLI, and the StreamWeaver University canvas in a window of its own. The canvas is the
47
+ control panel -- you drive the course from there; the agent tab stays clear for the agent (and for
48
+ the demo canvas panes it opens as you work through the steps). If iTerm2 isn't set up, the canvas
49
+ falls back to a plain browser tab instead (same course) -- pass `--degraded` to skip straight to
50
+ that.
51
+
52
+ **You're done when** the Getting Started panel shows "Start with step 1." -- click "Run step 1" and
53
+ watch it type the first prompt into the agent tab for you.
54
+
55
+ Your progress persists to disk as you go, so you can close everything and come back later -- even
56
+ after a reboot -- and `get-started` picks up exactly where you left off. Want to start over? Click
57
+ the quiet "Reset course" link at the bottom of the list (or run `streamweaver university-reset`).
58
+
59
+ If your agent CLI has browser control -- claude-in-chrome, playwright-cli, or gstack's `/browse`
60
+ skill -- the worker session moves faster, but it's optional: every course step verifies what it did
61
+ with `curl` first, so none of this blocks you from finishing the course without it.
62
+
63
+ ## Non-developer version
64
+
65
+ > The gist link below is a placeholder -- replace it with the real one before sending.
66
+
67
+ > Here's a doc StreamWeaver generated, viewable two ways:
68
+
69
+ | Version | Link |
70
+ |---|---|
71
+ | Plain (works everywhere, no install) | `<GIST LINK — replace before sending>` |
72
+ | Rendered (sidebar nav, callouts, tables, live diagrams) | Same gist link, after installing the [StreamWeaver Doc Viewer extension](https://chromewebstore.google.com/detail/streamweaver-doc-viewer/odjjednfpfiagefgpcfdlelldphmpcgj) |
73
+
74
+ The gist link already renders as readable text without the extension -- the extension just makes it
75
+ look exactly like it did in the original canvas: sidebar navigation, callouts, cards, tables, and
76
+ live Mermaid diagrams, all in the browser, no install beyond the one-click extension. The extension
77
+ is Chrome/Chromium only -- other browsers just see the plain org text, which is still readable.
@@ -0,0 +1,109 @@
1
+ # Worker-Session Mining: StreamWeaver University Live UAT
2
+
3
+ Source transcripts (read-only mining, session IDs only — full paths omitted):
4
+
5
+ | Session | Date | Duration | Steps covered |
6
+ |---|---|---|---|
7
+ | `9fc075bb` | 2026-08-31 | ~9 min | Step 1 (hello canvas), Step 2 (counter app) |
8
+ | `25b1a28c` | 2026-09-03 | ~1h45m (elapsed; most of the gap is between step 1 and step 2, likely the human stepping away) | Steps 1-5 (hello canvas, counter, radio_group form + canvas-wait, growing-doc, org-export + gist + extension) |
9
+
10
+ Only two worker sessions in the project's transcript directory matched the course signature
11
+ (`"Using stream_weaver, ..."` prompts). The coordinator session and several other large
12
+ transcripts were general StreamWeaver dev work, not course UAT, and were excluded.
13
+
14
+ ## 1. User-customization collisions
15
+
16
+ | Collision | What happened | Course impact |
17
+ |---|---|---|
18
+ | `gstack /browse`-only rule vs. reflex reach for Chrome MCP | In `25b1a28c`, right after starting the counter app, Claude ran `ToolSearch` for `mcp__claude-in-chrome__*` tools, got an empty result, then self-corrected: *"I need to correct course — the repo's CLAUDE.md says to use the gstack `/browse` skill for web browsing, not `mcp__claude-in-chrome__*` directly."* | One wasted tool call + a visible reasoning detour per session. Self-corrected cleanly both times, but it's a repeatable tax the course prompts could pre-empt by naming the browser tool up front. |
19
+ | Headless-browser CLAUDE.md rule vs. "install the extension" course step | Step 5 in `25b1a28c` asks the agent to install a Chrome extension and compare rendering. Claude correctly identified that `gstack browse` is a disposable headless Chromium daemon with no access to the user's real, logged-in Chrome — so it can't install a Web Store extension or show its effect. It flagged this constraint honestly *before* attempting anything, then used `AskUserQuestion` with three options: (1) "You install it yourself (recommended)" — click the Web Store link in real Chrome, refresh, click "View rendered"; (2) "I attempt it in the headless browser anyway" — likely blocked, won't persist, but shows what happens; (3) "Skip it." | This is the biggest single collision: a course step that structurally requires a persistent, logged-in browser profile is incompatible with the project's headless-only browsing rule. The user picked "I have it installed, just open the page" (a fourth, unlisted answer), and Claude correctly explained *again* that it still can't reach the user's real Chrome from the headless daemon — it can only show the plain-GitHub view it already had. |
20
+ | Push-hook / git-push restriction | Not triggered in either session — neither worker committed or pushed. No evidence either way from these transcripts. |
21
+ | Skill auto-triggering | The `browse` skill's own preamble (gstack-update-check, session bookkeeping, proactive-mode check) ran automatically and correctly both times with no visible friction — this one helped, not hindered. |
22
+
23
+ ## 2. Browser-control dependency
24
+
25
+ Every step from the counter app onward needed a browser to verify server-rendered UI (StreamWeaver's whole pitch is "no client JS," so verification means watching the DOM change after a real click). Both sessions reached for the same tool, in this order:
26
+
27
+ 1. Reflexive attempt at `mcp__claude-in-chrome__*` tools (session `25b1a28c` only; `9fc075bb` went straight to `/browse`, suggesting the reflex is intermittent, not universal).
28
+ 2. Self-correct (or start directly) with the gstack `browse` CLI: `goto`, `text`, `snapshot -i`, `click @ref`, `network`, `prettyscreenshot`.
29
+ 3. For the counter/radio/growing-doc steps, `browse` fully sufficed — headless is enough to click a button and read the resulting text, and it worked cleanly except for one recurring quirk (below).
30
+ 4. For the extension-install step, headless was a hard wall — no logged-in profile, no persisted extensions, no way to reach the user's real Chrome from the agent's side at all.
31
+
32
+ **What a user with no browser tooling would experience**: steps 1-4 of the course would fail outright, or fall back to a much weaker "trust me, curl showed the HTML changed" verification (both sessions actually did use `curl`/`grep` on the raw HTML as a first check before touching a browser at all, so a no-browser session isn't *totally* blind — it can confirm the server round-trip via `POST .../action/... → 200` and grep the returned HTML for the expected text). Step 5's extension comparison would be entirely unreachable without the user doing it by hand — which, as this UAT showed, is true even *with* the best browser tooling available in this repo (headless), since that step needs the user's own logged-in Chrome regardless.
33
+
34
+ **Recommended course phrasing**: state the browser dependency and the fallback explicitly, e.g.:
35
+ > "This step verifies UI state by reading the rendered page. If you have `claude-in-chrome`, `playwright-cli`, or a project `/browse`-style skill configured, use it to click through and confirm the count/text visually. If none of those are available, verify via `curl <url> | grep <expected text>` before and after the action, plus `POST .../action/... → 200` in the app's own log — that's sufficient to prove the DSL block re-ran server-side, just without a screenshot."
36
+
37
+ For the extension step specifically, the course should say up front: *"This step needs your own logged-in Chrome — no automated/headless browser tool can complete it, by design (Chrome blocks extension installs from automation, and a headless session has no access to your profile). Expect the agent to hand this step back to you."*
38
+
39
+ ## 3. Good response patterns worth institutionalizing
40
+
41
+ | Pattern | Where | Why it landed well |
42
+ |---|---|---|
43
+ | Honest capability boundary + structured choice | `25b1a28c` step 5: named the exact constraint (headless daemon, no logged-in profile, Web Store blocks automated installs), then gave 3 concrete options with one-line tradeoffs each, rather than silently attempting and failing or silently skipping. | Textbook "surface the tradeoff, don't hide confusion" — the user could make an informed call in one turn. Worth turning into a named pattern (something like "capability-boundary AskUserQuestion") for any course step that touches a resource the agent structurally cannot reach. |
44
+ | Mechanism-level counter explanation | Both sessions, step 2: after clicking the button, Claude didn't just report the count — it pulled the raw HTML (`hx-post="/action/btn_increment_..."`), then in `9fc075bb` explicitly ran `browse network` to show the real `POST .../action/... → 200` calls, concluding *"each click fires a real HTTP POST ... the server re-runs the app block ... no client-side JS holding the counter."* | This is exactly the pedagogical point of StreamWeaver (server-side re-execution, not client JS) proven with evidence, not asserted. The course's own step-2 prompt already asks for this ("that's the same DSL block re-running, not JavaScript") — Claude's response consistently over-delivered on it by showing the network trace unprompted. |
45
+ | Proactive location-mismatch flag | `25b1a28c` step 4: the saved doc landed at `~/.streamweaver/canvas/doc-demo-*.rb` instead of the course's assumed `docs/streamweaver_canvas/doc-demo.rb`, because the canvas bridge process (started in an earlier session) has a different cwd. Claude flagged this immediately and precisely ("likely because the canvas bridge process ... has a different working directory than this shell") rather than silently adjusting or failing later. | Saved the *next* course step from breaking silently — step 5's prompt hard-codes the `docs/streamweaver_canvas/doc-demo.rb` path, and Claude had already told the user it wouldn't be there. |
46
+
47
+ ## 4. Friction/waste
48
+
49
+ | Issue | Sessions | Detail |
50
+ |---|---|---|
51
+ | Step-2 startup failure, two different causes | Both | `9fc075bb` (Aug 31): first draft of `app.rb` had no `.run!` — bare `app "Title" do...end` only builds the app, doesn't start the server; process exited immediately with no error, requiring several minutes of `lsof`/`ps`/source-diving to find `SinatraApp.run!`. `25b1a28c` (Sep 3): first draft was missing `require 'stream_weaver'` — hit `NoMethodError: undefined method 'app'` immediately, fixed in one turn by checking `streamweaver llm` and an example file. Same course step, same 6-line-app goal, two independent trial-and-error paths — evidence the "6 lines" framing in the prompt undercounts the two lines (`require` + `.run!`) that are structurally necessary but easy to omit. |
52
+ | Slow first-run discovery path | `9fc075bb` | Rather than checking `streamweaver llm` (which `25b1a28c` used immediately and successfully), this session dispatched an `Explore` subagent to grep the repo for the button/state DSL, then used `ScheduleWakeup` to poll it twice (90s, then 120s) before the result came back — roughly 90+ seconds of pure waiting that the Sep 3 session avoided entirely by running `streamweaver llm | grep -n -i state` directly. |
53
+ | `browse` "ambiguous selector" on ref-based clicks | `25b1a28c`, twice | Clicking a `@ref` returned from `snapshot -i` (not a CSS selector) twice failed with "Selector matched multiple elements. Be more specific or use @refs from 'snapshot'" — once on a radio button (`@e2`), once on a modal's Save button (`@e5`, which collided with a hidden "Saving..." `<span>` sharing structure). Both times Claude recovered by falling back to a hand-written CSS class selector (`.sw-save-doc-save`) found by grepping the raw HTML — a working but manual detour that cost 2-3 extra tool calls each time. |
54
+ | Redundant polling loops for background server startup | Both | Both sessions ran 2-3 rounds of `sleep N && cat task-output.log` waiting for the "server started" banner to flush, then fell back to `lsof`/`ps` to find the bound port because the banner never appeared in the captured output at all (output buffering, not a real failure). Worked, but ~6 tool calls per session spent on what should be a fixed, known port-discovery recipe. |
55
+ | Gist defaulted to secret | `25b1a28c` | `gh gist create` produced a **secret** gist (gh's default) even though the course step describes opening it and sharing/comparing rendering — Claude flagged this after the fact ("let me know if you wanted it public instead") rather than asking first or defaulting to `--public`. Not wrong, but a step that explicitly plans to open the gist URL and later compare it via a public-facing browser extension probably wants `--public` by default. |
56
+
57
+ ## Recommended changes
58
+
59
+ ### Course-prompt changes
60
+
61
+ - Step 2 (counter app): explicitly note the two "invisible" required lines — `require 'stream_weaver'` at the top and `.run!` chained onto the `app do...end` block — so the "6-line app" framing doesn't cost agents a debug cycle discovering both independently. Alternatively, point straight at `streamweaver llm`'s canonical counter example as the first move, before writing anything from memory.
62
+ - Step 5 (org-export + gist + extension): split into two explicit sub-steps with different expectations — (a) org-export + gist + view plain rendering, fully agent-completable; (b) extension install + "View rendered" comparison, framed as "hand this back to the user" rather than something the agent should attempt. State the headless-browser limitation up front instead of letting the agent discover and explain it live.
63
+ - Step 5: default `gh gist create` to `--public` in the course's own suggested command, since the step's payoff depends on opening the URL and later showing it with the extension.
64
+ - Any step that reads back rendered state after a click: suggest verifying via the app/action HTTP log (`POST .../action/... → 200`) as a browser-optional fallback, not just as bonus evidence.
65
+
66
+ ### Get-started/prereq changes
67
+
68
+ - State explicitly, before the course begins, which browser-automation tool the session should use and why (e.g. "this repo's CLAUDE.md restricts browsing to gstack's `/browse` skill — don't reach for `claude-in-chrome` or `playwright-cli` directly here"), so the reflexive `ToolSearch` → correction detour seen in `25b1a28c` doesn't recur per session.
69
+ - Add a one-line callout that the extension-install step is structurally out of reach for any headless/automated browser tool (claude-in-chrome-headless included), regardless of which one is configured — it needs the user's real, logged-in browser.
70
+ - Document the fixed port-discovery recipe (`lsof -i :4567-4620 -sTCP:LISTEN`, per the project's own memory note) as the canonical way to find a backgrounded StreamWeaver server's port, instead of leaving agents to rediscover the `sleep && cat log` → `lsof` fallback each time.
71
+
72
+ ### Docs changes
73
+
74
+ - README/growing_doc step: note that a doc saved via the canvas "Save as doc" button lands under `~/.streamweaver/canvas/` (not `docs/streamweaver_canvas/<repo>/`) whenever the canvas bridge process was started from a different working directory than the current shell — this is exactly what broke the assumed path in the org-export step of the Sep 3 session, and the design doc (`docs/plans/canvas-doc-location...`) is already flagged as not-yet-built for the global-vs-repo toggle.
75
+ - `streamweaver llm` reference: confirm it's positioned as the first stop for DSL syntax questions (it worked well when used) — the Aug 31 session's slower path (dispatching an Explore subagent instead) suggests it isn't yet the obvious/first instinct.
76
+
77
+ ## Round-5 latency + portability pass (2026-09-03)
78
+
79
+ Two additional Sep-3 worker sessions matched the course signature and were not yet in this report. `f53afb90` (21:27-22:05 UTC, ~38 min) ran the full redesigned 5-step course end to end and is the primary source below. `5cf4fbb1` (20:36-20:45, ~9 min) is an earlier, isolated run of just the dashboard-push step — fast and clean, no drag worth noting. A third large file, `bc7f531e`, was excluded: it spans Aug 11-Sep 2 and is general dev work, not a course run.
80
+
81
+ ### Latency ledger (`f53afb90`)
82
+
83
+ | Step | Prompt arrival | First payoff | Elapsed (agent-active) | Notable drag |
84
+ |---|---|---|---|---|
85
+ | 1. Dashboard (3 pushes) | 21:27:58 | 21:30:54 | ~3 min | none — clean |
86
+ | 2. Counter app | 21:35:12 | 21:37:52 | ~2.7 min | none — clean |
87
+ | 3. Decision form (standalone + canvas + canvas-wait) | 21:47:34 | 21:57:17 | ~9.7 min | see below |
88
+ | 4. Growing doc | 21:57:47 | 22:01:00 | ~3.2 min | none — clean |
89
+ | 5. Org-export + gist | 22:04:29 | 22:05:55 | ~1.5 min | none — clean |
90
+
91
+ ### Top time sinks
92
+
93
+ 1. **`canvas-wait` foreground block + background promotion (~3.5 min, step 3)**: the session ran `canvas-wait decision` in the foreground, hit the harness's 120s foreground-block ceiling, got silently demoted to a background task, then had to wait for a `task-notification` and poll for the result before it could read the submitted JSON (21:53:44 to 21:57:17). This is the single largest agent-side sink found in this pass — a form-submission confirmation that should read back in a few seconds cost 3.5 minutes of wall clock, all spent waiting on the harness's own polling/promotion mechanics, not on the app or the model.
94
+ 2. **Step-to-step human/driver advance gaps (~4-10 min each)**: gaps of ~4.3 min (step 1→2) and ~9.7 min (step 2→3) between the agent finishing a step and the next step-prompt arriving. Confirmed by an `away_summary` system event at 21:40:55 stating the session was idle, "waiting for you to click it, then say so" — this is pacing/human latency, not agent drag, and matches the same pattern already noted for `25b1a28c` in the original report.
95
+ 3. **Doc-save location re-discovery (~10s, step 5)**: the session had to re-derive that the saved org file landed under `~/.streamweaver/canvas/` instead of the course's assumed `docs/streamweaver_canvas/` path — same known issue as the prior round, but resolved in one quick check this time rather than costing a debug cycle.
96
+
97
+ ### Env-portability risks
98
+
99
+ | Risk | Detail |
100
+ |---|---|
101
+ | Hardcoded personal skill path | Every `browse` invocation ran `B="~/.claude/skills/gstack/browse/dist/browse"` directly via Bash — a personal global skill-install path that would not exist on a coworker's machine even with this repo checked out fresh. |
102
+ | `gh` CLI auth dependency | Step 5 (gist) hard-requires `gh auth status` to already show an authenticated session with gist scope, with no fallback path or check-first framing if it's missing or unauthenticated. |
103
+ | Dynamically-discovered bridge port baked into commands | The bridge bound port 4700 this run; once discovered, curl calls hardcoded that port rather than resolving it per call — fine within one session, but any copy-pasted command from a transcript or doc breaks on a different run/machine where the port differs (consistent with the project's own port-auto-detection memory note). |
104
+
105
+ ### Fixed vs. still-open (against the canned-demo redesign)
106
+
107
+ **Already fixed** (both observed working cleanly in `f53afb90`): the `require 'stream_weaver'` + `.run!` omission from the original report is gone — the session wrote both deliberately and called them out explicitly; port discovery used `lsof` directly instead of the `sleep && cat log` polling loop; the gist step used `gh gist create --public` outright instead of defaulting to secret.
108
+
109
+ **Still open**: the `canvas-wait` foreground-block/background-promotion tax (new finding, not in the original report — worth a course-prompt or harness-level fix, e.g. a shorter poll-and-report pattern instead of a blocking wait); the human/driver step-pacing gaps (framework-level, not prompt-fixable); the hardcoded personal `gstack/browse` path and `gh` auth dependency (both env-portability, need either a repo-relative resolution or an explicit prereq check at course start); the doc-save-location mismatch (already tracked, not yet built).
@@ -0,0 +1,173 @@
1
+ # Rendering StreamWeaver Docs Without Ruby: Browser Extension and npm Package
2
+
3
+ **Date:** 2026-08-07
4
+ **Status:** Spike complete — end-to-end verified, not productized
5
+
6
+ **TL;DR:** A StreamWeaver doc can be compiled from Ruby source and rendered
7
+ entirely in JavaScript, with no Ruby installed — verified end to end at 640ms
8
+ for the 385-line reference PRD, producing output identical to the Ruby-built
9
+ version. One blocker stood in the way and is now solved: **Opal's self-hosted
10
+ parser cannot lex heredocs of any form**, and doc DSL files are full of them.
11
+ A source rewrite (`sw-heredoc-rewrite.js`) clears it. The same artifact serves
12
+ both the browser extension and an npm package — they are one build, not two.
13
+
14
+ ---
15
+
16
+ ## The Realization That Reframes Both
17
+
18
+ A browser extension that renders docs from GitHub has to handle Ruby *source* —
19
+ that's what GitHub serves. Rendering it client-side therefore means **compiling
20
+ Ruby in the browser**, not merely running a precompiled bundle.
21
+
22
+ That is the same requirement an npm package has for anyone without Ruby. So the
23
+ extension and the npm package are not two projects sharing a theme; they are
24
+ one compiled artifact with two wrappers.
25
+
26
+ ```
27
+ .rb source (GitHub raw, local file, stdin)
28
+
29
+
30
+ sw-heredoc-rewrite.js ← required; see below
31
+
32
+
33
+ Opal.compile() (opal-parser.js, self-hosted)
34
+
35
+
36
+ StreamWeaver Opal runtime + Adapter::Static renderers
37
+
38
+ ┌───────────────┴───────────────┐
39
+ ▼ ▼
40
+ browser extension npm package
41
+ (viewer page/side panel) (CLI, CI, VS Code preview)
42
+ ```
43
+
44
+ ---
45
+
46
+ ## Verified
47
+
48
+ Every claim below was executed, not reasoned about.
49
+
50
+ | Claim | Result |
51
+ |---|---|
52
+ | Opal compiles itself to JS | `opal` + `opal-parser` → **2.98MB** bundle |
53
+ | That bundle compiles Ruby in Node, no Ruby installed | `Opal.compile("[1,2,3].map { \|x\| x * 2 }.inspect")` → `[2, 4, 6]` |
54
+ | StreamWeaver runtime + compiler in one bundle | **3.65MB** |
55
+ | A doc compiles and renders in-browser from source | ✅ 640ms for `prd_dsl.rb` |
56
+ | Output matches the Ruby-built version | 12,972 chars, 27 headings, 5 tables, 7 code blocks, 11 TOC links, 3 callouts — identical |
57
+ | Page errors | none |
58
+
59
+ Size: ~3.65MB runtime + 3.5MB mermaid ≈ 7.2MB uncompressed. Unremarkable for an
60
+ extension (loaded from local disk, no download per page) and fine for an npm
61
+ package. Roughly 1–1.5MB gzipped if ever served over a network.
62
+
63
+ ---
64
+
65
+ ## The Blocker: Heredocs
66
+
67
+ `opal-parser.js` **fails on every heredoc form**:
68
+
69
+ ```
70
+ <<~MD → "unterminated string meets end of file"
71
+ <<-MD → same
72
+ <<MD → same
73
+ <<~"MD" → same
74
+ ```
75
+
76
+ Multi-line strings, `%w[]`, and `#{}` interpolation all parse fine — it is
77
+ specific to heredoc lexing. The MRI-hosted compiler handles heredocs correctly,
78
+ so this is a gap in the self-hosted parser only (Opal 1.8.3). Adding `strscan`
79
+ and `racc/parser` to the build does not help; `StringScanner` itself works.
80
+
81
+ This matters because heredocs are how doc DSL files are written —
82
+ `md <<~MD`, `code_block(<<~TXT, lang: "ruby")`, `mermaid <<~MERMAID`. The
83
+ reference PRD has **17 of them**. Without a fix, in-browser compilation fails on
84
+ essentially every real document.
85
+
86
+ ### The fix
87
+
88
+ `lib/stream_weaver/assets/js/sw-heredoc-rewrite.js` rewrites heredocs into
89
+ double-quoted string literals before compilation. Double quotes preserve `#{}`
90
+ interpolation, so the rewrite is transparent for the interpolating forms;
91
+ `<<~'X'` is emitted single-quoted to stay non-interpolating. Squiggly heredocs
92
+ get their common indentation stripped, matching Ruby's semantics.
93
+
94
+ Verified against: plain squiggly, heredoc-as-argument with a following keyword
95
+ argument, interpolation, embedded double quotes, and the full 17-heredoc PRD.
96
+
97
+ **Known limits.** One heredoc per line; multiple heredocs opened on a single
98
+ line are not handled. Nested heredocs are not handled. Neither appears in
99
+ StreamWeaver doc files, but both would need work before this is a general Ruby
100
+ tool. It is a targeted preprocessor, not a Ruby parser.
101
+
102
+ ---
103
+
104
+ ## Two Products, One Artifact
105
+
106
+ ### Browser extension
107
+
108
+ Fetch raw `.rb` → rewrite → compile → render in an extension page or side panel.
109
+ Per the earlier analysis, an extension *page* rather than in-page injection
110
+ avoids both style collision with GitHub and the DOM-churn maintenance tax.
111
+
112
+ Remote **script** is what MV3 forbids, which is why mermaid and Prism had to be
113
+ bundled. Remote stylesheets and fonts are not restricted by the default policy,
114
+ so Google Fonts is fine — worth an opt-out only for offline or privacy reasons.
115
+
116
+ ### npm package
117
+
118
+ The same bundle plus a thin API:
119
+
120
+ ```js
121
+ const { render } = require('@streamweaver/render')
122
+ const html = render(fs.readFileSync('doc.sw.rb', 'utf8'))
123
+ ```
124
+
125
+ Uses that don't involve GitHub at all:
126
+
127
+ - `npx streamweaver-render doc.rb > doc.html` for people with no Ruby
128
+ - Static site generation and CI, without adding Ruby to the image
129
+ - A VS Code preview pane
130
+ - Any JS toolchain that wants StreamWeaver docs as HTML
131
+
132
+ **One caveat for the Node path.** The Opal runtime's render pass builds an HTML
133
+ string without touching the DOM, but `OpalBridge` and `patch_dom` do. A Node
134
+ renderer therefore needs either jsdom or a render-to-string entry point that
135
+ skips the bridge. The string path exists inside the runtime already
136
+ (`OpalRuntime#render_html`); it just is not exposed. That is the main piece of
137
+ work between this spike and a shippable npm package.
138
+
139
+ ---
140
+
141
+ ## Recommended Order
142
+
143
+ 1. **Expose render-to-string** in the Opal runtime, bypassing the bridge. Small,
144
+ and it unblocks the entire Node path.
145
+ 2. **npm package** wrapping compiler + runtime + assets + rewriter. Testable in
146
+ CI, no browser or store review involved.
147
+ 3. **Browser extension** on top of the same package. Now the only new work is
148
+ the GitHub URL detection and the viewer page.
149
+
150
+ Doing the npm package first means the extension inherits something already
151
+ proven, and gets a test surface that does not require a browser.
152
+
153
+ ---
154
+
155
+ ## Open Questions
156
+
157
+ 1. **Upstream the heredoc gap?** Worth an Opal issue — a self-hosted parser that
158
+ cannot lex heredocs is a general limitation, not a StreamWeaver one. If it is
159
+ fixed upstream, the rewriter can be deleted.
160
+ 2. **Is `ruby.wasm` a better long-term base?** `@ruby/wasm-wasi` runs real CRuby,
161
+ so no parser gaps at all — but it is 10–30MB and would need the gem's server
162
+ dependencies stubbed. Worth revisiting if the parser gap widens.
163
+ 3. **Which file extension marks a StreamWeaver doc?** The extension needs to
164
+ decide what to render. `.sw.rb` is unambiguous; plain `.rb` under
165
+ `docs/streamweaver_canvas/` is convention-based and would misfire elsewhere.
166
+ 4. **Interactive components in a viewer.** Buttons and inputs work in Opal, but a
167
+ doc viewer probably wants them inert. Worth deciding whether the viewer
168
+ renders read-only.
169
+
170
+ ---
171
+
172
+ *Spike verified against Opal 1.8.3, Ruby 3.3.6, Chromium (headless) with all
173
+ HTTP blocked. Reference document: `examples/components/prd_dsl.rb`.*