Step 1991: Wire AST sidecar lifecycle hooks - openFile (DispatchPart2.h): auto-calls loadSidecarAST() after parse, returns annotationsRestored + staleAnnotations - saveBuffer (DispatchPart3.h): auto-calls saveSidecarAST() + saveSemannoSidecar() after write, returns annotationsSaved + sidecarPath + semannoPath Step 1992: Fix 9 schema stubs in Sprint 142/143/144 tool registrations - All 9 text/AST sync and merge tools now have proper inputSchema with required id and documented optional parameters (language, text, mode) Step 1993: Fix ingest_legacy_to_ir → generate_cpp_from_ir chain - ingest_legacy_to_ir now emits ir field (SemanticCoreIR format) alongside existing graph/api_intent/assumption/readiness fields - RecoveryNode → IRNode mapping: intent→kind, confidence in metadata - Neighbor lists become inferred edges; moduleId = "legacy:" + slug - generate_cpp_from_ir now accepts result["ir"] directly 15/15 tests passing across all three steps. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
139 lines
5.5 KiB
C++
139 lines
5.5 KiB
C++
// Step 1993: ingest_legacy_to_ir → generate_cpp_from_ir chain fix
|
|
// Verifies that ingest_legacy_to_ir emits a valid SemanticCoreIR "ir" field
|
|
// that can be passed directly to generate_cpp_from_ir without transformation.
|
|
|
|
#include "MCPServer.h"
|
|
#include "SemanticCoreIR.h"
|
|
|
|
#include <cassert>
|
|
#include <iostream>
|
|
#include <string>
|
|
|
|
static json callTool(MCPServer& srv, const std::string& name, json params = json::object()) {
|
|
json req = {{"jsonrpc", "2.0"}, {"id", 1},
|
|
{"method", "tools/call"},
|
|
{"params", {{"name", name}, {"arguments", std::move(params)}}}};
|
|
return srv.handleRequest(req);
|
|
}
|
|
|
|
static bool isSuccess(const json& resp) {
|
|
return resp.contains("result") &&
|
|
resp["result"].contains("isError") &&
|
|
resp["result"]["isError"] == false;
|
|
}
|
|
|
|
// Parse the content text back to json for deeper inspection.
|
|
static json resultJson(const json& resp) {
|
|
if (!resp.contains("result") || !resp["result"].contains("content")) return json::object();
|
|
const auto& content = resp["result"]["content"];
|
|
if (!content.is_array() || content.empty()) return json::object();
|
|
const std::string text = content[0].value("text", "{}");
|
|
return json::parse(text, nullptr, false);
|
|
}
|
|
|
|
int main() {
|
|
int passed = 0;
|
|
|
|
MCPServer srv;
|
|
|
|
// ----------------------------------------------------------------
|
|
// Test 1: ingest_legacy_to_ir returns an "ir" field
|
|
// ----------------------------------------------------------------
|
|
{
|
|
auto resp = callTool(srv, "whetstone_ingest_legacy_to_ir",
|
|
{{"source", "MyLegacyModule"}, {"language", "cpp"}});
|
|
assert(isSuccess(resp));
|
|
json r = resultJson(resp);
|
|
assert(r["success"] == true);
|
|
assert(r.contains("ir"));
|
|
assert(r["ir"].is_object());
|
|
std::cout << "Test 1 PASSED: ingest_legacy_to_ir emits ir field\n";
|
|
++passed;
|
|
}
|
|
|
|
// ----------------------------------------------------------------
|
|
// Test 2: ir field is a valid SemanticCoreIR (parses + validates)
|
|
// ----------------------------------------------------------------
|
|
{
|
|
auto resp = callTool(srv, "whetstone_ingest_legacy_to_ir",
|
|
{{"source", "MyLegacyModule"}, {"language", "cpp"}});
|
|
json r = resultJson(resp);
|
|
const json& irJson = r["ir"];
|
|
|
|
SemanticCoreIR ir;
|
|
std::string err;
|
|
bool parsed = SemanticCoreIRModel::fromJson(irJson, &ir, &err);
|
|
assert(parsed && "SemanticCoreIRModel::fromJson failed");
|
|
bool valid = SemanticCoreIRModel::validate(ir, &err);
|
|
assert(valid && "SemanticCoreIRModel::validate failed");
|
|
assert(!ir.moduleId.empty());
|
|
assert(!ir.nodes.empty());
|
|
std::cout << "Test 2 PASSED: ir field parses and validates as SemanticCoreIR\n";
|
|
++passed;
|
|
}
|
|
|
|
// ----------------------------------------------------------------
|
|
// Test 3: ir nodes have unique ids and known kinds
|
|
// ----------------------------------------------------------------
|
|
{
|
|
auto resp = callTool(srv, "whetstone_ingest_legacy_to_ir",
|
|
{{"source", "SomeSource"}, {"language", "cpp"}});
|
|
json r = resultJson(resp);
|
|
SemanticCoreIR ir;
|
|
std::string err;
|
|
SemanticCoreIRModel::fromJson(r["ir"], &ir, &err);
|
|
|
|
std::set<std::string> ids;
|
|
for (const auto& n : ir.nodes) {
|
|
assert(!n.id.empty());
|
|
assert(ids.insert(n.id).second && "duplicate node id");
|
|
assert(n.language == "cpp");
|
|
}
|
|
for (const auto& e : ir.edges) {
|
|
assert(!e.fromId.empty() && !e.toId.empty());
|
|
assert(e.relation == "inferred");
|
|
}
|
|
std::cout << "Test 3 PASSED: IR nodes have unique ids and valid edges\n";
|
|
++passed;
|
|
}
|
|
|
|
// ----------------------------------------------------------------
|
|
// Test 4: ir field passes directly to generate_cpp_from_ir
|
|
// ----------------------------------------------------------------
|
|
{
|
|
auto ingestResp = callTool(srv, "whetstone_ingest_legacy_to_ir",
|
|
{{"source", "MyLegacyModule"}, {"language", "cpp"}});
|
|
assert(isSuccess(ingestResp));
|
|
json ingestResult = resultJson(ingestResp);
|
|
|
|
auto genResp = callTool(srv, "whetstone_generate_cpp_from_ir",
|
|
{{"ir", ingestResult["ir"]}, {"profile", "safe-first"}});
|
|
assert(isSuccess(genResp));
|
|
json genResult = resultJson(genResp);
|
|
assert(genResult["success"] == true);
|
|
assert(genResult.contains("files"));
|
|
std::cout << "Test 4 PASSED: generate_cpp_from_ir accepts ir from ingest\n";
|
|
++passed;
|
|
}
|
|
|
|
// ----------------------------------------------------------------
|
|
// Test 5: existing ingest output fields are preserved
|
|
// ----------------------------------------------------------------
|
|
{
|
|
auto resp = callTool(srv, "whetstone_ingest_legacy_to_ir",
|
|
{{"source", "LegacyTodoModule TODO fix me"}, {"language", "cpp"}});
|
|
json r = resultJson(resp);
|
|
assert(r.contains("graph"));
|
|
assert(r.contains("api_intent"));
|
|
assert(r.contains("assumption"));
|
|
assert(r.contains("ambiguity"));
|
|
assert(r.contains("readiness"));
|
|
assert(r.contains("ir")); // new field alongside existing
|
|
std::cout << "Test 5 PASSED: existing fields preserved alongside ir\n";
|
|
++passed;
|
|
}
|
|
|
|
std::cout << "\n" << passed << "/5 passed\n";
|
|
return passed == 5 ? 0 : 1;
|
|
}
|