Files
whetstone_DSL/editor/tests/step1887_test.cpp
Bill d98fc26c05 Sprint 271: LanguageFitnessScorer — polyglot Phase 1 (steps 1883-1887)
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>
2026-03-01 13:55:38 -07:00

138 lines
5.5 KiB
C++

// Step 1887: Sprint 271 Integration — end-to-end spec section → features → scores → ranked list
//
// t1: supervision-tree spec section → Elixir ranked #1
// t2: data-crunching AST → Rust in top-2
// t3: event-loop web server → TypeScript or Go in top-3
// t4: pure math spec → Haskell or Lisp in top-2
// t5: profile count is 8 and result is sorted; rationale non-empty on mismatched profiles
#include "ASTFeatureExtractor.h"
#include "LanguageIdiomProfile.h"
#include "LanguageFitnessScorer.h"
#include <nlohmann/json.hpp>
#include <iostream>
#include <string>
#include <algorithm>
using json = nlohmann::json;
using AFF = whetstone::ASTFeatures;
static int p=0,f=0;
#define T(n) { std::cout<<" "<<#n<<"... "; }
#define P() { std::cout<<"PASS\n"; ++p; }
#define F(m) { std::cout<<"FAIL: "<<m<<"\n"; ++f; }
#define C(c,m) if(!(c)){F(m);return;}
static int rankOf(const json& arr, const std::string& lang) {
for (int i = 0; i < (int)arr.size(); ++i)
if (arr[i]["language"] == lang) return i;
return -1;
}
void t1(){
T(supervision_tree_spec_elixir_first);
// Supervision tree: actors + event-driven + tail recursive
auto ast = json::parse(R"({
"type": "module",
"children": [
{"type": "actor_spawn", "children": []},
{"type": "receive_msg", "children": []},
{"type": "return_stmt", "children": [
{"type": "call_expr", "children": []}
]},
{"type": "emit_event", "children": []}
]
})");
auto feat = whetstone::ASTFeatureExtractor::extract(ast);
C(feat.concurrencyPrimitive == AFF::ConcurrencyPrimitive::Actors,
"actors not detected");
C(feat.ioPattern == AFF::IOPattern::EventDriven,
"event-driven not detected");
auto ranked = whetstone::LanguageFitnessScorer::score(feat);
C(ranked[0]["language"] == "Elixir",
"Elixir should rank #1, got: " + ranked[0]["language"].get<std::string>());
P();
}
void t2(){
T(data_crunching_rust_in_top2);
// Data crunch: shared memory, tree-recursive calls, low mutation, async, generics
AFF feat;
feat.mutationRatio = 0.1f;
feat.recursionShape = AFF::RecursionShape::TreeRecursive;
feat.concurrencyPrimitive = AFF::ConcurrencyPrimitive::SharedMemory;
feat.ioPattern = AFF::IOPattern::Async;
feat.typeComplexity = AFF::TypeComplexity::SimpleGenerics;
auto ranked = whetstone::LanguageFitnessScorer::score(feat);
int rustRank = rankOf(ranked, "Rust");
C(rustRank != -1 && rustRank < 2, "Rust should be in top-2 for data-crunch spec, rank=" + std::to_string(rustRank));
P();
}
void t3(){
T(event_loop_ts_or_go_top3);
// Event-loop web server: FlatLoop, async I/O, no concurrency, simple generics
AFF feat;
feat.mutationRatio = 0.3f;
feat.recursionShape = AFF::RecursionShape::FlatLoop;
feat.concurrencyPrimitive = AFF::ConcurrencyPrimitive::None;
feat.ioPattern = AFF::IOPattern::Async;
feat.typeComplexity = AFF::TypeComplexity::SimpleGenerics;
auto ranked = whetstone::LanguageFitnessScorer::score(feat);
int tsRank = rankOf(ranked, "TypeScript");
int goRank = rankOf(ranked, "Go");
bool eitherTop3 = (tsRank != -1 && tsRank < 3) || (goRank != -1 && goRank < 3);
C(eitherTop3, "TypeScript or Go should be in top-3 for event-loop spec, ts=" + std::to_string(tsRank) + " go=" + std::to_string(goRank));
P();
}
void t4(){
T(pure_math_haskell_or_lisp_top2);
// Pure math: no mutation, tail recursive, no concurrency, no I/O, possibly dependent types
AFF feat;
feat.mutationRatio = 0.0f;
feat.recursionShape = AFF::RecursionShape::TailRecursive;
feat.concurrencyPrimitive = AFF::ConcurrencyPrimitive::None;
feat.ioPattern = AFF::IOPattern::None;
feat.typeComplexity = AFF::TypeComplexity::None;
auto ranked = whetstone::LanguageFitnessScorer::score(feat);
int haskellRank = rankOf(ranked, "Haskell");
int lispRank = rankOf(ranked, "Lisp");
bool eitherTop2 = (haskellRank != -1 && haskellRank < 2) || (lispRank != -1 && lispRank < 2);
C(eitherTop2, "Haskell or Lisp should be in top-2 for pure math, haskell=" + std::to_string(haskellRank) + " lisp=" + std::to_string(lispRank));
P();
}
void t5(){
T(profile_count_sorted_rationale_non_empty_on_mismatch);
AFF feat;
feat.mutationRatio = 0.5f; // unusual value
feat.recursionShape = AFF::RecursionShape::TreeRecursive;
feat.concurrencyPrimitive = AFF::ConcurrencyPrimitive::Actors;
feat.ioPattern = AFF::IOPattern::Blocking;
feat.typeComplexity = AFF::TypeComplexity::DependentTypes;
auto ranked = whetstone::LanguageFitnessScorer::score(feat);
C(ranked.size() == 8, "must return 8 profiles, got " + std::to_string(ranked.size()));
// Verify sorted descending
for (size_t i = 1; i < ranked.size(); ++i) {
float prev = ranked[i-1]["score"].get<float>();
float curr = ranked[i]["score"].get<float>();
C(prev >= curr, "not sorted at index " + std::to_string(i));
}
// At least some entries should have non-empty rationale (since the feat is unusual)
int rationales = 0;
for (const auto& e : ranked) {
if (!e["rationale"].get<std::string>().empty() &&
e["rationale"].get<std::string>() != "ideal match") ++rationales;
}
C(rationales > 0, "expected some non-empty rationale for mismatched features");
P();
}
int main(){
std::cout << "Step 1887: Sprint 271 Integration\n";
t1(); t2(); t3(); t4(); t5();
std::cout << "\n" << p << "/" << (p+f) << " passed\n";
return f > 0 ? 1 : 0;
}