Pass reverse-engineered business logic into migration/conversion pipeline#45
Pass reverse-engineered business logic into migration/conversion pipeline#45
Conversation
…line Co-authored-by: jkordick <52427852+jkordick@users.noreply.github.com>
… business logic persistence - Added dependency mapping step to ChunkedReverseEngineeringProcess and ReverseEngineeringProcess. - Integrated IMigrationRepository for saving and reusing business logic and dependency maps. - Updated RunMigrationAsync to support loading persisted business logic from previous runs with --reuse-re flag. - Modified command-line interface to include options for reusing business logic and improved documentation. - Enhanced README and architecture documentation to reflect changes in business logic persistence and usage.
There was a problem hiding this comment.
Pull request overview
This pull request addresses issue #20 by implementing end-to-end persistence and injection of business logic extracted during reverse engineering into the conversion prompts. Previously, the BusinessLogicExtractorAgent output was generated for documentation only; now it is stored in a new SQLite table and passed to converter agents to guide AI-based code translation.
Changes:
- Added
SetBusinessLogicContext()toICodeConverterAgentand implemented it across all four converter agents (Java, C#, and chunk-aware variants), with a sharedFormatBusinessLogicContext()helper inAgentBase - Introduced
business_logicSQLite table and three new repository methods (SaveBusinessLogicAsync,GetBusinessLogicAsync,DeleteBusinessLogicAsync) to persist and retrieve business logic per run - Extended
ReverseEngineeringProcessandChunkedReverseEngineeringProcessto persist business logic and dependency maps, and added a--reuse-reCLI flag to load cached results when running conversion-only mode - Added REST API endpoints (
GET/DELETE /api/runs/{runId}/business-logic) and Portal UI (🔬 RE Resultsbutton per run) to view and delete persisted business logic - Updated
SmartMigrationOrchestrator,MigrationProcess, andChunkedMigrationProcessto accept and thread business logic context and dependency maps through the pipeline - Enhanced
doctor.shto prompt for--reuse-reinconvert-onlymode and updated documentation across README, CHANGELOG, and REVERSE_ENGINEERING_ARCHITECTURE
Reviewed changes
Copilot reviewed 26 out of 26 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
Agents/Interfaces/ICodeConverterAgent.cs |
Added SetBusinessLogicContext() method to interface |
Agents/JavaConverterAgent.cs |
Implemented business logic context injection into Java conversion prompts |
Agents/CSharpConverterAgent.cs |
Implemented business logic context injection into C# conversion prompts |
Agents/ChunkAwareJavaConverter.cs |
Implemented business logic context injection for chunked Java conversion |
Agents/ChunkAwareCSharpConverter.cs |
Implemented business logic context injection for chunked C# conversion |
Agents/Infrastructure/AgentBase.cs |
Added shared FormatBusinessLogicContext() helper method |
Agents/BusinessLogicExtractorAgent.cs |
Fixed feature extraction to match "Use Case" and "Operation" headings |
Persistence/IMigrationRepository.cs |
Added business logic persistence methods to interface |
Persistence/SqliteMigrationRepository.cs |
Created business_logic table and implemented save/get/delete methods |
Persistence/HybridMigrationRepository.cs |
Delegated business logic methods to SQLite repository |
Processes/ReverseEngineeringProcess.cs |
Added dependency mapping step and business logic persistence |
Processes/ChunkedReverseEngineeringProcess.cs |
Added dependency mapping step and business logic persistence for chunked RE |
Processes/MigrationProcess.cs |
Added SetBusinessLogicContext() and dependency map reuse logic |
Processes/ChunkedMigrationProcess.cs |
Added SetBusinessLogicContext() and dependency map reuse logic |
Processes/SmartMigrationOrchestrator.cs |
Extended RunAsync() to accept and pass business logic and dependency map context |
Program.cs |
Added --reuse-re flag, logic to load cached business logic, and wiring to pass context to orchestrator |
Helpers/FileHelper.cs |
Added SaveDependencyOutputsAsync() helper method |
McpChatWeb/Program.cs |
Added REST API endpoints for business logic retrieval/deletion and injected RE results into chat context |
McpChatWeb/Services/McpProcessClient.cs |
Updated chat system prompt to mention RE context availability |
McpChatWeb/wwwroot/runs-viewer.js |
Added UI for viewing and deleting per-run RE results |
McpChatWeb/wwwroot/run-selector.js |
Added run type label display in selector |
McpChatWeb/wwwroot/index.html |
Renamed modal title to "Reverse Engineering Results" |
doctor.sh |
Added interactive prompt for --reuse-re in convert-only mode and updated help text |
docs/REVERSE_ENGINEERING_ARCHITECTURE.md |
Documented business logic persistence feature and updated diagrams |
README.md |
Added business logic persistence section with usage modes table |
CHANGELOG.md |
Documented all changes in v2.5.0 release |
Comments suppressed due to low confidence (1)
Program.cs:1454
- The ChunkedReverseEngineeringProcess constructor is missing the migrationRepository parameter. In the main migration flow (lines 656-666), the chunked RE process receives migrationRepository as the last parameter, but in the reverse-eng command flow (here), it's omitted. This means when running
./doctor.sh reverse-engwith large files, business logic won't be persisted to the database, breaking the --reuse-re functionality. Add migrationRepository as the last constructor parameter to match the pattern at line 666.
var chunkedProcess = new ChunkedReverseEngineeringProcess(
cobolAnalyzerAgent,
businessLogicExtractorAgent,
dependencyMapperAgent,
fileHelper,
settings.ChunkingSettings,
chunkingOrchestrator,
loggerFactory.CreateLogger<ChunkedReverseEngineeringProcess>(),
enhancedLogger,
databasePath);
McpChatWeb/Program.cs
Outdated
| (SELECT COUNT(*) FROM json_each(user_stories_json)) AS story_count, | ||
| (SELECT COUNT(*) FROM json_each(features_json)) AS feature_count, | ||
| (SELECT COUNT(*) FROM json_each(business_rules_json)) AS rule_count, |
There was a problem hiding this comment.
The SQL query uses json_each() to count array elements, which will fail silently if the JSON is null or invalid, returning 0. However, the table schema shows these columns can be NULL (inserted via DBNull.Value). When a column is NULL, json_each(NULL) will return an empty set, so COUNT will correctly return 0. This is actually safe, but consider adding COALESCE to make the intent clearer: COALESCE((SELECT COUNT(*) FROM json_each(user_stories_json)), 0).
| (SELECT COUNT(*) FROM json_each(user_stories_json)) AS story_count, | |
| (SELECT COUNT(*) FROM json_each(features_json)) AS feature_count, | |
| (SELECT COUNT(*) FROM json_each(business_rules_json)) AS rule_count, | |
| COALESCE((SELECT COUNT(*) FROM json_each(user_stories_json)), 0) AS story_count, | |
| COALESCE((SELECT COUNT(*) FROM json_each(features_json)), 0) AS feature_count, | |
| COALESCE((SELECT COUNT(*) FROM json_each(business_rules_json)), 0) AS rule_count, |
| protected static string FormatBusinessLogicContext(BusinessLogic businessLogic) | ||
| { | ||
| var sb = new StringBuilder(); | ||
| if (!string.IsNullOrWhiteSpace(businessLogic.BusinessPurpose)) | ||
| { | ||
| sb.AppendLine($"Purpose: {businessLogic.BusinessPurpose}"); | ||
| } | ||
| if (businessLogic.BusinessRules.Count > 0) | ||
| { | ||
| sb.AppendLine("Business Rules:"); | ||
| foreach (var rule in businessLogic.BusinessRules) | ||
| { | ||
| sb.AppendLine($"- {rule.Description}"); | ||
| if (!string.IsNullOrWhiteSpace(rule.Condition)) | ||
| sb.AppendLine($" Condition: {rule.Condition}"); | ||
| if (!string.IsNullOrWhiteSpace(rule.Action)) | ||
| sb.AppendLine($" Action: {rule.Action}"); | ||
| } | ||
| } | ||
| if (businessLogic.Features.Count > 0) | ||
| { | ||
| sb.AppendLine("Features:"); | ||
| foreach (var feature in businessLogic.Features) | ||
| { | ||
| sb.AppendLine($"- {feature.Name}: {feature.Description}"); | ||
| } | ||
| } | ||
| if (businessLogic.UserStories.Count > 0) | ||
| { | ||
| sb.AppendLine("Feature Descriptions:"); | ||
| foreach (var story in businessLogic.UserStories) | ||
| { | ||
| sb.AppendLine($"- {story.Title}: {story.Action}"); | ||
| } | ||
| } | ||
| sb.AppendLine(); | ||
| return sb.ToString(); | ||
| } |
There was a problem hiding this comment.
The FormatBusinessLogicContext method always appends a blank line at the end (line 856), even when the BusinessLogic object has no content (all lists are empty and BusinessPurpose is null/empty). This means converters will inject "Here is the extracted business logic..." followed by just a blank line when no business logic was extracted. Consider adding a check at the start: if (string.IsNullOrWhiteSpace(businessLogic.BusinessPurpose) && businessLogic.BusinessRules.Count == 0 && businessLogic.Features.Count == 0 && businessLogic.UserStories.Count == 0) return string.Empty; to avoid injecting empty context.
| features_json TEXT, | ||
| business_rules_json TEXT, | ||
| created_at TEXT DEFAULT CURRENT_TIMESTAMP, | ||
| FOREIGN KEY(run_id) REFERENCES runs(id), |
There was a problem hiding this comment.
The business_logic table's FOREIGN KEY constraint is missing ON DELETE CASCADE. The documentation at docs/REVERSE_ENGINEERING_ARCHITECTURE.md line 469 shows the schema should include ON DELETE CASCADE, which makes sense since business logic entries should be deleted when their parent run is deleted. Without this, orphaned business_logic records will remain in the database if a run is manually deleted. Add ON DELETE CASCADE to the FOREIGN KEY constraint on line 296 to match the documented schema and prevent orphaned records.
Co-authored-by: jkordick <52427852+jkordick@users.noreply.github.com>
…tion) (#53) * docs: fill three documentation gaps from PR #45 (BL injection) Co-authored-by: jkordick <52427852+jkordick@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: jkordick <52427852+jkordick@users.noreply.github.com>
The output of reverse engineering (business logic, features, business rules) was captured but never forwarded to the conversion step — converters operated with only the structural
CobolAnalysis, missing the semantic context extracted byBusinessLogicExtractorAgent.Changes
Interface & converter agents
SetBusinessLogicContext(List<BusinessLogic>)toICodeConverterAgent(mirrors the existingSetRunIdpattern)JavaConverterAgent,CSharpConverterAgent,ChunkAwareJavaConverter,ChunkAwareCSharpConverterBusinessLogicentry by filename at conversion time and injects it into the AI prompt:FormatBusinessLogicContexthelper toAgentBaseto avoid duplication across all four converter classesPipeline wiring
MigrationProcessandChunkedMigrationProcesseach exposeSetBusinessLogicContext, which delegates to their respective converter agentSmartMigrationOrchestrator.RunAsync()gains an optionalList<BusinessLogic>? businessLogicExtractsparameter and callsSetBusinessLogicContexton whichever sub-process is instantiated (direct or chunked), afterInitializeAgents()Program.cs:reverseEngResultis now persisted in outer scope andreverseEngResult.BusinessLogicExtractsis passed intosmartOrchestrator.RunAsync()— effective for both the full pipeline and skip-reverse-engineering modes (context isnull/empty when RE was skipped, converters no-op gracefully)Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.