fix_engine/context.rs
1//! Fix context trait for framework/ruleset-specific LLM prompt customization.
2//!
3//! The fix engine is generic — it works with any Konveyor analysis output.
4//! But LLM-assisted fixes benefit from framework-specific guidance (prompt
5//! constraints, priority ordering, system prompts). This module defines the
6//! trait that framework crates implement, plus a generic fallback.
7
8/// Trait that framework/ruleset-specific crates implement to provide
9/// LLM prompt context for the fix engine.
10///
11/// Implementations are registered in a [`FixContextRegistry`](super::registry::FixContextRegistry)
12/// and looked up by ruleset name at runtime.
13pub trait FixContext: Send + Sync {
14 /// Unique name that matches the `RuleSet.name` field from Konveyor
15 /// analysis output. Used for registry lookup.
16 fn ruleset_name(&self) -> &str;
17
18 /// Human-readable description of the migration (e.g., "PatternFly v5 to v6").
19 /// Used in LLM system prompts and prompt preambles.
20 fn migration_description(&self) -> &str;
21
22 /// Framework-specific constraints to include in LLM prompts.
23 /// Each string is a single constraint line (will be prefixed with "- ").
24 fn llm_constraints(&self) -> &[String];
25
26 /// Warning text about revert pitfalls for batch mode context sections.
27 /// Shown when a batch prompt includes previously-applied fixes to warn
28 /// the LLM about framework-specific patterns it should not undo.
29 ///
30 /// Returns `None` if no revert warnings are needed.
31 fn revert_warnings(&self) -> Option<&str> {
32 None
33 }
34
35 /// Determine the processing priority of a fix request within a batch.
36 /// Lower number = processed first. This ensures structural migration
37 /// rules come before informational/review-only rules.
38 ///
39 /// Default: all rules have equal priority (3).
40 fn fix_priority(&self, _rule_id: &str) -> u8 {
41 3
42 }
43
44 /// Examples of change types for LLM prompt context.
45 ///
46 /// Used in batch prompts to describe the kinds of changes the LLM should
47 /// look for. Should be a parenthetical list of language-specific actions.
48 ///
49 /// Default: generic description suitable for any language.
50 fn change_type_examples(&self) -> &str {
51 "add/remove/rename identifiers, update references"
52 }
53
54 /// Optional verification instructions appended to the LLM batch prompt.
55 ///
56 /// Framework-specific checks the LLM should perform after applying fixes.
57 /// Returns `None` if no verification instructions are needed.
58 fn verification_prompt(&self) -> Option<&str> {
59 None
60 }
61
62 /// System prompt for the OpenAI-compatible LLM client.
63 ///
64 /// Default implementation builds a generic prompt from `migration_description()`.
65 fn llm_system_prompt(&self) -> String {
66 format!(
67 "You are a {} assistant. \
68 Given a code snippet and a migration message, output ONLY the corrected \
69 code for the affected lines. Output in this exact format:\n\n\
70 ```fix\n\
71 LINE:<line_number>\n\
72 OLD:<exact old text on that line>\n\
73 NEW:<replacement text>\n\
74 ```\n\n\
75 You may output multiple fix blocks. Do not include any explanation outside \
76 the fix blocks. Only output fixes for lines that need to change.",
77 self.migration_description()
78 )
79 }
80}
81
82/// Generic fallback context with no framework-specific guidance.
83///
84/// Used when no registered `FixContext` matches the ruleset name.
85/// Provides reasonable defaults that work for any Konveyor analysis output.
86pub struct GenericFixContext;
87
88impl FixContext for GenericFixContext {
89 fn ruleset_name(&self) -> &str {
90 ""
91 }
92
93 fn migration_description(&self) -> &str {
94 "code migration"
95 }
96
97 fn llm_constraints(&self) -> &[String] {
98 &[]
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn test_generic_context_defaults() {
108 let ctx = GenericFixContext;
109 assert_eq!(ctx.ruleset_name(), "");
110 assert_eq!(ctx.migration_description(), "code migration");
111 assert!(ctx.llm_constraints().is_empty());
112 assert!(ctx.revert_warnings().is_none());
113 assert_eq!(ctx.fix_priority("any-rule"), 3);
114 }
115
116 #[test]
117 fn test_generic_context_system_prompt() {
118 let ctx = GenericFixContext;
119 let prompt = ctx.llm_system_prompt();
120 assert!(prompt.contains("code migration"));
121 assert!(prompt.contains("```fix"));
122 assert!(prompt.contains("LINE:"));
123 }
124}