aia 1.1.0 → 2.0.0.0.pre.alpha

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 (170) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +5 -1
  3. data/.loki +231 -0
  4. data/.quality/flay_baseline.txt +1 -0
  5. data/.quality/flog_baseline.txt +29 -0
  6. data/.quality/reek_baseline.txt +80 -0
  7. data/.rubocop.yml +116 -0
  8. data/.version +1 -1
  9. data/CHANGELOG.md +266 -42
  10. data/IMPLEMENTATION_PLAN.md +506 -0
  11. data/README.md +266 -238
  12. data/Rakefile +118 -5
  13. data/architecture_review.md +314 -0
  14. data/bin/aia +16 -0
  15. data/docs/AGENTS.md +40 -0
  16. data/docs/advanced-prompting.md +67 -3
  17. data/docs/cli-reference.md +312 -56
  18. data/docs/configuration.md +130 -19
  19. data/docs/contributing.md +56 -2
  20. data/docs/directives-reference.md +593 -78
  21. data/docs/faq.md +85 -3
  22. data/docs/guides/available-models.md +1 -1
  23. data/docs/guides/basic-usage.md +6 -6
  24. data/docs/guides/chat.md +40 -16
  25. data/docs/guides/crew.md +239 -0
  26. data/docs/guides/executable-prompts.md +1 -1
  27. data/docs/guides/index.md +1 -0
  28. data/docs/guides/models.md +15 -0
  29. data/docs/index.md +29 -2
  30. data/docs/installation.md +44 -17
  31. data/docs/mcp-integration.md +40 -0
  32. data/docs/prompt_management.md +85 -86
  33. data/docs/security.md +47 -0
  34. data/docs/special_projects_guide.md +386 -0
  35. data/docs/tools-and-mcp-examples.md +23 -0
  36. data/docs/workflows-and-pipelines.md +84 -7
  37. data/examples/.gitignore +1 -0
  38. data/examples/00_setup_aia.sh +27 -44
  39. data/examples/11_multi_model.sh +4 -14
  40. data/examples/12_token_usage.sh +3 -12
  41. data/examples/18_tools.sh +10 -2
  42. data/examples/22_chat_mode.sh +0 -10
  43. data/examples/23_verify.sh +139 -0
  44. data/examples/24_decompose.sh +139 -0
  45. data/examples/25_spawn.sh +139 -0
  46. data/examples/26_debate.sh +97 -0
  47. data/examples/27_mention_routing.sh +157 -0
  48. data/examples/28_model_switching.sh +106 -0
  49. data/examples/29_agent_harness.sh +177 -0
  50. data/examples/README.md +65 -0
  51. data/examples/advanced_multi_robot_capabilities_without_examples.md +106 -0
  52. data/examples/aia_config.yml +1 -1
  53. data/examples/aia_config_orchestrator.yml +45 -0
  54. data/examples/common.sh +19 -0
  55. data/examples/context/tech_stack.md +2 -2
  56. data/examples/prompts_dir/project_summary +2 -2
  57. data/examples/prompts_dir/roles/orchestrator.md +21 -0
  58. data/examples/requirements/sinatra_taskflow_app.md +139 -0
  59. data/examples/rules/01_classify_ruby.rb +16 -0
  60. data/examples/rules/02_prefer_claude_for_code.rb +19 -0
  61. data/examples/rules/03_gate_prompt_length.rb +19 -0
  62. data/examples/rules/04_tool_selection.rb +41 -0
  63. data/examples/rules/README.md +30 -0
  64. data/examples/run_all.sh +48 -15
  65. data/examples/tools/word_count_tool.rb +1 -1
  66. data/lib/AGENTS.md +57 -0
  67. data/lib/aia/chat_loop.rb +306 -159
  68. data/lib/aia/config/cli_parser.rb +174 -111
  69. data/lib/aia/config/defaults.yml +62 -33
  70. data/lib/aia/config/mcp_parser.rb +39 -46
  71. data/lib/aia/config/model_spec.rb +34 -2
  72. data/lib/aia/config/validator.rb +121 -138
  73. data/lib/aia/config.rb +110 -145
  74. data/lib/aia/content_extractor.rb +153 -0
  75. data/lib/aia/cost_calculator.rb +38 -0
  76. data/lib/aia/crew.rb +164 -0
  77. data/lib/aia/debate_handler.rb +166 -0
  78. data/lib/aia/delegate_handler.rb +112 -0
  79. data/lib/aia/directive.rb +33 -18
  80. data/lib/aia/directive_processor.rb +16 -7
  81. data/lib/aia/directives/configuration_directives.rb +160 -20
  82. data/lib/aia/directives/context_directives.rb +38 -26
  83. data/lib/aia/directives/execution_directives.rb +136 -4
  84. data/lib/aia/directives/model_directives.rb +76 -34
  85. data/lib/aia/directives/trakflow_directives.rb +44 -0
  86. data/lib/aia/directives/utility_directives.rb +203 -6
  87. data/lib/aia/directives/web_and_file_directives.rb +96 -60
  88. data/lib/aia/errors.rb +15 -0
  89. data/lib/aia/fact_asserter.rb +27 -0
  90. data/lib/aia/fzf.rb +9 -31
  91. data/lib/aia/handler_context.rb +17 -0
  92. data/lib/aia/handler_protocol.rb +19 -0
  93. data/lib/aia/history_transfer.rb +55 -0
  94. data/lib/aia/input_collector.rb +3 -3
  95. data/lib/aia/layered_orchestrator.rb +448 -0
  96. data/lib/aia/logger.rb +24 -4
  97. data/lib/aia/mcp_config_normalizer.rb +35 -0
  98. data/lib/aia/mcp_connection_manager.rb +305 -0
  99. data/lib/aia/mcp_discovery.rb +44 -0
  100. data/lib/aia/mcp_grouper.rb +33 -0
  101. data/lib/aia/mcp_utility.rb +57 -0
  102. data/lib/aia/mention_router.rb +260 -0
  103. data/lib/aia/model_alias_registry.rb +97 -0
  104. data/lib/aia/model_switch_handler.rb +100 -0
  105. data/lib/aia/network_builder.rb +155 -0
  106. data/lib/aia/network_memory_manager.rb +55 -0
  107. data/lib/aia/patches/ruby_llm_streaming_error.rb +43 -0
  108. data/lib/aia/patches/ruby_llm_tool_error.rb +96 -0
  109. data/lib/aia/pipeline_orchestrator.rb +262 -0
  110. data/lib/aia/plugin_loader.rb +170 -0
  111. data/lib/aia/plugin_monitor.rb +208 -0
  112. data/lib/aia/prompt_decomposer.rb +157 -0
  113. data/lib/aia/prompt_handler.rb +19 -39
  114. data/lib/aia/robot_builder.rb +51 -0
  115. data/lib/aia/robot_factory.rb +334 -0
  116. data/lib/aia/robot_namer.rb +116 -0
  117. data/lib/aia/session.rb +83 -17
  118. data/lib/aia/session_tracker.rb +209 -0
  119. data/lib/aia/similarity_scorer.rb +39 -0
  120. data/lib/aia/skill_utils.rb +105 -1
  121. data/lib/aia/spawn_handler.rb +129 -0
  122. data/lib/aia/spawn_spec_parser.rb +65 -0
  123. data/lib/aia/special_mode_handler.rb +302 -0
  124. data/lib/aia/startup_coordinator.rb +150 -0
  125. data/lib/aia/streaming_runner.rb +169 -0
  126. data/lib/aia/system_prompt_assembler.rb +88 -0
  127. data/lib/aia/task_coordinator.rb +202 -0
  128. data/lib/aia/task_decomposer.rb +57 -0
  129. data/lib/aia/task_executor.rb +51 -0
  130. data/lib/aia/tfidf_math.rb +27 -0
  131. data/lib/aia/tool_filter/tfidf.rb +116 -0
  132. data/lib/aia/tool_filter/wordnet_expander.rb +127 -0
  133. data/lib/aia/tool_filter.rb +82 -0
  134. data/lib/aia/tool_filter_registry.rb +30 -0
  135. data/lib/aia/tool_filter_strategy.rb +143 -0
  136. data/lib/aia/tool_loader.rb +210 -0
  137. data/lib/aia/tool_utility.rb +30 -0
  138. data/lib/aia/tools/delegate_to_foreman_tool.rb +70 -0
  139. data/lib/aia/tools/recruit_robot_tool.rb +60 -0
  140. data/lib/aia/tools/reskill_robot_tool.rb +44 -0
  141. data/lib/aia/tools/task_board_tool.rb +114 -0
  142. data/lib/aia/trakflow_bridge.rb +173 -0
  143. data/lib/aia/turn_state.rb +94 -0
  144. data/lib/aia/ui_presenter.rb +166 -198
  145. data/lib/aia/utility.rb +134 -87
  146. data/lib/aia/{history_manager.rb → variable_input_collector.rb} +8 -9
  147. data/lib/aia/verification_network.rb +58 -0
  148. data/lib/aia.rb +108 -63
  149. data/mkdocs.yml +1 -0
  150. metadata +179 -56
  151. data/justfile +0 -215
  152. data/lib/aia/adapter/chat_execution.rb +0 -242
  153. data/lib/aia/adapter/error_handler.rb +0 -68
  154. data/lib/aia/adapter/gem_activator.rb +0 -57
  155. data/lib/aia/adapter/mcp_connector.rb +0 -274
  156. data/lib/aia/adapter/modality_handlers.rb +0 -167
  157. data/lib/aia/adapter/model_registry.rb +0 -81
  158. data/lib/aia/adapter/multi_model_chat.rb +0 -218
  159. data/lib/aia/adapter/provider_configurator.rb +0 -59
  160. data/lib/aia/adapter/tool_filter.rb +0 -85
  161. data/lib/aia/adapter/tool_loader.rb +0 -90
  162. data/lib/aia/chat_processor_service.rb +0 -164
  163. data/lib/aia/prompt_pipeline.rb +0 -183
  164. data/lib/aia/ruby_llm_adapter.rb +0 -95
  165. data/lib/extensions/openstruct_merge.rb +0 -48
  166. data/lib/extensions/ruby_llm/.irbrc +0 -56
  167. data/lib/extensions/ruby_llm/modalities.rb +0 -36
  168. data/lib/extensions/ruby_llm/provider_fix.rb +0 -79
  169. data/lib/refinements/string.rb +0 -16
  170. data/main.just +0 -76
@@ -0,0 +1,506 @@
1
+ # AIA v2.0.0 — Implementation Plan
2
+ ## Date: 2026-03-27
3
+ ## Source: Comprehensive Architecture Review (2026-03-27)
4
+ ## Scope: 34 improvement items, sequenced for testable and releasable increments
5
+
6
+ Each section is independently testable and releasable. No section depends on unreleased work from a later section.
7
+
8
+ ---
9
+
10
+ ## Section 1 — Safety Net (P0 Critical Fixes) ✓ COMPLETED
11
+
12
+ **Release tag:** `v2.0.1.alpha` — tagged 2026-03-27
13
+
14
+ These are all small, self-contained fixes with no interdependencies. Each can be implemented and tested in isolation. Together they eliminate the critical production risks identified in the architecture review.
15
+
16
+ ### 1.1 — MCPConnectionManager: Mutex on All Reads ✓
17
+ **Source:** C1 / P0-1 | **File:** `mcp_connection_manager.rb:72-83` | **Size:** S
18
+
19
+ `inject_into()` reads `@connected_clients` and `@connected_tools` without acquiring `@mutex`. Threads writing in `connect_one()` race with the main thread reading in `inject_into()`.
20
+
21
+ **Fix:** Wrap all reads of `@connected_clients` and `@connected_tools` in `@mutex.synchronize`. Snapshot state under lock before iteration.
22
+
23
+ **Test:** Write a test that runs `connect_one` threads concurrently with `inject_into` calls and asserts no partial state is observed.
24
+
25
+ ---
26
+
27
+ ### 1.2 — AIA.reset! for Test Isolation ✓
28
+ **Source:** C2 / P0-2 | **File:** `aia.rb:77-88` | **Size:** S
29
+
30
+ Six mutable class-level singletons (`config`, `client`, `session_tracker`, `turn_state`, `task_coordinator`, `decisions`, `rule_router`) with no reset mechanism. Tests must manually nil each one; forgetting one causes cross-test state leakage.
31
+
32
+ **Fix:**
33
+ ```ruby
34
+ def self.reset!
35
+ @config = @client = @session_tracker = @turn_state =
36
+ @task_coordinator = @decisions = @rule_router = nil
37
+ end
38
+ ```
39
+
40
+ Call `AIA.reset!` in every test `teardown`.
41
+
42
+ **Test:** Verify that state set in one test does not leak to the next when `reset!` is called in teardown.
43
+
44
+ ---
45
+
46
+ ### 1.3 — Decisions Schema: Reject nil Model ✓
47
+ **Source:** C3 / P0-3 | **File:** `decisions.rb:22-30` | **Size:** S
48
+
49
+ `decisions.add(:model_decision, model: nil)` is accepted silently. Downstream callers pass `nil` to `RobotLab.build(model: nil)` — crash with no indication of source.
50
+
51
+ **Fix:** In `add()`, validate required keys for known decision types. For `:model_decision`, assert `:model` is non-nil before storing.
52
+
53
+ **Test:** Assert that `decisions.add(:model_decision, model: nil)` raises `ArgumentError` with a meaningful message.
54
+
55
+ ---
56
+
57
+ ### 1.4 — HistoryManager: Raise Instead of exit(1) ✓
58
+ **Source:** C7 / P0-5 | **File:** `history_manager.rb:32,39,45` | **Size:** S
59
+
60
+ Any error during variable collection terminates the process via `exit(1)`. No way to handle gracefully in tests or non-CLI contexts.
61
+
62
+ **Fix:** Replace all `exit(1)` calls with `raise` (appropriate exception class). Let the CLI entry point in `bin/aia` catch and call `exit(1)`.
63
+
64
+ **Test:** Simulate error conditions in `HistoryManager` and assert they raise exceptions rather than calling `exit`.
65
+
66
+ ---
67
+
68
+ ### 1.5 — TFIDF Filter: Fix Undefined `logger` ✓
69
+ **Source:** C8 / P0-6 | **File:** `tool_filter/tfidf.rb:58` | **Size:** S
70
+
71
+ `logger.warn(...)` will raise `NoMethodError` on the error path. Currently masked because the error path is never hit in tests.
72
+
73
+ **Fix:** Replace `logger.warn(...)` with `warn(...)` (Kernel#warn), or include the `LoggerManager` mixin, whichever is consistent with other tool filter classes.
74
+
75
+ **Test:** Trigger the error path in `ToolFilter::TFIDF` and assert the warning is emitted without raising `NoMethodError`.
76
+
77
+ ---
78
+
79
+ ## Section 2 — Correctness Completions (P0 + P1 Small Fixes) ✓ COMPLETED
80
+
81
+ **Release tag:** `v2.0.2.alpha` — tagged 2026-03-27
82
+ **Depends on:** Section 1 (requires `AIA.reset!` for test isolation)
83
+
84
+ ### 2.1 — DecisionApplier: Surface Failed Temp Robot Build ✓
85
+ **Source:** C4 / P0-4 | **File:** `decision_applier.rb:100-116` | **Size:** S
86
+
87
+ If `build_temp_robot` returns nil, the turn proceeds silently with the original robot. `context.model_overridden` is never set.
88
+
89
+ **Fix:** Log a warning when `build_temp_robot` returns nil. Set `context.model_overridden = false` explicitly. Consider raising if this indicates a KBS misconfiguration.
90
+
91
+ **Test:** Stub `build_temp_robot` to return nil and assert the warning is emitted and the original robot is used.
92
+
93
+ ---
94
+
95
+ ### 2.2 — FactAsserter: Add Null Guards ✓
96
+ **Source:** P1-10 | **File:** `fact_asserter.rb:17-32` | **Size:** S
97
+
98
+ `assert_model_facts` calls `config.models.each` without nil check. `assert_session_facts` accesses `AIA.session_tracker` with no null guard.
99
+
100
+ **Fix:** Guard all `AIA.*` accesses with nil checks. Return early (no-op) if the resource is not yet initialized.
101
+
102
+ **Test:** Call fact asserter methods when `AIA.session_tracker` is nil and assert no exception is raised.
103
+
104
+ ---
105
+
106
+ ### 2.3 — RuleRouter: Validate Pipeline Completeness ✓
107
+ **Source:** C5 / P1-11 | **File:** `rule_router.rb:84-105` | **Size:** S
108
+
109
+ Missing KBs are skipped with `next unless kb` — no warning. If `:classify` is absent, downstream KBs receive zero facts and all rules silently fail.
110
+
111
+ **Fix:** Before pipeline evaluation, check that each expected KB exists. Emit a structured warning (`warn "[RuleRouter] KB :#{name} not found — downstream rules may fail"`) for any missing KB.
112
+
113
+ **Test:** Remove a KB from the pipeline and assert the warning is emitted.
114
+
115
+ ---
116
+
117
+ ### 2.4 — TaskCoordinator: Remove bridge.send(:db) ✓
118
+ **Source:** P1-14 | **File:** `task_coordinator.rb:14` | **Size:** S
119
+
120
+ `@db = bridge.send(:db)` bypasses private access. If `TrakFlowBridge` renames `db`, `TaskCoordinator` silently fails with `NoMethodError`.
121
+
122
+ **Fix:** Add a public `db` accessor (or a `with_db` yield method) to `TrakFlowBridge`. Remove `send(:db)` from `TaskCoordinator`.
123
+
124
+ **Test:** Verify `TaskCoordinator` accesses the database without using `send`.
125
+
126
+ ---
127
+
128
+ ### 2.5 — Extract CostCalculator Service ✓
129
+ **Source:** P1-15 | **Files:** `session_tracker.rb`, `ui_presenter.rb`, `prompt_handler.rb` | **Size:** S
130
+
131
+ Cost calculation (fetch price from RubyLLM, multiply tokens, divide by 1,000,000) is duplicated in three places.
132
+
133
+ **Fix:** Create `lib/aia/cost_calculator.rb` with a single `CostCalculator.calculate(model:, input_tokens:, output_tokens:)` method. Replace all three inline implementations.
134
+
135
+ **Test:** Unit test `CostCalculator` directly. Verify the three call sites use it.
136
+
137
+ ---
138
+
139
+ ## Section 3 — Handler Cleanup (P1 + P3) ✓ COMPLETED
140
+
141
+ **Release tag:** `v2.0.3.alpha` — tagged 2026-03-27
142
+ **Depends on:** Section 2 (uses `CostCalculator` from 2.5)
143
+
144
+ ### 3.1 — Consolidate extract_reply into ContentExtractor ✓
145
+ **Source:** P1-9 | **Files:** `spawn_handler.rb`, `debate_handler.rb`, `delegate_handler.rb` | **Size:** S
146
+
147
+ Three identical `extract_reply` implementations. All three include `ContentExtractor` but don't use it.
148
+
149
+ **Fix:** Implement `ContentExtractor#extract_content` as the canonical method. Delete the three local copies. Each handler calls `extract_content(response)`.
150
+
151
+ **Test:** Verify each handler's reply extraction uses `ContentExtractor#extract_content`.
152
+
153
+ ---
154
+
155
+ ### 3.2 — Define HandlerProtocol: Unify 5 Handler Signatures ✓
156
+ **Source:** P1-8 | **Files:** `spawn_handler.rb`, `debate_handler.rb`, `delegate_handler.rb`, `mention_router.rb`, `model_switch_handler.rb` | **Size:** M
157
+
158
+ Five handlers, five incompatible signatures — no generic dispatch is possible.
159
+
160
+ **Fix:** Define `module HandlerProtocol` requiring a `handle(context)` method where `context` is a value object carrying `robot`, `prompt`, `decisions`, and `config`. Migrate all five handlers to this signature. Update all call sites.
161
+
162
+ **Test:** Verify all five handlers respond to `handle(context)` with the same interface. Test dispatch via a generic caller.
163
+
164
+ ---
165
+
166
+ ### 3.3 — Remove FZF Dead Code (tempfile_path) ✓
167
+ **Source:** P3-29 | **File:** `fzf.rb:63-73` | **Size:** S
168
+
169
+ `Fzf#tempfile_path` creates a tempfile that is never used.
170
+
171
+ **Fix:** Delete the method.
172
+
173
+ **Test:** Verify `Fzf` no longer defines `tempfile_path`.
174
+
175
+ ---
176
+
177
+ ### 3.4 — Move Cost Calculation out of UIPresenter ✓
178
+ **Source:** P3-34 | **File:** `ui_presenter.rb:284-310` | **Size:** S
179
+
180
+ Cost calculation lives in the display layer (depends on Section 2.5 `CostCalculator`).
181
+
182
+ **Fix:** Replace the inline cost logic in `UIPresenter` with a call to `CostCalculator.calculate(...)`.
183
+
184
+ **Test:** Verify `UIPresenter` does not contain cost calculation logic.
185
+
186
+ ---
187
+
188
+ ## Section 4 — Tool Infrastructure (P1 + P3) ✓ COMPLETED
189
+
190
+ **Release tag:** `v2.0.4.alpha` — tagged 2026-03-27
191
+ **Depends on:** Section 1 (thread safety groundwork)
192
+
193
+ ### 4.1 — Convert ToolLoader to Instantiable Class ✓
194
+ **Source:** P1-12 | **File:** `lib/aia/tool_loader.rb` | **Size:** M
195
+
196
+ `module_function` with `@tool_cache` ivar is not thread-safe and leaks between tests. The module acts as a singleton with shared state.
197
+
198
+ **Fix:** Convert to a class with proper instance state. Inject dependencies (`config`) at construction time. Replace the `@tool_cache` module ivar with an instance ivar. Update all callers.
199
+
200
+ **Test:** Verify two `ToolLoader` instances have independent caches. Verify `clear_cache!` only affects the instance.
201
+
202
+ ---
203
+
204
+ ### 4.2 — Extract ToolFilterRegistry ✓
205
+ **Source:** P1-7 | **File:** `session.rb:26-392` | **Size:** M
206
+
207
+ Five identical `if/elsif` branches for tool filter initialization in `Session`.
208
+
209
+ ```ruby
210
+ if AIA.config.flags.tool_filter_a
211
+ kbs_filter = ToolFilter::KBS.new(...); kbs_filter.prep; @filters[:kbs] = kbs_filter
212
+ elsif ...
213
+ # repeated 4 more times
214
+ ```
215
+
216
+ **Fix:** Create `ToolFilterRegistry.build_from_config(config, tools)` that returns a populated `@filters` hash. Session calls this one method.
217
+
218
+ **Test:** Unit test `ToolFilterRegistry` with each config flag. Verify correct filter type is returned.
219
+
220
+ ---
221
+
222
+ ### 4.3 — Fix SQLiteVec Rowid Mapping ✓
223
+ **Source:** P3-31 | **File:** `tool_filter/sqlite_vec.rb` | **Size:** S
224
+
225
+ Use explicit `tool_id` column instead of implicit rowid for SQLite-vec row mapping.
226
+
227
+ **Fix:** Add explicit `tool_id INTEGER` column to the virtual table schema. Update insert and query logic.
228
+
229
+ **Test:** Verify tool lookup by ID is stable after deletions/reinsertions.
230
+
231
+ ---
232
+
233
+ ### 4.4 — Extract Embedding Model Loader Mixin ✓
234
+ **Source:** P3-25 | **Files:** `tool_filter/zvec.rb:172-174`, `tool_filter/sqlite_vec.rb:160-162` | **Size:** S
235
+
236
+ Identical embedding model loading code in two files.
237
+
238
+ **Fix:** Create `module EmbeddingModelLoader` with `load_embedding_model` method. Include in both `ToolFilter::Zvec` and `ToolFilter::SqliteVec`.
239
+
240
+ **Test:** Verify both classes use the shared mixin.
241
+
242
+ ---
243
+
244
+ ### 4.5 — Cache TFIDF Vectorizer in do_prep ✓
245
+ **Source:** P3-24 | **File:** `tool_filter/tfidf.rb:37-56` | **Size:** S
246
+
247
+ `Classifier::TFIDF.new`, `fit`, and `transform` are called on every user turn.
248
+
249
+ **Fix:** Move vectorizer construction and fitting into `do_prep`. Cache the fitted vectorizer as an instance variable. Per-turn, only call `transform` on the query.
250
+
251
+ **Test:** Verify `do_prep` is called once and `Classifier::TFIDF.new` is not called during per-turn filtering.
252
+
253
+ ---
254
+
255
+ ## Section 5 — State Machine & Lifecycle (P1 + P2 + P3) ✓ COMPLETED
256
+
257
+ **Release tag:** `v2.0.5.alpha` — tagged 2026-03-27
258
+ **Depends on:** Section 3 (HandlerProtocol from 3.2)
259
+
260
+ ### 5.1 — TurnState: Define Valid State Combinations ✓
261
+ **Source:** P1-13 | **File:** `turn_state.rb` | **Size:** M
262
+
263
+ Eight `force_*` flags with no state machine. Multiple flags can be true simultaneously. Flag clearing is distributed across `ChatLoop`, `SpecialModeHandler`, and individual directives.
264
+
265
+ **Fix:** Define a state machine or command queue. Directives enqueue a command object; `SpecialModeHandler` dequeues and executes. Enforce mutual exclusion at enqueue time (not after the fact). Centralize flag clearing.
266
+
267
+ **Test:** Attempt to set two conflicting `force_*` flags and assert only the later one is active. Verify flags are cleared after `SpecialModeHandler` executes.
268
+
269
+ ---
270
+
271
+ ### 5.2 — Manage Spawned Robot Lifecycle ✓
272
+ **Source:** P2-22 | **File:** `spawn_handler.rb:18,39` | **Size:** M
273
+
274
+ `@spawned = {}` caches specialist robots indefinitely. No cleanup on session end. No resource limits. Reuses cached specialist with accumulated history.
275
+
276
+ **Fix:** Add a max cache size (configurable, default 5). Add `cleanup!` method called on session end. Optionally clear conversation history when reusing a cached specialist.
277
+
278
+ **Test:** Verify spawned robots are evicted when cache exceeds max size. Verify `cleanup!` releases all cached robots.
279
+
280
+ ---
281
+
282
+ ### 5.3 — Improve Debate Convergence ✓
283
+ **Source:** P2-21 | **File:** `debate_handler.rb:96-98` | **Size:** M
284
+
285
+ Convergence check is a string match for `"CONVERGED"` — one robot mentioning the word ends the debate prematurely.
286
+
287
+ **Fix:** Replace keyword check with semantic similarity scoring (using `SimilarityScorer`) between consecutive rounds. Require a minimum round count before convergence is allowed.
288
+
289
+ **Test:** Verify a round containing "CONVERGED" incidentally does not end the debate before the minimum round count. Verify high similarity scores trigger convergence.
290
+
291
+ ---
292
+
293
+ ### 5.4 — MentionRouter: Strip Mentions from Prompt Before Sending ✓
294
+ **Source:** P3-26 | **File:** `mention_router.rb` | **Size:** S
295
+
296
+ Mentions (`@robot_name`) are parsed but not stripped from the prompt sent to the target robot.
297
+
298
+ **Fix:** After extracting the mention target, strip the `@robot_name` prefix from the prompt text before routing.
299
+
300
+ **Test:** Assert the prompt delivered to the target robot does not contain the `@mention` prefix.
301
+
302
+ ---
303
+
304
+ ### 5.5 — Cache model_exists? Lookups in ModelSwitchHandler ✓
305
+ **Source:** P3-27 | **File:** `model_switch_handler.rb` | **Size:** S
306
+
307
+ Repeated `model_exists?` calls with no caching.
308
+
309
+ **Fix:** Memoize results in a class-level or instance-level hash keyed by model name. Invalidate on config change if needed.
310
+
311
+ **Test:** Verify `model_exists?` for the same model name hits the provider once and uses cache on subsequent calls.
312
+
313
+ ---
314
+
315
+ ## Section 6 — Structural Decompositions (P2) ✓ COMPLETED
316
+
317
+ **Release tag:** `v2.0.6.alpha` — tagged 2026-03-28
318
+ **Depends on:** Sections 4 (ToolFilterRegistry), 3 (HandlerProtocol)
319
+
320
+ These are large refactors. Each produces a working, tested replacement before the original is removed.
321
+
322
+ ### 6.1 — Split ConfigValidator into Composable Step Objects ✓
323
+ **Source:** P2-18 | **File:** `config/validator.rb` | **Size:** M
324
+
325
+ `tailor()` runs 14 sequential steps; some perform I/O, some cause early exit. `EarlyExit` exception used as `goto`.
326
+
327
+ **Fix:** Each step becomes a callable object (`ConfigStep`) with a `call(config) => Result` signature. `Result` is `:continue | :early_exit | raise`. `tailor()` iterates steps and checks result. Remove `EarlyExit` exception class.
328
+
329
+ **Test:** Unit test each step in isolation. Verify early-exit steps don't run subsequent steps. Verify I/O steps can be stubbed independently.
330
+
331
+ ---
332
+
333
+ ### 6.2 — Integrate or Remove ExpertRouter ✓
334
+ **Source:** P2-19 | **File:** `expert_router.rb` | **Size:** M
335
+
336
+ `ExpertRouter` compiles but is never instantiated or called. Also duplicates `DecisionApplier#build_temp_robot` logic.
337
+
338
+ **Fix:** Decision: integrate into `DecisionApplier` (preferred) or remove entirely. If integrating, use the `HandlerProtocol` context object from Section 3.2.
339
+
340
+ **Test:** If integrated: test the expert routing path end-to-end. If removed: verify no production code references `ExpertRouter`.
341
+
342
+ ---
343
+
344
+ ### 6.3 — Decouple DelegateHandler ✓
345
+ **Source:** P2-23 | **File:** `delegate_handler.rb` | **Size:** M
346
+
347
+ `DelegateHandler` mixes task decomposition, task execution, and TrakFlow coordination into one class.
348
+
349
+ **Fix:** Extract `TaskDecomposer` (breaks prompt into sub-tasks) and `TaskExecutor` (runs a sub-task against a specialist robot). `DelegateHandler` becomes a thin coordinator calling both.
350
+
351
+ **Test:** Unit test `TaskDecomposer` with various prompts. Unit test `TaskExecutor` with a stubbed robot. Test `DelegateHandler` as an integration.
352
+
353
+ ---
354
+
355
+ ### 6.4 — Split Session into PipelineOrchestrator + StartupCoordinator ✓
356
+ **Source:** P2-16 | **File:** `session.rb:26-392` | **Size:** L
357
+
358
+ `Session` has 10+ responsibilities including startup, MCP connection, tool loading, filter initialization, KBS evaluation, and turn orchestration.
359
+
360
+ **Fix:**
361
+ - `StartupCoordinator` — MCP connection, tool loading, filter initialization, KBS startup evaluation
362
+ - `PipelineOrchestrator` — per-turn pipeline: fact assertion, rule evaluation, decision application, robot dispatch
363
+ - `Session` becomes a thin shell that sequences `StartupCoordinator` then hands off to `PipelineOrchestrator` per turn
364
+
365
+ **Test:** Unit test `StartupCoordinator` with stubbed MCP/tool dependencies. Unit test `PipelineOrchestrator` with a stubbed robot and known facts. Integration test via `Session`.
366
+
367
+ ---
368
+
369
+ ### 6.5 — Split RobotFactory into Focused Builders ✓
370
+ **Source:** P2-17 | **File:** `robot_factory.rb` | **Size:** L
371
+
372
+ `RobotFactory` has 13 responsibilities: building robots, networks, concurrent networks, normalizing MCP config, managing network memory, setting up message bus, loading tools, assembling system prompts, configuring RobotLab globally, configuring local providers, resolving provider slugs, transferring history, generating run config.
373
+
374
+ **Fix:** Extract:
375
+ - `RobotBuilder` — single robot construction with system prompt and tools
376
+ - `NetworkAssembler` — wraps multiple robots into RobotLab networks (parallel, consensus, pipeline, concurrent-MCP)
377
+ - `MCPConfigNormalizer` — normalizes symbol/string key MCP server configs into uniform structs
378
+ - `NetworkMemoryManager` — attaches and manages memory backends for networks
379
+
380
+ `RobotFactory` becomes an orchestrator calling these in sequence.
381
+
382
+ **Test:** Unit test each builder in isolation. Verify `RobotFactory` integration test still passes.
383
+
384
+ ---
385
+
386
+ ## Section 7 — MCPDiscovery Decision (P2) ✓ COMPLETED
387
+
388
+ **Release tag:** `v2.0.7.alpha` — tagged 2026-03-28
389
+ **Depends on:** Section 6 (Session refactor from 6.4)
390
+
391
+ ### 7.1 — Wire MCPDiscovery or Remove It ✓
392
+ **Source:** P2-20 | **Files:** `mcp_discovery.rb`, `mcp_grouper.rb` | **Size:** M
393
+
394
+ `MCPDiscovery` and `MCPGrouper` are fully implemented but never invoked. The KBS rule-based server selection path (`mcp_activations`) is never triggered because no caller populates it.
395
+
396
+ **Decision: Option A — Wire.**
397
+
398
+ **Fix (Option A — Wire):** After KBS evaluation in `StartupCoordinator` (Section 6.4), call `MCPDiscovery.servers_for(decisions)` and pass results to `MCPConnectionManager`. Ensure `MCPDiscovery` sees both RubyLLM::MCP and RobotLab::MCP servers.
399
+
400
+ **Fix (Option B — Remove):** Delete `mcp_discovery.rb` and `mcp_grouper.rb`. Update the dead code list in `architecture_review.md`.
401
+
402
+ **Decision criteria:** If the KBS-driven MCP activation feature is on the near-term roadmap, choose Option A. Otherwise, choose Option B and avoid carrying dead weight.
403
+
404
+ **Test (if Option A):** Verify KBS decisions that activate MCP servers result in those servers being connected. Verify servers not in decisions are skipped.
405
+
406
+ ---
407
+
408
+ ## Section 8 — Backlog Cleanup (P3) ✓ COMPLETED
409
+
410
+ **Release tag:** `v2.0.8.alpha` — tagged 2026-03-28
411
+ **Depends on:** Sections 4–7
412
+
413
+ These are housekeeping items. None are blocking; all improve long-term maintainability.
414
+
415
+ ### 8.1 — Consolidate server_name Inline Patterns ✓
416
+ **Source:** P3-28 | **Files:** 5+ call sites | **Size:** S
417
+
418
+ `server[:name] || server['name']` appears inline in 5+ places. `Utility.server_name()` exists but is inconsistently used.
419
+
420
+ **Fix:** Replace all inline `server[:name] || server['name']` usages with `Utility.server_name(server)` (or `AIA::Utility.server_name(server)` where the module isn't in scope). Updated `fact_asserter.rb`, `config/validator.rb` (×2), and `mcp_discovery.rb` (×2).
421
+
422
+ ---
423
+
424
+ ### 8.2 — Split Utility into Domain-Specific Modules ✓
425
+ **Source:** P3-30 | **File:** `utility.rb` | **Size:** M
426
+
427
+ `Utility` was a grab-bag class with MCP, tool, and display methods mixed together.
428
+
429
+ **Fix:** Extracted `MCPUtility` module (`lib/aia/mcp_utility.rb`) for MCP server query methods, and `ToolUtility` module (`lib/aia/tool_utility.rb`) for tool query methods. Both are included into `Utility` via `class << self include`. `utility.rb` retains only display/banner and model-refresh methods.
430
+
431
+ ---
432
+
433
+ ### 8.3 — Pin robot_lab and kbs Version Constraints ✓
434
+ **Source:** P3-32 | **File:** `aia.gemspec` | **Size:** S
435
+
436
+ `robot_lab` and `kbs` used redundant double-constraint notation (`'~> 0.0', '>= 0.0.9'`).
437
+
438
+ **Fix:** Simplified to single pessimistic constraints: `robot_lab '~> 0.0.9'` and `kbs '~> 0.2.1'`.
439
+
440
+ ---
441
+
442
+ ### 8.4 — Rename HistoryManager → VariableInputCollector ✓
443
+ **Source:** P3-33 | **File:** `history_manager.rb` | **Size:** S
444
+
445
+ `HistoryManager` was named for a feature it doesn't implement. It only does input prompting for prompt variables.
446
+
447
+ **Fix:** Created `lib/aia/variable_input_collector.rb` with `VariableInputCollector` class and `HistoryManager = VariableInputCollector` alias. `history_manager.rb` is now a shim that requires the new file. Updated `input_collector.rb` and `lib/aia.rb` to use `VariableInputCollector`. New test file `test/aia/variable_input_collector_test.rb` covers all cases including the alias.
448
+
449
+ ---
450
+
451
+ ---
452
+
453
+ ## Section 9 — Design Correctness ✓ COMPLETED
454
+
455
+ **Release tag:** `v2.0.9.alpha` — tagged 2026-03-28
456
+ **Depends on:** Sections 6–8
457
+
458
+ Post-review corrections identified after all planned sections were complete.
459
+
460
+ ### 9.1 — Eliminate `MCPConfigNormalizer.filter_servers` ✓
461
+ Removed `filter_servers` entirely. It duplicated use/skip/KBS filtering that `MCPDiscovery` already owns, and read from a different activation source (`TurnState` vs. `Decisions`), risking split-brain. `RobotBuilder` and `RobotFactory.mcp_server_configs` now normalize all configured servers without filtering — MCPDiscovery is the single authority.
462
+
463
+ ### 9.2 — `Utility` class → module ✓
464
+ `class Utility` with only `class << self` methods is a Ruby anti-pattern. Changed to `module Utility` — identical for callers, idiomatically correct.
465
+
466
+ ### 9.3 — Banner methods returned to `Utility` ✓
467
+ `banner_mcp`, `mcp_client_labels`, and `banner_tools` were display concerns mixed into data-query modules (`MCPUtility`, `ToolUtility`). Moved back into `Utility`. The domain modules are now pure query interfaces.
468
+
469
+ ### 9.4 — `MCPDiscovery` takes `decisions` directly ✓
470
+ `MCPDiscovery` only used `rule_router.decisions` — passing the whole router was over-coupling. Constructor changed to `MCPDiscovery.new(decisions)`; all three callers updated. Dependency is now explicit and narrow.
471
+
472
+ ### 9.5 — Delete `history_manager.rb` shim ✓
473
+ The backward-compat shim was removed. `session.rb` requires `variable_input_collector` directly. The `HistoryManager = VariableInputCollector` Ruby constant alias remains.
474
+
475
+ ---
476
+
477
+ ## Summary Table
478
+
479
+ | Section | Focus | Items | Size | Release |
480
+ |---------|-------|-------|------|---------|
481
+ | 1 | P0 Critical Safety | 5 | S×5 | v2.0.1.alpha |
482
+ | 2 | Correctness Completions | 5 | S×5 | v2.0.2.alpha |
483
+ | 3 | Handler Cleanup | 4 | S×3, M×1 | v2.0.3.alpha |
484
+ | 4 | Tool Infrastructure | 5 | S×4, M×1 | v2.0.4.alpha |
485
+ | 5 | State Machine & Lifecycle | 5 | S×3, M×2 | v2.0.5.alpha |
486
+ | 6 | Structural Decompositions | 5 | S×1, M×2, L×2 | v2.0.6.alpha |
487
+ | 7 | MCPDiscovery Decision | 1 | M×1 | v2.0.7.alpha |
488
+ | 8 | Backlog Cleanup | 4 | S×3, M×1 | v2.0.8.alpha |
489
+ | 9 | Design Correctness | 5 | S×4, M×1 | v2.0.9.alpha |
490
+ | **Total** | | **39** | | |
491
+
492
+ ### Key Dependencies
493
+
494
+ ```
495
+ Section 1 → Section 2 (AIA.reset! enables test isolation for all subsequent work)
496
+ Section 2 → Section 3 (CostCalculator before UIPresenter cleanup)
497
+ Section 3 → Section 5 (HandlerProtocol before TurnState state machine)
498
+ Section 4 → Section 6 (ToolFilterRegistry before Session split)
499
+ Section 3+4 → Section 6 (HandlerProtocol + ToolFilterRegistry before Session/RobotFactory split)
500
+ Section 6 → Section 7 (Session refactor before MCPDiscovery wiring)
501
+ Sections 4–7 → Section 8 (cleanup after structural work is stable)
502
+ ```
503
+
504
+ ---
505
+
506
+ *Plan generated from: `.architecture/reviews/comprehensive-architecture-review-2026-03-27.md`*