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,511 @@
|
|
|
1
|
+
# StreamWeaver Plan Format Exploration: Ruby DSL vs MDX vs Org-mode
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-06-17
|
|
4
|
+
**Status:** Exploratory design — not implemented
|
|
5
|
+
**TL;DR:** Org-mode wins for checked-in plan files. Ruby DSL wins for live canvas. The formats serve different moments and don't need to compete.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## The Problem
|
|
10
|
+
|
|
11
|
+
Builder's visual-plan uses MDX (Markdown + JSX components). It's clever, but
|
|
12
|
+
they themselves acknowledge a tension: **MDX looks ugly when read raw**. Custom
|
|
13
|
+
components like `<ImplementationMap files={[{path: "...", note: "..."}]} />` are
|
|
14
|
+
not human-readable without the renderer. The whole point of a checked-in plan
|
|
15
|
+
file is that a human (or Claude) can read it cold without spinning up a server.
|
|
16
|
+
|
|
17
|
+
StreamWeaver already has a live Ruby DSL for the canvas. But a canvas-push DSL
|
|
18
|
+
as a committed artifact is also awkward — it's executable Ruby, not prose.
|
|
19
|
+
|
|
20
|
+
So three candidates emerge:
|
|
21
|
+
|
|
22
|
+
1. **MDX** — what Builder does (Markdown + JSX props)
|
|
23
|
+
2. **Org-mode** — structured plaintext with native extensibility
|
|
24
|
+
3. **"SW-Ruby"** — StreamWeaver DSL embedded in a plan file format
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## What We Proved in 30 Minutes
|
|
29
|
+
|
|
30
|
+
`org-ruby 0.9.12` is **already installed** in the StreamWeaver gem environment.
|
|
31
|
+
After a quick spike, the org-ruby parser:
|
|
32
|
+
|
|
33
|
+
- Parses `#+TITLE:`, `#+DATE:`, `#+SW_VERSION:` as in-buffer settings cleanly
|
|
34
|
+
- Exposes `:PROPERTIES:` drawers per-headline with full key-value access
|
|
35
|
+
- Headline + property combo maps directly to StreamWeaver component dispatch
|
|
36
|
+
- `#+BEGIN_SRC ruby` blocks are code-highlighted already
|
|
37
|
+
- Tables parse as table objects
|
|
38
|
+
|
|
39
|
+
This isn't theoretical. A working prototype parser is ~60 lines of Ruby.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Format Comparison: Same Plan in All Three
|
|
44
|
+
|
|
45
|
+
### The Plan: Guest Auth Flow
|
|
46
|
+
|
|
47
|
+
A plan for adding guest checkout to a Rails app.
|
|
48
|
+
We want to show: prose, file map, a decision, a mermaid diagram, annotated code,
|
|
49
|
+
and a risk callout.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
### Option A: MDX (Builder's way)
|
|
54
|
+
|
|
55
|
+
```mdx
|
|
56
|
+
---
|
|
57
|
+
title: "Guest Auth Flow"
|
|
58
|
+
brief: "Enable checkout without account creation."
|
|
59
|
+
version: 2
|
|
60
|
+
planId: "plan_84f748..."
|
|
61
|
+
source: "agent-native-plan"
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
<RichText id="b1">
|
|
65
|
+
### Overview
|
|
66
|
+
|
|
67
|
+
Enable users to proceed through checkout without creating an account.
|
|
68
|
+
Currently all paths require registration, causing ~30% mobile drop-off.
|
|
69
|
+
</RichText>
|
|
70
|
+
|
|
71
|
+
<ImplementationMap
|
|
72
|
+
id="b2"
|
|
73
|
+
files={[
|
|
74
|
+
{ path: "lib/auth/session.rb", note: "Add guest token issuer" },
|
|
75
|
+
{ path: "app/routes/checkout.rb", note: "Branch on guest vs auth user" },
|
|
76
|
+
{ path: "db/schema.rb", note: "Add guest_sessions table" },
|
|
77
|
+
]}
|
|
78
|
+
/>
|
|
79
|
+
|
|
80
|
+
<Decision
|
|
81
|
+
id="b3"
|
|
82
|
+
question={"How should guest sessions be stored?"}
|
|
83
|
+
options={[
|
|
84
|
+
{ id: "o1", label: "JWT", detail: "Stateless, no DB lookup" },
|
|
85
|
+
{ id: "o2", label: "Opaque token", detail: "Revocable, account merge later", recommended: true },
|
|
86
|
+
]}
|
|
87
|
+
/>
|
|
88
|
+
|
|
89
|
+
<Callout id="b4" tone="risk">
|
|
90
|
+
Guest sessions must expire. Proposed TTL: 24h — confirm with data team.
|
|
91
|
+
</Callout>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Raw readability:** ⚠️ Moderate. Prose sections are fine. Component props
|
|
95
|
+
are JSON-in-JSX — readable but verbose. The `files={[...]}` syntax is noise
|
|
96
|
+
that disappears in the renderer.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
### Option B: Org-mode (proposed SW-Org format)
|
|
101
|
+
|
|
102
|
+
```org
|
|
103
|
+
#+TITLE: Guest Auth Flow
|
|
104
|
+
#+DATE: 2026-06-17
|
|
105
|
+
#+BRIEF: Enable checkout without account creation.
|
|
106
|
+
#+SW_VERSION: 1
|
|
107
|
+
|
|
108
|
+
* Overview
|
|
109
|
+
|
|
110
|
+
Enable users to proceed through checkout without creating an account.
|
|
111
|
+
Currently all paths require registration, causing ~30% mobile drop-off.
|
|
112
|
+
|
|
113
|
+
* Implementation Map
|
|
114
|
+
:PROPERTIES:
|
|
115
|
+
:SW_COMPONENT: implementation_map
|
|
116
|
+
:END:
|
|
117
|
+
|
|
118
|
+
| lib/auth/session.rb | Add guest token issuer |
|
|
119
|
+
| app/routes/checkout.rb | Branch on guest vs auth user |
|
|
120
|
+
| db/schema.rb | Add guest_sessions table |
|
|
121
|
+
|
|
122
|
+
* Decision: Token Storage
|
|
123
|
+
:PROPERTIES:
|
|
124
|
+
:SW_COMPONENT: decision
|
|
125
|
+
:SW_RECOMMENDED: opaque
|
|
126
|
+
:END:
|
|
127
|
+
|
|
128
|
+
| jwt | Stateless, no DB lookup |
|
|
129
|
+
| opaque | Revocable, supports account merge later |
|
|
130
|
+
|
|
131
|
+
* Auth Flow Architecture
|
|
132
|
+
:PROPERTIES:
|
|
133
|
+
:SW_COMPONENT: diagram
|
|
134
|
+
:SW_DIAGRAM: mermaid
|
|
135
|
+
:END:
|
|
136
|
+
|
|
137
|
+
#+BEGIN_SRC mermaid
|
|
138
|
+
sequenceDiagram
|
|
139
|
+
User->>App: Request checkout
|
|
140
|
+
App->>Auth: Issue guest token
|
|
141
|
+
Auth-->>App: JWT (role: guest, TTL: 24h)
|
|
142
|
+
App-->>User: Checkout continues
|
|
143
|
+
#+END_SRC
|
|
144
|
+
|
|
145
|
+
* Issue Guest Token
|
|
146
|
+
:PROPERTIES:
|
|
147
|
+
:SW_COMPONENT: annotated_code
|
|
148
|
+
:SW_LANG: ruby
|
|
149
|
+
:END:
|
|
150
|
+
|
|
151
|
+
#+BEGIN_SRC ruby
|
|
152
|
+
def issue_guest_token(email)
|
|
153
|
+
JWT.encode({ sub: email, role: 'guest' }, SECRET, exp: 24.hours.from_now)
|
|
154
|
+
end
|
|
155
|
+
#+END_SRC
|
|
156
|
+
|
|
157
|
+
#+BEGIN_ANNOTATIONS
|
|
158
|
+
[1] Guest tokens use restricted scope — limits what APIs they can call
|
|
159
|
+
[2] 24h TTL matches cleanup job cadence on the data team's cron
|
|
160
|
+
#+END_ANNOTATIONS
|
|
161
|
+
|
|
162
|
+
* Risk: Session Expiry
|
|
163
|
+
:PROPERTIES:
|
|
164
|
+
:SW_COMPONENT: callout
|
|
165
|
+
:SW_TONE: risk
|
|
166
|
+
:END:
|
|
167
|
+
|
|
168
|
+
Guest sessions must expire to prevent unbounded DB growth.
|
|
169
|
+
Proposed TTL: 24h — needs confirmation from data team.
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Raw readability:** ✅ Excellent. Every section reads as plain prose/table.
|
|
173
|
+
The `:PROPERTIES:` drawers are structured metadata that org-mode users understand
|
|
174
|
+
immediately. Tables are ASCII-legible. Code blocks are standard. Nothing requires
|
|
175
|
+
a renderer to understand.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
### Option C: StreamWeaver Ruby DSL (canvas-push format)
|
|
180
|
+
|
|
181
|
+
```ruby
|
|
182
|
+
# guest-auth-flow.sw.rb
|
|
183
|
+
# SW Plan: Guest Auth Flow
|
|
184
|
+
# Brief: Enable checkout without account creation.
|
|
185
|
+
|
|
186
|
+
theme_preset :technical
|
|
187
|
+
|
|
188
|
+
header2 "Guest Auth Flow"
|
|
189
|
+
md "Enable users to proceed through checkout without creating an account."
|
|
190
|
+
|
|
191
|
+
implementation_map files: [
|
|
192
|
+
{ path: "lib/auth/session.rb", note: "Add guest token issuer" },
|
|
193
|
+
{ path: "app/routes/checkout.rb", note: "Branch on guest vs auth user" },
|
|
194
|
+
{ path: "db/schema.rb", note: "Add guest_sessions table" }
|
|
195
|
+
]
|
|
196
|
+
|
|
197
|
+
decision question: "How should guest sessions be stored?" do
|
|
198
|
+
option "jwt", "Stateless, no DB lookup"
|
|
199
|
+
option "opaque", "Revocable, supports account merge later", recommended: true
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
mermaid do
|
|
203
|
+
<<~MERMAID
|
|
204
|
+
sequenceDiagram
|
|
205
|
+
User->>App: Request checkout
|
|
206
|
+
App->>Auth: Issue guest token
|
|
207
|
+
Auth-->>App: JWT (role: guest, TTL: 24h)
|
|
208
|
+
MERMAID
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
annotated_code language: :ruby, annotations: [
|
|
212
|
+
{ line: 1, note: "Guest tokens use restricted scope" },
|
|
213
|
+
{ line: 2, note: "24h TTL matches cleanup job cadence" }
|
|
214
|
+
] do
|
|
215
|
+
<<~RUBY
|
|
216
|
+
def issue_guest_token(email)
|
|
217
|
+
JWT.encode({ sub: email, role: 'guest' }, SECRET, exp: 24.hours.from_now)
|
|
218
|
+
end
|
|
219
|
+
RUBY
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
callout :risk do
|
|
223
|
+
"Guest sessions must expire to prevent unbounded DB growth. TTL: 24h."
|
|
224
|
+
end
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**Raw readability:** ✅ Good for Ruby devs, readable for anyone. Clean method
|
|
228
|
+
names, block syntax is familiar. But it's executable — `require 'stream_weaver'`
|
|
229
|
+
and it runs. That's a feature AND a bug: nice for live push, weird for a
|
|
230
|
+
committed plan artifact.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Side-by-Side Analysis
|
|
235
|
+
|
|
236
|
+
| Criterion | MDX | Org-mode | SW-Ruby DSL |
|
|
237
|
+
|---|---|---|---|
|
|
238
|
+
| **Raw readability** | ⚠️ Moderate | ✅ Excellent | ✅ Good |
|
|
239
|
+
| **LLM writability** | ✅ Easy | ✅ Easy | ✅ Easy |
|
|
240
|
+
| **GitHub renders nicely** | ❌ Shows JSX noise | ✅ Renders as plain text, tables intact | ⚠️ Syntax highlighted but not semantic |
|
|
241
|
+
| **Emacs native** | ❌ | ✅ Full org-mode tooling | ❌ |
|
|
242
|
+
| **VS Code** | ✅ MDX extension | ✅ org-mode extension | ✅ Ruby extension |
|
|
243
|
+
| **Structured metadata** | ✅ Frontmatter YAML | ✅ In-buffer settings + drawers | ⚠️ Comments only |
|
|
244
|
+
| **Extensible blocks** | ✅ Custom JSX | ✅ Custom drawers + keywords | ✅ Method calls |
|
|
245
|
+
| **Parse complexity** | High (JSX AST) | Medium (org-ruby exists) | Low (just Ruby eval) |
|
|
246
|
+
| **SW integration** | Would need port | **~60 line parser → SW components** | **Direct — already SW** |
|
|
247
|
+
| **Stands alone without renderer** | ❌ JSX unreadable | ✅ Yes | ⚠️ Only with Ruby context |
|
|
248
|
+
| **Diff-friendly** | ⚠️ Okay | ✅ Great | ✅ Great |
|
|
249
|
+
| **Works in GitHub PR review** | ❌ | ✅ Readable tables, code blocks | ✅ Ruby coloring |
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## The Org-mode Advantage: What GitHub Actually Does
|
|
254
|
+
|
|
255
|
+
GitHub renders `.org` files natively — headings, bold, italic, links, tables,
|
|
256
|
+
code blocks. The `:PROPERTIES:` drawer is the one thing that doesn't render
|
|
257
|
+
beautifully (it shows as literal text), but it's concise and clearly structured.
|
|
258
|
+
|
|
259
|
+
Compare how a Decision block looks raw on GitHub:
|
|
260
|
+
|
|
261
|
+
**MDX (what you'd see in a PR):**
|
|
262
|
+
```
|
|
263
|
+
<Decision
|
|
264
|
+
id="b3"
|
|
265
|
+
question={"How should guest sessions be stored?"}
|
|
266
|
+
options={[
|
|
267
|
+
{ id: "o1", label: "JWT", detail: "Stateless, no DB lookup" },
|
|
268
|
+
{ id: "o2", label: "Opaque token", detail: "Revocable", recommended: true },
|
|
269
|
+
]}
|
|
270
|
+
/>
|
|
271
|
+
```
|
|
272
|
+
→ Unrenderable JSX. A reviewer sees noise.
|
|
273
|
+
|
|
274
|
+
**Org-mode (what you'd see in a PR):**
|
|
275
|
+
```
|
|
276
|
+
* Decision: Token Storage
|
|
277
|
+
:PROPERTIES:
|
|
278
|
+
:SW_COMPONENT: decision
|
|
279
|
+
:SW_RECOMMENDED: opaque
|
|
280
|
+
:END:
|
|
281
|
+
|
|
282
|
+
| jwt | Stateless, no DB lookup |
|
|
283
|
+
| opaque | Revocable, supports account merge later |
|
|
284
|
+
```
|
|
285
|
+
→ GitHub renders the table. The properties are visible but terse. A reviewer
|
|
286
|
+
understands the intent immediately, even without the StreamWeaver renderer.
|
|
287
|
+
|
|
288
|
+
This directly addresses Builder's cited concern: plan files should be readable
|
|
289
|
+
*raw*.
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Implementation Plan: SW-Org Format
|
|
294
|
+
|
|
295
|
+
### Phase 1 — Parser (~1 session, ~150 lines Ruby)
|
|
296
|
+
|
|
297
|
+
```ruby
|
|
298
|
+
# lib/stream_weaver/plan/org_parser.rb
|
|
299
|
+
|
|
300
|
+
module StreamWeaver
|
|
301
|
+
module Plan
|
|
302
|
+
class OrgParser
|
|
303
|
+
COMPONENT_MAP = {
|
|
304
|
+
"implementation_map" => :render_implementation_map,
|
|
305
|
+
"decision" => :render_decision,
|
|
306
|
+
"annotated_code" => :render_annotated_code,
|
|
307
|
+
"diagram" => :render_diagram,
|
|
308
|
+
"callout" => :render_callout,
|
|
309
|
+
"wireframe" => :render_wireframe,
|
|
310
|
+
"api_endpoint" => :render_api_endpoint,
|
|
311
|
+
nil => :render_prose, # no SW_COMPONENT = plain markdown
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
def initialize(org_text)
|
|
315
|
+
@parser = Orgmode::Parser.new(org_text)
|
|
316
|
+
@meta = @parser.in_buffer_settings
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def title = @meta["TITLE"]
|
|
320
|
+
def brief = @meta["BRIEF"]
|
|
321
|
+
def date = @meta["DATE"]
|
|
322
|
+
|
|
323
|
+
# Yields DSL blocks to a StreamWeaver App context
|
|
324
|
+
def to_canvas_dsl
|
|
325
|
+
@parser.headlines.map do |headline|
|
|
326
|
+
props = headline.property_drawer || {}
|
|
327
|
+
component = props["SW_COMPONENT"]
|
|
328
|
+
body = extract_body(headline)
|
|
329
|
+
{ component:, props:, headline: headline.headline_text, body: }
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
private
|
|
334
|
+
|
|
335
|
+
def extract_body(headline)
|
|
336
|
+
# Get raw lines, skip property drawer lines
|
|
337
|
+
lines = headline.body_lines.map do |l|
|
|
338
|
+
next unless l.respond_to?(:line)
|
|
339
|
+
next if [:property_drawer_begin_block,
|
|
340
|
+
:property_drawer_item,
|
|
341
|
+
:property_drawer_end_block].include?(l.paragraph_type)
|
|
342
|
+
l.line
|
|
343
|
+
end.compact
|
|
344
|
+
lines.join("\n").strip
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Phase 2 — Component renderers (~1-2 sessions)
|
|
352
|
+
|
|
353
|
+
Each `SW_COMPONENT` type gets a renderer that emits StreamWeaver DSL:
|
|
354
|
+
|
|
355
|
+
```ruby
|
|
356
|
+
# For implementation_map: parse org table rows → files array
|
|
357
|
+
def render_implementation_map(headline:, props:, body:, **)
|
|
358
|
+
files = parse_org_table(body).map do |path, note|
|
|
359
|
+
{ path: path.strip, note: note.strip }
|
|
360
|
+
end
|
|
361
|
+
implementation_map(files:)
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
# For decision: first column = option id/label, second = detail
|
|
365
|
+
# SW_RECOMMENDED property picks the winner
|
|
366
|
+
def render_decision(headline:, props:, body:, **)
|
|
367
|
+
recommended = props["SW_RECOMMENDED"]
|
|
368
|
+
options = parse_org_table(body).map do |label, detail|
|
|
369
|
+
{ label: label.strip, detail: detail.strip,
|
|
370
|
+
recommended: label.strip.downcase == recommended&.downcase }
|
|
371
|
+
end
|
|
372
|
+
decision(question: headline, options:)
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
# For annotated_code: SRC block + ANNOTATIONS block
|
|
376
|
+
def render_annotated_code(headline:, props:, body:, **)
|
|
377
|
+
code = extract_src_block(body)
|
|
378
|
+
notes = extract_annotations_block(body)
|
|
379
|
+
lang = props["SW_LANG"]&.to_sym || :ruby
|
|
380
|
+
annotated_code(language: lang, code:, annotations: notes)
|
|
381
|
+
end
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### Phase 3 — CLI integration (~half session)
|
|
385
|
+
|
|
386
|
+
```bash
|
|
387
|
+
# Push org plan to live canvas
|
|
388
|
+
streamweaver canvas-push <session> --file plan.org
|
|
389
|
+
|
|
390
|
+
# Serve org plan as standalone page
|
|
391
|
+
streamweaver serve plan.org
|
|
392
|
+
|
|
393
|
+
# Export running canvas as org plan
|
|
394
|
+
streamweaver canvas-export <session> --format org > plan.org
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Phase 4 — Bidirectional: Canvas → Org (~1 session)
|
|
398
|
+
|
|
399
|
+
The really interesting capability: Claude Code builds a live canvas, then you
|
|
400
|
+
can export it as a checked-in org plan file. Builder has no equivalent — their
|
|
401
|
+
plans live in the hosted DB. Your plans live in git.
|
|
402
|
+
|
|
403
|
+
```bash
|
|
404
|
+
streamweaver canvas-export guest-auth --format org > docs/plans/guest-auth.org
|
|
405
|
+
git add docs/plans/guest-auth.org && git commit -m "add guest auth plan"
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
---
|
|
409
|
+
|
|
410
|
+
## The Hybrid Architecture
|
|
411
|
+
|
|
412
|
+
These formats aren't competing — they serve different moments:
|
|
413
|
+
|
|
414
|
+
```
|
|
415
|
+
Planning Phase
|
|
416
|
+
──────────────
|
|
417
|
+
Claude Code thinks → canvas-push (live Ruby DSL)
|
|
418
|
+
↓ (user reviews in browser)
|
|
419
|
+
User approves → canvas-export --format org (checked-in artifact)
|
|
420
|
+
↓ (in git, readable in GitHub PR)
|
|
421
|
+
Future Claude Code → canvas-push --file plan.org (restore from org)
|
|
422
|
+
↓ (live again for the implementation phase)
|
|
423
|
+
Implementation done → streamweaver recap <session> (visual-recap equivalent)
|
|
424
|
+
↓
|
|
425
|
+
docs/recaps/YYYY-MM-DD-feature.org (committed recap)
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
The Ruby DSL is the **live wire** format — what Claude Code pushes in real time.
|
|
429
|
+
The org file is the **resting state** format — what gets committed, reviewed,
|
|
430
|
+
and loaded back.
|
|
431
|
+
|
|
432
|
+
You never need to write org by hand. Claude Code writes it. Humans read it raw.
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## Is This Better Than MDX?
|
|
437
|
+
|
|
438
|
+
For StreamWeaver's use case: yes, clearly, on three dimensions:
|
|
439
|
+
|
|
440
|
+
**1. Raw readability.** Org tables and headings render well on GitHub. MDX JSX
|
|
441
|
+
component syntax doesn't. Builder acknowledged this as a concern — org solves
|
|
442
|
+
it without any compromise.
|
|
443
|
+
|
|
444
|
+
**2. No external dependency.** MDX requires a React renderer and a hosted Plan
|
|
445
|
+
app. Org-mode parsing is a pure Ruby library, already installed. A plan file
|
|
446
|
+
is readable with zero infrastructure — any text editor, GitHub's web UI, Emacs,
|
|
447
|
+
VS Code with the org extension. Builder's plan files are essentially useless
|
|
448
|
+
without plan.agent-native.com.
|
|
449
|
+
|
|
450
|
+
**3. Bidirectionality.** Canvas → Org export is natural. MDX round-trips are
|
|
451
|
+
hard (JSX isn't designed for serialization). Org is a text format that's
|
|
452
|
+
designed to be written by programs and read by humans.
|
|
453
|
+
|
|
454
|
+
Where MDX wins: if you're in a React/JS ecosystem with a polished hosted viewer.
|
|
455
|
+
Builder has that viewer. StreamWeaver's viewer is your local browser tab. For
|
|
456
|
+
that context, org-mode is strictly better.
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
460
|
+
## Effort Estimate
|
|
461
|
+
|
|
462
|
+
| Phase | Work | Estimate |
|
|
463
|
+
|---|---|---|
|
|
464
|
+
| Org parser + component dispatch | New file, ~150 lines | 1 session |
|
|
465
|
+
| Component renderers (5 types) | New file, ~200 lines | 1-2 sessions |
|
|
466
|
+
| CLI `--file plan.org` flag | Extend canvas-push | 0.5 session |
|
|
467
|
+
| Canvas → Org export | New renderer | 1 session |
|
|
468
|
+
| Tests + example plans | — | 0.5 session |
|
|
469
|
+
| **Total** | | **4-5 sessions** |
|
|
470
|
+
|
|
471
|
+
Comparison: MDX support would require a JS parser (or porting org-ruby's
|
|
472
|
+
equivalent), a React component registry, and a hosted viewer. That's a full
|
|
473
|
+
project, not a feature.
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
## Open Questions
|
|
478
|
+
|
|
479
|
+
1. **Property drawer vs tag syntax?** Could use org tags instead of drawers
|
|
480
|
+
for component type: `* Implementation Map :implementation_map:` — less
|
|
481
|
+
verbose, but tags are normally for categorization not typing. Drawer is
|
|
482
|
+
semantically cleaner.
|
|
483
|
+
|
|
484
|
+
2. **Nested components in org?** Org doesn't have great support for nesting
|
|
485
|
+
blocks. A `columns` layout with two `wireframe` children is harder to express
|
|
486
|
+
than in MDX or Ruby DSL. Could use level-2 headlines under a level-1
|
|
487
|
+
`SW_COMPONENT: columns` headline. Works but is slightly awkward.
|
|
488
|
+
|
|
489
|
+
3. **Live push vs file format duality.** Should `canvas-push` accept both
|
|
490
|
+
a heredoc DSL (current) AND a `--file plan.org`? Yes — they're the same
|
|
491
|
+
push operation, different source formats. The parser is the adapter layer.
|
|
492
|
+
|
|
493
|
+
4. **Lossiness on export.** Some live canvas state (Alpine.js interactions,
|
|
494
|
+
chart data loaded from state, button callbacks) can't round-trip to org.
|
|
495
|
+
Export should be a planning document, not a full canvas snapshot. Clarify
|
|
496
|
+
that boundary early.
|
|
497
|
+
|
|
498
|
+
5. **Alternative: a simpler YAML-frontmatter markdown format?** Something like:
|
|
499
|
+
```markdown
|
|
500
|
+
---
|
|
501
|
+
sw_component: implementation_map
|
|
502
|
+
---
|
|
503
|
+
| path | note |
|
|
504
|
+
|------|------|
|
|
505
|
+
```
|
|
506
|
+
Lighter than org, but loses the native hierarchy. Org's heading structure
|
|
507
|
+
maps more naturally to a document with sections.
|
|
508
|
+
|
|
509
|
+
---
|
|
510
|
+
|
|
511
|
+
*Exploration by Selene — June 2026*
|