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,139 @@
1
+ # StreamWeaver Market Positioning Research
2
+
3
+ Researched 2026-07-02. Three topics: (1) the Anthropic/industry shift from Markdown to HTML for agent output, (2) charm-ruby.dev site anatomy as a docs-site template, (3) pareto video-production stack for solo-dev OSS demos.
4
+
5
+ ---
6
+
7
+ ## 1. Anthropic's move from Markdown to HTML for agent output
8
+
9
+ ### The core event: Thariq Shihipar's post, official Anthropic blog, May 2026
10
+
11
+ The load-bearing artifact for StreamWeaver's positioning is a post published **on Anthropic's own blog** (not just a personal take): "Using Claude Code: The Unreasonable Effectiveness of HTML," by **Thariq Shihipar, member of technical staff on the Claude Code team**.
12
+
13
+ - Official post: https://claude.com/blog/using-claude-code-the-unreasonable-effectiveness-of-html
14
+ - Companion gallery (20 self-contained HTML files generated by Claude Code for common use cases): https://thariqs.github.io/html-effectiveness/
15
+ - Companion GitHub repo: https://github.com/anthropics/html-effectiveness
16
+ - Simon Willison's amplification/commentary (useful as an independent, widely-read second source): https://simonwillison.net/2026/May/8/unreasonable-effectiveness-of-html/
17
+ - InfoQ coverage: https://www.infoq.com/news/2026/06/anthropic-html-markdown-agent/
18
+
19
+ **Exact quotes from Shihipar (via Anthropic's blog):**
20
+
21
+ > "HTML can convey much richer information compared to Markdown."
22
+
23
+ > "there is almost no set of information that Claude can read that you cannot efficiently represent with HTML"
24
+
25
+ > "I tend to not actually read more than a 100-line Markdown file"
26
+
27
+ > "Markdown files are fairly hard to share since most browsers do not render them natively well" / "The chance of someone actually reading your spec, report, or PR writeup is much higher if it's in HTML"
28
+
29
+ > "HTML can also allow you to interact with the document"
30
+
31
+ > "As agents have become more and more powerful, I've found that Markdown has become an increasingly restrictive format." (via InfoQ)
32
+
33
+ > "I've started preferring HTML as an output format instead of Markdown and increasingly see this pattern being applied by others on the Claude Code team." (via InfoQ)
34
+
35
+ **The token-cost admission — this is the key line for StreamWeaver's pitch.** Shihipar does NOT deny the token cost; he concedes it and argues it's worth paying because Anthropic's context windows have grown large enough to absorb it:
36
+
37
+ > "While Markdown often uses fewer tokens, I've found that the added expressiveness of HTML and the much higher likelihood of me reading it means I get overall better output."
38
+
39
+ He justifies this by pointing to Opus 4.7's 1M-token context window, saying the extra usage "is not really noticeable in the context window." No Anthropic-sourced dollar or exact-multiplier figure was found in the official post itself.
40
+
41
+ **The specific multiplier claim (secondary source, not Anthropic-official, but widely repeated and directly quotable for a "10x" framing):**
42
+
43
+ Independent commentator Tom Crawshaw (via theaiarchitects.com), citing his own week of side-by-side client testing, put concrete numbers on it:
44
+
45
+ > Markdown: "~250 tokens per 1,000 words"
46
+ > HTML: "~1,500 tokens per 1,000 words"
47
+ > "This represents a 4 to 8x increase" for HTML, with generation time also 2-4x longer (30s vs 90-120s for a 1,000-word doc).
48
+
49
+ This is the closest thing in the wild to a citable "HTML costs ~5-8x more tokens than Markdown" data point. It's not an Anthropic-published number, so it should be cited as "industry practitioner benchmark" rather than attributed to Anthropic. It undershoots "10x" slightly (4-8x) — worth noting if StreamWeaver's own "10x" claim needs to be defensible; StreamWeaver's own benchmarking (README/docs) should be the primary source for that number, with this as corroborating context.
50
+
51
+ Additional overview article worth linking for more color: https://theaiarchitects.com/blog/markdown-vs-html-claude-code — argues HTML wins "for anything you send to a human — audits, reports, plans, pricing models, dashboards" while Markdown "still wins for chained agents, git repos, short outputs, and anything inside the engineering loop." This is a useful frame for StreamWeaver: it's positioned squarely in the "human-facing agent output" zone this article says HTML wins, but at Markdown-like cost.
52
+
53
+ ### The broader trend: MCP Apps (the interactive-UI-over-MCP standard)
54
+
55
+ This is the more important structural signal for positioning StreamWeaver as infrastructure, not just a nice-to-have. MCP Apps is now a **ratified, joint Anthropic/OpenAI/MCP-UI standard** — evidence that "rich HTML output from agents" is becoming protocol-level, not a one-off blog opinion.
56
+
57
+ - Official spec announcement: https://blog.modelcontextprotocol.io/posts/2026-01-26-mcp-apps/
58
+ - Original MCP-UI proposal (Nov 2025): https://blog.modelcontextprotocol.io/posts/2025-11-21-mcp-apps/
59
+ - Spec overview docs: https://modelcontextprotocol.io/extensions/apps/overview
60
+ - MCP-UI open-source SDK (predecessor project, still active): https://github.com/MCP-UI-Org/mcp-ui
61
+ - Shopify engineering writeup, good "why" framing: https://shopify.engineering/mcp-ui-breaking-the-text-wall
62
+ - WorkOS technical deep dive: https://workos.com/blog/mcp-ui-a-technical-deep-dive-into-interactive-agent-interfaces
63
+
64
+ Key facts:
65
+ - "MCP Apps is the first official MCP extension... with clients like ChatGPT, Claude, Goose and Visual Studio Code having shipped support."
66
+ - Built on "the amazing work of MCP-UI and the OpenAI Apps SDK, with Anthropic, OpenAI, and MCP-UI collaborating to create a shared open standard."
67
+ - Architecture: a `ui://` URI scheme for declaring UI resources tied to tool calls, rendered in a **sandboxed iframe**, with bi-directional JSON-RPC communication between the UI and host.
68
+ - Use-case framing directly usable in StreamWeaver marketing: "a user asks 'show me sales by region,' an MCP App can render an interactive map where users click regions to drill down, hover for details, and toggle between metrics, all without additional prompts."
69
+
70
+ ### Competitor landscape: OpenAI and Google equivalents
71
+
72
+ - **OpenAI Canvas**: https://openai.com/index/introducing-canvas/ — originally a separate collaborative window for writing/coding; per Help Center notes, canvas functionality has since been folded back into inline chat "writing blocks and code blocks" in GPT-5.5, and OpenAI's separate visual **Agent Builder** (drag-and-drop agent canvas) is being wound down (sunset from Nov 30, 2026). Net: OpenAI's dedicated canvas-as-a-surface bet has been partially reversed/absorbed, which is a mild point in favor of "rich inline HTML output" (StreamWeaver's model) over "separate canvas app" as the durable pattern.
73
+ - **Google A2UI**: https://developers.googleblog.com/introducing-a2ui-an-open-project-for-agent-driven-interfaces/ — open project for agent-generated, cross-platform UI, explicitly framed around agents choosing "the interface which best suits the current conversation."
74
+ - **Google Stitch** (I/O 2026): AI-native UI design canvas, infinite canvas + design agent + interactive prototypes — more a design tool than an agent-output format, but shows the same directional bet on rich, generated interactive surfaces. Coverage: https://www.animaapp.com/blog/ai-en/google-io-2026/
75
+
76
+ ### Positioning synthesis for StreamWeaver
77
+
78
+ The strongest version of the pitch: *"Anthropic's own Claude Code lead just admitted, on Anthropic's blog, that HTML is 4-8x (practitioner-measured) more token-expensive than Markdown — and said it's worth it anyway because rich output keeps humans engaged. MCP Apps just standardized interactive HTML-in-iframe as the agent-to-human interface layer across Anthropic, OpenAI, and the broader ecosystem. StreamWeaver lets you get that same richness at Markdown's token cost, from a Ruby DSL, without hand-authoring HTML/CSS/JS or wiring an iframe sandbox."* That's a real, citable "expensive default vs. our product" tension — the Crawshaw 4-8x number is the number to lead with (attributed as an independent benchmark, not an Anthropic claim), with Shihipar's Anthropic-official quotes establishing that the *trend itself* is real and blessed by Anthropic, not fringe.
79
+
80
+ ---
81
+
82
+ ## 2. charm-ruby.dev site anatomy
83
+
84
+ Fetched: https://charm-ruby.dev/
85
+
86
+ **Hero & message**: Large ASCII-art wordmark spelling "CHARM" up top, tagline about making "the Ruby command line glamorous" — building "glamorous TUIs, style terminal output, create beautiful forms, and make your Ruby CLIs sparkle." The whole site commits to one adjective ("glamorous") and repeats it as the aesthetic thesis.
87
+
88
+ **Navigation** (4 items only): Libraries, Get Started, Examples, GitHub link. Extremely shallow IA — no mega-menu, no deep category tree. Page flow is strictly top-down: hero → install options → library showcase → getting-started guide → code examples.
89
+
90
+ **Progressive disclosure pattern**: Users are walked from zero-commitment overview → install → concrete library cards → runnable examples. Nobody has to understand the whole gem family before doing anything; each section commits you a little further.
91
+
92
+ **How it sells the gems**:
93
+ - Install as one metagem (`charm`) or piecemeal per-library — lowers commitment friction for a first try.
94
+ - Library cards grouped by function (Build / Style / Interactive Tools) rather than alphabetically — organizes by *what you're trying to do*, not by gem name.
95
+ - Each card: emoji + one-line description + gem name + links out to GitHub/RubyGems. No walls of prose.
96
+ - Five examples of increasing complexity — "show, don't tell": the persuasion mechanism is working code, not marketing copy.
97
+
98
+ **Tech/design signals**: Reads as a static site generator (Jekyll-like — clean semantic HTML, markdown-friendly content, no visible JS framework fingerprints). Minimalist palette anchored on a primary purple (`#7D56F4`) with a status-red accent (`#FF6B6B`) pulled straight from example code output — i.e., the brand color *is* the terminal-styling color, reinforcing the product. Monospace type for all code, ASCII art as the only "decorative" element. Minimal interactivity in the page itself — the *gems* are the interactive layer; the site just demonstrates them in static code blocks.
99
+
100
+ **Direct applicability to StreamWeaver**: same playbook — pick a metagem install path, group components by "what you're building" (not alphabetically), lead with runnable code over prose, and let the brand color be literally a color StreamWeaver itself can render (since StreamWeaver *is* the output surface, this is even more natural than for charm-ruby, where the terminal only echoes ANSI escapes).
101
+
102
+ ### Other reference: dry-rb / Hanakai
103
+
104
+ Note: dry-rb.org now 301-redirects to https://hanakai.org/dry — as of May 2026, dry-rb + Hanami + rom-rb merged their docs under a unified "Hanakai" umbrella. That site uses genuine progressive disclosure: a hovering top nav revealing project categories, then drilling into docs; gems organized thematically (Business Logic / Foundations / Architecture) rather than alphabetically; hero copy is a single flat claim — "Ruby gems for better code" — plus one supporting line about composability. Worth citing as a second data point for "small-OSS-family site" conventions: shallow top nav + thematic (not alphabetical) grouping + one-sentence hero + Learn/Blog/Community as separate top-level sections once the project matures past a single-page pitch. No Astro/Starlight-specific ruby-gem examples turned up in search; the Jekyll/plain-static-site pattern (as used by both charm-ruby.dev and apparently Hanakai) seems to be the dominant convention in this space rather than Starlight.
105
+
106
+ ---
107
+
108
+ ## 3. Pareto video stack for solo-dev OSS demos (2026)
109
+
110
+ Recommended stack, cheapest effort first:
111
+
112
+ | Need | Tool | Why |
113
+ |---|---|---|
114
+ | Terminal/CLI demo | VHS (charmbracelet) | Scripted `.tape` files -> deterministic, reproducible GIF/MP4/WebM. No manual screen recording, no retakes. |
115
+ | Browser/UI demo | Playwright v1.59 screencast API | Agent drives the browser itself; new screencast API supports chapter markers, mid-test start/stop, and is explicitly designed for coding agents to produce "video receipts." |
116
+ | Voiceover | ElevenLabs | Best-in-class synthetic voice in 2026 (listeners pick it ~7/10 times over next-best in blind tests); generate MP3/WAV separately. |
117
+ | Assembly/editing | Descript | Text-based editing (edit the transcript, video follows), integrated screen recording, filler-word removal — glue layer between raw captures and a finished video. |
118
+ | Fancier programmatic marketing video (optional, higher effort) | Remotion | Videos as React components — good for a polished launch trailer, data-driven, but adds a whole frontend build; save for v2, not first drop. |
119
+
120
+ **Recommended minimum-effort path for a rubygem launch:**
121
+ 1. Script the CLI/terminal parts of the demo as a VHS `.tape` file → GIF for the README, MP4 for a landing page hero. https://github.com/charmbracelet/vhs
122
+ 2. If StreamWeaver's differentiator is *browser-rendered rich output* (which it is), drive the browser with Playwright's screencast API to capture the actual rendered HTML output live, rather than screenshotting — this directly demonstrates the "markdown-cost, HTML-richness" claim in motion. https://playwright.dev/agent-cli/commands/video-recording
123
+ 3. Generate a short voiceover script, synthesize in ElevenLabs, drop the audio track under the VHS/Playwright footage.
124
+ 4. Assemble/trim in Descript (text-based editing means no timeline fiddling) and export.
125
+ 5. Skip Remotion and asciinema/agg for v1 — they're valid alternatives (agg is a fine VHS substitute if you want SVG output for smaller file size: https://github.com/asciinema/agg) but add tooling surface without adding capability StreamWeaver doesn't already need from VHS + Playwright.
126
+
127
+ This whole pipeline is scriptable end-to-end (VHS tapes, Playwright scripts, ElevenLabs API calls are all automatable), which matters for a repo where Claude Code is already doing agentic development — the demo-video generation step can itself become a repeatable `bin/` task rather than a one-off manual recording session.
128
+
129
+ Sources for this section:
130
+ - https://github.com/charmbracelet/vhs
131
+ - https://playwright.dev/agent-cli/commands/video-recording
132
+ - https://playwright.dev/docs/videos
133
+ - https://purpleowl.io/blog/we-automated-our-product-walkthrough-video-the-whole-thing
134
+ - https://elevenlabs.io/blog/elevenlabs-vs-descript
135
+ - https://getstacksmart.com/blog/elevenlabs-vs-descript-2026
136
+ - https://www.remotion.dev/
137
+ - https://github.com/remotion-dev/remotion
138
+ - https://github.com/asciinema/agg
139
+ - https://docs.asciinema.org/manual/agg/
@@ -0,0 +1,270 @@
1
+ # Production-Readiness Patterns: Precedent Survey
2
+
3
+ Research for the StreamWeaver "production-ready 20%" strategy. StreamWeaver
4
+ (this repo) grew from quick helper-UI
5
+ scripts into full web apps. The owner wants Rails-grade production mechanisms
6
+ *without* building Rails — the minimal proven mechanisms that give 80% of
7
+ production readiness for 20% of the engineering cost.
8
+
9
+ Known pain points driving this research:
10
+
11
+ - **(a)** State lived in session cookies until they overflowed the 4KB cookie limit.
12
+ - **(b)** Multi-app-in-one-server (`streamweaver app.rb` service mode) produces
13
+ hash-based URLs instead of human-readable ones.
14
+ - **(c)** Users occasionally hit a wall and rewrite their app in raw Sinatra.
15
+
16
+ Five topics below. Each ends with a proven-pattern table and a recommendation.
17
+
18
+ ---
19
+
20
+ ## 1. Server-side state/session
21
+
22
+ **The scaling story, in order:** cookie session (client-side, ~4KB cap) →
23
+ in-memory server-side session (`Rack::Session::Pool`, single-process only) →
24
+ external store (Redis/Memcached, the traditional answer) → **database-backed
25
+ store on the app's existing DB** (the 2025-26 answer, popularized by Rails 8's
26
+ "Solid" trio — Solid Cache, Solid Queue, Solid Cable — explicitly built to
27
+ avoid requiring Redis as extra infrastructure).
28
+
29
+ **Streamlit** hits this exact wall: `st.session_state` is in-process/in-memory
30
+ per browser session by default, which is explicitly documented as
31
+ insufficient once you have multiple users or server restarts/multiple nodes.
32
+ Streamlit's own docs point users to Redis or to hand-rolling a
33
+ disk/EFS-backed session-key scheme — i.e., Streamlit does *not* solve this for
34
+ you; it punts to the deployer. That's the trap StreamWeaver should avoid
35
+ repeating.
36
+
37
+ **Sinatra/Rack apps** graduate off `enable :sessions` (cookie-based) to
38
+ `use Rack::Session::Pool` (server-side, in-memory hash — good for single
39
+ process, breaks under multiple workers/dynos) or plug in **moneta**, a gem
40
+ that provides one unified key-value interface over ~20 backends (Memory,
41
+ SQLite3, PStore/YAML file, Redis, Memcached, ActiveRecord, Sequel, etc.) and
42
+ ships a `Rack::MonetaStore` middleware plus direct Sinatra/Rails integration.
43
+ Moneta is the "swap the backend without rewriting the app" abstraction layer
44
+ — exactly the pattern needed for a gem that has to work identically for a
45
+ solo dev (file-backed) and a small team (DB-backed).
46
+
47
+ **Rails 8's Solid trio** is the strongest recent precedent for the philosophy
48
+ itself: default to the database you already have (SQLite is explicitly fine
49
+ — "SSDs are only marginally slower than RAM for reads, at a fraction of the
50
+ cost and complexity of running Redis"). Basecamp/HEY run this in production.
51
+ The win isn't the specific gem, it's the stance: **zero extra infrastructure
52
+ by default, pluggable up to Redis only if you need it.**
53
+
54
+ | Pattern | Project | Adopt/Adapt/Skip |
55
+ |---|---|---|
56
+ | In-memory session (works until multi-process/restart) | Rack::Session::Pool, current StreamWeaver | Skip as the *only* option — keep as dev-mode default |
57
+ | Unified key-value store abstraction (memory/SQLite/file/Redis, swappable) | moneta gem | **Adapt** — thin StreamWeaver-native abstraction over SQLite-first, Redis-optional |
58
+ | DB-backed session/cache using an embedded DB, no external service required by default | Rails 8 Solid Cache/Solid Queue philosophy | **Adopt the philosophy** — SQLite-backed session store as the 1.0 default |
59
+ | Punt state scaling to the deployer (EFS/Redis, docs-only) | Streamlit | Skip — this is the exact pain point already reported |
60
+
61
+ **Recommendation:** Ship a `StreamWeaver::SessionStore` interface with two
62
+ built-in backends: in-memory (today's default, fine for `ruby app.rb`
63
+ single-process) and **SQLite-backed** (default for `streamweaver` service
64
+ mode and anywhere `STREAMWEAVER_HOST=0.0.0.0`). No Redis dependency in core;
65
+ document a Redis/moneta adapter as an escape hatch for teams that already run
66
+ Redis.
67
+
68
+ ---
69
+
70
+ ## 2. App naming/routing (human-readable URLs for multi-app service mode)
71
+
72
+ The web has few frameworks solving "many small apps, one server, friendly
73
+ URLs" as a first-class feature — most precedent comes from tunnel/PaaS
74
+ products (ngrok, Heroku-style) and from Rails engine mounting, not from
75
+ peer micro-frameworks.
76
+
77
+ - **ngrok**: paid tiers let you claim a human-readable static subdomain
78
+ (`myapp.ngrok.app`) per tunnel; multiple local services get separate named
79
+ tunnels via a config file, each mapped 1:1 to a name. Wildcard domains
80
+ (route-by-subdomain to one process) require the enterprise tier — i.e. even
81
+ ngrok treats "one name = one process" as the easy case and "one process,
82
+ many subpath names" as the harder, less-supported case.
83
+ - **Puma-dev** (already used by StreamWeaver, see README puma-dev mode):
84
+ solves this locally for free — `puma-dev link` in a directory gives you
85
+ `http://<dirname>.test` with zero config, auto-starts on first request. This
86
+ is the actual precedent StreamWeaver already leans on, and it's the right
87
+ shape: **name = directory/app name, not a hash.**
88
+ - `puma-dev` doesn't do subpath routing at all — it's one name per process,
89
+ which sidesteps the routing problem instead of solving it.
90
+ - **Rack::Builder / Sinatra modular mounting**: `run Rack::Builder.new { use
91
+ App1; use App2 }` mounts multiple Sinatra apps in one process, but every
92
+ request passes through every app's before-filters until one claims it —
93
+ a real cost at scale, and the community's own conclusion (via projects like
94
+ `sinatra-router`) is that a dedicated slug-based router in front of
95
+ `Rack::Builder` is worth adding rather than relying on middleware stacking.
96
+
97
+ | Pattern | Project | Adopt/Adapt/Skip |
98
+ |---|---|---|
99
+ | Named local domain, zero config, one process | Puma-dev (already integrated) | **Adopt more** — make service-mode registration name-first, not hash-first |
100
+ | Named subdomain per tunnel/app | ngrok | Adapt the naming *convention* (slug from app name/directory), not the tunnel infra |
101
+ | Subpath router in front of Rack::Builder to avoid middleware-stacking cost | sinatra-router pattern | **Adopt** — give service mode a slug router: `/apps/<slug>` derived from `app "Name"` title or explicit `slug:` option |
102
+
103
+ **Recommendation:** In service mode, derive `/apps/<slug>` from the app's
104
+ declared name (slugified) or an explicit `app "Meeting Notes", slug: "notes"`
105
+ option, replacing the current hash. Keep Puma-dev mode as the "one app, one
106
+ memorable domain" path for users who want that instead of subpaths — don't
107
+ force a single routing model.
108
+
109
+ ---
110
+
111
+ ## 3. Escape hatches (never rewrite in Sinatra)
112
+
113
+ Every framework in this class has learned the same lesson: users will hit a
114
+ wall the DSL doesn't cover, and if there's no sanctioned drop-to-raw path they
115
+ leave the framework entirely. The mature answer is always **a small, explicit
116
+ API for "give me a raw request/response here," not a philosophy of covering
117
+ every case in the DSL.**
118
+
119
+ - **Streamlit**: `st.components.v1.html()` / `.iframe()` for one-way raw
120
+ HTML/embed, and a full **Components API** (bidirectional, JS↔Python) for
121
+ cases needing two-way data — explicitly documented as "start with html/
122
+ iframe first, only reach for the bidirectional Components template if you
123
+ need callbacks back into Python." This graduated-complexity offering (raw
124
+ HTML → iframe → full bidirectional component) is a strong minimal template.
125
+ - **Phoenix LiveView**: `Hooks` — `phx-hook="MyHook"` lets you write arbitrary
126
+ client JS and have LiveView guarantee it's wired up/cleaned up on
127
+ mount/update/destroy, without leaving the LiveView model. Router-level,
128
+ LiveView routes coexist with plain controller routes in the same router —
129
+ users aren't forced to choose one paradigm for the whole app.
130
+ - **Hotwire/Turbo**: Turbo Frames/Streams degrade gracefully to normal
131
+ full-page navigation, and standard Rails routes/controllers sit right next
132
+ to Turbo-powered ones — there's no separate "Hotwire app" vs "Rails app,"
133
+ it's additive.
134
+ - **Rack ecosystem generally**: any Sinatra/Roda/Rails app can `mount` a bare
135
+ Rack app or define a raw route block that skips all the DSL and just
136
+ returns `[status, headers, body]` — the universal Ruby web escape hatch.
137
+
138
+ | Pattern | Project | Adopt/Adapt/Skip |
139
+ |---|---|---|
140
+ | Raw HTML/iframe embed, no callback wiring | Streamlit `components.v1.html/iframe` | **Adopt** — `raw_html { }` / iframe component, already low-cost to add |
141
+ | Full bidirectional custom component (JS↔server), offered as escalation, not the default path | Streamlit Components API | **Adapt** — a `custom_component` hook for canvas/push mode only, document as "advanced" |
142
+ | Client-side hook wired to component lifecycle without leaving the framework's data model | Phoenix LiveView `Hooks` | **Adapt** — a `js_hook:` option on any component for custom client behavior |
143
+ | Raw Rack route block coexisting with DSL routes in the same app | Sinatra/Roda/Rails universal pattern | **Adopt** — `route "/webhook", methods: [:post] { |req| ... }` escape hatch returning a raw Rack triple, so users never need a second Sinatra process |
144
+
145
+ **Recommendation:** Ship two escape hatches, both small: (1) a `raw_html`/
146
+ `raw` component for arbitrary markup (Streamlit's html/iframe tier), and (2) a
147
+ `route` DSL method that registers a raw Rack endpoint inside the same app/
148
+ server (the universal Sinatra/Roda pattern) — covering webhooks, custom JSON
149
+ APIs, and file downloads without a second process. Skip building a full
150
+ bidirectional custom-component protocol until there's evidence of real demand
151
+ past (1) and (2); that's the expensive tier Streamlit only added because
152
+ "just embed HTML" wasn't enough for chart libraries specifically.
153
+
154
+ ---
155
+
156
+ ## 4. 1.0 gem production checklist
157
+
158
+ There isn't a single canonical 2026 checklist, but the RubyGems guides plus
159
+ current framework practice converge on the same short list:
160
+
161
+ - **Semver**: RubyGems' own guidance is to follow semantic versioning
162
+ strictly from the first 1.0 tag — breaking changes bump major, and the gem
163
+ should declare a `required_ruby_version` in the gemspec rather than assume.
164
+ - **Ruby version support**: as of 2026, Ruby 3.2 is EOL and 3.3 is
165
+ security-only; a credible 1.0 targets 3.3 (min, if backward compat
166
+ matters) through 3.4/4.0, tested via CI matrix (GitHub Actions with a Ruby
167
+ version matrix is the de facto standard across Sinatra/Roda/Rails-family
168
+ gems).
169
+ - **Security posture — bind address**: both Rails-adjacent minimal frameworks
170
+ (Roda, Hanami) and StreamWeaver's own README already get this right in
171
+ principle — default to `127.0.0.1`, require an explicit opt-in
172
+ (`STREAMWEAVER_HOST=0.0.0.0`) for LAN/Tailscale exposure. That's the
173
+ correct minimal default and should be called out explicitly as a security
174
+ feature in 1.0 docs, not just a config knob.
175
+ - **CSRF**: Roda ships a `route_csrf` plugin (opt-in, per-route tokens) and
176
+ Hanami bakes CSRF + secure-by-default headers (CSP, X-Frame-Options) into
177
+ the framework itself rather than leaving it to the app. StreamWeaver's
178
+ auto-submit form components are exactly the surface that needs CSRF
179
+ protection once apps are bound beyond localhost — currently likely
180
+ unprotected given the localhost-only original design assumption.
181
+ - **Auth hook for exposing beyond localhost**: neither Roda nor Hanami forces
182
+ a specific auth scheme; both expose a middleware slot. The minimal
183
+ equivalent for StreamWeaver is a documented Rack middleware insertion point
184
+ (e.g. HTTP Basic Auth via `Rack::Auth::Basic`) that's trivial to enable
185
+ when `STREAMWEAVER_HOST` isn't localhost.
186
+ - **Deployment story**: Kamal 2 is now the default Rails answer for
187
+ "containerize and deploy to any VPS with zero-downtime," and it works for
188
+ any Dockerized Ruby app, not just Rails — a Kamal-friendly `Dockerfile` +
189
+ short "deploy with Kamal" doc is more credible in 2026 than a bespoke
190
+ deploy guide. Puma-dev (already used) covers the "run persistently on my
191
+ own machine" case well.
192
+
193
+ | Pattern | Project | Adopt/Adapt/Skip |
194
+ |---|---|---|
195
+ | Semver from 1.0, `required_ruby_version` in gemspec, GH Actions Ruby matrix (3.3–3.4/4.0) | RubyGems guides, Rails-family gems | **Adopt** — mechanical, low cost |
196
+ | Bind to `127.0.0.1` by default, explicit opt-in for `0.0.0.0` | Roda/Hanami convention; StreamWeaver already does this | **Adopt & document as a security feature**, not just a config default |
197
+ | Built-in CSRF plugin, opt-in but one-liner | Roda `route_csrf` | **Adapt** — wire CSRF token into StreamWeaver's existing auto-submit forms, on by default once host != localhost |
198
+ | Secure-by-default headers (CSP, X-Frame-Options) baked into framework | Hanami | **Adapt** — ship as default middleware, overridable |
199
+ | Documented middleware slot for auth (Basic Auth, etc.), not a built auth system | Roda/Hanami middleware pattern | **Adopt** — one doc page + example, don't build auth |
200
+ | Kamal-ready Dockerfile + deploy doc | Rails 8 default | **Adapt** — one example Dockerfile in `examples/`, not a StreamWeaver-specific deploy tool |
201
+
202
+ **Recommendation:** The 1.0 checklist is mostly *documentation and defaults*,
203
+ not new engineering: lock semver + Ruby CI matrix, keep localhost-default
204
+ bind but frame it explicitly as security posture, add opt-in CSRF for
205
+ auto-submit forms, and ship one example Dockerfile for Kamal deploys. Skip
206
+ building any bespoke auth system — a middleware insertion point plus a
207
+ Basic Auth example is the whole scope.
208
+
209
+ ---
210
+
211
+ ## 5. Real-time push at production quality (SSE/WebSocket robustness)
212
+
213
+ StreamWeaver's canvas push mode is SSE-based (per the bridge/session files in
214
+ the repo). The precedent split is stark: **LiveView-style (stateful,
215
+ WebSocket, server holds a process per client) vs. Datastar-style (stateless,
216
+ SSE, server recomputes from state on each push)** — and the failure modes are
217
+ different for each.
218
+
219
+ - **LiveView's known weak spot**: when the WebSocket drops, the server-side
220
+ GenServer holding that session's state dies with it — everything in
221
+ `assigns` is gone on reconnect unless it was persisted somewhere durable.
222
+ The documented fix is architectural discipline: treat in-memory state as a
223
+ disposable cache and always keep a "recipe" (DB row, URL params) to
224
+ reconstruct it, never treat process memory as the source of truth.
225
+ - **SSE reconnection (the protocol StreamWeaver already uses)**: the
226
+ browser's native `EventSource` auto-reconnects, but correctness requires
227
+ the server to (1) send a `retry:` field to control backoff, and (2) assign
228
+ a monotonic `id:` to every event so a reconnecting client's `Last-Event-ID`
229
+ header lets the server replay only what was missed. Skipping `id:` is
230
+ called out repeatedly as the most common silent bug — reconnection appears
231
+ to work in dev (small gaps) and silently drops events under real network
232
+ interruption.
233
+ - **Backpressure**: the consistent production pattern (Ktor/Kotlin Flow
234
+ examples, Phoenix GenServer mailbox examples) is that the transport must
235
+ apply backpressure by suspending/queuing at the producer when a client
236
+ can't keep up, rather than unbounded buffering — for StreamWeaver's push
237
+ model (owner explicitly pushes content, not a firehose) this is lower risk
238
+ than a chat/metrics use case, but batching rapid successive pushes to the
239
+ same session is the cheap defensive move.
240
+ - **Datastar's stance** (SSE + hypermedia, no WebSocket, no per-client
241
+ process) is the closest architectural cousin to StreamWeaver's canvas
242
+ push — worth reading as validation that SSE-only is a legitimate production
243
+ choice, not just "the easy version," as long as event IDs + replay are
244
+ handled.
245
+
246
+ | Pattern | Project | Adopt/Adapt/Skip |
247
+ |---|---|---|
248
+ | Monotonic `id:` on every SSE event + honor `Last-Event-ID` on reconnect to replay only missed events | SSE spec / general production guidance | **Adopt** — likely the single highest-leverage fix; check if `bridge_server.rb`/`protocol.rb` already assign event IDs |
249
+ | `retry:` field to control client backoff | SSE spec | **Adopt** — one-line addition |
250
+ | Never treat in-process state as sole source of truth; keep a durable "recipe" to rebuild on reconnect | Phoenix LiveView lesson-learned | **Adapt** — pairs directly with topic 1's session-store recommendation; canvas session state should live in the same SQLite-backed store, not only in the bridge process |
251
+ | Batch/coalesce rapid successive pushes per session rather than unbounded queuing | Ktor/GenServer backpressure pattern | **Adapt** — cheap given StreamWeaver's push is owner-driven, not high-frequency |
252
+ | Stateless SSE + hypermedia as a legitimate alternative to stateful WebSocket | Datastar | Reference/validate current approach — no change needed, just confirms SSE-only is production-credible |
253
+
254
+ **Recommendation:** Audit `lib/stream_weaver/canvas/protocol.rb` and
255
+ `bridge_server.rb` for event IDs and `Last-Event-ID` handling — if absent,
256
+ that's the top-priority fix for push-mode robustness. Add a `retry:` field.
257
+ Tie canvas session state to the same durable session store from topic 1 so a
258
+ bridge restart doesn't lose in-flight canvas content.
259
+
260
+ ---
261
+
262
+ ## Summary Table
263
+
264
+ | # | Topic | Adopt | Adapt | Skip |
265
+ |---|---|---|---|---|
266
+ | 1 | State/session | DB-backed-by-default philosophy (Rails 8 Solid) | moneta-style pluggable backend abstraction, SQLite default | Redis-required, EFS-style punt (Streamlit's gap) |
267
+ | 2 | App naming/routing | Slug-based `/apps/<name>` router | ngrok's naming convention (not its infra) | Hash-based mounting, wildcard-domain routing |
268
+ | 3 | Escape hatches | Raw HTML/iframe tier, raw Rack `route` block | LiveView-style `js_hook:` for client behavior | Full bidirectional custom-component protocol (until demand proven) |
269
+ | 4 | 1.0 checklist | Semver + Ruby CI matrix, localhost-default bind, middleware auth slot, Kamal-ready Dockerfile | Roda's opt-in CSRF, Hanami's secure headers-by-default | Building a bespoke auth system |
270
+ | 5 | Real-time push | SSE `id:`/`Last-Event-ID` replay, `retry:` field | Durable session-backed canvas state, push coalescing | Switching to WebSocket/per-client process model |