diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index f48c32c..4a3fb6c 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -3406,4 +3406,13 @@ target_link_libraries(step502_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step503_test tests/step503_test.cpp) +target_include_directories(step503_test PRIVATE src) +target_link_libraries(step503_test PRIVATE + nlohmann_json::nlohmann_json + unofficial::tree-sitter::tree-sitter + tree_sitter_python tree_sitter_cpp tree_sitter_elisp + tree_sitter_javascript tree_sitter_typescript + tree_sitter_java tree_sitter_rust tree_sitter_go) + # Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies) diff --git a/editor/src/APIBoundaryPreserver.h b/editor/src/APIBoundaryPreserver.h index 1ad710f..8f8becc 100644 --- a/editor/src/APIBoundaryPreserver.h +++ b/editor/src/APIBoundaryPreserver.h @@ -24,7 +24,7 @@ struct TypeMapping { std::string note; // compatibility note }; -struct ContractAnnotation { +struct APIMigrationContract { std::string functionName; std::string precondition; std::string postcondition; @@ -43,7 +43,7 @@ struct APIBoundaryReport { std::string targetLanguage; std::vector publicAPI; std::vector typeMappings; - std::vector contracts; + std::vector contracts; std::vector ffiBoundaries; bool apiPreserved = true; @@ -198,11 +198,11 @@ private: return mappings; } - static std::vector generateContracts( + static std::vector generateContracts( const std::vector& api, const std::string& source) { - std::vector contracts; + std::vector contracts; for (const auto& f : api) { - ContractAnnotation c; + APIMigrationContract c; c.functionName = f.name; // Generate pre/post conditions from signature if (!f.paramTypes.empty()) { diff --git a/editor/src/MigrationTestGenerator.h b/editor/src/MigrationTestGenerator.h index dfa5bcc..98f1cfa 100644 --- a/editor/src/MigrationTestGenerator.h +++ b/editor/src/MigrationTestGenerator.h @@ -176,7 +176,7 @@ private: } static std::string generateReturnEdgeCaseBody(const FunctionSignature& func, - const ContractAnnotation& contract, + const APIMigrationContract& contract, const std::string& lang) { if (lang == "rust") { return "#[test]\nfn test_" + func.name + "_return_contract() {\n" diff --git a/editor/src/Phase25bIntegration.h b/editor/src/Phase25bIntegration.h new file mode 100644 index 0000000..fa7bbc0 --- /dev/null +++ b/editor/src/Phase25bIntegration.h @@ -0,0 +1,229 @@ +#pragma once + +// Step 503: Phase 25b Integration +// Integrates the four end-to-end scenario runners from steps 499-502. + +#include "CrossLanguagePortScenarioRunner.h" +#include "GreenfieldScenarioRunner.h" +#include "LegacyModernizationScenarioRunner.h" +#include "MultiModelOrchestrationScenarioRunner.h" + +#include +#include +#include +#include + +struct Phase25bEvent { + std::string scenario; + std::string stage; + std::string detail; +}; + +struct ScenarioCostSnapshot { + std::string scenario; + int estimatedTokens = 0; + int actualTokens = 0; + + int delta() const { return std::abs(estimatedTokens - actualTokens); } +}; + +struct Phase25bIntegrationResult { + GreenfieldScenarioResult greenfield; + LegacyModernizationScenarioResult legacy; + CrossLanguagePortScenarioResult crossLanguage; + MultiModelOrchestrationScenarioResult multiModel; + + std::vector costs; + std::vector events; + std::map scenarioPass; + int totalSecurityFindings = 0; + bool allScenariosPassed = false; + bool uniqueWorkflowPathsObserved = false; + bool costTrackingAccurate = false; + bool eventStreamComplete = false; + std::vector notes; +}; + +class Phase25bIntegration { +public: + static Phase25bIntegrationResult run() { + Phase25bIntegrationResult out; + + emit(out, "greenfield", "start", "execute scenario"); + out.greenfield = GreenfieldScenarioRunner::run(tempRoot(), "phase25b_greenfield"); + emit(out, "greenfield", "complete", "tasks=" + std::to_string(out.greenfield.tasks.size())); + + emit(out, "legacy", "start", "execute scenario"); + out.legacy = LegacyModernizationScenarioRunner::run(); + emit(out, "legacy", "complete", + "suggestions=" + std::to_string(out.legacy.modernizationReport.suggestions.size())); + + emit(out, "cross-language", "start", "execute scenario"); + out.crossLanguage = CrossLanguagePortScenarioRunner::run(); + emit(out, "cross-language", "complete", + "functions=" + std::to_string(out.crossLanguage.functions.size())); + + emit(out, "multi-model", "start", "execute scenario"); + out.multiModel = MultiModelOrchestrationScenarioRunner::run(); + emit(out, "multi-model", "complete", + "tasks=" + std::to_string(out.multiModel.tasks.size())); + + evaluatePassFlags(out); + buildCostSnapshots(out); + evaluateIntegrationSignals(out); + + out.notes.push_back("Phase 25b integration pipeline executed"); + out.notes.push_back(out.allScenariosPassed ? "all scenarios passed" : "one or more scenarios failed"); + return out; + } + +private: + static void emit(Phase25bIntegrationResult& out, + const std::string& scenario, + const std::string& stage, + const std::string& detail) { + out.events.push_back({scenario, stage, detail}); + } + + static std::string tempRoot() { + return "/tmp"; + } + + static void evaluatePassFlags(Phase25bIntegrationResult& out) { + out.scenarioPass["greenfield"] = + out.greenfield.scaffoldApplied && !out.greenfield.createdPaths.empty(); + out.scenarioPass["legacy"] = + !out.legacy.modernizationReport.suggestions.empty() && + out.legacy.migrationPlan.targetLanguage == "rust"; + out.scenarioPass["cross-language"] = + out.crossLanguage.sourceLanguage == "python" && + out.crossLanguage.targetLanguage == "rust" && + !out.crossLanguage.functions.empty(); + out.scenarioPass["multi-model"] = + out.multiModel.tasks.size() == 20 && out.multiModel.reviewGatesExercised; + + out.allScenariosPassed = true; + for (const auto& kv : out.scenarioPass) { + if (!kv.second) out.allScenariosPassed = false; + } + } + + static void buildCostSnapshots(Phase25bIntegrationResult& out) { + ScenarioCostSnapshot g; + g.scenario = "greenfield"; + g.estimatedTokens = static_cast(out.greenfield.tasks.size()) * 500; + g.actualTokens = estimateGreenfieldActual(out.greenfield); + out.costs.push_back(g); + + ScenarioCostSnapshot l; + l.scenario = "legacy"; + l.estimatedTokens = static_cast(out.legacy.modernizationWorkflow.items.size()) * 550; + l.actualTokens = estimateLegacyActual(out.legacy); + out.costs.push_back(l); + + ScenarioCostSnapshot c; + c.scenario = "cross-language"; + c.estimatedTokens = static_cast(out.crossLanguage.functions.size()) * 700; + c.actualTokens = estimateCrossLanguageActual(out.crossLanguage); + out.costs.push_back(c); + + ScenarioCostSnapshot m; + m.scenario = "multi-model"; + m.estimatedTokens = out.multiModel.estimatedTotalTokens; + m.actualTokens = out.multiModel.actualTotalTokens; + out.costs.push_back(m); + } + + static void evaluateIntegrationSignals(Phase25bIntegrationResult& out) { + out.totalSecurityFindings = + static_cast(out.greenfield.securityFindings.size()) + + static_cast(out.legacy.safetyReport.findings.size()); + + out.uniqueWorkflowPathsObserved = + hasUniquePathGreenfield(out.greenfield) && + hasUniquePathLegacy(out.legacy) && + hasUniquePathCrossLanguage(out.crossLanguage) && + hasUniquePathMultiModel(out.multiModel); + + out.costTrackingAccurate = true; + for (const auto& c : out.costs) { + if (c.estimatedTokens <= 0 || c.actualTokens < 0) { + out.costTrackingAccurate = false; + break; + } + if (c.delta() > c.estimatedTokens) { + out.costTrackingAccurate = false; + break; + } + } + + out.eventStreamComplete = true; + for (const auto& name : {"greenfield", "legacy", "cross-language", "multi-model"}) { + bool hasStart = false; + bool hasComplete = false; + for (const auto& e : out.events) { + if (e.scenario != name) continue; + if (e.stage == "start") hasStart = true; + if (e.stage == "complete") hasComplete = true; + } + if (!hasStart || !hasComplete) out.eventStreamComplete = false; + } + } + + static int estimateGreenfieldActual(const GreenfieldScenarioResult& r) { + int total = 0; + for (const auto& t : r.tasks) { + if (t.routeTo == "deterministic") total += 0; + else if (t.routeTo == "llm") total += 700; + else total += 0; + } + return total; + } + + static int estimateLegacyActual(const LegacyModernizationScenarioResult& r) { + int total = 0; + for (const auto& i : r.modernizationWorkflow.items) { + if (i.routing == WorkItemRouting::Deterministic) total += 80; + else if (i.routing == WorkItemRouting::LLM) total += 500; + else total += 0; + } + return total; + } + + static int estimateCrossLanguageActual(const CrossLanguagePortScenarioResult& r) { + int total = 0; + for (const auto& f : r.functions) { + if (f.confidence.reviewRequired) total += 800; + else total += 450; + } + return total; + } + + static bool hasUniquePathGreenfield(const GreenfieldScenarioResult& r) { + bool hasDet = false, hasLlm = false, hasHuman = false; + for (const auto& t : r.tasks) { + if (t.routeTo == "deterministic") hasDet = true; + if (t.routeTo == "llm") hasLlm = true; + if (t.routeTo == "human") hasHuman = true; + } + return hasDet && hasLlm && hasHuman; + } + + static bool hasUniquePathLegacy(const LegacyModernizationScenarioResult& r) { + return r.modernizationWorkflow.countByRouting(WorkItemRouting::Deterministic) > 0 && + r.modernizationWorkflow.countByRouting(WorkItemRouting::LLM) > 0; + } + + static bool hasUniquePathCrossLanguage(const CrossLanguagePortScenarioResult& r) { + return !r.reviewRequiredFunctions.empty() && + r.concurrencyAnalysis.hasPattern("async/await"); + } + + static bool hasUniquePathMultiModel(const MultiModelOrchestrationScenarioResult& r) { + return r.routedCounts.count("deterministic") && + r.routedCounts.count("template") && + r.routedCounts.count("slm") && + r.routedCounts.count("llm") && + r.routedCounts.count("human"); + } +}; diff --git a/editor/tests/step503_test.cpp b/editor/tests/step503_test.cpp new file mode 100644 index 0000000..549a65a --- /dev/null +++ b/editor/tests/step503_test.cpp @@ -0,0 +1,97 @@ +// Step 503: Phase 25b Integration Tests (8 tests) + +#include "Phase25bIntegration.h" + +#include + +static int passed = 0, failed = 0; +#define TEST(name) { std::cout << " " << #name << "... "; } +#define PASS() { std::cout << "PASS\n"; ++passed; } +#define FAIL(msg) { std::cout << "FAIL: " << msg << "\n"; ++failed; } +#define CHECK(cond, msg) if (!(cond)) { FAIL(msg); return; } else {} + +static Phase25bIntegrationResult runNow() { + return Phase25bIntegration::run(); +} + +void test_all_four_scenarios_run_successfully() { + TEST(all_four_scenarios_run_successfully); + auto r = runNow(); + CHECK(r.scenarioPass.size() == 4, "expected 4 scenario pass flags"); + CHECK(r.allScenariosPassed, "all scenarios should pass"); + PASS(); +} + +void test_each_scenario_exercises_distinct_workflow_path_signals() { + TEST(each_scenario_exercises_distinct_workflow_path_signals); + auto r = runNow(); + CHECK(r.uniqueWorkflowPathsObserved, "expected unique workflow path coverage"); + PASS(); +} + +void test_cost_tracking_contains_all_scenarios_and_positive_estimates() { + TEST(cost_tracking_contains_all_scenarios_and_positive_estimates); + auto r = runNow(); + CHECK(r.costs.size() == 4, "expected 4 cost snapshots"); + for (const auto& c : r.costs) { + CHECK(c.estimatedTokens > 0, "estimated tokens must be positive"); + CHECK(c.actualTokens >= 0, "actual tokens must be non-negative"); + } + PASS(); +} + +void test_cost_tracking_accuracy_gate_passes_for_integration() { + TEST(cost_tracking_accuracy_gate_passes_for_integration); + auto r = runNow(); + CHECK(r.costTrackingAccurate, "cost tracking should be marked accurate"); + PASS(); +} + +void test_security_findings_are_aggregated_from_generated_code_paths() { + TEST(security_findings_are_aggregated_from_generated_code_paths); + auto r = runNow(); + CHECK(r.totalSecurityFindings > 0, "expected aggregated security findings"); + PASS(); +} + +void test_event_stream_captures_start_and_complete_for_each_scenario() { + TEST(event_stream_captures_start_and_complete_for_each_scenario); + auto r = runNow(); + CHECK(r.eventStreamComplete, "expected complete start/complete event pairs"); + CHECK(r.events.size() >= 8, "expected at least start+complete for 4 scenarios"); + PASS(); +} + +void test_multi_model_scenario_keeps_review_gates_exercised_in_integration() { + TEST(multi_model_scenario_keeps_review_gates_exercised_in_integration); + auto r = runNow(); + CHECK(r.multiModel.reviewGatesExercised, "multi-model review gates should be exercised"); + CHECK(r.multiModel.reviewGateCount >= 5, "expected review gates from llm + human tasks"); + PASS(); +} + +void test_integration_notes_include_execution_summary_and_status() { + TEST(integration_notes_include_execution_summary_and_status); + auto r = runNow(); + CHECK(r.notes.size() >= 2, "expected integration notes"); + CHECK(r.notes[0].find("integration pipeline executed") != std::string::npos, + "missing execution summary"); + PASS(); +} + +int main() { + std::cout << "Step 503: Phase 25b Integration Tests\n"; + + test_all_four_scenarios_run_successfully(); // 1 + test_each_scenario_exercises_distinct_workflow_path_signals(); // 2 + test_cost_tracking_contains_all_scenarios_and_positive_estimates(); // 3 + test_cost_tracking_accuracy_gate_passes_for_integration(); // 4 + test_security_findings_are_aggregated_from_generated_code_paths(); // 5 + test_event_stream_captures_start_and_complete_for_each_scenario(); // 6 + test_multi_model_scenario_keeps_review_gates_exercised_in_integration(); // 7 + test_integration_notes_include_execution_summary_and_status(); // 8 + + std::cout << "\nResults: " << passed << "/" << (passed + failed) + << " passed\n"; + return failed == 0 ? 0 : 1; +} diff --git a/progress.md b/progress.md index 72aef55..94b3950 100644 --- a/progress.md +++ b/progress.md @@ -7745,3 +7745,47 @@ that exercises all worker classes and review gates. - `editor/src/MultiModelOrchestrationScenarioRunner.h` within header-size limit (`177` <= `600`) - `editor/tests/step502_test.cpp` within test-file size guidance (`152` lines) - Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md` + +### Step 503: Phase 25b Integration +**Status:** PASS (8/8 tests) + +Integrates all four Phase 25b scenario runners (steps 499-502) into a single +integration pass with scenario-level status, cost snapshots, security aggregation, +workflow-path diversity checks, and event-stream completeness signals. + +**Files added:** +- `editor/src/Phase25bIntegration.h` - integration compositor: + - executes Greenfield, Legacy Modernization, Cross-Language Port, and Multi-Model scenarios + - emits per-scenario start/complete events + - computes per-scenario estimated vs actual token snapshots + - validates cross-scenario path diversity and cost/event gating + - aggregates security findings from generated code/audits +- `editor/tests/step503_test.cpp` - 8 integration tests covering: + - all-scenario pass state + - unique workflow-path signal coverage + - cost tracking shape/accuracy gating + - security finding aggregation + - event stream completeness + - integrated review-gate behavior retention +- `editor/CMakeLists.txt` - `step503_test` target + +**Compatibility fix applied during integration:** +- `editor/src/APIBoundaryPreserver.h` +- `editor/src/MigrationTestGenerator.h` + - renamed API migration contract struct to `APIMigrationContract` to resolve + a symbol collision with AST `ContractAnnotation` when scenario headers are composed + in one translation unit. + +**Verification run:** +- `cmake -S editor -B editor/build-native` - PASS +- `cmake --build editor/build-native --target step503_test step502_test` - PASS +- `./editor/build-native/step503_test` - PASS (8/8) +- `./editor/build-native/step502_test` - PASS (12/12) regression coverage + +**Architecture gate check:** +- `editor/src/Phase25bIntegration.h` within header-size limit (`229` <= `600`) +- `editor/tests/step503_test.cpp` within test-file size guidance (`97` lines) +- Updated integration dependencies remain within header-size limits: + - `editor/src/APIBoundaryPreserver.h` (`253` <= `600`) + - `editor/src/MigrationTestGenerator.h` (`213` <= `600`) +- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`