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:
Bill
2026-03-02 10:04:39 -07:00
parent b5c8b46117
commit 2a40cb1ba5
11 changed files with 728 additions and 0 deletions

View 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;
}