robot_lab 0.2.8 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +65 -0
  3. data/README.md +16 -18
  4. data/lib/robot_lab/ask_user.rb +3 -3
  5. data/lib/robot_lab/config/defaults.yml +5 -5
  6. data/lib/robot_lab/config.rb +2 -2
  7. data/lib/robot_lab/mcp/transports/streamable_http.rb +2 -4
  8. data/lib/robot_lab/memory.rb +16 -7
  9. data/lib/robot_lab/robot/hooking.rb +26 -0
  10. data/lib/robot_lab/robot/result_building.rb +119 -0
  11. data/lib/robot_lab/robot.rb +37 -120
  12. data/lib/robot_lab/run_config.rb +52 -20
  13. data/lib/robot_lab/tool.rb +7 -12
  14. data/lib/robot_lab/version.rb +1 -1
  15. data/lib/robot_lab.rb +3 -3
  16. metadata +15 -274
  17. data/.architecture/AGENTS.md +0 -32
  18. data/.architecture/config.yml +0 -8
  19. data/.architecture/members.yml +0 -60
  20. data/.architecture/reviews/feature-free-will.md +0 -490
  21. data/.architecture/reviews/overall-codebase.md +0 -427
  22. data/.claude/settings.local.json +0 -57
  23. data/.codex/config.toml +0 -2
  24. data/.envrc +0 -6
  25. data/.github/workflows/deploy-github-pages.yml +0 -52
  26. data/.irbrc +0 -6
  27. data/.loki +0 -65
  28. data/.quality/reek_baseline.txt +0 -43
  29. data/.rubocop.yml +0 -10
  30. data/Archspec.rb +0 -44
  31. data/CLAUDE.md +0 -139
  32. data/COMMITS.md +0 -196
  33. data/Rakefile +0 -193
  34. data/_typos.toml +0 -21
  35. data/agent2agent_review.md +0 -192
  36. data/agentf_improvements.md +0 -253
  37. data/agents.md +0 -14
  38. data/docs/api/core/index.md +0 -147
  39. data/docs/api/core/memory.md +0 -578
  40. data/docs/api/core/network.md +0 -553
  41. data/docs/api/core/result.md +0 -211
  42. data/docs/api/core/robot.md +0 -1667
  43. data/docs/api/core/state.md +0 -145
  44. data/docs/api/core/tool.md +0 -520
  45. data/docs/api/errors.md +0 -323
  46. data/docs/api/hooks.md +0 -469
  47. data/docs/api/index.md +0 -137
  48. data/docs/api/mcp/client.md +0 -387
  49. data/docs/api/mcp/index.md +0 -267
  50. data/docs/api/mcp/server.md +0 -252
  51. data/docs/api/mcp/transports.md +0 -339
  52. data/docs/api/messages/index.md +0 -126
  53. data/docs/api/messages/text-message.md +0 -170
  54. data/docs/api/messages/tool-call-message.md +0 -206
  55. data/docs/api/messages/tool-result-message.md +0 -242
  56. data/docs/api/messages/user-message.md +0 -232
  57. data/docs/api/skills.md +0 -423
  58. data/docs/api/streaming/context.md +0 -280
  59. data/docs/api/streaming/events.md +0 -185
  60. data/docs/api/streaming/index.md +0 -156
  61. data/docs/api/support.md +0 -846
  62. data/docs/architecture/core-concepts.md +0 -575
  63. data/docs/architecture/index.md +0 -252
  64. data/docs/architecture/message-flow.md +0 -366
  65. data/docs/architecture/network-orchestration.md +0 -478
  66. data/docs/architecture/robot-execution.md +0 -396
  67. data/docs/architecture/state-management.md +0 -410
  68. data/docs/assets/css/custom.css +0 -56
  69. data/docs/assets/images/robot_lab.jpg +0 -0
  70. data/docs/concepts.md +0 -641
  71. data/docs/examples/basic-chat.md +0 -258
  72. data/docs/examples/index.md +0 -425
  73. data/docs/examples/mcp-server.md +0 -403
  74. data/docs/examples/multi-robot-network.md +0 -476
  75. data/docs/examples/tool-usage.md +0 -468
  76. data/docs/getting-started/configuration.md +0 -645
  77. data/docs/getting-started/index.md +0 -56
  78. data/docs/getting-started/installation.md +0 -200
  79. data/docs/getting-started/quick-start.md +0 -293
  80. data/docs/guides/building-robots.md +0 -1187
  81. data/docs/guides/creating-networks.md +0 -736
  82. data/docs/guides/hooks.md +0 -1206
  83. data/docs/guides/index.md +0 -78
  84. data/docs/guides/knowledge.md +0 -219
  85. data/docs/guides/mcp-integration.md +0 -740
  86. data/docs/guides/memory.md +0 -458
  87. data/docs/guides/observability.md +0 -733
  88. data/docs/guides/streaming.md +0 -323
  89. data/docs/guides/using-tools.md +0 -780
  90. data/docs/index.md +0 -280
  91. data/docs/superpowers/plans/2026-05-06-agentskills.md +0 -1303
  92. data/docs/superpowers/specs/2026-05-06-agentskills-design.md +0 -247
  93. data/examples/.envrc +0 -1
  94. data/examples/01_simple_robot.rb +0 -31
  95. data/examples/02_tools.rb +0 -109
  96. data/examples/03_network.rb +0 -118
  97. data/examples/04_mcp.rb +0 -215
  98. data/examples/05_streaming.rb +0 -137
  99. data/examples/06_prompt_templates.rb +0 -325
  100. data/examples/07_network_memory.rb +0 -313
  101. data/examples/08_llm_config.rb +0 -162
  102. data/examples/09_chaining.rb +0 -251
  103. data/examples/10_memory.rb +0 -315
  104. data/examples/11_network_introspection.rb +0 -239
  105. data/examples/12_message_bus.rb +0 -67
  106. data/examples/13_spawn.rb +0 -85
  107. data/examples/14_rusty_circuit/.envrc +0 -1
  108. data/examples/14_rusty_circuit/.gitignore +0 -1
  109. data/examples/14_rusty_circuit/README.md +0 -1
  110. data/examples/14_rusty_circuit/comic.rb +0 -149
  111. data/examples/14_rusty_circuit/display.rb +0 -217
  112. data/examples/14_rusty_circuit/heckler.rb +0 -65
  113. data/examples/14_rusty_circuit/open_mic.rb +0 -132
  114. data/examples/14_rusty_circuit/prompts/open_mic_comic.md +0 -20
  115. data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +0 -23
  116. data/examples/14_rusty_circuit/prompts/open_mic_scout.md +0 -20
  117. data/examples/14_rusty_circuit/scout.rb +0 -165
  118. data/examples/14_rusty_circuit/show.log +0 -234
  119. data/examples/15_memory_network_and_bus/.envrc +0 -1
  120. data/examples/15_memory_network_and_bus/editor_in_chief.rb +0 -24
  121. data/examples/15_memory_network_and_bus/editorial_pipeline.rb +0 -213
  122. data/examples/15_memory_network_and_bus/linux_writer.rb +0 -80
  123. data/examples/15_memory_network_and_bus/os_editor.rb +0 -48
  124. data/examples/15_memory_network_and_bus/os_writer.rb +0 -49
  125. data/examples/15_memory_network_and_bus/output/combined_article.md +0 -13
  126. data/examples/15_memory_network_and_bus/output/final_article.md +0 -13
  127. data/examples/15_memory_network_and_bus/output/linux_draft.md +0 -7
  128. data/examples/15_memory_network_and_bus/output/mac_draft.md +0 -7
  129. data/examples/15_memory_network_and_bus/output/memory.json +0 -13
  130. data/examples/15_memory_network_and_bus/output/revision_1.md +0 -18
  131. data/examples/15_memory_network_and_bus/output/revision_2.md +0 -13
  132. data/examples/15_memory_network_and_bus/output/windows_draft.md +0 -7
  133. data/examples/15_memory_network_and_bus/prompts/os_advocate.md +0 -13
  134. data/examples/15_memory_network_and_bus/prompts/os_chief.md +0 -13
  135. data/examples/15_memory_network_and_bus/prompts/os_editor.md +0 -13
  136. data/examples/16_writers_room/.envrc +0 -1
  137. data/examples/16_writers_room/display.rb +0 -158
  138. data/examples/16_writers_room/output/.gitignore +0 -4
  139. data/examples/16_writers_room/output/README.md +0 -69
  140. data/examples/16_writers_room/output/opus_001.md +0 -263
  141. data/examples/16_writers_room/output/opus_001_notes.log +0 -470
  142. data/examples/16_writers_room/output/opus_002.md +0 -245
  143. data/examples/16_writers_room/output/opus_002_notes.log +0 -546
  144. data/examples/16_writers_room/output/opus_002_screenplay.md +0 -7989
  145. data/examples/16_writers_room/output/opus_002_screenplay_notes.md +0 -993
  146. data/examples/16_writers_room/prompts/screenplay_writer.md +0 -66
  147. data/examples/16_writers_room/prompts/writer.md +0 -37
  148. data/examples/16_writers_room/room.rb +0 -186
  149. data/examples/16_writers_room/tools.rb +0 -173
  150. data/examples/16_writers_room/writer.rb +0 -121
  151. data/examples/16_writers_room/writers_room.rb +0 -256
  152. data/examples/17_skills.rb +0 -234
  153. data/examples/18_rails/.envrc +0 -3
  154. data/examples/18_rails/.gitignore +0 -5
  155. data/examples/18_rails/Gemfile +0 -11
  156. data/examples/18_rails/README.md +0 -67
  157. data/examples/18_rails/Rakefile +0 -4
  158. data/examples/18_rails/app/controllers/application_controller.rb +0 -4
  159. data/examples/18_rails/app/controllers/chat_controller.rb +0 -50
  160. data/examples/18_rails/app/jobs/application_job.rb +0 -4
  161. data/examples/18_rails/app/jobs/robot_run_job.rb +0 -25
  162. data/examples/18_rails/app/models/application_record.rb +0 -5
  163. data/examples/18_rails/app/models/robot_lab_result.rb +0 -36
  164. data/examples/18_rails/app/models/robot_lab_thread.rb +0 -23
  165. data/examples/18_rails/app/robots/chat_robot.rb +0 -25
  166. data/examples/18_rails/app/tools/time_tool.rb +0 -9
  167. data/examples/18_rails/app/views/chat/_user_message.html.erb +0 -1
  168. data/examples/18_rails/app/views/chat/index.html.erb +0 -67
  169. data/examples/18_rails/app/views/layouts/application.html.erb +0 -49
  170. data/examples/18_rails/bin/dev +0 -7
  171. data/examples/18_rails/bin/rails +0 -6
  172. data/examples/18_rails/bin/setup +0 -15
  173. data/examples/18_rails/config/application.rb +0 -33
  174. data/examples/18_rails/config/cable.yml +0 -2
  175. data/examples/18_rails/config/database.yml +0 -5
  176. data/examples/18_rails/config/environment.rb +0 -4
  177. data/examples/18_rails/config/initializers/robot_lab.rb +0 -11
  178. data/examples/18_rails/config/routes.rb +0 -6
  179. data/examples/18_rails/config.ru +0 -4
  180. data/examples/18_rails/db/migrate/001_create_robot_lab_tables.rb +0 -32
  181. data/examples/19_token_tracking.rb +0 -138
  182. data/examples/20_circuit_breaker.rb +0 -147
  183. data/examples/21_learning_loop.rb +0 -181
  184. data/examples/22_context_compression.rb +0 -172
  185. data/examples/23_convergence.rb +0 -133
  186. data/examples/24_structured_delegation.rb +0 -151
  187. data/examples/25_history_search/conversation.jsonl +0 -30
  188. data/examples/25_history_search.rb +0 -133
  189. data/examples/26_document_store/api_versioning_adr.md +0 -52
  190. data/examples/26_document_store/incident_postmortem.md +0 -46
  191. data/examples/26_document_store/postgres_runbook.md +0 -49
  192. data/examples/26_document_store/redis_caching_guide.md +0 -48
  193. data/examples/26_document_store/sidekiq_guide.md +0 -51
  194. data/examples/26_document_store.rb +0 -140
  195. data/examples/27_incident_response/incident_response.rb +0 -261
  196. data/examples/28_mcp_discovery.rb +0 -113
  197. data/examples/29_ractor_tools.rb +0 -240
  198. data/examples/30_ractor_network.rb +0 -252
  199. data/examples/31_launch_assessment.rb +0 -246
  200. data/examples/32_newsletter_reader.rb +0 -193
  201. data/examples/33_stock_generator.rb +0 -80
  202. data/examples/33_stock_predictor.rb +0 -327
  203. data/examples/34_agentskills.rb +0 -76
  204. data/examples/35_hooks.rb +0 -266
  205. data/examples/README.md +0 -533
  206. data/examples/common.rb +0 -148
  207. data/examples/prompts/assistant.md +0 -5
  208. data/examples/prompts/audit_trail.md +0 -8
  209. data/examples/prompts/billing.md +0 -10
  210. data/examples/prompts/classifier.md +0 -7
  211. data/examples/prompts/comedian.md +0 -6
  212. data/examples/prompts/comedy_critic.md +0 -10
  213. data/examples/prompts/configurable.md +0 -9
  214. data/examples/prompts/dispatcher.md +0 -12
  215. data/examples/prompts/entity_extractor.md +0 -14
  216. data/examples/prompts/escalation.md +0 -42
  217. data/examples/prompts/frontmatter_mcp_test.md +0 -9
  218. data/examples/prompts/frontmatter_named_test.md +0 -5
  219. data/examples/prompts/frontmatter_tools_test.md +0 -6
  220. data/examples/prompts/general.md +0 -7
  221. data/examples/prompts/github_assistant.md +0 -14
  222. data/examples/prompts/helper.md +0 -4
  223. data/examples/prompts/incident_responder.md +0 -18
  224. data/examples/prompts/keyword_extractor.md +0 -11
  225. data/examples/prompts/llm_config_demo.md +0 -20
  226. data/examples/prompts/order_support.md +0 -35
  227. data/examples/prompts/os_advocate.md +0 -13
  228. data/examples/prompts/os_chief.md +0 -13
  229. data/examples/prompts/os_editor.md +0 -13
  230. data/examples/prompts/parameterized_main_test.md +0 -6
  231. data/examples/prompts/pii_redactor.md +0 -7
  232. data/examples/prompts/product_support.md +0 -37
  233. data/examples/prompts/runbook_protocol.md +0 -12
  234. data/examples/prompts/sentiment_analyzer.md +0 -12
  235. data/examples/prompts/skill_a_test.md +0 -4
  236. data/examples/prompts/skill_b_test.md +0 -4
  237. data/examples/prompts/skill_config_test.md +0 -5
  238. data/examples/prompts/skill_cycle_a_test.md +0 -6
  239. data/examples/prompts/skill_cycle_b_test.md +0 -6
  240. data/examples/prompts/skill_description_test.md +0 -4
  241. data/examples/prompts/skill_leaf_test.md +0 -4
  242. data/examples/prompts/skill_nested_test.md +0 -6
  243. data/examples/prompts/skill_refs_main_test.md +0 -6
  244. data/examples/prompts/skill_self_ref_test.md +0 -6
  245. data/examples/prompts/skill_with_mcp_test.md +0 -9
  246. data/examples/prompts/skill_with_params_test.md +0 -6
  247. data/examples/prompts/skill_with_robot_name_test.md +0 -5
  248. data/examples/prompts/skill_with_tools_test.md +0 -6
  249. data/examples/prompts/sre_compliance.md +0 -10
  250. data/examples/prompts/structured_output.md +0 -7
  251. data/examples/prompts/synthesizer.md +0 -17
  252. data/examples/prompts/technical.md +0 -10
  253. data/examples/prompts/template_with_skills_test.md +0 -6
  254. data/examples/prompts/triage.md +0 -22
  255. data/examples/ruboruby.md +0 -423
  256. data/examples/xyzzy.rb +0 -97
  257. data/logfile +0 -8
  258. data/mkdocs.yml +0 -209
  259. data/robot_concurrency.md +0 -38
  260. data/simple_acp_review.md +0 -298
  261. data/temp.md +0 -6
  262. data/tool_manifest_plan.md +0 -155
@@ -1,155 +0,0 @@
1
- # ToolManifest Evolution Plan
2
-
3
- ## Concept: Dynamic Tool Selection in Pipelines
4
-
5
- There is a pattern where the total number of tools available within RobotLab numbers in the hundreds -- too many to send to any single LLM call. A pipeline uses a "selector" robot that reviews the next robot's prompt and determines which tools should be made available. The selected subset is then applied to the target robot via `with_tools(...)`.
6
-
7
- ### Example Flow
8
-
9
- ```
10
- Selector Robot (step 1)
11
- - Receives the user prompt destined for step 2
12
- - Has access to a summary of all available tools (name + description)
13
- - Outputs a list of tool names relevant to the task
14
-
15
- Target Robot (step 2)
16
- - Receives the selected tools via with_tools(...)
17
- - Runs with only the relevant subset (e.g., 5-10 tools instead of hundreds)
18
- ```
19
-
20
- ## Current State
21
-
22
- `ToolManifest` (lib/robot_lab/tool_manifest.rb, 218 lines) is a name-keyed Hash wrapper with:
23
- - Lookup by name (`[]`, `fetch`)
24
- - `Enumerable` support
25
- - Merge/replace operations
26
- - Serialization (`to_h`, `to_json`, `from_hash`)
27
-
28
- It is not currently used by any production code path. `Robot` stores tools as plain arrays (`@local_tools`, `@mcp_tools`) and `all_tools` concatenates them.
29
-
30
- ## What It Needs to Become
31
-
32
- ### 1. Global Registry Entry Point
33
-
34
- A singleton or module-level registry where all available tools accumulate:
35
-
36
- ```ruby
37
- RobotLab.tool_registry # => ToolManifest (global instance)
38
- RobotLab.tool_registry.add(MyTool)
39
- RobotLab.tool_registry.names # => ["my_tool", "weather", "calculator", ...]
40
- ```
41
-
42
- ### 2. Metadata for Selection
43
-
44
- Categories, tags, or other metadata so the selector robot can reason about which tools are relevant without seeing full schemas:
45
-
46
- ```ruby
47
- # Registration with metadata
48
- RobotLab.tool_registry.add(MyTool, tags: [:database, :read_only])
49
-
50
- # Filtering
51
- RobotLab.tool_registry.tagged(:database) # => subset manifest
52
- ```
53
-
54
- ### 3. Search-Friendly Summary Output
55
-
56
- The selector robot needs a compact summary it can reason over -- name + description pairs, not full JSON schemas:
57
-
58
- ```ruby
59
- RobotLab.tool_registry.summary
60
- # => [{ name: "get_weather", description: "Get current weather for a location" },
61
- # { name: "calculate", description: "Performs basic arithmetic" }, ...]
62
- ```
63
-
64
- ### 4. Subset Extraction by Name
65
-
66
- Resolve a list of tool names (the selector robot's output) into actual tool objects:
67
-
68
- ```ruby
69
- names = ["get_weather", "calculate"] # from selector robot
70
- tools = RobotLab.tool_registry.select_by_names(names)
71
- target_robot.with_tools(tools)
72
- ```
73
-
74
- ### 5. Integration with Robot
75
-
76
- `Robot#all_tools` and `with_tools(...)` should accept a ToolManifest or be able to pull from the global registry.
77
-
78
- ## Design Decisions Still Needed
79
-
80
- - Should MCP-discovered tools auto-register in the global manifest?
81
- - How does the selector robot receive the tool summary -- as part of its system prompt, injected into the user message, or via a tool call?
82
- - Should categories/tags be defined on the Tool class itself (e.g., `Tool.create(name: ..., tags: [...])`), or only at registration time?
83
- - Does the manifest need versioning or hot-reload support for long-running processes?
84
-
85
- ---
86
-
87
- ## Review Notes
88
-
89
- ### The Problem Statement is Strong
90
-
91
- The "selector robot" pattern — an LLM choosing tools from hundreds based on a compact summary — is a real need. Sending 200+ tool schemas in every API call is both expensive and degrades LLM accuracy. The plan correctly identifies that `ToolManifest` as it exists today is a passive container that nothing actually uses.
92
-
93
- ### What's Solid
94
-
95
- - **Global registry** (section 1) — The right call. Tools currently scatter across `@local_tools` and `@mcp_tools` per-robot with no central view. `RobotLab.tool_registry` is the obvious home.
96
- - **Summary output** (section 3) — Essential for the selector pattern. Name + description pairs are the right granularity for LLM reasoning.
97
- - **Subset extraction** (section 4) — `select_by_names` maps cleanly to what `ToolConfig.filter_tools` already does, but at the registry level.
98
-
99
- ### Gaps and Concerns
100
-
101
- **1. Tags/metadata may be premature.**
102
-
103
- Section 2 proposes tags (`tags: [:database, :read_only]`) and `.tagged(:database)` filtering. This adds a classification system that someone has to maintain. In the selector-robot pattern, the LLM is doing the classification — that's the whole point. If you also hand-tag tools, you're duplicating the selector's job.
104
-
105
- A simpler alternative: let `summary` be the only interface the selector sees. If you later want pre-filtering (e.g., "only show the selector database-related tools"), add it then. Right now it's YAGNI.
106
-
107
- **2. MCP tool auto-registration is the key decision, not a footnote.**
108
-
109
- The plan lists "Should MCP-discovered tools auto-register in the global manifest?" as an open question. This is actually the central architectural decision. Looking at `MCPManagement#discover_mcp_tools`, MCP tools are created dynamically per-robot, per-connection. They hold closures over their specific `mcp_client` instance. If you auto-register them globally, you need to handle:
110
-
111
- - **Lifecycle** — MCP tools are only valid while their client is connected. The global registry would hold dead references after `disconnect`.
112
- - **Duplicates** — Two robots connecting to the same MCP server would register the same tool names with different client closures.
113
- - **Scope** — An MCP tool created for robot A shouldn't be callable by robot B unless they share the client.
114
-
115
- Suggestion: MCP tools should **not** auto-register. The global registry should hold "tool definitions" (name, description, schema) — not live callable instances. The selector robot picks names; the target robot resolves those names against its own live tools.
116
-
117
- **3. The plan doesn't address the split between "tool definition" and "tool instance."**
118
-
119
- Currently `Tool` is both the definition (name, description, parameters) and the callable (the `execute` method with its closure). For a global registry, you want the definition without the closure. The selector robot doesn't need to call tools — it just needs to read summaries and output names.
120
-
121
- This suggests `ToolManifest` should store lightweight descriptors, and `Robot#filtered_tools` continues resolving names to live instances from `all_tools`.
122
-
123
- **4. Integration with `Robot#with_tools` needs more thought.**
124
-
125
- The plan says "`with_tools(...)` should accept a ToolManifest." But `with_tools` currently delegates to `@chat.with_tools` from RubyLLM, which expects tool class/instance objects. Accepting a manifest means either:
126
-
127
- - Converting manifest entries back to tool instances (requires the live tool lookup), or
128
- - Passing tool names and letting the robot resolve internally (which is what `filtered_tools` already does)
129
-
130
- The second path is simpler and already mostly works. The plan could just say: "The selector robot outputs tool names. Pass them to `robot.run(..., tools: names)`." That's the existing `tools:` parameter.
131
-
132
- **5. Missing: how the selector robot itself gets built.**
133
-
134
- The plan describes the pattern but not the API. Is this a special network task type? A built-in robot template? A method on `ToolManifest`? Something like:
135
-
136
- ```ruby
137
- network = RobotLab.create_network(name: "smart_pipeline") do
138
- task :selector, selector_robot, depends_on: :none
139
- task :worker, worker_robot, tools: :from_selector, depends_on: [:selector]
140
- end
141
- ```
142
-
143
- The `tools: :from_selector` (or similar) is the glue that needs designing.
144
-
145
- ### Suggested Simplification
146
-
147
- Instead of evolving ToolManifest into a full registry with tags, a narrower v1:
148
-
149
- 1. **`RobotLab.tool_registry`** — Global `ToolManifest` that accumulates tool descriptors (name + description only, no live instances)
150
- 2. **`ToolManifest#summary`** — Returns the compact format for selector prompts
151
- 3. **`ToolManifest#select_by_names(names)`** — Returns a sub-manifest (already close to what exists)
152
- 4. **Auto-registration of local tools at `Robot.new`** — Push descriptors into the registry when robots are created
153
- 5. **No auto-registration of MCP tools** — Too coupled to connection lifecycle
154
-
155
- Skip tags, skip `with_tools(manifest)` integration, skip hot-reload. Those can come later if needed.