From 21037d75d93d61442fca438aaadd68c7506fc224 Mon Sep 17 00:00:00 2001 From: Bill Date: Mon, 16 Feb 2026 20:03:30 -0700 Subject: [PATCH] Complete step458 transpilation RPC and MCP tools with tests --- editor/CMakeLists.txt | 9 ++ editor/src/TranspilationRPC.h | 255 ++++++++++++++++++++++++++++++++++ editor/tests/step458_test.cpp | 193 +++++++++++++++++++++++++ progress.md | 37 +++++ 4 files changed, 494 insertions(+) create mode 100644 editor/src/TranspilationRPC.h create mode 100644 editor/tests/step458_test.cpp diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index a744fa0..10b1a36 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -3001,4 +3001,13 @@ target_link_libraries(step457_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step458_test tests/step458_test.cpp) +target_include_directories(step458_test PRIVATE src) +target_link_libraries(step458_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/TranspilationRPC.h b/editor/src/TranspilationRPC.h new file mode 100644 index 0000000..323b446 --- /dev/null +++ b/editor/src/TranspilationRPC.h @@ -0,0 +1,255 @@ +#pragma once + +// Step 458: Transpilation RPC + MCP tool definitions for semantic transpilation, +// reporting, equivalence checks, and confidence scoring. + +#include "BehavioralEquivalence.h" +#include "IntentTranslator.h" +#include "TranslationReport.h" + +#include +#include +#include + +using json = nlohmann::json; + +inline json confidenceToJson(const ConfidenceScore& s) { + return { + {"functionName", s.functionName}, + {"sourceLanguage", s.sourceLanguage}, + {"targetLanguage", s.targetLanguage}, + {"score", s.score}, + {"reviewRequired", s.reviewRequired}, + {"annotation", s.annotation}, + {"reviewReason", s.reviewReason} + }; +} + +inline json fnReportToJson(const FunctionTranslationReport& f) { + return { + {"functionName", f.functionName}, + {"sourceLanguage", f.sourceLanguage}, + {"targetLanguage", f.targetLanguage}, + {"sourceConstruct", f.sourceConstruct}, + {"targetConstruct", f.targetConstruct}, + {"confidence", f.confidence}, + {"idiomatic", f.idiomatic}, + {"reviewRequired", f.reviewRequired}, + {"reviewReason", f.reviewReason}, + {"safetyDelta", f.safetyDelta}, + {"rationale", f.rationale}, + {"annotations", { + {"added", f.annotations.added}, + {"removed", f.annotations.removed}, + {"changed", f.annotations.changed} + }} + }; +} + +inline json equivalenceToJson(const BehavioralCheckResult& r) { + json assertions = json::array(); + for (const auto& a : r.assertions) { + assertions.push_back({ + {"functionName", a.functionName}, + {"inputSpec", a.inputSpec}, + {"expectedOutput", a.expectedOutput}, + {"sameErrors", a.sameErrors}, + {"confidenceScore", a.confidenceScore} + }); + } + return { + {"functionName", r.functionName}, + {"sourceLanguage", r.sourceLanguage}, + {"targetLanguage", r.targetLanguage}, + {"assertions", assertions}, + {"allPassing", r.allPassing}, + {"annotation", r.annotation} + }; +} + +class TranspilationRPCHandler { +public: + static json handleTranspile(const json& params) { + FunctionIntent fn{ + params.value("functionName", "function"), + params.value("intent", ""), + params.value("source", "") + }; + std::string sourceLanguage = params.value("sourceLanguage", "python"); + std::string targetLanguage = params.value("targetLanguage", "rust"); + auto result = IntentTranslator::translate({fn}, sourceLanguage, targetLanguage); + auto translation = result.getTranslation(fn.name); + + return { + {"functionName", translation.functionName}, + {"sourceLanguage", sourceLanguage}, + {"targetLanguage", targetLanguage}, + {"sourceCode", translation.sourceCode}, + {"targetCode", translation.targetCode}, + {"confidence", translation.confidence}, + {"idiomatic", translation.isIdiomatic}, + {"needsReview", translation.needsReview}, + {"reviewReason", translation.reviewReason} + }; + } + + static json handleGetTranslationReport(const json& params) { + std::string sourceLanguage = params.value("sourceLanguage", "python"); + std::string targetLanguage = params.value("targetLanguage", "rust"); + json funcs = params.value("functions", json::array()); + std::vector fns; + for (const auto& f : funcs) { + fns.push_back({ + f.value("name", "function"), + f.value("intent", ""), + f.value("source", "") + }); + } + auto translated = IntentTranslator::translate(fns, sourceLanguage, targetLanguage); + auto report = TranslationReportGenerator::generate(translated); + + json functionReports = json::array(); + for (const auto& fn : report.functions) + functionReports.push_back(fnReportToJson(fn)); + + return { + {"sourceLanguage", report.sourceLanguage}, + {"targetLanguage", report.targetLanguage}, + {"functions", functionReports}, + {"summary", { + {"totalFunctions", report.summary.totalFunctions}, + {"idiomaticCount", report.summary.idiomaticCount}, + {"literalCount", report.summary.literalCount}, + {"flaggedForReviewCount", report.summary.flaggedForReviewCount}, + {"idiomaticPercent", report.summary.idiomaticPercent()}, + {"literalPercent", report.summary.literalPercent()}, + {"reviewPercent", report.summary.reviewPercent()} + }} + }; + } + + static json handleVerifyEquivalence(const json& params) { + std::string functionName = params.value("functionName", "function"); + std::string sourceCode = params.value("sourceCode", ""); + std::string targetCode = params.value("targetCode", ""); + std::string sourceLanguage = params.value("sourceLanguage", "python"); + std::string targetLanguage = params.value("targetLanguage", "rust"); + + std::vector sampleInputs; + if (params.contains("sampleInputs")) { + for (const auto& s : params["sampleInputs"]) + sampleInputs.push_back(s.get()); + } + + auto result = BehavioralChecker::check( + functionName, sourceCode, targetCode, sourceLanguage, targetLanguage, sampleInputs); + return equivalenceToJson(result); + } + + static json handleGetConfidence(const json& params) { + std::string functionName = params.value("functionName", "function"); + std::string sourceCode = params.value("sourceCode", ""); + std::string targetCode = params.value("targetCode", ""); + std::string sourceLanguage = params.value("sourceLanguage", "python"); + std::string targetLanguage = params.value("targetLanguage", "rust"); + bool hasIntent = params.value("hasIntent", false); + + auto score = TranspilationScorer::score( + functionName, sourceCode, targetCode, sourceLanguage, targetLanguage, hasIntent); + return confidenceToJson(score); + } + + static bool canHandle(const std::string& method) { + return method == "transpile" || + method == "getTranslationReport" || + method == "verifyEquivalence" || + method == "getConfidence"; + } + + static json dispatch(const std::string& method, const json& params, const json& id) { + json result; + if (method == "transpile") + result = handleTranspile(params); + else if (method == "getTranslationReport") + result = handleGetTranslationReport(params); + else if (method == "verifyEquivalence") + result = handleVerifyEquivalence(params); + else if (method == "getConfidence") + result = handleGetConfidence(params); + else + return {{"jsonrpc", "2.0"}, {"id", id}, + {"error", {{"code", -32601}, {"message", "Unknown method"}}}}; + return {{"jsonrpc", "2.0"}, {"id", id}, {"result", result}}; + } + + struct MCPToolDef { + std::string name; + std::string description; + json inputSchema; + }; + + static std::vector toolDefinitions() { + json transpileSchema = { + {"type", "object"}, + {"properties", { + {"functionName", {{"type", "string"}}}, + {"intent", {{"type", "string"}}}, + {"source", {{"type", "string"}}}, + {"sourceLanguage", {{"type", "string"}}}, + {"targetLanguage", {{"type", "string"}}} + }}, + {"required", json::array({"functionName", "source"})} + }; + + json reportSchema = { + {"type", "object"}, + {"properties", { + {"functions", {{"type", "array"}}}, + {"sourceLanguage", {{"type", "string"}}}, + {"targetLanguage", {{"type", "string"}}} + }}, + {"required", json::array({"functions"})} + }; + + json eqSchema = { + {"type", "object"}, + {"properties", { + {"functionName", {{"type", "string"}}}, + {"sourceCode", {{"type", "string"}}}, + {"targetCode", {{"type", "string"}}}, + {"sourceLanguage", {{"type", "string"}}}, + {"targetLanguage", {{"type", "string"}}}, + {"sampleInputs", {{"type", "array"}}} + }}, + {"required", json::array({"functionName", "sourceCode", "targetCode"})} + }; + + json confidenceSchema = { + {"type", "object"}, + {"properties", { + {"functionName", {{"type", "string"}}}, + {"sourceCode", {{"type", "string"}}}, + {"targetCode", {{"type", "string"}}}, + {"sourceLanguage", {{"type", "string"}}}, + {"targetLanguage", {{"type", "string"}}}, + {"hasIntent", {{"type", "boolean"}}} + }}, + {"required", json::array({"functionName", "sourceCode", "targetCode"})} + }; + + return { + {"whetstone_transpile", + "Transpile a function using semantic intent-aware translation.", + transpileSchema}, + {"whetstone_get_translation_report", + "Generate per-function and project-level translation report.", + reportSchema}, + {"whetstone_verify_equivalence", + "Generate behavioral equivalence assertions for source vs target.", + eqSchema}, + {"whetstone_get_confidence", + "Get confidence score and review requirement for a translation.", + confidenceSchema} + }; + } +}; diff --git a/editor/tests/step458_test.cpp b/editor/tests/step458_test.cpp new file mode 100644 index 0000000..2c9445e --- /dev/null +++ b/editor/tests/step458_test.cpp @@ -0,0 +1,193 @@ +// Step 458: Transpilation RPC + MCP Tests (12 tests) + +#include "TranspilationRPC.h" + +#include + +using json = nlohmann::json; + +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 {} + +void test_can_handle_all_methods() { + TEST(can_handle_all_methods); + CHECK(TranspilationRPCHandler::canHandle("transpile"), "missing transpile"); + CHECK(TranspilationRPCHandler::canHandle("getTranslationReport"), "missing getTranslationReport"); + CHECK(TranspilationRPCHandler::canHandle("verifyEquivalence"), "missing verifyEquivalence"); + CHECK(TranspilationRPCHandler::canHandle("getConfidence"), "missing getConfidence"); + PASS(); +} + +void test_handle_transpile_returns_target_code() { + TEST(handle_transpile_returns_target_code); + json params = { + {"functionName", "sort_items"}, + {"intent", "sort list ascending"}, + {"source", "items.sort()"}, + {"sourceLanguage", "python"}, + {"targetLanguage", "rust"} + }; + auto r = TranspilationRPCHandler::handleTranspile(params); + CHECK(r["functionName"] == "sort_items", "wrong function name"); + CHECK(r["targetCode"].get().find("sort") != std::string::npos, + "expected sort mapping"); + CHECK(r["idiomatic"].get(), "expected idiomatic translation"); + PASS(); +} + +void test_handle_translation_report_returns_summary() { + TEST(handle_translation_report_returns_summary); + json params = { + {"sourceLanguage", "python"}, + {"targetLanguage", "rust"}, + {"functions", json::array({ + {{"name", "sort_items"}, {"intent", "sort list ascending"}, {"source", "items.sort()"}}, + {{"name", "mystery"}, {"intent", ""}, {"source", "x = a + b"}} + })} + }; + auto r = TranspilationRPCHandler::handleGetTranslationReport(params); + CHECK(r.contains("summary"), "missing summary"); + CHECK(r["summary"]["totalFunctions"] == 2, "wrong function count"); + CHECK(r["functions"].size() == 2, "wrong function report count"); + PASS(); +} + +void test_handle_verify_equivalence_returns_assertions() { + TEST(handle_verify_equivalence_returns_assertions); + json params = { + {"functionName", "sort_items"}, + {"sourceCode", "sort values"}, + {"targetCode", "items.sort()"}, + {"sourceLanguage", "python"}, + {"targetLanguage", "rust"} + }; + auto r = TranspilationRPCHandler::handleVerifyEquivalence(params); + CHECK(r["functionName"] == "sort_items", "wrong function"); + CHECK(r["assertions"].size() >= 1, "expected assertions"); + CHECK(r.contains("annotation"), "missing annotation"); + PASS(); +} + +void test_handle_get_confidence_returns_score() { + TEST(handle_get_confidence_returns_score); + json params = { + {"functionName", "sort_items"}, + {"sourceCode", "sort values"}, + {"targetCode", "items.sort()"}, + {"sourceLanguage", "python"}, + {"targetLanguage", "rust"}, + {"hasIntent", true} + }; + auto r = TranspilationRPCHandler::handleGetConfidence(params); + CHECK(r["score"].get() >= 0.90f, "expected high confidence"); + CHECK(!r["reviewRequired"].get(), "high confidence should not need review"); + PASS(); +} + +void test_dispatch_wraps_result_in_jsonrpc_shape() { + TEST(dispatch_wraps_result_in_jsonrpc_shape); + json params = { + {"functionName", "sort_items"}, + {"intent", "sort list ascending"}, + {"source", "items.sort()"} + }; + auto r = TranspilationRPCHandler::dispatch("transpile", params, 7); + CHECK(r["jsonrpc"] == "2.0", "wrong jsonrpc version"); + CHECK(r["id"] == 7, "wrong id"); + CHECK(r.contains("result"), "missing result"); + PASS(); +} + +void test_dispatch_unknown_method_returns_error() { + TEST(dispatch_unknown_method_returns_error); + auto r = TranspilationRPCHandler::dispatch("unknownMethod", json::object(), 8); + CHECK(r.contains("error"), "missing error"); + CHECK(r["error"]["code"] == -32601, "wrong error code"); + PASS(); +} + +void test_tool_definitions_contains_four_tools() { + TEST(tool_definitions_contains_four_tools); + auto defs = TranspilationRPCHandler::toolDefinitions(); + CHECK(defs.size() == 4, "expected 4 tool defs"); + CHECK(defs[0].name == "whetstone_transpile", "first tool mismatch"); + CHECK(defs[1].name == "whetstone_get_translation_report", "second tool mismatch"); + PASS(); +} + +void test_tool_schemas_have_required_fields() { + TEST(tool_schemas_have_required_fields); + auto defs = TranspilationRPCHandler::toolDefinitions(); + CHECK(defs[0].inputSchema.contains("required"), "missing required on transpile schema"); + CHECK(defs[2].inputSchema.contains("required"), "missing required on equivalence schema"); + PASS(); +} + +void test_transpile_rpc_without_intent_marks_review() { + TEST(transpile_rpc_without_intent_marks_review); + json params = { + {"functionName", "mystery"}, + {"intent", ""}, + {"source", "x = a + b"}, + {"sourceLanguage", "python"}, + {"targetLanguage", "rust"} + }; + auto r = TranspilationRPCHandler::handleTranspile(params); + CHECK(r["needsReview"].get(), "missing review flag for no-intent case"); + PASS(); +} + +void test_confidence_rpc_low_score_requests_review() { + TEST(confidence_rpc_low_score_requests_review); + json params = { + {"functionName", "unknown"}, + {"sourceCode", "custom op"}, + {"targetCode", "TODO: translate"}, + {"sourceLanguage", "python"}, + {"targetLanguage", "rust"}, + {"hasIntent", false} + }; + auto r = TranspilationRPCHandler::handleGetConfidence(params); + CHECK(r["score"].get() <= 0.30f, "expected low confidence"); + CHECK(r["reviewRequired"].get(), "expected review requirement"); + PASS(); +} + +void test_equivalence_rpc_uses_custom_inputs() { + TEST(equivalence_rpc_uses_custom_inputs); + json params = { + {"functionName", "custom"}, + {"sourceCode", "custom op"}, + {"targetCode", "custom_op(data)"}, + {"sourceLanguage", "python"}, + {"targetLanguage", "java"}, + {"sampleInputs", json::array({"A", "B"})} + }; + auto r = TranspilationRPCHandler::handleVerifyEquivalence(params); + CHECK(r["assertions"].size() == 2, "expected two custom assertions"); + PASS(); +} + +int main() { + std::cout << "Step 458: Transpilation RPC + MCP Tests\n"; + + test_can_handle_all_methods(); // 1 + test_handle_transpile_returns_target_code(); // 2 + test_handle_translation_report_returns_summary(); // 3 + test_handle_verify_equivalence_returns_assertions(); // 4 + test_handle_get_confidence_returns_score(); // 5 + test_dispatch_wraps_result_in_jsonrpc_shape(); // 6 + test_dispatch_unknown_method_returns_error(); // 7 + test_tool_definitions_contains_four_tools(); // 8 + test_tool_schemas_have_required_fields(); // 9 + test_transpile_rpc_without_intent_marks_review(); // 10 + test_confidence_rpc_low_score_requests_review(); // 11 + test_equivalence_rpc_uses_custom_inputs(); // 12 + + std::cout << "\nResults: " << passed << "/" << (passed + failed) + << " passed\n"; + return failed == 0 ? 0 : 1; +} diff --git a/progress.md b/progress.md index 6e18b9d..c67d96d 100644 --- a/progress.md +++ b/progress.md @@ -5941,3 +5941,40 @@ and review-flag rollups. **Architecture gate check:** - `editor/src/TranslationReport.h` within header-size limit (`174` <= `600`) - `editor/tests/step457_test.cpp` within test-file size guidance (`168` lines) + +### Step 458: Transpilation RPC + MCP +**Status:** PASS (12/12 tests) + +Exposes semantic transpilation/reporting/equivalence/confidence as JSON-RPC +handlers and MCP tool definitions. + +**Files added:** +- `editor/src/TranspilationRPC.h` — RPC + MCP handler: + - JSON handlers: + - `transpile` + - `getTranslationReport` + - `verifyEquivalence` + - `getConfidence` + - `canHandle` + `dispatch` integration shape (JSON-RPC 2.0 envelope) + - MCP tool definitions: + - `whetstone_transpile` + - `whetstone_get_translation_report` + - `whetstone_verify_equivalence` + - `whetstone_get_confidence` + - JSON serialization helpers for confidence/report/equivalence payloads +- `editor/tests/step458_test.cpp` — 12 tests covering: + - handler method coverage and dispatch behavior + - result schema/content validation for each RPC method + - unknown-method error envelope + - MCP tool definition count/names/schema-required fields + - low-confidence review and no-intent review behavior +- `editor/CMakeLists.txt` — `step458_test` target + +**Verification run:** +- `cmake --build editor/build-native --target step458_test` — PASS +- `./editor/build-native/step458_test` — PASS (12/12) +- `./editor/build-native/step457_test` — PASS (12/12) regression coverage + +**Architecture gate check:** +- `editor/src/TranspilationRPC.h` within header-size limit (`255` <= `600`) +- `editor/tests/step458_test.cpp` within test-file size guidance (`193` lines)