Add migration planning tools and step899-908 test suite
This commit is contained in:
30
PROGRESS.md
30
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) |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
63
editor/src/graduation/MigrationAuditLog.h
Normal file
63
editor/src/graduation/MigrationAuditLog.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
// Step 904: Migration audit log — append-only event log for migration activity.
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
struct MigrationAuditEntry {
|
||||
std::string entryId; // "AE-<N>"
|
||||
std::string pairId;
|
||||
std::string event;
|
||||
std::string timestamp; // ISO-8601 string supplied by caller
|
||||
std::string detail;
|
||||
};
|
||||
|
||||
class MigrationAuditLog {
|
||||
std::vector<MigrationAuditEntry> entries_;
|
||||
int nextId_ = 1;
|
||||
|
||||
public:
|
||||
void append(const MigrationAuditEntry& entry) {
|
||||
entries_.push_back(entry);
|
||||
}
|
||||
|
||||
const std::vector<MigrationAuditEntry>& entries() const { return entries_; }
|
||||
|
||||
std::size_t count() const { return entries_.size(); }
|
||||
|
||||
void clear() {
|
||||
entries_.clear();
|
||||
nextId_ = 1;
|
||||
}
|
||||
|
||||
std::vector<MigrationAuditEntry> filterByPair(const std::string& pairId) const {
|
||||
std::vector<MigrationAuditEntry> 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}
|
||||
};
|
||||
}
|
||||
};
|
||||
71
editor/src/graduation/MigrationCheckpointStore.h
Normal file
71
editor/src/graduation/MigrationCheckpointStore.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
// Step 906: Migration checkpoint store — saves and restores migration checkpoints.
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
struct MigrationCheckpoint {
|
||||
std::string checkpointId; // "CP-<N>"
|
||||
std::string pairId;
|
||||
int stepIndex = 0;
|
||||
std::string state; // "saved" | "restored" | "expired"
|
||||
};
|
||||
|
||||
class MigrationCheckpointStore {
|
||||
std::unordered_map<std::string, MigrationCheckpoint> 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}
|
||||
};
|
||||
}
|
||||
};
|
||||
74
editor/src/graduation/MigrationFeasibilityEngine.h
Normal file
74
editor/src/graduation/MigrationFeasibilityEngine.h
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
// Step 901: Migration feasibility engine — scores a runtime pair and emits a verdict.
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
struct FeasibilityResult {
|
||||
std::string pairId;
|
||||
float score = 0.0f; // 0.0 – 1.0
|
||||
std::string verdict; // "feasible" | "marginal" | "infeasible"
|
||||
std::vector<std::string> 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}
|
||||
};
|
||||
}
|
||||
};
|
||||
46
editor/src/graduation/MigrationPathCandidate.h
Normal file
46
editor/src/graduation/MigrationPathCandidate.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
// Step 899: Migration path candidate — source/target runtime pair with feasibility score.
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
struct MigrationPathCandidate {
|
||||
std::string pairId;
|
||||
std::string sourceRuntime;
|
||||
std::string targetRuntime;
|
||||
std::vector<std::string> 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<std::string>& 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}
|
||||
};
|
||||
}
|
||||
};
|
||||
27
editor/src/graduation/MigrationPathRanker.h
Normal file
27
editor/src/graduation/MigrationPathRanker.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
// Step 900: Migration path ranker — sorts candidates by feasibility descending.
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include "MigrationPathCandidate.h"
|
||||
|
||||
class MigrationPathRanker {
|
||||
public:
|
||||
// Returns a new vector sorted by feasibility descending (highest first).
|
||||
static std::vector<MigrationPathCandidate> rank(
|
||||
std::vector<MigrationPathCandidate> 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<MigrationPathCandidate> topN(
|
||||
std::vector<MigrationPathCandidate> candidates, std::size_t n) {
|
||||
auto ranked = rank(std::move(candidates));
|
||||
if (n < ranked.size()) ranked.resize(n);
|
||||
return ranked;
|
||||
}
|
||||
};
|
||||
78
editor/src/graduation/MigrationProgressTracker.h
Normal file
78
editor/src/graduation/MigrationProgressTracker.h
Normal file
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
// Step 905: Migration progress tracker — tracks per-pair step completion state.
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
struct MigrationProgress {
|
||||
std::string pairId;
|
||||
int totalSteps = 0;
|
||||
int completedSteps = 0;
|
||||
std::string status; // "pending" | "in_progress" | "complete" | "failed"
|
||||
};
|
||||
|
||||
class MigrationProgressTracker {
|
||||
std::unordered_map<std::string, MigrationProgress> 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<float>(p->completedSteps) /
|
||||
static_cast<float>(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}
|
||||
};
|
||||
}
|
||||
};
|
||||
53
editor/src/graduation/MigrationRollbackPolicy.h
Normal file
53
editor/src/graduation/MigrationRollbackPolicy.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
// Step 903: Migration rollback policy — defines what happens when a migration step fails.
|
||||
#include <string>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
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}
|
||||
};
|
||||
}
|
||||
};
|
||||
69
editor/src/graduation/MigrationStepSequencer.h
Normal file
69
editor/src/graduation/MigrationStepSequencer.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
// Step 902: Migration step sequencer — produces an ordered list of migration steps.
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
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<MigrationStep> sequence(const std::string& pairId,
|
||||
const std::string& srcRuntime,
|
||||
const std::string& tgtRuntime) {
|
||||
std::vector<MigrationStep> 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}
|
||||
};
|
||||
}
|
||||
};
|
||||
87
editor/src/mcp/RegisterMigrationPlanningTools.h
Normal file
87
editor/src/mcp/RegisterMigrationPlanningTools.h
Normal file
@@ -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<std::string> 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;
|
||||
}
|
||||
@@ -62,6 +62,7 @@
|
||||
registerCostPlanningTools();
|
||||
registerUpgradeQueueTools();
|
||||
registerRuntimePackTools();
|
||||
registerMigrationPlanningTools();
|
||||
registerOnboardingTools();
|
||||
}
|
||||
};
|
||||
|
||||
42
editor/tests/step899_test.cpp
Normal file
42
editor/tests/step899_test.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
// Step 899: Migration path candidate schema (8 tests)
|
||||
#include "graduation/MigrationPathCandidate.h"
|
||||
#include <iostream>
|
||||
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: "<<p<<"/"<<(p+f)<<" passed\n";
|
||||
return f?1:0;
|
||||
}
|
||||
54
editor/tests/step900_test.cpp
Normal file
54
editor/tests/step900_test.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
// Step 900: Migration path ranker (8 tests)
|
||||
#include "graduation/MigrationPathRanker.h"
|
||||
#include <iostream>
|
||||
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: "<<p<<"/"<<(p+f)<<" passed\n";
|
||||
return f?1:0;
|
||||
}
|
||||
47
editor/tests/step901_test.cpp
Normal file
47
editor/tests/step901_test.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
// Step 901: Migration feasibility engine (8 tests)
|
||||
#include "graduation/MigrationFeasibilityEngine.h"
|
||||
#include <iostream>
|
||||
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: "<<p<<"/"<<(p+f)<<" passed\n";
|
||||
return f?1:0;
|
||||
}
|
||||
42
editor/tests/step902_test.cpp
Normal file
42
editor/tests/step902_test.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
// Step 902: Migration step sequencer (8 tests)
|
||||
#include "graduation/MigrationStepSequencer.h"
|
||||
#include <iostream>
|
||||
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: "<<p<<"/"<<(p+f)<<" passed\n";
|
||||
return f?1:0;
|
||||
}
|
||||
42
editor/tests/step903_test.cpp
Normal file
42
editor/tests/step903_test.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
// Step 903: Migration rollback policy (8 tests)
|
||||
#include "graduation/MigrationRollbackPolicy.h"
|
||||
#include <iostream>
|
||||
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: "<<p<<"/"<<(p+f)<<" passed\n";
|
||||
return f?1:0;
|
||||
}
|
||||
53
editor/tests/step904_test.cpp
Normal file
53
editor/tests/step904_test.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
// Step 904: Migration audit log (8 tests)
|
||||
#include "graduation/MigrationAuditLog.h"
|
||||
#include <iostream>
|
||||
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: "<<p<<"/"<<(p+f)<<" passed\n";
|
||||
return f?1:0;
|
||||
}
|
||||
58
editor/tests/step905_test.cpp
Normal file
58
editor/tests/step905_test.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
// Step 905: Migration progress tracker (8 tests)
|
||||
#include "graduation/MigrationProgressTracker.h"
|
||||
#include <iostream>
|
||||
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: "<<p<<"/"<<(p+f)<<" passed\n";
|
||||
return f?1:0;
|
||||
}
|
||||
49
editor/tests/step906_test.cpp
Normal file
49
editor/tests/step906_test.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
// Step 906: Migration checkpoint store (8 tests)
|
||||
#include "graduation/MigrationCheckpointStore.h"
|
||||
#include <iostream>
|
||||
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: "<<p<<"/"<<(p+f)<<" passed\n";
|
||||
return f?1:0;
|
||||
}
|
||||
66
editor/tests/step907_test.cpp
Normal file
66
editor/tests/step907_test.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
// Step 907: whetstone_plan_migration_path + whetstone_track_migration_progress MCP tools (8 tests)
|
||||
#include "MCPServer.h"
|
||||
#include <iostream>
|
||||
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<std::string>());
|
||||
C(!res["success"].get<bool>(),"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<std::string>());
|
||||
C(res["success"].get<bool>()&&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<std::string>());
|
||||
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<std::string>());
|
||||
C(!res["success"].get<bool>(),"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<std::string>());
|
||||
C(res["success"].get<bool>(),"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<std::string>());
|
||||
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: "<<p<<"/"<<(p+f)<<" passed\n";
|
||||
return f?1:0;
|
||||
}
|
||||
31
editor/tests/step908_test.cpp
Normal file
31
editor/tests/step908_test.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
// Step 908: Sprint 67 integration summary (6 tests)
|
||||
#include "Sprint67IntegrationSummary.h"
|
||||
#include <iostream>
|
||||
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: "<<p<<"/"<<(p+f)<<" passed\n";
|
||||
return f?1:0;
|
||||
}
|
||||
Reference in New Issue
Block a user