Add language fitness scoring infrastructure for the polyglot orchestrator. New components: - ASTFeatureExtractor.h: extract 5 scalar features from JSON AST subtrees (mutation ratio, recursion shape, concurrency primitive, I/O pattern, type complexity) - LanguageIdiomProfile.h: static ideal feature vectors for 8 languages (Python, Rust, Go, Haskell, C++, TypeScript, Elixir, Lisp) - LanguageFitnessScorer.h: weighted scoring against profiles, returns JSON array sorted descending with per-language score and rationale - RegisterLanguageFitnessTools.h: whetstone_score_language_fitness MCP tool - Sprint271IntegrationSummary.h: sprint metadata 25/25 tests passing (steps 1883-1887). whetstone_mcp rebuilt with new tool. Also: merge PROGRESS.md into progress.md (single authoritative log, 15k lines), update CLAUDE.md to current state (step 1887, sprint 271, 91 tools). LoRA session recorded: sprint271-polyglot-fitness-2026-03-01 (5 tool calls: architect_intake, generate_taskitems, 3x generate_code) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
79 lines
2.3 KiB
C++
79 lines
2.3 KiB
C++
#pragma once
|
|
#include "ASTFeatureExtractor.h"
|
|
#include "LanguageIdiomProfile.h"
|
|
#include <nlohmann/json.hpp>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <sstream>
|
|
|
|
namespace whetstone {
|
|
|
|
struct FitnessResult {
|
|
std::string language;
|
|
float score;
|
|
std::string rationale;
|
|
};
|
|
|
|
class LanguageFitnessScorer {
|
|
public:
|
|
// Returns JSON array [{language, score, rationale}, ...] sorted descending
|
|
static nlohmann::json score(const ASTFeatures& features) {
|
|
auto profiles = LanguageIdiomProfile::allProfiles();
|
|
std::vector<FitnessResult> results;
|
|
results.reserve(profiles.size());
|
|
for (const auto& prof : profiles) {
|
|
results.push_back(scoreOne(features, prof));
|
|
}
|
|
std::sort(results.begin(), results.end(),
|
|
[](const FitnessResult& a, const FitnessResult& b){
|
|
return a.score > b.score;
|
|
});
|
|
nlohmann::json arr = nlohmann::json::array();
|
|
for (const auto& r : results) {
|
|
arr.push_back({{"language", r.language},
|
|
{"score", r.score},
|
|
{"rationale", r.rationale}});
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
private:
|
|
static FitnessResult scoreOne(const ASTFeatures& f, const LanguageIdiomProfile& p) {
|
|
float s = 100.0f;
|
|
std::ostringstream rat;
|
|
|
|
float mutDelta = std::abs(f.mutationRatio - p.idealMutationRatio) * 30.0f;
|
|
s -= mutDelta;
|
|
if (mutDelta > 0.5f) rat << "mutation-" << static_cast<int>(mutDelta) << " ";
|
|
|
|
if (f.recursionShape != p.idealRecursion) {
|
|
s -= 20.0f;
|
|
rat << "recursion-mismatch ";
|
|
}
|
|
if (f.concurrencyPrimitive != p.idealConcurrency) {
|
|
s -= 25.0f;
|
|
rat << "concurrency-mismatch ";
|
|
}
|
|
if (f.ioPattern != p.idealIO) {
|
|
s -= 20.0f;
|
|
rat << "io-mismatch ";
|
|
}
|
|
if (f.typeComplexity != p.idealTypeComplexity) {
|
|
s -= 15.0f;
|
|
rat << "type-mismatch ";
|
|
}
|
|
|
|
s = std::max(0.0f, std::min(100.0f, s));
|
|
|
|
std::string ratStr = rat.str();
|
|
if (ratStr.empty()) ratStr = "ideal match";
|
|
if (!ratStr.empty() && ratStr.back() == ' ') ratStr.pop_back();
|
|
|
|
return {p.language, s, ratStr};
|
|
}
|
|
};
|
|
|
|
} // namespace whetstone
|