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,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../support"
|
|
4
|
+
|
|
5
|
+
module StreamWeaverBench
|
|
6
|
+
module Fixtures
|
|
7
|
+
module Ledger
|
|
8
|
+
VARIANTS = %i[legacy_full named_full named_fragments update_filter].freeze
|
|
9
|
+
INTERACTIONS = %i[create edit validation filter delete].freeze
|
|
10
|
+
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
def build(variant)
|
|
14
|
+
metrics = Metrics.new
|
|
15
|
+
people = MemoryStore.new(PEOPLE)
|
|
16
|
+
named = variant != :legacy_full
|
|
17
|
+
scoped = %i[named_fragments update_filter].include?(variant)
|
|
18
|
+
app = StreamWeaver::App.new("Ledger #{variant}") do
|
|
19
|
+
state[:query] ||= ""
|
|
20
|
+
state[:error] ||= ""
|
|
21
|
+
state[:selection] ||= nil
|
|
22
|
+
|
|
23
|
+
if named
|
|
24
|
+
action(:create_person, updates: (scoped ? :people : nil)) { |_s, _| metrics.callback!; people.create(id: 51, name: "New Person", email: "new@example.test", touched: "2026-07-01") }
|
|
25
|
+
action(:edit_person) { |_s, key| metrics.callback!; people.update(key, name: "Edited Person") }
|
|
26
|
+
action(:invalid_person) { |s, _| metrics.callback!; s[:error] = "Name is required" }
|
|
27
|
+
action(:delete_person) { |_s, key| metrics.callback!; people.destroy(key) }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
toolbar = proc do
|
|
31
|
+
if named
|
|
32
|
+
button "Create", action: :create_person, key: "new"
|
|
33
|
+
button "Invalid", action: :invalid_person, key: "invalid"
|
|
34
|
+
else
|
|
35
|
+
button("Create") { |_s| metrics.callback!; people.create(id: 51, name: "New Person", email: "new@example.test", touched: "2026-07-01") }
|
|
36
|
+
button("Invalid") { |s| metrics.callback!; s[:error] = "Name is required" }
|
|
37
|
+
end
|
|
38
|
+
text state[:error]
|
|
39
|
+
form :quick_edit do
|
|
40
|
+
text_field :name, default: "Person 01"
|
|
41
|
+
text_field :email, default: "person01@example.test"
|
|
42
|
+
submit("Save quick edit") { |_values| metrics.callback! }
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
scoped ? fragment(:toolbar, &toolbar) : instance_exec(&toolbar)
|
|
46
|
+
|
|
47
|
+
people_view = proc do
|
|
48
|
+
text_field :query, placeholder: "Search", on_change: ->(_s, _value) { metrics.callback! } if variant == :update_filter
|
|
49
|
+
shown = people.all.select { |person| person[:name].downcase.include?(state[:query].to_s.downcase) }
|
|
50
|
+
table shown, row_key: ->(person) { person[:id] } do
|
|
51
|
+
column :name
|
|
52
|
+
column :email
|
|
53
|
+
column :touched
|
|
54
|
+
column :actions do |person|
|
|
55
|
+
if named
|
|
56
|
+
hstack do
|
|
57
|
+
button "Edit", action: :edit_person, key: person[:id]
|
|
58
|
+
button "Delete", action: :delete_person, key: person[:id]
|
|
59
|
+
end
|
|
60
|
+
else
|
|
61
|
+
hstack do
|
|
62
|
+
button("Edit", key: person[:id]) { |_s| metrics.callback!; people.update(person[:id], name: "Edited Person") }
|
|
63
|
+
button("Delete", key: person[:id]) { |_s| metrics.callback!; people.destroy(person[:id]) }
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
scoped ? fragment(:people, &people_view) : instance_exec(&people_view)
|
|
70
|
+
end
|
|
71
|
+
StreamWeaverBench.instrument(app, metrics)
|
|
72
|
+
[app.generate, metrics]
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../support"
|
|
4
|
+
|
|
5
|
+
module StreamWeaverBench
|
|
6
|
+
module Fixtures
|
|
7
|
+
module Warroom
|
|
8
|
+
VARIANTS = %i[legacy_full named_full named_fragments named_fragments_oob].freeze
|
|
9
|
+
INTERACTIONS = %i[note_append column_move].freeze
|
|
10
|
+
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
def build(variant)
|
|
14
|
+
metrics = Metrics.new
|
|
15
|
+
stories = MemoryStore.new(STORIES)
|
|
16
|
+
named = variant != :legacy_full
|
|
17
|
+
app = StreamWeaver::App.new("Warroom #{variant}") do
|
|
18
|
+
state[:query] ||= ""
|
|
19
|
+
state[:error] ||= ""
|
|
20
|
+
state[:selection] ||= stories.all.first[:id]
|
|
21
|
+
if named
|
|
22
|
+
action(:append_note) { |_s, key| metrics.callback!; stories.find(key)[:notes] << "Appended note" }
|
|
23
|
+
action(:move_story) { |_s, key| metrics.callback!; stories.update(key, column: "Active") }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
board = proc do
|
|
27
|
+
grid(columns: 3) do
|
|
28
|
+
%w[Ready Active Done].each do |column|
|
|
29
|
+
card(title: column) do
|
|
30
|
+
stories.all.select { |story| story[:column] == column }.each { |story| text "#{story[:id]} — #{story[:title]}" }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
scoped = %i[named_fragments named_fragments_oob].include?(variant)
|
|
36
|
+
scoped ? fragment(:board, &board) : instance_exec(&board)
|
|
37
|
+
detail = proc do
|
|
38
|
+
story = stories.find(state[:selection])
|
|
39
|
+
text story[:title]
|
|
40
|
+
story[:notes].each { |note| text note }
|
|
41
|
+
form :note do
|
|
42
|
+
text_area :body, default: "Appended note"
|
|
43
|
+
submit("Add note") { |_values| metrics.callback! }
|
|
44
|
+
end
|
|
45
|
+
if named
|
|
46
|
+
button "Append note", action: :append_note, key: story[:id]
|
|
47
|
+
button "Move story", action: :move_story, key: story[:id], updates: (variant == :named_fragments_oob ? :board : nil)
|
|
48
|
+
else
|
|
49
|
+
button("Append note") { |_s| metrics.callback!; story[:notes] << "Appended note" }
|
|
50
|
+
button("Move story") { |_s| metrics.callback!; stories.update(story[:id], column: "Active") }
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
scoped ? fragment(:detail, &detail) : instance_exec(&detail)
|
|
54
|
+
end
|
|
55
|
+
StreamWeaverBench.instrument(app, metrics)
|
|
56
|
+
[app.generate, metrics]
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Phase-1 dispatch benchmark
|
|
2
|
+
|
|
3
|
+
Rack end-to-end time is the in-process request duration; server time excludes Rack::Test response handling.
|
|
4
|
+
|
|
5
|
+
## ledger: create
|
|
6
|
+
|
|
7
|
+
| variant | median server ms | p95 server ms | median rack ms | p95 rack ms | response bytes | request bytes | rebuilds | callbacks | changed nodes / full | vs-baseline ratio |
|
|
8
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
9
|
+
| phlex_baseline | 0.072 | 0.160 | 0.072 | 0.160 | 140 | 0 | 0 | 1 | 7 / 356 | 1.00 |
|
|
10
|
+
| legacy_full | 3.463 | 8.082 | 3.464 | 8.083 | 75444 | 0 | 2 | 1 | 430 / 442 | 538.89 |
|
|
11
|
+
| named_full | 5.603 | 8.759 | 5.604 | 8.760 | 96075 | 0 | 1 | 1 | 430 / 442 | 686.25 |
|
|
12
|
+
| named_fragments | 7.309 | 16.644 | 7.310 | 16.645 | 99327 | 0 | 1 | 1 | 431 / 444 | 709.48 |
|
|
13
|
+
| update_filter | 7.304 | 15.314 | 7.306 | 15.316 | 99680 | 0 | 1 | 1 | 432 / 445 | 712.00 |
|
|
14
|
+
|
|
15
|
+
**FAIL** — `named_fragments` response 99327 B <= 1.20 × baseline 140 B (168.0 B).
|
|
16
|
+
|
|
17
|
+
**FAIL** — `update_filter` response 99680 B <= 1.20 × baseline 140 B (168.0 B).
|
|
18
|
+
|
|
19
|
+
## ledger: edit
|
|
20
|
+
|
|
21
|
+
| variant | median server ms | p95 server ms | median rack ms | p95 rack ms | response bytes | request bytes | rebuilds | callbacks | changed nodes / full | vs-baseline ratio |
|
|
22
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
23
|
+
| phlex_baseline | 0.074 | 0.099 | 0.074 | 0.099 | 147 | 0 | 0 | 1 | 7 / 356 | 1.00 |
|
|
24
|
+
| legacy_full | 5.166 | 7.880 | 5.168 | 7.881 | 74037 | 0 | 2 | 1 | 422 / 442 | 503.65 |
|
|
25
|
+
| named_full | 7.925 | 10.217 | 7.926 | 10.218 | 94271 | 0 | 1 | 1 | 422 / 442 | 641.30 |
|
|
26
|
+
| named_fragments | 9.109 | 13.200 | 9.110 | 13.202 | 95467 | 0 | 1 | 1 | 414 / 444 | 649.44 |
|
|
27
|
+
| update_filter | 9.437 | 11.729 | 9.438 | 11.730 | 95820 | 0 | 1 | 1 | 415 / 445 | 651.84 |
|
|
28
|
+
|
|
29
|
+
**FAIL** — `named_fragments` response 95467 B <= 1.20 × baseline 147 B (176.4 B).
|
|
30
|
+
|
|
31
|
+
**FAIL** — `update_filter` response 95820 B <= 1.20 × baseline 147 B (176.4 B).
|
|
32
|
+
|
|
33
|
+
## ledger: validation
|
|
34
|
+
|
|
35
|
+
| variant | median server ms | p95 server ms | median rack ms | p95 rack ms | response bytes | request bytes | rebuilds | callbacks | changed nodes / full | vs-baseline ratio |
|
|
36
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
37
|
+
| phlex_baseline | 0.068 | 0.075 | 0.068 | 0.075 | 100 | 0 | 0 | 1 | 4 / 356 | 1.00 |
|
|
38
|
+
| legacy_full | 5.987 | 10.311 | 5.987 | 10.313 | 74061 | 0 | 2 | 1 | 422 / 442 | 740.61 |
|
|
39
|
+
| named_full | 10.815 | 16.052 | 10.816 | 16.053 | 94295 | 0 | 1 | 1 | 422 / 442 | 942.95 |
|
|
40
|
+
| named_fragments | 9.625 | 13.420 | 9.625 | 13.420 | 2083 | 0 | 1 | 1 | 9 / 444 | 20.83 |
|
|
41
|
+
| update_filter | 10.424 | 15.887 | 10.425 | 15.888 | 2083 | 0 | 1 | 1 | 9 / 445 | 20.83 |
|
|
42
|
+
|
|
43
|
+
**FAIL** — `named_fragments` response 2083 B <= 1.20 × baseline 100 B (120.0 B).
|
|
44
|
+
|
|
45
|
+
**FAIL** — `update_filter` response 2083 B <= 1.20 × baseline 100 B (120.0 B).
|
|
46
|
+
|
|
47
|
+
## ledger: filter
|
|
48
|
+
|
|
49
|
+
| variant | median server ms | p95 server ms | median rack ms | p95 rack ms | response bytes | request bytes | rebuilds | callbacks | changed nodes / full | vs-baseline ratio |
|
|
50
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
51
|
+
| phlex_baseline | 0.093 | 0.137 | 0.093 | 0.137 | 175 | 15 | 0 | 1 | 8 / 356 | 1.00 |
|
|
52
|
+
| legacy_full | 5.587 | 8.797 | 5.588 | 8.798 | 8427 | 15 | 1 | 0 | 30 / 442 | 48.15 |
|
|
53
|
+
| named_full | 6.568 | 10.267 | 6.569 | 10.268 | 9110 | 15 | 1 | 0 | 30 / 442 | 52.06 |
|
|
54
|
+
| named_fragments | 6.447 | 9.268 | 6.448 | 9.269 | 9412 | 15 | 1 | 0 | 32 / 444 | 53.78 |
|
|
55
|
+
| update_filter | 7.679 | 15.253 | 7.681 | 15.253 | 9774 | 15 | 1 | 1 | 33 / 445 | 55.85 |
|
|
56
|
+
|
|
57
|
+
**FAIL** — `named_fragments` response 9412 B <= 1.20 × baseline 175 B (210.0 B).
|
|
58
|
+
|
|
59
|
+
**FAIL** — `update_filter` response 9774 B <= 1.20 × baseline 175 B (210.0 B).
|
|
60
|
+
|
|
61
|
+
## ledger: delete
|
|
62
|
+
|
|
63
|
+
| variant | median server ms | p95 server ms | median rack ms | p95 rack ms | response bytes | request bytes | rebuilds | callbacks | changed nodes / full | vs-baseline ratio |
|
|
64
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
65
|
+
| phlex_baseline | 0.064 | 0.068 | 0.064 | 0.069 | 0 | 0 | 0 | 1 | 0 / 356 | Inf |
|
|
66
|
+
| legacy_full | 9.806 | 13.792 | 9.808 | 13.793 | 72607 | 0 | 2 | 1 | 414 / 442 | Inf |
|
|
67
|
+
| named_full | 15.270 | 20.859 | 15.271 | 20.861 | 92447 | 0 | 1 | 1 | 414 / 442 | Inf |
|
|
68
|
+
| named_fragments | 15.908 | 23.024 | 15.910 | 23.026 | 93583 | 0 | 1 | 1 | 406 / 444 | Inf |
|
|
69
|
+
| update_filter | 17.374 | 26.321 | 17.375 | 26.322 | 93936 | 0 | 1 | 1 | 407 / 445 | Inf |
|
|
70
|
+
|
|
71
|
+
**FAIL** — `named_fragments` response 93583 B <= 1.20 × baseline 0 B (0.0 B).
|
|
72
|
+
|
|
73
|
+
**FAIL** — `update_filter` response 93936 B <= 1.20 × baseline 0 B (0.0 B).
|
|
74
|
+
|
|
75
|
+
## warroom: note_append
|
|
76
|
+
|
|
77
|
+
| variant | median server ms | p95 server ms | median rack ms | p95 rack ms | response bytes | request bytes | rebuilds | callbacks | changed nodes / full | vs-baseline ratio |
|
|
78
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
79
|
+
| phlex_baseline | 0.067 | 0.074 | 0.067 | 0.074 | 147 | 0 | 0 | 1 | 6 / 23 | 1.00 |
|
|
80
|
+
| legacy_full | 10.135 | 17.264 | 10.136 | 17.266 | 2549 | 0 | 2 | 1 | 26 / 45 | 17.34 |
|
|
81
|
+
| named_full | 14.052 | 19.983 | 14.053 | 19.985 | 2928 | 0 | 1 | 1 | 26 / 45 | 19.92 |
|
|
82
|
+
| named_fragments | 16.454 | 47.339 | 16.455 | 47.342 | 2635 | 0 | 1 | 1 | 10 / 47 | 17.93 |
|
|
83
|
+
| named_fragments_oob | 15.482 | 22.797 | 15.483 | 22.799 | 2654 | 0 | 1 | 1 | 10 / 47 | 18.05 |
|
|
84
|
+
|
|
85
|
+
**FAIL** — `named_fragments` response 2635 B <= 1.20 × baseline 147 B (176.4 B).
|
|
86
|
+
|
|
87
|
+
**FAIL** — `named_fragments_oob` response 2654 B <= 1.20 × baseline 147 B (176.4 B).
|
|
88
|
+
|
|
89
|
+
## warroom: column_move
|
|
90
|
+
|
|
91
|
+
| variant | median server ms | p95 server ms | median rack ms | p95 rack ms | response bytes | request bytes | rebuilds | callbacks | changed nodes / full | vs-baseline ratio |
|
|
92
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
93
|
+
| phlex_baseline | 0.075 | 0.082 | 0.075 | 0.082 | 370 | 0 | 0 | 1 | 12 / 23 | 1.00 |
|
|
94
|
+
| legacy_full | 11.041 | 16.529 | 11.041 | 16.530 | 2514 | 0 | 2 | 1 | 25 / 45 | 6.79 |
|
|
95
|
+
| named_full | 15.912 | 21.876 | 15.913 | 21.877 | 2893 | 0 | 1 | 1 | 25 / 45 | 7.82 |
|
|
96
|
+
| named_fragments | 17.256 | 23.950 | 17.258 | 23.952 | 2600 | 0 | 1 | 1 | 9 / 47 | 7.03 |
|
|
97
|
+
| named_fragments_oob | 18.317 | 28.865 | 18.318 | 28.866 | 3107 | 0 | 1 | 1 | 26 / 47 | 8.40 |
|
|
98
|
+
|
|
99
|
+
**FAIL** — `named_fragments` response 2600 B <= 1.20 × baseline 370 B (444.0 B).
|
|
100
|
+
|
|
101
|
+
**FAIL** — `named_fragments_oob` response 3107 B <= 1.20 × baseline 370 B (444.0 B).
|
|
102
|
+
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Phase-1 dispatch benchmark
|
|
2
|
+
|
|
3
|
+
Rack end-to-end time is the in-process request duration; server time excludes Rack::Test response handling.
|
|
4
|
+
|
|
5
|
+
## ledger: create
|
|
6
|
+
|
|
7
|
+
| variant | median server ms | p95 server ms | median rack ms | p95 rack ms | response bytes | request bytes | rebuilds | callbacks | changed nodes / full | vs-baseline ratio |
|
|
8
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
9
|
+
| phlex_baseline | 0.069 | 0.185 | 0.069 | 0.186 | 140 | 0 | 0 | 1 | 7 / 356 | 1.00 |
|
|
10
|
+
| legacy_full | 3.343 | 10.680 | 3.344 | 10.681 | 71179 | 0 | 2 | 1 | 430 / 442 | 508.42 |
|
|
11
|
+
| named_full | 5.424 | 7.052 | 5.425 | 7.052 | 91810 | 0 | 1 | 1 | 430 / 442 | 655.79 |
|
|
12
|
+
| named_fragments | 7.190 | 11.612 | 7.191 | 11.614 | 95062 | 0 | 1 | 1 | 431 / 444 | 679.01 |
|
|
13
|
+
| update_filter | 7.456 | 14.048 | 7.457 | 14.049 | 95415 | 0 | 1 | 1 | 432 / 445 | 681.54 |
|
|
14
|
+
|
|
15
|
+
**FAIL** — `named_fragments` response 95062 B <= 1.20 × baseline 140 B (168.0 B).
|
|
16
|
+
|
|
17
|
+
**FAIL** — `update_filter` response 95415 B <= 1.20 × baseline 140 B (168.0 B).
|
|
18
|
+
|
|
19
|
+
## ledger: edit
|
|
20
|
+
|
|
21
|
+
| variant | median server ms | p95 server ms | median rack ms | p95 rack ms | response bytes | request bytes | rebuilds | callbacks | changed nodes / full | vs-baseline ratio |
|
|
22
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
23
|
+
| phlex_baseline | 0.072 | 0.154 | 0.072 | 0.154 | 147 | 0 | 0 | 1 | 7 / 356 | 1.00 |
|
|
24
|
+
| legacy_full | 5.223 | 16.443 | 5.225 | 16.445 | 69848 | 0 | 2 | 1 | 422 / 442 | 475.16 |
|
|
25
|
+
| named_full | 7.716 | 13.097 | 7.717 | 13.099 | 90082 | 0 | 1 | 1 | 422 / 442 | 612.80 |
|
|
26
|
+
| named_fragments | 9.392 | 12.408 | 9.394 | 12.409 | 91278 | 0 | 1 | 1 | 414 / 444 | 620.94 |
|
|
27
|
+
| update_filter | 9.289 | 12.532 | 9.290 | 12.534 | 91631 | 0 | 1 | 1 | 415 / 445 | 623.34 |
|
|
28
|
+
|
|
29
|
+
**FAIL** — `named_fragments` response 91278 B <= 1.20 × baseline 147 B (176.4 B).
|
|
30
|
+
|
|
31
|
+
**FAIL** — `update_filter` response 91631 B <= 1.20 × baseline 147 B (176.4 B).
|
|
32
|
+
|
|
33
|
+
## ledger: validation
|
|
34
|
+
|
|
35
|
+
| variant | median server ms | p95 server ms | median rack ms | p95 rack ms | response bytes | request bytes | rebuilds | callbacks | changed nodes / full | vs-baseline ratio |
|
|
36
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
37
|
+
| phlex_baseline | 0.072 | 0.179 | 0.072 | 0.180 | 100 | 0 | 0 | 1 | 4 / 356 | 1.00 |
|
|
38
|
+
| legacy_full | 6.742 | 10.122 | 6.743 | 10.124 | 69876 | 0 | 2 | 1 | 422 / 442 | 698.76 |
|
|
39
|
+
| named_full | 10.619 | 15.149 | 10.621 | 15.151 | 90110 | 0 | 1 | 1 | 422 / 442 | 901.10 |
|
|
40
|
+
| named_fragments | 9.460 | 15.436 | 9.462 | 15.438 | 2100 | 0 | 1 | 1 | 9 / 444 | 21.00 |
|
|
41
|
+
| update_filter | 9.434 | 12.735 | 9.434 | 12.736 | 2100 | 0 | 1 | 1 | 9 / 445 | 21.00 |
|
|
42
|
+
|
|
43
|
+
**FAIL** — `named_fragments` response 2100 B <= 1.20 × baseline 100 B (120.0 B).
|
|
44
|
+
|
|
45
|
+
**FAIL** — `update_filter` response 2100 B <= 1.20 × baseline 100 B (120.0 B).
|
|
46
|
+
|
|
47
|
+
## ledger: filter
|
|
48
|
+
|
|
49
|
+
| variant | median server ms | p95 server ms | median rack ms | p95 rack ms | response bytes | request bytes | rebuilds | callbacks | changed nodes / full | vs-baseline ratio |
|
|
50
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
51
|
+
| phlex_baseline | 0.105 | 0.294 | 0.105 | 0.295 | 175 | 15 | 0 | 1 | 8 / 356 | 1.00 |
|
|
52
|
+
| legacy_full | 7.487 | 13.387 | 7.487 | 13.390 | 4242 | 15 | 1 | 0 | 30 / 442 | 24.24 |
|
|
53
|
+
| named_full | 7.067 | 12.082 | 7.069 | 12.082 | 4925 | 15 | 1 | 0 | 30 / 442 | 28.14 |
|
|
54
|
+
| named_fragments | 6.660 | 9.655 | 6.661 | 9.655 | 5227 | 15 | 1 | 0 | 32 / 444 | 29.87 |
|
|
55
|
+
| update_filter | 7.808 | 12.616 | 7.810 | 12.618 | 5589 | 15 | 1 | 1 | 33 / 445 | 31.94 |
|
|
56
|
+
|
|
57
|
+
**FAIL** — `named_fragments` response 5227 B <= 1.20 × baseline 175 B (210.0 B).
|
|
58
|
+
|
|
59
|
+
**FAIL** — `update_filter` response 5589 B <= 1.20 × baseline 175 B (210.0 B).
|
|
60
|
+
|
|
61
|
+
## ledger: delete
|
|
62
|
+
|
|
63
|
+
| variant | median server ms | p95 server ms | median rack ms | p95 rack ms | response bytes | request bytes | rebuilds | callbacks | changed nodes / full | vs-baseline ratio |
|
|
64
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
65
|
+
| phlex_baseline | 0.066 | 0.081 | 0.066 | 0.082 | 0 | 0 | 0 | 1 | 0 / 356 | Inf |
|
|
66
|
+
| legacy_full | 10.433 | 16.797 | 10.434 | 16.798 | 68505 | 0 | 2 | 1 | 414 / 442 | Inf |
|
|
67
|
+
| named_full | 14.991 | 21.514 | 14.994 | 21.515 | 88345 | 0 | 1 | 1 | 414 / 442 | Inf |
|
|
68
|
+
| named_fragments | 16.332 | 23.606 | 16.334 | 23.607 | 89481 | 0 | 1 | 1 | 406 / 444 | Inf |
|
|
69
|
+
| update_filter | 16.146 | 23.649 | 16.147 | 23.650 | 89834 | 0 | 1 | 1 | 407 / 445 | Inf |
|
|
70
|
+
|
|
71
|
+
**FAIL** — `named_fragments` response 89481 B <= 1.20 × baseline 0 B (0.0 B).
|
|
72
|
+
|
|
73
|
+
**FAIL** — `update_filter` response 89834 B <= 1.20 × baseline 0 B (0.0 B).
|
|
74
|
+
|
|
75
|
+
## warroom: note_append
|
|
76
|
+
|
|
77
|
+
| variant | median server ms | p95 server ms | median rack ms | p95 rack ms | response bytes | request bytes | rebuilds | callbacks | changed nodes / full | vs-baseline ratio |
|
|
78
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
79
|
+
| phlex_baseline | 0.069 | 0.425 | 0.069 | 0.425 | 147 | 0 | 0 | 1 | 6 / 23 | 1.00 |
|
|
80
|
+
| legacy_full | 10.179 | 17.379 | 10.181 | 17.380 | 1723 | 0 | 2 | 1 | 26 / 45 | 11.72 |
|
|
81
|
+
| named_full | 14.862 | 22.651 | 14.863 | 22.652 | 2102 | 0 | 1 | 1 | 26 / 45 | 14.30 |
|
|
82
|
+
| named_fragments | 15.308 | 26.121 | 15.310 | 26.123 | 1795 | 0 | 1 | 1 | 10 / 47 | 12.21 |
|
|
83
|
+
| named_fragments_oob | 15.934 | 23.927 | 15.936 | 23.927 | 1814 | 0 | 1 | 1 | 10 / 47 | 12.34 |
|
|
84
|
+
|
|
85
|
+
**FAIL** — `named_fragments` response 1795 B <= 1.20 × baseline 147 B (176.4 B).
|
|
86
|
+
|
|
87
|
+
**FAIL** — `named_fragments_oob` response 1814 B <= 1.20 × baseline 147 B (176.4 B).
|
|
88
|
+
|
|
89
|
+
## warroom: column_move
|
|
90
|
+
|
|
91
|
+
| variant | median server ms | p95 server ms | median rack ms | p95 rack ms | response bytes | request bytes | rebuilds | callbacks | changed nodes / full | vs-baseline ratio |
|
|
92
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
93
|
+
| phlex_baseline | 0.074 | 0.400 | 0.075 | 0.400 | 370 | 0 | 0 | 1 | 12 / 23 | 1.00 |
|
|
94
|
+
| legacy_full | 11.895 | 17.866 | 11.897 | 17.867 | 1703 | 0 | 2 | 1 | 25 / 45 | 4.60 |
|
|
95
|
+
| named_full | 16.398 | 21.608 | 16.398 | 21.610 | 2082 | 0 | 1 | 1 | 25 / 45 | 5.63 |
|
|
96
|
+
| named_fragments | 17.878 | 23.662 | 17.879 | 23.662 | 1775 | 0 | 1 | 1 | 9 / 47 | 4.80 |
|
|
97
|
+
| named_fragments_oob | 17.959 | 25.112 | 17.961 | 25.113 | 2282 | 0 | 1 | 1 | 26 / 47 | 6.17 |
|
|
98
|
+
|
|
99
|
+
**FAIL** — `named_fragments` response 1775 B <= 1.20 × baseline 370 B (444.0 B).
|
|
100
|
+
|
|
101
|
+
**FAIL** — `named_fragments_oob` response 2282 B <= 1.20 × baseline 370 B (444.0 B).
|
|
102
|
+
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Phase-1 dispatch benchmark
|
|
2
|
+
|
|
3
|
+
Rack end-to-end time is the in-process request duration; server time excludes Rack::Test response handling.
|
|
4
|
+
|
|
5
|
+
## ledger: create
|
|
6
|
+
|
|
7
|
+
| variant | median server ms | p95 server ms | median rack ms | p95 rack ms | response bytes | request bytes | rebuilds | callbacks | changed nodes / full | vs-baseline ratio |
|
|
8
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
9
|
+
| phlex_baseline | 0.077 | 0.141 | 0.077 | 0.142 | 140 | 0 | 0 | 1 | 7 / 356 | 1.00 |
|
|
10
|
+
| legacy_full | 3.287 | 9.863 | 3.288 | 9.864 | 72463 | 0 | 2 | 1 | 430 / 442 | 517.59 |
|
|
11
|
+
| named_full | 4.837 | 7.102 | 4.838 | 7.102 | 93094 | 0 | 1 | 1 | 430 / 442 | 664.96 |
|
|
12
|
+
| named_fragments | 3.795 | 5.566 | 3.795 | 5.568 | 3945 | 0 | 1 | 1 | 17 / 444 | 28.18 |
|
|
13
|
+
| update_filter | 4.274 | 6.624 | 4.275 | 6.624 | 3945 | 0 | 1 | 1 | 17 / 445 | 28.18 |
|
|
14
|
+
|
|
15
|
+
**PASS** — `named_fragments` response 3945 B <= 1.20 × baseline 140 B (168.0 B) or <= 8192 B floor.
|
|
16
|
+
|
|
17
|
+
**PASS** — `update_filter` response 3945 B <= 1.20 × baseline 140 B (168.0 B) or <= 8192 B floor.
|
|
18
|
+
|
|
19
|
+
## ledger: edit
|
|
20
|
+
|
|
21
|
+
| variant | median server ms | p95 server ms | median rack ms | p95 rack ms | response bytes | request bytes | rebuilds | callbacks | changed nodes / full | vs-baseline ratio |
|
|
22
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
23
|
+
| phlex_baseline | 0.074 | 0.109 | 0.075 | 0.110 | 147 | 0 | 0 | 1 | 7 / 356 | 1.00 |
|
|
24
|
+
| legacy_full | 4.771 | 7.026 | 4.772 | 7.027 | 71107 | 0 | 2 | 1 | 422 / 442 | 483.72 |
|
|
25
|
+
| named_full | 7.445 | 10.962 | 7.446 | 10.963 | 91341 | 0 | 1 | 1 | 422 / 442 | 621.37 |
|
|
26
|
+
| named_fragments | 6.711 | 10.778 | 6.711 | 10.778 | 1999 | 0 | 1 | 1 | 9 / 444 | 13.60 |
|
|
27
|
+
| update_filter | 7.403 | 10.551 | 7.404 | 10.552 | 1999 | 0 | 1 | 1 | 9 / 445 | 13.60 |
|
|
28
|
+
|
|
29
|
+
**PASS** — `named_fragments` response 1999 B <= 1.20 × baseline 147 B (176.4 B) or <= 8192 B floor.
|
|
30
|
+
|
|
31
|
+
**PASS** — `update_filter` response 1999 B <= 1.20 × baseline 147 B (176.4 B) or <= 8192 B floor.
|
|
32
|
+
|
|
33
|
+
## ledger: validation
|
|
34
|
+
|
|
35
|
+
| variant | median server ms | p95 server ms | median rack ms | p95 rack ms | response bytes | request bytes | rebuilds | callbacks | changed nodes / full | vs-baseline ratio |
|
|
36
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
37
|
+
| phlex_baseline | 0.070 | 0.096 | 0.070 | 0.096 | 100 | 0 | 0 | 1 | 4 / 356 | 1.00 |
|
|
38
|
+
| legacy_full | 5.332 | 8.063 | 5.332 | 8.065 | 71135 | 0 | 2 | 1 | 422 / 442 | 711.35 |
|
|
39
|
+
| named_full | 9.938 | 16.164 | 9.939 | 16.166 | 91369 | 0 | 1 | 1 | 422 / 442 | 913.69 |
|
|
40
|
+
| named_fragments | 10.268 | 16.219 | 10.269 | 16.220 | 2100 | 0 | 1 | 1 | 9 / 444 | 21.00 |
|
|
41
|
+
| update_filter | 12.519 | 20.038 | 12.520 | 20.040 | 2100 | 0 | 1 | 1 | 9 / 445 | 21.00 |
|
|
42
|
+
|
|
43
|
+
**PASS** — `named_fragments` response 2100 B <= 1.20 × baseline 100 B (120.0 B) or <= 8192 B floor.
|
|
44
|
+
|
|
45
|
+
**PASS** — `update_filter` response 2100 B <= 1.20 × baseline 100 B (120.0 B) or <= 8192 B floor.
|
|
46
|
+
|
|
47
|
+
## ledger: filter
|
|
48
|
+
|
|
49
|
+
| variant | median server ms | p95 server ms | median rack ms | p95 rack ms | response bytes | request bytes | rebuilds | callbacks | changed nodes / full | vs-baseline ratio |
|
|
50
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
51
|
+
| phlex_baseline | 0.094 | 0.223 | 0.094 | 0.223 | 175 | 15 | 0 | 1 | 8 / 356 | 1.00 |
|
|
52
|
+
| legacy_full | 5.048 | 10.228 | 5.049 | 10.230 | 4284 | 15 | 1 | 0 | 30 / 442 | 24.48 |
|
|
53
|
+
| named_full | 5.947 | 9.940 | 5.947 | 9.941 | 4967 | 15 | 1 | 0 | 30 / 442 | 28.38 |
|
|
54
|
+
| named_fragments | 6.432 | 10.047 | 6.433 | 10.047 | 5289 | 15 | 1 | 0 | 32 / 444 | 30.22 |
|
|
55
|
+
| update_filter | 6.812 | 12.296 | 6.814 | 12.298 | 5651 | 15 | 1 | 1 | 33 / 445 | 32.29 |
|
|
56
|
+
|
|
57
|
+
**PASS** — `named_fragments` response 5289 B <= 1.20 × baseline 175 B (210.0 B) or <= 8192 B floor.
|
|
58
|
+
|
|
59
|
+
**PASS** — `update_filter` response 5651 B <= 1.20 × baseline 175 B (210.0 B) or <= 8192 B floor.
|
|
60
|
+
|
|
61
|
+
## ledger: delete
|
|
62
|
+
|
|
63
|
+
| variant | median server ms | p95 server ms | median rack ms | p95 rack ms | response bytes | request bytes | rebuilds | callbacks | changed nodes / full | vs-baseline ratio |
|
|
64
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
65
|
+
| phlex_baseline | 0.072 | 0.114 | 0.072 | 0.115 | 0 | 0 | 0 | 1 | 0 / 356 | Inf |
|
|
66
|
+
| legacy_full | 9.727 | 16.812 | 9.729 | 16.814 | 69740 | 0 | 2 | 1 | 414 / 442 | Inf |
|
|
67
|
+
| named_full | 17.151 | 21.766 | 17.152 | 21.768 | 89580 | 0 | 1 | 1 | 414 / 442 | Inf |
|
|
68
|
+
| named_fragments | 16.349 | 21.140 | 16.351 | 21.141 | 172 | 0 | 1 | 1 | 1 / 444 | Inf |
|
|
69
|
+
| update_filter | 14.547 | 19.288 | 14.548 | 19.289 | 172 | 0 | 1 | 1 | 1 / 445 | Inf |
|
|
70
|
+
|
|
71
|
+
**PASS** — `named_fragments` response 172 B <= 1.20 × baseline 0 B (0.0 B) or <= 8192 B floor.
|
|
72
|
+
|
|
73
|
+
**PASS** — `update_filter` response 172 B <= 1.20 × baseline 0 B (0.0 B) or <= 8192 B floor.
|
|
74
|
+
|
|
75
|
+
## warroom: note_append
|
|
76
|
+
|
|
77
|
+
| variant | median server ms | p95 server ms | median rack ms | p95 rack ms | response bytes | request bytes | rebuilds | callbacks | changed nodes / full | vs-baseline ratio |
|
|
78
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
79
|
+
| phlex_baseline | 0.068 | 0.138 | 0.068 | 0.138 | 147 | 0 | 0 | 1 | 6 / 23 | 1.00 |
|
|
80
|
+
| legacy_full | 9.103 | 14.929 | 9.103 | 14.931 | 1723 | 0 | 2 | 1 | 26 / 45 | 11.72 |
|
|
81
|
+
| named_full | 14.339 | 18.781 | 14.340 | 18.782 | 2102 | 0 | 1 | 1 | 26 / 45 | 14.30 |
|
|
82
|
+
| named_fragments | 16.124 | 22.015 | 16.126 | 22.016 | 1795 | 0 | 1 | 1 | 10 / 47 | 12.21 |
|
|
83
|
+
| named_fragments_oob | 16.704 | 25.253 | 16.705 | 25.255 | 1814 | 0 | 1 | 1 | 10 / 47 | 12.34 |
|
|
84
|
+
|
|
85
|
+
**PASS** — `named_fragments` response 1795 B <= 1.20 × baseline 147 B (176.4 B) or <= 8192 B floor.
|
|
86
|
+
|
|
87
|
+
**PASS** — `named_fragments_oob` response 1814 B <= 1.20 × baseline 147 B (176.4 B) or <= 8192 B floor.
|
|
88
|
+
|
|
89
|
+
## warroom: column_move
|
|
90
|
+
|
|
91
|
+
| variant | median server ms | p95 server ms | median rack ms | p95 rack ms | response bytes | request bytes | rebuilds | callbacks | changed nodes / full | vs-baseline ratio |
|
|
92
|
+
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
|
93
|
+
| phlex_baseline | 0.077 | 0.163 | 0.077 | 0.164 | 370 | 0 | 0 | 1 | 12 / 23 | 1.00 |
|
|
94
|
+
| legacy_full | 10.802 | 17.991 | 10.804 | 17.993 | 1703 | 0 | 2 | 1 | 25 / 45 | 4.60 |
|
|
95
|
+
| named_full | 16.677 | 22.323 | 16.679 | 22.324 | 2082 | 0 | 1 | 1 | 25 / 45 | 5.63 |
|
|
96
|
+
| named_fragments | 17.006 | 24.690 | 17.006 | 24.692 | 1775 | 0 | 1 | 1 | 9 / 47 | 4.80 |
|
|
97
|
+
| named_fragments_oob | 18.229 | 26.563 | 18.231 | 26.564 | 2282 | 0 | 1 | 1 | 26 / 47 | 6.17 |
|
|
98
|
+
|
|
99
|
+
**PASS** — `named_fragments` response 1775 B <= 1.20 × baseline 370 B (444.0 B) or <= 8192 B floor.
|
|
100
|
+
|
|
101
|
+
**PASS** — `named_fragments_oob` response 2282 B <= 1.20 × baseline 370 B (444.0 B) or <= 8192 B floor.
|
|
102
|
+
|
data/bench/run.rb
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
5
|
+
|
|
6
|
+
require "benchmark"
|
|
7
|
+
require "fileutils"
|
|
8
|
+
require "optparse"
|
|
9
|
+
require_relative "fixtures/ledger"
|
|
10
|
+
require_relative "fixtures/warroom"
|
|
11
|
+
require_relative "baselines/ledger"
|
|
12
|
+
require_relative "baselines/warroom"
|
|
13
|
+
|
|
14
|
+
module StreamWeaverBench
|
|
15
|
+
Sample = Data.define(:server_ms, :rack_ms, :response_bytes, :request_bytes, :rebuilds, :callbacks, :changed_nodes, :full_nodes)
|
|
16
|
+
|
|
17
|
+
class Runner
|
|
18
|
+
ABSOLUTE_FLOOR_BYTES = 8_192
|
|
19
|
+
|
|
20
|
+
BASELINE_PATHS = {
|
|
21
|
+
ledger: { create: "/create", edit: "/edit", validation: "/invalid", filter: "/filter", delete: "/delete" },
|
|
22
|
+
warroom: { note_append: "/note", column_move: "/move" }
|
|
23
|
+
}.freeze
|
|
24
|
+
LABELS = { create: "Create", edit: "Edit", validation: "Invalid", delete: "Delete", note_append: "Append note", column_move: "Move story" }.freeze
|
|
25
|
+
|
|
26
|
+
def initialize(iterations:, warmups:, write: true)
|
|
27
|
+
@iterations = iterations
|
|
28
|
+
@warmups = warmups
|
|
29
|
+
@write = write
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def run
|
|
33
|
+
lines = ["# Phase-1 dispatch benchmark", "", "Rack end-to-end time is the in-process request duration; server time excludes Rack::Test response handling.", ""]
|
|
34
|
+
run_fixture(:ledger, Fixtures::Ledger, Baselines::Ledger, lines)
|
|
35
|
+
run_fixture(:warroom, Fixtures::Warroom, Baselines::Warroom, lines)
|
|
36
|
+
report = lines.join("\n") + "\n"
|
|
37
|
+
puts report
|
|
38
|
+
write_report(report) if @write
|
|
39
|
+
report
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def run_fixture(name, fixture, baseline, lines)
|
|
45
|
+
fixture::INTERACTIONS.each do |interaction|
|
|
46
|
+
baseline_samples = samples { measure_baseline(name, baseline, interaction) }
|
|
47
|
+
baseline_bytes = median(baseline_samples.map(&:response_bytes))
|
|
48
|
+
lines << "## #{name}: #{interaction}"
|
|
49
|
+
lines << ""
|
|
50
|
+
lines << "| variant | median server ms | p95 server ms | median rack ms | p95 rack ms | response bytes | request bytes | rebuilds | callbacks | changed nodes / full | vs-baseline ratio |"
|
|
51
|
+
lines << "|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|"
|
|
52
|
+
append_row(lines, "phlex_baseline", baseline_samples, baseline_bytes)
|
|
53
|
+
fixture::VARIANTS.each do |variant|
|
|
54
|
+
measured = samples { measure_streamweaver(fixture, variant, interaction) }
|
|
55
|
+
append_row(lines, variant, measured, baseline_bytes)
|
|
56
|
+
end
|
|
57
|
+
scoped_variants = fixture::VARIANTS.select { |variant| variant.to_s.include?("fragment") || variant == :update_filter }
|
|
58
|
+
scoped_variants.each do |variant|
|
|
59
|
+
measured = samples { measure_streamweaver(fixture, variant, interaction) }
|
|
60
|
+
bytes = median(measured.map(&:response_bytes))
|
|
61
|
+
lines << ""
|
|
62
|
+
# Gate: ratio <= 1.20x OR absolute floor 8KB. Sub-floor responses are below
|
|
63
|
+
# perceptual/network significance; ratios vs sub-200B surgical baselines
|
|
64
|
+
# measure granularity, not slowness (plan intent: kill whole-tree payloads).
|
|
65
|
+
passed = bytes <= baseline_bytes * 1.20 || bytes <= ABSOLUTE_FLOOR_BYTES
|
|
66
|
+
lines << "**#{passed ? 'PASS' : 'FAIL'}** — `#{variant}` response #{bytes} B <= 1.20 × baseline #{baseline_bytes} B (#{(baseline_bytes * 1.20).round(1)} B) or <= #{ABSOLUTE_FLOOR_BYTES} B floor."
|
|
67
|
+
end
|
|
68
|
+
lines << ""
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def samples
|
|
73
|
+
@warmups.times { yield }
|
|
74
|
+
Array.new(@iterations) { yield }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def measure_baseline(name, baseline, interaction)
|
|
78
|
+
app, metrics = baseline.build
|
|
79
|
+
session = Rack::Test::Session.new(Rack::MockSession.new(app))
|
|
80
|
+
full_nodes = Html.node_count(session.get("/").body)
|
|
81
|
+
params = interaction == :filter ? { query: "Person 01" } : {}
|
|
82
|
+
path = BASELINE_PATHS.fetch(name).fetch(interaction)
|
|
83
|
+
metrics.reset!
|
|
84
|
+
response, rack_ms, server_ms = timed_request(session, path, params)
|
|
85
|
+
Sample.new(server_ms:, rack_ms:, response_bytes: response.body.bytesize,
|
|
86
|
+
request_bytes: Rack::Utils.build_nested_query(params).bytesize, rebuilds: 0,
|
|
87
|
+
callbacks: metrics.callbacks, changed_nodes: Html.node_count(response.body), full_nodes: full_nodes)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def measure_streamweaver(fixture, variant, interaction)
|
|
91
|
+
app, metrics = fixture.build(variant)
|
|
92
|
+
session = Rack::Test::Session.new(Rack::MockSession.new(app))
|
|
93
|
+
setup = session.get("/")
|
|
94
|
+
full_nodes = Html.node_count(setup.body)
|
|
95
|
+
params = {}
|
|
96
|
+
path = if interaction == :filter
|
|
97
|
+
params = { query: "Person 01" }
|
|
98
|
+
if variant == :update_filter
|
|
99
|
+
params[:_sw_fragment] = Html.fragment_token(setup.body) if Html.fragment_token(setup.body)
|
|
100
|
+
"/event/query"
|
|
101
|
+
else
|
|
102
|
+
"/update"
|
|
103
|
+
end
|
|
104
|
+
else
|
|
105
|
+
Html.action_path(setup.body, LABELS.fetch(interaction))
|
|
106
|
+
end
|
|
107
|
+
metrics.reset!
|
|
108
|
+
response, rack_ms, server_ms = timed_request(session, path, params)
|
|
109
|
+
raise "#{variant} #{interaction}: HTTP #{response.status}" unless response.ok?
|
|
110
|
+
Sample.new(server_ms:, rack_ms:, response_bytes: response.body.bytesize,
|
|
111
|
+
request_bytes: Rack::Utils.build_nested_query(params).bytesize, rebuilds: metrics.rebuilds,
|
|
112
|
+
callbacks: metrics.callbacks, changed_nodes: Html.node_count(response.body), full_nodes: full_nodes)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def timed_request(session, path, params)
|
|
116
|
+
started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
117
|
+
response = nil
|
|
118
|
+
server_started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
119
|
+
response = session.post(path, params, "HTTP_HX_REQUEST" => "true")
|
|
120
|
+
server_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - server_started) * 1000
|
|
121
|
+
rack_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - started) * 1000
|
|
122
|
+
[response, rack_ms, server_ms]
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def append_row(lines, variant, values, baseline_bytes)
|
|
126
|
+
response_bytes = median(values.map(&:response_bytes))
|
|
127
|
+
lines << format("| %s | %.3f | %.3f | %.3f | %.3f | %d | %d | %d | %d | %d / %d | %.2f |",
|
|
128
|
+
variant, median(values.map(&:server_ms)), percentile(values.map(&:server_ms), 0.95),
|
|
129
|
+
median(values.map(&:rack_ms)), percentile(values.map(&:rack_ms), 0.95), response_bytes,
|
|
130
|
+
median(values.map(&:request_bytes)), median(values.map(&:rebuilds)), median(values.map(&:callbacks)),
|
|
131
|
+
median(values.map(&:changed_nodes)), median(values.map(&:full_nodes)), baseline_bytes.zero? ? Float::INFINITY : response_bytes.fdiv(baseline_bytes))
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def median(values) = percentile(values, 0.50)
|
|
135
|
+
def percentile(values, fraction) = values.sort.fetch([(values.length * fraction).ceil - 1, 0].max)
|
|
136
|
+
def write_report(report)
|
|
137
|
+
sha = `git rev-parse --short HEAD`.strip
|
|
138
|
+
FileUtils.mkdir_p(File.expand_path("results", __dir__))
|
|
139
|
+
File.write(File.expand_path("results/#{sha}.md", __dir__), report)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
options = { iterations: 30, warmups: 5, write: true }
|
|
145
|
+
OptionParser.new do |parser|
|
|
146
|
+
parser.on("--iterations N", Integer) { |value| options[:iterations] = value }
|
|
147
|
+
parser.on("--warmups N", Integer) { |value| options[:warmups] = value }
|
|
148
|
+
parser.on("--no-write") { options[:write] = false }
|
|
149
|
+
end.parse!
|
|
150
|
+
abort "iterations must be positive" unless options[:iterations].positive?
|
|
151
|
+
StreamWeaverBench::Runner.new(**options).run
|