diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index b9bb01b..086032d 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -11129,3 +11129,18 @@ target_include_directories(step1971_test PRIVATE src) add_executable(step1972_test tests/step1972_test.cpp) target_include_directories(step1972_test PRIVATE src) + +add_executable(step1973_test tests/step1973_test.cpp) +target_include_directories(step1973_test PRIVATE src) + +add_executable(step1974_test tests/step1974_test.cpp) +target_include_directories(step1974_test PRIVATE src) + +add_executable(step1975_test tests/step1975_test.cpp) +target_include_directories(step1975_test PRIVATE src) + +add_executable(step1976_test tests/step1976_test.cpp) +target_include_directories(step1976_test PRIVATE src) + +add_executable(step1977_test tests/step1977_test.cpp) +target_include_directories(step1977_test PRIVATE src) diff --git a/editor/src/AnnotatedTaskitemValidator.h b/editor/src/AnnotatedTaskitemValidator.h new file mode 100644 index 0000000..aed41b6 --- /dev/null +++ b/editor/src/AnnotatedTaskitemValidator.h @@ -0,0 +1,59 @@ +#pragma once +// Step 1975: AnnotatedTaskitemValidator +// Validates enrichment of EnrichedExecutionContracts. +// ValidationReport { passCount, failCount, errors (include taskDescription) }. +// If enrichmentRequired=true, any unenriched contract is an error. +// Also validates capabilityScore in [0,1] for enriched contracts. + +#include "EnrichedExecutionContract.h" +#include +#include + +namespace whetstone { + +struct ValidationReport { + int passCount = 0; + int failCount = 0; + std::vector errors; + + bool allPassed() const { return failCount == 0; } +}; + +class AnnotatedTaskitemValidator { +public: + ValidationReport validate( + const std::vector& contracts, + bool enrichmentRequired) const { + + ValidationReport report; + + for (const auto& c : contracts) { + const std::string desc = c.taskDescription.empty() + ? "(unnamed task)" : c.taskDescription; + + bool pass = true; + + // Check enrichment requirement + if (enrichmentRequired && !c.isEnriched()) { + report.errors.push_back("task not enriched: " + desc); + pass = false; + } + + // Validate fields on enriched contracts + if (c.isEnriched()) { + if (c.capabilityScore < 0.0f || c.capabilityScore > 1.0f) { + report.errors.push_back( + "capabilityScore out of range [0,1] for: " + desc); + pass = false; + } + } + + if (pass) ++report.passCount; + else ++report.failCount; + } + + return report; + } +}; + +} // namespace whetstone diff --git a/editor/src/DispatchJustificationReport.h b/editor/src/DispatchJustificationReport.h new file mode 100644 index 0000000..e6889c1 --- /dev/null +++ b/editor/src/DispatchJustificationReport.h @@ -0,0 +1,78 @@ +#pragma once +// Step 1976: DispatchJustificationReport +// Per-project structured report of every task -> library choice -> score -> why. +// finalize() computes averageScore from enriched entries (non-empty selectedLibrary). +// serialize() returns JSON string via nlohmann::json. + +#include +#include +#include + +namespace whetstone { + +struct JustificationEntry { + std::string taskDescription; + std::string selectedLibrary; // empty if unenriched + std::string operationDomain; + float capabilityScore = 0.0f; + std::string justification; + std::vector avoidedLibraries; // names only +}; + +class DispatchJustificationReport { +public: + std::string projectId; + std::vector entries; + int totalTasks = 0; + int enrichedCount = 0; + float averageScore = 0.0f; + + void add(JustificationEntry entry) { + ++totalTasks; + if (!entry.selectedLibrary.empty()) ++enrichedCount; + entries.push_back(std::move(entry)); + } + + // Compute averageScore from enriched entries. + void finalize() { + if (enrichedCount == 0) { + averageScore = 0.0f; + return; + } + float sum = 0.0f; + int count = 0; + for (const auto& e : entries) { + if (!e.selectedLibrary.empty()) { + sum += e.capabilityScore; + ++count; + } + } + averageScore = count > 0 ? sum / static_cast(count) : 0.0f; + } + + // Serialize to JSON string (2-space indent). + std::string serialize() const { + nlohmann::json j; + j["projectId"] = projectId; + j["totalTasks"] = totalTasks; + j["enrichedCount"] = enrichedCount; + j["averageScore"] = averageScore; + + nlohmann::json jEntries = nlohmann::json::array(); + for (const auto& e : entries) { + nlohmann::json je; + je["taskDescription"] = e.taskDescription; + je["selectedLibrary"] = e.selectedLibrary; + je["operationDomain"] = e.operationDomain; + je["capabilityScore"] = e.capabilityScore; + je["justification"] = e.justification; + je["avoidedLibraries"] = e.avoidedLibraries; + jEntries.push_back(je); + } + j["entries"] = jEntries; + + return j.dump(2); + } +}; + +} // namespace whetstone diff --git a/editor/src/EnrichedExecutionContract.h b/editor/src/EnrichedExecutionContract.h new file mode 100644 index 0000000..54efc31 --- /dev/null +++ b/editor/src/EnrichedExecutionContract.h @@ -0,0 +1,33 @@ +#pragma once +// Step 1973: EnrichedExecutionContract +// Extends base execution contract with library dispatch fields. +// isEnriched() is true only when selectedLibrary and operationDomain are both set. +// Default-constructed contracts are not enriched. + +#include "LibrarySelection.h" +#include +#include + +namespace whetstone { + +struct EnrichedExecutionContract { + // Base contract fields + std::string language; + std::string stepId; + std::string taskDescription; + + // Library dispatch fields (empty by default — not enriched) + std::string selectedLibrary; + std::string operationDomain; + float capabilityScore = 0.0f; + std::vector preferredAPIs; + std::vector avoidedLibraries; + std::string justification; + + // True when selectedLibrary and operationDomain are both non-empty. + bool isEnriched() const { + return !selectedLibrary.empty() && !operationDomain.empty(); + } +}; + +} // namespace whetstone diff --git a/editor/src/Sprint289IntegrationSummary.h b/editor/src/Sprint289IntegrationSummary.h new file mode 100644 index 0000000..77038f0 --- /dev/null +++ b/editor/src/Sprint289IntegrationSummary.h @@ -0,0 +1,99 @@ +#pragma once +// Step 1977: Sprint 289 Integration Summary +// Full pipeline: spec -> classifier -> selector -> advisor -> enriched taskitems. +// 4 named tasks annotated using seed ledger (Sprint 286) + seed catalog (Sprint 288). +// +// Tasks: +// 1. "implement JSON serialization" -> serialization.json -> nlohmann_json (0.92) +// 2. "perform matrix multiply on GPU" -> compute.matrix -> cublas (0.99) +// 3. "serialize binary IPC message" -> serialization.binary -> protobuf (0.95) +// 4. "parallel array sort on GPU" -> compute.parallel -> thrust (0.97) + +#include "TaskitemLibraryAnnotator.h" +#include "AnnotatedTaskitemValidator.h" +#include "DispatchJustificationReport.h" +#include "Sprint286IntegrationSummary.h" +#include "Sprint288IntegrationSummary.h" +#include +#include + +namespace whetstone { + +class Sprint289IntegrationSummary { +public: + int stepsCompleted = 5; + bool success = true; + + std::string sprintName() const { + return "Sprint 289: generate_taskitems Integration"; + } + + // Build seed ledger + catalog, annotate 4 tasks, return enriched contracts. + static std::vector buildAnnotatedTaskitems() { + auto ledger = Sprint286IntegrationSummary::buildSeedLedger(); + auto catalog = Sprint288IntegrationSummary::buildSeedCatalog(); + TaskitemLibraryAnnotator annotator; + + std::vector result; + + // Task 1: JSON serialization (nlohmann_json vs protobuf) + result.push_back(annotator.annotate( + "implement JSON serialization", + {"nlohmann_json", "protobuf"}, + ledger, catalog)); + + // Task 2: GPU matrix multiply (cublas vs numpy) + result.push_back(annotator.annotate( + "perform matrix multiply on GPU", + {"cublas", "numpy"}, + ledger, catalog)); + + // Task 3: Binary IPC serialization (protobuf dominates) + result.push_back(annotator.annotate( + "serialize binary IPC message", + {"protobuf", "nlohmann_json"}, + ledger, catalog)); + + // Task 4: Parallel GPU sort (thrust dominates) + result.push_back(annotator.annotate( + "parallel array sort on GPU", + {"thrust", "numpy"}, + ledger, catalog)); + + return result; + } + + // Returns true when all 4 contracts are enriched. + static bool verifyAllEnriched() { + auto contracts = buildAnnotatedTaskitems(); + if (contracts.size() != 4) return false; + for (const auto& c : contracts) { + if (!c.isEnriched()) return false; + } + return true; + } + + // Build DispatchJustificationReport from the 4 annotated tasks. + static DispatchJustificationReport buildJustificationReport() { + auto contracts = buildAnnotatedTaskitems(); + DispatchJustificationReport report; + report.projectId = "sprint289-proof"; + + for (const auto& c : contracts) { + JustificationEntry entry; + entry.taskDescription = c.taskDescription; + entry.selectedLibrary = c.selectedLibrary; + entry.operationDomain = c.operationDomain; + entry.capabilityScore = c.capabilityScore; + entry.justification = c.justification; + for (const auto& a : c.avoidedLibraries) { + entry.avoidedLibraries.push_back(a.name); + } + report.add(entry); + } + report.finalize(); + return report; + } +}; + +} // namespace whetstone diff --git a/editor/src/TaskitemLibraryAnnotator.h b/editor/src/TaskitemLibraryAnnotator.h new file mode 100644 index 0000000..ebd3f09 --- /dev/null +++ b/editor/src/TaskitemLibraryAnnotator.h @@ -0,0 +1,64 @@ +#pragma once +// Step 1974: TaskitemLibraryAnnotator +// Runs OperationClassifier -> PerTaskLibrarySelector -> LibrarySymbolAdvisor per taskitem. +// Returns EnrichedExecutionContract. Unenriched if domain unknown or no library scores > 0. +// Always sets taskDescription on the returned contract. + +#include "EnrichedExecutionContract.h" +#include "OperationClassifier.h" +#include "PerTaskLibrarySelector.h" +#include "LibrarySymbolAdvisor.h" +#include "LibraryCapabilityLedger.h" +#include "LibrarySymbolCatalog.h" +#include +#include + +namespace whetstone { + +class TaskitemLibraryAnnotator { +public: + EnrichedExecutionContract annotate( + const std::string& taskDescription, + const std::vector& availableLibraries, + const LibraryCapabilityLedger& ledger, + const LibrarySymbolCatalog& catalog) const { + + EnrichedExecutionContract ec; + ec.taskDescription = taskDescription; + + // Step 1: classify the task description into an operation domain + auto classifier = OperationClassifier::withDefaultTaxonomy(); + auto cr = classifier.classify(taskDescription); + if (!cr.isKnown) { + return ec; // domain unknown — unenriched + } + + // Step 2: select best library for the domain + PerTaskLibrarySelector selector; + auto sel = selector.select(cr.domain, availableLibraries, ledger); + if (!sel.hasSelection()) { + return ec; // no library scores > 0 — unenriched + } + + // Step 3: populate enriched fields from selection + ec.selectedLibrary = sel.selectedLibrary; + ec.operationDomain = sel.operationDomain; + ec.capabilityScore = sel.capabilityScore; + ec.preferredAPIs = sel.preferredAPIs; + ec.avoidedLibraries = sel.avoidedLibraries; + ec.justification = sel.justification; + + // Step 4: supplement preferredAPIs from symbol advisor if not already populated + if (ec.preferredAPIs.empty()) { + LibrarySymbolAdvisor advisor; + auto syms = advisor.advise(sel.selectedLibrary, sel.operationDomain, catalog); + for (const auto& s : syms) { + ec.preferredAPIs.push_back(s.functionName); + } + } + + return ec; + } +}; + +} // namespace whetstone diff --git a/editor/tests/step1973_test.cpp b/editor/tests/step1973_test.cpp new file mode 100644 index 0000000..5ec29af --- /dev/null +++ b/editor/tests/step1973_test.cpp @@ -0,0 +1,84 @@ +// Step 1973: EnrichedExecutionContract +// +// t1: default-constructed contract is not enriched +// t2: setting selectedLibrary + operationDomain makes it enriched +// t3: only selectedLibrary set -> not enriched +// t4: only operationDomain set -> not enriched +// t5: all fields set and readable + +#include "EnrichedExecutionContract.h" +#include + +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_not_enriched); + ws::EnrichedExecutionContract ec; + C(!ec.isEnriched(), "default not enriched"); + C(ec.selectedLibrary.empty(), "selectedLibrary empty"); + C(ec.operationDomain.empty(), "operationDomain empty"); + C(ec.capabilityScore == 0.0f, "capabilityScore 0"); + C(ec.preferredAPIs.empty(), "preferredAPIs empty"); + C(ec.avoidedLibraries.empty(), "avoidedLibraries empty"); + P(); +} + +void t2() { + T(enriched_after_both_fields_set); + ws::EnrichedExecutionContract ec; + ec.selectedLibrary = "nlohmann_json"; + ec.operationDomain = "serialization.json"; + C(ec.isEnriched(), "enriched after both set"); + P(); +} + +void t3() { + T(not_enriched_only_library_set); + ws::EnrichedExecutionContract ec; + ec.selectedLibrary = "cublas"; + C(!ec.isEnriched(), "not enriched without operationDomain"); + P(); +} + +void t4() { + T(not_enriched_only_domain_set); + ws::EnrichedExecutionContract ec; + ec.operationDomain = "compute.matrix"; + C(!ec.isEnriched(), "not enriched without selectedLibrary"); + P(); +} + +void t5() { + T(all_fields_set_and_readable); + ws::EnrichedExecutionContract ec; + ec.language = "C++"; + ec.stepId = "serialize"; + ec.taskDescription = "implement JSON serialization"; + ec.selectedLibrary = "nlohmann_json"; + ec.operationDomain = "serialization.json"; + ec.capabilityScore = 0.92f; + ec.preferredAPIs = {"nlohmann::json::dump()", "nlohmann::json::parse()"}; + ec.avoidedLibraries = {{"protobuf", "score 0.60 < selected 0.92", 0.60f}}; + ec.justification = "nlohmann_json scores 0.92 vs protobuf at 0.60"; + C(ec.isEnriched(), "enriched"); + C(ec.language == "C++", "language"); + C(ec.stepId == "serialize", "stepId"); + C(ec.taskDescription == "implement JSON serialization", "taskDescription"); + C(ec.capabilityScore == 0.92f, "capabilityScore"); + C(ec.preferredAPIs.size() == 2, "2 preferredAPIs"); + C(ec.avoidedLibraries.size() == 1, "1 avoidedLibrary"); + C(ec.avoidedLibraries[0].name == "protobuf","avoidedLibrary name"); + P(); +} + +int main() { + std::cout << "Step 1973: EnrichedExecutionContract\n"; + t1(); t2(); t3(); t4(); t5(); + std::cout << "\n" << p << "/" << (p + f) << " passed\n"; + return f > 0 ? 1 : 0; +} diff --git a/editor/tests/step1974_test.cpp b/editor/tests/step1974_test.cpp new file mode 100644 index 0000000..f3ff56a --- /dev/null +++ b/editor/tests/step1974_test.cpp @@ -0,0 +1,99 @@ +// Step 1974: TaskitemLibraryAnnotator +// +// t1: annotates JSON serialization task with nlohmann_json +// t2: returns unenriched for unknown domain text +// t3: returns unenriched when no library scores > 0 for domain +// t4: avoidedLibraries populated when runner-up has score > 0 +// t5: preferredAPIs populated from ledger record + +#include "TaskitemLibraryAnnotator.h" +#include "Sprint286IntegrationSummary.h" +#include "Sprint288IntegrationSummary.h" +#include + +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::LibraryCapabilityLedger makeLedger() { + return ws::Sprint286IntegrationSummary::buildSeedLedger(); +} +static ws::LibrarySymbolCatalog makeCatalog() { + return ws::Sprint288IntegrationSummary::buildSeedCatalog(); +} + +void t1() { + T(annotates_json_task_with_nlohmann); + auto ledger = makeLedger(); + auto catalog = makeCatalog(); + ws::TaskitemLibraryAnnotator ann; + auto ec = ann.annotate("implement JSON serialization", + {"nlohmann_json", "protobuf"}, ledger, catalog); + C(ec.isEnriched(), "enriched"); + C(ec.selectedLibrary == "nlohmann_json", "nlohmann_json selected"); + C(ec.operationDomain == "serialization.json", "domain serialization.json"); + C(ec.capabilityScore > 0.0f, "score > 0"); + C(ec.taskDescription == "implement JSON serialization", "taskDescription set"); + P(); +} + +void t2() { + T(unenriched_for_unknown_domain); + auto ledger = makeLedger(); + auto catalog = makeCatalog(); + ws::TaskitemLibraryAnnotator ann; + auto ec = ann.annotate("xyzzy frobnicate quantum entanglement", + {"nlohmann_json", "protobuf"}, ledger, catalog); + C(!ec.isEnriched(), "not enriched for unknown domain"); + C(ec.selectedLibrary.empty(), "selectedLibrary empty"); + C(ec.taskDescription == "xyzzy frobnicate quantum entanglement", "taskDescription still set"); + P(); +} + +void t3() { + T(unenriched_when_no_library_scores_above_zero); + auto ledger = makeLedger(); + auto catalog = makeCatalog(); + ws::TaskitemLibraryAnnotator ann; + // Use compute.fft domain; neither library has any score in ledger + auto ec = ann.annotate("compute FFT transform", {"nlohmann_json", "protobuf"}, ledger, catalog); + C(!ec.isEnriched(), "not enriched when no library qualifies"); + P(); +} + +void t4() { + T(avoided_libraries_populated_for_runner_up); + auto ledger = makeLedger(); + auto catalog = makeCatalog(); + ws::TaskitemLibraryAnnotator ann; + // nlohmann_json (0.92) vs protobuf (0.60) for serialization.json + auto ec = ann.annotate("JSON serialization task", + {"nlohmann_json", "protobuf"}, ledger, catalog); + C(ec.isEnriched(), "enriched"); + C(!ec.avoidedLibraries.empty(), "avoidedLibraries non-empty"); + C(ec.avoidedLibraries[0].name == "protobuf", "protobuf avoided"); + P(); +} + +void t5() { + T(preferred_apis_populated); + auto ledger = makeLedger(); + auto catalog = makeCatalog(); + ws::TaskitemLibraryAnnotator ann; + auto ec = ann.annotate("perform matrix multiply on GPU", + {"cublas", "numpy"}, ledger, catalog); + C(ec.isEnriched(), "enriched"); + C(ec.selectedLibrary == "cublas", "cublas selected"); + C(!ec.preferredAPIs.empty(), "preferredAPIs non-empty"); + P(); +} + +int main() { + std::cout << "Step 1974: TaskitemLibraryAnnotator\n"; + t1(); t2(); t3(); t4(); t5(); + std::cout << "\n" << p << "/" << (p + f) << " passed\n"; + return f > 0 ? 1 : 0; +} diff --git a/editor/tests/step1975_test.cpp b/editor/tests/step1975_test.cpp new file mode 100644 index 0000000..81ba00d --- /dev/null +++ b/editor/tests/step1975_test.cpp @@ -0,0 +1,116 @@ +// Step 1975: AnnotatedTaskitemValidator +// +// t1: all enriched contracts, required=true -> all pass +// t2: unenriched contract, required=true -> fails with taskDescription in error +// t3: unenriched contract, required=false -> passes +// t4: enriched contract with capabilityScore > 1 -> fails +// t5: empty contracts vector -> 0 pass, 0 fail + +#include "AnnotatedTaskitemValidator.h" +#include +#include + +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::EnrichedExecutionContract makeEnriched(const std::string& task, + const std::string& lib, + const std::string& domain, + float score) { + ws::EnrichedExecutionContract ec; + ec.taskDescription = task; + ec.selectedLibrary = lib; + ec.operationDomain = domain; + ec.capabilityScore = score; + return ec; +} + +static ws::EnrichedExecutionContract makeUnenriched(const std::string& task) { + ws::EnrichedExecutionContract ec; + ec.taskDescription = task; + return ec; +} + +void t1() { + T(all_enriched_required_true_all_pass); + ws::AnnotatedTaskitemValidator v; + std::vector contracts = { + makeEnriched("task A", "nlohmann_json", "serialization.json", 0.92f), + makeEnriched("task B", "cublas", "compute.matrix", 0.99f), + }; + auto report = v.validate(contracts, true); + C(report.passCount == 2, "2 pass"); + C(report.failCount == 0, "0 fail"); + C(report.allPassed(), "allPassed"); + P(); +} + +void t2() { + T(unenriched_required_true_fails_with_taskdesc); + ws::AnnotatedTaskitemValidator v; + std::vector contracts = { + makeEnriched("enriched task", "cublas", "compute.matrix", 0.99f), + makeUnenriched("unenriched task"), + }; + auto report = v.validate(contracts, true); + C(report.passCount == 1, "1 pass"); + C(report.failCount == 1, "1 fail"); + C(!report.errors.empty(), "errors non-empty"); + // Error must include taskDescription + bool found = false; + for (const auto& e : report.errors) { + if (e.find("unenriched task") != std::string::npos) found = true; + } + C(found, "error contains taskDescription"); + P(); +} + +void t3() { + T(unenriched_required_false_passes); + ws::AnnotatedTaskitemValidator v; + std::vector contracts = { + makeUnenriched("unnamed work"), + makeEnriched("known task", "thrust", "compute.parallel", 0.97f), + }; + auto report = v.validate(contracts, false); + C(report.passCount == 2, "2 pass"); + C(report.failCount == 0, "0 fail"); + C(report.allPassed(), "allPassed"); + P(); +} + +void t4() { + T(enriched_bad_capability_score_fails); + ws::AnnotatedTaskitemValidator v; + ws::EnrichedExecutionContract bad = makeEnriched("bad task", "lib", "domain", 1.5f); + auto report = v.validate({bad}, true); + C(report.failCount == 1, "1 fail"); + C(!report.errors.empty(), "errors non-empty"); + bool found = false; + for (const auto& e : report.errors) { + if (e.find("bad task") != std::string::npos) found = true; + } + C(found, "error mentions bad task"); + P(); +} + +void t5() { + T(empty_contracts_zero_counts); + ws::AnnotatedTaskitemValidator v; + auto report = v.validate({}, true); + C(report.passCount == 0, "0 pass"); + C(report.failCount == 0, "0 fail"); + C(report.allPassed(), "allPassed on empty"); + P(); +} + +int main() { + std::cout << "Step 1975: AnnotatedTaskitemValidator\n"; + t1(); t2(); t3(); t4(); t5(); + std::cout << "\n" << p << "/" << (p + f) << " passed\n"; + return f > 0 ? 1 : 0; +} diff --git a/editor/tests/step1976_test.cpp b/editor/tests/step1976_test.cpp new file mode 100644 index 0000000..4008ac6 --- /dev/null +++ b/editor/tests/step1976_test.cpp @@ -0,0 +1,117 @@ +// Step 1976: DispatchJustificationReport +// +// t1: add entries, finalize computes averageScore correctly +// t2: serialize returns valid JSON string (parseable by nlohmann) +// t3: JSON contains all required top-level fields +// t4: unenriched entry (empty selectedLibrary) not counted in enrichedCount +// t5: projectId present in JSON and entries array present + +#include "DispatchJustificationReport.h" +#include + +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::JustificationEntry makeEntry(const std::string& task, + const std::string& lib, + const std::string& domain, + float score) { + ws::JustificationEntry e; + e.taskDescription = task; + e.selectedLibrary = lib; + e.operationDomain = domain; + e.capabilityScore = score; + e.justification = lib + " scores " + std::to_string(score); + return e; +} + +void t1() { + T(finalize_computes_average_score); + ws::DispatchJustificationReport r; + r.projectId = "test-project"; + r.add(makeEntry("task A", "nlohmann_json", "serialization.json", 0.92f)); + r.add(makeEntry("task B", "cublas", "compute.matrix", 0.99f)); + r.finalize(); + C(r.totalTasks == 2, "2 total tasks"); + C(r.enrichedCount == 2, "2 enriched"); + // averageScore = (0.92 + 0.99) / 2 = 0.955 + C(r.averageScore > 0.9f && r.averageScore < 1.0f, "averageScore in (0.9, 1.0)"); + P(); +} + +void t2() { + T(serialize_returns_valid_json); + ws::DispatchJustificationReport r; + r.projectId = "parse-test"; + r.add(makeEntry("task X", "thrust", "compute.parallel", 0.97f)); + r.finalize(); + std::string json = r.serialize(); + C(!json.empty(), "JSON non-empty"); + // Must be parseable + bool ok = false; + try { + auto parsed = nlohmann::json::parse(json); + ok = true; + } catch (...) {} + C(ok, "JSON parseable"); + P(); +} + +void t3() { + T(json_has_all_required_fields); + ws::DispatchJustificationReport r; + r.projectId = "fields-test"; + r.add(makeEntry("task Y", "protobuf", "serialization.binary", 0.95f)); + r.finalize(); + auto j = nlohmann::json::parse(r.serialize()); + C(j.contains("projectId"), "projectId"); + C(j.contains("totalTasks"), "totalTasks"); + C(j.contains("enrichedCount"), "enrichedCount"); + C(j.contains("averageScore"), "averageScore"); + C(j.contains("entries"), "entries"); + C(j["entries"].is_array(), "entries is array"); + P(); +} + +void t4() { + T(unenriched_entry_not_counted_in_enriched_count); + ws::DispatchJustificationReport r; + r.projectId = "mixed"; + r.add(makeEntry("enriched task", "thrust", "compute.parallel", 0.97f)); + // Unenriched entry: empty selectedLibrary + ws::JustificationEntry ue; + ue.taskDescription = "unenriched task"; + r.add(ue); + r.finalize(); + C(r.totalTasks == 2, "2 total"); + C(r.enrichedCount == 1, "1 enriched"); + // averageScore should be based on enriched entry only + C(r.averageScore > 0.9f, "averageScore based on enriched only"); + P(); +} + +void t5() { + T(project_id_in_json_and_entries_array); + ws::DispatchJustificationReport r; + r.projectId = "my-special-project"; + r.add(makeEntry("a", "cublas", "compute.matrix", 0.99f)); + r.add(makeEntry("b", "nlohmann_json", "serialization.json", 0.92f)); + r.finalize(); + auto j = nlohmann::json::parse(r.serialize()); + C(j["projectId"] == "my-special-project", "projectId value"); + C(j["entries"].size() == 2, "2 entries"); + C(j["entries"][0].contains("taskDescription"), "entry has taskDescription"); + C(j["entries"][0].contains("avoidedLibraries"), "entry has avoidedLibraries"); + P(); +} + +int main() { + std::cout << "Step 1976: DispatchJustificationReport\n"; + t1(); t2(); t3(); t4(); t5(); + std::cout << "\n" << p << "/" << (p + f) << " passed\n"; + return f > 0 ? 1 : 0; +} diff --git a/editor/tests/step1977_test.cpp b/editor/tests/step1977_test.cpp new file mode 100644 index 0000000..b186bc4 --- /dev/null +++ b/editor/tests/step1977_test.cpp @@ -0,0 +1,78 @@ +// Step 1977: Sprint 289 Integration +// +// t1: buildAnnotatedTaskitems returns 4 contracts +// t2: verifyAllEnriched passes +// t3: JSON serialization task selects nlohmann_json +// t4: matrix multiply task selects cublas +// t5: Sprint289IntegrationSummary metadata correct + report averageScore > 0 + +#include "Sprint289IntegrationSummary.h" +#include + +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(build_annotated_returns_4_contracts); + auto contracts = ws::Sprint289IntegrationSummary::buildAnnotatedTaskitems(); + C(contracts.size() == 4, "4 contracts"); + for (const auto& c : contracts) { + C(!c.taskDescription.empty(), "taskDescription set on each contract"); + } + P(); +} + +void t2() { + T(verify_all_enriched_passes); + C(ws::Sprint289IntegrationSummary::verifyAllEnriched(), "verifyAllEnriched"); + P(); +} + +void t3() { + T(json_task_selects_nlohmann_json); + auto contracts = ws::Sprint289IntegrationSummary::buildAnnotatedTaskitems(); + // Contract 0: "implement JSON serialization" + const auto& c = contracts[0]; + C(c.isEnriched(), "enriched"); + C(c.selectedLibrary == "nlohmann_json", "nlohmann_json selected"); + C(c.operationDomain == "serialization.json", "domain serialization.json"); + C(c.capabilityScore > 0.9f, "score > 0.9"); + P(); +} + +void t4() { + T(matrix_multiply_task_selects_cublas); + auto contracts = ws::Sprint289IntegrationSummary::buildAnnotatedTaskitems(); + // Contract 1: "perform matrix multiply on GPU" + const auto& c = contracts[1]; + C(c.isEnriched(), "enriched"); + C(c.selectedLibrary == "cublas", "cublas selected"); + C(c.operationDomain == "compute.matrix", "domain compute.matrix"); + C(c.capabilityScore > 0.95f, "cublas score > 0.95"); + P(); +} + +void t5() { + T(metadata_and_report); + ws::Sprint289IntegrationSummary s; + C(s.stepsCompleted == 5, "5 steps"); + C(s.success, "success"); + C(s.sprintName() == "Sprint 289: generate_taskitems Integration", "sprintName"); + auto report = ws::Sprint289IntegrationSummary::buildJustificationReport(); + C(report.totalTasks == 4, "4 total tasks in report"); + C(report.enrichedCount == 4, "4 enriched in report"); + C(report.averageScore > 0.0f, "averageScore > 0"); + C(report.projectId == "sprint289-proof", "projectId"); + P(); +} + +int main() { + std::cout << "Step 1977: Sprint 289 Integration\n"; + t1(); t2(); t3(); t4(); t5(); + std::cout << "\n" << p << "/" << (p + f) << " passed\n"; + return f > 0 ? 1 : 0; +}