From 33103b5d71ea8e507af9af958a4b93a8c01b6a3d Mon Sep 17 00:00:00 2001 From: Bill Date: Sun, 22 Feb 2026 19:21:19 -0700 Subject: [PATCH] Add migration planning tools and step899-908 test suite --- PROGRESS.md | 30 ++++++- editor/CMakeLists.txt | 40 +++++++++ editor/src/MCPServer.h | 6 ++ editor/src/graduation/MigrationAuditLog.h | 63 ++++++++++++++ .../src/graduation/MigrationCheckpointStore.h | 71 +++++++++++++++ .../graduation/MigrationFeasibilityEngine.h | 74 ++++++++++++++++ .../src/graduation/MigrationPathCandidate.h | 46 ++++++++++ editor/src/graduation/MigrationPathRanker.h | 27 ++++++ .../src/graduation/MigrationProgressTracker.h | 78 +++++++++++++++++ .../src/graduation/MigrationRollbackPolicy.h | 53 +++++++++++ .../src/graduation/MigrationStepSequencer.h | 69 +++++++++++++++ .../src/mcp/RegisterMigrationPlanningTools.h | 87 +++++++++++++++++++ .../src/mcp/RegisterOnboardingAndAllTools.h | 1 + editor/tests/step899_test.cpp | 42 +++++++++ editor/tests/step900_test.cpp | 54 ++++++++++++ editor/tests/step901_test.cpp | 47 ++++++++++ editor/tests/step902_test.cpp | 42 +++++++++ editor/tests/step903_test.cpp | 42 +++++++++ editor/tests/step904_test.cpp | 53 +++++++++++ editor/tests/step905_test.cpp | 58 +++++++++++++ editor/tests/step906_test.cpp | 49 +++++++++++ editor/tests/step907_test.cpp | 66 ++++++++++++++ editor/tests/step908_test.cpp | 31 +++++++ 23 files changed, 1125 insertions(+), 4 deletions(-) create mode 100644 editor/src/graduation/MigrationAuditLog.h create mode 100644 editor/src/graduation/MigrationCheckpointStore.h create mode 100644 editor/src/graduation/MigrationFeasibilityEngine.h create mode 100644 editor/src/graduation/MigrationPathCandidate.h create mode 100644 editor/src/graduation/MigrationPathRanker.h create mode 100644 editor/src/graduation/MigrationProgressTracker.h create mode 100644 editor/src/graduation/MigrationRollbackPolicy.h create mode 100644 editor/src/graduation/MigrationStepSequencer.h create mode 100644 editor/src/mcp/RegisterMigrationPlanningTools.h create mode 100644 editor/tests/step899_test.cpp create mode 100644 editor/tests/step900_test.cpp create mode 100644 editor/tests/step901_test.cpp create mode 100644 editor/tests/step902_test.cpp create mode 100644 editor/tests/step903_test.cpp create mode 100644 editor/tests/step904_test.cpp create mode 100644 editor/tests/step905_test.cpp create mode 100644 editor/tests/step906_test.cpp create mode 100644 editor/tests/step907_test.cpp create mode 100644 editor/tests/step908_test.cpp diff --git a/PROGRESS.md b/PROGRESS.md index e0eb565..8664489 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -16,11 +16,13 @@ Whetstone is a semantic annotation DSL (SemAnno) and structured editor for cross ## Current State -**Step 688 / Sprint 45 — COMPLETE** +**Step 858 / Sprint 62 — COMPLETE** -Last recorded: Sprint 45 integration summary (Step 688). 52/52 tests passing. -MCP tool count: **90 tools**. Architecture gate: all headers ≤ 600 lines. -`whetstone_mcp` binary: `/home/bill/Documents/CLionProjects/whetstone_DSL/editor/build-native/whetstone_mcp` (built 2026-02-19) +Last recorded: Sprint 62 (Step 858). 30/30 sprint 60-62 tests passing. +Sprints 46-62 complete (steps 689-858). Sprints 63-120 in progress. +Sprints 121-130 pre-built (steps 1449-1548, debug workflow tooling). +MCP tool count: **90+ tools**. Architecture gate: all headers ≤ 600 lines. +`whetstone_mcp` binary: `editor/build-native/whetstone_mcp_stable` --- @@ -73,6 +75,26 @@ MCP tool count: **90 tools**. Architecture gate: all headers ≤ 600 lines. | Sprint 43 | 674–678 | **Complete** | Context assembly: WorkspaceFileIndex, ContextSliceAssembler, TokenBudgetEnforcer, `whetstone_assemble_context` MCP tool. Tool count 86→87. | | Sprint 44 | 679–683 | **Complete** | Taskitem quality: PrerequisiteOpResolver, SelfContainmentScorer, TaskitemQualityAuditor, `whetstone_validate_taskitem` MCP tool. Tool count 87→88. | | Sprint 45 | 684–688 | **Complete** | Agent metrics: AgentSessionRecorder, TaskCompletionMetrics, ABTestComparison, `whetstone_start_recording` + `whetstone_get_metrics` MCP tools. Live tool-call instrumentation. Tool count 88→90. | +| Sprint 46 | 689–698 | **Complete** | Porting foundation: SemanticCoreIR, LanguageToIRAdapter, IRToLanguageAdapter, LanguageSupportTier, LanguageCapabilityMatrix, MigrationAcceptanceContract. | +| Sprint 47 | 699–708 | **Complete** | Rust IR semantics: ownership/borrow extractor, lifetime region lowering, trait/impl lowering, async intent, error model, macro boundary, unsafe risk, generic intent. | +| Sprint 48 | 709–718 | **Complete** | C++ IR raising: ownership policy, borrow mapping, template raising, async mapping, error model, algorithm lifting, build artifact generator. | +| Sprint 49 | 719–728 | **Complete** | Equivalence checking: differential execution harness, test vector spec, fuzz runner, property runner, behavioral divergence packet, evidence bundle, Rust/C++ runner adapters. | +| Sprint 50 | 729–738 | **Complete** | Porting gates: gate severity policy, sanitizer gate, perf benchmarking/comparator, security scan orchestrator, supply chain audit, C++ review report template. | +| Sprint 51 | 739–748 | **Complete** | Systems language family: C/Go/Java adapters + raising adapters, systems compatibility matrix, family acceptance report. | +| Sprint 52 | 749–758 | **Complete** | Managed language family: C#/Kotlin/F#/VB.NET adapters, ADT pattern lowering, async model bridge, nullability/optionality bridge, family promotion. | +| Sprint 53 | 759–768 | **Complete** | Dynamic language family: Python/JS/TS/Ruby/Lua adapters V2, dynamic risk classifier, strictness policy, family acceptance report. | +| Sprint 54 | 769–778 | **Complete** | AST-native family: S-expression lowering, Lisp/Scheme/Elisp/Smalltalk adapters, macro hygiene boundary, eval runtime risk, projection benchmark. | +| Sprint 55 | 779–788 | **Complete** | Logic/Actor family: Prolog/Erlang/Elixir adapters, supervision tree packet, semantic blocklist gate, logic→imperative policy, async projection policy, family report. | +| Sprint 56 | 789–798 | **Complete** | Low-level family: x86/ARM/Wasm/C adapters, ABI calling convention, memory layout checker, host boundary contract, family acceptance report. | +| Sprint 57 | 799–808 | **Complete** | Legacy ingestion: semantic recovery graph, assumption inference, ambiguity packet, cross-file API inference, confidence calibration, review queue generator, modernization dossier. | +| Sprint 58 | 809–818 | **Complete** | Data query family: SQL canonical IR, MySQL/PostgreSQL/T-SQL adapters, transaction isolation packet, divergence classifier, query equivalence runner, family acceptance report. | +| Sprint 59 | 819–828 | **Complete** | Governance: PortingReviewBoard, ReviewerDecisionLedger, WaiverPolicyPacket, PolicyPackSystem, AmbiguityTriageModel, DecisionReplayChecker, GovernanceAuditReport, `whetstone_review_porting_decision` MCP tool. | +| Sprint 60 | 829–838 | **Complete** | Language graduation: PairMatrixRunner, TierPromotionEngine, KnownLimitationsGenerator, ReleaseReadinessScoreboard, StableCorpusCertifier, RegressionWatchlist, DocumentationPackGenerator, `whetstone_get_transpile_support_matrix` MCP tool. | +| Sprint 61 | 839–848 | **Complete** | Continuous certification: CertificationJobScheduler, PairSelectionStrategy, EvidenceArchive, PairRegressionDetector, BlastRadiusAnalyzer, AutoBlockRuleEngine, CertificationDashboard, `whetstone_run_certification_cycle` + `whetstone_get_certification_status` MCP tools. | +| Sprint 62 | 849–858 | **Complete** | Failure telemetry: FailureTaxonomy, PacketTagging, FailureAggregationIndexer, TrendDetector, PrioritizationScorer, RemediationGenerator, WeeklyQualityReport, `whetstone_get_failure_trends` + `whetstone_get_top_adapter_gaps` MCP tools. | +| Sprint 63 | 859–868 | **In Progress** | Learned adapter hints (next to implement) | +| Sprints 64–120 | 869–1448 | **Pending** | Cost planning, queue optimization, runtime packs, drift detection, corpus benchmarks, tenant policy, improvement proposals, web/API semantics, data pipelines, embedded constraints, and more | +| Sprints 121–130 | 1449–1548 | **Complete** | Pre-built debug workflow tooling (closed-loop iteration epoch) | --- diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index f4955be..ecb0b84 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -7486,3 +7486,43 @@ target_link_libraries(step897_test PRIVATE nlohmann_json::nlohmann_json unoffici add_executable(step898_test tests/step898_test.cpp) target_include_directories(step898_test PRIVATE src) target_link_libraries(step898_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(step899_test tests/step899_test.cpp) +target_include_directories(step899_test PRIVATE src) +target_link_libraries(step899_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(step900_test tests/step900_test.cpp) +target_include_directories(step900_test PRIVATE src) +target_link_libraries(step900_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(step901_test tests/step901_test.cpp) +target_include_directories(step901_test PRIVATE src) +target_link_libraries(step901_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(step902_test tests/step902_test.cpp) +target_include_directories(step902_test PRIVATE src) +target_link_libraries(step902_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(step903_test tests/step903_test.cpp) +target_include_directories(step903_test PRIVATE src) +target_link_libraries(step903_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(step904_test tests/step904_test.cpp) +target_include_directories(step904_test PRIVATE src) +target_link_libraries(step904_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(step905_test tests/step905_test.cpp) +target_include_directories(step905_test PRIVATE src) +target_link_libraries(step905_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(step906_test tests/step906_test.cpp) +target_include_directories(step906_test PRIVATE src) +target_link_libraries(step906_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(step907_test tests/step907_test.cpp) +target_include_directories(step907_test PRIVATE src) +target_link_libraries(step907_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(step908_test tests/step908_test.cpp) +target_include_directories(step908_test PRIVATE src) +target_link_libraries(step908_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 6fd494c..c8a7eac 100644 --- a/editor/src/MCPServer.h +++ b/editor/src/MCPServer.h @@ -38,6 +38,10 @@ #include "graduation/UpgradePriorityPolicy.h" #include "graduation/RuntimeAssumptionExtractor.h" #include "graduation/RuntimeProfileStore.h" +#include "graduation/MigrationStepSequencer.h" +#include "graduation/MigrationFeasibilityEngine.h" +#include "graduation/MigrationPathCandidate.h" +#include "graduation/MigrationProgressTracker.h" #include "TaskCompletionMetrics.h" #include "ABTestComparison.h" #include "LanguageCapabilityMatrix.h" @@ -282,6 +286,7 @@ private: std::string recordingSessionId_; PairUpgradeQueue upgradeQueue_; RuntimeProfileStore runtimeProfileStore_; + MigrationProgressTracker migrationTracker_; // --------------------------------------------------------------- // Protocol handlers @@ -747,4 +752,5 @@ private: #include "mcp/RegisterCostPlanningTools.h" #include "mcp/RegisterUpgradeQueueTools.h" #include "mcp/RegisterRuntimePackTools.h" +#include "mcp/RegisterMigrationPlanningTools.h" #include "mcp/RegisterOnboardingAndAllTools.h" diff --git a/editor/src/graduation/MigrationAuditLog.h b/editor/src/graduation/MigrationAuditLog.h new file mode 100644 index 0000000..132e7ef --- /dev/null +++ b/editor/src/graduation/MigrationAuditLog.h @@ -0,0 +1,63 @@ +#pragma once +// Step 904: Migration audit log — append-only event log for migration activity. +#include +#include +#include + +struct MigrationAuditEntry { + std::string entryId; // "AE-" + std::string pairId; + std::string event; + std::string timestamp; // ISO-8601 string supplied by caller + std::string detail; +}; + +class MigrationAuditLog { + std::vector entries_; + int nextId_ = 1; + +public: + void append(const MigrationAuditEntry& entry) { + entries_.push_back(entry); + } + + const std::vector& entries() const { return entries_; } + + std::size_t count() const { return entries_.size(); } + + void clear() { + entries_.clear(); + nextId_ = 1; + } + + std::vector filterByPair(const std::string& pairId) const { + std::vector out; + for (const auto& e : entries_) + if (e.pairId == pairId) out.push_back(e); + return out; + } + + // Factory: creates a new entry with a generated entryId. + // The caller is responsible for inserting into the log via append(). + MigrationAuditEntry makeEntry(const std::string& pairId, + const std::string& event, + const std::string& detail) { + MigrationAuditEntry e; + e.entryId = "AE-" + std::to_string(nextId_++); + e.pairId = pairId; + e.event = event; + e.timestamp = "1970-01-01T00:00:00Z"; // caller should override + e.detail = detail; + return e; + } + + static nlohmann::json toJson(const MigrationAuditEntry& e) { + return { + {"entry_id", e.entryId}, + {"pair_id", e.pairId}, + {"event", e.event}, + {"timestamp", e.timestamp}, + {"detail", e.detail} + }; + } +}; diff --git a/editor/src/graduation/MigrationCheckpointStore.h b/editor/src/graduation/MigrationCheckpointStore.h new file mode 100644 index 0000000..68994b2 --- /dev/null +++ b/editor/src/graduation/MigrationCheckpointStore.h @@ -0,0 +1,71 @@ +#pragma once +// Step 906: Migration checkpoint store — saves and restores migration checkpoints. +#include +#include +#include + +struct MigrationCheckpoint { + std::string checkpointId; // "CP-" + std::string pairId; + int stepIndex = 0; + std::string state; // "saved" | "restored" | "expired" +}; + +class MigrationCheckpointStore { + std::unordered_map store_; + int nextId_ = 1; + +public: + // Persist a checkpoint for pairId at stepIndex. Returns the created checkpoint. + MigrationCheckpoint save(const std::string& pairId, int stepIndex) { + MigrationCheckpoint cp; + cp.checkpointId = "CP-" + std::to_string(nextId_++); + cp.pairId = pairId; + cp.stepIndex = stepIndex; + cp.state = "saved"; + store_[cp.checkpointId] = cp; + return cp; + } + + // Restore a checkpoint by ID: sets state to "restored". Returns false if not found. + bool restore(const std::string& checkpointId) { + auto it = store_.find(checkpointId); + if (it == store_.end()) return false; + if (it->second.state == "expired") return false; + it->second.state = "restored"; + return true; + } + + // Expire a checkpoint by ID. Returns false if not found. + bool expire(const std::string& checkpointId) { + auto it = store_.find(checkpointId); + if (it == store_.end()) return false; + it->second.state = "expired"; + return true; + } + + bool has(const std::string& checkpointId) const { + return store_.count(checkpointId) > 0; + } + + const MigrationCheckpoint* get(const std::string& checkpointId) const { + auto it = store_.find(checkpointId); + return (it != store_.end()) ? &it->second : nullptr; + } + + std::size_t count() const { return store_.size(); } + + void clear() { + store_.clear(); + nextId_ = 1; + } + + static nlohmann::json toJson(const MigrationCheckpoint& cp) { + return { + {"checkpoint_id", cp.checkpointId}, + {"pair_id", cp.pairId}, + {"step_index", cp.stepIndex}, + {"state", cp.state} + }; + } +}; diff --git a/editor/src/graduation/MigrationFeasibilityEngine.h b/editor/src/graduation/MigrationFeasibilityEngine.h new file mode 100644 index 0000000..2315f97 --- /dev/null +++ b/editor/src/graduation/MigrationFeasibilityEngine.h @@ -0,0 +1,74 @@ +#pragma once +// Step 901: Migration feasibility engine — scores a runtime pair and emits a verdict. +#include +#include +#include + +struct FeasibilityResult { + std::string pairId; + float score = 0.0f; // 0.0 – 1.0 + std::string verdict; // "feasible" | "marginal" | "infeasible" + std::vector blockers; // reasons that reduce feasibility +}; + +class MigrationFeasibilityEngine { +public: + // Assess migration feasibility for a runtime pair. + // riskScore : normalised risk [0.0, 1.0]; higher means more risk + // hasSourcePack : true when a RuntimeSemanticsPack exists for srcRuntime + // hasTargetPack : true when a RuntimeSemanticsPack exists for tgtRuntime + static FeasibilityResult assess(const std::string& pairId, + const std::string& srcRuntime, + const std::string& tgtRuntime, + float riskScore, + bool hasSourcePack, + bool hasTargetPack) { + FeasibilityResult r; + r.pairId = pairId; + + // Base score starts at 1.0 and is reduced by risk and missing data. + float score = 1.0f; + + // Clamp riskScore to [0, 1]. + if (riskScore < 0.0f) riskScore = 0.0f; + if (riskScore > 1.0f) riskScore = 1.0f; + score -= riskScore * 0.5f; // risk contributes up to 0.5 penalty + + if (!hasSourcePack) { + score -= 0.2f; + r.blockers.push_back("missing_pack:" + srcRuntime); + } + if (!hasTargetPack) { + score -= 0.2f; + r.blockers.push_back("missing_pack:" + tgtRuntime); + } + if (srcRuntime.empty()) { + score -= 0.1f; + r.blockers.push_back("empty_source_runtime"); + } + if (tgtRuntime.empty()) { + score -= 0.1f; + r.blockers.push_back("empty_target_runtime"); + } + + if (score < 0.0f) score = 0.0f; + r.score = score; + + if (score >= 0.6f) r.verdict = "feasible"; + else if (score >= 0.35f) r.verdict = "marginal"; + else r.verdict = "infeasible"; + + return r; + } + + static nlohmann::json toJson(const FeasibilityResult& r) { + nlohmann::json blockerArr = nlohmann::json::array(); + for (const auto& b : r.blockers) blockerArr.push_back(b); + return { + {"pair_id", r.pairId}, + {"score", r.score}, + {"verdict", r.verdict}, + {"blockers", blockerArr} + }; + } +}; diff --git a/editor/src/graduation/MigrationPathCandidate.h b/editor/src/graduation/MigrationPathCandidate.h new file mode 100644 index 0000000..b9c244d --- /dev/null +++ b/editor/src/graduation/MigrationPathCandidate.h @@ -0,0 +1,46 @@ +#pragma once +// Step 899: Migration path candidate — source/target runtime pair with feasibility score. +#include +#include +#include + +struct MigrationPathCandidate { + std::string pairId; + std::string sourceRuntime; + std::string targetRuntime; + std::vector steps; + float feasibility = 0.0f; // 0.0 (impossible) to 1.0 (fully feasible) + std::string rationale; +}; + +class MigrationPathCandidateFactory { +public: + static MigrationPathCandidate make(const std::string& pairId, + const std::string& sourceRuntime, + const std::string& targetRuntime, + const std::vector& steps, + float feasibility, + const std::string& rationale) { + MigrationPathCandidate c; + c.pairId = pairId; + c.sourceRuntime = sourceRuntime; + c.targetRuntime = targetRuntime; + c.steps = steps; + c.feasibility = feasibility < 0.0f ? 0.0f : (feasibility > 1.0f ? 1.0f : feasibility); + c.rationale = rationale; + return c; + } + + static nlohmann::json toJson(const MigrationPathCandidate& c) { + nlohmann::json stepArr = nlohmann::json::array(); + for (const auto& s : c.steps) stepArr.push_back(s); + return { + {"pair_id", c.pairId}, + {"source_runtime", c.sourceRuntime}, + {"target_runtime", c.targetRuntime}, + {"steps", stepArr}, + {"feasibility", c.feasibility}, + {"rationale", c.rationale} + }; + } +}; diff --git a/editor/src/graduation/MigrationPathRanker.h b/editor/src/graduation/MigrationPathRanker.h new file mode 100644 index 0000000..56c81f2 --- /dev/null +++ b/editor/src/graduation/MigrationPathRanker.h @@ -0,0 +1,27 @@ +#pragma once +// Step 900: Migration path ranker — sorts candidates by feasibility descending. +#include +#include +#include +#include "MigrationPathCandidate.h" + +class MigrationPathRanker { +public: + // Returns a new vector sorted by feasibility descending (highest first). + static std::vector rank( + std::vector candidates) { + std::stable_sort(candidates.begin(), candidates.end(), + [](const MigrationPathCandidate& a, const MigrationPathCandidate& b) { + return a.feasibility > b.feasibility; + }); + return candidates; + } + + // Returns the top N candidates after ranking. If n >= size, returns all. + static std::vector topN( + std::vector candidates, std::size_t n) { + auto ranked = rank(std::move(candidates)); + if (n < ranked.size()) ranked.resize(n); + return ranked; + } +}; diff --git a/editor/src/graduation/MigrationProgressTracker.h b/editor/src/graduation/MigrationProgressTracker.h new file mode 100644 index 0000000..f43e526 --- /dev/null +++ b/editor/src/graduation/MigrationProgressTracker.h @@ -0,0 +1,78 @@ +#pragma once +// Step 905: Migration progress tracker — tracks per-pair step completion state. +#include +#include +#include + +struct MigrationProgress { + std::string pairId; + int totalSteps = 0; + int completedSteps = 0; + std::string status; // "pending" | "in_progress" | "complete" | "failed" +}; + +class MigrationProgressTracker { + std::unordered_map records_; + +public: + // Initialise tracking for a pair. Status becomes "pending". + void start(const std::string& pairId, int totalSteps) { + MigrationProgress p; + p.pairId = pairId; + p.totalSteps = totalSteps > 0 ? totalSteps : 1; + p.completedSteps = 0; + p.status = "pending"; + records_[pairId] = p; + } + + // Increment completedSteps by 1. Sets status to "in_progress" if not already complete. + void advance(const std::string& pairId) { + auto it = records_.find(pairId); + if (it == records_.end()) return; + auto& p = it->second; + if (p.status == "complete" || p.status == "failed") return; + p.completedSteps++; + p.status = "in_progress"; + if (p.completedSteps >= p.totalSteps) p.status = "complete"; + } + + // Force status to "complete". + void complete(const std::string& pairId) { + auto it = records_.find(pairId); + if (it == records_.end()) return; + it->second.completedSteps = it->second.totalSteps; + it->second.status = "complete"; + } + + // Force status to "failed". + void fail(const std::string& pairId) { + auto it = records_.find(pairId); + if (it == records_.end()) return; + it->second.status = "failed"; + } + + // Returns pointer to progress record, or nullptr if pair not tracked. + const MigrationProgress* get(const std::string& pairId) const { + auto it = records_.find(pairId); + return (it != records_.end()) ? &it->second : nullptr; + } + + // Returns completion percentage [0.0, 100.0], or -1.0 if pair not tracked. + float percentComplete(const std::string& pairId) const { + const auto* p = get(pairId); + if (!p || p->totalSteps == 0) return -1.0f; + return (static_cast(p->completedSteps) / + static_cast(p->totalSteps)) * 100.0f; + } + + std::size_t count() const { return records_.size(); } + + static nlohmann::json toJson(const MigrationProgress& p) { + return { + {"pair_id", p.pairId}, + {"total_steps", p.totalSteps}, + {"completed_steps", p.completedSteps}, + {"status", p.status} + }; + } +}; diff --git a/editor/src/graduation/MigrationRollbackPolicy.h b/editor/src/graduation/MigrationRollbackPolicy.h new file mode 100644 index 0000000..17501d6 --- /dev/null +++ b/editor/src/graduation/MigrationRollbackPolicy.h @@ -0,0 +1,53 @@ +#pragma once +// Step 903: Migration rollback policy — defines what happens when a migration step fails. +#include +#include + +struct RollbackPolicy { + std::string pairId; + std::string triggerCondition; // human-readable description of when policy fires + std::string action; // "abort" | "checkpoint" | "retry" + int maxRetries = 0; +}; + +class MigrationRollbackPolicy { +public: + // Default policy: checkpoint on failure, allow up to 3 retries. + static RollbackPolicy defaultPolicy(const std::string& pairId) { + RollbackPolicy p; + p.pairId = pairId; + p.triggerCondition = "any_step_failure"; + p.action = "checkpoint"; + p.maxRetries = 3; + return p; + } + + // Strict policy: abort immediately on first failure, no retries. + static RollbackPolicy strict(const std::string& pairId) { + RollbackPolicy p; + p.pairId = pairId; + p.triggerCondition = "any_step_failure"; + p.action = "abort"; + p.maxRetries = 0; + return p; + } + + // Retry-only policy: retry up to N times before aborting. + static RollbackPolicy retryPolicy(const std::string& pairId, int maxRetries) { + RollbackPolicy p; + p.pairId = pairId; + p.triggerCondition = "any_step_failure"; + p.action = "retry"; + p.maxRetries = maxRetries > 0 ? maxRetries : 1; + return p; + } + + static nlohmann::json toJson(const RollbackPolicy& p) { + return { + {"pair_id", p.pairId}, + {"trigger_condition", p.triggerCondition}, + {"action", p.action}, + {"max_retries", p.maxRetries} + }; + } +}; diff --git a/editor/src/graduation/MigrationStepSequencer.h b/editor/src/graduation/MigrationStepSequencer.h new file mode 100644 index 0000000..b45f1f6 --- /dev/null +++ b/editor/src/graduation/MigrationStepSequencer.h @@ -0,0 +1,69 @@ +#pragma once +// Step 902: Migration step sequencer — produces an ordered list of migration steps. +#include +#include +#include + +struct MigrationStep { + std::string stepId; + std::string description; + std::string prerequisite; // empty string means no prerequisite + bool required = true; +}; + +class MigrationStepSequencer { +public: + // Generate a canonical step sequence for migrating srcRuntime -> tgtRuntime. + // Always includes at minimum: analyze_source, adapt_semantics, verify_target. + static std::vector sequence(const std::string& pairId, + const std::string& srcRuntime, + const std::string& tgtRuntime) { + std::vector steps; + + steps.push_back({ + pairId + ":analyze_source", + "Analyze source runtime (" + srcRuntime + ") semantics and dependencies", + "", + true + }); + steps.push_back({ + pairId + ":adapt_semantics", + "Adapt semantic constructs from " + srcRuntime + " to " + tgtRuntime, + pairId + ":analyze_source", + true + }); + steps.push_back({ + pairId + ":verify_target", + "Verify adapted code against target runtime (" + tgtRuntime + ") constraints", + pairId + ":adapt_semantics", + true + }); + + // Optional enrichment steps when both runtimes are non-empty. + if (!srcRuntime.empty() && !tgtRuntime.empty()) { + steps.push_back({ + pairId + ":run_equivalence_check", + "Run equivalence checks to validate behavioral parity post-migration", + pairId + ":verify_target", + false + }); + steps.push_back({ + pairId + ":generate_migration_report", + "Generate migration report summarising findings and any residual risks", + pairId + ":run_equivalence_check", + false + }); + } + + return steps; + } + + static nlohmann::json toJson(const MigrationStep& s) { + return { + {"step_id", s.stepId}, + {"description", s.description}, + {"prerequisite", s.prerequisite}, + {"required", s.required} + }; + } +}; diff --git a/editor/src/mcp/RegisterMigrationPlanningTools.h b/editor/src/mcp/RegisterMigrationPlanningTools.h new file mode 100644 index 0000000..0438357 --- /dev/null +++ b/editor/src/mcp/RegisterMigrationPlanningTools.h @@ -0,0 +1,87 @@ +// Sprint 67: Migration Planning MCP tools (Steps 907) +// Included inside MCPServer class body. + + void registerMigrationPlanningTools() { + tools_.push_back({"whetstone_plan_migration_path", + "Generate a cross-runtime migration path plan with step sequence and feasibility.", + {{"type", "object"}, {"properties", { + {"pair_id", {{"type", "string"}}}, + {"source_runtime", {{"type", "string"}}}, + {"target_runtime", {{"type", "string"}}}, + {"risk_score", {{"type", "number"}}} + }}, {"required", nlohmann::json::array({"pair_id", "source_runtime", "target_runtime"})}} + }); + toolHandlers_["whetstone_plan_migration_path"] = + [this](const nlohmann::json& args) { return runPlanMigrationPath(args); }; + + tools_.push_back({"whetstone_track_migration_progress", + "Track migration progress for a language pair (start/advance/complete/fail/get).", + {{"type", "object"}, {"properties", { + {"pair_id", {{"type", "string"}}}, + {"action", {{"type", "string"}}}, + {"total_steps", {{"type", "integer"}}} + }}, {"required", nlohmann::json::array({"pair_id", "action"})}} + }); + toolHandlers_["whetstone_track_migration_progress"] = + [this](const nlohmann::json& args) { return runTrackMigrationProgress(args); }; + } + + nlohmann::json runPlanMigrationPath(const nlohmann::json& args) { + if (!args.is_object()) return {{"success", false}, {"error", "pair_id required"}}; + std::string pairId = args.value("pair_id", ""); + std::string sourceRuntime = args.value("source_runtime", ""); + std::string targetRuntime = args.value("target_runtime", ""); + if (pairId.empty()) return {{"success", false}, {"error", "pair_id required"}}; + if (sourceRuntime.empty()) return {{"success", false}, {"error", "source_runtime required"}}; + if (targetRuntime.empty()) return {{"success", false}, {"error", "target_runtime required"}}; + float riskScore = args.value("risk_score", 0.2f); + + // Generate step sequence + auto seqSteps = MigrationStepSequencer::sequence(pairId, sourceRuntime, targetRuntime); + std::vector stepIds; + for (const auto& s : seqSteps) stepIds.push_back(s.stepId); + + // Assess feasibility + auto feas = MigrationFeasibilityEngine::assess(pairId, sourceRuntime, targetRuntime, + riskScore, true, true); + + // Build candidate + auto candidate = MigrationPathCandidateFactory::make( + pairId, sourceRuntime, targetRuntime, stepIds, feas.score, feas.verdict); + + nlohmann::json stepsArr = nlohmann::json::array(); + for (const auto& s : seqSteps) stepsArr.push_back(MigrationStepSequencer::toJson(s)); + + return {{"success", true}, {"pair_id", pairId}, + {"source_runtime", sourceRuntime}, {"target_runtime", targetRuntime}, + {"feasibility", feas.score}, {"verdict", feas.verdict}, + {"steps", stepsArr}, {"step_count", (int)seqSteps.size()}}; + } + + nlohmann::json runTrackMigrationProgress(const nlohmann::json& args) { + if (!args.is_object()) return {{"success", false}, {"error", "pair_id required"}}; + std::string pairId = args.value("pair_id", ""); + std::string action = args.value("action", ""); + if (pairId.empty()) return {{"success", false}, {"error", "pair_id required"}}; + if (action.empty()) return {{"success", false}, {"error", "action required"}}; + + if (action == "start") { + int totalSteps = args.value("total_steps", 5); + migrationTracker_.start(pairId, totalSteps); + return {{"success", true}, {"pair_id", pairId}, {"action", "start"}, + {"total_steps", totalSteps}}; + } else if (action == "advance") { + migrationTracker_.advance(pairId); + } else if (action == "complete") { + migrationTracker_.complete(pairId); + } else if (action == "fail") { + migrationTracker_.fail(pairId); + } else if (action != "get") { + return {{"success", false}, {"error", "unknown action"}}; + } + auto* pg = migrationTracker_.get(pairId); + if (!pg) return {{"success", false}, {"error", "pair not tracked"}}; + auto j = MigrationProgressTracker::toJson(*pg); + j["success"] = true; + return j; + } diff --git a/editor/src/mcp/RegisterOnboardingAndAllTools.h b/editor/src/mcp/RegisterOnboardingAndAllTools.h index d9064f0..668d341 100644 --- a/editor/src/mcp/RegisterOnboardingAndAllTools.h +++ b/editor/src/mcp/RegisterOnboardingAndAllTools.h @@ -62,6 +62,7 @@ registerCostPlanningTools(); registerUpgradeQueueTools(); registerRuntimePackTools(); + registerMigrationPlanningTools(); registerOnboardingTools(); } }; diff --git a/editor/tests/step899_test.cpp b/editor/tests/step899_test.cpp new file mode 100644 index 0000000..2b5d02a --- /dev/null +++ b/editor/tests/step899_test.cpp @@ -0,0 +1,42 @@ +// Step 899: Migration path candidate schema (8 tests) +#include "graduation/MigrationPathCandidate.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(pair_id_set); + auto c=MigrationPathCandidateFactory::make("py->cpp","cpython3","gcc",{"a","b"},0.8f,"ok"); + C(c.pairId=="py->cpp","id");P();} +void t2(){T(source_runtime_set); + auto c=MigrationPathCandidateFactory::make("py->cpp","cpython3","gcc",{"a"},0.8f,"ok"); + C(c.sourceRuntime=="cpython3","src");P();} +void t3(){T(target_runtime_set); + auto c=MigrationPathCandidateFactory::make("py->cpp","cpython3","gcc",{"a"},0.8f,"ok"); + C(c.targetRuntime=="gcc","tgt");P();} +void t4(){T(feasibility_set); + auto c=MigrationPathCandidateFactory::make("py->cpp","cpython3","gcc",{"a"},0.75f,"ok"); + C(c.feasibility==0.75f,"feas");P();} +void t5(){T(steps_set); + auto c=MigrationPathCandidateFactory::make("py->cpp","cpython3","gcc",{"s1","s2"},0.8f,"ok"); + C(c.steps.size()==2,"steps");P();} +void t6(){T(rationale_set); + auto c=MigrationPathCandidateFactory::make("py->cpp","cpython3","gcc",{"a"},0.8f,"reason"); + C(c.rationale=="reason","rat");P();} +void t7(){T(to_json_has_pair_id); + auto c=MigrationPathCandidateFactory::make("py->cpp","cpython3","gcc",{"a"},0.8f,"ok"); + auto j=MigrationPathCandidateFactory::toJson(c); + C(j.contains("pair_id"),"json");P();} +void t8(){T(to_json_has_feasibility); + auto c=MigrationPathCandidateFactory::make("py->cpp","cpython3","gcc",{"a"},0.8f,"ok"); + auto j=MigrationPathCandidateFactory::toJson(c); + C(j.contains("feasibility"),"json");P();} + +int main(){ + std::cout<<"Step 899: Migration path candidate schema\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(rank_empty_returns_empty); + auto r=MigrationPathRanker::rank({}); + C(r.empty(),"empty");P();} +void t2(){T(rank_single); + auto c=MigrationPathCandidateFactory::make("p1","src","tgt",{"a"},0.7f,"ok"); + auto r=MigrationPathRanker::rank({c}); + C(r.size()==1,"one");P();} +void t3(){T(rank_descending_feasibility); + auto c1=MigrationPathCandidateFactory::make("p1","src","tgt",{"a"},0.3f,"ok"); + auto c2=MigrationPathCandidateFactory::make("p2","src","tgt",{"a"},0.9f,"ok"); + auto r=MigrationPathRanker::rank({c1,c2}); + C(r[0].feasibility>r[1].feasibility,"desc");P();} +void t4(){T(rank_preserves_count); + auto c1=MigrationPathCandidateFactory::make("p1","src","tgt",{"a"},0.5f,"ok"); + auto c2=MigrationPathCandidateFactory::make("p2","src","tgt",{"a"},0.7f,"ok"); + auto c3=MigrationPathCandidateFactory::make("p3","src","tgt",{"a"},0.3f,"ok"); + auto r=MigrationPathRanker::rank({c1,c2,c3}); + C(r.size()==3,"count");P();} +void t5(){T(top_n_limits); + auto c1=MigrationPathCandidateFactory::make("p1","src","tgt",{"a"},0.5f,"ok"); + auto c2=MigrationPathCandidateFactory::make("p2","src","tgt",{"a"},0.7f,"ok"); + auto c3=MigrationPathCandidateFactory::make("p3","src","tgt",{"a"},0.9f,"ok"); + auto r=MigrationPathRanker::topN({c1,c2,c3},2); + C(r.size()==2,"top2");P();} +void t6(){T(top_n_returns_best); + auto c1=MigrationPathCandidateFactory::make("p1","src","tgt",{"a"},0.1f,"ok"); + auto c2=MigrationPathCandidateFactory::make("p2","src","tgt",{"a"},0.9f,"ok"); + auto r=MigrationPathRanker::topN({c1,c2},1); + C(r[0].feasibility==0.9f,"best");P();} +void t7(){T(top_n_larger_than_count); + auto c1=MigrationPathCandidateFactory::make("p1","src","tgt",{"a"},0.5f,"ok"); + auto r=MigrationPathRanker::topN({c1},5); + C(r.size()==1,"bounded");P();} +void t8(){T(rank_first_has_highest_feasibility); + auto c1=MigrationPathCandidateFactory::make("p1","src","tgt",{"a"},0.4f,"ok"); + auto c2=MigrationPathCandidateFactory::make("p2","src","tgt",{"a"},0.8f,"ok"); + auto r=MigrationPathRanker::rank({c1,c2}); + C(r[0].pairId=="p2","first");P();} + +int main(){ + std::cout<<"Step 900: Migration path ranker\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(feasible_verdict); + // riskScore=0.1 -> score=1-0.05=0.95 >= 0.6 -> feasible + auto r=MigrationFeasibilityEngine::assess("p1","jvm11","cpython3",0.1f,true,true); + C(r.verdict=="feasible","feas");P();} +void t2(){T(marginal_verdict); + // riskScore=0.9 -> score=1-0.45=0.55, 0.35<=0.55<0.6 -> marginal + auto r=MigrationFeasibilityEngine::assess("p1","jvm11","cpython3",0.9f,true,true); + C(r.verdict=="marginal","marg");P();} +void t3(){T(infeasible_verdict); + // riskScore=0.9 + both packs missing -> score=0.55-0.4=0.15 < 0.35 -> infeasible + auto r=MigrationFeasibilityEngine::assess("p1","jvm11","cpython3",0.9f,false,false); + C(r.verdict=="infeasible","inf");P();} +void t4(){T(pair_id_set); + auto r=MigrationFeasibilityEngine::assess("myPair","jvm11","cpython3",0.1f,true,true); + C(r.pairId=="myPair","id");P();} +void t5(){T(missing_source_pack_adds_blocker); + auto r=MigrationFeasibilityEngine::assess("p1","jvm11","cpython3",0.1f,false,true); + bool found=false; + for(const auto& b:r.blockers) if(b.rfind("missing_pack",0)==0) found=true; + C(found,"blocker");P();} +void t6(){T(missing_target_pack_adds_blocker); + auto r=MigrationFeasibilityEngine::assess("p1","jvm11","cpython3",0.1f,true,false); + bool found=false; + for(const auto& b:r.blockers) if(b.rfind("missing_pack",0)==0) found=true; + C(found,"blocker");P();} +void t7(){T(no_blockers_when_packs_available); + auto r=MigrationFeasibilityEngine::assess("p1","jvm11","cpython3",0.1f,true,true); + C(r.blockers.empty(),"no blockers");P();} +void t8(){T(score_positive); + auto r=MigrationFeasibilityEngine::assess("p1","jvm11","cpython3",0.1f,true,true); + C(r.score>0.0f,"score");P();} + +int main(){ + std::cout<<"Step 901: Migration feasibility engine\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;} + +// Step IDs are prefixed with pairId: "p1:analyze_source" etc. +void t1(){T(at_least_three_steps); + auto s=MigrationStepSequencer::sequence("p1","jvm11","cpython3"); + C(s.size()>=3,"three");P();} +void t2(){T(first_step_has_analyze_source); + auto s=MigrationStepSequencer::sequence("p1","jvm11","cpython3"); + C(s[0].stepId.find("analyze_source")!=std::string::npos,"first");P();} +void t3(){T(second_step_has_adapt_semantics); + auto s=MigrationStepSequencer::sequence("p1","jvm11","cpython3"); + C(s[1].stepId.find("adapt_semantics")!=std::string::npos,"second");P();} +void t4(){T(third_step_has_verify_target); + auto s=MigrationStepSequencer::sequence("p1","jvm11","cpython3"); + C(s[2].stepId.find("verify_target")!=std::string::npos,"third");P();} +void t5(){T(steps_have_description); + auto s=MigrationStepSequencer::sequence("p1","jvm11","cpython3"); + C(!s[0].description.empty(),"desc");P();} +void t6(){T(first_step_no_prerequisite); + auto s=MigrationStepSequencer::sequence("p1","jvm11","cpython3"); + C(s[0].prerequisite.empty(),"no prereq");P();} +void t7(){T(second_step_required); + auto s=MigrationStepSequencer::sequence("p1","jvm11","cpython3"); + C(s[1].required,"required");P();} +void t8(){T(to_json_has_step_id); + auto s=MigrationStepSequencer::sequence("p1","jvm11","cpython3"); + auto j=MigrationStepSequencer::toJson(s[0]); + C(j.contains("step_id"),"json");P();} + +int main(){ + std::cout<<"Step 902: Migration step sequencer\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(default_action_checkpoint); + auto pol=MigrationRollbackPolicy::defaultPolicy("p1"); + C(pol.action=="checkpoint","ckpt");P();} +void t2(){T(default_max_retries_three); + auto pol=MigrationRollbackPolicy::defaultPolicy("p1"); + C(pol.maxRetries==3,"retries");P();} +void t3(){T(strict_action_abort); + auto pol=MigrationRollbackPolicy::strict("p1"); + C(pol.action=="abort","abort");P();} +void t4(){T(strict_max_retries_zero); + auto pol=MigrationRollbackPolicy::strict("p1"); + C(pol.maxRetries==0,"zero");P();} +void t5(){T(pair_id_set_default); + auto pol=MigrationRollbackPolicy::defaultPolicy("myPair"); + C(pol.pairId=="myPair","id");P();} +void t6(){T(pair_id_set_strict); + auto pol=MigrationRollbackPolicy::strict("myPair"); + C(pol.pairId=="myPair","id");P();} +void t7(){T(to_json_has_action); + auto pol=MigrationRollbackPolicy::defaultPolicy("p1"); + auto j=MigrationRollbackPolicy::toJson(pol); + C(j.contains("action"),"json");P();} +void t8(){T(to_json_has_max_retries); + auto pol=MigrationRollbackPolicy::defaultPolicy("p1"); + auto j=MigrationRollbackPolicy::toJson(pol); + C(j.contains("max_retries"),"json");P();} + +int main(){ + std::cout<<"Step 903: Migration rollback policy\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;} + +// Note: makeEntry is an instance method (uses nextId_ state) +void t1(){T(empty_count_zero); + MigrationAuditLog log; + C(log.count()==0,"zero");P();} +void t2(){T(append_increments_count); + MigrationAuditLog log; + log.append(log.makeEntry("p1","start","desc")); + C(log.count()==1,"one");P();} +void t3(){T(entry_id_sequential); + MigrationAuditLog log; + auto e1=log.makeEntry("p1","start","desc"); + auto e2=log.makeEntry("p1","step","desc"); + C(e1.entryId=="AE-1"&&e2.entryId=="AE-2","ids");P();} +void t4(){T(pair_id_set); + MigrationAuditLog log; + auto e=log.makeEntry("myPair","event","detail"); + C(e.pairId=="myPair","id");P();} +void t5(){T(event_set); + MigrationAuditLog log; + auto e=log.makeEntry("p1","my_event","detail"); + C(e.event=="my_event","event");P();} +void t6(){T(filter_by_pair); + MigrationAuditLog log; + log.append(log.makeEntry("p1","start","d")); + log.append(log.makeEntry("p2","start","d")); + auto filtered=log.filterByPair("p1"); + C(filtered.size()==1,"filter");P();} +void t7(){T(clear_removes_all); + MigrationAuditLog log; + log.append(log.makeEntry("p1","start","d")); + log.clear(); + C(log.count()==0,"clear");P();} +void t8(){T(to_json_has_entry_id); + MigrationAuditLog log; + auto e=log.makeEntry("p1","start","d"); + auto j=MigrationAuditLog::toJson(e); + C(j.contains("entry_id"),"json");P();} + +int main(){ + std::cout<<"Step 904: Migration audit log\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(start_status_pending); + MigrationProgressTracker tr; + tr.start("p1",5); + auto* pg=tr.get("p1"); + C(pg&&pg->status=="pending","pending");P();} +void t2(){T(advance_increments_completed); + MigrationProgressTracker tr; + tr.start("p1",5); + tr.advance("p1"); + auto* pg=tr.get("p1"); + C(pg&&pg->completedSteps==1,"completed");P();} +void t3(){T(complete_sets_status); + MigrationProgressTracker tr; + tr.start("p1",5); + tr.complete("p1"); + auto* pg=tr.get("p1"); + C(pg&&pg->status=="complete","complete");P();} +void t4(){T(fail_sets_status); + MigrationProgressTracker tr; + tr.start("p1",5); + tr.fail("p1"); + auto* pg=tr.get("p1"); + C(pg&&pg->status=="failed","failed");P();} +void t5(){T(get_unknown_null); + MigrationProgressTracker tr; + C(tr.get("unknown")==nullptr,"null");P();} +void t6(){T(percent_complete_zero); + MigrationProgressTracker tr; + tr.start("p1",4); + C(tr.percentComplete("p1")==0.0f,"zero");P();} +void t7(){T(percent_complete_half); + MigrationProgressTracker tr; + tr.start("p1",4); + tr.advance("p1"); + tr.advance("p1"); + C(tr.percentComplete("p1")==50.0f,"half");P();} +void t8(){T(to_json_has_status); + MigrationProgressTracker tr; + tr.start("p1",5); + auto pg=tr.get("p1"); + auto j=MigrationProgressTracker::toJson(*pg); + C(j.contains("status"),"json");P();} + +int main(){ + std::cout<<"Step 905: Migration progress tracker\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(save_returns_checkpoint); + MigrationCheckpointStore st; + auto cp=st.save("p1",0); + C(!cp.checkpointId.empty(),"id");P();} +void t2(){T(checkpoint_id_sequential); + MigrationCheckpointStore st; + auto cp1=st.save("p1",0); + auto cp2=st.save("p1",1); + C(cp1.checkpointId=="CP-1"&&cp2.checkpointId=="CP-2","ids");P();} +void t3(){T(has_after_save); + MigrationCheckpointStore st; + auto cp=st.save("p1",0); + C(st.has(cp.checkpointId),"has");P();} +void t4(){T(not_has_unknown); + MigrationCheckpointStore st; + C(!st.has("CP-99"),"not has");P();} +void t5(){T(restore_returns_true); + MigrationCheckpointStore st; + auto cp=st.save("p1",0); + C(st.restore(cp.checkpointId),"restore");P();} +void t6(){T(restore_unknown_false); + MigrationCheckpointStore st; + C(!st.restore("CP-99"),"false");P();} +void t7(){T(count_increments); + MigrationCheckpointStore st; + st.save("p1",0); + st.save("p1",1); + C(st.count()==2,"count");P();} +void t8(){T(to_json_has_checkpoint_id); + MigrationCheckpointStore st; + auto cp=st.save("p1",0); + auto j=MigrationCheckpointStore::toJson(cp); + C(j.contains("checkpoint_id"),"json");P();} + +int main(){ + std::cout<<"Step 906: Migration checkpoint store\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(plan_migration_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_plan_migration_path") found=true; + C(found,"tool found");P();} + +void t2(){T(track_migration_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_track_migration_progress") found=true; + C(found,"tool found");P();} + +void t3(){T(plan_migration_missing_args_fail); + MCPServer srv; + auto r=srv.handleRequest({{"jsonrpc","2.0"},{"id",1},{"method","tools/call"},{"params",{{"name","whetstone_plan_migration_path"},{"arguments",nlohmann::json::object()}}}}); + auto res=nlohmann::json::parse(r["result"]["content"][0]["text"].get()); + C(!res["success"].get(),"fail");P();} + +void t4(){T(plan_migration_returns_steps); + MCPServer srv; + auto r=srv.handleRequest({{"jsonrpc","2.0"},{"id",1},{"method","tools/call"},{"params",{{"name","whetstone_plan_migration_path"},{"arguments",{{"pair_id","py->cpp"},{"source_runtime","cpython3"},{"target_runtime","gcc"}}}}}}); + auto res=nlohmann::json::parse(r["result"]["content"][0]["text"].get()); + C(res["success"].get()&&res.contains("steps"),"steps");P();} + +void t5(){T(plan_migration_has_feasibility); + MCPServer srv; + auto r=srv.handleRequest({{"jsonrpc","2.0"},{"id",1},{"method","tools/call"},{"params",{{"name","whetstone_plan_migration_path"},{"arguments",{{"pair_id","py->cpp"},{"source_runtime","cpython3"},{"target_runtime","gcc"}}}}}}); + auto res=nlohmann::json::parse(r["result"]["content"][0]["text"].get()); + C(res.contains("feasibility"),"feas");P();} + +void t6(){T(track_progress_missing_args_fail); + MCPServer srv; + auto r=srv.handleRequest({{"jsonrpc","2.0"},{"id",1},{"method","tools/call"},{"params",{{"name","whetstone_track_migration_progress"},{"arguments",nlohmann::json::object()}}}}); + auto res=nlohmann::json::parse(r["result"]["content"][0]["text"].get()); + C(!res["success"].get(),"fail");P();} + +void t7(){T(track_progress_starts_tracking); + MCPServer srv; + auto r=srv.handleRequest({{"jsonrpc","2.0"},{"id",1},{"method","tools/call"},{"params",{{"name","whetstone_track_migration_progress"},{"arguments",{{"pair_id","py->cpp"},{"action","start"},{"total_steps",5}}}}}}); + auto res=nlohmann::json::parse(r["result"]["content"][0]["text"].get()); + C(res["success"].get(),"success");P();} + +void t8(){T(track_progress_has_status); + MCPServer srv; + srv.handleRequest({{"jsonrpc","2.0"},{"id",1},{"method","tools/call"},{"params",{{"name","whetstone_track_migration_progress"},{"arguments",{{"pair_id","py->cpp"},{"action","start"},{"total_steps",5}}}}}}); + auto r=srv.handleRequest({{"jsonrpc","2.0"},{"id",2},{"method","tools/call"},{"params",{{"name","whetstone_track_migration_progress"},{"arguments",{{"pair_id","py->cpp"},{"action","get"}}}}}}); + auto res=nlohmann::json::parse(r["result"]["content"][0]["text"].get()); + C(res.contains("status"),"status");P();} + +int main(){ + std::cout<<"Step 907: Migration planning MCP tools\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(sprint_number); + C(Sprint67IntegrationSummary::sprintNumber==67,"num");P();} +void t2(){T(steps_completed); + C(Sprint67IntegrationSummary::stepsCompleted==10,"steps");P();} +void t3(){T(theme_set); + std::string t=Sprint67IntegrationSummary::theme; + C(!t.empty(),"theme");P();} +void t4(){T(verify_true); + C(Sprint67IntegrationSummary::verify(),"verify");P();} +void t5(){T(to_json_sprint_field); + auto j=Sprint67IntegrationSummary::toJson(); + C(j.contains("sprint")&&j["sprint"]==67,"json");P();} +void t6(){T(to_json_tools_added); + auto j=Sprint67IntegrationSummary::toJson(); + C(j.contains("tools_added"),"tools");P();} + +int main(){ + std::cout<<"Step 908: Sprint 67 integration summary\n"; + t1();t2();t3();t4();t5();t6(); + std::cout<<"\nResults: "<