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,780 @@
|
|
|
1
|
+
<title>PRD: Calendar-Driven Travel State — aria</title>
|
|
2
|
+
<style>
|
|
3
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
4
|
+
|
|
5
|
+
:root {
|
|
6
|
+
--ground: #F5F4EF;
|
|
7
|
+
--surface: #EDECE6;
|
|
8
|
+
--border: #E0DED6;
|
|
9
|
+
--text: #181714;
|
|
10
|
+
--mid: #6B6860;
|
|
11
|
+
--faint: #A09D96;
|
|
12
|
+
--accent: #1E4ED8;
|
|
13
|
+
--accent-lt: #EEF2FF;
|
|
14
|
+
--code-bg: #ECEAE3;
|
|
15
|
+
--warn: #92400E;
|
|
16
|
+
--warn-bg: #FEF3C7;
|
|
17
|
+
--good: #065F46;
|
|
18
|
+
--good-bg: #D1FAE5;
|
|
19
|
+
|
|
20
|
+
--serif: Charter, 'Bitstream Charter', 'Sitka Text', Cambria, Georgia, serif;
|
|
21
|
+
--sans: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Arial, sans-serif;
|
|
22
|
+
--mono: 'SFMono-Regular', 'SF Mono', 'Cascadia Code', Menlo, 'Courier New', monospace;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
html { background: var(--ground); color: var(--text); font-family: var(--sans); font-size: 15px; line-height: 1.65; }
|
|
26
|
+
|
|
27
|
+
/* ── Layout ── */
|
|
28
|
+
.page { max-width: 1120px; margin: 0 auto; padding: 0 24px; }
|
|
29
|
+
|
|
30
|
+
.layout { display: grid; grid-template-columns: 1fr; gap: 0; }
|
|
31
|
+
|
|
32
|
+
@media (min-width: 900px) {
|
|
33
|
+
.layout { grid-template-columns: 220px 1fr; gap: 0 56px; align-items: start; }
|
|
34
|
+
.toc { position: sticky; top: 48px; }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* ── Doc header ── */
|
|
38
|
+
.doc-header {
|
|
39
|
+
border-bottom: 1px solid var(--border);
|
|
40
|
+
padding: 48px 0 32px;
|
|
41
|
+
margin-bottom: 48px;
|
|
42
|
+
}
|
|
43
|
+
.doc-eyebrow {
|
|
44
|
+
font-family: var(--mono);
|
|
45
|
+
font-size: 11px;
|
|
46
|
+
letter-spacing: .12em;
|
|
47
|
+
text-transform: uppercase;
|
|
48
|
+
color: var(--mid);
|
|
49
|
+
margin-bottom: 12px;
|
|
50
|
+
}
|
|
51
|
+
.doc-title {
|
|
52
|
+
font-family: var(--serif);
|
|
53
|
+
font-size: 2.2rem;
|
|
54
|
+
font-weight: 400;
|
|
55
|
+
line-height: 1.2;
|
|
56
|
+
text-wrap: balance;
|
|
57
|
+
color: var(--text);
|
|
58
|
+
margin-bottom: 20px;
|
|
59
|
+
}
|
|
60
|
+
.doc-meta {
|
|
61
|
+
display: flex;
|
|
62
|
+
flex-wrap: wrap;
|
|
63
|
+
gap: 8px 20px;
|
|
64
|
+
font-size: 13px;
|
|
65
|
+
color: var(--mid);
|
|
66
|
+
align-items: center;
|
|
67
|
+
}
|
|
68
|
+
.pill {
|
|
69
|
+
display: inline-flex;
|
|
70
|
+
align-items: center;
|
|
71
|
+
gap: 5px;
|
|
72
|
+
font-size: 11.5px;
|
|
73
|
+
font-weight: 500;
|
|
74
|
+
letter-spacing: .04em;
|
|
75
|
+
padding: 3px 9px;
|
|
76
|
+
border-radius: 3px;
|
|
77
|
+
background: var(--accent-lt);
|
|
78
|
+
color: var(--accent);
|
|
79
|
+
}
|
|
80
|
+
.pill-warn { background: var(--warn-bg); color: var(--warn); }
|
|
81
|
+
.pill-good { background: var(--good-bg); color: var(--good); }
|
|
82
|
+
|
|
83
|
+
/* ── TOC ── */
|
|
84
|
+
.toc { padding-top: 4px; }
|
|
85
|
+
.toc-label {
|
|
86
|
+
font-family: var(--mono);
|
|
87
|
+
font-size: 10.5px;
|
|
88
|
+
letter-spacing: .1em;
|
|
89
|
+
text-transform: uppercase;
|
|
90
|
+
color: var(--faint);
|
|
91
|
+
margin-bottom: 12px;
|
|
92
|
+
}
|
|
93
|
+
.toc ol { list-style: none; counter-reset: toc-counter; }
|
|
94
|
+
.toc li { counter-increment: toc-counter; margin-bottom: 1px; }
|
|
95
|
+
.toc a {
|
|
96
|
+
display: flex;
|
|
97
|
+
align-items: baseline;
|
|
98
|
+
gap: 8px;
|
|
99
|
+
color: var(--mid);
|
|
100
|
+
text-decoration: none;
|
|
101
|
+
font-size: 13px;
|
|
102
|
+
line-height: 1.5;
|
|
103
|
+
padding: 4px 8px;
|
|
104
|
+
border-radius: 3px;
|
|
105
|
+
transition: background .1s, color .1s;
|
|
106
|
+
}
|
|
107
|
+
.toc a::before {
|
|
108
|
+
content: counter(toc-counter, decimal-leading-zero);
|
|
109
|
+
font-family: var(--mono);
|
|
110
|
+
font-size: 10px;
|
|
111
|
+
color: var(--faint);
|
|
112
|
+
flex-shrink: 0;
|
|
113
|
+
}
|
|
114
|
+
.toc a:hover { background: var(--surface); color: var(--text); }
|
|
115
|
+
|
|
116
|
+
/* ── Main content ── */
|
|
117
|
+
.main { min-width: 0; padding-bottom: 80px; }
|
|
118
|
+
|
|
119
|
+
section { margin-bottom: 52px; }
|
|
120
|
+
|
|
121
|
+
.section-eyebrow {
|
|
122
|
+
font-family: var(--mono);
|
|
123
|
+
font-size: 10.5px;
|
|
124
|
+
letter-spacing: .1em;
|
|
125
|
+
text-transform: uppercase;
|
|
126
|
+
color: var(--faint);
|
|
127
|
+
margin-bottom: 10px;
|
|
128
|
+
display: flex;
|
|
129
|
+
align-items: center;
|
|
130
|
+
gap: 10px;
|
|
131
|
+
}
|
|
132
|
+
.section-eyebrow::after { content: ''; flex: 1; height: 1px; background: var(--border); }
|
|
133
|
+
|
|
134
|
+
h2 {
|
|
135
|
+
font-family: var(--serif);
|
|
136
|
+
font-size: 1.45rem;
|
|
137
|
+
font-weight: 400;
|
|
138
|
+
line-height: 1.25;
|
|
139
|
+
text-wrap: balance;
|
|
140
|
+
color: var(--text);
|
|
141
|
+
margin-bottom: 16px;
|
|
142
|
+
}
|
|
143
|
+
h3 {
|
|
144
|
+
font-family: var(--sans);
|
|
145
|
+
font-size: .875rem;
|
|
146
|
+
font-weight: 600;
|
|
147
|
+
letter-spacing: .02em;
|
|
148
|
+
text-transform: uppercase;
|
|
149
|
+
color: var(--text);
|
|
150
|
+
margin: 28px 0 10px;
|
|
151
|
+
}
|
|
152
|
+
h4 {
|
|
153
|
+
font-family: var(--sans);
|
|
154
|
+
font-size: .925rem;
|
|
155
|
+
font-weight: 600;
|
|
156
|
+
color: var(--text);
|
|
157
|
+
margin: 20px 0 8px;
|
|
158
|
+
}
|
|
159
|
+
p { margin-bottom: 14px; font-size: .95rem; }
|
|
160
|
+
p:last-child { margin-bottom: 0; }
|
|
161
|
+
|
|
162
|
+
ul, ol { margin: 0 0 14px 20px; font-size: .95rem; }
|
|
163
|
+
li { margin-bottom: 5px; }
|
|
164
|
+
li:last-child { margin-bottom: 0; }
|
|
165
|
+
|
|
166
|
+
/* ── Callouts ── */
|
|
167
|
+
.callout {
|
|
168
|
+
border-left: 3px solid var(--accent);
|
|
169
|
+
background: var(--accent-lt);
|
|
170
|
+
padding: 14px 16px;
|
|
171
|
+
border-radius: 0 4px 4px 0;
|
|
172
|
+
margin: 20px 0;
|
|
173
|
+
font-size: .9rem;
|
|
174
|
+
}
|
|
175
|
+
.callout strong { color: var(--accent); }
|
|
176
|
+
.callout-warn { border-color: var(--warn); background: var(--warn-bg); }
|
|
177
|
+
.callout-warn strong { color: var(--warn); }
|
|
178
|
+
|
|
179
|
+
/* ── Code ── */
|
|
180
|
+
pre {
|
|
181
|
+
background: var(--code-bg);
|
|
182
|
+
border: 1px solid var(--border);
|
|
183
|
+
border-radius: 5px;
|
|
184
|
+
padding: 16px 18px;
|
|
185
|
+
overflow-x: auto;
|
|
186
|
+
font-family: var(--mono);
|
|
187
|
+
font-size: .8rem;
|
|
188
|
+
line-height: 1.6;
|
|
189
|
+
margin: 16px 0;
|
|
190
|
+
color: #2D2B26;
|
|
191
|
+
}
|
|
192
|
+
code {
|
|
193
|
+
font-family: var(--mono);
|
|
194
|
+
font-size: .825em;
|
|
195
|
+
background: var(--code-bg);
|
|
196
|
+
padding: 1px 5px;
|
|
197
|
+
border-radius: 3px;
|
|
198
|
+
color: #2D2B26;
|
|
199
|
+
}
|
|
200
|
+
pre code { background: none; padding: 0; font-size: inherit; }
|
|
201
|
+
|
|
202
|
+
/* ── Tables ── */
|
|
203
|
+
.table-wrap { overflow-x: auto; margin: 16px 0; }
|
|
204
|
+
table { width: 100%; border-collapse: collapse; font-size: .875rem; }
|
|
205
|
+
th {
|
|
206
|
+
text-align: left;
|
|
207
|
+
font-size: 11px;
|
|
208
|
+
font-weight: 600;
|
|
209
|
+
letter-spacing: .07em;
|
|
210
|
+
text-transform: uppercase;
|
|
211
|
+
color: var(--mid);
|
|
212
|
+
padding: 8px 12px;
|
|
213
|
+
border-bottom: 2px solid var(--border);
|
|
214
|
+
white-space: nowrap;
|
|
215
|
+
}
|
|
216
|
+
td { padding: 9px 12px; border-bottom: 1px solid var(--border); vertical-align: top; }
|
|
217
|
+
tr:last-child td { border-bottom: none; }
|
|
218
|
+
td:first-child { font-family: var(--mono); font-size: .8rem; color: var(--accent); white-space: nowrap; }
|
|
219
|
+
|
|
220
|
+
/* ── Components ── */
|
|
221
|
+
.component-card {
|
|
222
|
+
border: 1px solid var(--border);
|
|
223
|
+
border-radius: 6px;
|
|
224
|
+
margin: 20px 0;
|
|
225
|
+
overflow: hidden;
|
|
226
|
+
}
|
|
227
|
+
.component-header {
|
|
228
|
+
display: flex;
|
|
229
|
+
align-items: center;
|
|
230
|
+
gap: 12px;
|
|
231
|
+
padding: 14px 18px;
|
|
232
|
+
background: var(--surface);
|
|
233
|
+
border-bottom: 1px solid var(--border);
|
|
234
|
+
}
|
|
235
|
+
.component-num {
|
|
236
|
+
font-family: var(--mono);
|
|
237
|
+
font-size: 11px;
|
|
238
|
+
font-weight: 600;
|
|
239
|
+
color: var(--accent);
|
|
240
|
+
background: var(--accent-lt);
|
|
241
|
+
padding: 2px 7px;
|
|
242
|
+
border-radius: 3px;
|
|
243
|
+
flex-shrink: 0;
|
|
244
|
+
}
|
|
245
|
+
.component-title { font-weight: 600; font-size: .95rem; }
|
|
246
|
+
.component-sub { font-size: .8rem; color: var(--mid); margin-left: auto; white-space: nowrap; }
|
|
247
|
+
.component-body { padding: 18px; }
|
|
248
|
+
|
|
249
|
+
/* ── Arch diagram ── */
|
|
250
|
+
.arch {
|
|
251
|
+
display: flex;
|
|
252
|
+
align-items: center;
|
|
253
|
+
gap: 0;
|
|
254
|
+
margin: 24px 0;
|
|
255
|
+
flex-wrap: wrap;
|
|
256
|
+
}
|
|
257
|
+
.arch-node {
|
|
258
|
+
background: var(--surface);
|
|
259
|
+
border: 1px solid var(--border);
|
|
260
|
+
border-radius: 5px;
|
|
261
|
+
padding: 12px 16px;
|
|
262
|
+
font-size: .82rem;
|
|
263
|
+
text-align: center;
|
|
264
|
+
min-width: 130px;
|
|
265
|
+
flex-shrink: 0;
|
|
266
|
+
}
|
|
267
|
+
.arch-node strong { display: block; font-size: .875rem; margin-bottom: 3px; }
|
|
268
|
+
.arch-node.primary { border-color: var(--accent); background: var(--accent-lt); }
|
|
269
|
+
.arch-node.primary strong { color: var(--accent); }
|
|
270
|
+
.arch-arrow {
|
|
271
|
+
color: var(--faint);
|
|
272
|
+
font-size: 1.1rem;
|
|
273
|
+
padding: 0 8px;
|
|
274
|
+
flex-shrink: 0;
|
|
275
|
+
}
|
|
276
|
+
@media (max-width: 640px) {
|
|
277
|
+
.arch { flex-direction: column; align-items: flex-start; }
|
|
278
|
+
.arch-arrow { transform: rotate(90deg); padding: 4px 0 4px 20px; }
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/* ── Success criteria ── */
|
|
282
|
+
.criteria { list-style: none; margin-left: 0; }
|
|
283
|
+
.criteria li {
|
|
284
|
+
display: flex;
|
|
285
|
+
gap: 10px;
|
|
286
|
+
align-items: baseline;
|
|
287
|
+
padding: 10px 0;
|
|
288
|
+
border-bottom: 1px solid var(--border);
|
|
289
|
+
font-size: .9rem;
|
|
290
|
+
}
|
|
291
|
+
.criteria li:last-child { border-bottom: none; }
|
|
292
|
+
.criteria-num {
|
|
293
|
+
font-family: var(--mono);
|
|
294
|
+
font-size: 11px;
|
|
295
|
+
color: var(--accent);
|
|
296
|
+
flex-shrink: 0;
|
|
297
|
+
width: 24px;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/* ── OQ ── */
|
|
301
|
+
.oq { list-style: none; margin-left: 0; }
|
|
302
|
+
.oq li {
|
|
303
|
+
padding: 12px 0;
|
|
304
|
+
border-bottom: 1px solid var(--border);
|
|
305
|
+
display: grid;
|
|
306
|
+
grid-template-columns: auto 1fr;
|
|
307
|
+
gap: 10px 14px;
|
|
308
|
+
align-items: start;
|
|
309
|
+
}
|
|
310
|
+
.oq li:last-child { border-bottom: none; }
|
|
311
|
+
.oq-q { font-weight: 600; font-size: .9rem; grid-column: 2; }
|
|
312
|
+
.oq-a { grid-column: 2; font-size: .875rem; color: var(--mid); }
|
|
313
|
+
.oq-num { font-family: var(--mono); font-size: 11px; color: var(--faint); grid-row: span 2; padding-top: 2px; }
|
|
314
|
+
|
|
315
|
+
/* ── Scope table ── */
|
|
316
|
+
.scope-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin: 16px 0; }
|
|
317
|
+
@media (max-width: 640px) { .scope-grid { grid-template-columns: 1fr; } }
|
|
318
|
+
.scope-col { border: 1px solid var(--border); border-radius: 5px; overflow: hidden; }
|
|
319
|
+
.scope-head {
|
|
320
|
+
padding: 8px 14px;
|
|
321
|
+
font-size: 11px;
|
|
322
|
+
font-weight: 600;
|
|
323
|
+
letter-spacing: .07em;
|
|
324
|
+
text-transform: uppercase;
|
|
325
|
+
background: var(--surface);
|
|
326
|
+
border-bottom: 1px solid var(--border);
|
|
327
|
+
}
|
|
328
|
+
.scope-head.in { color: var(--good); }
|
|
329
|
+
.scope-head.out { color: var(--faint); }
|
|
330
|
+
.scope-col ul { list-style: none; margin: 0; padding: 12px 14px; }
|
|
331
|
+
.scope-col li { font-size: .875rem; padding: 4px 0; border-bottom: 1px solid var(--border); }
|
|
332
|
+
.scope-col li:last-child { border-bottom: none; }
|
|
333
|
+
</style>
|
|
334
|
+
|
|
335
|
+
<div class="page">
|
|
336
|
+
|
|
337
|
+
<header class="doc-header">
|
|
338
|
+
<div class="doc-eyebrow">aria · Personal OS</div>
|
|
339
|
+
<h1 class="doc-title">Calendar-Driven Travel State</h1>
|
|
340
|
+
<div class="doc-meta">
|
|
341
|
+
<span class="pill">Draft</span>
|
|
342
|
+
<span>June 25, 2026</span>
|
|
343
|
+
<span>Author: Maya Chen</span>
|
|
344
|
+
<span>Owner: scheduler secretary + aria_dev</span>
|
|
345
|
+
</div>
|
|
346
|
+
</header>
|
|
347
|
+
|
|
348
|
+
<div class="layout">
|
|
349
|
+
|
|
350
|
+
<!-- TOC -->
|
|
351
|
+
<nav class="toc" aria-label="Table of contents">
|
|
352
|
+
<div class="toc-label">Contents</div>
|
|
353
|
+
<ol>
|
|
354
|
+
<li><a href="#problem">Problem Statement</a></li>
|
|
355
|
+
<li><a href="#principle">Design Principle</a></li>
|
|
356
|
+
<li><a href="#architecture">Architecture</a></li>
|
|
357
|
+
<li><a href="#c1">Component 1: Enforcement</a></li>
|
|
358
|
+
<li><a href="#c2">Component 2: Sync Script</a></li>
|
|
359
|
+
<li><a href="#c3">Component 3: Checkin Gate</a></li>
|
|
360
|
+
<li><a href="#data-model">Data Model</a></li>
|
|
361
|
+
<li><a href="#integrations">Integration Points</a></li>
|
|
362
|
+
<li><a href="#scope">Scope</a></li>
|
|
363
|
+
<li><a href="#success">Success Criteria</a></li>
|
|
364
|
+
<li><a href="#open">Open Questions</a></li>
|
|
365
|
+
</ol>
|
|
366
|
+
</nav>
|
|
367
|
+
|
|
368
|
+
<!-- Main -->
|
|
369
|
+
<main class="main">
|
|
370
|
+
|
|
371
|
+
<!-- Problem -->
|
|
372
|
+
<section id="problem">
|
|
373
|
+
<div class="section-eyebrow">01</div>
|
|
374
|
+
<h2>Problem Statement</h2>
|
|
375
|
+
<p>During the June 24 daily checkin, the grandmaster analysis fabricated "Austin, Texas" as Maya's location throughout — she was in Frederick, Maryland. The cause was a two-part failure:</p>
|
|
376
|
+
<ol>
|
|
377
|
+
<li><code>current-state.yaml</code> didn't exist at checkin time, so Claude had no ground truth</li>
|
|
378
|
+
<li>Claude pattern-matched "the Rio" (a Maryland venue) against prior journal context that had mentioned Austin, and confabulated a coherent but entirely wrong narrative</li>
|
|
379
|
+
</ol>
|
|
380
|
+
<div class="callout callout-warn">
|
|
381
|
+
<strong>Root cause:</strong> Stale or absent location context causes Claude to infer location from indirect signals. Confident wrong inferences propagate silently through analysis — they don't look uncertain, they look authoritative.
|
|
382
|
+
</div>
|
|
383
|
+
<p>The immediate fix (creating <code>current-state.yaml</code> and adding the Ask-Don't-Fabricate directive) addresses the symptom. This PRD addresses the cause: <code>current-state.yaml</code> was created as a manually-maintained file, which will inevitably go stale when juggling 20+ concurrent sessions with no bandwidth to remember to update a doc.</p>
|
|
384
|
+
</section>
|
|
385
|
+
|
|
386
|
+
<!-- Principle -->
|
|
387
|
+
<section id="principle">
|
|
388
|
+
<div class="section-eyebrow">02</div>
|
|
389
|
+
<h2>Design Principle: Calendar Is Truth</h2>
|
|
390
|
+
<p>Maya already maintains two calendars — Outlook (work PTO, flights, meetings) and Google (family events). These are the existing ground truth for where she is and what she's doing. Rather than maintaining a parallel file, the system should read from what already exists.</p>
|
|
391
|
+
|
|
392
|
+
<div class="arch">
|
|
393
|
+
<div class="arch-node primary">
|
|
394
|
+
<strong>Calendar</strong>
|
|
395
|
+
Outlook + Google<br>ground truth
|
|
396
|
+
</div>
|
|
397
|
+
<div class="arch-arrow">→</div>
|
|
398
|
+
<div class="arch-node">
|
|
399
|
+
<strong>bin/sync-travel-state</strong>
|
|
400
|
+
daily derivation
|
|
401
|
+
</div>
|
|
402
|
+
<div class="arch-arrow">→</div>
|
|
403
|
+
<div class="arch-node">
|
|
404
|
+
<strong>current-state.yaml</strong>
|
|
405
|
+
read-only cache
|
|
406
|
+
</div>
|
|
407
|
+
<div class="arch-arrow">→</div>
|
|
408
|
+
<div class="arch-node">
|
|
409
|
+
<strong>Claude sessions</strong>
|
|
410
|
+
consume, never write
|
|
411
|
+
</div>
|
|
412
|
+
</div>
|
|
413
|
+
|
|
414
|
+
<p><code>current-state.yaml</code> becomes a derived cache — never hand-edited, always overwritten by the sync script. The daily checkin adds a human confirmation gate to catch calendar errors before they reach analysis.</p>
|
|
415
|
+
|
|
416
|
+
<div class="callout">
|
|
417
|
+
<strong>Process change required:</strong> Driving trips (e.g., Austin) have no flights to detect. The convention is: all travel — personal or work, driving or flying — must have an Outlook PTO block before the system will track it. The cabinet enforces this rather than relying on memory.
|
|
418
|
+
</div>
|
|
419
|
+
</section>
|
|
420
|
+
|
|
421
|
+
<!-- Architecture -->
|
|
422
|
+
<section id="architecture">
|
|
423
|
+
<div class="section-eyebrow">03</div>
|
|
424
|
+
<h2>Architecture Overview</h2>
|
|
425
|
+
<p>Three components work together. Each has a single responsibility.</p>
|
|
426
|
+
|
|
427
|
+
<div class="table-wrap">
|
|
428
|
+
<table>
|
|
429
|
+
<thead>
|
|
430
|
+
<tr>
|
|
431
|
+
<th>Component</th>
|
|
432
|
+
<th>Responsibility</th>
|
|
433
|
+
<th>Owner</th>
|
|
434
|
+
<th>Trigger</th>
|
|
435
|
+
</tr>
|
|
436
|
+
</thead>
|
|
437
|
+
<tbody>
|
|
438
|
+
<tr>
|
|
439
|
+
<td>Enforcement</td>
|
|
440
|
+
<td>Require calendar entry before travel is acknowledged</td>
|
|
441
|
+
<td>scheduler secretary</td>
|
|
442
|
+
<td>Any travel mention</td>
|
|
443
|
+
</tr>
|
|
444
|
+
<tr>
|
|
445
|
+
<td>bin/sync-travel-state</td>
|
|
446
|
+
<td>Read calendars → write current-state.yaml</td>
|
|
447
|
+
<td>launchd / morning brief</td>
|
|
448
|
+
<td>Daily 6am + post-checkin</td>
|
|
449
|
+
</tr>
|
|
450
|
+
<tr>
|
|
451
|
+
<td>Checkin gate</td>
|
|
452
|
+
<td>Human confirms derived state is accurate</td>
|
|
453
|
+
<td>aria-checkin Step 0</td>
|
|
454
|
+
<td>Every daily checkin</td>
|
|
455
|
+
</tr>
|
|
456
|
+
</tbody>
|
|
457
|
+
</table>
|
|
458
|
+
</div>
|
|
459
|
+
</section>
|
|
460
|
+
|
|
461
|
+
<!-- Component 1 -->
|
|
462
|
+
<section id="c1">
|
|
463
|
+
<div class="section-eyebrow">04</div>
|
|
464
|
+
<h2>Component 1: Calendar Entry Enforcement</h2>
|
|
465
|
+
|
|
466
|
+
<div class="component-card">
|
|
467
|
+
<div class="component-header">
|
|
468
|
+
<span class="component-num">C1</span>
|
|
469
|
+
<span class="component-title">Scheduler Secretary Behavior</span>
|
|
470
|
+
<span class="component-sub">secretary work cycle + direct requests</span>
|
|
471
|
+
</div>
|
|
472
|
+
<div class="component-body">
|
|
473
|
+
<p>When travel is mentioned in any context — daily checkin narrative, voicenote, direct message to scheduler — the secretary checks for a corresponding calendar entry before acknowledging or routing the trip.</p>
|
|
474
|
+
|
|
475
|
+
<h3>Detection Triggers</h3>
|
|
476
|
+
<ul>
|
|
477
|
+
<li>User says "I'm going to X" / "we're traveling to X" / "trip to X"</li>
|
|
478
|
+
<li>Voicenote mentions destination + date range</li>
|
|
479
|
+
<li>Checkin narrative references upcoming travel not in calendar</li>
|
|
480
|
+
</ul>
|
|
481
|
+
|
|
482
|
+
<h3>Behavior: entry exists</h3>
|
|
483
|
+
<p>Acknowledge and flag for sync script to pick up at next run. No further action.</p>
|
|
484
|
+
|
|
485
|
+
<h3>Behavior: no entry found</h3>
|
|
486
|
+
<p>Create the calendar entry via <code>aria-outlook</code> or <code>aria-google</code>, confirm with user, then acknowledge. Secretary does <em>not</em> write to <code>current-state.yaml</code> — that is exclusively the sync script's domain.</p>
|
|
487
|
+
|
|
488
|
+
<h4>Entry format (Outlook PTO)</h4>
|
|
489
|
+
<pre>Title: Maya – PTO [Destination]
|
|
490
|
+
e.g., "Maya – PTO Maryland"
|
|
491
|
+
"Maya – PTO Austin"
|
|
492
|
+
Type: All-day event, multi-day
|
|
493
|
+
Calendar: Outlook personal</pre>
|
|
494
|
+
|
|
495
|
+
<h4>Entry format (work travel)</h4>
|
|
496
|
+
<pre>Title: [Trip name] e.g., "Meridian Analytics summer trip 2026"
|
|
497
|
+
Flight events are detected separately; PTO block still required
|
|
498
|
+
Type: All-day event
|
|
499
|
+
Calendar: Outlook work</pre>
|
|
500
|
+
|
|
501
|
+
<h3>Type Inference Rules</h3>
|
|
502
|
+
<div class="table-wrap">
|
|
503
|
+
<table>
|
|
504
|
+
<thead><tr><th>Title pattern</th><th>Inferred type</th></tr></thead>
|
|
505
|
+
<tbody>
|
|
506
|
+
<tr><td>contains "Meridian" / "work" / trip name from work context</td><td>work</td></tr>
|
|
507
|
+
<tr><td>"Maya – PTO [Destination]" with no work keywords</td><td>vacation</td></tr>
|
|
508
|
+
<tr><td>Flight pair without PTO block (day trip)</td><td>work</td></tr>
|
|
509
|
+
<tr><td>Google family calendar multi-day event</td><td>vacation</td></tr>
|
|
510
|
+
</tbody>
|
|
511
|
+
</table>
|
|
512
|
+
</div>
|
|
513
|
+
</div>
|
|
514
|
+
</div>
|
|
515
|
+
</section>
|
|
516
|
+
|
|
517
|
+
<!-- Component 2 -->
|
|
518
|
+
<section id="c2">
|
|
519
|
+
<div class="section-eyebrow">05</div>
|
|
520
|
+
<h2>Component 2: bin/sync-travel-state</h2>
|
|
521
|
+
|
|
522
|
+
<div class="component-card">
|
|
523
|
+
<div class="component-header">
|
|
524
|
+
<span class="component-num">C2</span>
|
|
525
|
+
<span class="component-title">Calendar Sync Script</span>
|
|
526
|
+
<span class="component-sub">bin/sync-travel-state · Ruby</span>
|
|
527
|
+
</div>
|
|
528
|
+
<div class="component-body">
|
|
529
|
+
<p>Reads both calendars, derives travel state, overwrites <code>current-state.yaml</code>. Idempotent — safe to run multiple times per day.</p>
|
|
530
|
+
|
|
531
|
+
<h3>Calendar Sources</h3>
|
|
532
|
+
<ul>
|
|
533
|
+
<li><strong>Outlook</strong> — via <code>aria-outlook calendar list</code>. Reads 60-day window. Detects: PTO all-day events, flight events (title contains "flight" / airline names / airport codes).</li>
|
|
534
|
+
<li><strong>Google</strong> — via <code>aria-google calendar list</code>. Reads Maya's primary calendar + Sam's shared calendar. Detects: multi-day events (family trips, shared PTO).</li>
|
|
535
|
+
</ul>
|
|
536
|
+
|
|
537
|
+
<h3>Detection Logic</h3>
|
|
538
|
+
<ul>
|
|
539
|
+
<li><strong>Active travel:</strong> any PTO event (Outlook) or multi-day non-recurring family event (Google) that spans today's date</li>
|
|
540
|
+
<li><strong>Upcoming trips:</strong> PTO events or flight pairs within the next 60 days, grouped by date proximity (±2 days = same trip)</li>
|
|
541
|
+
<li><strong>Trip end date:</strong> last day of PTO block or last flight's arrival date, whichever is later</li>
|
|
542
|
+
<li><strong>Destination:</strong> parsed from PTO event title suffix or location field, falling back to flight arrival airport city</li>
|
|
543
|
+
</ul>
|
|
544
|
+
|
|
545
|
+
<h3>Schedule</h3>
|
|
546
|
+
<ul>
|
|
547
|
+
<li>Daily at 6:00am via launchd (runs before any sessions likely to start)</li>
|
|
548
|
+
<li>After daily checkin completes (hooked into checkin flow)</li>
|
|
549
|
+
<li>Manual: <code>bin/sync-travel-state --force</code></li>
|
|
550
|
+
</ul>
|
|
551
|
+
|
|
552
|
+
<h3>Staleness guard</h3>
|
|
553
|
+
<p>If <code>aria-outlook</code> or <code>aria-google</code> returns an auth error (OAuth expired), the script logs the failure and exits without overwriting the existing file. It emits a warning to the COS inbox. It never writes a stale derivation.</p>
|
|
554
|
+
|
|
555
|
+
<pre>bin/sync-travel-state # normal run
|
|
556
|
+
bin/sync-travel-state --dry-run # print derived state, don't write
|
|
557
|
+
bin/sync-travel-state --force # run even if synced_at is recent</pre>
|
|
558
|
+
</div>
|
|
559
|
+
</div>
|
|
560
|
+
</section>
|
|
561
|
+
|
|
562
|
+
<!-- Component 3 -->
|
|
563
|
+
<section id="c3">
|
|
564
|
+
<div class="section-eyebrow">06</div>
|
|
565
|
+
<h2>Component 3: Checkin Verification Gate</h2>
|
|
566
|
+
|
|
567
|
+
<div class="component-card">
|
|
568
|
+
<div class="component-header">
|
|
569
|
+
<span class="component-num">C3</span>
|
|
570
|
+
<span class="component-title">Daily Checkin — Step 0</span>
|
|
571
|
+
<span class="component-sub">bin/aria-checkin · before form opens</span>
|
|
572
|
+
</div>
|
|
573
|
+
<div class="component-body">
|
|
574
|
+
<p>A brief confirmation step added before the StreamWeaver form opens. Shows derived state and asks for a binary confirm. Catches calendar errors before they flow into analysis.</p>
|
|
575
|
+
|
|
576
|
+
<h3>When to show</h3>
|
|
577
|
+
<ul>
|
|
578
|
+
<li><code>travel.active: true</code> — confirm travel is still ongoing</li>
|
|
579
|
+
<li><code>travel.ends</code> is today or yesterday — confirm return status</li>
|
|
580
|
+
<li><code>synced_at</code> is >24h old — warn and offer to re-sync</li>
|
|
581
|
+
<li>Upcoming trip starts within 3 days — surface it for awareness</li>
|
|
582
|
+
</ul>
|
|
583
|
+
|
|
584
|
+
<h3>When to skip</h3>
|
|
585
|
+
<ul>
|
|
586
|
+
<li><code>travel.active: false</code> and no trip starting within 3 days — no confirmation needed, proceed directly to form</li>
|
|
587
|
+
</ul>
|
|
588
|
+
|
|
589
|
+
<h3>UI behavior</h3>
|
|
590
|
+
<p>Single yes/no question added to the StreamWeaver form as a pre-flight card. Displayed above the main form sections. Estimated time: 5 seconds.</p>
|
|
591
|
+
|
|
592
|
+
<pre>"Derived from calendar: traveling in Maryland through Jun 28.
|
|
593
|
+
Still accurate?"
|
|
594
|
+
→ [Yes, continue] [No, something's wrong]</pre>
|
|
595
|
+
|
|
596
|
+
<p>If the user selects "No": display a correction prompt, update the calendar entry (not the yaml directly), trigger <code>bin/sync-travel-state --force</code>, then proceed to the main form with updated context.</p>
|
|
597
|
+
</div>
|
|
598
|
+
</div>
|
|
599
|
+
</section>
|
|
600
|
+
|
|
601
|
+
<!-- Data Model -->
|
|
602
|
+
<section id="data-model">
|
|
603
|
+
<div class="section-eyebrow">07</div>
|
|
604
|
+
<h2>Data Model</h2>
|
|
605
|
+
<div class="callout">
|
|
606
|
+
<strong>Do not hand-edit this file.</strong> It is written exclusively by <code>bin/sync-travel-state</code>. Manual edits will be overwritten at next sync. For urgent corrections, update the source calendar entry and run <code>bin/sync-travel-state --force</code>.
|
|
607
|
+
</div>
|
|
608
|
+
|
|
609
|
+
<h3>current-state.yaml schema</h3>
|
|
610
|
+
<pre># ~/aria-os/current-state.yaml
|
|
611
|
+
# DERIVED FROM CALENDAR — do not hand-edit.
|
|
612
|
+
# Run bin/sync-travel-state to refresh.
|
|
613
|
+
|
|
614
|
+
synced_at: "2026-06-25T06:00:00-07:00" # ISO8601, written by sync script
|
|
615
|
+
|
|
616
|
+
location:
|
|
617
|
+
current: "Frederick, MD" # from PTO event location or title suffix
|
|
618
|
+
home: "Ashland, OR" # static — set once in script config
|
|
619
|
+
|
|
620
|
+
# travel.active is the primary flag all Claude sessions check.
|
|
621
|
+
# true = away from home, any type. Repeating calendar events do not occur.
|
|
622
|
+
travel:
|
|
623
|
+
active: true
|
|
624
|
+
type: vacation # vacation | work | mixed
|
|
625
|
+
destination: "Maryland" # parsed from calendar
|
|
626
|
+
ends: "2026-06-28" # PTO end date
|
|
627
|
+
source_event_id: "AAMkADk1..." # Outlook/Google event ID for traceability
|
|
628
|
+
source_calendar: outlook # outlook | google
|
|
629
|
+
|
|
630
|
+
mode: vacation # normal | vacation | work-travel | deadline-crunch
|
|
631
|
+
# Note: deadline-crunch is the only mode not derivable from calendar.
|
|
632
|
+
# It must be set manually via: aria-cabinet cos-state set mode deadline-crunch
|
|
633
|
+
|
|
634
|
+
mode_implications:
|
|
635
|
+
hobby_teaching: skip # auto: true when travel.active is true
|
|
636
|
+
hobby_training: skip # auto: true when travel.type is vacation
|
|
637
|
+
calendar_note: >
|
|
638
|
+
Repeating events (volunteer tutoring, weekend pottery class, etc.) appear
|
|
639
|
+
on calendar but do not occur during travel. Do not log absences.
|
|
640
|
+
|
|
641
|
+
travel_schedule:
|
|
642
|
+
- destination: "Austin, TX"
|
|
643
|
+
start: "2026-07-11"
|
|
644
|
+
end: "2026-07-18"
|
|
645
|
+
type: vacation
|
|
646
|
+
source: outlook_pto
|
|
647
|
+
source_event_id: "BBMkADk2..."
|
|
648
|
+
|
|
649
|
+
- destination: "Denver, CO"
|
|
650
|
+
start: "2026-07-20"
|
|
651
|
+
end: null # return flight not yet booked
|
|
652
|
+
type: work
|
|
653
|
+
source: flight_pair
|
|
654
|
+
flight_departure: "2026-07-20T06:00-04:00"
|
|
655
|
+
flight_return: null</pre>
|
|
656
|
+
|
|
657
|
+
<h3>Non-calendar context (separate file)</h3>
|
|
658
|
+
<p>Data that cannot be derived from calendar — active work deadlines, upcoming notes — lives in a separate file read by relevant secretaries:</p>
|
|
659
|
+
<pre># ~/aria-os/cabinet/secretaries/cos/context.yaml
|
|
660
|
+
# Hand-maintained by COS secretary. Not overwritten by sync.
|
|
661
|
+
|
|
662
|
+
deadline_crunch_status:
|
|
663
|
+
status: "Board deck due Friday — deprioritizing non-essential secretary work"
|
|
664
|
+
review_meeting_planned: "2026-06-27"</pre>
|
|
665
|
+
</section>
|
|
666
|
+
|
|
667
|
+
<!-- Integrations -->
|
|
668
|
+
<section id="integrations">
|
|
669
|
+
<div class="section-eyebrow">08</div>
|
|
670
|
+
<h2>Integration Points</h2>
|
|
671
|
+
|
|
672
|
+
<h3>Systems that read current-state.yaml</h3>
|
|
673
|
+
<div class="table-wrap">
|
|
674
|
+
<table>
|
|
675
|
+
<thead><tr><th>Consumer</th><th>What it reads</th><th>Purpose</th></tr></thead>
|
|
676
|
+
<tbody>
|
|
677
|
+
<tr><td>bin/aria-checkin</td><td>travel, mode</td><td>Step 0 verification gate; grandmaster analysis context</td></tr>
|
|
678
|
+
<tr><td>SessionStart hook</td><td>synced_at, travel.ends</td><td>Staleness warning before any session</td></tr>
|
|
679
|
+
<tr><td>CLAUDE.md directive</td><td>location.current, travel.active</td><td>Ground truth for location-sensitive reasoning</td></tr>
|
|
680
|
+
<tr><td>Hobby schedule reasoning</td><td>travel.active, travel.type</td><td>Skip training/teaching when traveling</td></tr>
|
|
681
|
+
<tr><td>Scheduler secretary</td><td>travel_schedule</td><td>Plan-ahead awareness, avoid scheduling conflicts</td></tr>
|
|
682
|
+
</tbody>
|
|
683
|
+
</table>
|
|
684
|
+
</div>
|
|
685
|
+
|
|
686
|
+
<h3>SessionStart staleness check</h3>
|
|
687
|
+
<p>Add to the SessionStart hook (alongside <code>bin/aria-session-nudge</code>):</p>
|
|
688
|
+
<pre>ruby -ryaml -rdate -e '
|
|
689
|
+
f = File.expand_path("~/aria-os/current-state.yaml")
|
|
690
|
+
exit unless File.exist?(f)
|
|
691
|
+
s = YAML.load_file(f)
|
|
692
|
+
synced = Date.parse(s["synced_at"].to_s.split("T").first) rescue nil
|
|
693
|
+
if synced && Date.today - synced > 1
|
|
694
|
+
puts "⚠️ current-state.yaml is stale (#{Date.today - synced}d) — run bin/sync-travel-state"
|
|
695
|
+
end
|
|
696
|
+
ends_str = s.dig("travel", "ends")
|
|
697
|
+
if ends_str && s.dig("travel", "active") && Date.parse(ends_str) < Date.today
|
|
698
|
+
puts "⚠️ travel.ends (#{ends_str}) is past but travel.active is true — run bin/sync-travel-state"
|
|
699
|
+
end
|
|
700
|
+
'</pre>
|
|
701
|
+
</section>
|
|
702
|
+
|
|
703
|
+
<!-- Scope -->
|
|
704
|
+
<section id="scope">
|
|
705
|
+
<div class="section-eyebrow">09</div>
|
|
706
|
+
<h2>Scope</h2>
|
|
707
|
+
<div class="scope-grid">
|
|
708
|
+
<div class="scope-col">
|
|
709
|
+
<div class="scope-head in">In Scope</div>
|
|
710
|
+
<ul>
|
|
711
|
+
<li>Outlook PTO event detection</li>
|
|
712
|
+
<li>Google family calendar detection</li>
|
|
713
|
+
<li>Outlook flight event detection</li>
|
|
714
|
+
<li>Trip type inference from event titles</li>
|
|
715
|
+
<li>Destination extraction from title/location field</li>
|
|
716
|
+
<li>Checkin verification step</li>
|
|
717
|
+
<li>Scheduler enforcement (require calendar entry)</li>
|
|
718
|
+
<li>SessionStart staleness warning</li>
|
|
719
|
+
<li>launchd schedule (6am daily)</li>
|
|
720
|
+
</ul>
|
|
721
|
+
</div>
|
|
722
|
+
<div class="scope-col">
|
|
723
|
+
<div class="scope-head out">Out of Scope</div>
|
|
724
|
+
<ul>
|
|
725
|
+
<li>Sub-day location tracking</li>
|
|
726
|
+
<li>Multi-timezone handling</li>
|
|
727
|
+
<li>Historical travel reconstruction</li>
|
|
728
|
+
<li>Automatic deadline-crunch mode detection</li>
|
|
729
|
+
<li>Seattle trip (not yet booked — tracks once calendar entry exists)</li>
|
|
730
|
+
<li>Drive trip detection without PTO block (process rule: add PTO)</li>
|
|
731
|
+
</ul>
|
|
732
|
+
</div>
|
|
733
|
+
</div>
|
|
734
|
+
</section>
|
|
735
|
+
|
|
736
|
+
<!-- Success -->
|
|
737
|
+
<section id="success">
|
|
738
|
+
<div class="section-eyebrow">10</div>
|
|
739
|
+
<h2>Success Criteria</h2>
|
|
740
|
+
<ul class="criteria">
|
|
741
|
+
<li><span class="criteria-num">SC1</span><span>Zero location fabrications in grandmaster analysis after build — Claude reads ground truth or asks, never infers from indirect signals</span></li>
|
|
742
|
+
<li><span class="criteria-num">SC2</span><span><code>current-state.yaml</code> reflects accurate travel state without manual updates on ≥90% of travel days</span></li>
|
|
743
|
+
<li><span class="criteria-num">SC3</span><span>Checkin verification gate catches any remaining discrepancies within 24h</span></li>
|
|
744
|
+
<li><span class="criteria-num">SC4</span><span>New travel appears in <code>current-state.yaml</code> within 24h of calendar entry creation</span></li>
|
|
745
|
+
<li><span class="criteria-num">SC5</span><span>Auth failures (OAuth expired) emit a COS inbox warning rather than silently writing stale data</span></li>
|
|
746
|
+
<li><span class="criteria-num">SC6</span><span>Hobby absence during travel is not logged — no false "missed training" entries accumulate</span></li>
|
|
747
|
+
</ul>
|
|
748
|
+
</section>
|
|
749
|
+
|
|
750
|
+
<!-- Open Questions -->
|
|
751
|
+
<section id="open">
|
|
752
|
+
<div class="section-eyebrow">11</div>
|
|
753
|
+
<h2>Open Questions</h2>
|
|
754
|
+
<ul class="oq">
|
|
755
|
+
<li>
|
|
756
|
+
<span class="oq-num">Q1</span>
|
|
757
|
+
<span class="oq-q">aria-google headless auth</span>
|
|
758
|
+
<span class="oq-a">The sync script runs via launchd (non-interactive). Confirm that aria-google's OAuth token refresh works headlessly. If not, the Google calendar read will silently fail — the script must detect this and fall back to Outlook-only with a warning rather than writing incomplete state.</span>
|
|
759
|
+
</li>
|
|
760
|
+
<li>
|
|
761
|
+
<span class="oq-num">Q2</span>
|
|
762
|
+
<span class="oq-q">Destination parsing from event title</span>
|
|
763
|
+
<span class="oq-a">PTO events may not have a location field set. Title parsing needs a convention: "Maya – PTO Maryland" works; "Maya – PTO" alone does not yield a destination. Worth establishing the naming convention and adding it to the scheduler secretary's entry template.</span>
|
|
764
|
+
</li>
|
|
765
|
+
<li>
|
|
766
|
+
<span class="oq-num">Q3</span>
|
|
767
|
+
<span class="oq-q">Sam's Google calendar access</span>
|
|
768
|
+
<span class="oq-a">The script needs read access to Sam's shared family calendar to detect joint trips. Confirm aria-google has the correct scope and that the calendar is shared with Maya's Google account.</span>
|
|
769
|
+
</li>
|
|
770
|
+
<li>
|
|
771
|
+
<span class="oq-num">Q4</span>
|
|
772
|
+
<span class="oq-q">Work trip hobby implications</span>
|
|
773
|
+
<span class="oq-a">Work travel (Denver) is different from vacation — Maya may or may not train depending on the trip. Currently the model sets <code>hobby_training: skip</code> only for vacation type. Confirm whether work trips should also skip or leave as ambiguous (requiring checkin verification to clarify).</span>
|
|
774
|
+
</li>
|
|
775
|
+
</ul>
|
|
776
|
+
</section>
|
|
777
|
+
|
|
778
|
+
</main>
|
|
779
|
+
</div>
|
|
780
|
+
</div>
|