From b5c8b46117be6eda506d0cf3c2dc451a8cbfb105 Mon Sep 17 00:00:00 2001 From: Bill Date: Mon, 2 Mar 2026 09:48:30 -0700 Subject: [PATCH] Sprint 286: OperationTaxonomy + LibraryCapabilityLedger (steps 1958-1962) Phase 6 Library Dispatch foundation. Five new headers: - OperationDomain: dot-key hierarchy with parent traversal - LibraryCapabilityRecord: score/APIs/weaknesses + validator - LibraryCapabilityLedger: (library, domain) -> record registry - OperationTaxonomy: 40+ domain tree, keyword-based classify() - Sprint286IntegrationSummary: 8-library seed ledger + verified lookups 25/25 tests passing. Co-Authored-By: Claude Sonnet 4.6 --- editor/CMakeLists.txt | 15 ++ editor/src/LibraryCapabilityLedger.h | 81 ++++++++ editor/src/LibraryCapabilityRecord.h | 43 +++++ editor/src/OperationDomain.h | 91 +++++++++ editor/src/OperationTaxonomy.h | 232 +++++++++++++++++++++++ editor/src/Sprint286IntegrationSummary.h | 108 +++++++++++ editor/tests/step1958_test.cpp | 89 +++++++++ editor/tests/step1959_test.cpp | 93 +++++++++ editor/tests/step1960_test.cpp | 95 ++++++++++ editor/tests/step1961_test.cpp | 90 +++++++++ editor/tests/step1962_test.cpp | 92 +++++++++ 11 files changed, 1029 insertions(+) create mode 100644 editor/src/LibraryCapabilityLedger.h create mode 100644 editor/src/LibraryCapabilityRecord.h create mode 100644 editor/src/OperationDomain.h create mode 100644 editor/src/OperationTaxonomy.h create mode 100644 editor/src/Sprint286IntegrationSummary.h create mode 100644 editor/tests/step1958_test.cpp create mode 100644 editor/tests/step1959_test.cpp create mode 100644 editor/tests/step1960_test.cpp create mode 100644 editor/tests/step1961_test.cpp create mode 100644 editor/tests/step1962_test.cpp diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 669bbc2..25db43f 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -11084,3 +11084,18 @@ target_include_directories(step1956_test PRIVATE src) add_executable(step1957_test tests/step1957_test.cpp) target_include_directories(step1957_test PRIVATE src) + +add_executable(step1958_test tests/step1958_test.cpp) +target_include_directories(step1958_test PRIVATE src) + +add_executable(step1959_test tests/step1959_test.cpp) +target_include_directories(step1959_test PRIVATE src) + +add_executable(step1960_test tests/step1960_test.cpp) +target_include_directories(step1960_test PRIVATE src) + +add_executable(step1961_test tests/step1961_test.cpp) +target_include_directories(step1961_test PRIVATE src) + +add_executable(step1962_test tests/step1962_test.cpp) +target_include_directories(step1962_test PRIVATE src) diff --git a/editor/src/LibraryCapabilityLedger.h b/editor/src/LibraryCapabilityLedger.h new file mode 100644 index 0000000..07cc56d --- /dev/null +++ b/editor/src/LibraryCapabilityLedger.h @@ -0,0 +1,81 @@ +#pragma once +// Step 1960: LibraryCapabilityLedger +// Registry of LibraryCapabilityRecords keyed by (libraryId, operationDomain). +// One-time annotation, deterministic reads forever. + +#include "LibraryCapabilityRecord.h" +#include +#include +#include +#include +#include + +namespace whetstone { + +class LibraryCapabilityLedger { +public: + void set(LibraryCapabilityRecord record) { + auto key = makeKey(record.libraryId, record.operationDomain); + records_[key] = std::move(record); + } + + std::optional get(const std::string& libraryId, + const std::string& operationDomain) const { + auto it = records_.find(makeKey(libraryId, operationDomain)); + if (it == records_.end()) return std::nullopt; + return it->second; + } + + bool has(const std::string& libraryId, const std::string& operationDomain) const { + return records_.count(makeKey(libraryId, operationDomain)) > 0; + } + + // Returns score for (library, domain), or 0.0 if not present. + float score(const std::string& libraryId, + const std::string& operationDomain) const { + auto it = records_.find(makeKey(libraryId, operationDomain)); + if (it == records_.end()) return 0.0f; + return it->second.score; + } + + std::vector allForLibrary(const std::string& libraryId) const { + std::vector result; + for (const auto& [k, v] : records_) { + if (v.libraryId == libraryId) result.push_back(v); + } + return result; + } + + std::vector allForDomain(const std::string& operationDomain) const { + std::vector result; + for (const auto& [k, v] : records_) { + if (v.operationDomain == operationDomain) result.push_back(v); + } + return result; + } + + // Sorted unique library IDs + std::vector libraries() const { + std::set libs; + for (const auto& [k, v] : records_) libs.insert(v.libraryId); + return {libs.begin(), libs.end()}; + } + + // Sorted unique operation domain keys + std::vector domains() const { + std::set doms; + for (const auto& [k, v] : records_) doms.insert(v.operationDomain); + return {doms.begin(), doms.end()}; + } + + size_t size() const { return records_.size(); } + +private: + static std::string makeKey(const std::string& lib, const std::string& domain) { + return lib + "::" + domain; + } + + std::unordered_map records_; +}; + +} // namespace whetstone diff --git a/editor/src/LibraryCapabilityRecord.h b/editor/src/LibraryCapabilityRecord.h new file mode 100644 index 0000000..e5a95c6 --- /dev/null +++ b/editor/src/LibraryCapabilityRecord.h @@ -0,0 +1,43 @@ +#pragma once +// Step 1959: LibraryCapabilityRecord +// Struct representing a library's capability for one operation domain. +// Score 0.0 = not applicable, 1.0 = reference implementation. + +#include +#include + +namespace whetstone { + +struct LibraryCapabilityRecord { + std::string libraryId; // e.g. "nlohmann_json" + std::string operationDomain; // e.g. "serialization.json" + float score = 0.0f; // [0.0, 1.0] + std::vector preferredAPIs; + std::vector knownWeaknesses; + bool notApplicable = false; + std::string annotatedAt; // ISO date string, e.g. "2026-03-02" + std::string source; // annotation source description +}; + +class LibraryCapabilityRecordValidator { +public: + struct ValidationResult { + bool valid = true; + std::string error; + }; + + ValidationResult validate(const LibraryCapabilityRecord& r) const { + if (r.libraryId.empty()) + return {false, "libraryId must not be empty"}; + if (r.operationDomain.empty()) + return {false, "operationDomain must not be empty"}; + if (r.score < 0.0f || r.score > 1.0f) + return {false, "score must be in [0.0, 1.0], got " + + std::to_string(r.score)}; + if (r.notApplicable && r.score > 0.0f) + return {false, "notApplicable=true is inconsistent with score > 0.0"}; + return {true, ""}; + } +}; + +} // namespace whetstone diff --git a/editor/src/OperationDomain.h b/editor/src/OperationDomain.h new file mode 100644 index 0000000..2c31b03 --- /dev/null +++ b/editor/src/OperationDomain.h @@ -0,0 +1,91 @@ +#pragma once +// Step 1958: OperationDomain +// Dot-separated operation domain key registry with parent traversal. +// e.g. "serialization.json" -> parent "serialization" -> parent "" (top-level) + +#include +#include +#include +#include +#include + +namespace whetstone { + +struct OperationDomain { + std::string key; // e.g. "serialization.json" + std::string displayName; // e.g. "JSON Serialization" + + // Returns the parent key: "serialization.json" -> "serialization", "serialization" -> "" + std::string parent() const { + auto pos = key.rfind('.'); + if (pos == std::string::npos) return ""; + return key.substr(0, pos); + } + + // True if no dot in key (e.g. "serialization", "compute") + bool isTopLevel() const { + return key.find('.') == std::string::npos; + } + + // Number of hierarchy levels (0 for top-level, 1 for one dot, etc.) + int depth() const { + return static_cast(std::count(key.begin(), key.end(), '.')); + } + + static OperationDomain fromKey(const std::string& k, + const std::string& name = "") { + return {k, name.empty() ? k : name}; + } + + bool operator==(const OperationDomain& other) const { return key == other.key; } + bool operator!=(const OperationDomain& other) const { return key != other.key; } +}; + +class OperationDomainRegistry { +public: + void registerDomain(OperationDomain domain) { + domains_[domain.key] = std::move(domain); + } + + std::optional lookup(const std::string& key) const { + auto it = domains_.find(key); + if (it == domains_.end()) return std::nullopt; + return it->second; + } + + bool has(const std::string& key) const { + return domains_.count(key) > 0; + } + + // All domains sorted by key (stable ordering) + std::vector all() const { + std::vector result; + result.reserve(domains_.size()); + for (const auto& [k, v] : domains_) result.push_back(v); + std::sort(result.begin(), result.end(), + [](const OperationDomain& a, const OperationDomain& b) { + return a.key < b.key; + }); + return result; + } + + // Direct children of parentKey (not grandchildren) + std::vector children(const std::string& parentKey) const { + std::vector result; + for (const auto& [k, v] : domains_) { + if (v.parent() == parentKey) result.push_back(v); + } + std::sort(result.begin(), result.end(), + [](const OperationDomain& a, const OperationDomain& b) { + return a.key < b.key; + }); + return result; + } + + size_t size() const { return domains_.size(); } + +private: + std::unordered_map domains_; +}; + +} // namespace whetstone diff --git a/editor/src/OperationTaxonomy.h b/editor/src/OperationTaxonomy.h new file mode 100644 index 0000000..0aa7a3c --- /dev/null +++ b/editor/src/OperationTaxonomy.h @@ -0,0 +1,232 @@ +#pragma once +// Step 1961: OperationTaxonomy +// Full domain tree with 40+ entries. classify(text) -> domain key via keyword matching. +// Domains are checked most-specific first (more dots = higher priority). + +#include "OperationDomain.h" +#include +#include +#include + +namespace whetstone { + +class OperationTaxonomy { +public: + // A matcher maps a domain key to a list of keyword sets. + // A domain matches if ANY keyword set fully appears in the lowercased text. + // Within a keyword set, ALL keywords must appear as substrings. + struct DomainMatcher { + std::string domainKey; + std::vector> keywordSets; + }; + + // Build the default taxonomy with 40+ domains across 8 top-level categories. + static OperationTaxonomy buildDefault() { + OperationTaxonomy t; + + // --- 2-dot domains (most specific, checked first) --- + + // serialization.*.* + t.add("serialization.protobuf", "Protocol Buffers", + {{"protobuf"}, {"proto buffer"}}); + t.add("serialization.msgpack", "MessagePack", + {{"msgpack"}, {"messagepack"}}); + + // compute.*.* + t.add("compute.ml.training", "ML Training", + {{"training", "model"}, {"backprop"}, {"gradient", "descent"}, {"train", "neural"}}); + t.add("compute.ml.inference", "ML Inference", + {{"inference", "model"}, {"forward pass"}, {"predict", "neural"}}); + + // network.*.* + t.add("network.http.client", "HTTP Client", + {{"http", "client"}, {"rest", "client"}, {"curl"}}); + t.add("network.http.server", "HTTP Server", + {{"http", "server"}, {"web server"}, {"rest", "server"}, {"http", "listen"}}); + + // io.*.* + t.add("io.stream.audio", "Audio Stream IO", + {{"audio", "stream"}, {"audio", "buffer"}, {"pcm"}}); + t.add("io.stream.video", "Video Stream IO", + {{"video", "stream"}, {"frame", "capture"}, {"video", "buffer"}}); + + // db.*.* + t.add("db.query.sql", "SQL Query", + {{"sql"}, {"select", "from"}, {"insert", "into"}, {"query", "table"}}); + t.add("db.query.nosql", "NoSQL Query", + {{"nosql"}, {"mongodb"}, {"document", "store"}, {"couchdb"}}); + + // --- 1-dot domains --- + + // serialization.* + t.add("serialization.json", "JSON Serialization", + {{"json"}}); + t.add("serialization.binary", "Binary Serialization", + {{"binary", "serial"}, {"binary", "encode"}, {"flatbuffer"}, {"capnproto"}}); + t.add("serialization.xml", "XML Serialization", + {{"xml"}, {"xslt"}}); + t.add("serialization.csv", "CSV Serialization", + {{"csv"}, {"comma", "separated"}}); + t.add("serialization.yaml", "YAML Serialization", + {{"yaml"}}); + + // compute.* + t.add("compute.matrix", "Matrix Computation", + {{"matrix"}, {"matmul"}, {"gemm"}, {"linear algebra"}}); + t.add("compute.parallel", "Parallel Computation", + {{"parallel"}, {"simd"}, {"vectorize"}}); + t.add("compute.statistics", "Statistical Computation", + {{"statistic"}, {"mean"}, {"variance"}, {"distribution"}}); + t.add("compute.fft", "Fast Fourier Transform", + {{"fft"}, {"fourier"}, {"frequency domain"}}); + t.add("compute.graph", "Graph Computation", + {{"graph", "algorithm"}, {"shortest path"}, {"bfs"}, {"dfs"}}); + t.add("compute.sort", "Sorting", + {{"sort"}, {"order", "elements"}}); + t.add("compute.hash_map", "Hash Map", + {{"hash map"}, {"hash table"}, {"hashmap"}, {"unordered map"}}); + + // network.* + t.add("network.websocket", "WebSocket", + {{"websocket"}, {"ws://"}}); + t.add("network.async", "Async Network IO", + {{"async", "io"}, {"async", "network"}, {"event loop"}, {"non-blocking", "io"}}); + t.add("network.grpc", "gRPC", + {{"grpc"}, {"rpc", "proto"}}); + t.add("network.tcp", "TCP", + {{"tcp"}, {"transmission control"}}); + t.add("network.udp", "UDP", + {{"udp"}, {"datagram"}}); + + // io.* + t.add("io.stream", "Stream IO", + {{"stream"}}); + t.add("io.file", "File IO", + {{"file", "read"}, {"file", "write"}, {"filesystem"}, {"file io"}}); + t.add("io.file.mmap", "Memory-Mapped IO", + {{"mmap"}, {"memory map"}, {"memory mapped"}}); + t.add("io.database", "Database IO", + {{"database", "read"}, {"database", "write"}}); + + // crypto.* + t.add("crypto.hash", "Cryptographic Hash", + {{"sha256"}, {"sha512"}, {"sha1"}, {"md5"}, {"checksum"}, {"hash", "file"}}); + t.add("crypto.symmetric", "Symmetric Encryption", + {{"aes"}, {"symmetric", "encrypt"}, {"chacha20"}, {"blowfish"}}); + t.add("crypto.asymmetric", "Asymmetric Encryption", + {{"rsa"}, {"ecdsa"}, {"public key"}, {"private key"}}); + t.add("crypto.tls", "TLS/SSL", + {{"tls"}, {"ssl"}, {"certificate"}}); + t.add("crypto.random", "Cryptographic Random", + {{"csprng"}, {"secure random"}, {"random bytes"}, {"secure", "rand"}}); + + // db.* + t.add("db.transaction", "Database Transaction", + {{"transaction"}, {"commit"}, {"rollback"}}); + t.add("db.orm", "ORM", + {{"orm"}, {"object relational"}, {"entity", "mapping"}}); + t.add("db.migration", "Database Migration", + {{"migration"}, {"schema change"}, {"db upgrade"}, {"db", "migrate"}}); + + // auth.* + t.add("auth.oauth", "OAuth", + {{"oauth"}}); + t.add("auth.jwt", "JWT", + {{"jwt"}, {"json web token"}}); + t.add("auth.session", "Session Auth", + {{"session"}, {"cookie", "auth"}}); + t.add("auth.mfa", "Multi-Factor Auth", + {{"mfa"}, {"2fa"}, {"totp"}, {"two factor"}}); + + // storage.* + t.add("storage.object", "Object Storage", + {{"object storage"}, {"s3"}, {"blob storage"}}); + t.add("storage.cache", "Cache Storage", + {{"cache"}, {"redis"}, {"memcache"}}); + t.add("storage.queue", "Message Queue", + {{"kafka"}, {"rabbitmq"}, {"message queue"}, {"message broker"}}); + + // --- 0-dot (top-level, checked last) --- + t.add("serialization", "Serialization", + {{"serial"}, {"marshal"}, {"encode", "data"}}); + t.add("compute", "Compute", + {{"compute"}, {"calculation"}}); + t.add("network", "Network", + {{"network"}, {"socket"}}); + t.add("io", "IO", + {{"file io"}, {"io stream"}}); + t.add("crypto", "Cryptography", + {{"crypto"}, {"encrypt"}, {"decrypt"}}); + t.add("db", "Database", + {{"database"}}); + t.add("auth", "Authentication", + {{"authenticate"}, {"authorization"}}); + t.add("storage", "Storage", + {{"store data"}, {"persist data"}}); + + // Sort matchers: more dots first, then longer key first. + std::stable_sort( + t.matchers_.begin(), t.matchers_.end(), + [](const DomainMatcher& a, const DomainMatcher& b) { + auto dots = [](const std::string& s) { + return std::count(s.begin(), s.end(), '.'); + }; + auto da = dots(a.domainKey), db = dots(b.domainKey); + if (da != db) return da > db; + return a.domainKey.size() > b.domainKey.size(); + }); + + return t; + } + + // Classify text to a domain key. Returns "unknown" if no match. + std::string classify(const std::string& text) const { + std::string lower = text; + std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower); + for (const auto& m : matchers_) { + if (matches(lower, m.keywordSets)) return m.domainKey; + } + return "unknown"; + } + + std::vector allDomains() const { return registry_.all(); } + size_t domainCount() const { return registry_.size(); } + const OperationDomainRegistry& registry() const { return registry_; } + + // Unique top-level category keys (domains with no dot) + std::vector topLevelCategories() const { + std::vector result; + for (const auto& d : registry_.all()) { + if (d.isTopLevel()) result.push_back(d.key); + } + return result; + } + +private: + OperationDomainRegistry registry_; + std::vector matchers_; + + void add(const std::string& key, const std::string& displayName, + std::vector> keywordSets) { + registry_.registerDomain({key, displayName}); + matchers_.push_back({key, std::move(keywordSets)}); + } + + // Returns true if ANY keyword set fully appears in lowerText. + static bool matches(const std::string& lowerText, + const std::vector>& sets) { + for (const auto& kwSet : sets) { + bool allFound = true; + for (const auto& kw : kwSet) { + if (lowerText.find(kw) == std::string::npos) { + allFound = false; + break; + } + } + if (allFound) return true; + } + return false; + } +}; + +} // namespace whetstone diff --git a/editor/src/Sprint286IntegrationSummary.h b/editor/src/Sprint286IntegrationSummary.h new file mode 100644 index 0000000..3737458 --- /dev/null +++ b/editor/src/Sprint286IntegrationSummary.h @@ -0,0 +1,108 @@ +#pragma once +// Step 1962: Sprint 286 Integration Summary +// Steps 1958–1962: OperationTaxonomy + LibraryCapabilityLedger +// +// 1958: OperationDomain — dot-key hierarchy + OperationDomainRegistry +// 1959: LibraryCapabilityRecord — score/APIs/weaknesses + validator +// 1960: LibraryCapabilityLedger — (libraryId, operationDomain) -> record registry +// 1961: OperationTaxonomy — 40+ domain tree + classify(text) +// 1962: Integration — seed 8 libraries, verify lookups + +#include "LibraryCapabilityLedger.h" +#include + +namespace whetstone { + +struct Sprint286IntegrationSummary { + int stepsCompleted = 5; + bool success = true; + std::string sprintName() const { + return "Sprint 286: OperationTaxonomy + LibraryCapabilityLedger"; + } + + // Seed ledger with 8 real libraries × their operation domains. + static LibraryCapabilityLedger buildSeedLedger() { + LibraryCapabilityLedger ledger; + + // nlohmann_json + ledger.set({"nlohmann_json", "serialization.json", 0.92f, + {"nlohmann::json::dump()", "nlohmann::json::parse()"}, + {}, + false, "2026-03-02", "benchmark+api-analysis"}); + + // protobuf + ledger.set({"protobuf", "serialization.binary", 0.95f, + {"Message::SerializeToString()", "Message::ParseFromString()"}, + {}, + false, "2026-03-02", "benchmark+api-analysis"}); + ledger.set({"protobuf", "serialization.json", 0.60f, + {"MessageToJsonString()", "JsonStringToMessage()"}, + {"no-pretty-print", "slow-large-arrays"}, + false, "2026-03-02", "benchmark+api-analysis"}); + + // serde_json (Rust) + ledger.set({"serde_json", "serialization.json", 0.98f, + {"serde_json::to_string()", "serde_json::from_str()"}, + {}, + false, "2026-03-02", "benchmark+api-analysis"}); + + // cublas + ledger.set({"cublas", "compute.matrix", 0.99f, + {"cublasSgemm()", "cublasDgemm()", "cublasCreate()"}, + {"requires-device-memory-management", "no-automatic-handle-lifecycle"}, + false, "2026-03-02", "benchmark"}); + + // thrust + ledger.set({"thrust", "compute.parallel", 0.97f, + {"thrust::transform()", "thrust::reduce()", "thrust::sort()"}, + {}, + false, "2026-03-02", "benchmark"}); + + // numpy + ledger.set({"numpy", "compute.matrix", 0.95f, + {"numpy.matmul()", "numpy.dot()", "numpy.linalg.solve()"}, + {"cpu-only", "gil-bound"}, + false, "2026-03-02", "benchmark"}); + ledger.set({"numpy", "compute.statistics", 0.93f, + {"numpy.mean()", "numpy.std()", "numpy.histogram()"}, + {}, + false, "2026-03-02", "benchmark"}); + + // tokio (Rust) + ledger.set({"tokio", "network.async", 0.97f, + {"tokio::spawn()", "tokio::select!()"}, + {}, + false, "2026-03-02", "benchmark"}); + ledger.set({"tokio", "io.stream", 0.95f, + {"tokio::io::AsyncReadExt", "tokio::io::AsyncWriteExt"}, + {}, + false, "2026-03-02", "api-analysis"}); + + // boost.asio + ledger.set({"boost.asio", "network.async", 0.91f, + {"boost::asio::io_context", "boost::asio::async_read()"}, + {}, + false, "2026-03-02", "benchmark"}); + ledger.set({"boost.asio", "network.http.client", 0.78f, + {"boost::beast::http::async_read()", "boost::beast::tcp_stream"}, + {"verbose-api", "requires-beast-for-http"}, + false, "2026-03-02", "api-analysis"}); + + return ledger; + } + + // Verify key lookups against seeded ledger. + static bool verifyLookups(const LibraryCapabilityLedger& ledger) { + // cublas/compute.matrix = 0.99 + if (ledger.score("cublas", "compute.matrix") != 0.99f) return false; + // protobuf/serialization.json = 0.60 (not 0.95 — domain specificity matters) + if (ledger.score("protobuf", "serialization.json") != 0.60f) return false; + // serde_json/serialization.json = 0.98 + if (ledger.score("serde_json", "serialization.json") != 0.98f) return false; + // unknown pair returns 0.0 + if (ledger.score("unknown_lib", "unknown_domain") != 0.0f) return false; + return true; + } +}; + +} // namespace whetstone diff --git a/editor/tests/step1958_test.cpp b/editor/tests/step1958_test.cpp new file mode 100644 index 0000000..ab106c2 --- /dev/null +++ b/editor/tests/step1958_test.cpp @@ -0,0 +1,89 @@ +// Step 1958: OperationDomain +// +// t1: key, displayName, parent() correct for "serialization.json" +// t2: parent() for top-level "serialization" is "" +// t3: isTopLevel() and depth() +// t4: OperationDomainRegistry register, lookup, has, size +// t5: children() returns only direct children, not grandchildren + +#include "OperationDomain.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(operation_domain_key_and_parent); + ws::OperationDomain d = ws::OperationDomain::fromKey("serialization.json", "JSON Serialization"); + C(d.key == "serialization.json", "key"); + C(d.displayName == "JSON Serialization", "displayName"); + C(d.parent() == "serialization", "parent"); + C(d.depth() == 1, "depth=1"); + P(); +} + +void t2() { + T(top_level_domain_has_empty_parent); + ws::OperationDomain d = ws::OperationDomain::fromKey("serialization"); + C(d.parent() == "", "parent of top-level is empty"); + C(d.isTopLevel(), "isTopLevel"); + C(d.depth() == 0, "depth=0"); + P(); +} + +void t3() { + T(deep_domain_depth_and_parent_chain); + ws::OperationDomain d = ws::OperationDomain::fromKey("compute.ml.training"); + C(d.depth() == 2, "depth=2"); + C(!d.isTopLevel(), "not top-level"); + C(d.parent() == "compute.ml", "parent is compute.ml"); + ws::OperationDomain mid = ws::OperationDomain::fromKey(d.parent()); + C(mid.parent() == "compute", "grandparent is compute"); + P(); +} + +void t4() { + T(domain_registry_register_lookup_has_size); + ws::OperationDomainRegistry reg; + reg.registerDomain({"serialization.json", "JSON"}); + reg.registerDomain({"serialization.binary", "Binary"}); + reg.registerDomain({"compute.matrix", "Matrix"}); + C(reg.size() == 3, "size=3"); + C(reg.has("serialization.json"), "has json"); + C(!reg.has("unknown"), "no unknown"); + auto d = reg.lookup("compute.matrix"); + C(d.has_value(), "lookup found"); + C(d->key == "compute.matrix", "lookup key"); + auto none = reg.lookup("missing"); + C(!none.has_value(), "lookup missing returns nullopt"); + P(); +} + +void t5() { + T(children_returns_only_direct_children); + ws::OperationDomainRegistry reg; + reg.registerDomain({"serialization", "S"}); + reg.registerDomain({"serialization.json", "SJ"}); + reg.registerDomain({"serialization.binary", "SB"}); + reg.registerDomain({"serialization.xml", "SX"}); + reg.registerDomain({"compute.matrix", "CM"}); // not a child of serialization + auto kids = reg.children("serialization"); + C(kids.size() == 3, "three direct children"); + C(kids[0].key == "serialization.binary", "sorted: binary first"); + C(kids[1].key == "serialization.json", "json second"); + C(kids[2].key == "serialization.xml", "xml third"); + auto none = reg.children("nonexistent_parent"); + C(none.empty(), "unregistered parent has no children"); + P(); +} + +int main() { + std::cout << "Step 1958: OperationDomain\n"; + t1(); t2(); t3(); t4(); t5(); + std::cout << "\n" << p << "/" << (p + f) << " passed\n"; + return f > 0 ? 1 : 0; +} diff --git a/editor/tests/step1959_test.cpp b/editor/tests/step1959_test.cpp new file mode 100644 index 0000000..ad7166a --- /dev/null +++ b/editor/tests/step1959_test.cpp @@ -0,0 +1,93 @@ +// Step 1959: LibraryCapabilityRecord +// +// t1: default field values are sane +// t2: validator rejects score < 0.0 +// t3: validator rejects score > 1.0 +// t4: validator rejects empty libraryId or empty operationDomain +// t5: validator rejects notApplicable=true with score > 0.0; accepts valid records + +#include "LibraryCapabilityRecord.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_record_fields); + ws::LibraryCapabilityRecord r; + C(r.libraryId.empty(), "empty libraryId by default"); + C(r.operationDomain.empty(), "empty operationDomain by default"); + C(r.score == 0.0f, "score defaults to 0.0"); + C(!r.notApplicable, "notApplicable defaults to false"); + C(r.preferredAPIs.empty(), "no APIs by default"); + P(); +} + +void t2() { + T(validator_rejects_negative_score); + ws::LibraryCapabilityRecordValidator v; + ws::LibraryCapabilityRecord r{"mylib", "serialization.json", -0.1f}; + auto res = v.validate(r); + C(!res.valid, "invalid"); + C(!res.error.empty(), "error message set"); + P(); +} + +void t3() { + T(validator_rejects_score_over_1); + ws::LibraryCapabilityRecordValidator v; + ws::LibraryCapabilityRecord r{"mylib", "serialization.json", 1.01f}; + auto res = v.validate(r); + C(!res.valid, "invalid for score 1.01"); + // Score exactly 1.0 must pass + r.score = 1.0f; + auto ok = v.validate(r); + C(ok.valid, "score 1.0 is valid"); + // Score exactly 0.0 must pass + r.score = 0.0f; + auto ok2 = v.validate(r); + C(ok2.valid, "score 0.0 is valid"); + P(); +} + +void t4() { + T(validator_rejects_empty_ids); + ws::LibraryCapabilityRecordValidator v; + ws::LibraryCapabilityRecord r{"", "serialization.json", 0.9f}; + C(!v.validate(r).valid, "empty libraryId invalid"); + r.libraryId = "mylib"; + r.operationDomain = ""; + C(!v.validate(r).valid, "empty operationDomain invalid"); + r.operationDomain = "serialization.json"; + C(v.validate(r).valid, "fully populated record is valid"); + P(); +} + +void t5() { + T(validator_rejects_notApplicable_with_nonzero_score); + ws::LibraryCapabilityRecordValidator v; + ws::LibraryCapabilityRecord r{"mylib", "compute.matrix", 0.5f}; + r.notApplicable = true; + auto bad = v.validate(r); + C(!bad.valid, "notApplicable=true with score=0.5 is invalid"); + // notApplicable=true with score=0.0 is fine + r.score = 0.0f; + auto ok = v.validate(r); + C(ok.valid, "notApplicable=true with score=0.0 is valid"); + // notApplicable=false with any valid score is fine + r.notApplicable = false; + r.score = 0.95f; + C(v.validate(r).valid, "notApplicable=false score=0.95 valid"); + P(); +} + +int main() { + std::cout << "Step 1959: LibraryCapabilityRecord\n"; + t1(); t2(); t3(); t4(); t5(); + std::cout << "\n" << p << "/" << (p + f) << " passed\n"; + return f > 0 ? 1 : 0; +} diff --git a/editor/tests/step1960_test.cpp b/editor/tests/step1960_test.cpp new file mode 100644 index 0000000..2324b54 --- /dev/null +++ b/editor/tests/step1960_test.cpp @@ -0,0 +1,95 @@ +// Step 1960: LibraryCapabilityLedger +// +// t1: set and get round-trip; has() before and after +// t2: score() returns correct value for known pair +// t3: score() returns 0.0 for unknown (library, domain) pair +// t4: allForLibrary() returns all records for one library +// t5: libraries() and domains() return sorted unique lists + +#include "LibraryCapabilityLedger.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::LibraryCapabilityLedger makeLedger() { + ws::LibraryCapabilityLedger l; + l.set({"nlohmann_json", "serialization.json", 0.92f}); + l.set({"protobuf", "serialization.binary", 0.95f}); + l.set({"protobuf", "serialization.json", 0.60f}); + l.set({"cublas", "compute.matrix", 0.99f}); + l.set({"numpy", "compute.matrix", 0.95f}); + l.set({"numpy", "compute.statistics", 0.93f}); + return l; +} + +void t1() { + T(set_get_and_has); + ws::LibraryCapabilityLedger l; + C(!l.has("nlohmann_json", "serialization.json"), "not present before set"); + l.set({"nlohmann_json", "serialization.json", 0.92f}); + C(l.has("nlohmann_json", "serialization.json"), "present after set"); + auto rec = l.get("nlohmann_json", "serialization.json"); + C(rec.has_value(), "get returns value"); + C(rec->score == 0.92f, "score 0.92"); + C(rec->libraryId == "nlohmann_json", "libraryId"); + P(); +} + +void t2() { + T(score_returns_correct_value); + auto l = makeLedger(); + C(l.score("cublas", "compute.matrix") == 0.99f, "cublas/compute.matrix 0.99"); + C(l.score("numpy", "compute.matrix") == 0.95f, "numpy/compute.matrix 0.95"); + C(l.score("protobuf", "serialization.binary") == 0.95f, "protobuf/serial.binary 0.95"); + C(l.score("protobuf", "serialization.json") == 0.60f, "protobuf/serial.json 0.60"); + P(); +} + +void t3() { + T(score_returns_zero_for_unknown_pair); + auto l = makeLedger(); + C(l.score("unknown_lib", "compute.matrix") == 0.0f, "unknown lib -> 0.0"); + C(l.score("cublas", "unknown_domain") == 0.0f, "unknown domain -> 0.0"); + C(l.score("unknown_lib", "unknown_domain") == 0.0f, "both unknown -> 0.0"); + P(); +} + +void t4() { + T(allForLibrary_returns_all_records_for_library); + auto l = makeLedger(); + auto protobuf_recs = l.allForLibrary("protobuf"); + C(protobuf_recs.size() == 2, "protobuf has 2 records"); + auto numpy_recs = l.allForLibrary("numpy"); + C(numpy_recs.size() == 2, "numpy has 2 records"); + auto none = l.allForLibrary("unknown"); + C(none.empty(), "unknown library has no records"); + auto json_recs = l.allForDomain("serialization.json"); + C(json_recs.size() == 2, "serialization.json has 2 records"); + P(); +} + +void t5() { + T(libraries_and_domains_return_sorted_unique); + auto l = makeLedger(); + auto libs = l.libraries(); + C(libs.size() == 4, "4 unique libraries"); + C(std::is_sorted(libs.begin(), libs.end()), "libraries sorted"); + auto doms = l.domains(); + C(doms.size() == 4, "4 unique domains"); + C(std::is_sorted(doms.begin(), doms.end()), "domains sorted"); + C(l.size() == 6, "total 6 records"); + P(); +} + +int main() { + std::cout << "Step 1960: LibraryCapabilityLedger\n"; + t1(); t2(); t3(); t4(); t5(); + std::cout << "\n" << p << "/" << (p + f) << " passed\n"; + return f > 0 ? 1 : 0; +} diff --git a/editor/tests/step1961_test.cpp b/editor/tests/step1961_test.cpp new file mode 100644 index 0000000..c2f212b --- /dev/null +++ b/editor/tests/step1961_test.cpp @@ -0,0 +1,90 @@ +// Step 1961: OperationTaxonomy +// +// t1: domainCount() >= 40 +// t2: topLevelCategories() has >= 6 entries +// t3: classify("serialize to JSON") == "serialization.json" +// t4: classify("matrix multiply on GPU") == "compute.matrix" +// t5: classify with several known domains; unknown text returns "unknown" + +#include "OperationTaxonomy.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(domain_count_at_least_40); + auto tax = ws::OperationTaxonomy::buildDefault(); + auto count = tax.domainCount(); + C(count >= 40, "at least 40 domains (got " + std::to_string(count) + ")"); + P(); +} + +void t2() { + T(top_level_categories_at_least_6); + auto tax = ws::OperationTaxonomy::buildDefault(); + auto cats = tax.topLevelCategories(); + C(cats.size() >= 6, "at least 6 top-level categories (got " + std::to_string(cats.size()) + ")"); + // Must include the 6 core categories from the plan + bool hasSer = false, hasCom = false, hasNet = false; + bool hasIo = false, hasCry = false, hasDb = false; + for (const auto& c : cats) { + if (c == "serialization") hasSer = true; + if (c == "compute") hasCom = true; + if (c == "network") hasNet = true; + if (c == "io") hasIo = true; + if (c == "crypto") hasCry = true; + if (c == "db") hasDb = true; + } + C(hasSer && hasCom && hasNet && hasIo && hasCry && hasDb, + "all 6 core categories present"); + P(); +} + +void t3() { + T(classify_serialize_to_json); + auto tax = ws::OperationTaxonomy::buildDefault(); + auto domain = tax.classify("serialize to JSON"); + C(domain == "serialization.json", + "expected serialization.json, got " + domain); + // Also check uppercase input is handled + auto domain2 = tax.classify("SERIALIZE TO JSON"); + C(domain2 == "serialization.json", "uppercase handled"); + P(); +} + +void t4() { + T(classify_matrix_multiply_on_GPU); + auto tax = ws::OperationTaxonomy::buildDefault(); + auto domain = tax.classify("matrix multiply on GPU"); + C(domain == "compute.matrix", + "expected compute.matrix, got " + domain); + auto domain2 = tax.classify("dense matrix multiplication using CUDA"); + C(domain2 == "compute.matrix", "CUDA matrix also compute.matrix"); + P(); +} + +void t5() { + T(classify_various_domains_and_unknown); + auto tax = ws::OperationTaxonomy::buildDefault(); + auto h = tax.classify("compute sha256 hash of a file"); + C(h == "crypto.hash", "sha256 -> crypto.hash, got " + h); + auto s = tax.classify("sql select query from table"); + C(s == "db.query.sql", "sql select -> db.query.sql, got " + s); + auto y = tax.classify("parse yaml configuration file"); + C(y == "serialization.yaml", "yaml -> serialization.yaml, got " + y); + auto u = tax.classify("xyzzy frobble zogzog"); + C(u == "unknown", "unknown text -> unknown, got " + u); + P(); +} + +int main() { + std::cout << "Step 1961: OperationTaxonomy\n"; + t1(); t2(); t3(); t4(); t5(); + std::cout << "\n" << p << "/" << (p + f) << " passed\n"; + return f > 0 ? 1 : 0; +} diff --git a/editor/tests/step1962_test.cpp b/editor/tests/step1962_test.cpp new file mode 100644 index 0000000..dc700cc --- /dev/null +++ b/editor/tests/step1962_test.cpp @@ -0,0 +1,92 @@ +// Step 1962: Sprint 286 Integration +// +// t1: buildSeedLedger() contains entries for all 8 libraries +// t2: score("cublas", "compute.matrix") == 0.99 +// t3: score("protobuf", "serialization.json") == 0.60 (not 0.95) +// t4: score("unknown_lib", "unknown_domain") == 0.0 +// t5: Sprint286IntegrationSummary metadata correct + +#include "Sprint286IntegrationSummary.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; } + +void t1() { + T(seed_ledger_has_all_8_libraries); + auto ledger = ws::Sprint286IntegrationSummary::buildSeedLedger(); + auto libs = ledger.libraries(); + auto has = [&](const std::string& lib) { + return std::find(libs.begin(), libs.end(), lib) != libs.end(); + }; + C(has("nlohmann_json"), "nlohmann_json"); + C(has("protobuf"), "protobuf"); + C(has("serde_json"), "serde_json"); + C(has("cublas"), "cublas"); + C(has("thrust"), "thrust"); + C(has("numpy"), "numpy"); + C(has("tokio"), "tokio"); + C(has("boost.asio"), "boost.asio"); + C(libs.size() == 8, "exactly 8 libraries"); + P(); +} + +void t2() { + T(cublas_compute_matrix_score_0_99); + auto ledger = ws::Sprint286IntegrationSummary::buildSeedLedger(); + float s = ledger.score("cublas", "compute.matrix"); + C(s == 0.99f, "cublas/compute.matrix == 0.99, got " + std::to_string(s)); + // Also check that preferredAPIs are set + auto rec = ledger.get("cublas", "compute.matrix"); + C(rec.has_value(), "record present"); + C(!rec->preferredAPIs.empty(), "preferredAPIs not empty"); + P(); +} + +void t3() { + T(protobuf_serialization_json_score_0_60_not_0_95); + auto ledger = ws::Sprint286IntegrationSummary::buildSeedLedger(); + float json_score = ledger.score("protobuf", "serialization.json"); + float binary_score = ledger.score("protobuf", "serialization.binary"); + C(json_score == 0.60f, "serialization.json == 0.60, got " + std::to_string(json_score)); + C(binary_score == 0.95f, "serialization.binary == 0.95, got " + std::to_string(binary_score)); + // Domain specificity: json != binary + C(json_score != binary_score, "json and binary scores differ"); + P(); +} + +void t4() { + T(unknown_pair_returns_zero); + auto ledger = ws::Sprint286IntegrationSummary::buildSeedLedger(); + C(ledger.score("unknown_lib", "unknown_domain") == 0.0f, "both unknown"); + C(ledger.score("cublas", "serialization.json") == 0.0f, "cublas no json score"); + C(!ledger.has("unknown_lib", "compute.matrix"), "has() false for unknown"); + // verifyLookups must pass + C(ws::Sprint286IntegrationSummary::verifyLookups(ledger), "verifyLookups passes"); + P(); +} + +void t5() { + T(sprint286_integration_summary_metadata); + ws::Sprint286IntegrationSummary s; + C(s.stepsCompleted == 5, "5 steps"); + C(s.success, "success flag"); + C(s.sprintName() == "Sprint 286: OperationTaxonomy + LibraryCapabilityLedger", "name"); + // Total seed records: nlohmann(1) + protobuf(2) + serde(1) + cublas(1) + + // thrust(1) + numpy(2) + tokio(2) + boost.asio(2) = 12 + auto ledger = ws::Sprint286IntegrationSummary::buildSeedLedger(); + C(ledger.size() == 12, "12 total seed records, got " + std::to_string(ledger.size())); + P(); +} + +int main() { + std::cout << "Step 1962: Sprint 286 Integration\n"; + t1(); t2(); t3(); t4(); t5(); + std::cout << "\n" << p << "/" << (p + f) << " passed\n"; + return f > 0 ? 1 : 0; +}