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.
- checksums.yaml +7 -0
- data/.beads/.gitignore +77 -0
- data/.beads/README.md +81 -0
- data/.beads/config.yaml +54 -0
- data/.beads/metadata.json +7 -0
- data/.rspec +3 -0
- data/AGENTS.md +91 -0
- data/CHANGELOG.md +294 -0
- data/CLAUDE.md +67 -0
- data/LICENSE.txt +21 -0
- data/README.md +565 -0
- data/Rakefile +13 -0
- data/assets/streamweaver-hero.jpg +0 -0
- data/bench/baselines/ledger.rb +58 -0
- data/bench/baselines/warroom.rb +51 -0
- data/bench/fixtures/ledger.rb +76 -0
- data/bench/fixtures/warroom.rb +60 -0
- data/bench/results/6daf8b8.md +102 -0
- data/bench/results/c8d749b.md +102 -0
- data/bench/results/eb06ab9.md +102 -0
- data/bench/run.rb +151 -0
- data/bench/support.rb +84 -0
- data/copy_this/dto_run_sample_run_7631.html +220 -0
- data/docs/SERVICE_MODE.md +302 -0
- data/docs/architecture/how_streamweaver_works.md +772 -0
- data/docs/blog/2026-01-16-streamweaver-introduction.md +515 -0
- data/docs/bug-2026-08-31-canvas-bridge-socket-collision.md +83 -0
- data/docs/canvas-ipc-session-summary.md +127 -0
- data/docs/canvas-panel-workflow.md +172 -0
- data/docs/canvas-read.md +147 -0
- data/docs/canvas-roadmap.md +275 -0
- data/docs/case-studies/2026-09-03-didx-canvas-read-shelf.md +147 -0
- data/docs/claude-code-companion-skill-spec.md +567 -0
- data/docs/components_reference.md +1223 -0
- data/docs/crud-patterns.md +265 -0
- data/docs/endpoints.md +122 -0
- data/docs/for_llms.md +1 -0
- data/docs/form-patterns.md +165 -0
- data/docs/frontend-only.md +169 -0
- data/docs/html-artifact-audit.md +238 -0
- data/docs/ideas/2025-01-01-as-a-service.md +203 -0
- data/docs/ideas/2026-01-16-charm-tui-exploration.md +542 -0
- data/docs/ideas/2026-08-27-static-doc-shelf-export.md +153 -0
- data/docs/ideas/dhh-review-future-refactors.md +142 -0
- data/docs/opal-jamstack.md +47 -0
- data/docs/opal-npm-direction.md +184 -0
- data/docs/opal-spike-findings.md +194 -0
- data/docs/plans/2025-12-16-form-blocks-design.md +124 -0
- data/docs/plans/2025-12-30-examples-browser-design.md +152 -0
- data/docs/plans/2026-01-02-charts-design.md +288 -0
- data/docs/plans/2026-01-10-canvas-ipc-design.md +247 -0
- data/docs/plans/2026-01-19-cabinet-control-components.md +176 -0
- data/docs/plans/2026-01-26-gem-release-and-panel.md +140 -0
- data/docs/plans/canvas-claude-project-design.md +238 -0
- data/docs/plans/canvas-doc-location-and-discovery.md +32 -0
- data/docs/plans/org-doc-preview-surfaces.md +293 -0
- data/docs/plans/shadcn-polish-plan.md +417 -0
- data/docs/porting-artifacts.md +252 -0
- data/docs/reference/agent-skills-comparison.md +369 -0
- data/docs/reference/travel-state-prd.artifact.html +780 -0
- data/docs/research/2026-08-17-hotwire-alike-landscape.md +268 -0
- data/docs/research/2026-08-17-hotwire-concept-map.md +404 -0
- data/docs/research/2026-08-22-lazy-fragments-trigger-decision.md +246 -0
- data/docs/research/2026-08-22-learnhotwire-syllabus-coverage.md +239 -0
- data/docs/research/frontend-only-matrix.md +278 -0
- data/docs/research/streamweaver-way-spike-findings.md +529 -0
- data/docs/resource-dsl.md +342 -0
- data/docs/routing.md +302 -0
- data/docs/ruby-ui-comparison.md +319 -0
- data/docs/shared-dsl-fragments.md +356 -0
- data/docs/streamweaver-for-ai-agents.md +401 -0
- data/docs/streamweaver-frontend-vision.md +123 -0
- data/docs/streamweaver_canvas/glimmer_initial_final_layer.rb +77 -0
- data/docs/streamweaver_canvas/save_example.rb +9 -0
- data/docs/templates.md +213 -0
- data/docs/testing.md +155 -0
- data/docs/theming-hooks.md +281 -0
- data/docs/tutorials/the-streamweaver-way.md +882 -0
- data/docs/university/capability-inventory.md +137 -0
- data/docs/university/dependency-survey.md +114 -0
- data/docs/university/design-spec.md +217 -0
- data/docs/university/mockups/code-block-doc-theme-dark.png +0 -0
- data/docs/university/mockups/code-block-doc-theme-light.png +0 -0
- data/docs/university/mockups/course-list-dark.png +0 -0
- data/docs/university/mockups/course-list-light.png +0 -0
- data/docs/university/mockups/course_canvas_mockup.rb +741 -0
- data/docs/university/mockups/step-screen-dark.png +0 -0
- data/docs/university/mockups/step-screen-light.png +0 -0
- data/docs/university/roadmap.md +72 -0
- data/docs/university/send-to-coworker.md +77 -0
- data/docs/university/worker-session-mining.md +109 -0
- data/docs/visual/browser-rendering-spike.md +173 -0
- data/docs/visual/builder-visual-plan-analysis.md +378 -0
- data/docs/visual/sw-plan-format-exploration.md +511 -0
- data/docs/visual/sw-plan-rendering-deep-dive.md +453 -0
- data/docs/visual-skills/PROGRESS.md +60 -0
- data/docs/visual-skills/SESSION-CONTEXT.md +207 -0
- data/docs/visual-skills/analysis/components.md +317 -0
- data/docs/visual-skills/analysis/overlap.md +360 -0
- data/docs/visual-skills/analysis/pi-design-deck.md +1102 -0
- data/docs/visual-skills/analysis/streamweaver-inventory.md +93 -0
- data/docs/visual-skills/analysis/unified-specs.feature +1003 -0
- data/docs/visual-skills/analysis/visual-explainer.md +964 -0
- data/docs/visual-skills/blog/blog-series-outline.md +39 -0
- data/docs/visual-skills/blog/token-efficiency.md +73 -0
- data/docs/visual-skills/design/architecture.md +1725 -0
- data/docs/visual-skills/design/codex-review.md +340 -0
- data/docs/visual-skills/design/dhh-review.md +209 -0
- data/docs/visual-skills/design/evolution.md +100 -0
- data/docs/visual-skills/design/gemini-review.md +55 -0
- data/docs/visual-skills/design/review-synthesis.md +149 -0
- data/docs/visual-skills/implementation/STATE.md +22 -0
- data/docs/visual-skills/implementation/plan.md +347 -0
- data/docs/visual-skills/implementation/spike-findings.md +237 -0
- data/docs/visual-skills/implementation/tasks.md +702 -0
- data/docs/visual-skills/lessons-learned/process.md +145 -0
- data/examples/README.md +96 -0
- data/examples/advanced/all_components.rb +177 -0
- data/examples/advanced/examples_browser.rb +394 -0
- data/examples/advanced/teachables_browser.rb +261 -0
- data/examples/advanced/theme_tweaker.rb +366 -0
- data/examples/advanced/tutorial.rb +1528 -0
- data/examples/agentic/agentic_form.rb +30 -0
- data/examples/agentic/agentic_form_autoclose.rb +30 -0
- data/examples/agentic/cultivation_tracker.rb +69 -0
- data/examples/basic/hello_world.rb +22 -0
- data/examples/basic/opal_tabs_table.rb +44 -0
- data/examples/basic/todo_list.rb +39 -0
- data/examples/button_loading_test.rb +36 -0
- data/examples/canvas/mermaid_canvas_demo.sh +151 -0
- data/examples/charts/bar_chart_demo.rb +57 -0
- data/examples/charts/line_chart_demo.rb +73 -0
- data/examples/charts/pie_area_demo.rb +87 -0
- data/examples/charts/stacked_bar_chart_demo.rb +69 -0
- data/examples/claude_code/README.md +85 -0
- data/examples/claude_code/codebreaker/.claude/commands/infiltrate.md +632 -0
- data/examples/claude_code/codebreaker/.claude/settings.json +11 -0
- data/examples/claude_code/codebreaker/.claude/settings.local.json +17 -0
- data/examples/claude_code/codebreaker/.claude/skills/infiltrate.md +0 -0
- data/examples/claude_code/codebreaker/README.md +213 -0
- data/examples/claude_code/tutorial/.claude/commands/learn.md +453 -0
- data/examples/claude_code/tutorial/README.md +102 -0
- data/examples/claude_code/verification_flow/.claude/commands/verify.md +171 -0
- data/examples/claude_code/verification_flow/README.md +126 -0
- data/examples/components/annotated_code_demo.rb +54 -0
- data/examples/components/callout_demo.rb +33 -0
- data/examples/components/checkbox_group_demo.rb +46 -0
- data/examples/components/design_review.css +468 -0
- data/examples/components/design_review_demo.rb +44 -0
- data/examples/components/design_review_dsl.rb +300 -0
- data/examples/components/diff_block_demo.rb +112 -0
- data/examples/components/events_demo.rb +184 -0
- data/examples/components/form_demo.rb +62 -0
- data/examples/components/lesson_demo.rb +53 -0
- data/examples/components/markdown_demo.rb +110 -0
- data/examples/components/mermaid_demo.rb +166 -0
- data/examples/components/pareto_set.rb +64 -0
- data/examples/components/prd_demo.rb +21 -0
- data/examples/components/prd_dsl.rb +385 -0
- data/examples/components/quiz_demo.rb +47 -0
- data/examples/components/run_viewer_demo.rb +100 -0
- data/examples/components/score_and_collapsible_demo.rb +82 -0
- data/examples/components/select_stale_value_smoke_test.rb +39 -0
- data/examples/components/table_demo.rb +247 -0
- data/examples/components/timer_with_state_demo.rb +58 -0
- data/examples/components/todo_due_dates.rb +63 -0
- data/examples/components/uat_gaps_demo.rb +112 -0
- data/examples/dashboard/feed_simulator.rb +60 -0
- data/examples/dashboard/live_dashboard.rb +80 -0
- data/examples/dashboard_components.rb +108 -0
- data/examples/deferred_fragments_demo.rb +62 -0
- data/examples/generate_more_spike/README.md +63 -0
- data/examples/generate_more_spike/app.rb +441 -0
- data/examples/git_health.sh +401 -0
- data/examples/layout/layout_components_demo.rb +174 -0
- data/examples/layout/modal_demo.rb +215 -0
- data/examples/layout/navigation_demo.rb +227 -0
- data/examples/layout/route_tabs_demo.rb +52 -0
- data/examples/layout/routing_demo.rb +52 -0
- data/examples/layout/scroll_box_demo.rb +154 -0
- data/examples/lazy_fragments_demo.rb +151 -0
- data/examples/my_todos/README.md +29 -0
- data/examples/my_todos/my_todos.rb +312 -0
- data/examples/my_todos/store.rb +86 -0
- data/examples/opal/reactive_demo.rb +62 -0
- data/examples/opal/scenarios/s1_counter.rb +15 -0
- data/examples/opal/scenarios/s2_search_filter.rb +17 -0
- data/examples/opal/scenarios/s3_sibling_tabs.rb +25 -0
- data/examples/opal/scenarios/s4_shopping_cart.rb +34 -0
- data/examples/opal/scenarios/s5_watch.rb +39 -0
- data/examples/opal/scenarios/s6_on_start.rb +20 -0
- data/examples/opal/scenarios/s7_wizard.rb +33 -0
- data/examples/opal/scenarios/s8_loan_calculator.rb +21 -0
- data/examples/opal/scenarios/s9_dashboard.rb +27 -0
- data/examples/operations_dashboard_demo.rb +167 -0
- data/examples/panel_demo.sh +447 -0
- data/examples/parity/assets/tyrion_slice.css +86 -0
- data/examples/parity/rivet_people_slice.rb +182 -0
- data/examples/parity/tyrion_components.css +777 -0
- data/examples/parity/tyrion_warroom_components.rb +414 -0
- data/examples/parity/tyrion_warroom_slice.rb +549 -0
- data/examples/puma_dev/README.md +124 -0
- data/examples/puma_dev/config.ru +34 -0
- data/examples/puma_dev/standalone_app.rb +34 -0
- data/examples/scaffolding/blog.rb +49 -0
- data/examples/scaffolding/utf_lite.rb +105 -0
- data/examples/styling/feedback_demo.rb +229 -0
- data/examples/styling/style_showcase.rb +172 -0
- data/examples/styling/theme_demo.rb +508 -0
- data/examples/timer_health_checker.rb +111 -0
- data/examples/timer_showcase.rb +131 -0
- data/examples/tutorials/render_markdown.rb +112 -0
- data/examples/tutorials/streamweaver_way_tutorial.rb +44 -0
- data/examples/tutorials/tutorial_content.rb +1082 -0
- data/examples/visual_skills/design_deck_demo.rb +314 -0
- data/examples/visual_skills/explainer_demo.rb +499 -0
- data/exe/streamweaver +9 -0
- data/gsd/ROADMAP-1.0.md +167 -0
- data/gsd/STATE.md +13 -0
- data/gsd/research/market-positioning-research.md +139 -0
- data/gsd/research/production-patterns-research.md +270 -0
- data/gsd/research/repo-audit-1.0.md +274 -0
- data/lib/stream_weaver/action_token.rb +58 -0
- data/lib/stream_weaver/adapter/alpinejs.rb +8249 -0
- data/lib/stream_weaver/adapter/base.rb +591 -0
- data/lib/stream_weaver/adapter/opal.rb +334 -0
- data/lib/stream_weaver/adapter/static.rb +1118 -0
- data/lib/stream_weaver/admin.rb +176 -0
- data/lib/stream_weaver/app.rb +1821 -0
- data/lib/stream_weaver/assets/js/sw-copy.js +50 -0
- data/lib/stream_weaver/assets/js/sw-heredoc-rewrite.js +68 -0
- data/lib/stream_weaver/assets/js/sw-keyboard.js +165 -0
- data/lib/stream_weaver/assets/js/sw-mermaid-zoom.js +621 -0
- data/lib/stream_weaver/assets/js/sw-route-tabs.js +53 -0
- data/lib/stream_weaver/assets/js/sw-sidebar-toc.js +116 -0
- data/lib/stream_weaver/assets/js/sw-slide-nav.js +103 -0
- data/lib/stream_weaver/canvas/bridge.rb +239 -0
- data/lib/stream_weaver/canvas/bridge_server.rb +646 -0
- data/lib/stream_weaver/canvas/client.rb +298 -0
- data/lib/stream_weaver/canvas/doc_roots.rb +233 -0
- data/lib/stream_weaver/canvas/doc_store.rb +252 -0
- data/lib/stream_weaver/canvas/gist_publisher.rb +264 -0
- data/lib/stream_weaver/canvas/gist_save_handler.rb +89 -0
- data/lib/stream_weaver/canvas/gist_store.rb +135 -0
- data/lib/stream_weaver/canvas/helpers.rb +109 -0
- data/lib/stream_weaver/canvas/history.rb +90 -0
- data/lib/stream_weaver/canvas/protocol.rb +86 -0
- data/lib/stream_weaver/canvas/reader.rb +853 -0
- data/lib/stream_weaver/canvas/save_doc_widget.rb +457 -0
- data/lib/stream_weaver/canvas/scroll_top_hint.rb +21 -0
- data/lib/stream_weaver/canvas/session.rb +132 -0
- data/lib/stream_weaver/cli.rb +3235 -0
- data/lib/stream_weaver/component_assets.rb +70 -0
- data/lib/stream_weaver/component_registry.rb +67 -0
- data/lib/stream_weaver/component_renderer.rb +48 -0
- data/lib/stream_weaver/components/annotated_code.rb +53 -0
- data/lib/stream_weaver/components/api_endpoint.rb +42 -0
- data/lib/stream_weaver/components/callout.rb +59 -0
- data/lib/stream_weaver/components/chart.rb +84 -0
- data/lib/stream_weaver/components/code_block.rb +77 -0
- data/lib/stream_weaver/components/comparison.rb +39 -0
- data/lib/stream_weaver/components/decision.rb +38 -0
- data/lib/stream_weaver/components/deck/close_overlay.rb +84 -0
- data/lib/stream_weaver/components/deck/confirmation_bar.rb +60 -0
- data/lib/stream_weaver/components/deck/deck_option.rb +61 -0
- data/lib/stream_weaver/components/deck/deck_slide.rb +77 -0
- data/lib/stream_weaver/components/deck/deck_state.rb +469 -0
- data/lib/stream_weaver/components/deck/deck_summary.rb +73 -0
- data/lib/stream_weaver/components/deck/design_deck.rb +60 -0
- data/lib/stream_weaver/components/deck/generate_more_controls.rb +84 -0
- data/lib/stream_weaver/components/deck/model_selector.rb +84 -0
- data/lib/stream_weaver/components/deck/skeleton_placeholder.rb +36 -0
- data/lib/stream_weaver/components/diff_block.rb +125 -0
- data/lib/stream_weaver/components/doc_header.rb +57 -0
- data/lib/stream_weaver/components/image_block.rb +70 -0
- data/lib/stream_weaver/components/implementation_map.rb +33 -0
- data/lib/stream_weaver/components/keyboard_shortcuts.rb +94 -0
- data/lib/stream_weaver/components/kpi_dashboard.rb +78 -0
- data/lib/stream_weaver/components/mermaid.rb +79 -0
- data/lib/stream_weaver/components/pipeline.rb +63 -0
- data/lib/stream_weaver/components/sidebar_toc.rb +46 -0
- data/lib/stream_weaver/components/slide_container.rb +141 -0
- data/lib/stream_weaver/components/timeline_event.rb +58 -0
- data/lib/stream_weaver/components/wireframe.rb +29 -0
- data/lib/stream_weaver/components/wireframe_block.rb +34 -0
- data/lib/stream_weaver/components.rb +2711 -0
- data/lib/stream_weaver/css.rb +230 -0
- data/lib/stream_weaver/dev_fallback_overlay.rb +61 -0
- data/lib/stream_weaver/display_dsl.rb +1004 -0
- data/lib/stream_weaver/export/html_exporter.rb +478 -0
- data/lib/stream_weaver/feed.rb +34 -0
- data/lib/stream_weaver/feed_builder.rb +31 -0
- data/lib/stream_weaver/fonts.rb +33 -0
- data/lib/stream_weaver/interaction_runner.rb +487 -0
- data/lib/stream_weaver/iterm.rb +460 -0
- data/lib/stream_weaver/layout_registry.rb +92 -0
- data/lib/stream_weaver/opal/bridge.rb +52 -0
- data/lib/stream_weaver/opal/builder.rb +193 -0
- data/lib/stream_weaver/opal/env.rb +29 -0
- data/lib/stream_weaver/opal/reactive_state.rb +75 -0
- data/lib/stream_weaver/opal/regexp_anchor_patch.rb +62 -0
- data/lib/stream_weaver/opal/renderer.rb +77 -0
- data/lib/stream_weaver/opal/runtime.rb +250 -0
- data/lib/stream_weaver/opal/shell.rb +145 -0
- data/lib/stream_weaver/opal/string_bridge.rb +50 -0
- data/lib/stream_weaver/opal/stubs/diff.min.js +1 -0
- data/lib/stream_weaver/opal/stubs/digest.rb +15 -0
- data/lib/stream_weaver/opal/stubs/marked.umd.js +79 -0
- data/lib/stream_weaver/opal/stubs/md5.rb +3 -0
- data/lib/stream_weaver/opal/stubs/morphdom.min.js +775 -0
- data/lib/stream_weaver/opal/stubs/prism-tomorrow.min.css +1 -0
- data/lib/stream_weaver/opal/stubs/prism.min.js +1967 -0
- data/lib/stream_weaver/opal_entry.rb +136 -0
- data/lib/stream_weaver/org/inline.rb +95 -0
- data/lib/stream_weaver/org/reader.rb +561 -0
- data/lib/stream_weaver/org/recording_context.rb +86 -0
- data/lib/stream_weaver/org/source_splitter.rb +63 -0
- data/lib/stream_weaver/org/writer.rb +300 -0
- data/lib/stream_weaver/page_shell.rb +530 -0
- data/lib/stream_weaver/portfile.rb +79 -0
- data/lib/stream_weaver/pushable.rb +42 -0
- data/lib/stream_weaver/resource/default_views.rb +111 -0
- data/lib/stream_weaver/resource/field_input.rb +25 -0
- data/lib/stream_weaver/resource/state_keys.rb +16 -0
- data/lib/stream_weaver/resource/store.rb +18 -0
- data/lib/stream_weaver/resource.rb +104 -0
- data/lib/stream_weaver/server.rb +1471 -0
- data/lib/stream_weaver/service.rb +1240 -0
- data/lib/stream_weaver/service_client.rb +104 -0
- data/lib/stream_weaver/session_store.rb +186 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/SKILL.md +66 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/examples/canvas-safe-showcase.rb +98 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/actions-and-buttons.md +47 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/charts-and-diagrams.md +38 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/deck.md +32 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/inputs-and-forms.md +65 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/tabs-and-navigation.md +53 -0
- data/lib/stream_weaver/skills/streamweaver-doc-builder/SKILL.md +298 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/SKILL.md +130 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example.css +468 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example.rb +39 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example_dsl.rb +300 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/doc-parity-example.rb +24 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/doc-parity-example_dsl.rb +385 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/references/checkpoints-and-forms.md +28 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/references/cleanup-and-panel.md +44 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/references/example-gallery.md +8 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/references/persistence.md +58 -0
- data/lib/stream_weaver/skills/streamweaver-way/SKILL.md +396 -0
- data/lib/stream_weaver/skills/visual-plan/SKILL.md +201 -0
- data/lib/stream_weaver/skills/visual-recap/SKILL.md +244 -0
- data/lib/stream_weaver/streamer.rb +63 -0
- data/lib/stream_weaver/templates/choices.rb +149 -0
- data/lib/stream_weaver/templates/code.rb +157 -0
- data/lib/stream_weaver/templates/confirm.rb +110 -0
- data/lib/stream_weaver/templates/diff.rb +195 -0
- data/lib/stream_weaver/templates/info.rb +142 -0
- data/lib/stream_weaver/templates/table.rb +169 -0
- data/lib/stream_weaver/templates/wizard.rb +271 -0
- data/lib/stream_weaver/theme/auto_mode.rb +116 -0
- data/lib/stream_weaver/theme/presets.rb +555 -0
- data/lib/stream_weaver/theme.rb +639 -0
- data/lib/stream_weaver/university/canvas.rb +924 -0
- data/lib/stream_weaver/university/course.rb +699 -0
- data/lib/stream_weaver/university/demos/counter.rb +33 -0
- data/lib/stream_weaver/university/demos/dashboard.rb +147 -0
- data/lib/stream_weaver/university/demos/decision_form.rb +143 -0
- data/lib/stream_weaver/university/demos.rb +50 -0
- data/lib/stream_weaver/university/listener.rb +467 -0
- data/lib/stream_weaver/university/progress.rb +201 -0
- data/lib/stream_weaver/university/runner.rb +138 -0
- data/lib/stream_weaver/university/scripts/growing_doc.rb +609 -0
- data/lib/stream_weaver/university/scripts/growing_doc_state.rb +91 -0
- data/lib/stream_weaver/utils.rb +31 -0
- data/lib/stream_weaver/version.rb +5 -0
- data/lib/stream_weaver/views/canvas/reader_layout.erb +800 -0
- data/lib/stream_weaver/views.rb +3940 -0
- data/lib/stream_weaver.rb +122 -0
- data/llms.txt +1269 -0
- data/sig/stream_weaver.rbs +4 -0
- metadata +598 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# Generate-More Spike Findings (T1)
|
|
2
|
+
|
|
3
|
+
*Date: 2026-03-12*
|
|
4
|
+
*Status: Complete*
|
|
5
|
+
*Consumed by: T10 (Generate-More Full Implementation)*
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. State Machine (Discovered)
|
|
10
|
+
|
|
11
|
+
The architecture doc predicted three states (IDLE, GENERATING, TIMED_OUT). Implementation revealed **four** are needed:
|
|
12
|
+
|
|
13
|
+
| State | Description | Entry Condition |
|
|
14
|
+
|-------|-------------|-----------------|
|
|
15
|
+
| `:idle` | No generation in progress. Generate button enabled. | Initial, or all options received, or timeout dismissed, or cancel complete |
|
|
16
|
+
| `:generating` | Request queued, skeletons visible, cancel button shown. | User clicks Generate |
|
|
17
|
+
| `:timed_out` | 15s elapsed. Warning shown, Generate re-enabled. | Server-side timer detects elapsed > 15s |
|
|
18
|
+
| `:cancelled` | User requested cancellation. Transient state. | User clicks Cancel during `:generating` |
|
|
19
|
+
|
|
20
|
+
### Transitions
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
idle -> generating (user clicks Generate)
|
|
24
|
+
generating -> idle (all options received: received_count >= requested_count)
|
|
25
|
+
generating -> timed_out (server-side timer: elapsed > 15s)
|
|
26
|
+
generating -> cancelled (user clicks Cancel)
|
|
27
|
+
cancelled -> idle (agent thread acknowledges, cleanup completes)
|
|
28
|
+
timed_out -> idle (user clicks Generate again, or page refresh)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Why `:cancelled` is needed
|
|
32
|
+
|
|
33
|
+
Originally tried cancelling by flipping directly to `:idle`, but the agent thread had a race window: it could push an option between the cancel request and the next cancellation check. The `:cancelled` state acts as a "stop flag" that the agent thread checks before each push. Once acknowledged, it transitions to `:idle`.
|
|
34
|
+
|
|
35
|
+
### Alternative: Signal-based cancellation
|
|
36
|
+
|
|
37
|
+
Instead of a state, cancellation could be a flag in a separate channel (as implemented in the spike via `SpikeState.cancel(session_id)`). This separates "agent should stop" from "UI state." T10 should evaluate whether the cancelled state should be part of the state machine or a side-channel flag. The spike used both: a side-channel flag for the agent thread, and a transient `:cancelled` state that resolves to `:idle`.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 2. Push-to-State: Validated
|
|
42
|
+
|
|
43
|
+
### What works
|
|
44
|
+
|
|
45
|
+
The core pattern works as designed:
|
|
46
|
+
1. Agent pushes option data into a shared server-side state store
|
|
47
|
+
2. After each push, `streamer.replace("#target", html)` sends re-rendered content
|
|
48
|
+
3. Browser receives complete HTML and morphs the DOM (via htmx + Alpine morph)
|
|
49
|
+
4. No phantom option race -- content is always rendered from current state
|
|
50
|
+
5. No partial/stale state -- each push renders the entire content area
|
|
51
|
+
|
|
52
|
+
### What the architecture doc got right
|
|
53
|
+
|
|
54
|
+
- Eliminating push-to-DOM avoids phantom options entirely
|
|
55
|
+
- Summary slide stays in sync because it reads the same state
|
|
56
|
+
- Simpler than SSE-to-DOM with multiple targets
|
|
57
|
+
|
|
58
|
+
### Key constraint discovered: Full re-render vs. incremental
|
|
59
|
+
|
|
60
|
+
The spike re-renders the ENTIRE content div on each option push. This works fine for 2-10 options. For 50+ options, T10 should consider:
|
|
61
|
+
- Append mode: SSE appends just the new option card, only if on the correct slide
|
|
62
|
+
- Or: continue full re-render but optimize the HTML size (skip unchanged options)
|
|
63
|
+
|
|
64
|
+
For the expected use case (2-5 generated options at a time), full re-render is fine.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 3. Session State Architecture: Critical Finding
|
|
69
|
+
|
|
70
|
+
### The problem
|
|
71
|
+
|
|
72
|
+
StreamWeaver's existing state lives in `session[:streamlit_state]` -- a Sinatra cookie session. The agent thread (which pushes state) has **no access** to this session:
|
|
73
|
+
|
|
74
|
+
- Cookie sessions are per-HTTP-request, scoped to the Rack middleware
|
|
75
|
+
- Background threads (agent simulator, timer threads) cannot read or write cookie sessions
|
|
76
|
+
- Even if they could, cookie size limit (4KB) would be exceeded by option data
|
|
77
|
+
|
|
78
|
+
### The solution: External state store
|
|
79
|
+
|
|
80
|
+
The spike uses an in-memory hash (`SpikeState`) keyed by session ID:
|
|
81
|
+
- Session ID is stored in the cookie session (`state[:spike_session]`)
|
|
82
|
+
- The App DSL block reads from `SpikeState.get(session_id)` during render
|
|
83
|
+
- The agent thread writes to `SpikeState.update(session_id)` when pushing options
|
|
84
|
+
- Thread-safe via Mutex
|
|
85
|
+
|
|
86
|
+
### T10 recommendation
|
|
87
|
+
|
|
88
|
+
Implement `DeckState` as a file-backed JSON store (as planned in architecture doc):
|
|
89
|
+
- Session ID assigned on first visit, stored in cookie
|
|
90
|
+
- Full state persisted to `tmp/streamweaver/sessions/{session_id}.json`
|
|
91
|
+
- The `App` DSL block reads from `DeckState` during render
|
|
92
|
+
- Agent pushes to `DeckState` via HTTP endpoint or direct file write
|
|
93
|
+
- Mutex or file locking for thread safety
|
|
94
|
+
|
|
95
|
+
This aligns with the Q1 resolution (file-backed state, option B) from the architecture doc.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 4. SSE Integration: Works As-Is
|
|
100
|
+
|
|
101
|
+
StreamWeaver's existing `Streamer` infrastructure works for push-to-state with no modifications:
|
|
102
|
+
|
|
103
|
+
- `streamer.replace("#target", html)` broadcasts to all SSE connections
|
|
104
|
+
- The browser's SSE handler (already built into StreamWeaver's JS) receives the event and morphs the target element
|
|
105
|
+
- No new SSE event types needed -- `replace` is sufficient
|
|
106
|
+
- No modifications to `Pushable`, `Streamer`, or `Feed` required
|
|
107
|
+
|
|
108
|
+
### Minor concern: `stream` block pattern
|
|
109
|
+
|
|
110
|
+
The spike uses the `stream` block to capture the streamer reference and run a timeout checker:
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
stream do |streamer|
|
|
114
|
+
$streamer_ref = streamer
|
|
115
|
+
loop do
|
|
116
|
+
sleep 1
|
|
117
|
+
# check timeouts
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
This works but feels like an abuse of the stream block (which is designed for app-defined push logic, not infrastructure). T10 should consider whether timeout checking should be a timer (`every(1)`) or a separate mechanism.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 5. Timeout Handling
|
|
127
|
+
|
|
128
|
+
### What works
|
|
129
|
+
|
|
130
|
+
Server-side timeout checking (in the stream/timer block) detects `:generating` states that have exceeded 15 seconds and transitions them to `:timed_out`. This is reliable because:
|
|
131
|
+
- It doesn't depend on the browser being connected
|
|
132
|
+
- It doesn't depend on the agent being responsive
|
|
133
|
+
- It runs independently of any request
|
|
134
|
+
|
|
135
|
+
### Edge case: Partial receipt
|
|
136
|
+
|
|
137
|
+
If 2 of 3 options arrive before timeout, the user sees 2 real options + 1 skeleton, then a timeout message. The received options are preserved in state. This is the correct behavior -- partial results are better than no results.
|
|
138
|
+
|
|
139
|
+
### T10 consideration: Configurable timeout
|
|
140
|
+
|
|
141
|
+
15 seconds is hardcoded in the spike. T10 should make this configurable:
|
|
142
|
+
```ruby
|
|
143
|
+
generate_more timeout: 30 # or whatever
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 6. Cancellation
|
|
149
|
+
|
|
150
|
+
### What works
|
|
151
|
+
|
|
152
|
+
Two-phase cancellation:
|
|
153
|
+
1. User clicks Cancel -> sets a cancellation flag
|
|
154
|
+
2. Agent thread checks flag before each option push
|
|
155
|
+
3. If cancelled, agent stops generating and transitions to `:idle`
|
|
156
|
+
4. SSE pushes re-rendered content (options received so far are preserved)
|
|
157
|
+
|
|
158
|
+
### Edge case: Race between cancel and push
|
|
159
|
+
|
|
160
|
+
If the agent pushes an option at the exact moment the user cancels, the option is still added to state. This is acceptable -- the user gets one extra option, which is harmless.
|
|
161
|
+
|
|
162
|
+
### Edge case: Cancel arrives after all options received
|
|
163
|
+
|
|
164
|
+
If the user clicks cancel but all options have already been received (`:generating` already transitioned to `:idle`), the cancel is a no-op. This is correct.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## 7. Edge Cases Found
|
|
169
|
+
|
|
170
|
+
| Edge Case | What Happens | Severity |
|
|
171
|
+
|-----------|-------------|----------|
|
|
172
|
+
| Double-click Generate | Second click is a no-op (button hidden during `:generating`) | Low -- handled |
|
|
173
|
+
| Cancel after timeout | No-op (already `:timed_out`) | Low -- handled |
|
|
174
|
+
| Page refresh during generation | New render reads current state, shows received options + skeletons | Medium -- works correctly |
|
|
175
|
+
| Agent dies mid-generation | Timeout fires after 15s, preserves partial options | Medium -- handled by timeout |
|
|
176
|
+
| Multiple browser tabs | All tabs share same session state (via session ID in cookie) | Low -- acceptable for spike |
|
|
177
|
+
| Very long prompt text | No issue (prompt stored in state, displayed in descriptions) | Low |
|
|
178
|
+
| Rapid generate-cancel-generate | Works but agent thread may have stale request in queue | Medium -- T10 should add request versioning |
|
|
179
|
+
|
|
180
|
+
### T10 should address: Request versioning
|
|
181
|
+
|
|
182
|
+
If a user clicks Generate, then Cancel, then Generate again quickly, the agent thread may still be processing the first request while the second is queued. T10 should add a `request_id` or version number to distinguish stale requests from current ones.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 8. Decisions for T10
|
|
187
|
+
|
|
188
|
+
| Decision | Spike Choice | T10 Recommendation |
|
|
189
|
+
|----------|-------------|-------------------|
|
|
190
|
+
| State store | In-memory hash | File-backed JSON (DeckState) |
|
|
191
|
+
| Re-render scope | Full content div | Full content div (adequate for <= 10 options) |
|
|
192
|
+
| Cancellation | Side-channel flag + transient state | Keep side-channel flag pattern |
|
|
193
|
+
| Timeout detection | Stream block loop | Timer (`every(1)`) or dedicated infrastructure |
|
|
194
|
+
| Agent communication | Direct shared memory | HTTP endpoints (`POST /deck/add_option`, `GET /deck/pending`) |
|
|
195
|
+
| Skeleton rendering | Inline HTML | Component class (`SkeletonPlaceholder`) |
|
|
196
|
+
| Status display | Inline HTML banner | Component class (`GenerateMoreControls`) |
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## 9. What StreamWeaver Needs (Gem Changes for T10)
|
|
201
|
+
|
|
202
|
+
### No changes needed to existing infrastructure
|
|
203
|
+
|
|
204
|
+
- `Pushable` module: works as-is
|
|
205
|
+
- `Streamer` class: works as-is
|
|
206
|
+
- `Feed` class: works as-is (for external agent pushing)
|
|
207
|
+
- SSE endpoint (`GET /stream`): works as-is
|
|
208
|
+
- Push endpoint (`POST /stream/push`): works as-is
|
|
209
|
+
|
|
210
|
+
### New additions needed
|
|
211
|
+
|
|
212
|
+
1. **`DeckState` class** -- File-backed state store keyed by session ID
|
|
213
|
+
2. **Custom routes** -- `POST /deck/generate`, `GET /deck/pending`, `POST /deck/add_option`
|
|
214
|
+
3. **`GenerateMoreControls` component** -- DSL method for generate UI
|
|
215
|
+
4. **`SkeletonPlaceholder` component** -- Shimmer loading cards
|
|
216
|
+
5. **State-change SSE notification** -- `streamer.notify_state_change` (or reuse `replace`)
|
|
217
|
+
6. **Timeout timer** -- Server-side timer checking generate timeouts
|
|
218
|
+
|
|
219
|
+
### Nice-to-have
|
|
220
|
+
|
|
221
|
+
- `notify_state_change` method on Streamer that triggers a full page re-render (instead of replacing a specific target). Currently the spike uses `replace("#spike-content", html)` which requires rendering HTML outside the DSL block. A `notify_state_change` that tells the browser to re-fetch `/update` would be cleaner and more aligned with StreamWeaver's reactive model.
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## 10. Summary
|
|
226
|
+
|
|
227
|
+
**Push-to-state works.** The spike validates the core architectural decision. The pattern is:
|
|
228
|
+
1. User action sets generate state to `:generating`
|
|
229
|
+
2. Skeletons appear (rendered from state by the component tree)
|
|
230
|
+
3. Agent pushes options to a shared state store
|
|
231
|
+
4. SSE notifies browser to re-render
|
|
232
|
+
5. Component tree reads updated state, renders new options + remaining skeletons
|
|
233
|
+
6. When all options received, state returns to `:idle`
|
|
234
|
+
|
|
235
|
+
**Key constraint:** Session state MUST be in a server-side store accessible to background threads. Cookie sessions are insufficient. File-backed JSON (DeckState) is the right approach.
|
|
236
|
+
|
|
237
|
+
**The existing StreamWeaver Pushable/Streamer/Feed infrastructure works without modification** for the push-to-state pattern. The only new infrastructure needed is the state store and the HTTP endpoints for agent communication.
|