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
data/examples/README.md DELETED
@@ -1,533 +0,0 @@
1
- # RobotLab Examples
2
-
3
- Working demonstrations of RobotLab features, from single-robot basics to multi-robot orchestration and message bus communication.
4
-
5
- ## Prerequisites
6
-
7
- - Ruby >= 3.2
8
- - `bundle install` (from the project root)
9
- - [Ollama](https://ollama.com) running locally with the demo model pulled:
10
-
11
- ```bash
12
- ollama serve
13
- ollama pull qwen3.6
14
- ```
15
-
16
- Every example runs against a **local** model — no API keys, no egress, no
17
- per-token cost. The model, provider and endpoint are set in one place,
18
- `examples/common.rb`:
19
-
20
- ```ruby
21
- LLM = {
22
- default: LlmConfig.new(provider: "ollama", model: "qwen3.6:latest"),
23
- small: LlmConfig.new(provider: "ollama", model: "qwen2.5:7b"),
24
- large: LlmConfig.new(provider: "ollama", model: "llama3.3:latest")
25
- }
26
- ```
27
-
28
- Robots pick it up with the `llm_opts` helper:
29
-
30
- ```ruby
31
- robot = RobotLab.build(name: "helper", **llm_opts) # default model
32
- cheap = RobotLab.build(name: "helper", **llm_opts(:small)) # smaller model
33
- ```
34
-
35
- `provider:` is not optional here. Ollama models are absent from RubyLLM's
36
- model registry, so passing `model:` alone raises `RubyLLM::ModelNotFoundError`
37
- — supplying `provider:` is what makes RubyLLM skip the registry lookup. Note
38
- also that `RunConfig` has no `provider` field, so provider and model travel
39
- together on the robot even when other settings come from a shared `RunConfig`.
40
-
41
- `common.rb` also raises `request_timeout` to 900s. A 20B+ model on consumer
42
- hardware routinely takes longer than robot_lab's 120s default on a long
43
- answer, which otherwise surfaces mid-example as `Net::ReadTimeout`. Override
44
- with `LLM_REQUEST_TIMEOUT`.
45
-
46
- ## Runtime: pick your model with `LLM_PROFILE`
47
-
48
- Every example is a *sequence* of LLM calls, so wall-clock is dominated by
49
- `calls × seconds-per-call`. On `qwen3.6:latest` expect roughly **60-70s per
50
- call**. That is fine for the single-robot examples and painful for the
51
- multi-robot ones:
52
-
53
- | Example | LLM calls | `qwen3.6:latest` | `LLM_PROFILE=small` |
54
- |---------|-----------|------------------|---------------------|
55
- | 01, 02, 19, 20 | 1-6 | seconds to ~5 min | fast |
56
- | 03, 06, 07, 24, 27, 31 | 4-8 | ~5-10 min | ~2 min |
57
- | 13, 14, 15 | ~20-30 | **30-40 min** | ~5-10 min |
58
- | 16 | unbounded (600s cap) | usually hits the cap | often completes |
59
-
60
- `LLM_PROFILE` switches the model for any example without editing it:
61
-
62
- ```bash
63
- LLM_PROFILE=small bundle exec ruby examples/14_rusty_circuit/open_mic.rb
64
- ```
65
-
66
- Valid values are the keys of `LLM` in `common.rb`: `default`, `small`, `large`.
67
-
68
- **Long silences are not hangs.** In example 14 the scout writes its notes to
69
- `output/scout_notes.md` rather than STDOUT, and a scout turn is 1-4 sequential
70
- calls — minutes of blank terminal. The `· Scout …` lines exist so you can tell
71
- progress from a stall. Example 16 is the same idea at larger scale: writers
72
- coordinate through shared memory, and much of the work shows up in
73
- `output/room.log` rather than on screen.
74
-
75
- ## Running Examples
76
-
77
- ```bash
78
- # Run a specific example by number
79
- bundle exec rake examples:run[1]
80
-
81
- # Run all examples
82
- bundle exec rake examples:all
83
-
84
- # Run directly
85
- bundle exec ruby examples/01_simple_robot.rb
86
- ```
87
-
88
- ## Tools require `tools: :inherit` at run time
89
-
90
- This trips up everyone once. `Robot#run` defaults to `tools: :none`, which
91
- means *"send zero tools this turn"* — the robot still holds them in
92
- `local_tools`, but the provider never sees them and the model answers from
93
- memory instead of calling anything:
94
-
95
- ```ruby
96
- robot = RobotLab.build(name: "bot", **llm_opts, local_tools: [Calculator])
97
-
98
- robot.run("What is 15 * 7?") # Calculator is NEVER offered
99
- robot.run("What is 15 * 7?", tools: :inherit) # Calculator is offered
100
- ```
101
-
102
- The same applies to MCP (`mcp: :inherit`), to network tasks
103
- (`task :name, robot, tools: :inherit`), and to anything that forwards keywords
104
- into `run` — including `RobotLab::RailsIntegration::Job`. Examples 02, 04, 06,
105
- 14, 15, 16, 20, 33 and the Rails app all pass it explicitly.
106
-
107
- ## Directory Structure
108
-
109
- ```
110
- examples/
111
- common.rb # Shared LLM config (Ollama), llm_opts, output helpers
112
- xyzzy.rb # Single-file hook extension used by 35
113
- 01_simple_robot.rb # Basic robot with template
114
- 02_tools.rb # Robot with custom tools
115
- 03_network.rb # Multi-robot network with routing
116
- 04_mcp.rb # MCP server integration (GitHub)
117
- 05_streaming.rb # Real-time streaming callbacks
118
- 06_prompt_templates.rb # Template-based e-commerce support
119
- 07_network_memory.rb # Shared memory with concurrent robots
120
- 08_llm_config.rb # Configuration hierarchy demo
121
- 09_chaining.rb # with_* method chaining & reconfiguration
122
- 10_memory.rb # Advanced Memory API operations
123
- 11_network_introspection.rb # Network visualization & inspection
124
- 12_message_bus.rb # Bidirectional robot communication
125
- 13_spawn.rb # Dynamic specialist robot spawning
126
- 14_rusty_circuit/ # Multi-robot open mic with self-modification
127
- open_mic.rb # Main entrypoint — wires up the show
128
- comic.rb # Comedian with self-modification tools
129
- heckler.rb # Audience heckler (can stay silent or counter-joke)
130
- scout.rb # Talent scout with analyst spawning
131
- display.rb # Terminal formatting (color, wrapping, file output)
132
- prompts/ # Templates for comic, heckler, and scout
133
- 15_memory_network_and_bus/ # Network + memory + bus + spawn in one pipeline
134
- editorial_pipeline.rb # Main entrypoint — three writers, editor, chief
135
- 16_writers_room/ # Self-organizing group: no orchestration at all
136
- writers_room.rb # Main entrypoint — book and screenplay modes
137
- 17_skills.rb # Composable skills, flat and recursive
138
- 18_rails/ # Minimal Rails 8 demo app (full integration)
139
- app/robots/chat_robot.rb # Robot factory with system prompt + TimeTool
140
- app/tools/time_tool.rb # Custom RobotLab::Tool subclass
141
- app/jobs/robot_run_job.rb # Background job with Turbo Stream callbacks
142
- app/controllers/ # Chat controller (index + create)
143
- app/views/ # Layout with CDN importmap, chat view with streaming
144
- app/models/ # RobotLabThread, RobotLabResult
145
- config/ # Minimal Rails 8 config (async adapters, no asset pipeline)
146
- db/migrate/ # Migration from generator template
147
- 19_token_tracking.rb # Per-robot token & cost tracking
148
- 20_circuit_breaker.rb # Tool loop circuit breaker with max_tool_rounds
149
- 21_learning_loop.rb # Learning accumulation across runs with robot.learn
150
- 22_context_compression.rb # Context window compression with HistoryCompressor
151
- 23_convergence.rb # Debate convergence detection and reconciler fast-path
152
- 24_structured_delegation.rb # Structured delegation with duration and token tracking
153
- 25_history_search.rb # Semantic search over a robot's conversation history
154
- 26_document_store.rb # Embedding-based document store (RAG) via fastembed
155
- 27_incident_response/ # BusPoller, reactive memory, poller groups
156
- incident_response.rb # Main entrypoint — wires up the war room
157
- 28_mcp_discovery.rb # Semantic MCP server selection before connecting
158
- 29_ractor_tools.rb # Ractor-safe tools: worker pool, freeze_deep, parallel batch
159
- 30_ractor_network.rb # Ractor network scheduler: dependency waves, parallel_mode
160
- 31_launch_assessment.rb # 6 parallel analysts, max_concurrent_robots: 4 semaphore cap
161
- 32_newsletter_reader.rb # Utility script (no RobotLab) — RSS to Markdown
162
- 33_stock_generator.rb # Companion publisher for 33_stock_predictor (Redis)
163
- 33_stock_predictor.rb # Durable cross-session learning via robot_lab-durable
164
- 34_agentskills.rb # AgentSkills.io folder-format skills, matched per run
165
- 35_hooks.rb # Hook architecture demo using xyzzy.rb
166
- prompts/ # Prompt templates (.md with YAML front matter)
167
- ```
168
-
169
- ## Examples
170
-
171
- ### 01 — Simple Robot
172
-
173
- Create and run a basic robot using a prompt template. Sends a single message and displays the response.
174
-
175
- **Requires:** Ollama
176
-
177
- ### 02 — Tools
178
-
179
- Give a robot custom tools (`Calculator`, `FortuneCookie`) defined as `RubyLLM::Tool` subclasses. The LLM decides when to call each tool based on the user's request.
180
-
181
- **Requires:** Ollama
182
-
183
- ### 03 — Multi-Robot Network
184
-
185
- Build a customer support network with a classifier robot that routes requests to billing, technical, or general specialists. Uses SimpleFlow's optional task activation for conditional routing.
186
-
187
- **Requires:** Ollama
188
-
189
- ### 04 — MCP Integration
190
-
191
- Connect to the GitHub MCP server via stdio transport. Part 1 demonstrates direct `MCP::Client` usage (listing tools, calling `search_repositories`). Part 2 wraps the MCP server inside a robot for natural-language queries.
192
-
193
- **Requires:** Ollama, `GITHUB_PERSONAL_ACCESS_TOKEN`, `github-mcp-server` installed
194
-
195
- ### 05 — Streaming
196
-
197
- Real-time streaming of robot responses through four routes: the stored `on_content:` callback wired at build time, a per-call block passed to `run()`, both together (stored fires first, then the block), and `on_content` arriving through a `RunConfig`.
198
-
199
- **Requires:** Ollama (makes four live calls)
200
-
201
- ### 06 — Prompt Templates
202
-
203
- Full e-commerce support system using prompt_manager templates with YAML front matter. A triage robot classifies customer requests and routes to order, product, or escalation specialists. Demonstrates build-time context (company info, policies) and run-time context (customer data, order history).
204
-
205
- **Requires:** Ollama
206
-
207
- ### 07 — Network Memory
208
-
209
- Reactive shared memory with concurrent robots. Multiple analysis robots (sentiment, entity extraction, keywords) run in parallel and write to shared memory. A synthesizer robot waits for all results using blocking reads, then produces a combined analysis. Demonstrates subscriptions, notifications, and network broadcast.
210
-
211
- **Requires:** Ollama
212
-
213
- ### 08 — LLM Configuration
214
-
215
- Walks through the full configuration hierarchy without making LLM calls:
216
-
217
- 1. Bundled defaults
218
- 2. Environment-specific overrides
219
- 3. XDG user config
220
- 4. Project config
221
- 5. Environment variables
222
- 6. Template front matter
223
- 7. Constructor parameters
224
- 8. `with_*` method chaining
225
- 9. Run-time context
226
-
227
- **Requires:** None (no LLM calls)
228
-
229
- ### 09 — Chaining & Reconfiguration
230
-
231
- Demonstrates the Robot API surface for runtime configuration: `with_*` method chaining, `update()` for reconfiguration, `to_h` introspection, config diffs between steps, and how constructor params override template front matter.
232
-
233
- **Requires:** None (no LLM calls)
234
-
235
- ### 10 — Advanced Memory
236
-
237
- Comprehensive Memory API demo: `StateProxy` for method-style access, key and pattern subscriptions, `MemoryChange` objects, key enumeration, serialization round-trips, clone for isolated copies, delete with reserved key protection, and clear vs reset.
238
-
239
- **Requires:** None (no LLM calls)
240
-
241
- ### 11 — Network Introspection
242
-
243
- Network visualization and inspection tools: `to_mermaid()` for diagram export, `to_dot()` for Graphviz, `execution_plan()` for text output, `visualize()` for ASCII pipelines, robot access by name, dynamic `add_robot()`, `to_h()` introspection, task-specific config, and `broadcast()`.
244
-
245
- **Requires:** None (no LLM calls)
246
-
247
- ### 12 — Message Bus
248
-
249
- Bidirectional robot communication via TypedBus. A comedy critic (Alice) tasks a comedian (Bob) to tell robot jokes. Alice evaluates each joke with her LLM; if it's not funny, she sends Bob back for another attempt. Bob's temperature ramps from 0.2 to 1.0 across retries for increasing creativity. The loop continues until Alice approves or `MAX_ATTEMPTS` is reached.
250
-
251
- Demonstrates: Robot subclasses, prompt templates, auto-ack `on_message`, `reply()` convenience, temperature ramping, convergence patterns.
252
-
253
- **Requires:** Ollama
254
-
255
- ### 13 — Spawning Robots
256
-
257
- Dynamic specialist creation at runtime. A dispatcher robot receives questions, asks its LLM what kind of specialist is needed, then uses `spawn` to create one on the fly. The bus is created lazily on the first spawn — no explicit bus setup required. Spawned specialists are reused across questions of the same type.
258
-
259
- Demonstrates: `spawn` for dynamic robot creation, lazy bus creation, `on_message` for reply handling, LLM-driven delegation.
260
-
261
- **Requires:** Ollama
262
-
263
- ### 14 — The Rusty Circuit (Open Mic Night)
264
-
265
- A comedy club where three robots interact through a shared message bus. A comedian performs stand-up armed with self-modification tools (style reinvention, energy adjustment, coaching). A heckler reacts from the audience — heckling weak material, telling counter-jokes with the comic as the punch line, showing grudging respect, or staying silent when a bit doesn't warrant a response. A talent scout observes silently, spawning specialist analysts and refining evaluation criteria before delivering a final verdict.
266
-
267
- Terminal output is color-formatted: comic bits in cyan (left-aligned), heckler reactions in yellow (right-indented), tool annotations dimmed. Scout notes go to `scout_notes.md` instead of STDOUT. The final verdict appears in green on both STDOUT and the scout file.
268
-
269
- Demonstrates: Robot subclasses, self-modification via tool side effects, dynamic spawning (`spawn`), shared `:room` channel + personal channels, `enqueue_delivery` for serializing async deliveries, `[SILENCE]` opt-out pattern, style reinvention via user-prompt injection.
270
-
271
- **Requires:** Ollama. The most expensive example in the suite — ~30 LLM calls, 30+ minutes on `qwen3.6`. Use `LLM_PROFILE=small` to watch it in under ten.
272
-
273
- ### 15 — Memory, Network, Bus & Spawn Together
274
-
275
- An editorial pipeline where three writers advocate for macOS, Windows, and Linux/BSD as a home AI research lab platform. The network runs the writers in parallel and hands their drafts to an editor through shared memory; the Linux writer spawns three distro specialists mid-pipeline; an editor-in-chief outside the pipeline reviews the combined article over the bus and can demand revisions.
276
-
277
- Demonstrates: all four coordination mechanisms in one program, plus the direct `shared_memory` reference pattern that parallel pipeline steps need (`extract_run_context` deletes from a shared hash, so only the first parallel step would otherwise see `network_memory`).
278
-
279
- **Requires:** Ollama
280
-
281
- ### 16 — The Writers' Room (Self-Organizing Group)
282
-
283
- A team of identical writer robots produces a 10-chapter novella with no orchestration, no pipeline, and no assigned roles. Each writer subscribes to a `:room` broadcast channel and a personal channel, and has tools to read/write shared memory, broadcast, DM, spawn more writers, and mark the work complete. The script seeds the room with an assignment and waits.
284
-
285
- `--screenplay-from output/memory.json` re-runs the room in screenplay mode, adapting a finished book into a 4-act TV movie pilot at scene granularity.
286
-
287
- Demonstrates: emergent coordination, `clear_messages(keep_system: true)` as a per-message conversation reset, dynamic team growth, memory as the single source of truth.
288
-
289
- **Requires:** Ollama (long-running — `--timeout` defaults to 600s)
290
-
291
- ### 17 — Composable Skills
292
-
293
- Skills are ordinary templates whose bodies are prepended to the main template. An SRE incident responder is composed from `runbook_protocol` and `structured_output` (flat skills) plus `sre_compliance`, which recursively expands to `pii_redactor` and `audit_trail`. The assembled system prompt is printed line-by-line so the depth-first expansion order is visible, and a skill-less robot is built alongside it for size comparison.
294
-
295
- **Requires:** Ollama
296
-
297
- ### 19 — Token & Cost Tracking
298
-
299
- Track token usage across runs using `result.input_tokens` / `result.output_tokens` for per-run counts and `robot.total_input_tokens` / `robot.total_output_tokens` for running totals. Demonstrates `reset_token_totals` to start a fresh batch. Local inference is free, so cost is reported as `$0.00000 (local)`; set `RATE_INPUT_CPM` / `RATE_OUTPUT_CPM` to price the same traffic against a hosted provider.
300
-
301
- **Requires:** Ollama
302
-
303
- ### 20 — Tool Loop Circuit Breaker
304
-
305
- Guards against runaway tool call loops using `max_tool_rounds:`. A step processor tool is designed to always return "more steps remain", which would loop indefinitely without a guard. The circuit breaker fires after the configured limit and raises `RobotLab::ToolLoopError`. Shows how to rescue the error gracefully and confirms the robot is fully reusable after a breaker trip.
306
-
307
- **Requires:** Ollama
308
-
309
- ### 21 — Learning Accumulation Loop
310
-
311
- Builds up cross-run observations with `robot.learn(text)`. A code reviewer accumulates one key insight after each review. On subsequent runs, learnings are automatically prepended to the user message as a "LEARNINGS FROM PREVIOUS RUNS:" block. Demonstrates bidirectional substring deduplication (broader learnings replace narrower ones), the `robot.learnings` accessor, and how learnings survive a robot rebuild via the shared `Memory` object.
312
-
313
- **Requires:** Ollama
314
-
315
- ### 22 — Context Window Compression
316
-
317
- Demonstrates `robot.compress_history()` for reducing token usage in long conversations. Old turns are scored against the recent context using stemmed term-frequency cosine similarity (via the `classifier` gem). High-relevance turns are kept verbatim; irrelevant turns are dropped; medium-relevance turns can optionally be summarized by a second robot. Shows both drop-mode and summarizer-lambda patterns, plus the LLM summarizer integration recipe.
318
-
319
- **Requires:** `gem 'classifier', '~> 2.3'` in your Gemfile (no LLM calls in the demo itself)
320
-
321
- ### 23 — Debate Convergence Detection
322
-
323
- Demonstrates `RobotLab::Convergence` for detecting when two independent agents have reached the same conclusion. Scores pairs of texts from identical → semantically similar → partially related → unrelated, showing how the similarity metric varies. Includes the fast-path pattern: a gate robot compares two verifiers' replies and only activates the expensive reconciler — via SimpleFlow optional-task activation — when they disagree.
324
-
325
- **Requires:** `gem 'classifier', '~> 2.3'` in your Gemfile (no LLM calls in the demo itself)
326
-
327
- ### 24 — Structured Delegation
328
-
329
- Demonstrates `robot.delegate(to:, task:)` for synchronous and asynchronous inter-robot delegation. The manager robot delegates document analysis to a summarizer and an analyst. Shows synchronous (sequential, blocking) and asynchronous (parallel fan-out, `DelegationFuture`) modes with wall-time comparison. Each result carries `delegated_by`, `duration`, and token counts.
330
-
331
- **Requires:** Ollama
332
-
333
- ### 25 — Chat History Search
334
-
335
- Demonstrates `robot.search_history(query, limit:)` — semantic search over accumulated conversation turns using stemmed TF cosine similarity. No LLM calls: messages are injected directly. Shows relevance ranking, role preservation, short-message filtering, and the `DependencyError` guard.
336
-
337
- **Requires:** `gem 'classifier', '~> 2.3'` in your Gemfile (no LLM calls)
338
-
339
- ### 26 — Embedding-Based Document Store
340
-
341
- Demonstrates `memory.store_document(key, text)` and `memory.search_documents(query, limit:)` — a lightweight RAG store using `fastembed` (BAAI/bge-small-en-v1.5). Documents are embedded once; queries are compared by cosine similarity at search time. Includes the `RobotLab::DocumentStore` standalone API and a RAG pattern sketch showing how to pass retrieved context to a robot.
342
-
343
- **Requires:** `robot_lab-document_store` gem (`gem "robot_lab-document_store"` in your Gemfile); downloads the ~23 MB ONNX model on first run (cached in `~/.cache/fastembed/`)
344
-
345
- ### 27 — Production Incident War Room
346
-
347
- Three SRE scout robots investigate a payment-service outage in parallel — database layer, network layer, and application layer. Each scout stores its findings in shared reactive memory and broadcasts a status update to a war-room coordinator via TypedBus.
348
-
349
- Demonstrates all four Phase 5 infrastructure improvements together:
350
-
351
- | Feature | How it shows up |
352
- |---------|----------------|
353
- | **IO.pipe Waiter** (#13) | Commander blocks on `memory.get(:db_finding, :net_finding, :app_finding, wait: 60)`; wakes the instant the last scout writes its key |
354
- | **BusPoller** (#14) | All three scouts send bus messages to the war room; BusPoller serializes delivery so war_room processes them one at a time, in arrival order, with no dropped messages |
355
- | **Poller Groups** (#15) | Scout tasks labeled `poller_group: :investigation`; commander task labeled `poller_group: :command`; group list printed before the run |
356
- | **Reactive Memory** | `memory.subscribe` callbacks fire in real-time as each scout writes, while the blocking waiter runs independently |
357
-
358
- **Run:**
359
- ```bash
360
- bundle exec ruby examples/27_incident_response/incident_response.rb
361
- # or
362
- bundle exec rake examples:run[27]
363
- ```
364
-
365
- **Requires:** Ollama
366
-
367
- ### 28 — MCP Server Discovery
368
-
369
- When a robot has many MCP servers configured, connecting to all of them upfront is wasteful. `mcp_discovery: true` enables semantic server selection: before the first connection, `MCP::ServerDiscovery` scores each server's `name + description` against the user query using TF cosine similarity and connects only the relevant subset.
370
-
371
- Demonstrates: `MCP::ServerDiscovery.select(query, from:, threshold:)`, the `description:` field on MCP server configs, `mcp_discovery: true` on Robot, and all four fallback cases (no descriptions, blank query, classifier unavailable, no match above threshold).
372
-
373
- **Requires:** None (no LLM calls — exercises the discovery module directly)
374
-
375
- ### 29 — Ractor-Safe CPU Tools
376
-
377
- Demonstrates Track 1 of RobotLab's Ractor parallelism: CPU-bound tool classes that
378
- bypass the GVL by routing through a pool of Ractor workers.
379
-
380
- | Section | What it shows |
381
- |---------|--------------|
382
- | `ractor_safe?` flags | `WordStatsTool`, `ReadabilityTool`, `HeavyDigestTool` inherit `ractor_safe true` from `TextTool`; `RequestCounterTool` (mutable class variable) does not |
383
- | `RactorBoundary.freeze_deep` | Deep-freezes nested hashes/arrays; raises `RactorBoundaryError` on `StringIO` |
384
- | Single pool submit | Direct `RobotLab.ractor_pool.submit(class_name, args)` calls |
385
- | `ToolError` propagation | `nil.scan(...)` inside a Ractor worker → `NoMethodError` → `RobotLab::ToolError` |
386
- | Parallel batch | 6 threads each submitting a `HeavyDigestTool` job (5 000 SHA-256 rounds) simultaneously vs sequentially; speedup visible on multi-core hardware |
387
-
388
- **Requires:** None (no LLM calls)
389
-
390
- ### 30 — Ractor Network Scheduler
391
-
392
- Demonstrates Track 2 of RobotLab's Ractor parallelism: running a multi-robot
393
- pipeline under `parallel_mode: :ractor`, which dispatches independent tasks in
394
- true parallel waves and respects `depends_on` ordering.
395
-
396
- Pipeline topology (4 robots):
397
- ```
398
- headline_finder ──┐
399
- background_brief ──┼──► report_writer
400
- fact_checker ──┘
401
- ```
402
- Wave 1 (headline_finder + background_brief + fact_checker) runs in parallel;
403
- Wave 2 (report_writer) runs after all three complete.
404
-
405
- **Part 1** — `SimulatedScheduler` (overrides `execute_spec` with `sleep`) shows
406
- wave ordering and timing without any API calls. Expected: ~1.3 s parallel vs 2.2 s sequential.
407
-
408
- **Part 2** — Walks through `Network.new(parallel_mode: :ractor)` configuration
409
- and the `pipeline.step_dependencies` dependency graph inspection.
410
-
411
- **Part 3** — Live LLM run (enabled automatically when `ANTHROPIC_API_KEY` is set).
412
-
413
- **Requires:** None for Parts 1 & 2. `RUN_LIVE=1` plus Ollama for Part 3 (expected to fail — ruby_llm is not Ractor-safe yet).
414
-
415
- ### 31 — Product Launch Assessment (Concurrency Cap)
416
-
417
- Six specialist robots evaluate a product launch simultaneously: market, competitive, technical, risk, financial, and legal analysts. `RunConfig.new(max_concurrent_robots: 4)` caps the `Async::Semaphore` at 4 in-flight LLM calls — robots 5 and 6 queue until a slot opens. A `LaunchDirector` reads all six findings from shared reactive memory and issues a GO / NO-GO recommendation. Start timestamps in the output make the semaphore behavior visible.
418
-
419
- Demonstrates: `max_concurrent_robots:` on `RunConfig`, `Async::Semaphore` back-pressure via `simple_flow`, six parallel `depends_on: :none` tasks, shared memory writes and blocking reads.
420
-
421
- **Requires:** Ollama
422
-
423
- ### 32 — Newsletter Reader
424
-
425
- A plain utility script that fetches unprocessed issues from Ruby newsletter RSS feeds and saves them as Markdown. **It uses no part of RobotLab** — it lives here as a content feeder for other experiments, not as a capability demo. Set `CLIPPINGS_DIR` to choose the output folder.
426
-
427
- **Requires:** `html2markdown` on `PATH`
428
-
429
- ### 33 — Durable Cross-Session Learning
430
-
431
- `33_stock_generator.rb` publishes synthetic XYZZY prices to a Redis channel using geometric Brownian motion. `33_stock_predictor.rb` consumes them, predicts each window's high/low with an SMA + EMA ensemble, and after every window asks a tuner robot to adjust the predictor's parameters.
432
-
433
- The tuner uses `robot_lab-durable`. Note the API: `learn:` / `learn_domain:` are **not** constructor parameters (`Robot#initialize` takes a fixed keyword list with no `**rest`, so passing them raises `ArgumentError`). Call `robot.setup_durable_learning(domain:)` after `build`, which seeds `robot.learnings` from `~/.robot_lab/durable/<domain>.yml` and appends the `RecallKnowledge` / `RecordKnowledge` tools. Core `Robot#run` does not invoke the reflector, so the caller drives `robot.run_reflector` to promote learnings back to the store.
434
-
435
- Run the two scripts in separate terminals.
436
-
437
- **Requires:** Ollama, Redis on localhost:6379, `robot_lab-durable`
438
-
439
- ### 34 — AgentSkills.io Integration
440
-
441
- Skills declared as `skills: [:code_reviewer]` are resolved from `~/.prompts/skills/<name>/SKILL.md` and matched per-run by embedding similarity — a code-review question activates the skill, an unrelated question does not. Run the two queries and compare.
442
-
443
- **Requires:** Ollama, `robot_lab-document_store` gem, a `SKILL.md` at `~/.prompts/skills/code_reviewer/`
444
-
445
- ### 35 — Hooks Architecture
446
-
447
- Loads `xyzzy.rb`, a single-file `RobotLab::Hook` subclass that registers for every hook and logs each callback with the context it receives. Demonstrates robot run, LLM generation, tool call, network run, task, and error hooks. Stubs `robot.chat.ask` for the deterministic sections; the LLM-generation section makes four real calls to show `around_llm_generation` serving two of them from a cache.
448
-
449
- `RobotLab::Hook` has no logger accessor — handlers own their output. `xyzzy.rb` freezes its log destination from `XYZZY_LOG_PATH` at load time, so the env var must be set before the `require`.
450
-
451
- **Requires:** Ollama (four calls in the LLM-loop section)
452
-
453
- ### 18 — Rails Integration Demo
454
-
455
- A minimal, hand-built Rails 8 app that exercises every piece of RobotLab's Rails integration end-to-end. No `rails new` — every file is hand-crafted for minimum size.
456
-
457
- **What it demonstrates:**
458
- - **ChatRobot** with a custom `TimeTool` (`app/robots/`, `app/tools/`)
459
- - **RobotRunJob** — background execution via `ActiveJob` async adapter
460
- - **Turbo Stream token streaming** — real-time content chunks broadcast to the browser over ActionCable
461
- - **Persistence** — `RobotLabThread` + `RobotLabResult` with conversation history
462
- - **Auto-scrolling chat** — MutationObserver keeps the view pinned to the latest streaming content
463
-
464
- **No Redis, no Solid Queue, no asset pipeline.** Uses `:async` adapters for both ActiveJob and ActionCable. Turbo JS loaded via importmap from CDN (`@hotwired/turbo-rails`).
465
-
466
- **Two API details worth copying:** the job's superclass is `RobotLab::RailsIntegration::Job` (there is no `RobotLab::Job` alias), and the controller enqueues with `tools: :inherit` so that keyword reaches `robot.run` — otherwise `TimeTool` is never offered and the model guesses the time.
467
-
468
- ```bash
469
- cd examples/18_rails
470
- bin/setup # bundle install + db:create + db:migrate
471
- bin/dev # starts Puma on http://localhost:3000
472
- ```
473
-
474
- **Requires:** Ollama, Ruby 3.2+
475
-
476
- ## Prompt Templates
477
-
478
- Templates live in `examples/prompts/` as `.md` files with YAML front matter. Each template defines a robot's personality and behavior:
479
-
480
- ```markdown
481
- ---
482
- description: Simple helpful assistant
483
- temperature: 0.7
484
- parameters:
485
- company_name: null
486
- ---
487
- You are a helpful assistant for <%= company_name %>.
488
- ```
489
-
490
- Front matter keys like `model`, `temperature`, `top_p`, `max_tokens` are applied to the robot's chat automatically. Parameters with `null` values are required and must be provided via `context:` at build time.
491
-
492
- ### Template Inventory
493
-
494
- | Template | Used By | Description |
495
- |----------|---------|-------------|
496
- | `helper.md` | 01 | Simple helpful assistant |
497
- | `assistant.md` | 02 | Assistant with tool access |
498
- | `classifier.md` | 03 | Request classifier (billing/technical/general) |
499
- | `billing.md` | 03 | Billing specialist |
500
- | `technical.md` | 03 | Technical support specialist |
501
- | `general.md` | 03 | General support |
502
- | `github_assistant.md` | 04 | GitHub-aware assistant |
503
- | `triage.md` | 06 | E-commerce request triage |
504
- | `order_support.md` | 06 | Order inquiry specialist |
505
- | `product_support.md` | 06 | Product questions specialist |
506
- | `escalation.md` | 06 | Complex issue handler |
507
- | `sentiment_analyzer.md` | 07 | Sentiment analysis |
508
- | `entity_extractor.md` | 07 | Entity extraction |
509
- | `keyword_extractor.md` | 07 | Keyword extraction |
510
- | `synthesizer.md` | 07 | Multi-source synthesis |
511
- | `llm_config_demo.md` | 08 | Configuration demo |
512
- | `configurable.md` | 09 | Configurable template with front matter |
513
- | `comedian.md` | 12 | Robot joke teller |
514
- | `comedy_critic.md` | 12 | Joke evaluator (FUNNY/NOT_FUNNY) |
515
- | `dispatcher.md` | 13 | Specialist role dispatcher |
516
- | `open_mic_comic.md` | 14 | Observational comedian with self-modification |
517
- | `open_mic_heckler.md` | 14 | Tough audience heckler (can stay silent or counter-joke) |
518
- | `open_mic_scout.md` | 14 | Talent scout with analyst recruitment |
519
- | `os_advocate.md` | 15 | Operating-system advocacy writer |
520
- | `os_editor.md` | 15 | Synthesizes the three advocacy drafts |
521
- | `os_chief.md` | 15 | Editor-in-chief (APPROVED / REVISE) |
522
- | `writer.md` | 16 | Self-organizing novella writer |
523
- | `screenplay_writer.md` | 16 | Self-organizing screenplay writer |
524
- | `incident_responder.md` | 17 | SRE on-call analyst (main template) |
525
- | `runbook_protocol.md` | 17 | Flat skill: 5-step incident protocol |
526
- | `structured_output.md` | 17 | Flat skill: JSON response format |
527
- | `sre_compliance.md` | 17 | Recursive skill: bundles the two leaves below |
528
- | `pii_redactor.md` | 17 | Leaf skill: redact PII |
529
- | `audit_trail.md` | 17 | Leaf skill: audit metadata |
530
-
531
- Templates matching `*_test.md` are fixtures for the test suite, not examples.
532
-
533
- Templates for 14, 15 and 16 live in those examples' own `prompts/` directories; each entrypoint sets `ROBOT_LAB_TEMPLATE_PATH` before requiring `common.rb`.