Class: Ace::Review::Molecules::FeedbackSynthesizer
- Inherits:
-
Object
- Object
- Ace::Review::Molecules::FeedbackSynthesizer
- Defined in:
- lib/ace/review/molecules/feedback_synthesizer.rb
Overview
Synthesizes feedback items from multiple review reports.
Reads ALL reports in a single LLM pass and outputs deduplicated FeedbackItems with reviewer arrays tracking which models found each issue.
Constant Summary collapse
- FALLBACK_SYSTEM_PROMPT =
Cached system prompt and prompt-path lookups to avoid repeated shell/file reads during test and command-heavy runs.
"Synthesize feedback from code review reports into unique findings.\n\nFor each unique issue found:\n1. Track which reviewers identified it (by their model names)\n2. Merge file references from all sources\n3. Use the most comprehensive description\n4. Mark consensus=true if 3+ reviewers agree\n\nReturn valid JSON with this schema:\n{\n \"findings\": [\n {\n \"title\": \"Short title (max 60 chars)\",\n \"files\": [\"path/file.rb:10-20\"],\n \"reviewers\": [\"gemini-2.5-flash\", \"claude-3.5-sonnet\"],\n \"consensus\": false,\n \"priority\": \"high|medium|low|critical\",\n \"finding\": \"Description of the issue\",\n \"context\": \"Why this matters\"\n }\n ]\n}\n\nIMPORTANT:\n- When only one report: extract all findings as-is with that reviewer\n- When multiple reports: deduplicate findings that describe the same issue\n- When multiple reviewers find the same issue, list ALL of them in reviewers array\n- Preserve every distinct finding, including ones reported by only one reviewer\n- Consensus is metadata, never a reason to omit a finding\n- Merge file arrays from all sources for each finding\n- Return ONLY the JSON, no markdown code fences\n"
Instance Attribute Summary collapse
-
#llm_executor ⇒ Object
readonly
Returns the value of attribute llm_executor.
Class Method Summary collapse
- .clear_prompt_cache! ⇒ Object
- .resolve_prompt_path_cached(prompt_name) ⇒ Object
- .system_prompt(prompt_name) ⇒ Object
Instance Method Summary collapse
-
#initialize(llm_executor: nil) ⇒ FeedbackSynthesizer
constructor
A new instance of FeedbackSynthesizer.
-
#synthesize(report_paths:, session_dir: nil, model: nil) ⇒ Hash
Synthesize feedback items from review reports.
Constructor Details
#initialize(llm_executor: nil) ⇒ FeedbackSynthesizer
Returns a new instance of FeedbackSynthesizer.
125 126 127 |
# File 'lib/ace/review/molecules/feedback_synthesizer.rb', line 125 def initialize(llm_executor: nil) @llm_executor = llm_executor || LlmExecutor.new end |
Instance Attribute Details
#llm_executor ⇒ Object (readonly)
Returns the value of attribute llm_executor.
123 124 125 |
# File 'lib/ace/review/molecules/feedback_synthesizer.rb', line 123 def llm_executor @llm_executor end |
Class Method Details
.clear_prompt_cache! ⇒ Object
95 96 97 98 99 100 101 102 |
# File 'lib/ace/review/molecules/feedback_synthesizer.rb', line 95 def clear_prompt_cache! system_prompt_cache_mutex.synchronize do @system_prompt_cache = {} end prompt_path_cache_mutex.synchronize do @prompt_path_cache = {} end end |
.resolve_prompt_path_cached(prompt_name) ⇒ Object
81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/ace/review/molecules/feedback_synthesizer.rb', line 81 def resolve_prompt_path_cached(prompt_name) prompt_path_cache[prompt_name] ||= begin nav_result = begin `ace-nav prompt://#{prompt_name} 2>/dev/null`.strip rescue "" end return nav_result unless nav_result.empty? File.join(__dir__, "../../../../handbook/prompts", prompt_name) end end |
.system_prompt(prompt_name) ⇒ Object
67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/ace/review/molecules/feedback_synthesizer.rb', line 67 def system_prompt(prompt_name) system_prompt_cache_mutex.synchronize do system_prompt_cache.fetch(prompt_name) do prompt_path = resolve_prompt_path_cached(prompt_name) system_prompt_cache[prompt_name] = if prompt_path && File.exist?(prompt_path) File.read(prompt_path) else FALLBACK_SYSTEM_PROMPT end end end end |
Instance Method Details
#synthesize(report_paths:, session_dir: nil, model: nil) ⇒ Hash
Synthesize feedback items from review reports
Unified process that handles single or multiple reports:
- Single report: extracts findings directly
- Multiple reports: deduplicates and tracks consensus across reviewers
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/ace/review/molecules/feedback_synthesizer.rb', line 139 def synthesize(report_paths:, session_dir: nil, model: nil) # Validate inputs return error_result("No report paths provided") if report_paths.nil? || report_paths.empty? # Create session dir if needed session_dir ||= create_temp_session_dir FileUtils.mkdir_p(session_dir) unless Dir.exist?(session_dir) # Read all reports reports = read_reports(report_paths) return error_result("No valid reports found") if reports.empty? # Synthesize (handles both single and multiple reports uniformly) synthesize_reports(reports, session_dir, model) rescue => e error_result("Synthesis failed: #{e.message}", backtrace: e.backtrace.first(5)) end |