Sprint 287: PerTaskLibrarySelector (steps 1963-1967)
Deterministic library dispatch layer. Five new headers: - LibrarySelection: result struct with avoidedLibraries + justification - OperationClassifier: wraps taxonomy with ClassificationResult - PerTaskLibrarySelector: sort by score, pick max, build avoided list - SelectionJustificationLog: append-only auditable selection log - Sprint287IntegrationSummary: 8-task spec, all selections verified 25/25 tests passing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11099,3 +11099,18 @@ target_include_directories(step1961_test PRIVATE src)
|
|||||||
|
|
||||||
add_executable(step1962_test tests/step1962_test.cpp)
|
add_executable(step1962_test tests/step1962_test.cpp)
|
||||||
target_include_directories(step1962_test PRIVATE src)
|
target_include_directories(step1962_test PRIVATE src)
|
||||||
|
|
||||||
|
add_executable(step1963_test tests/step1963_test.cpp)
|
||||||
|
target_include_directories(step1963_test PRIVATE src)
|
||||||
|
|
||||||
|
add_executable(step1964_test tests/step1964_test.cpp)
|
||||||
|
target_include_directories(step1964_test PRIVATE src)
|
||||||
|
|
||||||
|
add_executable(step1965_test tests/step1965_test.cpp)
|
||||||
|
target_include_directories(step1965_test PRIVATE src)
|
||||||
|
|
||||||
|
add_executable(step1966_test tests/step1966_test.cpp)
|
||||||
|
target_include_directories(step1966_test PRIVATE src)
|
||||||
|
|
||||||
|
add_executable(step1967_test tests/step1967_test.cpp)
|
||||||
|
target_include_directories(step1967_test PRIVATE src)
|
||||||
|
|||||||
28
editor/src/LibrarySelection.h
Normal file
28
editor/src/LibrarySelection.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
// Step 1963: LibrarySelection
|
||||||
|
// Result struct from PerTaskLibrarySelector — fully specifies which library
|
||||||
|
// to use for a given operation domain and why.
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace whetstone {
|
||||||
|
|
||||||
|
struct AvoidedLibrary {
|
||||||
|
std::string name;
|
||||||
|
std::string reason; // e.g. "score 0.60 < selected 0.92"
|
||||||
|
float score = 0.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LibrarySelection {
|
||||||
|
std::string selectedLibrary; // empty if no library qualifies
|
||||||
|
std::string operationDomain;
|
||||||
|
float capabilityScore = 0.0f;
|
||||||
|
std::vector<std::string> preferredAPIs;
|
||||||
|
std::vector<AvoidedLibrary> avoidedLibraries; // sorted by score desc
|
||||||
|
std::string justification;
|
||||||
|
|
||||||
|
bool hasSelection() const { return !selectedLibrary.empty(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace whetstone
|
||||||
39
editor/src/OperationClassifier.h
Normal file
39
editor/src/OperationClassifier.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
// Step 1964: OperationClassifier
|
||||||
|
// Wraps OperationTaxonomy with a ClassificationResult that includes
|
||||||
|
// confidence and isKnown flags.
|
||||||
|
|
||||||
|
#include "OperationTaxonomy.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace whetstone {
|
||||||
|
|
||||||
|
struct ClassificationResult {
|
||||||
|
std::string domain; // e.g. "serialization.json" or "unknown"
|
||||||
|
float confidence; // 1.0 if known, 0.0 if "unknown"
|
||||||
|
bool isKnown;
|
||||||
|
};
|
||||||
|
|
||||||
|
class OperationClassifier {
|
||||||
|
public:
|
||||||
|
explicit OperationClassifier(OperationTaxonomy taxonomy)
|
||||||
|
: taxonomy_(std::move(taxonomy)) {}
|
||||||
|
|
||||||
|
ClassificationResult classify(const std::string& text) const {
|
||||||
|
auto domain = taxonomy_.classify(text);
|
||||||
|
bool known = (domain != "unknown");
|
||||||
|
return {domain, known ? 1.0f : 0.0f, known};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience: build with the default 40+ domain taxonomy
|
||||||
|
static OperationClassifier withDefaultTaxonomy() {
|
||||||
|
return OperationClassifier(OperationTaxonomy::buildDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
const OperationTaxonomy& taxonomy() const { return taxonomy_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
OperationTaxonomy taxonomy_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace whetstone
|
||||||
90
editor/src/PerTaskLibrarySelector.h
Normal file
90
editor/src/PerTaskLibrarySelector.h
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
#pragma once
|
||||||
|
// Step 1965: PerTaskLibrarySelector
|
||||||
|
// Deterministic library dispatch: given (operationDomain, availableLibraries, ledger)
|
||||||
|
// rank by score, pick max, build avoidedLibraries + justification.
|
||||||
|
|
||||||
|
#include "LibraryCapabilityLedger.h"
|
||||||
|
#include "LibrarySelection.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace whetstone {
|
||||||
|
|
||||||
|
class PerTaskLibrarySelector {
|
||||||
|
public:
|
||||||
|
// Core selection: deterministic, no inference.
|
||||||
|
LibrarySelection select(const std::string& operationDomain,
|
||||||
|
const std::vector<std::string>& availableLibraries,
|
||||||
|
const LibraryCapabilityLedger& ledger) const {
|
||||||
|
LibrarySelection result;
|
||||||
|
result.operationDomain = operationDomain;
|
||||||
|
|
||||||
|
if (availableLibraries.empty()) {
|
||||||
|
result.justification = "no library with score > 0";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Score all candidates
|
||||||
|
struct Candidate { std::string lib; float score; };
|
||||||
|
std::vector<Candidate> candidates;
|
||||||
|
candidates.reserve(availableLibraries.size());
|
||||||
|
for (const auto& lib : availableLibraries) {
|
||||||
|
candidates.push_back({lib, ledger.score(lib, operationDomain)});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stable sort by score descending (stable for deterministic tie-breaking)
|
||||||
|
std::stable_sort(candidates.begin(), candidates.end(),
|
||||||
|
[](const Candidate& a, const Candidate& b) {
|
||||||
|
return a.score > b.score;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (candidates[0].score == 0.0f) {
|
||||||
|
result.justification = "no library with score > 0";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Selected: highest score
|
||||||
|
result.selectedLibrary = candidates[0].lib;
|
||||||
|
result.capabilityScore = candidates[0].score;
|
||||||
|
|
||||||
|
// Preferred APIs from ledger record
|
||||||
|
auto rec = ledger.get(candidates[0].lib, operationDomain);
|
||||||
|
if (rec) result.preferredAPIs = rec->preferredAPIs;
|
||||||
|
|
||||||
|
// Avoided: remaining with score > 0, sorted desc (already sorted)
|
||||||
|
for (size_t i = 1; i < candidates.size(); ++i) {
|
||||||
|
if (candidates[i].score > 0.0f) {
|
||||||
|
result.avoidedLibraries.push_back({
|
||||||
|
candidates[i].lib,
|
||||||
|
"score " + fmt(candidates[i].score) + " < selected " + fmt(candidates[0].score),
|
||||||
|
candidates[i].score
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Justification string
|
||||||
|
if (result.avoidedLibraries.empty()) {
|
||||||
|
result.justification = result.selectedLibrary + " scores " +
|
||||||
|
fmt(result.capabilityScore) + " (only qualified library)";
|
||||||
|
} else {
|
||||||
|
result.justification = result.selectedLibrary + " scores " +
|
||||||
|
fmt(result.capabilityScore) + " vs best alternative " +
|
||||||
|
result.avoidedLibraries[0].name + " at " +
|
||||||
|
fmt(result.avoidedLibraries[0].score);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::string fmt(float s) {
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << std::fixed << std::setprecision(2) << s;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace whetstone
|
||||||
59
editor/src/SelectionJustificationLog.h
Normal file
59
editor/src/SelectionJustificationLog.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#pragma once
|
||||||
|
// Step 1966: SelectionJustificationLog
|
||||||
|
// Append-only auditable log of every library selection decision.
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace whetstone {
|
||||||
|
|
||||||
|
struct SelectionLogEntry {
|
||||||
|
std::string taskId;
|
||||||
|
std::string operationDomain;
|
||||||
|
std::string selectedLibrary; // empty if no selection was made
|
||||||
|
float capabilityScore = 0.0f;
|
||||||
|
std::string justification;
|
||||||
|
std::string timestamp; // ISO date/time string
|
||||||
|
};
|
||||||
|
|
||||||
|
class SelectionJustificationLog {
|
||||||
|
public:
|
||||||
|
void append(SelectionLogEntry entry) {
|
||||||
|
entries_.push_back(std::move(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<SelectionLogEntry>& entries() const { return entries_; }
|
||||||
|
|
||||||
|
size_t size() const { return entries_.size(); }
|
||||||
|
|
||||||
|
std::vector<SelectionLogEntry> findByDomain(const std::string& domain) const {
|
||||||
|
std::vector<SelectionLogEntry> result;
|
||||||
|
for (const auto& e : entries_) {
|
||||||
|
if (e.operationDomain == domain) result.push_back(e);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<SelectionLogEntry> findByLibrary(const std::string& lib) const {
|
||||||
|
std::vector<SelectionLogEntry> result;
|
||||||
|
for (const auto& e : entries_) {
|
||||||
|
if (e.selectedLibrary == lib) result.push_back(e);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<SelectionLogEntry> findByTask(const std::string& taskId) const {
|
||||||
|
std::vector<SelectionLogEntry> result;
|
||||||
|
for (const auto& e : entries_) {
|
||||||
|
if (e.taskId == taskId) result.push_back(e);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() { entries_.clear(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<SelectionLogEntry> entries_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace whetstone
|
||||||
60
editor/src/Sprint287IntegrationSummary.h
Normal file
60
editor/src/Sprint287IntegrationSummary.h
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#pragma once
|
||||||
|
// Step 1967: Sprint 287 Integration Summary
|
||||||
|
// Steps 1963–1967: PerTaskLibrarySelector
|
||||||
|
//
|
||||||
|
// 1963: LibrarySelection — result struct with avoidedLibraries + justification
|
||||||
|
// 1964: OperationClassifier — wraps taxonomy with ClassificationResult
|
||||||
|
// 1965: PerTaskLibrarySelector — deterministic max-score dispatch
|
||||||
|
// 1966: SelectionJustificationLog — append-only auditable selection log
|
||||||
|
// 1967: Integration — 8-task spec, all selections verified
|
||||||
|
|
||||||
|
#include "LibraryCapabilityLedger.h"
|
||||||
|
#include "PerTaskLibrarySelector.h"
|
||||||
|
#include "Sprint286IntegrationSummary.h"
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace whetstone {
|
||||||
|
|
||||||
|
struct Sprint287IntegrationSummary {
|
||||||
|
int stepsCompleted = 5;
|
||||||
|
bool success = true;
|
||||||
|
std::string sprintName() const {
|
||||||
|
return "Sprint 287: PerTaskLibrarySelector";
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Task {
|
||||||
|
std::string taskId;
|
||||||
|
std::string operationDomain;
|
||||||
|
std::vector<std::string> availableLibraries;
|
||||||
|
std::string expectedLibrary;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 8 tasks covering all seed libraries; each task has a verifiable expected selection.
|
||||||
|
static std::vector<Task> buildTaskSpec() {
|
||||||
|
return {
|
||||||
|
{"t1", "serialization.json", {"nlohmann_json", "protobuf"}, "nlohmann_json"},
|
||||||
|
{"t2", "compute.matrix", {"cublas", "numpy"}, "cublas"},
|
||||||
|
{"t3", "serialization.binary", {"protobuf", "nlohmann_json"}, "protobuf"},
|
||||||
|
{"t4", "compute.statistics", {"numpy"}, "numpy"},
|
||||||
|
{"t5", "network.async", {"tokio", "boost.asio"}, "tokio"},
|
||||||
|
{"t6", "compute.parallel", {"thrust"}, "thrust"},
|
||||||
|
{"t7", "io.stream", {"tokio", "boost.asio"}, "tokio"},
|
||||||
|
{"t8", "network.http.client", {"boost.asio", "tokio"}, "boost.asio"},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run selector over all 8 tasks; return true if all selections match expected.
|
||||||
|
static bool verifyAllSelections() {
|
||||||
|
auto ledger = Sprint286IntegrationSummary::buildSeedLedger();
|
||||||
|
PerTaskLibrarySelector selector;
|
||||||
|
for (const auto& task : buildTaskSpec()) {
|
||||||
|
auto sel = selector.select(task.operationDomain,
|
||||||
|
task.availableLibraries, ledger);
|
||||||
|
if (sel.selectedLibrary != task.expectedLibrary) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace whetstone
|
||||||
92
editor/tests/step1963_test.cpp
Normal file
92
editor/tests/step1963_test.cpp
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
// Step 1963: LibrarySelection
|
||||||
|
//
|
||||||
|
// t1: default LibrarySelection has no selection (hasSelection=false)
|
||||||
|
// t2: populated selection fields are correct
|
||||||
|
// t3: avoidedLibraries struct fields
|
||||||
|
// t4: multiple avoided libraries
|
||||||
|
// t5: selection with no avoidedLibraries (only one qualified)
|
||||||
|
|
||||||
|
#include "LibrarySelection.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace ws = whetstone;
|
||||||
|
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; }
|
||||||
|
|
||||||
|
void t1() {
|
||||||
|
T(default_selection_has_no_selection);
|
||||||
|
ws::LibrarySelection s;
|
||||||
|
C(!s.hasSelection(), "hasSelection false by default");
|
||||||
|
C(s.selectedLibrary.empty(), "empty selectedLibrary");
|
||||||
|
C(s.capabilityScore == 0.0f, "score 0");
|
||||||
|
C(s.preferredAPIs.empty(), "no APIs");
|
||||||
|
C(s.avoidedLibraries.empty(), "no avoided");
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
void t2() {
|
||||||
|
T(populated_selection_fields);
|
||||||
|
ws::LibrarySelection s;
|
||||||
|
s.selectedLibrary = "nlohmann_json";
|
||||||
|
s.operationDomain = "serialization.json";
|
||||||
|
s.capabilityScore = 0.92f;
|
||||||
|
s.preferredAPIs = {"nlohmann::json::dump()", "nlohmann::json::parse()"};
|
||||||
|
s.justification = "nlohmann_json scores 0.92 vs best alternative protobuf at 0.60";
|
||||||
|
C(s.hasSelection(), "hasSelection true");
|
||||||
|
C(s.selectedLibrary == "nlohmann_json", "selectedLibrary");
|
||||||
|
C(s.capabilityScore == 0.92f, "capabilityScore");
|
||||||
|
C(s.preferredAPIs.size() == 2, "2 preferredAPIs");
|
||||||
|
C(!s.justification.empty(), "justification set");
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
void t3() {
|
||||||
|
T(avoided_library_struct_fields);
|
||||||
|
ws::AvoidedLibrary av;
|
||||||
|
av.name = "protobuf";
|
||||||
|
av.reason = "score 0.60 < selected 0.92";
|
||||||
|
av.score = 0.60f;
|
||||||
|
C(av.name == "protobuf", "name");
|
||||||
|
C(av.score == 0.60f, "score");
|
||||||
|
C(av.reason.find("0.60") != std::string::npos, "reason contains score");
|
||||||
|
C(av.reason.find("0.92") != std::string::npos, "reason contains selected score");
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
void t4() {
|
||||||
|
T(multiple_avoided_libraries_sorted_by_score_desc);
|
||||||
|
ws::LibrarySelection s;
|
||||||
|
s.selectedLibrary = "serde_json";
|
||||||
|
s.capabilityScore = 0.98f;
|
||||||
|
s.avoidedLibraries = {
|
||||||
|
{"nlohmann_json", "score 0.92 < selected 0.98", 0.92f},
|
||||||
|
{"protobuf", "score 0.60 < selected 0.98", 0.60f},
|
||||||
|
};
|
||||||
|
C(s.avoidedLibraries.size() == 2, "2 avoided");
|
||||||
|
C(s.avoidedLibraries[0].name == "nlohmann_json", "best avoided first");
|
||||||
|
C(s.avoidedLibraries[0].score > s.avoidedLibraries[1].score, "sorted desc");
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
void t5() {
|
||||||
|
T(selection_with_no_avoided_only_one_qualified);
|
||||||
|
ws::LibrarySelection s;
|
||||||
|
s.selectedLibrary = "thrust";
|
||||||
|
s.operationDomain = "compute.parallel";
|
||||||
|
s.capabilityScore = 0.97f;
|
||||||
|
s.justification = "thrust scores 0.97 (only qualified library)";
|
||||||
|
C(s.hasSelection(), "hasSelection");
|
||||||
|
C(s.avoidedLibraries.empty(), "no avoided when only one qualifies");
|
||||||
|
C(s.justification.find("only") != std::string::npos, "justification says only");
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::cout << "Step 1963: LibrarySelection\n";
|
||||||
|
t1(); t2(); t3(); t4(); t5();
|
||||||
|
std::cout << "\n" << p << "/" << (p + f) << " passed\n";
|
||||||
|
return f > 0 ? 1 : 0;
|
||||||
|
}
|
||||||
75
editor/tests/step1964_test.cpp
Normal file
75
editor/tests/step1964_test.cpp
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
// Step 1964: OperationClassifier
|
||||||
|
//
|
||||||
|
// t1: classify known text returns isKnown=true, confidence=1.0
|
||||||
|
// t2: classify unknown text returns isKnown=false, confidence=0.0
|
||||||
|
// t3: classify "serialize to JSON" -> serialization.json
|
||||||
|
// t4: classify "matrix multiply" -> compute.matrix
|
||||||
|
// t5: withDefaultTaxonomy() builds correctly; taxonomy() accessible
|
||||||
|
|
||||||
|
#include "OperationClassifier.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace ws = whetstone;
|
||||||
|
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; }
|
||||||
|
|
||||||
|
void t1() {
|
||||||
|
T(known_text_returns_confidence_1);
|
||||||
|
auto cls = ws::OperationClassifier::withDefaultTaxonomy();
|
||||||
|
auto r = cls.classify("serialize to JSON");
|
||||||
|
C(r.isKnown, "isKnown true");
|
||||||
|
C(r.confidence == 1.0f, "confidence 1.0");
|
||||||
|
C(!r.domain.empty(), "domain not empty");
|
||||||
|
C(r.domain != "unknown", "domain is not unknown");
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
void t2() {
|
||||||
|
T(unknown_text_returns_confidence_0);
|
||||||
|
auto cls = ws::OperationClassifier::withDefaultTaxonomy();
|
||||||
|
auto r = cls.classify("xyzzy frobble zogzog");
|
||||||
|
C(!r.isKnown, "isKnown false");
|
||||||
|
C(r.confidence == 0.0f, "confidence 0.0");
|
||||||
|
C(r.domain == "unknown", "domain is unknown");
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
void t3() {
|
||||||
|
T(classify_json_text);
|
||||||
|
auto cls = ws::OperationClassifier::withDefaultTaxonomy();
|
||||||
|
auto r = cls.classify("serialize to JSON");
|
||||||
|
C(r.domain == "serialization.json",
|
||||||
|
"expected serialization.json, got " + r.domain);
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
void t4() {
|
||||||
|
T(classify_matrix_multiply_text);
|
||||||
|
auto cls = ws::OperationClassifier::withDefaultTaxonomy();
|
||||||
|
auto r = cls.classify("matrix multiply on GPU");
|
||||||
|
C(r.domain == "compute.matrix",
|
||||||
|
"expected compute.matrix, got " + r.domain);
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
void t5() {
|
||||||
|
T(withDefaultTaxonomy_and_taxonomy_accessor);
|
||||||
|
auto cls = ws::OperationClassifier::withDefaultTaxonomy();
|
||||||
|
C(cls.taxonomy().domainCount() >= 40, ">=40 domains");
|
||||||
|
auto cats = cls.taxonomy().topLevelCategories();
|
||||||
|
C(cats.size() >= 6, ">=6 top-level categories");
|
||||||
|
// Classify a few more domains
|
||||||
|
C(cls.classify("sql select query").domain == "db.query.sql", "sql");
|
||||||
|
C(cls.classify("compute sha256 hash").domain == "crypto.hash", "hash");
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::cout << "Step 1964: OperationClassifier\n";
|
||||||
|
t1(); t2(); t3(); t4(); t5();
|
||||||
|
std::cout << "\n" << p << "/" << (p + f) << " passed\n";
|
||||||
|
return f > 0 ? 1 : 0;
|
||||||
|
}
|
||||||
88
editor/tests/step1965_test.cpp
Normal file
88
editor/tests/step1965_test.cpp
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
// Step 1965: PerTaskLibrarySelector
|
||||||
|
//
|
||||||
|
// t1: selects nlohmann_json (0.92) over protobuf (0.60) for serialization.json
|
||||||
|
// t2: selects cublas (0.99) over numpy (0.95) for compute.matrix
|
||||||
|
// t3: avoidedLibraries populated correctly (sorted desc)
|
||||||
|
// t4: returns empty selection when no library has score > 0
|
||||||
|
// t5: single qualified library: no avoided, justification says "only"
|
||||||
|
|
||||||
|
#include "PerTaskLibrarySelector.h"
|
||||||
|
#include "Sprint286IntegrationSummary.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace ws = whetstone;
|
||||||
|
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; }
|
||||||
|
|
||||||
|
void t1() {
|
||||||
|
T(selects_nlohmann_over_protobuf_for_json);
|
||||||
|
auto ledger = ws::Sprint286IntegrationSummary::buildSeedLedger();
|
||||||
|
ws::PerTaskLibrarySelector sel;
|
||||||
|
auto r = sel.select("serialization.json", {"nlohmann_json", "protobuf"}, ledger);
|
||||||
|
C(r.hasSelection(), "hasSelection");
|
||||||
|
C(r.selectedLibrary == "nlohmann_json", "nlohmann_json selected");
|
||||||
|
C(r.capabilityScore == 0.92f, "score 0.92");
|
||||||
|
C(!r.justification.empty(), "justification set");
|
||||||
|
C(r.justification.find("nlohmann_json") != std::string::npos, "justification names winner");
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
void t2() {
|
||||||
|
T(selects_cublas_over_numpy_for_compute_matrix);
|
||||||
|
auto ledger = ws::Sprint286IntegrationSummary::buildSeedLedger();
|
||||||
|
ws::PerTaskLibrarySelector sel;
|
||||||
|
auto r = sel.select("compute.matrix", {"cublas", "numpy"}, ledger);
|
||||||
|
C(r.selectedLibrary == "cublas", "cublas selected");
|
||||||
|
C(r.capabilityScore == 0.99f, "score 0.99");
|
||||||
|
C(!r.preferredAPIs.empty(), "preferredAPIs populated");
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
void t3() {
|
||||||
|
T(avoided_libraries_sorted_desc);
|
||||||
|
auto ledger = ws::Sprint286IntegrationSummary::buildSeedLedger();
|
||||||
|
// Add serde_json so there are 3 candidates for serialization.json
|
||||||
|
ledger.set({"serde_json", "serialization.json", 0.98f});
|
||||||
|
ws::PerTaskLibrarySelector sel;
|
||||||
|
auto r = sel.select("serialization.json",
|
||||||
|
{"nlohmann_json", "protobuf", "serde_json"}, ledger);
|
||||||
|
C(r.selectedLibrary == "serde_json", "serde_json selected (0.98)");
|
||||||
|
C(r.avoidedLibraries.size() == 2, "2 avoided");
|
||||||
|
C(r.avoidedLibraries[0].name == "nlohmann_json", "nlohmann first avoided (0.92)");
|
||||||
|
C(r.avoidedLibraries[0].score > r.avoidedLibraries[1].score, "sorted desc");
|
||||||
|
C(r.avoidedLibraries[1].name == "protobuf", "protobuf second avoided (0.60)");
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
void t4() {
|
||||||
|
T(empty_selection_when_no_score);
|
||||||
|
auto ledger = ws::Sprint286IntegrationSummary::buildSeedLedger();
|
||||||
|
ws::PerTaskLibrarySelector sel;
|
||||||
|
// "cublas" has no score for "serialization.json"
|
||||||
|
auto r = sel.select("serialization.json", {"cublas"}, ledger);
|
||||||
|
C(!r.hasSelection(), "no selection");
|
||||||
|
C(r.selectedLibrary.empty(), "empty selectedLibrary");
|
||||||
|
C(r.justification.find("no library") != std::string::npos, "justification says no library");
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
void t5() {
|
||||||
|
T(single_qualified_library_no_avoided);
|
||||||
|
auto ledger = ws::Sprint286IntegrationSummary::buildSeedLedger();
|
||||||
|
ws::PerTaskLibrarySelector sel;
|
||||||
|
auto r = sel.select("compute.parallel", {"thrust"}, ledger);
|
||||||
|
C(r.selectedLibrary == "thrust", "thrust selected");
|
||||||
|
C(r.avoidedLibraries.empty(), "no avoided");
|
||||||
|
C(r.justification.find("only") != std::string::npos, "justification says only");
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::cout << "Step 1965: PerTaskLibrarySelector\n";
|
||||||
|
t1(); t2(); t3(); t4(); t5();
|
||||||
|
std::cout << "\n" << p << "/" << (p + f) << " passed\n";
|
||||||
|
return f > 0 ? 1 : 0;
|
||||||
|
}
|
||||||
99
editor/tests/step1966_test.cpp
Normal file
99
editor/tests/step1966_test.cpp
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
// Step 1966: SelectionJustificationLog
|
||||||
|
//
|
||||||
|
// t1: append and size
|
||||||
|
// t2: findByDomain returns matching entries only
|
||||||
|
// t3: findByLibrary filters correctly
|
||||||
|
// t4: findByTask returns correct entries
|
||||||
|
// t5: clear resets the log; entries() ordering preserved
|
||||||
|
|
||||||
|
#include "SelectionJustificationLog.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace ws = whetstone;
|
||||||
|
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 ws::SelectionJustificationLog makeLog() {
|
||||||
|
ws::SelectionJustificationLog log;
|
||||||
|
log.append({"t1", "serialization.json", "nlohmann_json", 0.92f,
|
||||||
|
"nlohmann_json scores 0.92 vs protobuf at 0.60", "2026-03-02"});
|
||||||
|
log.append({"t2", "compute.matrix", "cublas", 0.99f,
|
||||||
|
"cublas scores 0.99 vs numpy at 0.95", "2026-03-02"});
|
||||||
|
log.append({"t3", "serialization.json", "serde_json", 0.98f,
|
||||||
|
"serde_json scores 0.98 (only qualified)", "2026-03-02"});
|
||||||
|
log.append({"t4", "compute.statistics", "numpy", 0.93f,
|
||||||
|
"numpy scores 0.93 (only qualified library)", "2026-03-02"});
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
|
||||||
|
void t1() {
|
||||||
|
T(append_and_size);
|
||||||
|
ws::SelectionJustificationLog log;
|
||||||
|
C(log.size() == 0, "empty initially");
|
||||||
|
log.append({"t1", "serialization.json", "nlohmann_json", 0.92f, "j", "2026-03-02"});
|
||||||
|
C(log.size() == 1, "size 1 after append");
|
||||||
|
log.append({"t2", "compute.matrix", "cublas", 0.99f, "j", "2026-03-02"});
|
||||||
|
C(log.size() == 2, "size 2 after second append");
|
||||||
|
C(log.entries().size() == 2, "entries() matches size");
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
void t2() {
|
||||||
|
T(findByDomain_filters_correctly);
|
||||||
|
auto log = makeLog();
|
||||||
|
auto json_entries = log.findByDomain("serialization.json");
|
||||||
|
C(json_entries.size() == 2, "2 serialization.json entries");
|
||||||
|
C(json_entries[0].taskId == "t1", "first is t1");
|
||||||
|
C(json_entries[1].taskId == "t3", "second is t3");
|
||||||
|
auto none = log.findByDomain("network.async");
|
||||||
|
C(none.empty(), "no entries for network.async");
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
void t3() {
|
||||||
|
T(findByLibrary_filters_correctly);
|
||||||
|
auto log = makeLog();
|
||||||
|
auto cublas = log.findByLibrary("cublas");
|
||||||
|
C(cublas.size() == 1, "1 cublas entry");
|
||||||
|
C(cublas[0].operationDomain == "compute.matrix", "correct domain");
|
||||||
|
auto numpy = log.findByLibrary("numpy");
|
||||||
|
C(numpy.size() == 1, "1 numpy entry");
|
||||||
|
C(numpy[0].capabilityScore == 0.93f, "correct score");
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
void t4() {
|
||||||
|
T(findByTask_returns_correct_entry);
|
||||||
|
auto log = makeLog();
|
||||||
|
auto t2 = log.findByTask("t2");
|
||||||
|
C(t2.size() == 1, "1 t2 entry");
|
||||||
|
C(t2[0].selectedLibrary == "cublas", "cublas for t2");
|
||||||
|
auto missing = log.findByTask("t99");
|
||||||
|
C(missing.empty(), "no t99 entry");
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
void t5() {
|
||||||
|
T(clear_resets_and_ordering_preserved);
|
||||||
|
auto log = makeLog();
|
||||||
|
C(log.size() == 4, "4 entries before clear");
|
||||||
|
log.clear();
|
||||||
|
C(log.size() == 0, "empty after clear");
|
||||||
|
C(log.entries().empty(), "entries() empty after clear");
|
||||||
|
// Re-append and verify ordering preserved
|
||||||
|
log.append({"a", "serialization.json", "nlohmann_json", 0.92f, "j", "2026-03-02"});
|
||||||
|
log.append({"b", "compute.matrix", "cublas", 0.99f, "j", "2026-03-02"});
|
||||||
|
C(log.entries()[0].taskId == "a", "first entry preserved");
|
||||||
|
C(log.entries()[1].taskId == "b", "second entry preserved");
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::cout << "Step 1966: SelectionJustificationLog\n";
|
||||||
|
t1(); t2(); t3(); t4(); t5();
|
||||||
|
std::cout << "\n" << p << "/" << (p + f) << " passed\n";
|
||||||
|
return f > 0 ? 1 : 0;
|
||||||
|
}
|
||||||
83
editor/tests/step1967_test.cpp
Normal file
83
editor/tests/step1967_test.cpp
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
// Step 1967: Sprint 287 Integration
|
||||||
|
//
|
||||||
|
// t1: all 8 tasks produce correct library selection
|
||||||
|
// t2: verifyAllSelections() passes
|
||||||
|
// t3: avoidedLibraries present for tasks with multiple candidates
|
||||||
|
// t4: tasks with single qualified library have no avoided entries
|
||||||
|
// t5: Sprint287IntegrationSummary metadata correct
|
||||||
|
|
||||||
|
#include "Sprint287IntegrationSummary.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace ws = whetstone;
|
||||||
|
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; }
|
||||||
|
|
||||||
|
void t1() {
|
||||||
|
T(all_8_tasks_correct_library);
|
||||||
|
auto ledger = ws::Sprint286IntegrationSummary::buildSeedLedger();
|
||||||
|
ws::PerTaskLibrarySelector sel;
|
||||||
|
auto tasks = ws::Sprint287IntegrationSummary::buildTaskSpec();
|
||||||
|
C(tasks.size() == 8, "8 tasks");
|
||||||
|
for (const auto& task : tasks) {
|
||||||
|
auto r = sel.select(task.operationDomain, task.availableLibraries, ledger);
|
||||||
|
C(r.selectedLibrary == task.expectedLibrary,
|
||||||
|
task.taskId + ": expected " + task.expectedLibrary + " got " + r.selectedLibrary);
|
||||||
|
}
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
void t2() {
|
||||||
|
T(verifyAllSelections_passes);
|
||||||
|
C(ws::Sprint287IntegrationSummary::verifyAllSelections(), "verifyAllSelections true");
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
void t3() {
|
||||||
|
T(avoided_libraries_present_for_multi_candidate_tasks);
|
||||||
|
auto ledger = ws::Sprint286IntegrationSummary::buildSeedLedger();
|
||||||
|
ws::PerTaskLibrarySelector sel;
|
||||||
|
// t1: nlohmann vs protobuf -> 1 avoided
|
||||||
|
auto r1 = sel.select("serialization.json", {"nlohmann_json", "protobuf"}, ledger);
|
||||||
|
C(!r1.avoidedLibraries.empty(), "t1 has avoided");
|
||||||
|
C(r1.avoidedLibraries[0].name == "protobuf", "t1 avoided is protobuf");
|
||||||
|
// t5: tokio vs boost.asio -> 1 avoided
|
||||||
|
auto r5 = sel.select("network.async", {"tokio", "boost.asio"}, ledger);
|
||||||
|
C(!r5.avoidedLibraries.empty(), "t5 has avoided");
|
||||||
|
C(r5.avoidedLibraries[0].name == "boost.asio", "t5 avoided is boost.asio");
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
void t4() {
|
||||||
|
T(single_qualified_has_no_avoided);
|
||||||
|
auto ledger = ws::Sprint286IntegrationSummary::buildSeedLedger();
|
||||||
|
ws::PerTaskLibrarySelector sel;
|
||||||
|
// t4: only numpy for compute.statistics
|
||||||
|
auto r4 = sel.select("compute.statistics", {"numpy"}, ledger);
|
||||||
|
C(r4.selectedLibrary == "numpy", "numpy selected");
|
||||||
|
C(r4.avoidedLibraries.empty(), "no avoided for t4");
|
||||||
|
// t6: only thrust for compute.parallel
|
||||||
|
auto r6 = sel.select("compute.parallel", {"thrust"}, ledger);
|
||||||
|
C(r6.selectedLibrary == "thrust", "thrust selected");
|
||||||
|
C(r6.avoidedLibraries.empty(), "no avoided for t6");
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
void t5() {
|
||||||
|
T(sprint287_metadata);
|
||||||
|
ws::Sprint287IntegrationSummary s;
|
||||||
|
C(s.stepsCompleted == 5, "5 steps");
|
||||||
|
C(s.success, "success");
|
||||||
|
C(s.sprintName() == "Sprint 287: PerTaskLibrarySelector", "name");
|
||||||
|
P();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::cout << "Step 1967: Sprint 287 Integration\n";
|
||||||
|
t1(); t2(); t3(); t4(); t5();
|
||||||
|
std::cout << "\n" << p << "/" << (p + f) << " passed\n";
|
||||||
|
return f > 0 ? 1 : 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user