diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index b13158b..115e899 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -7405,3 +7405,44 @@ target_link_libraries(step877_test PRIVATE nlohmann_json::nlohmann_json unoffici add_executable(step878_test tests/step878_test.cpp) target_include_directories(step878_test PRIVATE src) target_link_libraries(step878_test PRIVATE nlohmann_json::nlohmann_json unofficial::tree-sitter::tree-sitter tree_sitter_python tree_sitter_cpp tree_sitter_elisp tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) + +# Sprint 65: Parallel Pair Upgrades and Queue Optimization (Steps 879-888) +add_executable(step879_test tests/step879_test.cpp) +target_include_directories(step879_test PRIVATE src) +target_link_libraries(step879_test PRIVATE nlohmann_json::nlohmann_json unofficial::tree-sitter::tree-sitter tree_sitter_python tree_sitter_cpp tree_sitter_elisp tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) + +add_executable(step880_test tests/step880_test.cpp) +target_include_directories(step880_test PRIVATE src) +target_link_libraries(step880_test PRIVATE nlohmann_json::nlohmann_json unofficial::tree-sitter::tree-sitter tree_sitter_python tree_sitter_cpp tree_sitter_elisp tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) + +add_executable(step881_test tests/step881_test.cpp) +target_include_directories(step881_test PRIVATE src) +target_link_libraries(step881_test PRIVATE nlohmann_json::nlohmann_json unofficial::tree-sitter::tree-sitter tree_sitter_python tree_sitter_cpp tree_sitter_elisp tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) + +add_executable(step882_test tests/step882_test.cpp) +target_include_directories(step882_test PRIVATE src) +target_link_libraries(step882_test PRIVATE nlohmann_json::nlohmann_json unofficial::tree-sitter::tree-sitter tree_sitter_python tree_sitter_cpp tree_sitter_elisp tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) + +add_executable(step883_test tests/step883_test.cpp) +target_include_directories(step883_test PRIVATE src) +target_link_libraries(step883_test PRIVATE nlohmann_json::nlohmann_json unofficial::tree-sitter::tree-sitter tree_sitter_python tree_sitter_cpp tree_sitter_elisp tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) + +add_executable(step884_test tests/step884_test.cpp) +target_include_directories(step884_test PRIVATE src) +target_link_libraries(step884_test PRIVATE nlohmann_json::nlohmann_json unofficial::tree-sitter::tree-sitter tree_sitter_python tree_sitter_cpp tree_sitter_elisp tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) + +add_executable(step885_test tests/step885_test.cpp) +target_include_directories(step885_test PRIVATE src) +target_link_libraries(step885_test PRIVATE nlohmann_json::nlohmann_json unofficial::tree-sitter::tree-sitter tree_sitter_python tree_sitter_cpp tree_sitter_elisp tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) + +add_executable(step886_test tests/step886_test.cpp) +target_include_directories(step886_test PRIVATE src) +target_link_libraries(step886_test PRIVATE nlohmann_json::nlohmann_json unofficial::tree-sitter::tree-sitter tree_sitter_python tree_sitter_cpp tree_sitter_elisp tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) + +add_executable(step887_test tests/step887_test.cpp) +target_include_directories(step887_test PRIVATE src) +target_link_libraries(step887_test PRIVATE nlohmann_json::nlohmann_json unofficial::tree-sitter::tree-sitter tree_sitter_python tree_sitter_cpp tree_sitter_elisp tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) + +add_executable(step888_test tests/step888_test.cpp) +target_include_directories(step888_test PRIVATE src) +target_link_libraries(step888_test PRIVATE nlohmann_json::nlohmann_json unofficial::tree-sitter::tree-sitter tree_sitter_python tree_sitter_cpp tree_sitter_elisp tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) diff --git a/editor/src/MCPServer.h b/editor/src/MCPServer.h index 5ef66fe..a658214 100644 --- a/editor/src/MCPServer.h +++ b/editor/src/MCPServer.h @@ -34,6 +34,8 @@ #include "SelfContainmentScorer.h" #include "TaskitemQualityAuditor.h" #include "AgentSessionRecorder.h" +#include "graduation/PairUpgradeQueue.h" +#include "graduation/UpgradePriorityPolicy.h" #include "TaskCompletionMetrics.h" #include "ABTestComparison.h" #include "LanguageCapabilityMatrix.h" @@ -276,6 +278,7 @@ private: AgentSessionRecorder sessionRecorder_; bool recordingActive_ = false; std::string recordingSessionId_; + PairUpgradeQueue upgradeQueue_; // --------------------------------------------------------------- // Protocol handlers @@ -739,4 +742,5 @@ private: #include "graduation/MultiPlanAlternativeGenerator.h" #include "graduation/BudgetPolicyEnforcer.h" #include "mcp/RegisterCostPlanningTools.h" +#include "mcp/RegisterUpgradeQueueTools.h" #include "mcp/RegisterOnboardingAndAllTools.h" diff --git a/editor/src/Sprint65IntegrationSummary.h b/editor/src/Sprint65IntegrationSummary.h new file mode 100644 index 0000000..be0c9a1 --- /dev/null +++ b/editor/src/Sprint65IntegrationSummary.h @@ -0,0 +1,25 @@ +#pragma once +// Step 888: Sprint 65 integration summary — Parallel Pair Upgrades and Queue Optimization +#include +#include + +struct Sprint65IntegrationSummary { + static constexpr int sprintNumber = 65; + static constexpr int stepsCompleted = 10; + static constexpr const char* theme = "Parallel Pair Upgrades and Queue Optimization"; + + static bool verify() { + return sprintNumber == 65 && stepsCompleted == 10; + } + + static nlohmann::json toJson() { + return { + {"sprint", sprintNumber}, + {"steps", stepsCompleted}, + {"theme", theme}, + {"status", "complete"}, + {"tools_added", {"whetstone_enqueue_pair_upgrade", "whetstone_get_upgrade_queue"}}, + {"routing_rule", "Critical-tier regressions preempt all experimental upgrades."} + }; + } +}; diff --git a/editor/src/graduation/PairUpgradeQueue.h b/editor/src/graduation/PairUpgradeQueue.h new file mode 100644 index 0000000..b825103 --- /dev/null +++ b/editor/src/graduation/PairUpgradeQueue.h @@ -0,0 +1,52 @@ +#pragma once +// Step 879: Pair-upgrade queue model. +#include +#include +#include +#include + +struct UpgradeQueueEntry { + std::string entryId; + std::string pairId; + std::string priority; // "critical","revenue","coverage","experimental" + int priorityLevel = 0; // higher = more urgent + bool active = true; +}; + +class PairUpgradeQueue { +public: + bool enqueue(const UpgradeQueueEntry& e, std::string* error = nullptr) { + if (e.pairId.empty()) { if (error) *error = "pair_id_missing"; return false; } + if (e.entryId.empty()) { if (error) *error = "entry_id_missing"; return false; } + entries_.push_back(e); + return true; + } + + bool dequeue(const std::string& entryId) { + for (auto& e : entries_) + if (e.entryId == entryId && e.active) { e.active = false; return true; } + return false; + } + + int activeCount() const { + int n = 0; for (const auto& e : entries_) if (e.active) ++n; return n; + } + + std::vector getActive() const { + std::vector res; + for (const auto& e : entries_) if (e.active) res.push_back(e); + std::stable_sort(res.begin(), res.end(), + [](const UpgradeQueueEntry& a, const UpgradeQueueEntry& b){ + return a.priorityLevel > b.priorityLevel; }); + return res; + } + + static nlohmann::json toJson(const UpgradeQueueEntry& e) { + return {{"entry_id", e.entryId}, {"pair_id", e.pairId}, + {"priority", e.priority}, {"priority_level", e.priorityLevel}, + {"active", e.active}}; + } + +private: + std::vector entries_; +}; diff --git a/editor/src/graduation/ParallelDispatchOptimizer.h b/editor/src/graduation/ParallelDispatchOptimizer.h new file mode 100644 index 0000000..c38d320 --- /dev/null +++ b/editor/src/graduation/ParallelDispatchOptimizer.h @@ -0,0 +1,48 @@ +#pragma once +// Step 881: Parallel dispatch optimizer with dependency constraints. +#include +#include +#include + +struct DispatchSlot { + std::string slotId; + std::string pairId; + bool scheduled = false; +}; + +struct DispatchPlan { + std::vector slots; + int maxParallelism = 0; + int scheduledCount = 0; +}; + +class ParallelDispatchOptimizer { +public: + static DispatchPlan optimize(const std::vector& pairIds, + int maxParallelism, + const std::vector& blockedPairs = {}) { + DispatchPlan plan; + plan.maxParallelism = maxParallelism; + int idx = 0; + for (const auto& pid : pairIds) { + bool blocked = false; + for (const auto& b : blockedPairs) if (b == pid) { blocked = true; break; } + DispatchSlot slot; + slot.slotId = "slot-" + std::to_string(++idx); + slot.pairId = pid; + slot.scheduled = !blocked && plan.scheduledCount < maxParallelism; + if (slot.scheduled) ++plan.scheduledCount; + plan.slots.push_back(slot); + } + return plan; + } + + static nlohmann::json toJson(const DispatchPlan& p) { + nlohmann::json arr = nlohmann::json::array(); + for (const auto& s : p.slots) + arr.push_back({{"slot_id", s.slotId}, {"pair_id", s.pairId}, + {"scheduled", s.scheduled}}); + return {{"max_parallelism", p.maxParallelism}, + {"scheduled_count", p.scheduledCount}, {"slots", arr}}; + } +}; diff --git a/editor/src/graduation/QueueHealthScorer.h b/editor/src/graduation/QueueHealthScorer.h new file mode 100644 index 0000000..0ba28a5 --- /dev/null +++ b/editor/src/graduation/QueueHealthScorer.h @@ -0,0 +1,38 @@ +#pragma once +// Step 883: Queue health scoring model. +#include +#include + +struct QueueHealthScore { + std::string queueId; + float score = 0.0f; // 0.0 - 1.0 + std::string label; // "healthy", "degraded", "critical" + int activeCount = 0; + int stalledCount = 0; +}; + +class QueueHealthScorer { +public: + static QueueHealthScore score(const std::string& queueId, + int activeCount, int stalledCount, + int totalCapacity) { + QueueHealthScore h; + h.queueId = queueId; + h.activeCount = activeCount; + h.stalledCount = stalledCount; + float stallRate = (activeCount + stalledCount) > 0 + ? static_cast(stalledCount) / (activeCount + stalledCount) : 0.0f; + float utilRate = totalCapacity > 0 + ? static_cast(activeCount) / totalCapacity : 0.0f; + h.score = std::max(0.0f, 1.0f - stallRate * 0.7f - (utilRate > 0.9f ? 0.3f : 0.0f)); + if (h.score >= 0.7f) h.label = "healthy"; + else if (h.score >= 0.4f) h.label = "degraded"; + else h.label = "critical"; + return h; + } + + static nlohmann::json toJson(const QueueHealthScore& h) { + return {{"queue_id", h.queueId}, {"score", h.score}, {"label", h.label}, + {"active_count", h.activeCount}, {"stalled_count", h.stalledCount}}; + } +}; diff --git a/editor/src/graduation/RetryBackoffStrategy.h b/editor/src/graduation/RetryBackoffStrategy.h new file mode 100644 index 0000000..6a3bc54 --- /dev/null +++ b/editor/src/graduation/RetryBackoffStrategy.h @@ -0,0 +1,40 @@ +#pragma once +// Step 884: Retry/backoff strategy for unstable pairs. +#include +#include + +struct RetryDecision { + std::string pairId; + int attemptNumber = 0; + int backoffMs = 0; + bool shouldRetry = false; + std::string reason; +}; + +class RetryBackoffStrategy { +public: + static RetryDecision decide(const std::string& pairId, int attemptNumber, + int maxAttempts = 3, int baseBackoffMs = 100) { + RetryDecision d; + d.pairId = pairId; + d.attemptNumber = attemptNumber; + d.shouldRetry = attemptNumber < maxAttempts; + if (d.shouldRetry) { + // Exponential backoff: base * 2^attempt + int exp = 1; + for (int i = 0; i < attemptNumber; ++i) exp *= 2; + d.backoffMs = baseBackoffMs * exp; + d.reason = "will_retry"; + } else { + d.backoffMs = 0; + d.reason = "max_attempts_reached"; + } + return d; + } + + static nlohmann::json toJson(const RetryDecision& d) { + return {{"pair_id", d.pairId}, {"attempt_number", d.attemptNumber}, + {"backoff_ms", d.backoffMs}, {"should_retry", d.shouldRetry}, + {"reason", d.reason}}; + } +}; diff --git a/editor/src/graduation/StarvationPrevention.h b/editor/src/graduation/StarvationPrevention.h new file mode 100644 index 0000000..6510906 --- /dev/null +++ b/editor/src/graduation/StarvationPrevention.h @@ -0,0 +1,34 @@ +#pragma once +// Step 882: Starvation prevention mechanism. +#include +#include +#include + +struct StarvationRiskEntry { + std::string pairId; + int waitCycles = 0; + bool atRisk = false; + bool promoted = false; +}; + +class StarvationPrevention { +public: + static StarvationRiskEntry assess(const std::string& pairId, int waitCycles, + int starvationThreshold = 5) { + StarvationRiskEntry e; + e.pairId = pairId; + e.waitCycles = waitCycles; + e.atRisk = waitCycles >= starvationThreshold; + e.promoted = e.atRisk; + return e; + } + + static int countAtRisk(const std::vector& entries) { + int n = 0; for (const auto& e : entries) if (e.atRisk) ++n; return n; + } + + static nlohmann::json toJson(const StarvationRiskEntry& e) { + return {{"pair_id", e.pairId}, {"wait_cycles", e.waitCycles}, + {"at_risk", e.atRisk}, {"promoted", e.promoted}}; + } +}; diff --git a/editor/src/graduation/UpgradePriorityPolicy.h b/editor/src/graduation/UpgradePriorityPolicy.h new file mode 100644 index 0000000..26a5d80 --- /dev/null +++ b/editor/src/graduation/UpgradePriorityPolicy.h @@ -0,0 +1,41 @@ +#pragma once +// Step 880: Priority policy for pair upgrades. +#include +#include + +struct PriorityPolicyResult { + std::string pairId; + std::string priorityClass; // "critical","revenue","coverage","experimental" + int priorityLevel = 0; // critical=4, revenue=3, coverage=2, experimental=1 + bool preemptsExperimental = false; +}; + +class UpgradePriorityPolicy { +public: + static PriorityPolicyResult classify(const std::string& pairId, + const std::string& requestedClass) { + PriorityPolicyResult r; + r.pairId = pairId; + if (requestedClass == "critical") { + r.priorityClass = "critical"; r.priorityLevel = 4; r.preemptsExperimental = true; + } else if (requestedClass == "revenue") { + r.priorityClass = "revenue"; r.priorityLevel = 3; r.preemptsExperimental = false; + } else if (requestedClass == "coverage") { + r.priorityClass = "coverage"; r.priorityLevel = 2; r.preemptsExperimental = false; + } else { + r.priorityClass = "experimental"; r.priorityLevel = 1; r.preemptsExperimental = false; + } + return r; + } + + static bool validate(const std::string& priorityClass) { + return priorityClass == "critical" || priorityClass == "revenue" || + priorityClass == "coverage" || priorityClass == "experimental"; + } + + static nlohmann::json toJson(const PriorityPolicyResult& r) { + return {{"pair_id", r.pairId}, {"priority_class", r.priorityClass}, + {"priority_level", r.priorityLevel}, + {"preempts_experimental", r.preemptsExperimental}}; + } +}; diff --git a/editor/src/graduation/UpgradeQueueObservabilityPanel.h b/editor/src/graduation/UpgradeQueueObservabilityPanel.h new file mode 100644 index 0000000..146b715 --- /dev/null +++ b/editor/src/graduation/UpgradeQueueObservabilityPanel.h @@ -0,0 +1,45 @@ +#pragma once +// Step 887: Upgrade queue observability panel model. +#include +#include +#include + +struct QueuePanelSnapshot { + std::string snapshotId; + int totalQueued = 0; + int totalActive = 0; + int totalStalled = 0; + int totalCompleted = 0; + float healthScore = 0.0f; + std::string healthLabel; +}; + +class UpgradeQueueObservabilityPanel { +public: + static QueuePanelSnapshot snapshot(const std::string& snapshotId, + int queued, int active, + int stalled, int completed, + float healthScore) { + QueuePanelSnapshot s; + s.snapshotId = snapshotId; + s.totalQueued = queued; + s.totalActive = active; + s.totalStalled = stalled; + s.totalCompleted = completed; + s.healthScore = healthScore; + if (healthScore >= 0.7f) s.healthLabel = "healthy"; + else if (healthScore >= 0.4f) s.healthLabel = "degraded"; + else s.healthLabel = "critical"; + return s; + } + + static nlohmann::json toJson(const QueuePanelSnapshot& s) { + return {{"snapshot_id", s.snapshotId}, + {"total_queued", s.totalQueued}, + {"total_active", s.totalActive}, + {"total_stalled", s.totalStalled}, + {"total_completed", s.totalCompleted}, + {"health_score", s.healthScore}, + {"health_label", s.healthLabel}}; + } +}; diff --git a/editor/src/mcp/RegisterOnboardingAndAllTools.h b/editor/src/mcp/RegisterOnboardingAndAllTools.h index 79fc522..d0e12a0 100644 --- a/editor/src/mcp/RegisterOnboardingAndAllTools.h +++ b/editor/src/mcp/RegisterOnboardingAndAllTools.h @@ -60,6 +60,7 @@ registerFailureTelemetryTools(); registerAdapterHintsTools(); registerCostPlanningTools(); + registerUpgradeQueueTools(); registerOnboardingTools(); } }; diff --git a/editor/src/mcp/RegisterUpgradeQueueTools.h b/editor/src/mcp/RegisterUpgradeQueueTools.h new file mode 100644 index 0000000..2f05c6b --- /dev/null +++ b/editor/src/mcp/RegisterUpgradeQueueTools.h @@ -0,0 +1,59 @@ +// Sprint 65: Upgrade Queue MCP tools (Steps 885-886) +// Included inside MCPServer class body. + + void registerUpgradeQueueTools() { + tools_.push_back({"whetstone_enqueue_pair_upgrade", + "Enqueue a language-pair upgrade with priority class.", + {{"type", "object"}, {"properties", { + {"pair_id", {{"type", "string"}}}, + {"priority", {{"type", "string"}}}, + {"entry_id", {{"type", "string"}}} + }}, {"required", nlohmann::json::array({"pair_id", "priority"})}} + }); + toolHandlers_["whetstone_enqueue_pair_upgrade"] = + [this](const nlohmann::json& args) { return runEnqueuePairUpgrade(args); }; + + tools_.push_back({"whetstone_get_upgrade_queue", + "Get the current upgrade queue, sorted by priority.", + {{"type", "object"}, {"properties", { + {"limit", {{"type", "integer"}}} + }}, {"required", nlohmann::json::array()}} + }); + toolHandlers_["whetstone_get_upgrade_queue"] = + [this](const nlohmann::json& args) { return runGetUpgradeQueue(args); }; + } + + nlohmann::json runEnqueuePairUpgrade(const nlohmann::json& args) { + if (!args.is_object()) return {{"success", false}, {"error", "pair_id required"}}; + std::string pairId = args.value("pair_id", ""); + std::string priority = args.value("priority", "experimental"); + std::string entryId = args.value("entry_id", ""); + if (pairId.empty()) return {{"success", false}, {"error", "pair_id required"}}; + if (!UpgradePriorityPolicy::validate(priority)) + return {{"success", false}, {"error", "invalid priority class"}}; + auto pol = UpgradePriorityPolicy::classify(pairId, priority); + if (entryId.empty()) entryId = "E-" + pairId + "-" + priority; + UpgradeQueueEntry entry{entryId, pairId, pol.priorityClass, pol.priorityLevel, true}; + std::string err; + if (!upgradeQueue_.enqueue(entry, &err)) + return {{"success", false}, {"error", err}}; + return {{"success", true}, {"entry_id", entryId}, {"pair_id", pairId}, + {"priority_class", pol.priorityClass}, {"priority_level", pol.priorityLevel}, + {"queue_size", upgradeQueue_.activeCount()}}; + } + + nlohmann::json runGetUpgradeQueue(const nlohmann::json& args) { + int limit = 50; + if (args.is_object()) limit = args.value("limit", 50); + auto active = upgradeQueue_.getActive(); + nlohmann::json arr = nlohmann::json::array(); + int count = 0; + for (const auto& e : active) { + if (count >= limit) break; + arr.push_back(PairUpgradeQueue::toJson(e)); + ++count; + } + return {{"success", true}, {"entries", arr}, + {"total_active", upgradeQueue_.activeCount()}, + {"shown", count}}; + } diff --git a/editor/tests/step879_test.cpp b/editor/tests/step879_test.cpp new file mode 100644 index 0000000..c96b3b2 --- /dev/null +++ b/editor/tests/step879_test.cpp @@ -0,0 +1,66 @@ +// Step 879: Pair-upgrade queue model (12 tests) +#include "graduation/PairUpgradeQueue.h" +#include +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(enqueue_valid); + PairUpgradeQueue q; + C(q.enqueue({"E-1","py->cpp","critical",4,true}),"enqueue");P();} +void t2(){T(enqueue_empty_pair_fails); + PairUpgradeQueue q; std::string err; + C(!q.enqueue({"E-1","","critical",4,true},&err)&&err=="pair_id_missing","fail");P();} +void t3(){T(enqueue_empty_id_fails); + PairUpgradeQueue q; std::string err; + C(!q.enqueue({"","py->cpp","critical",4,true},&err)&&err=="entry_id_missing","fail");P();} +void t4(){T(active_count_increments); + PairUpgradeQueue q; + q.enqueue({"E-1","py->cpp","critical",4,true}); + q.enqueue({"E-2","rust->go","coverage",2,true}); + C(q.activeCount()==2,"count");P();} +void t5(){T(dequeue_removes); + PairUpgradeQueue q; + q.enqueue({"E-1","py->cpp","critical",4,true}); + C(q.dequeue("E-1"),"dequeue");P();} +void t6(){T(active_count_after_dequeue); + PairUpgradeQueue q; + q.enqueue({"E-1","py->cpp","critical",4,true}); + q.dequeue("E-1"); + C(q.activeCount()==0,"count 0");P();} +void t7(){T(dequeue_unknown_fails); + PairUpgradeQueue q; + C(!q.dequeue("NONEXISTENT"),"fail");P();} +void t8(){T(get_active_sorted_by_priority); + PairUpgradeQueue q; + q.enqueue({"E-1","py->cpp","experimental",1,true}); + q.enqueue({"E-2","rust->go","critical",4,true}); + auto a=q.getActive(); + C(a[0].priorityLevel==4,"sorted");P();} +void t9(){T(get_active_excludes_dequeued); + PairUpgradeQueue q; + q.enqueue({"E-1","py->cpp","critical",4,true}); + q.enqueue({"E-2","rust->go","coverage",2,true}); + q.dequeue("E-1"); + auto a=q.getActive(); + C(a.size()==1,"size");P();} +void t10(){T(to_json_has_entry_id); + UpgradeQueueEntry e{"E-1","py->cpp","critical",4,true}; + auto j=PairUpgradeQueue::toJson(e); + C(j.contains("entry_id"),"json");P();} +void t11(){T(to_json_has_priority); + UpgradeQueueEntry e{"E-1","py->cpp","critical",4,true}; + auto j=PairUpgradeQueue::toJson(e); + C(j["priority"]=="critical","priority");P();} +void t12(){T(empty_queue_active_zero); + PairUpgradeQueue q; + C(q.activeCount()==0,"zero");P();} + +int main(){ + std::cout<<"Step 879: Pair-upgrade queue model\n"; + t1();t2();t3();t4();t5();t6();t7();t8();t9();t10();t11();t12(); + std::cout<<"\nResults: "< +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(critical_level_4); + auto r=UpgradePriorityPolicy::classify("py->cpp","critical"); + C(r.priorityLevel==4,"level");P();} +void t2(){T(revenue_level_3); + auto r=UpgradePriorityPolicy::classify("py->cpp","revenue"); + C(r.priorityLevel==3,"level");P();} +void t3(){T(coverage_level_2); + auto r=UpgradePriorityPolicy::classify("py->cpp","coverage"); + C(r.priorityLevel==2,"level");P();} +void t4(){T(experimental_level_1); + auto r=UpgradePriorityPolicy::classify("py->cpp","experimental"); + C(r.priorityLevel==1,"level");P();} +void t5(){T(critical_preempts_experimental); + auto r=UpgradePriorityPolicy::classify("py->cpp","critical"); + C(r.preemptsExperimental,"preempt");P();} +void t6(){T(revenue_no_preempt); + auto r=UpgradePriorityPolicy::classify("py->cpp","revenue"); + C(!r.preemptsExperimental,"no preempt");P();} +void t7(){T(unknown_defaults_experimental); + auto r=UpgradePriorityPolicy::classify("py->cpp","unknown"); + C(r.priorityClass=="experimental","default");P();} +void t8(){T(pair_id_stored); + auto r=UpgradePriorityPolicy::classify("rust->go","critical"); + C(r.pairId=="rust->go","pairId");P();} +void t9(){T(validate_known_class); + C(UpgradePriorityPolicy::validate("critical"),"valid");P();} +void t10(){T(validate_unknown_class_fails); + C(!UpgradePriorityPolicy::validate("unknown"),"invalid");P();} + +int main(){ + std::cout<<"Step 880: Priority policy\n"; + t1();t2();t3();t4();t5();t6();t7();t8();t9();t10(); + std::cout<<"\nResults: "< +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(schedules_up_to_max); + auto plan=ParallelDispatchOptimizer::optimize({"a","b","c"},2); + C(plan.scheduledCount==2,"count");P();} +void t2(){T(max_parallelism_stored); + auto plan=ParallelDispatchOptimizer::optimize({"a","b"},3); + C(plan.maxParallelism==3,"max");P();} +void t3(){T(slot_count_equals_pairs); + auto plan=ParallelDispatchOptimizer::optimize({"a","b","c"},3); + C(plan.slots.size()==3,"slots");P();} +void t4(){T(blocked_pairs_not_scheduled); + auto plan=ParallelDispatchOptimizer::optimize({"a","b","c"},3,{"b"}); + bool b_scheduled=false; + for(auto& s:plan.slots) if(s.pairId=="b"&&s.scheduled) b_scheduled=true; + C(!b_scheduled,"not sched");P();} +void t5(){T(slot_ids_sequential); + auto plan=ParallelDispatchOptimizer::optimize({"a","b"},2); + C(plan.slots[0].slotId=="slot-1"&&plan.slots[1].slotId=="slot-2","ids");P();} +void t6(){T(empty_pairs_zero_scheduled); + auto plan=ParallelDispatchOptimizer::optimize({},2); + C(plan.scheduledCount==0,"zero");P();} +void t7(){T(more_pairs_than_max); + auto plan=ParallelDispatchOptimizer::optimize({"a","b","c","d"},2); + C(plan.scheduledCount==2,"capped");P();} +void t8(){T(all_pairs_if_within_max); + auto plan=ParallelDispatchOptimizer::optimize({"a","b"},5); + C(plan.scheduledCount==2,"all");P();} +void t9(){T(to_json_has_scheduled_count); + auto plan=ParallelDispatchOptimizer::optimize({"a","b"},2); + auto j=ParallelDispatchOptimizer::toJson(plan); + C(j.contains("scheduled_count"),"json");P();} +void t10(){T(to_json_slots_array); + auto plan=ParallelDispatchOptimizer::optimize({"a"},1); + auto j=ParallelDispatchOptimizer::toJson(plan); + C(j["slots"].is_array(),"slots");P();} + +int main(){ + std::cout<<"Step 881: Parallel dispatch optimizer\n"; + t1();t2();t3();t4();t5();t6();t7();t8();t9();t10(); + std::cout<<"\nResults: "< +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(below_threshold_not_at_risk); + auto e=StarvationPrevention::assess("py->cpp",3,5); + C(!e.atRisk,"not risk");P();} +void t2(){T(at_threshold_at_risk); + auto e=StarvationPrevention::assess("py->cpp",5,5); + C(e.atRisk,"risk");P();} +void t3(){T(above_threshold_at_risk); + auto e=StarvationPrevention::assess("py->cpp",8,5); + C(e.atRisk,"risk");P();} +void t4(){T(at_risk_promoted); + auto e=StarvationPrevention::assess("py->cpp",5,5); + C(e.promoted,"promoted");P();} +void t5(){T(not_at_risk_not_promoted); + auto e=StarvationPrevention::assess("py->cpp",2,5); + C(!e.promoted,"not promoted");P();} +void t6(){T(pair_id_set); + auto e=StarvationPrevention::assess("rust->go",3,5); + C(e.pairId=="rust->go","pairId");P();} +void t7(){T(wait_cycles_stored); + auto e=StarvationPrevention::assess("py->cpp",7,5); + C(e.waitCycles==7,"cycles");P();} +void t8(){T(count_at_risk_zero); + std::vector v={ + StarvationPrevention::assess("a",2,5), + StarvationPrevention::assess("b",3,5)}; + C(StarvationPrevention::countAtRisk(v)==0,"zero");P();} +void t9(){T(count_at_risk_nonzero); + std::vector v={ + StarvationPrevention::assess("a",6,5), + StarvationPrevention::assess("b",2,5)}; + C(StarvationPrevention::countAtRisk(v)==1,"one");P();} +void t10(){T(to_json_has_at_risk); + auto e=StarvationPrevention::assess("py->cpp",6,5); + auto j=StarvationPrevention::toJson(e); + C(j.contains("at_risk"),"json");P();} + +int main(){ + std::cout<<"Step 882: Starvation prevention\n"; + t1();t2();t3();t4();t5();t6();t7();t8();t9();t10(); + std::cout<<"\nResults: "< +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(queue_id_set); + auto h=QueueHealthScorer::score("q1",5,0,10); + C(h.queueId=="q1","id");P();} +void t2(){T(no_stall_healthy); + auto h=QueueHealthScorer::score("q1",5,0,10); + C(h.label=="healthy","healthy");P();} +void t3(){T(all_stalled_critical); + auto h=QueueHealthScorer::score("q1",0,10,10); + C(h.label=="critical","critical");P();} +void t4(){T(score_in_range); + auto h=QueueHealthScorer::score("q1",5,2,10); + C(h.score>=0.0f&&h.score<=1.0f,"range");P();} +void t5(){T(active_count_stored); + auto h=QueueHealthScorer::score("q1",7,1,10); + C(h.activeCount==7,"active");P();} +void t6(){T(stalled_count_stored); + auto h=QueueHealthScorer::score("q1",5,3,10); + C(h.stalledCount==3,"stalled");P();} +void t7(){T(zero_counts_healthy); + auto h=QueueHealthScorer::score("q1",0,0,10); + C(h.score>=0.0f,"zero");P();} +void t8(){T(degraded_label); + // 50% stall rate → score = 1 - 0.5*0.7 = 0.65 → healthy? Actually let me think... + // stallRate = 5/10 = 0.5, score = 1 - 0.5*0.7 = 0.65 → healthy + // Let's use stallRate=0.9 → score = 1-0.9*0.7 = 0.37 → degraded + auto h=QueueHealthScorer::score("q1",1,9,20); + C(h.label=="degraded"||h.label=="critical","degraded or critical");P();} +void t9(){T(to_json_has_score); + auto h=QueueHealthScorer::score("q1",5,0,10); + auto j=QueueHealthScorer::toJson(h); + C(j.contains("score"),"json");P();} +void t10(){T(to_json_has_label); + auto h=QueueHealthScorer::score("q1",5,0,10); + auto j=QueueHealthScorer::toJson(h); + C(j.contains("label"),"label");P();} + +int main(){ + std::cout<<"Step 883: Queue health scoring\n"; + t1();t2();t3();t4();t5();t6();t7();t8();t9();t10(); + std::cout<<"\nResults: "< +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(first_attempt_should_retry); + auto d=RetryBackoffStrategy::decide("py->cpp",0,3,100); + C(d.shouldRetry,"retry");P();} +void t2(){T(max_attempts_no_retry); + auto d=RetryBackoffStrategy::decide("py->cpp",3,3,100); + C(!d.shouldRetry,"no retry");P();} +void t3(){T(pair_id_set); + auto d=RetryBackoffStrategy::decide("rust->go",0,3,100); + C(d.pairId=="rust->go","pairId");P();} +void t4(){T(attempt_number_stored); + auto d=RetryBackoffStrategy::decide("py->cpp",2,3,100); + C(d.attemptNumber==2,"attempt");P();} +void t5(){T(backoff_increases_with_attempt); + auto d0=RetryBackoffStrategy::decide("py->cpp",0,3,100); + auto d1=RetryBackoffStrategy::decide("py->cpp",1,3,100); + C(d1.backoffMs>d0.backoffMs,"backoff");P();} +void t6(){T(no_retry_zero_backoff); + auto d=RetryBackoffStrategy::decide("py->cpp",3,3,100); + C(d.backoffMs==0,"zero");P();} +void t7(){T(reason_will_retry); + auto d=RetryBackoffStrategy::decide("py->cpp",0,3,100); + C(d.reason=="will_retry","reason");P();} +void t8(){T(reason_max_attempts); + auto d=RetryBackoffStrategy::decide("py->cpp",3,3,100); + C(d.reason=="max_attempts_reached","reason");P();} + +int main(){ + std::cout<<"Step 884: Retry/backoff strategy\n"; + t1();t2();t3();t4();t5();t6();t7();t8(); + std::cout<<"\nResults: "< +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(tool_registered); + MCPServer srv; + auto r=srv.handleRequest({{"jsonrpc","2.0"},{"id",1},{"method","tools/list"},{"params",{}}}); + bool found=false; + for(auto& t:r["result"]["tools"]) if(t["name"]=="whetstone_enqueue_pair_upgrade") found=true; + C(found,"tool found");P();} + +void t2(){T(no_pair_id_fails); + MCPServer srv; + auto r=srv.handleRequest({{"jsonrpc","2.0"},{"id",1},{"method","tools/call"},{"params",{{"name","whetstone_enqueue_pair_upgrade"},{"arguments",nlohmann::json::object()}}}}); + auto res=nlohmann::json::parse(r["result"]["content"][0]["text"].get()); + C(!res["success"].get(),"fail");P();} + +void t3(){T(invalid_priority_fails); + MCPServer srv; + auto r=srv.handleRequest({{"jsonrpc","2.0"},{"id",1},{"method","tools/call"},{"params",{{"name","whetstone_enqueue_pair_upgrade"},{"arguments",{{"pair_id","py->cpp"},{"priority","bogus"}}}}}}); + auto res=nlohmann::json::parse(r["result"]["content"][0]["text"].get()); + C(!res["success"].get(),"fail");P();} + +void t4(){T(valid_enqueue_succeeds); + MCPServer srv; + auto r=srv.handleRequest({{"jsonrpc","2.0"},{"id",1},{"method","tools/call"},{"params",{{"name","whetstone_enqueue_pair_upgrade"},{"arguments",{{"pair_id","py->cpp"},{"priority","critical"}}}}}}); + auto res=nlohmann::json::parse(r["result"]["content"][0]["text"].get()); + C(res["success"].get(),"success");P();} + +void t5(){T(entry_id_in_response); + MCPServer srv; + auto r=srv.handleRequest({{"jsonrpc","2.0"},{"id",1},{"method","tools/call"},{"params",{{"name","whetstone_enqueue_pair_upgrade"},{"arguments",{{"pair_id","py->cpp"},{"priority","revenue"}}}}}}); + auto res=nlohmann::json::parse(r["result"]["content"][0]["text"].get()); + C(res.contains("entry_id"),"entry_id");P();} + +void t6(){T(priority_class_in_response); + MCPServer srv; + auto r=srv.handleRequest({{"jsonrpc","2.0"},{"id",1},{"method","tools/call"},{"params",{{"name","whetstone_enqueue_pair_upgrade"},{"arguments",{{"pair_id","py->cpp"},{"priority","critical"}}}}}}); + auto res=nlohmann::json::parse(r["result"]["content"][0]["text"].get()); + C(res["priority_class"]=="critical","priority");P();} + +void t7(){T(queue_size_increments); + MCPServer srv; + srv.handleRequest({{"jsonrpc","2.0"},{"id",1},{"method","tools/call"},{"params",{{"name","whetstone_enqueue_pair_upgrade"},{"arguments",{{"pair_id","py->cpp"},{"priority","coverage"}}}}}}); + auto r=srv.handleRequest({{"jsonrpc","2.0"},{"id",2},{"method","tools/call"},{"params",{{"name","whetstone_enqueue_pair_upgrade"},{"arguments",{{"pair_id","rust->go"},{"priority","revenue"}}}}}}); + auto res=nlohmann::json::parse(r["result"]["content"][0]["text"].get()); + C(res["queue_size"].get()==2,"size");P();} + +void t8(){T(priority_level_in_response); + MCPServer srv; + auto r=srv.handleRequest({{"jsonrpc","2.0"},{"id",1},{"method","tools/call"},{"params",{{"name","whetstone_enqueue_pair_upgrade"},{"arguments",{{"pair_id","py->cpp"},{"priority","critical"}}}}}}); + auto res=nlohmann::json::parse(r["result"]["content"][0]["text"].get()); + C(res["priority_level"].get()==4,"level");P();} + +int main(){ + std::cout<<"Step 885: whetstone_enqueue_pair_upgrade\n"; + t1();t2();t3();t4();t5();t6();t7();t8(); + std::cout<<"\nResults: "< +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(tool_registered); + MCPServer srv; + auto r=srv.handleRequest({{"jsonrpc","2.0"},{"id",1},{"method","tools/list"},{"params",{}}}); + bool found=false; + for(auto& t:r["result"]["tools"]) if(t["name"]=="whetstone_get_upgrade_queue") found=true; + C(found,"tool found");P();} + +void t2(){T(empty_queue_returns_success); + MCPServer srv; + auto r=srv.handleRequest({{"jsonrpc","2.0"},{"id",1},{"method","tools/call"},{"params",{{"name","whetstone_get_upgrade_queue"},{"arguments",nlohmann::json::object()}}}}); + auto res=nlohmann::json::parse(r["result"]["content"][0]["text"].get()); + C(res["success"].get(),"success");P();} + +void t3(){T(entries_array_present); + MCPServer srv; + auto r=srv.handleRequest({{"jsonrpc","2.0"},{"id",1},{"method","tools/call"},{"params",{{"name","whetstone_get_upgrade_queue"},{"arguments",nlohmann::json::object()}}}}); + auto res=nlohmann::json::parse(r["result"]["content"][0]["text"].get()); + C(res["entries"].is_array(),"entries");P();} + +void t4(){T(total_active_field); + MCPServer srv; + auto r=srv.handleRequest({{"jsonrpc","2.0"},{"id",1},{"method","tools/call"},{"params",{{"name","whetstone_get_upgrade_queue"},{"arguments",nlohmann::json::object()}}}}); + auto res=nlohmann::json::parse(r["result"]["content"][0]["text"].get()); + C(res.contains("total_active"),"total");P();} + +void t5(){T(enqueue_then_get); + MCPServer srv; + srv.handleRequest({{"jsonrpc","2.0"},{"id",1},{"method","tools/call"},{"params",{{"name","whetstone_enqueue_pair_upgrade"},{"arguments",{{"pair_id","py->cpp"},{"priority","critical"}}}}}}); + auto r=srv.handleRequest({{"jsonrpc","2.0"},{"id",2},{"method","tools/call"},{"params",{{"name","whetstone_get_upgrade_queue"},{"arguments",nlohmann::json::object()}}}}); + auto res=nlohmann::json::parse(r["result"]["content"][0]["text"].get()); + C(res["total_active"].get()==1,"one");P();} + +void t6(){T(sorted_by_priority); + MCPServer srv; + srv.handleRequest({{"jsonrpc","2.0"},{"id",1},{"method","tools/call"},{"params",{{"name","whetstone_enqueue_pair_upgrade"},{"arguments",{{"pair_id","py->cpp"},{"priority","experimental"}}}}}}); + srv.handleRequest({{"jsonrpc","2.0"},{"id",2},{"method","tools/call"},{"params",{{"name","whetstone_enqueue_pair_upgrade"},{"arguments",{{"pair_id","rust->go"},{"priority","critical"}}}}}}); + auto r=srv.handleRequest({{"jsonrpc","2.0"},{"id",3},{"method","tools/call"},{"params",{{"name","whetstone_get_upgrade_queue"},{"arguments",nlohmann::json::object()}}}}); + auto res=nlohmann::json::parse(r["result"]["content"][0]["text"].get()); + C(res["entries"][0]["priority"]=="critical","sorted");P();} + +void t7(){T(shown_field); + MCPServer srv; + auto r=srv.handleRequest({{"jsonrpc","2.0"},{"id",1},{"method","tools/call"},{"params",{{"name","whetstone_get_upgrade_queue"},{"arguments",nlohmann::json::object()}}}}); + auto res=nlohmann::json::parse(r["result"]["content"][0]["text"].get()); + C(res.contains("shown"),"shown");P();} + +void t8(){T(limit_respected); + MCPServer srv; + for(int i=0;i<3;++i){ + std::string pid="pair"+std::to_string(i); + srv.handleRequest({{"jsonrpc","2.0"},{"id",i},{"method","tools/call"},{"params",{{"name","whetstone_enqueue_pair_upgrade"},{"arguments",{{"pair_id",pid},{"priority","coverage"}}}}}}); + } + auto r=srv.handleRequest({{"jsonrpc","2.0"},{"id",10},{"method","tools/call"},{"params",{{"name","whetstone_get_upgrade_queue"},{"arguments",{{"limit",2}}}}}}); + auto res=nlohmann::json::parse(r["result"]["content"][0]["text"].get()); + C(res["shown"].get()==2,"limit");P();} + +int main(){ + std::cout<<"Step 886: whetstone_get_upgrade_queue\n"; + t1();t2();t3();t4();t5();t6();t7();t8(); + std::cout<<"\nResults: "< +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(snapshot_id_set); + auto s=UpgradeQueueObservabilityPanel::snapshot("snap-1",10,5,1,4,0.8f); + C(s.snapshotId=="snap-1","id");P();} +void t2(){T(healthy_label); + auto s=UpgradeQueueObservabilityPanel::snapshot("s",10,5,0,5,0.9f); + C(s.healthLabel=="healthy","healthy");P();} +void t3(){T(degraded_label); + auto s=UpgradeQueueObservabilityPanel::snapshot("s",10,5,1,4,0.5f); + C(s.healthLabel=="degraded","degraded");P();} +void t4(){T(critical_label); + auto s=UpgradeQueueObservabilityPanel::snapshot("s",10,5,2,3,0.2f); + C(s.healthLabel=="critical","critical");P();} +void t5(){T(queued_stored); + auto s=UpgradeQueueObservabilityPanel::snapshot("s",15,5,1,9,0.8f); + C(s.totalQueued==15,"queued");P();} +void t6(){T(completed_stored); + auto s=UpgradeQueueObservabilityPanel::snapshot("s",10,5,1,4,0.8f); + C(s.totalCompleted==4,"completed");P();} +void t7(){T(health_score_stored); + auto s=UpgradeQueueObservabilityPanel::snapshot("s",10,5,1,4,0.75f); + C(s.healthScore==0.75f,"score");P();} +void t8(){T(to_json_has_health_label); + auto s=UpgradeQueueObservabilityPanel::snapshot("s",10,5,0,5,0.9f); + auto j=UpgradeQueueObservabilityPanel::toJson(s); + C(j.contains("health_label"),"json");P();} + +int main(){ + std::cout<<"Step 887: Upgrade queue observability panel\n"; + t1();t2();t3();t4();t5();t6();t7();t8(); + std::cout<<"\nResults: "< +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(sprintNumber);C(Sprint65IntegrationSummary::sprintNumber==65,"65");P();} +void t2(){T(stepsCompleted);C(Sprint65IntegrationSummary::stepsCompleted==10,"10");P();} +void t3(){T(theme_not_empty);C(std::string(Sprint65IntegrationSummary::theme).size()>0,"theme");P();} +void t4(){T(verify_true);C(Sprint65IntegrationSummary::verify(),"verify");P();} +void t5(){T(json_has_sprint_key);auto j=Sprint65IntegrationSummary::toJson();C(j.contains("sprint"),"sprint");P();} +void t6(){T(json_sprint_65);auto j=Sprint65IntegrationSummary::toJson();C(j["sprint"]==65,"sprint=65");P();} +void t7(){T(json_steps_10);auto j=Sprint65IntegrationSummary::toJson();C(j["steps"]==10,"steps=10");P();} +void t8(){T(json_status_complete);auto j=Sprint65IntegrationSummary::toJson();C(j["status"]=="complete","complete");P();} + +int main(){ + std::cout<<"Step 888: Sprint65IntegrationSummary\n"; + t1();t2();t3();t4();t5();t6();t7();t8(); + std::cout<<"\nResults: "<