Complete step457 translation report generation with tests

This commit is contained in:
Bill
2026-02-16 20:01:38 -07:00
parent 4491e487a7
commit 339389df24
4 changed files with 382 additions and 0 deletions

View File

@@ -2992,4 +2992,13 @@ target_link_libraries(step456_test PRIVATE
tree_sitter_javascript tree_sitter_typescript
tree_sitter_java tree_sitter_rust tree_sitter_go)
add_executable(step457_test tests/step457_test.cpp)
target_include_directories(step457_test PRIVATE src)
target_link_libraries(step457_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)

View File

@@ -0,0 +1,174 @@
#pragma once
// Step 457: Translation Report — per-function and project-level reporting for
// transpilation choices, confidence, annotation changes, and review flags.
#include "IntentTranslator.h"
#include "TranspilationConfidence.h"
#include <string>
#include <vector>
struct AnnotationDelta {
std::vector<std::string> added;
std::vector<std::string> removed;
std::vector<std::string> changed;
};
struct FunctionTranslationReport {
std::string functionName;
std::string sourceLanguage;
std::string targetLanguage;
std::string sourceConstruct;
std::string targetConstruct;
float confidence = 0.0f;
bool idiomatic = false;
bool reviewRequired = false;
std::string reviewReason;
std::string safetyDelta; // gained/lost/neutral
AnnotationDelta annotations;
std::string rationale;
};
struct ProjectTranslationSummary {
int totalFunctions = 0;
int idiomaticCount = 0;
int literalCount = 0;
int flaggedForReviewCount = 0;
float idiomaticPercent() const {
return totalFunctions == 0 ? 0.0f :
(100.0f * static_cast<float>(idiomaticCount) / static_cast<float>(totalFunctions));
}
float literalPercent() const {
return totalFunctions == 0 ? 0.0f :
(100.0f * static_cast<float>(literalCount) / static_cast<float>(totalFunctions));
}
float reviewPercent() const {
return totalFunctions == 0 ? 0.0f :
(100.0f * static_cast<float>(flaggedForReviewCount) / static_cast<float>(totalFunctions));
}
};
struct TranslationReport {
std::string sourceLanguage;
std::string targetLanguage;
std::vector<FunctionTranslationReport> functions;
ProjectTranslationSummary summary;
bool hasFunction(const std::string& functionName) const {
for (const auto& fn : functions)
if (fn.functionName == functionName) return true;
return false;
}
FunctionTranslationReport getFunction(const std::string& functionName) const {
for (const auto& fn : functions)
if (fn.functionName == functionName) return fn;
return {};
}
};
class TranslationReportGenerator {
public:
static TranslationReport generate(
const IntentTranslationResult& translations,
const std::vector<bool>& hasIntents = {}) {
TranslationReport report;
report.sourceLanguage = translations.sourceLanguage;
report.targetLanguage = translations.targetLanguage;
for (size_t i = 0; i < translations.translations.size(); ++i) {
const auto& choice = translations.translations[i];
bool hasIntent = choice.intent.size() > 0;
if (i < hasIntents.size()) hasIntent = hasIntents[i];
auto score = TranspilationScorer::score(
choice.functionName,
choice.sourceCode,
choice.targetCode,
translations.sourceLanguage,
translations.targetLanguage,
hasIntent);
FunctionTranslationReport fn;
fn.functionName = choice.functionName;
fn.sourceLanguage = choice.sourceLanguage;
fn.targetLanguage = choice.targetLanguage;
fn.sourceConstruct = classifyConstruct(choice.sourceCode);
fn.targetConstruct = classifyConstruct(choice.targetCode);
fn.confidence = score.score;
fn.idiomatic = choice.isIdiomatic;
fn.reviewRequired = choice.needsReview || score.reviewRequired;
fn.reviewReason = !choice.reviewReason.empty() ? choice.reviewReason : score.reviewReason;
fn.safetyDelta = inferSafetyDelta(choice.sourceLanguage, choice.targetLanguage, choice.targetCode);
fn.annotations = inferAnnotationDelta(choice, score);
fn.rationale = buildRationale(choice, score);
report.functions.push_back(std::move(fn));
}
report.summary = computeSummary(report.functions);
return report;
}
private:
static std::string classifyConstruct(const std::string& code) {
if (code.find("sort") != std::string::npos) return "sort";
if (code.find("find") != std::string::npos || code.find("lookup") != std::string::npos) return "lookup";
if (code.find("filter") != std::string::npos) return "filter";
if (code.find("map") != std::string::npos || code.find("transform") != std::string::npos) return "transform";
if (code.find("fold") != std::string::npos || code.find("sum") != std::string::npos ||
code.find("accumulate") != std::string::npos) return "reduce";
if (code.find("read") != std::string::npos || code.find("open(") != std::string::npos) return "io";
if (code.find("TODO") != std::string::npos) return "unknown";
return "structural";
}
static std::string inferSafetyDelta(const std::string& srcLang,
const std::string& tgtLang,
const std::string& targetCode) {
if (srcLang == "c" && (tgtLang == "rust" || tgtLang == "java" || tgtLang == "python"))
return "gained";
if (srcLang == "rust" && tgtLang == "c")
return "lost";
if (targetCode.find("TODO") != std::string::npos)
return "neutral";
return "neutral";
}
static AnnotationDelta inferAnnotationDelta(const TranslationChoice& choice,
const ConfidenceScore& score) {
AnnotationDelta d;
d.added.push_back(score.annotation);
if (choice.isIdiomatic) d.added.push_back("@Idiomatic(true)");
else d.added.push_back("@Idiomatic(false)");
if (choice.needsReview || score.reviewRequired)
d.added.push_back("@Review(required,human)");
return d;
}
static std::string buildRationale(const TranslationChoice& choice,
const ConfidenceScore& score) {
if (choice.isIdiomatic)
return "Selected idiomatic target construct for intent: " + choice.intent;
if (score.category == TranslationCategory::StructuralWithIntent)
return "Intent present but no direct idiomatic mapping; structural translation used";
if (score.category == TranslationCategory::StructuralNoIntent)
return "No explicit intent; literal/structural translation preserved";
return "Unrecognized translation shape; review recommended";
}
static ProjectTranslationSummary computeSummary(
const std::vector<FunctionTranslationReport>& functions) {
ProjectTranslationSummary s;
s.totalFunctions = static_cast<int>(functions.size());
for (const auto& fn : functions) {
if (fn.idiomatic) ++s.idiomaticCount;
else ++s.literalCount;
if (fn.reviewRequired) ++s.flaggedForReviewCount;
}
return s;
}
};

View File

@@ -0,0 +1,168 @@
// Step 457: Translation Report Tests (12 tests)
#include "IntentTranslator.h"
#include "TranslationReport.h"
#include <iostream>
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 IntentTranslationResult makeSampleTranslations() {
std::vector<FunctionIntent> funcs = {
{"sort_items", "sort list ascending", "items.sort()"},
{"find_user", "find element by key", "users.get(key)"},
{"mystery", "", "x = a + b * c"}
};
return IntentTranslator::translate(funcs, "python", "rust");
}
void test_report_contains_all_functions() {
TEST(report_contains_all_functions);
auto tr = makeSampleTranslations();
auto report = TranslationReportGenerator::generate(tr);
CHECK(report.functions.size() == 3, "expected 3 function reports");
CHECK(report.hasFunction("sort_items"), "missing sort_items");
CHECK(report.hasFunction("find_user"), "missing find_user");
CHECK(report.hasFunction("mystery"), "missing mystery");
PASS();
}
void test_source_and_target_languages_propagated() {
TEST(source_and_target_languages_propagated);
auto tr = makeSampleTranslations();
auto report = TranslationReportGenerator::generate(tr);
CHECK(report.sourceLanguage == "python", "wrong source language");
CHECK(report.targetLanguage == "rust", "wrong target language");
PASS();
}
void test_idiomatic_and_literal_counts() {
TEST(idiomatic_and_literal_counts);
auto tr = makeSampleTranslations();
auto report = TranslationReportGenerator::generate(tr);
CHECK(report.summary.totalFunctions == 3, "wrong total");
CHECK(report.summary.idiomaticCount == 2, "expected 2 idiomatic");
CHECK(report.summary.literalCount == 1, "expected 1 literal");
PASS();
}
void test_review_flag_count_includes_no_intent() {
TEST(review_flag_count_includes_no_intent);
auto tr = makeSampleTranslations();
auto report = TranslationReportGenerator::generate(tr);
CHECK(report.summary.flaggedForReviewCount == 1, "expected one review-required function");
CHECK(report.getFunction("mystery").reviewRequired, "mystery should require review");
PASS();
}
void test_construct_classification_detects_sort() {
TEST(construct_classification_detects_sort);
auto tr = makeSampleTranslations();
auto report = TranslationReportGenerator::generate(tr);
auto fn = report.getFunction("sort_items");
CHECK(fn.targetConstruct == "sort", "expected sort construct");
PASS();
}
void test_annotation_delta_includes_confidence_and_idiomatic() {
TEST(annotation_delta_includes_confidence_and_idiomatic);
auto tr = makeSampleTranslations();
auto report = TranslationReportGenerator::generate(tr);
auto fn = report.getFunction("sort_items");
CHECK(!fn.annotations.added.empty(), "expected added annotations");
bool hasConfidence = false;
bool hasIdiomatic = false;
for (const auto& a : fn.annotations.added) {
if (a.find("@Confidence(") != std::string::npos) hasConfidence = true;
if (a.find("@Idiomatic(") != std::string::npos) hasIdiomatic = true;
}
CHECK(hasConfidence, "expected confidence annotation");
CHECK(hasIdiomatic, "expected idiomatic annotation");
PASS();
}
void test_rationale_mentions_idiomatic_when_applicable() {
TEST(rationale_mentions_idiomatic_when_applicable);
auto tr = makeSampleTranslations();
auto report = TranslationReportGenerator::generate(tr);
auto fn = report.getFunction("find_user");
CHECK(fn.rationale.find("idiomatic") != std::string::npos, "expected idiomatic rationale");
PASS();
}
void test_safety_delta_c_to_rust_gained() {
TEST(safety_delta_c_to_rust_gained);
std::vector<FunctionIntent> funcs = {{"sum_values", "sum list", "total += x"}};
auto tr = IntentTranslator::translate(funcs, "c", "rust");
auto report = TranslationReportGenerator::generate(tr);
auto fn = report.getFunction("sum_values");
CHECK(fn.safetyDelta == "gained", "expected gained safety delta for c->rust");
PASS();
}
void test_safety_delta_rust_to_c_lost() {
TEST(safety_delta_rust_to_c_lost);
std::vector<FunctionIntent> funcs = {{"sum_values", "sum list", "values.iter().fold(0, |a, x| a + x)"}};
auto tr = IntentTranslator::translate(funcs, "rust", "c");
auto report = TranslationReportGenerator::generate(tr);
auto fn = report.getFunction("sum_values");
CHECK(fn.safetyDelta == "lost", "expected lost safety delta for rust->c");
PASS();
}
void test_percentages_are_computed() {
TEST(percentages_are_computed);
auto tr = makeSampleTranslations();
auto report = TranslationReportGenerator::generate(tr);
CHECK(report.summary.idiomaticPercent() > 66.0f && report.summary.idiomaticPercent() < 67.0f,
"expected idiomatic percent ~66.67");
CHECK(report.summary.literalPercent() > 33.0f && report.summary.literalPercent() < 34.0f,
"expected literal percent ~33.33");
PASS();
}
void test_get_function_returns_empty_for_missing_name() {
TEST(get_function_returns_empty_for_missing_name);
auto tr = makeSampleTranslations();
auto report = TranslationReportGenerator::generate(tr);
auto fn = report.getFunction("does_not_exist");
CHECK(fn.functionName.empty(), "missing function should return empty report entry");
PASS();
}
void test_has_intent_override_affects_confidence_path() {
TEST(has_intent_override_affects_confidence_path);
std::vector<FunctionIntent> funcs = {{"custom", "", "custom_transform(data)"}};
auto tr = IntentTranslator::translate(funcs, "python", "java");
auto reportNoIntent = TranslationReportGenerator::generate(tr, {false});
auto reportWithIntent = TranslationReportGenerator::generate(tr, {true});
auto a = reportNoIntent.getFunction("custom");
auto b = reportWithIntent.getFunction("custom");
CHECK(a.confidence != b.confidence, "intent override should affect confidence class");
PASS();
}
int main() {
std::cout << "Step 457: Translation Report Tests\n";
test_report_contains_all_functions(); // 1
test_source_and_target_languages_propagated(); // 2
test_idiomatic_and_literal_counts(); // 3
test_review_flag_count_includes_no_intent(); // 4
test_construct_classification_detects_sort(); // 5
test_annotation_delta_includes_confidence_and_idiomatic(); // 6
test_rationale_mentions_idiomatic_when_applicable(); // 7
test_safety_delta_c_to_rust_gained(); // 8
test_safety_delta_rust_to_c_lost(); // 9
test_percentages_are_computed(); // 10
test_get_function_returns_empty_for_missing_name(); // 11
test_has_intent_override_affects_confidence_path(); // 12
std::cout << "\nResults: " << passed << "/" << (passed + failed)
<< " passed\n";
return failed == 0 ? 0 : 1;
}

View File

@@ -5910,3 +5910,34 @@ translated functions, including aggregated report statistics.
**Architecture gate check:**
- `editor/src/TranspilationConfidence.h` within header-size limit (`153` <= `600`)
- `editor/tests/step456_test.cpp` within test-file size guidance (`155` lines)
### Step 457: Translation Report
**Status:** PASS (12/12 tests)
Adds per-function and project-level transpilation reporting, including mapping
shape, confidence, annotation deltas, safety delta, idiomatic/literal split,
and review-flag rollups.
**Files added:**
- `editor/src/TranslationReport.h` — report generator:
- function-level report model with construct mapping, confidence, rationale,
safety delta, and annotation-change tracking
- project summary metrics: total, idiomatic/literal counts, review flags,
and percentage rollups
- integration with intent translation + confidence scorer
- `editor/tests/step457_test.cpp` — 12 tests covering:
- per-function presence and retrieval behavior
- summary counts and percentage calculations
- annotation/rationale generation
- safety delta behavior for `c→rust` and `rust→c`
- intent override impact on confidence path
- `editor/CMakeLists.txt``step457_test` target
**Verification run:**
- `cmake --build editor/build-native --target step457_test` — PASS
- `./editor/build-native/step457_test` — PASS (12/12)
- `./editor/build-native/step456_test` — PASS (12/12) regression coverage
**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)