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
data/Rakefile CHANGED
@@ -24,24 +24,137 @@ require "minitest/test_task"
24
24
  Minitest::TestTask.create(:test) do |t|
25
25
  t.libs << "test"
26
26
  t.libs << "lib"
27
- t.warning = false
27
+ t.warning = false
28
28
  # Load SimpleCov before minitest/autorun so at_exit ordering is correct
29
29
  t.test_prelude = 'ENV["TEST_SUITE"]="unit"; require "simplecov_helper"'
30
30
  # Include all unit tests under test/, excluding integration tests
31
31
  # Dir.glob does not support ! negation, so compute the file list manually
32
- t.test_globs = Dir["test/**/*_test.rb"].reject { |f| f.start_with?("test/integration/") }
32
+ t.test_globs = Dir["test/**/*_test.rb"].reject { |f| f.start_with?("test/integration/") }
33
33
  end
34
34
 
35
35
  Minitest::TestTask.create(:integration) do |t|
36
36
  t.libs << "test"
37
37
  t.libs << "lib"
38
- t.warning = false
38
+ t.warning = false
39
39
  # Load SimpleCov before minitest/autorun so at_exit ordering is correct
40
40
  t.test_prelude = 'ENV["TEST_SUITE"]="integration"; require "simplecov_helper"'
41
- t.test_globs = ["test/integration/**/*_test.rb"]
41
+ t.test_globs = ["test/integration/**/*_test.rb"]
42
42
  end
43
43
 
44
44
  desc "Run all tests including integration tests"
45
- task all_tests: [:test, :integration]
45
+ task all_tests: %i[test integration]
46
+
47
+ # Quality gates use a committed BASELINE so accumulated debt doesn't block every
48
+ # change: they fail only on NEW or WORSENED items, not on pre-existing ones.
49
+ # Regenerate after intentionally accepting (or clearing) debt with the
50
+ # corresponding *_baseline task. Ratchet down by re-baselining once you fix items.
51
+ QUALITY_DIR = '.quality'
52
+ FLOG_BASELINE = File.join(QUALITY_DIR, 'flog_baseline.txt')
53
+ FLAY_BASELINE = File.join(QUALITY_DIR, 'flay_baseline.txt')
54
+ FLOG_FAIL = 50.0
55
+ FLOG_WARN = 20.0
56
+ FLOG_EPSILON = 0.5 # tolerate float jitter when comparing to the baseline
57
+
58
+ # @return [Hash{String=>Float}] method => score for methods over the fail line
59
+ def flog_failures
60
+ require 'flog'
61
+ flogger = Flog.new(all: true)
62
+ flogger.flog(*Dir.glob('lib/**/*.rb'))
63
+ failures = {}
64
+ flogger.each_by_score do |method, score|
65
+ next if method.end_with?('#none')
66
+
67
+ failures[method] = score if score > FLOG_FAIL
68
+ end
69
+ failures
70
+ end
71
+
72
+ def load_flog_baseline
73
+ return {} unless File.exist?(FLOG_BASELINE)
74
+
75
+ File.readlines(FLOG_BASELINE).each_with_object({}) do |line, acc|
76
+ score, method = line.strip.split("\t", 2)
77
+ acc[method] = score.to_f if method && !method.empty?
78
+ end
79
+ end
80
+
81
+ # @return [Flay] processed + analyzed flay instance
82
+ def flay_patterns
83
+ require 'flay'
84
+ flay = Flay.new(mass: 50, diff: false, verbose: false, summary: false, timeout: 60)
85
+ flay.process(*Dir.glob('lib/**/*.rb'))
86
+ flay.analyze
87
+ flay
88
+ end
89
+
90
+ def load_flay_baseline
91
+ return [] unless File.exist?(FLAY_BASELINE)
92
+
93
+ File.readlines(FLAY_BASELINE).map(&:strip).reject(&:empty?)
94
+ end
95
+
96
+ desc "Regenerate the flog baseline (grandfathers current methods >= #{FLOG_FAIL})"
97
+ task :flog_baseline do
98
+ require 'fileutils'
99
+ FileUtils.mkdir_p(QUALITY_DIR)
100
+ failures = flog_failures
101
+ body = failures.sort_by { |_, s| -s }.map { |m, s| format("%.1f\t%s", s, m) }.join("\n")
102
+ File.write(FLOG_BASELINE, body.empty? ? '' : "#{body}\n")
103
+ puts "Wrote #{failures.size} grandfathered method(s) to #{FLOG_BASELINE}"
104
+ end
105
+
106
+ desc "Check complexity with Flog (fails only on NEW or worsened methods >= #{FLOG_FAIL})"
107
+ task :flog_check do
108
+ current = flog_failures
109
+ baseline = load_flog_baseline
110
+
111
+ new_items = current.reject { |m, _| baseline.key?(m) }
112
+ worsened = current.select { |m, s| baseline.key?(m) && s > baseline[m] + FLOG_EPSILON }
113
+ fixed = baseline.keys - current.keys
114
+
115
+ puts "\nFlog: #{current.size} method(s) >= #{FLOG_FAIL} (#{baseline.size} grandfathered)."
116
+ puts " #{fixed.size} now under threshold — run `rake flog_baseline` to prune." unless fixed.empty?
117
+
118
+ problems = new_items.map { |m, s| format("NEW %.1f: %s", s, m) } +
119
+ worsened.map { |m, s| format("WORSENED %.1f (baseline %.1f): %s", s, baseline[m], m) }
120
+
121
+ if problems.empty?
122
+ puts "Flog quality gate passed (no new or worsened methods)."
123
+ else
124
+ puts "\nFlog quality gate failed:"
125
+ problems.each { |p| puts " #{p}" }
126
+ abort "\nFlog: #{problems.size} new/worsened method(s). Refactor them, or `rake flog_baseline` if intentional."
127
+ end
128
+ end
129
+
130
+ desc "Regenerate the flay baseline (grandfathers current duplication patterns)"
131
+ task :flay_baseline do
132
+ require 'fileutils'
133
+ FileUtils.mkdir_p(QUALITY_DIR)
134
+ hashes = flay_patterns.hashes.keys.map(&:to_s).sort
135
+ File.write(FLAY_BASELINE, hashes.empty? ? '' : "#{hashes.join("\n")}\n")
136
+ puts "Wrote #{hashes.size} grandfathered duplication pattern(s) to #{FLAY_BASELINE}"
137
+ end
138
+
139
+ desc "Check duplication with Flay (fails only on NEW patterns, mass >= 50)"
140
+ task :flay_check do
141
+ flay = flay_patterns
142
+ baseline = load_flay_baseline
143
+ current = flay.hashes.keys.map(&:to_s)
144
+
145
+ new_patterns = current - baseline
146
+ fixed = baseline - current
147
+
148
+ puts "\nFlay: #{current.size} duplication pattern(s) (#{baseline.size} grandfathered)."
149
+ puts " #{fixed.size} baseline pattern(s) gone — run `rake flay_baseline` to prune." unless fixed.empty?
150
+
151
+ if new_patterns.empty?
152
+ puts "Flay quality gate passed (no new duplication)."
153
+ else
154
+ puts "\nFlay quality gate failed: #{new_patterns.size} NEW pattern(s) (see report):"
155
+ flay.report
156
+ abort "\nFlay: new duplication introduced. Remove it, or `rake flay_baseline` if intentional."
157
+ end
158
+ end
46
159
 
47
160
  task default: :test
@@ -0,0 +1,314 @@
1
+ # Comprehensive Architecture Review — AIA v2.0.0
2
+ ## Date: 2026-03-27
3
+ ## Scope: Full codebase (69 Ruby source files, ~10K LOC)
4
+ ## Method: Six parallel specialist reviewers — Core, KBS/Rules, MCP/Tools, Chat/Directives, Handlers/Network, Infrastructure
5
+
6
+ ---
7
+
8
+ ## 1. Overall Assessment
9
+
10
+ AIA v2.0.0 demonstrates thoughtful feature design and strong domain separation at the module level. The `robot_lab` + `kbs` adoption was the right call. The codebase is functionally correct and feature-rich.
11
+
12
+ The structural problems are at the **integration layer**: global state abuse, god-objects, missing handler protocol, silent error swallowing, and no dependency injection. These compound over time and make the codebase increasingly expensive to change.
13
+
14
+ ---
15
+
16
+ ## 2. Structural Strengths
17
+
18
+ - **Stateless extracted modules.** `FactAsserter`, `ToolLoader`, `SystemPromptAssembler`, `KBDefinitions`, `DynamicRuleBuilder` are all stateless with injected dependencies.
19
+ - **Good domain decomposition.** Most concerns are in the right file even if the files themselves are too large.
20
+ - **`RobotNamer`**, **`ModelAliasRegistry`**, **`PromptDecomposer`**, **`SimilarityScorer`** are exemplary — single responsibility, no external coupling, testable.
21
+ - **MCPConnectionManager connection logic** — the `connect_one`/threads/spinners architecture is solid; only reads are unsafe.
22
+ - **LoggerManager test mode** — proper test device injection shows good instincts.
23
+
24
+ ---
25
+
26
+ ## 3. Critical Issues (Fix Before Production)
27
+
28
+ ### C1. Thread Safety — MCPConnectionManager Reads Without Mutex
29
+ **`mcp_connection_manager.rb:72-83`**
30
+
31
+ `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()`. Robots can start with missing MCP tools.
32
+
33
+ **Fix:** Wrap all reads in `@mutex.synchronize` or snapshot state under lock before use.
34
+
35
+ ### C2. No `AIA.reset!` — Tests Leak State
36
+ **`aia.rb:77-88`**
37
+
38
+ 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. Running tests in parallel is unsafe.
39
+
40
+ **Fix:** Add `AIA.reset!` that zeros all accessors. Call in every test teardown.
41
+
42
+ ### C3. Decisions Schema Accepts nil Values
43
+ **`decisions.rb:22-30`**
44
+
45
+ `decisions.add(:model_decision, model: nil)` is accepted silently. Downstream callers do `decisions.model_decisions.first&.dig(:model)` and pass `nil` to `RobotLab.build(model: nil)` — crash with no indication of source.
46
+
47
+ **Fix:** Validate required keys in `add()`. Assert `:model` is non-nil for model decisions.
48
+
49
+ ### C4. DecisionApplier Silent Fall-Through
50
+ **`decision_applier.rb:100-116`**
51
+
52
+ If `build_temp_robot` returns nil, the turn proceeds with the original robot and `context.model_overridden` is never set. User gets no feedback that the KBS recommendation was ignored.
53
+
54
+ ### C5. Silent KBS Pipeline Failure
55
+ **`rule_router.rb:84-105`**
56
+
57
+ Missing KBs are skipped with `next unless kb` — no warning. If `:classify` is absent, `:model_select` receives zero `classification_decision` facts; all rules silently fail.
58
+
59
+ **Fix:** Validate pipeline completeness before evaluation. Warn if upstream KB was skipped.
60
+
61
+ ### C6. Fact Assertion Has No Null Guards
62
+ **`fact_asserter.rb:17-32`**
63
+
64
+ `assert_model_facts` calls `config.models.each` without nil check. `assert_session_facts` accesses `AIA.session_tracker` with no null guard. If gate KB runs before session tracker initializes, all gate rules silently fail.
65
+
66
+ ### C7. HistoryManager Calls `exit(1)` Directly
67
+ **`history_manager.rb:32, 39, 45`**
68
+
69
+ Any error during variable collection terminates the process. No way to handle gracefully in tests or non-CLI contexts.
70
+
71
+ **Fix:** Raise exceptions; let callers decide handling.
72
+
73
+ ### C8. `logger` Undefined in TFIDF Filter
74
+ **`tool_filter/tfidf.rb:58`**
75
+
76
+ `logger.warn(...)` will raise `NoMethodError`. Currently masked because the error path is never hit in tests.
77
+
78
+ ---
79
+
80
+ ## 4. High Priority Issues (Fix in Next Release)
81
+
82
+ ### Session God-Object
83
+ **`session.rb:26-392`** — 10+ responsibilities including five identical tool filter initialization branches:
84
+
85
+ ```ruby
86
+ if AIA.config.flags.tool_filter_a
87
+ kbs_filter = ToolFilter::KBS.new(...); kbs_filter.prep; @filters[:kbs] = kbs_filter
88
+ elsif ...
89
+ # repeated 4 more times identically
90
+ ```
91
+
92
+ **Fix:** `ToolFilterRegistry.build_from_config(config, tools)` — one call, returns `@filters` hash.
93
+
94
+ ### RobotFactory Has 13 Responsibilities
95
+ **`robot_factory.rb`** — builds robots, networks, concurrent networks, normalizes MCP config, manages network memory, sets up message bus, loads tools, assembles system prompts, configures RobotLab globally, configures local providers, resolves provider slugs, transfers history, generates run config.
96
+
97
+ **Fix:** Extract `RobotBuilder`, `NetworkAssembler`, `MCPConfigNormalizer`, `NetworkMemoryManager`.
98
+
99
+ ### ConfigValidator Does 14 Steps + EarlyExit Anti-Pattern
100
+ **`config/validator.rb`** — `tailor()` runs 14 sequential steps; some perform I/O, some cause early exit. `EarlyExit` is an exception used as `goto`.
101
+
102
+ **Fix:** Replace with result object: `tailor()` returns `:continue`, `:early_exit`, or raises real errors.
103
+
104
+ ### No Handler Protocol
105
+ **`spawn_handler.rb`, `debate_handler.rb`, `delegate_handler.rb`, `mention_router.rb`, `model_switch_handler.rb`**
106
+
107
+ Five handlers, five incompatible signatures:
108
+ ```ruby
109
+ SpawnHandler#handle(prompt, specialist_type: nil)
110
+ DebateHandler#handle(prompt)
111
+ DelegateHandler#handle(prompt)
112
+ MentionRouter#handle(robot, prompt) # different parameter order
113
+ ModelSwitchHandler#handle(decisions, config) # completely different
114
+ ```
115
+
116
+ Cannot build a generic dispatch mechanism. Adding a sixth handler requires understanding all five.
117
+
118
+ **Fix:** Define `HandlerProtocol` — a unified `handle(context)` where context carries robot, prompt, decisions, config.
119
+
120
+ ### Content Extraction Duplicated in Three Handlers
121
+ `SpawnHandler#extract_reply`, `DebateHandler#extract_reply`, `DelegateHandler#extract_reply` are identical. All three include `ContentExtractor` but don't use it.
122
+
123
+ **Fix:** Make `ContentExtractor#extract_content` the canonical implementation and remove the local copies.
124
+
125
+ ### TurnState Has No Invariants
126
+ **`turn_state.rb`** — eight attributes (`force_verify`, `force_decompose`, `force_concurrent_mcp`, `force_debate`, `force_delegate`, `force_spawn`, `active_mcp_servers`, `active_tools`) with no state machine. Multiple `force_*` flags can be true simultaneously. Behavior is undefined. Flag clearing is distributed across `ChatLoop`, `SpecialModeHandler`, and individual directives.
127
+
128
+ **Fix:** State machine or command queue. Directives enqueue commands; `SpecialModeHandler` dequeues.
129
+
130
+ ### TaskCoordinator Accesses Bridge's Private Database
131
+ **`task_coordinator.rb:14`**
132
+
133
+ ```ruby
134
+ @db = bridge.send(:db) # Bypasses private access
135
+ ```
136
+
137
+ Direct encapsulation break. If `TrakFlowBridge` renames `db`, `TaskCoordinator` silently fails with `NoMethodError`.
138
+
139
+ ### Cost Calculation Duplicated Three Times
140
+ `SessionTracker`, `UIPresenter`, and `PromptHandler` each implement the same logic: fetch price from RubyLLM, multiply tokens, divide by 1,000,000.
141
+
142
+ **Fix:** Single `CostCalculator` service.
143
+
144
+ ---
145
+
146
+ ## 5. Medium Priority Issues
147
+
148
+ ### ChatLoop `run_loop` Has 13 Conditional Branches
149
+ **`chat_loop.rb:84-188`** — handles input reading, directive dispatch, PM parse errors, KBS evaluation, model switching, quality gate, special modes, expert routing, mention routing, streaming, metrics, output, speech, MCP filter clearing. Untestable as a unit.
150
+
151
+ ### ChatLoop Hardcodes Directive Names
152
+ **`chat_loop.rb:231-240`**
153
+ ```ruby
154
+ if follow_up_prompt.strip.start_with?("/clear", "/checkpoint", "/restore", "/review", "/context")
155
+ ```
156
+ Adding a new context directive requires editing this list. Open/Closed violation.
157
+
158
+ **Fix:** Directive registry with categories. Context directives self-register.
159
+
160
+ ### ExpertRouter is Dead Code
161
+ **`expert_router.rb`** — compiles but is never instantiated or called from any production code path. Also duplicates `DecisionApplier#build_temp_robot` logic.
162
+
163
+ **Fix:** Integrate into `DecisionApplier` or remove.
164
+
165
+ ### MCPDiscovery and MCPGrouper are Unused
166
+ **`mcp_discovery.rb`**, **`mcp_grouper.rb`** — fully implemented, never invoked. The KBS rule-based server selection path (`mcp_activations`) is never triggered because no caller populates it.
167
+
168
+ **Fix:** Wire `MCPDiscovery` into `Session#connect_mcp_servers` after KBS evaluation, or remove.
169
+
170
+ ### TFIDF Rebuilds Vectorizer Per Query
171
+ **`tool_filter/tfidf.rb:37-56`** — calls `Classifier::TFIDF.new`, `fit`, and `transform` on every user turn.
172
+
173
+ **Fix:** Pre-compute in `do_prep`, cache vectors.
174
+
175
+ ### Debate Convergence is a Keyword Match
176
+ **`debate_handler.rb:96-98`**
177
+ ```ruby
178
+ round_results.any? { |r| r[:content].to_s.include?("CONVERGED") }
179
+ ```
180
+ One robot mentioning "CONVERGED" anywhere ends the debate. No consensus check, no semantic similarity, no minimum round count.
181
+
182
+ ### Spawned Robot Lifecycle Unmanaged
183
+ **`spawn_handler.rb:18, 39`** — `@spawned = {}` caches specialist robots indefinitely. No cleanup on session end. No resource limits. Reuses cached specialist with accumulated conversation history.
184
+
185
+ ### PromptHandler Mutates Global Config
186
+ **`prompt_handler.rb:104-122, 173-221`** — `apply_metadata_config` and `apply_root_shorthands` write directly to `AIA.config`. No transaction semantics.
187
+
188
+ ### UIPresenter Contains Business Logic
189
+ **`ui_presenter.rb:284-310`** — cost calculation (`RubyLLM::Models.find`, price × tokens / 1,000,000) lives in the display layer.
190
+
191
+ ---
192
+
193
+ ## 6. Duplication Inventory
194
+
195
+ | Pattern | Locations | Count |
196
+ |---------|-----------|-------|
197
+ | `extract_reply` | SpawnHandler, DebateHandler, DelegateHandler | 3× identical |
198
+ | `collect_mcp_tools` traversal (robot → first network robot → RubyLLM::MCP fallback) | Session, FactAsserter, Utility | 3× identical |
199
+ | `output_to_file` | ChatLoop, Session, SpecialModeHandler | 3× identical |
200
+ | Cost calculation | SessionTracker, UIPresenter, PromptHandler | 3× near-identical |
201
+ | Post-execution block (extract → track → display → output → metrics → speak → separator) | ChatLoop#run_loop, route_to_expert, process_initial_context, SpecialModeHandler (4 handlers) | ~7× near-identical |
202
+ | `build_opts` hash in network builders | build_parallel_network, build_consensus_network, build_pipeline_network, build_concurrent_mcp_network | 4× similar |
203
+ | Embedding model loading | zvec.rb:172-174, sqlite_vec.rb:160-162 | 2× identical |
204
+ | MCP server name extraction (`server[:name] \|\| server['name']`) | 5+ call sites across config, factory, utility | 5× inline |
205
+
206
+ ---
207
+
208
+ ## 7. Dead Code / Unused Features
209
+
210
+ | Item | File | Status |
211
+ |------|------|--------|
212
+ | `ExpertRouter` | `expert_router.rb` | Compiles, never called |
213
+ | `MCPDiscovery` KBS path | `mcp_discovery.rb:23-31` | Code exists, never triggered |
214
+ | `MCPGrouper` | `mcp_grouper.rb` | Implemented, never called |
215
+ | `Fzf#tempfile_path` | `fzf.rb:63-73` | Creates tempfile that is never used |
216
+ | `HistoryManager` history features | `history_manager.rb` | Named "history manager" but only does input prompting |
217
+
218
+ ---
219
+
220
+ ## 8. Cross-Cutting Issues
221
+
222
+ ### No Dependency Injection
223
+ Every class hardwires dependencies via `AIA.*` globals. Unit tests for `Session`, `ChatLoop`, `RuleRouter`, `FactAsserter`, and all handlers are actually integration tests requiring a full AIA stack.
224
+
225
+ ### Silent Error Swallowing is the Default
226
+ `rescue StandardError; warn "Warning: #{e.message}"; return nil` appears in 20+ locations. No backtraces. No structured logging. Cannot distinguish "expected degradation" from "bug."
227
+
228
+ ### Two MCP Systems Not Fully Unified
229
+ `RubyLLM::MCP` (--require path) and `RobotLab::MCP` (config file path) are bridged via `absorb_ruby_llm_mcp_clients`, but `MCPDiscovery` and `MCPGrouper` only see the config-file path. Tools loaded via `--require` are invisible to rule-based server selection.
230
+
231
+ ### Config Precedence Bug
232
+ **`config.rb:357-362`** — documented precedence says CLI > env vars, but `apply_models_env_var()` runs *after* CLI overrides are applied.
233
+
234
+ ### MCP Server Name Extraction Scattered
235
+ `server[:name] || server['name']` appears inline in 5+ places. `Utility.server_name()` exists but is inconsistently used.
236
+
237
+ **Fix:** `MCPServerConfig` value object that normalizes on construction.
238
+
239
+ ---
240
+
241
+ ## 9. Test Coverage Gaps
242
+
243
+ | Component | Estimated Coverage | Risk |
244
+ |-----------|--------------------|------|
245
+ | `ChatLoop#run_loop` | ~39% | High — core REPL, 13 branches |
246
+ | `SpecialModeHandler` | ~17% | High — 6 mode handlers |
247
+ | `MentionRouter` | ~22% | Medium |
248
+ | `StreamingRunner` | ~21% | Medium |
249
+ | `ExpertRouter` | ~0% | N/A (dead code) |
250
+ | KBS network turn recording | Low | Medium |
251
+
252
+ ---
253
+
254
+ ## 10. Priority Recommendations
255
+
256
+ ### P0 — Fix Before Production
257
+
258
+ | # | Item | Location |
259
+ |---|------|----------|
260
+ | 1 | MCPConnectionManager race condition (reads without mutex) | `mcp_connection_manager.rb:72-83` |
261
+ | 2 | Add `AIA.reset!` for test isolation | `aia.rb` |
262
+ | 3 | Decisions schema — reject nil model | `decisions.rb:22-30` |
263
+ | 4 | DecisionApplier — log/surface failed temp robot build | `decision_applier.rb:100-116` |
264
+ | 5 | HistoryManager — raise instead of exit | `history_manager.rb:32,39,45` |
265
+ | 6 | Fix undefined `logger` in TFIDF filter | `tool_filter/tfidf.rb:58` |
266
+
267
+ ### P1 — Next Release
268
+
269
+ | # | Item | Effort |
270
+ |---|------|--------|
271
+ | 7 | Extract `ToolFilterRegistry` — eliminate 5 if-branches in Session | Medium |
272
+ | 8 | Define `HandlerProtocol` — unify 5 handler signatures | Medium |
273
+ | 9 | Consolidate `extract_reply` into `ContentExtractor` | Small |
274
+ | 10 | Add null guards to FactAsserter fact assertion methods | Small |
275
+ | 11 | Add pipeline validation to RuleRouter (warn on missing KB) | Small |
276
+ | 12 | Convert ToolLoader to instantiable class (fix module cache) | Medium |
277
+ | 13 | Fix TurnState — define valid state combinations | Medium |
278
+ | 14 | Fix TaskCoordinator — remove `bridge.send(:db)` | Small |
279
+ | 15 | Extract `CostCalculator` service | Small |
280
+
281
+ ### P2 — Near Term
282
+
283
+ | # | Item | Effort |
284
+ |---|------|--------|
285
+ | 16 | Split Session into `PipelineOrchestrator` + `StartupCoordinator` | Large |
286
+ | 17 | Split RobotFactory into `RobotBuilder` + `NetworkAssembler` + `MCPConfigNormalizer` | Large |
287
+ | 18 | Split ConfigValidator into composable step objects | Medium |
288
+ | 19 | Integrate or remove ExpertRouter | Medium |
289
+ | 20 | Wire MCPDiscovery into session startup, or remove | Medium |
290
+ | 21 | Improve debate convergence (semantic similarity, minimum rounds) | Medium |
291
+ | 22 | Manage spawned robot lifecycle (cleanup, resource limits) | Medium |
292
+ | 23 | Decouple DelegateHandler (extract TaskDecomposer + TaskExecutor) | Medium |
293
+
294
+ ### P3 — Backlog
295
+
296
+ | # | Item |
297
+ |---|------|
298
+ | 24 | Cache TFIDF vectorizer in `do_prep` |
299
+ | 25 | Extract embedding model loader mixin (Zvec + SqliteVec) |
300
+ | 26 | Fix MentionRouter — strip mentions from prompt before sending |
301
+ | 27 | Cache `model_exists?` lookups in ModelSwitchHandler |
302
+ | 28 | Extract `MCPServerConfig` value object (normalize symbol/string keys once) |
303
+ | 29 | Fix FZF dead code (`tempfile_path` creates but never uses temp file) |
304
+ | 30 | Split Utility into domain-specific managers |
305
+ | 31 | Fix SQLiteVec rowid mapping (use explicit tool_id column) |
306
+ | 32 | Pin `robot_lab` and `kbs` version constraints once they reach 0.1.0 |
307
+ | 33 | Rename `HistoryManager` → `VariableInputCollector` |
308
+ | 34 | Move cost calculation out of UIPresenter into CostCalculator |
309
+
310
+ ---
311
+
312
+ ## 11. Full Review
313
+
314
+ See `.architecture/reviews/comprehensive-architecture-review-2026-03-27.md` for the complete six-domain analysis with file-level citations and code examples.
data/bin/aia CHANGED
@@ -1,6 +1,22 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
+ # In development (running directly from the repo), activate all gems via
5
+ # Bundler so dependency version constraints are resolved before any require.
6
+ # When installed as a standalone gem there is no adjacent Gemfile, so this
7
+ # block is skipped and RubyGems handles activation as normal.
8
+ gemfile = File.expand_path('../Gemfile', __dir__)
9
+ if File.exist?(gemfile)
10
+ ENV['BUNDLE_GEMFILE'] ||= gemfile
11
+ require 'bundler/setup'
12
+ end
13
+
14
+ # Ruby 4.x ships bigdecimal 4.x as a default gem. Some transitive deps (e.g. oj)
15
+ # declare `bigdecimal >= 3.0`, which can cause RubyGems to try activating the
16
+ # installed 3.x gem after 4.x is already active. Pin to >= 4.0 first so the
17
+ # activation order is settled before any other gem touches bigdecimal.
18
+ gem 'bigdecimal', '>= 4.0'
19
+
4
20
  require_relative '../lib/aia'
5
21
 
6
22
  # Handle Ctrl-C gracefully
data/docs/AGENTS.md ADDED
@@ -0,0 +1,40 @@
1
+ <!-- Tocer[start]: Auto-generated, don't remove. -->
2
+
3
+ ## Table of Contents
4
+
5
+ - [Local Agent Context: docs](#local-agent-context-docs)
6
+ - [Setup & Commands](#setup--commands)
7
+ - [Code Style & Patterns](#code-style--patterns)
8
+ - [Implementation Details](#implementation-details)
9
+
10
+ <!-- Tocer[finish]: Auto-generated, don't remove. -->
11
+
12
+ ### Local Agent Context: docs
13
+
14
+ ## Setup & Commands
15
+
16
+ - **Build**: Run `bundle exec jekyll build` to generate the static site from Markdown documents and configurations.
17
+ - **Serve**: Execute `bundle exec jekyll serve` to start a local server and view the documentation site at `http://localhost:4000`.
18
+ - **Lint**: Use `mdl ./docs` to perform style and markdown checks on all documents within the `docs` directory.
19
+ - **Link Check**: Ensure all internal links are operational by executing `html-proofer --assume-extension ./_site`.
20
+
21
+ ## Code Style & Patterns
22
+
23
+ - **YAML Front Matter**: All markdown files must use YAML front matter for metadata (`---` delimiters) as evident in `index.md`.
24
+ - **Environment Variables**: Use double underscores (e.g., `AIA_PROMPTS__DIR`) within configuration files as stated in `configuration.md`.
25
+ - **Comment Directives**: Use `#{}` for embedding Ruby in documentation, as seen in the Dynamic Configuration section of `advanced-prompting.md`.
26
+ - **Directive Prefix**: AIA directives use a single `/` prefix (e.g., `/skill`, `/llms`). Never document them with `//`.
27
+
28
+ ## Implementation Details
29
+
30
+ - **Primary Index**: Start with `docs/index.md` for the main entry point of the documentation, containing core sections such as key features and quick start.
31
+ - **Guide Locations**: Individual guides, such as `guides/basic-usage.md` and `guides/image-generation.md`, offer in-depth tutorials and are referenced from `guides/index.md`.
32
+ - **Image Assets**: Store all images in `assets/images/`, referencing them relatively from documents, as used in `index.md` for the `aia.png`.
33
+ - **Examples Configuration**: Examples are categorized within `examples/` subdirectories (`mcp`, `prompts`, `tools`) and provide clear usage scenarios.
34
+ - **Command Line Reference**: `cli-reference.md` documents every CLI flag. `--list-skills` output is a formatted markdown document: H2 heading per skill ID followed by a two-column YAML front matter table.
35
+ - **Directives Reference**: `directives-reference.md` documents all `/`-prefixed chat directives.
36
+ - `/skill <id>`: reads `SKILL.md` from the configured skills directory; errors print to stdout and return nil (not sent to AI).
37
+ - `/skills [terms...]`: prints `skill_id: name\n description` per skill; supports AND search terms and AND NOT terms with `-`/`~`/`!` prefix.
38
+ - `/llms [terms...]`: lists AI models; supports the same AND/AND NOT search term syntax as `/skills`.
39
+ - Skills directory resolves via: `AIA.config.skills.dir` → `$AIA_PROMPTS__DIR/$AIA_PROMPTS__SKILLS_PREFIX` → `~/.prompts/skills`.
40
+ - **Versioning Note**: Document any significant version changes and breaking updates in the `index.md` warnings, following the structure for version change notes.
@@ -1,3 +1,53 @@
1
+ <!-- Tocer[start]: Auto-generated, don't remove. -->
2
+
3
+ ## Table of Contents
4
+
5
+ - [Stage 1: Data Preparation](#stage-1-data-preparation)
6
+ - [Data Analysis Pipeline - Stage 1: Preparation](#data-analysis-pipeline---stage-1-preparation)
7
+ - [Input Data Overview](#input-data-overview)
8
+ - [Data Quality Assessment](#data-quality-assessment)
9
+ - [Determine analysis approach](#determine-analysis-approach)
10
+ - [Layer 1: Project Context](#layer-1-project-context)
11
+ - [Layer 2: Domain Context](#layer-2-domain-context)
12
+ - [Code Review with Clipboard Content](#code-review-with-clipboard-content)
13
+ - [Code to Review](#code-to-review)
14
+ - [Review Guidelines](#review-guidelines)
15
+ - [Multi-format document generator](#multi-format-document-generator)
16
+ - [<%= document_type.capitalize %> Document](#-document_typecapitalize--document)
17
+ - [Source Material](#source-material)
18
+ - [Additional Context](#additional-context)
19
+ - [Clipboard Content (if applicable)](#clipboard-content-if-applicable)
20
+ - [Multi-model analysis system](#multi-model-analysis-system)
21
+ - [Phase 1: Creative Ideation (High Temperature)](#phase-1-creative-ideation-high-temperature)
22
+ - [Phase 3: Synthesis and Recommendation](#phase-3-synthesis-and-recommendation)
23
+ - [Advanced Tool Integration](#advanced-tool-integration)
24
+ - [Custom Tool Workflows](#custom-tool-workflows)
25
+ - [Analyze content to determine best tools](#analyze-content-to-determine-best-tools)
26
+ - [Multi-Format Report Generator](#multi-format-report-generator)
27
+ - [1. Executive Summary (Business Format)](#1-executive-summary-business-format)
28
+ - [2. Technical Detail (Developer Format)](#2-technical-detail-developer-format)
29
+ - [3. Academic Format (Research Paper Style)](#3-academic-format-research-paper-style)
30
+ - [4. Action Items (Project Management Format)](#4-action-items-project-management-format)
31
+ - [Structured data extraction](#structured-data-extraction)
32
+ - [Post-process extracted JSON](#post-process-extracted-json)
33
+ - [Robust prompt with fallbacks](#robust-prompt-with-fallbacks)
34
+ - [Output validation system](#output-validation-system)
35
+ - [This will be used to validate the AI response](#this-will-be-used-to-validate-the-ai-response)
36
+ - [Smart caching system](#smart-caching-system)
37
+ - [Intelligent batch processing](#intelligent-batch-processing)
38
+ - [Switch to faster model for large batches](#switch-to-faster-model-for-large-batches)
39
+ - [enterprise_code_review.md](#enterprise_code_reviewmd)
40
+ - [Enterprise Code Review System](#enterprise-code-review-system)
41
+ - [Multi-phase review process](#multi-phase-review-process)
42
+ - [Intelligent Research Assistant](#intelligent-research-assistant)
43
+ - [Adaptive Research Analysis](#adaptive-research-analysis)
44
+ - [Query: <%= research_query %>](#query--research_query-)
45
+ - [Dynamic source inclusion based on domain](#dynamic-source-inclusion-based-on-domain)
46
+ - [Advanced Execution Modes](#advanced-execution-modes)
47
+ - [Related Documentation](#related-documentation)
48
+
49
+ <!-- Tocer[finish]: Auto-generated, don't remove. -->
50
+
1
51
  # Advanced Prompting Techniques
2
52
 
3
53
  Master sophisticated prompting strategies to get the most out of AIA's capabilities with complex workflows, dynamic content generation, and expert-level AI interactions.
@@ -21,7 +71,7 @@ if File.exist?(config_file)
21
71
 
22
72
  ```markdown
23
73
  <%
24
- model = AIA.config.model
74
+ model = AIA.config.models.first&.name
25
75
  case model
26
76
  when /gpt-4/
27
77
  %>
@@ -343,7 +393,7 @@ Tailor prompts for specific model strengths:
343
393
 
344
394
  ```markdown
345
395
  <%
346
- model = AIA.config.model
396
+ model = AIA.config.models.first&.name
347
397
  case model
348
398
  when /gpt-4/
349
399
  # GPT-4 excels at complex reasoning and code
@@ -563,7 +613,7 @@ Implement smart caching for expensive operations:
563
613
  <%=
564
614
  require 'digest'
565
615
 
566
- cache_key = Digest::MD5.hexdigest('<%= input_data %>' + AIA.config.model)
616
+ cache_key = Digest::MD5.hexdigest('<%= input_data %>' + AIA.config.models.first&.name)
567
617
  cache_file = "/tmp/aia_cache_#{cache_key}.json"
568
618
  cache_duration = 3600 # 1 hour
569
619
 
@@ -737,6 +787,20 @@ end
737
787
  %>
738
788
  ```
739
789
 
790
+ ## Advanced Execution Modes
791
+
792
+ In interactive chat sessions, the following directives invoke specialized multi-robot execution modes:
793
+
794
+ - `/verify` — Generates two independent answers to the same question, then has a third robot reconcile them into a final response.
795
+ - `/decompose` — Breaks a complex prompt into parallel sub-tasks, executes them concurrently, and merges the results.
796
+ - `/debate` — Initiates a multi-round debate between robots with convergence detection; rounds end when agreement is reached or the round limit is hit.
797
+ - `/spawn` — Dynamically spawns a one-shot specialist robot to handle a specific subtask without disrupting the current session. For a persistent, `@mention`-able crew member, use `/add_recruit` instead (see the [Crews guide](guides/crew.md)).
798
+ - `/add_recruit` (`/add`) / `/drop_recruit` (`/drop`) — Add or remove a persistent robot in the session's crew; recruits answer to `@name` and inherit the chief's tools and MCP servers.
799
+ - `/delegate` — Delegates a subtask to a specialist via TrakFlow for asynchronous handling.
800
+ - `/concurrent` — Enables concurrent MCP server access for the immediately following prompt turn.
801
+
802
+ These modes are available at the chat prompt and require no additional configuration beyond having the relevant robots or MCP servers set up.
803
+
740
804
  ## Related Documentation
741
805
 
742
806
  - [Prompt Management](prompt_management.md) - Organizing and managing prompts