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,274 @@
1
+ # StreamWeaver Repo Audit — 1.0 Readiness
2
+
3
+ Audited 2026-07-02. Scope: factual inventory only, no design recommendations. This
4
+ report is input for a separate roadmap/strategy pass. Related existing research
5
+ already in this repo (not duplicated here): `gsd/research/market-positioning-research.md`
6
+ (HTML-vs-Markdown positioning) and `gsd/research/production-patterns-research.md`
7
+ (precedent survey for state/URLs/escape-hatch, written to inform exactly the gaps
8
+ this audit documents in sections 2-4).
9
+
10
+ ---
11
+
12
+ ## 1. Modes Inventory
13
+
14
+ StreamWeaver has evolved into **four distinct run modes**, documented in `README.md:67-72`:
15
+
16
+ | Mode | Invocation | Entry point | Maturity | Test coverage |
17
+ |---|---|---|---|---|
18
+ | Standalone script | `ruby app.rb` | `App#run!` in `lib/stream_weaver/app.rb`, own Sinatra server via `server.rb` | Mature — the original/primary mode | Broad, exercised by most of `spec/` |
19
+ | Agentic (popup input) | `app.run_once!` | Same `App` class, `run_once!` method | Mature, narrow use case | Covered by CLI eval/agentic specs |
20
+ | Service mode (multi-app-in-one-server) | `streamweaver app.rb` / `streamweaver serve` / `streamweaver run` | `lib/stream_weaver/service.rb`, dispatched via `lib/stream_weaver/cli.rb:15-107` (`case command`) | Functional but has the URL-slug gap (see section 3) | `spec/cli_spec.rb` and service-level specs |
21
+ | Canvas/panel push mode | `streamweaver live`, `streamweaver push`, `streamweaver panel`, `streamweaver canvas-*` (9 subcommands) | `lib/stream_weaver/canvas/{bridge.rb,bridge_server.rb,protocol.rb,session.rb,client.rb,doc_store.rb,history.rb,helpers.rb,reader.rb}` (2,231 lines total), CLI dispatch at `lib/stream_weaver/cli.rb:60-78` and `:1081-1467` | Actively developed, unevenly tested (see section 6) | `spec/canvas/` (9 files, ~1,196 lines, ~117 examples) + `spec/cli_canvas_push_spec.rb` + `spec/cli_canvas_read_default_spec.rb` — but **no direct spec file for `bridge_server.rb`** (1,111 lines, the largest and most load-bearing file in the subsystem) |
22
+
23
+ `lib/stream_weaver/cli.rb` is 1,897 lines and dispatches ~30 subcommands total
24
+ (`serve`, `run`, `list`, `remove`, `clear`, `admin`, `showcase`, `tutorial`, `stop`,
25
+ `status`, `llm`, `eval`, `prompt`, `live`, `push`, `live-list`, `live-close`, `wait`,
26
+ `template`, `canvas`, `canvas-push`, `canvas-wait`, `canvas-close`, `canvas-toast`,
27
+ `canvas-list`, `canvas-reset`, `canvas-stop`, `canvas-read`, `pick`, `confirm`,
28
+ `panel`, `opal-build`, `install-skill`, `setup`) — this is a large, sprawling CLI
29
+ surface for a single `exe/streamweaver` entry point.
30
+
31
+ A fifth mode exists at the deployment level rather than the DSL level: **Puma-dev
32
+ mode** (`README.md:156-188`, `examples/puma_dev/`), which runs a standard `config.ru`
33
+ under Puma-dev for memorable `http://myapp.test` URLs. This isn't a new run mode so
34
+ much as a deployment wrapper around standalone mode — but it's the one place in the
35
+ repo where a human-memorable URL story already works (see section 3 for why service
36
+ mode doesn't get the same treatment).
37
+
38
+ ---
39
+
40
+ ## 2. State Management
41
+
42
+ Two pluggable session stores exist, selected via `SW_SESSION_STORE` env var
43
+ (default `file`):
44
+
45
+ - **File store (current default)**: `StreamWeaver::FileSession < Rack::Session::Abstract::PersistedSecure`, `lib/stream_weaver/session_store.rb:66-165`. One `Marshal`-serialized file per session under `~/.config/stream_weaver/sessions/session_<id>`. No hard size limit. Sessions auto-expire after 24h (`expire_after: 86_400`); a startup cleanup in `lib/stream_weaver/server.rb:33-34` removes files older than 7 days.
46
+ - **Cookie store (legacy, opt-in)**: plain Sinatra `enable :sessions` (`server.rb:38-41`), hard-capped at 4096 bytes (`session_store.rb:34`). A `CookieStore#filter` (`session_store.rb:32-50`) strips blank values, known transient keys (`code_content`, `current_file_path`, `examples`, `_deck_state`, any `*_edited_code` key), and warns to stderr at 75% capacity (3,072B). No user-facing error on overflow — data is silently dropped past the filtered set.
47
+
48
+ **The "cookie too large" history is documented**: `CHANGELOG.md:205` records a
49
+ 2026-01-02 incident — *"Tutorial session overflow - Session cookie was exceeding
50
+ 4KB limit due to `*_edited_code` keys; now filtered from session storage."* The
51
+ `--reset` flag and `SW_DEBUG` env var (`CHANGELOG.md:36-37`) were added specifically
52
+ to troubleshoot this class of problem. A debug endpoint `GET /sw/session/size`
53
+ (`server.rb:862-878`) reports per-key byte sizes and an `over_limit` flag.
54
+
55
+ **Current state**: the file store is now default and effectively removes the 4KB
56
+ ceiling for standard installs. The cookie store remains available and still
57
+ silently truncates past 4KB with only a stderr warning — a data-loss risk if
58
+ `SW_SESSION_STORE=cookie` is set in production. Neither store is a shared/external
59
+ store (Redis, DB-backed) — file store is single-machine, filesystem-based, which is
60
+ adequate for local/dev use but not for multi-instance deployment.
61
+
62
+ ---
63
+
64
+ ## 3. Multi-App Server URL Generation (the hash-URL complaint)
65
+
66
+ **Exact generation point**: `lib/stream_weaver/service.rb:114-115`
67
+
68
+ ```ruby
69
+ def load_app(file_path, name: nil, source: nil)
70
+ app_id = SecureRandom.hex(4)
71
+ ```
72
+
73
+ This produces an 8-hex-character id (e.g. `a3f9c2e1`), used directly as the URL
74
+ segment in the `/load-app` response (`service.rb:405-412`):
75
+
76
+ ```ruby
77
+ url: "/apps/#{app_id}",
78
+ ```
79
+
80
+ The route is defined at `service.rb:868` (`get '/apps/:app_id' do ... end`), and
81
+ the CLI prints the raw hash URL directly to the user at `cli.rb:158-159`.
82
+
83
+ **A partial fix already exists in the code but isn't wired to the default path**:
84
+ `aliased_path_for` (`service.rb:186-196`) builds a human-readable `/source/name`
85
+ route from an app's `name`/`source` fields, matched by a Sinatra route at
86
+ `service.rb:814-825` (`get '/:source/:name'` redirects to `/apps/:app_id`). This
87
+ only activates when `load_app` is called with an explicit `source:` — used by
88
+ `examples_browser` and `tutorial` internally — but the ordinary `streamweaver run
89
+ <file.rb>` CLI path (`cli.rb:123-166`) never passes `source:`, so everyday usage
90
+ always surfaces the raw hex `app_id` URL rather than a memorable slug.
91
+
92
+ ---
93
+
94
+ ## 4. "Escape to Sinatra" Gaps
95
+
96
+ No `TODO`/`FIXME`/"escape hatch" comments exist anywhere in `lib/` — the framework
97
+ doesn't self-document its own limits. Gaps identified by inspecting the DSL surface:
98
+
99
+ - **No custom HTTP route DSL for app authors.** `App#route` (`app.rb:134`) and
100
+ `App#page` (`app.rb:119`) only register state-driven view matches, not arbitrary
101
+ HTTP endpoints. There is no `get`/`post`/`put`/`patch`/`delete` method exposed on
102
+ `App`. All real Sinatra routes (`/`, `/update`, `/action/:button_id`, `/submit`,
103
+ `/event/:key`, `/form/:form_name`, `/theme/:theme_name`, `/deck/*` in `server.rb`;
104
+ `/load-app`, `/live/*`, `/admin/*`, `/apps/:app_id/*` in `service.rb`) are
105
+ framework-internal and not extensible from the DSL block.
106
+ - **No file upload support.** No multipart/`params[:file]` handling anywhere in
107
+ `lib/` for app authors (only unrelated internal `Tempfile` usage and one unrelated
108
+ `params[:file]` lookup in `canvas/reader.rb:107`).
109
+ - **No authentication/authorization layer.** No auth/authorize/CSRF concept in
110
+ `lib/` beyond `set :protection, false if ENV['RACK_ENV'] == 'test'`
111
+ (`server.rb:46`), which only disables Sinatra's built-in protection in tests.
112
+ - **No custom middleware DSL.** `use` appears only internally (session store
113
+ wiring, `SW_DEBUG` logger) — never exposed to app authors.
114
+ - **Resource DSL is CRUD-only, single-table.** `docs/resource-dsl.md`'s store
115
+ protocol (`all`/`find`/`create`/`update`/`destroy`) has no joins, relations, or
116
+ nested resources — this is the newest subsystem (see gsd/plan.md /
117
+ gsd/tasks.md / gsd/STATE.md below) and is intentionally scoped narrow.
118
+ - **Routing is explicitly documented as limited in one place.**
119
+ `docs/routing.md:42`: *"One state key only. Doesn't handle parameterized paths
120
+ like `/initiative/:id`"* for `route_by` — `route_with` is offered as the
121
+ alternative but is still state-mapping, not general routing.
122
+
123
+ Net: any developer needing a webhook receiver, custom JSON API, non-GET/POST verb,
124
+ file upload, or auth has no documented in-framework path — they write raw Sinatra
125
+ alongside/instead of StreamWeaver. This is the gap `gsd/research/production-patterns-research.md`
126
+ was written to address (point "(c)" in that doc).
127
+
128
+ ---
129
+
130
+ ## 5. Gem Readiness
131
+
132
+ | Item | Status |
133
+ |---|---|
134
+ | Version | gemspec + `lib/stream_weaver/version.rb` both say **0.1.1**, but CHANGELOG.md has only ever cut `[0.1.0] - 2025-11-08` — everything since (including whatever justified the 0.1.1 bump) is still sitting in `[Unreleased]`. Version file and changelog are out of sync. |
135
+ | Dependencies | 10 runtime deps (sinatra, sinatra-contrib, phlex, puma, rackup, kramdown, kramdown-parser-gfm, ostruct, iterm2_ruby, diffy). **`iterm2_ruby ~> 0.1` is pointed at a local filesystem path in `Gemfile:9`** (`~/work/iterm2_ruby`), not published to rubygems.org — this is a hard blocker for `gem install stream_weaver` working for anyone but the author, since it's a runtime (not dev) dependency. |
136
+ | CI | **None.** No `.github/workflows/` directory exists. No automated tests, lint, or Ruby-version matrix on push/PR. |
137
+ | Test suite | Runs clean: **2,090 examples, 0 failures, 1 pending** (pending spec is an intentionally browser-only Opal test, not a real gap). ~5 second run time. Healthy suite size/health for a gem this size, but with zero CI it's only as good as manual discipline. |
138
+ | README | Hybrid — strong marketing/philosophy framing up top (mode comparison tables, "why StreamWeaver," collapsible longer story) with real Quick Start/install instructions further down. Reads more like a pitch/essay than a terse installation-first gem README; a first-time `gem install` user has to scroll past narrative to reach setup. |
139
+ | CHANGELOG | Real, substantial `[Unreleased]` section (~200 lines) with concrete entries, consistent Keep-a-Changelog formatting. Only one actual version cut (`0.1.0`) exists despite VERSION saying 0.1.1. |
140
+ | Docs tree | 73 files under `docs/`. Roughly 9 are genuine user-facing reference (`SERVICE_MODE.md`, `components_reference.md`, `crud-patterns.md`, `form-patterns.md`, `resource-dsl.md`, `routing.md`, `templates.md`, `testing.md`, `architecture/how_streamweaver_works.md`); the remainder is accumulated internal planning/scratch content — `docs/plans/*` (7 files), `docs/superpowers/plans/*` + `docs/superpowers/specs/*` (10 files, and per `.gitignore` these are supposed to be excluded from git but show up tracked in the working tree — worth reconciling), `docs/visual-skills/*` (25 files, session/build logs including `implementation/STATE.md`, `PROGRESS.md`, `SESSION-CONTEXT.md`), `docs/visual/*` (3 files), `docs/ideas/*` (3 files), `docs/blog/*`, stray `.rb` files inside `docs/streamweaver_canvas/`, and various spike/exploration docs (`opal-*`, `canvas-roadmap.md`, `canvas-ipc-session-summary.md`, `html-artifact-audit.md`, `ruby-ui-comparison.md`). This is not a coherent user-facing docs/ tree as-is. |
141
+ | exe/bin | `exe/streamweaver` is the actual packaged CLI entry point (wired via `spec.bindir = "exe"`). `bin/` (`build_scenarios`, `console`, `setup`, `opal_spike.rb`) is dev-only, correctly excluded from the gem by the gemspec's `spec.files` filter. |
142
+ | .gitignore | `*.gem` and `/pkg/` correctly ignored, none tracked. **`dist/` (2.7MB of built JS assets) is untracked but NOT in `.gitignore`** — currently shows as `?? dist/` in git status, one `git add -A` away from being accidentally committed. |
143
+ | Stray files at repo root | Two built `.gem` files sit at the repo root (`stream_weaver-0.1.0.gem`, `stream_weaver-0.1.1.gem`) — not tracked by git per the check above, but present on disk and should be confirmed as build artifacts, not something to ship. |
144
+
145
+ ---
146
+
147
+ ## 6. Canvas Mode Specifics
148
+
149
+ **Wire protocol** (`lib/stream_weaver/canvas/protocol.rb`, 74 lines): newline-delimited
150
+ JSON over a Unix domain socket (`~/.streamweaver/canvas.sock`) for Claude-to-Bridge
151
+ communication, plus WebSocket for Bridge-to-Browser. `Protocol.encode`/`.decode`/
152
+ `.parse_buffer` handle framing.
153
+
154
+ **Bridge architecture** (`bridge.rb`, 220 lines): `Bridge` holds an in-memory
155
+ `@sessions` hash (name to `Session`, no disk persistence — a crash loses all open
156
+ canvas sessions), dispatches messages by `type`. `handle_push` (line 112)
157
+ `instance_eval`s pushed DSL into a throwaway `App`, renders via the AlpineJS
158
+ adapter, and only persists the DSL on successful render — a bad push doesn't
159
+ clobber the last-good display state.
160
+
161
+ **Sessions**: `VALID_LAYOUTS = %i[default wide full fluid]` (`session.rb:9`);
162
+ theme is stored as a symbol with no validation against the actual theme registry.
163
+
164
+ **CLI surface**: 13 canvas-related subcommands in `cli.rb`, all functioning and
165
+ none stubbed.
166
+
167
+ **Documentation inconsistency found**: `docs/canvas-ipc-session-summary.md` is
168
+ headed "Status: OUTDATED — Canvas is now working and actively used" but its body
169
+ still documents a plan to delete `protocol.rb`, `session.rb`, `bridge.rb`,
170
+ `client.rb`, `bridge_server.rb`, `helpers.rb` in favor of an abandoned "templates"
171
+ approach — this actively contradicts the current (correct) architecture and should
172
+ be reconciled or removed before 1.0. `docs/canvas-roadmap.md` and
173
+ `docs/canvas-panel-workflow.md` are current and accurate.
174
+
175
+ **Relationship to the "visual companion" skill**: `lib/stream_weaver/skills/streamweaver-visual-companion/SKILL.md`
176
+ is a repo-shipped Claude Code skill built directly on canvas mode — it's an explicit
177
+ drop-in replacement for a Chrome-based visual companion, routing all interaction
178
+ through `panel`/`canvas-push`/`canvas-wait`. Canvas mode is therefore a dependency
179
+ of a shipped skill, not just an internal dev convenience, which raises the bar on
180
+ its required stability.
181
+
182
+ **Test coverage gap**: `spec/canvas/` covers session/doc_store/reader/history
183
+ behavior well, but **`bridge_server.rb` (1,111 lines, the largest and most
184
+ architecturally load-bearing file in the subsystem) has no dedicated spec file** —
185
+ only indirect coverage via CLI specs. Error handling across the small canvas files
186
+ is thin (13 `rescue` sites total across bridge/client/doc_store/helpers/history/reader).
187
+
188
+ **Overall**: an actively developed, real subsystem (not a throwaway experiment) —
189
+ but not fully mature for 1.0. In-memory-only session state (no crash recovery),
190
+ unvalidated theme/layout wire values, the largest file untested directly, and one
191
+ actively misleading doc file are the concrete gaps.
192
+
193
+ ---
194
+
195
+ ## 7. Tutorials
196
+
197
+ **CLI command**: `streamweaver tutorial` (`cli.rb:306`, `self.tutorial`) execs
198
+ `examples/advanced/tutorial.rb` as a standalone script.
199
+
200
+ **Content files**:
201
+ - `examples/advanced/tutorial.rb` (1,068 lines) — the live tutorial, 10 lessons
202
+ (Philosophy, Hello World, Getting Input, Making Choices, Taking Action, Layout,
203
+ Cards, Tables, Modals, Themes, Patterns). Last touched 2026-01-02 (the
204
+ cookie-overflow fix commit).
205
+ - `examples_playground/advanced/tutorial.rb` — near-duplicate copy for the
206
+ examples/showcase playground.
207
+ - `examples/claude_code/tutorial/README.md` — separate, small, Claude-Code-agent-oriented walkthrough.
208
+
209
+ **Staleness — confirmed stale.** The tutorial predates essentially all of the
210
+ `CHANGELOG.md` `[Unreleased]` section (there has been no version cut since 0.1.0
211
+ on 2025-11-08, so everything shipped since is "Unreleased"). Grep for
212
+ `canvas|navbar|doc theme|CardHeader|resource\b|route_by|route_with|opal` in
213
+ `tutorial.rb` returns zero real hits. The Themes lesson (`tutorial.rb:350-358`)
214
+ lists only `:default`, `:dashboard`, `:document` — missing the newer `:doc` theme
215
+ (added this cycle, commit `e98df87`) and the `:sketch` preset. No coverage of the
216
+ `resource` DSL/CRUD scaffolding, `navbar`/`nav_item`/`link_to`, `card_header`
217
+ badge/meta options, canvas/panel mode, or theme_switcher/theme_toggle — all
218
+ shipped after the tutorial's last edit. Roughly six months of feature growth is
219
+ undocumented in the one onboarding path that's wired into the CLI.
220
+
221
+ ---
222
+
223
+ ## 8. Theming/Styling
224
+
225
+ **`auto_mode.rb`** (`lib/stream_weaver/theme/auto_mode.rb`): generates inline JS
226
+ for OS-following dark/light mode, sets `data-sw-theme`/`dark` attributes on
227
+ `<html>` before `DOMContentLoaded` (avoiding flash-of-wrong-theme), persists
228
+ preference to `localStorage`, exposes `window.swToggleTheme`/`swGetTheme`. The
229
+ `mode:` parameter was a documented dead parameter until a fix landed this cycle
230
+ (`CHANGELOG.md:21`).
231
+
232
+ **Theme registry**: `lib/stream_weaver/theme.rb:151-206`, `BUILT_IN_THEMES` hash
233
+ with four entries — `:default` ("Warm Industrial"), `:dashboard` ("Data Dense"),
234
+ `:document` ("Reading Mode"), and `:doc` ("Compact Editorial" — added this cycle,
235
+ commit `e98df87`). No `register_theme` custom-theme API was found in the audited
236
+ region of `theme.rb`, despite CHANGELOG history referencing one previously —
237
+ worth confirming this API's current status before 1.0 messaging.
238
+
239
+ **Recent churn — actively in flux, not stable.** Three consecutive recent commits
240
+ (`e98df87`, `83be0bf`, `365e13a`) built the `:doc` theme and "doc-parity" polish
241
+ (a git-commit-message tag for matching an external artifact's editorial styling
242
+ pixel-for-pixel: ALL-CAPS table headers, `DocHeader` component, sticky-sidebar CSS
243
+ grid). The working tree at time of audit has uncommitted changes to
244
+ `auto_mode.rb`, `views.rb`, `components.rb`, and new/modified specs
245
+ (`theme_enhanced_spec.rb`, `theme_switcher_spec.rb`, `theme_toggle_spec.rb`,
246
+ `card_header_spec.rb`) — theming is mid-iteration right now, not settled.
247
+
248
+ **Test coverage**: `spec/theme_enhanced_spec.rb` (presets, AutoMode, ThemeToggle,
249
+ backward-compat), `spec/canvas/theme_support_spec.rb` (canvas session theme
250
+ carrying), `spec/components/theme_switcher_spec.rb` and `theme_toggle_spec.rb`
251
+ (narrow HTML-rendering checks).
252
+
253
+ ---
254
+
255
+ ## Existing Plans Found (so this audit doesn't duplicate them)
256
+
257
+ - **`docs/for_llms.md`** and **`llms.txt`** — both are LLM-oriented quick-reference
258
+ docs (port auto-detection warning, "the DSL block re-executes on every
259
+ interaction" mental model, minimal examples). Overlapping content between the
260
+ two; not a planning doc.
261
+ - **`gsd/plan.md`, `gsd/tasks.md`, `gsd/STATE.md`** — a completed GSD Ralph-loop
262
+ execution plan for the **Resource DSL** feature (routing chain, `page`/`route`
263
+ DSL, `ResourceDefinition`, `DefaultViews`, override blocks, scaffolding
264
+ examples). All 6 tasks (T1-T6) are marked **DONE** with commit hashes in
265
+ `gsd/STATE.md` — this work is finished and merged, not in flight.
266
+ - **`gsd/research/production-patterns-research.md`** — a precedent survey
267
+ (Streamlit, Rails "Solid" trio, etc.) written specifically to address the three
268
+ pain points this audit independently confirmed: (a) cookie-session overflow
269
+ history, (b) hash-based multi-app URLs, (c) users dropping to raw Sinatra. This
270
+ is strategy input already sitting in the repo, not something to re-derive.
271
+ - **`gsd/research/market-positioning-research.md`** — positioning research
272
+ (Anthropic's Markdown-to-HTML shift, docs-site template survey, demo-video
273
+ stack) — orthogonal to the technical gaps in this audit, useful for the
274
+ marketing/positioning side of a 1.0 roadmap.
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
4
+ require "json"
5
+ require "openssl"
6
+ require "securerandom"
7
+
8
+ module StreamWeaver
9
+ module ActionToken
10
+ class Invalid < StandardError; end
11
+
12
+ module_function
13
+
14
+ def encode(payload)
15
+ body = Base64.urlsafe_encode64(JSON.generate(payload), padding: false)
16
+ "#{body}.#{signature(body)}"
17
+ end
18
+
19
+ def decode(token)
20
+ body, supplied = token.to_s.split(".", 2)
21
+ raise Invalid unless body && supplied && secure_compare(signature(body), supplied)
22
+
23
+ JSON.parse(Base64.urlsafe_decode64(body), symbolize_names: true)
24
+ rescue JSON::ParserError, ArgumentError
25
+ raise Invalid
26
+ end
27
+
28
+ def fingerprint(token)
29
+ OpenSSL::Digest::SHA256.hexdigest(token.to_s)
30
+ end
31
+
32
+ def secret
33
+ @secret ||= begin
34
+ configured = ENV["SW_SECRET"]
35
+ unless configured && !configured.empty?
36
+ warn "StreamWeaver: SW_SECRET is not set; action tokens will not work across multiple workers."
37
+ configured = SecureRandom.hex(32)
38
+ end
39
+ configured
40
+ end
41
+ end
42
+
43
+ def reset_secret!
44
+ @secret = nil
45
+ end
46
+
47
+ def signature(body)
48
+ Base64.urlsafe_encode64(OpenSSL::HMAC.digest("SHA256", secret, body), padding: false)
49
+ end
50
+ private_class_method :signature
51
+
52
+ def secure_compare(left, right)
53
+ return false unless left.bytesize == right.bytesize
54
+ OpenSSL.fixed_length_secure_compare(left, right)
55
+ end
56
+ private_class_method :secure_compare
57
+ end
58
+ end