Add Sprints 46-59: governance, porting foundation, and language graduation prep (Steps 689-828)

Sprints 46-58 implement the cross-language porting foundation: language-to-IR
adapters, equivalence checking, gate validation, legacy ingestion, managed/dynamic
families, low-level/logic-actor semantics, debug workflow tooling, AST-native
family tools, Rust/CPP raising tools, system-level orchestration, query family,
and porting gates. Sprint 59 adds the governance layer (policy packs, review
boards, waiver packets, ambiguity triage, decision ledger) with the
whetstone_review_porting_decision MCP tool.

Also includes: sprint plans 46-130, MCP taskitem pipeline scripts,
CLAUDE.md, docs, and full test matrix (steps 689-828).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Bill
2026-02-22 13:18:10 -07:00
parent 84bdad8e9e
commit 1696b92bb8
403 changed files with 21166 additions and 79 deletions

View File

@@ -0,0 +1,26 @@
#pragma once
// Step 1481: bisect candidate selector.
#include <algorithm>
#include <string>
#include <vector>
struct BisectCandidate {
std::string proposalId;
int index = 0;
};
class BisectCandidateSelector {
public:
static std::vector<BisectCandidate> ordered(const std::vector<std::string>& proposalIds) {
std::vector<BisectCandidate> out;
for (size_t i = 0; i < proposalIds.size(); ++i) out.push_back({proposalIds[i], static_cast<int>(i)});
std::sort(out.begin(), out.end(), [](const auto& a, const auto& b){ return a.proposalId < b.proposalId; });
return out;
}
static BisectCandidate midpoint(const std::vector<BisectCandidate>& orderedCandidates) {
if (orderedCandidates.empty()) return {"", -1};
return orderedCandidates[orderedCandidates.size() / 2];
}
};

View File

@@ -0,0 +1,70 @@
#pragma once
// Step 1499: campaign checkpoint model.
#include <algorithm>
#include <string>
#include <nlohmann/json.hpp>
#include "CampaignProgressTracker.h"
struct CampaignCheckpoint {
std::string checkpointId;
std::string campaignId;
int sequence = 0;
CampaignProgress progress;
std::string notes;
};
class CampaignCheckpointModel {
public:
static CampaignCheckpoint make(const std::string& campaignId,
int sequence,
const CampaignProgress& progress,
const std::string& notes) {
CampaignCheckpoint c;
c.campaignId = campaignId;
c.sequence = sequence;
c.progress = progress;
c.notes = notes;
c.checkpointId = stableId(campaignId, sequence, notes);
return c;
}
static nlohmann::json toJson(const CampaignCheckpoint& c) {
return {
{"checkpoint_id", c.checkpointId},
{"campaign_id", c.campaignId},
{"sequence", c.sequence},
{"progress", CampaignProgressTracker::toJson(c.progress)},
{"notes", c.notes}
};
}
static CampaignCheckpoint fromJson(const nlohmann::json& j) {
CampaignCheckpoint c;
c.checkpointId = j.value("checkpoint_id", "");
c.campaignId = j.value("campaign_id", "");
c.sequence = j.value("sequence", 0);
c.notes = j.value("notes", "");
auto p = j.value("progress", nlohmann::json::object());
c.progress.campaignId = p.value("campaign_id", c.campaignId);
c.progress.totalTargets = p.value("total_targets", 0);
c.progress.completedTargets = p.value("completed_targets", 0);
c.progress.greenTargets = p.value("green_targets", 0);
c.progress.escalatedTargets = p.value("escalated_targets", 0);
c.progress.stopped = p.value("stopped", false);
return c;
}
private:
static std::string stableId(const std::string& campaignId, int sequence, const std::string& notes) {
uint64_t h = 1469598103934665603ull;
std::string key = campaignId + "|" + std::to_string(sequence) + "|" + notes;
for (unsigned char ch : key) {
h ^= ch;
h *= 1099511628211ull;
}
return "cp_" + std::to_string(h);
}
};

View File

@@ -0,0 +1,53 @@
#pragma once
// Step 1500: campaign checkpoint store.
#include <algorithm>
#include <fstream>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
#include "CampaignCheckpoint.h"
class CampaignCheckpointStore {
public:
static bool save(const std::string& path, const CampaignCheckpoint& c) {
auto all = list(path);
all.push_back(c);
sortStable(all);
nlohmann::json arr = nlohmann::json::array();
for (const auto& x : all) arr.push_back(CampaignCheckpointModel::toJson(x));
std::ofstream out(path, std::ios::binary | std::ios::trunc);
if (!out) return false;
out << arr.dump(2);
return out.good();
}
static std::vector<CampaignCheckpoint> list(const std::string& path) {
std::ifstream in(path, std::ios::binary);
if (!in) return {};
nlohmann::json j;
try {
in >> j;
} catch (...) {
return {};
}
std::vector<CampaignCheckpoint> out;
if (!j.is_array()) return out;
for (const auto& it : j) out.push_back(CampaignCheckpointModel::fromJson(it));
sortStable(out);
return out;
}
private:
static void sortStable(std::vector<CampaignCheckpoint>& items) {
std::sort(items.begin(), items.end(), [](const CampaignCheckpoint& a, const CampaignCheckpoint& b) {
if (a.campaignId != b.campaignId) return a.campaignId < b.campaignId;
if (a.sequence != b.sequence) return a.sequence < b.sequence;
return a.checkpointId < b.checkpointId;
});
}
};

View File

@@ -0,0 +1,43 @@
#pragma once
// Step 1506: campaign export bundle model.
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
#include "CampaignCheckpoint.h"
#include "DebugCampaignSpec.h"
struct CampaignExportBundle {
DebugCampaignSpec spec;
CampaignProgress progress;
std::vector<CampaignCheckpoint> checkpoints;
nlohmann::json metrics = nlohmann::json::object();
};
class CampaignExportBundleModel {
public:
static CampaignExportBundle build(const DebugCampaignSpec& spec,
const CampaignProgress& progress,
const std::vector<CampaignCheckpoint>& checkpoints,
const nlohmann::json& metrics) {
CampaignExportBundle b;
b.spec = spec;
b.progress = progress;
b.checkpoints = checkpoints;
b.metrics = metrics;
return b;
}
static nlohmann::json toJson(const CampaignExportBundle& b) {
nlohmann::json cps = nlohmann::json::array();
for (const auto& c : b.checkpoints) cps.push_back(CampaignCheckpointModel::toJson(c));
return {
{"campaign", DebugCampaignSpecModel::toJson(b.spec)},
{"progress", CampaignProgressTracker::toJson(b.progress)},
{"checkpoints", cps},
{"metrics", b.metrics}
};
}
};

View File

@@ -0,0 +1,29 @@
#pragma once
// Step 1492: campaign progress tracker.
#include <string>
#include <nlohmann/json.hpp>
struct CampaignProgress {
std::string campaignId;
int totalTargets = 0;
int completedTargets = 0;
int greenTargets = 0;
int escalatedTargets = 0;
bool stopped = false;
};
class CampaignProgressTracker {
public:
static double percent(const CampaignProgress& p) {
if (p.totalTargets <= 0) return 0.0;
return 100.0 * p.completedTargets / p.totalTargets;
}
static nlohmann::json toJson(const CampaignProgress& p) {
return {{"campaign_id", p.campaignId}, {"total_targets", p.totalTargets}, {"completed_targets", p.completedTargets},
{"green_targets", p.greenTargets}, {"escalated_targets", p.escalatedTargets},
{"stopped", p.stopped}, {"percent", percent(p)}};
}
};

View File

@@ -0,0 +1,24 @@
#pragma once
// Step 1501: campaign queue policy model.
#include <algorithm>
#include <string>
#include <vector>
struct CampaignQueueItem {
std::string campaignId;
int priority = 0;
int ageMinutes = 0;
};
class CampaignQueuePolicy {
public:
static std::vector<CampaignQueueItem> order(std::vector<CampaignQueueItem> items) {
std::sort(items.begin(), items.end(), [](const CampaignQueueItem& a, const CampaignQueueItem& b) {
if (a.priority != b.priority) return a.priority > b.priority;
if (a.ageMinutes != b.ageMinutes) return a.ageMinutes > b.ageMinutes;
return a.campaignId < b.campaignId;
});
return items;
}
};

View File

@@ -0,0 +1,31 @@
#pragma once
// Step 1497: campaign report bundle.
#include <string>
#include <nlohmann/json.hpp>
#include "CampaignProgressTracker.h"
struct CampaignReportBundle {
std::string campaignId;
CampaignProgress progress;
nlohmann::json metrics = nlohmann::json::object();
};
class CampaignReportBundleModel {
public:
static CampaignReportBundle build(const std::string& campaignId,
const CampaignProgress& progress,
const nlohmann::json& metrics) {
CampaignReportBundle b;
b.campaignId = campaignId;
b.progress = progress;
b.metrics = metrics;
return b;
}
static nlohmann::json toJson(const CampaignReportBundle& b) {
return {{"campaign_id", b.campaignId}, {"progress", CampaignProgressTracker::toJson(b.progress)}, {"metrics", b.metrics}};
}
};

View File

@@ -0,0 +1,54 @@
#pragma once
// Step 1502: campaign resume planner model.
#include <algorithm>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
#include "CampaignCheckpoint.h"
struct CampaignResumePlan {
bool canResume = false;
std::string campaignId;
int resumedFromSequence = 0;
CampaignProgress restoredProgress;
};
class CampaignResumePlanner {
public:
static CampaignResumePlan plan(const std::string& campaignId,
const std::vector<CampaignCheckpoint>& checkpoints) {
CampaignResumePlan p;
p.campaignId = campaignId;
CampaignCheckpoint best;
bool found = false;
for (const auto& c : checkpoints) {
if (c.campaignId != campaignId) continue;
if (!found || c.sequence > best.sequence ||
(c.sequence == best.sequence && c.checkpointId > best.checkpointId)) {
best = c;
found = true;
}
}
if (found) {
p.canResume = true;
p.resumedFromSequence = best.sequence;
p.restoredProgress = best.progress;
p.restoredProgress.stopped = false;
}
return p;
}
static nlohmann::json toJson(const CampaignResumePlan& p) {
return {
{"can_resume", p.canResume},
{"campaign_id", p.campaignId},
{"resumed_from_sequence", p.resumedFromSequence},
{"restored_progress", CampaignProgressTracker::toJson(p.restoredProgress)}
};
}
};

View File

@@ -0,0 +1,24 @@
#pragma once
// Step 1490: campaign target prioritizer.
#include <algorithm>
#include <string>
#include <vector>
struct CampaignTarget {
std::string target;
int severity = 0;
int flakiness = 0;
};
class CampaignTargetPrioritizer {
public:
static std::vector<CampaignTarget> prioritize(std::vector<CampaignTarget> in) {
std::sort(in.begin(), in.end(), [](const auto& a, const auto& b) {
if (a.severity != b.severity) return a.severity > b.severity;
if (a.flakiness != b.flakiness) return a.flakiness < b.flakiness;
return a.target < b.target;
});
return in;
}
};

View File

@@ -0,0 +1,50 @@
#pragma once
// Step 1530: debug action budget model.
#include <algorithm>
#include <string>
#include <nlohmann/json.hpp>
struct DebugActionBudget {
std::string profile = "tiny";
int maxIterations = 3;
int maxTokenCost = 4000;
int maxMinutes = 15;
};
class DebugActionBudgetModel {
public:
static DebugActionBudget estimate(const std::string& profile,
int failingTargets,
int complexity) {
DebugActionBudget b;
b.profile = profile;
int t = std::max(1, failingTargets);
int c = std::max(1, complexity);
if (profile == "tiny") {
b.maxIterations = std::min(5, t + 1);
b.maxTokenCost = 2000 + c * 300;
b.maxMinutes = 10 + t;
} else if (profile == "small") {
b.maxIterations = std::min(8, t + 2);
b.maxTokenCost = 3500 + c * 500;
b.maxMinutes = 15 + t * 2;
} else {
b.maxIterations = std::min(12, t + 4);
b.maxTokenCost = 5000 + c * 700;
b.maxMinutes = 20 + t * 3;
}
return b;
}
static nlohmann::json toJson(const DebugActionBudget& b) {
return {
{"profile", b.profile},
{"max_iterations", b.maxIterations},
{"max_token_cost", b.maxTokenCost},
{"max_minutes", b.maxMinutes}
};
}
};

View File

@@ -0,0 +1,43 @@
#pragma once
// Step 1521: debug action validator model.
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
struct DebugActionValidation {
bool allowed = true;
std::vector<std::string> violations;
};
class DebugActionValidator {
public:
static DebugActionValidation validate(const std::string& action,
bool touchesForbiddenPath,
bool changesManyFiles,
bool bypassesTests) {
DebugActionValidation v;
if (action.empty()) {
v.allowed = false;
v.violations.push_back("action_empty");
}
if (touchesForbiddenPath) {
v.allowed = false;
v.violations.push_back("forbidden_path");
}
if (changesManyFiles) {
v.allowed = false;
v.violations.push_back("change_scope_exceeded");
}
if (bypassesTests) {
v.allowed = false;
v.violations.push_back("test_bypass_forbidden");
}
return v;
}
static nlohmann::json toJson(const DebugActionValidation& v) {
return {{"allowed", v.allowed}, {"violations", v.violations}};
}
};

View File

@@ -0,0 +1,35 @@
#pragma once
// Step 1517: debug assist packet bundle model.
#include <nlohmann/json.hpp>
#include "DebugHintTemplate.h"
#include "FailureTriageScore.h"
#include "MinimalReproReducer.h"
#include "PatchRiskLabeler.h"
struct DebugAssistPacket {
DebugHintPacket hints;
FailureTriageScore triage;
MinimalReproResult repro;
PatchRiskLabel risk;
};
class DebugAssistPacketModel {
public:
static DebugAssistPacket build(const DebugHintPacket& hints,
const FailureTriageScore& triage,
const MinimalReproResult& repro,
const PatchRiskLabel& risk) {
return {hints, triage, repro, risk};
}
static nlohmann::json toJson(const DebugAssistPacket& p) {
return {
{"hints", DebugHintTemplate::toJson(p.hints)},
{"triage", FailureTriageScorer::toJson(p.triage)},
{"repro", MinimalReproReducer::toJson(p.repro)},
{"risk", PatchRiskLabeler::toJson(p.risk)}
};
}
};

View File

@@ -0,0 +1,34 @@
#pragma once
// Step 1475: debug benchmark harness.
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
#include "FailureFixtureCatalog.h"
#include "FailurePacket.h"
struct DebugBenchmarkResult {
int fixtureCount = 0;
int packetizedCount = 0;
bool success = false;
};
class DebugBenchmarkHarness {
public:
static DebugBenchmarkResult run(const std::vector<FailureFixture>& fixtures) {
DebugBenchmarkResult r;
r.fixtureCount = static_cast<int>(fixtures.size());
for (const auto& f : fixtures) {
auto p = FailurePacketModel::make(f.command, f.raw, f.exitCode);
if (!p.packetId.empty()) ++r.packetizedCount;
}
r.success = r.packetizedCount == r.fixtureCount;
return r;
}
static nlohmann::json toJson(const DebugBenchmarkResult& r) {
return {{"fixture_count", r.fixtureCount}, {"packetized_count", r.packetizedCount}, {"success", r.success}};
}
};

View File

@@ -0,0 +1,51 @@
#pragma once
// Step 1489: debug campaign spec model.
#include <algorithm>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
struct DebugCampaignSpec {
std::string campaignId;
std::string name;
std::vector<std::string> targets;
std::string budgetMode = "tiny";
int maxIterationsPerTarget = 3;
bool applyPatches = true;
};
class DebugCampaignSpecModel {
public:
static DebugCampaignSpec make(const std::string& name,
std::vector<std::string> targets,
const std::string& budgetMode,
int maxIter,
bool apply) {
DebugCampaignSpec s;
s.name = name;
std::sort(targets.begin(), targets.end());
s.targets = std::move(targets);
s.budgetMode = budgetMode;
s.maxIterationsPerTarget = maxIter;
s.applyPatches = apply;
s.campaignId = stableId(s);
return s;
}
static nlohmann::json toJson(const DebugCampaignSpec& s) {
return {{"campaign_id", s.campaignId}, {"name", s.name}, {"targets", s.targets},
{"budget_mode", s.budgetMode}, {"max_iterations_per_target", s.maxIterationsPerTarget},
{"apply_patches", s.applyPatches}};
}
private:
static std::string stableId(const DebugCampaignSpec& s) {
uint64_t h = 1469598103934665603ull;
std::string key = s.name + "|" + s.budgetMode + "|" + std::to_string(s.maxIterationsPerTarget);
for (const auto& t : s.targets) key += "|" + t;
for (unsigned char c : key) { h ^= c; h *= 1099511628211ull; }
return "dc_" + std::to_string(h);
}
};

View File

@@ -0,0 +1,23 @@
#pragma once
// Step 1539: debug checklist item model.
#include <string>
#include <nlohmann/json.hpp>
struct DebugChecklistItem {
int index = 0;
std::string text;
bool done = false;
};
class DebugChecklistItemModel {
public:
static DebugChecklistItem make(int index, const std::string& text, bool done) {
return {index, text, done};
}
static nlohmann::json toJson(const DebugChecklistItem& i) {
return {{"index", i.index}, {"text", i.text}, {"done", i.done}};
}
};

View File

@@ -0,0 +1,48 @@
#pragma once
// Step 1540: debug checklist template model.
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
#include "DebugChecklistItem.h"
struct DebugChecklistTemplate {
std::string failureClass;
std::vector<DebugChecklistItem> items;
};
class DebugChecklistTemplateModel {
public:
static DebugChecklistTemplate build(const std::string& failureClass) {
DebugChecklistTemplate t;
t.failureClass = failureClass;
if (failureClass == "compile") {
t.items = {
DebugChecklistItemModel::make(1, "Reproduce compile error", false),
DebugChecklistItemModel::make(2, "Patch smallest compile unit", false),
DebugChecklistItemModel::make(3, "Run compile regression guard", false)
};
} else if (failureClass == "test") {
t.items = {
DebugChecklistItemModel::make(1, "Reproduce failing test", false),
DebugChecklistItemModel::make(2, "Minimize fixture/mocks", false),
DebugChecklistItemModel::make(3, "Run targeted + guard tests", false)
};
} else {
t.items = {
DebugChecklistItemModel::make(1, "Capture fresh failure packet", false),
DebugChecklistItemModel::make(2, "Select top root-cause cluster", false),
DebugChecklistItemModel::make(3, "Apply guarded fix and verify", false)
};
}
return t;
}
static nlohmann::json toJson(const DebugChecklistTemplate& t) {
nlohmann::json arr = nlohmann::json::array();
for (const auto& i : t.items) arr.push_back(DebugChecklistItemModel::toJson(i));
return {{"failure_class", t.failureClass}, {"items", arr}};
}
};

View File

@@ -0,0 +1,30 @@
#pragma once
// Step 1542: debug evidence index model.
#include <algorithm>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
struct DebugEvidenceEntry {
std::string kind;
std::string ref;
};
class DebugEvidenceIndexModel {
public:
static std::vector<DebugEvidenceEntry> sort(std::vector<DebugEvidenceEntry> entries) {
std::sort(entries.begin(), entries.end(), [](const DebugEvidenceEntry& a, const DebugEvidenceEntry& b) {
if (a.kind != b.kind) return a.kind < b.kind;
return a.ref < b.ref;
});
return entries;
}
static nlohmann::json toJson(const std::vector<DebugEvidenceEntry>& entries) {
nlohmann::json arr = nlohmann::json::array();
for (const auto& e : entries) arr.push_back({{"kind", e.kind}, {"ref", e.ref}});
return arr;
}
};

View File

@@ -0,0 +1,34 @@
#pragma once
// Step 1522: debug execution trace model.
#include <algorithm>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
struct DebugTraceEvent {
int index = 0;
std::string action;
std::string status;
};
class DebugExecutionTrace {
public:
static std::vector<DebugTraceEvent> append(std::vector<DebugTraceEvent> events,
const DebugTraceEvent& e) {
events.push_back(e);
std::sort(events.begin(), events.end(), [](const DebugTraceEvent& a, const DebugTraceEvent& b) {
if (a.index != b.index) return a.index < b.index;
if (a.action != b.action) return a.action < b.action;
return a.status < b.status;
});
return events;
}
static nlohmann::json toJson(const std::vector<DebugTraceEvent>& events) {
nlohmann::json arr = nlohmann::json::array();
for (const auto& e : events) arr.push_back({{"index", e.index}, {"action", e.action}, {"status", e.status}});
return arr;
}
};

View File

@@ -0,0 +1,35 @@
#pragma once
// Step 1537: debug guidance packet model.
#include <nlohmann/json.hpp>
#include "DebugPolicyConstraint.h"
#include "DebugActionBudget.h"
#include "DebugRecoveryAdvisor.h"
#include "DebugStopReasonClassifier.h"
struct DebugGuidancePacket {
DebugPolicyConstraint constraints;
DebugActionBudget budget;
DebugRecoveryAdvice recovery;
DebugStopReason stopReason;
};
class DebugGuidancePacketModel {
public:
static DebugGuidancePacket build(const DebugPolicyConstraint& constraints,
const DebugActionBudget& budget,
const DebugRecoveryAdvice& recovery,
const DebugStopReason& stopReason) {
return {constraints, budget, recovery, stopReason};
}
static nlohmann::json toJson(const DebugGuidancePacket& p) {
return {
{"constraints", DebugPolicyConstraintModel::toJson(p.constraints)},
{"budget", DebugActionBudgetModel::toJson(p.budget)},
{"recovery", DebugRecoveryAdvisor::toJson(p.recovery)},
{"stop_reason", DebugStopReasonClassifier::toJson(p.stopReason)}
};
}
};

View File

@@ -0,0 +1,31 @@
#pragma once
// Step 1547: debug handoff packet model.
#include <nlohmann/json.hpp>
#include "DebugChecklistTemplate.h"
#include "DebugSessionHandoff.h"
#include "DebugEvidenceIndex.h"
struct DebugHandoffPacket {
DebugChecklistTemplate checklist;
DebugSessionHandoff handoff;
std::vector<DebugEvidenceEntry> evidence;
};
class DebugHandoffPacketModel {
public:
static DebugHandoffPacket build(const DebugChecklistTemplate& checklist,
const DebugSessionHandoff& handoff,
const std::vector<DebugEvidenceEntry>& evidence) {
return {checklist, handoff, evidence};
}
static nlohmann::json toJson(const DebugHandoffPacket& p) {
return {
{"checklist", DebugChecklistTemplateModel::toJson(p.checklist)},
{"handoff", DebugSessionHandoffModel::toJson(p.handoff)},
{"evidence", DebugEvidenceIndexModel::toJson(DebugEvidenceIndexModel::sort(p.evidence))}
};
}
};

View File

@@ -0,0 +1,44 @@
#pragma once
// Step 1509: debug hint template model.
#include <algorithm>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
struct DebugHintPacket {
std::string failureClass;
std::vector<std::string> hints;
std::string confidence = "medium";
};
class DebugHintTemplate {
public:
static DebugHintPacket build(const std::string& failureClass,
const std::vector<std::string>& context) {
DebugHintPacket p;
p.failureClass = failureClass;
p.hints = baseHints(failureClass);
if (!context.empty()) p.hints.push_back("Focus file: " + context.front());
std::sort(p.hints.begin(), p.hints.end());
p.hints.erase(std::unique(p.hints.begin(), p.hints.end()), p.hints.end());
p.confidence = p.hints.size() > 2 ? "high" : "medium";
return p;
}
static nlohmann::json toJson(const DebugHintPacket& p) {
return {{"failure_class", p.failureClass}, {"hints", p.hints}, {"confidence", p.confidence}};
}
private:
static std::vector<std::string> baseHints(const std::string& failureClass) {
if (failureClass == "compile") {
return {"Check recent syntax edits", "Re-run single target first", "Review include/import order"};
}
if (failureClass == "test") {
return {"Minimize fixture setup", "Verify expectation order", "Run failing test in isolation"};
}
return {"Capture failure packet", "Cluster by root symptom", "Guard against regressions"};
}
};

View File

@@ -0,0 +1,82 @@
#pragma once
// Step 1457: deterministic debug loop orchestrator core.
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
#include "FailurePacket.h"
#include "FailureClusterer.h"
#include "FixContextAssembler.h"
#include "FailurePatchProposer.h"
#include "RegressionGuardPlanner.h"
struct DebugLoopResult {
std::string status; // green|escalated|stopped
int iterations = 0;
std::vector<nlohmann::json> transitions;
FailurePacket finalPacket;
RegressionGuardPlan guard;
};
class DebugLoopOrchestrator {
public:
static DebugLoopResult run(const std::string& command,
int maxIterations,
const std::string& contextBudget,
bool applyPatches) {
DebugLoopResult out;
out.status = "stopped";
for (int i = 1; i <= std::max(1, maxIterations); ++i) {
out.iterations = i;
out.transitions.push_back({{"iteration", i}, {"phase", "reproduce"}});
auto p = capturePacket(command);
out.finalPacket = p;
out.transitions.push_back({{"iteration", i}, {"phase", "capture"}, {"failure_class", p.failureClass}});
auto clusters = FailureClusterer::cluster({p});
out.transitions.push_back({{"iteration", i}, {"phase", "cluster"}, {"clusters", (int)clusters.size()}});
std::vector<FixContextSlice> slices = {{
p.primaryFile.empty() ? "editor/src/unknown.h" : p.primaryFile,
p.primaryLine > 0 ? p.primaryLine : 1,
p.primaryLine > 0 ? p.primaryLine + 5 : 10,
p.rawExcerpt
}};
auto ctx = FixContextAssembler::assemble(contextBudget, slices);
out.transitions.push_back({{"iteration", i}, {"phase", "assemble_context"}, {"chars", ctx.totalChars}});
auto suggestion = FailurePatchProposer::propose(clusters.empty() ? FailureCluster{} : clusters[0], ctx,
"step" + std::to_string(700 + i) + "_test");
out.transitions.push_back({{"iteration", i}, {"phase", "propose_patch"}, {"proposal_id", suggestion.proposal.proposalId}});
if (applyPatches) out.transitions.push_back({{"iteration", i}, {"phase", "apply_patch"}, {"applied", true}});
const bool fixable = command.find("fixable") != std::string::npos;
if (fixable && applyPatches) {
out.status = "green";
out.guard = RegressionGuardPlanner::plan({p.primaryFile}, 1457, "step1457_test");
out.transitions.push_back({{"iteration", i}, {"phase", "guard"}, {"status", "green"}});
return out;
}
}
out.status = "escalated";
out.guard = RegressionGuardPlanner::plan({out.finalPacket.primaryFile}, 1457, "step1457_test");
out.transitions.push_back({{"phase", "stop"}, {"reason", "max_iterations"}});
return out;
}
private:
static FailurePacket capturePacket(const std::string& command) {
if (command.rfind("mock:", 0) == 0) {
std::string raw = command.substr(5);
int exitCode = raw.find("ok") != std::string::npos ? 0 : 1;
return FailurePacketModel::make(command, raw, exitCode);
}
return FailurePacketModel::make(command, "runtime_error: non-mock command capture not enabled", 1);
}
};

View File

@@ -0,0 +1,52 @@
#pragma once
// Step 1467: debugging metrics packet.
#include <string>
#include <nlohmann/json.hpp>
struct DebugMetricsPacket {
std::string sessionId;
int iterationsToGreen = 0;
int timeToGreenMs = 0;
int tokenCostPerFix = 0;
int symptomToRootCauseCompression = 0;
int patchAcceptanceRatePct = 0;
int regressionEscapeRatePct = 0;
};
class DebugMetricsModel {
public:
static DebugMetricsPacket compute(const std::string& sessionId,
int iterations,
int durationMs,
int tokenCost,
int symptomCount,
int rootCauseCount,
int acceptedPatches,
int proposedPatches,
int regressions,
int totalRuns) {
DebugMetricsPacket m;
m.sessionId = sessionId;
m.iterationsToGreen = iterations;
m.timeToGreenMs = durationMs;
m.tokenCostPerFix = tokenCost;
m.symptomToRootCauseCompression = (rootCauseCount == 0) ? 0 : (100 * symptomCount / rootCauseCount);
m.patchAcceptanceRatePct = (proposedPatches == 0) ? 0 : (100 * acceptedPatches / proposedPatches);
m.regressionEscapeRatePct = (totalRuns == 0) ? 0 : (100 * regressions / totalRuns);
return m;
}
static nlohmann::json toJson(const DebugMetricsPacket& m) {
return {
{"session_id", m.sessionId},
{"iterations_to_green", m.iterationsToGreen},
{"time_to_green_ms", m.timeToGreenMs},
{"token_cost_per_fix", m.tokenCostPerFix},
{"symptom_to_root_cause_compression", m.symptomToRootCauseCompression},
{"patch_acceptance_rate_pct", m.patchAcceptanceRatePct},
{"regression_escape_rate_pct", m.regressionEscapeRatePct}
};
}
};

View File

@@ -0,0 +1,50 @@
#pragma once
// Step 1529: debug policy constraint model.
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
struct DebugPolicyConstraint {
std::string mode = "safe";
int maxFilesTouched = 3;
int maxLineChanges = 40;
bool requireTests = true;
std::vector<std::string> forbiddenPaths;
};
class DebugPolicyConstraintModel {
public:
static DebugPolicyConstraint forMode(const std::string& mode) {
DebugPolicyConstraint c;
c.mode = mode;
if (mode == "strict") {
c.maxFilesTouched = 2;
c.maxLineChanges = 25;
c.requireTests = true;
c.forbiddenPaths = {"editor/src/mcp/", "editor/src/MCPServer.h"};
} else if (mode == "normal") {
c.maxFilesTouched = 4;
c.maxLineChanges = 60;
c.requireTests = true;
c.forbiddenPaths = {"editor/src/mcp/"};
} else {
c.maxFilesTouched = 3;
c.maxLineChanges = 40;
c.requireTests = true;
c.forbiddenPaths = {"editor/src/mcp/"};
}
return c;
}
static nlohmann::json toJson(const DebugPolicyConstraint& c) {
return {
{"mode", c.mode},
{"max_files_touched", c.maxFilesTouched},
{"max_line_changes", c.maxLineChanges},
{"require_tests", c.requireTests},
{"forbidden_paths", c.forbiddenPaths}
};
}
};

View File

@@ -0,0 +1,49 @@
#pragma once
// Step 1520: debug recipe library model.
#include <algorithm>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
#include "DebugRecipeStep.h"
struct DebugRecipe {
std::string failureClass;
std::vector<DebugRecipeStep> steps;
};
class DebugRecipeLibrary {
public:
static DebugRecipe get(const std::string& failureClass) {
DebugRecipe r;
r.failureClass = failureClass;
if (failureClass == "compile") {
r.steps = {
DebugRecipeStepModel::make(1, "Run single target compile", "deterministic compile error"),
DebugRecipeStepModel::make(2, "Inspect first compiler diagnostic", "root parse/type issue"),
DebugRecipeStepModel::make(3, "Patch smallest failing unit", "compile green")
};
} else if (failureClass == "test") {
r.steps = {
DebugRecipeStepModel::make(1, "Run failing test only", "single deterministic failure"),
DebugRecipeStepModel::make(2, "Reduce fixture and mocks", "minimal repro"),
DebugRecipeStepModel::make(3, "Apply constrained patch", "test green")
};
} else {
r.steps = {
DebugRecipeStepModel::make(1, "Capture failure packet", "classified failure"),
DebugRecipeStepModel::make(2, "Cluster by root symptom", "primary cluster"),
DebugRecipeStepModel::make(3, "Apply guarded fix", "regression-safe result")
};
}
return r;
}
static nlohmann::json toJson(const DebugRecipe& r) {
nlohmann::json arr = nlohmann::json::array();
for (const auto& s : r.steps) arr.push_back(DebugRecipeStepModel::toJson(s));
return {{"failure_class", r.failureClass}, {"steps", arr}};
}
};

View File

@@ -0,0 +1,31 @@
#pragma once
// Step 1527: debug recipe packet model.
#include <nlohmann/json.hpp>
#include "DebugRecipeLibrary.h"
#include "DebugActionValidator.h"
#include "DebugExecutionTrace.h"
struct DebugRecipePacket {
DebugRecipe recipe;
DebugActionValidation validation;
std::vector<DebugTraceEvent> trace;
};
class DebugRecipePacketModel {
public:
static DebugRecipePacket build(const DebugRecipe& recipe,
const DebugActionValidation& validation,
const std::vector<DebugTraceEvent>& trace) {
return {recipe, validation, trace};
}
static nlohmann::json toJson(const DebugRecipePacket& p) {
return {
{"recipe", DebugRecipeLibrary::toJson(p.recipe)},
{"validation", DebugActionValidator::toJson(p.validation)},
{"trace", DebugExecutionTrace::toJson(p.trace)}
};
}
};

View File

@@ -0,0 +1,23 @@
#pragma once
// Step 1519: debug recipe step model.
#include <string>
#include <nlohmann/json.hpp>
struct DebugRecipeStep {
int index = 0;
std::string action;
std::string expected;
};
class DebugRecipeStepModel {
public:
static DebugRecipeStep make(int index, const std::string& action, const std::string& expected) {
return {index, action, expected};
}
static nlohmann::json toJson(const DebugRecipeStep& s) {
return {{"index", s.index}, {"action", s.action}, {"expected", s.expected}};
}
};

View File

@@ -0,0 +1,44 @@
#pragma once
// Step 1531: debug recovery advisor model.
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
struct DebugRecoveryAdvice {
std::string category = "generic";
std::vector<std::string> actions;
};
class DebugRecoveryAdvisor {
public:
static DebugRecoveryAdvice advise(const std::string& stopReason) {
DebugRecoveryAdvice a;
a.category = stopReason;
if (stopReason == "budget_exhausted") {
a.actions = {
"Reduce context budget to tiny",
"Replay last checkpoint",
"Prioritize highest-severity target"
};
} else if (stopReason == "policy_violation") {
a.actions = {
"Rollback last patch",
"Constrain file scope",
"Re-run regression guard"
};
} else {
a.actions = {
"Capture fresh failure packet",
"Re-cluster failures",
"Escalate to guarded review"
};
}
return a;
}
static nlohmann::json toJson(const DebugRecoveryAdvice& a) {
return {{"category", a.category}, {"actions", a.actions}};
}
};

View File

@@ -0,0 +1,26 @@
#pragma once
// Step 1541: debug session handoff model.
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
struct DebugSessionHandoff {
std::string sessionId;
std::string summary;
std::vector<std::string> nextActions;
};
class DebugSessionHandoffModel {
public:
static DebugSessionHandoff build(const std::string& sessionId,
const std::string& summary,
const std::vector<std::string>& nextActions) {
return {sessionId, summary, nextActions};
}
static nlohmann::json toJson(const DebugSessionHandoff& h) {
return {{"session_id", h.sessionId}, {"summary", h.summary}, {"next_actions", h.nextActions}};
}
};

View File

@@ -0,0 +1,39 @@
#pragma once
// Step 1532: debug stop-reason classifier model.
#include <string>
#include <nlohmann/json.hpp>
struct DebugStopReason {
std::string code = "unknown";
std::string summary = "Unknown stop reason";
};
class DebugStopReasonClassifier {
public:
static DebugStopReason classify(bool budgetExceeded,
bool policyViolation,
bool testsFailing,
bool patchRejected) {
DebugStopReason r;
if (policyViolation) {
r.code = "policy_violation";
r.summary = "Policy constraint violated";
} else if (budgetExceeded) {
r.code = "budget_exhausted";
r.summary = "Budget exhausted before green";
} else if (patchRejected) {
r.code = "patch_rejected";
r.summary = "Patch proposal rejected by checks";
} else if (testsFailing) {
r.code = "tests_still_failing";
r.summary = "Tests still failing after attempts";
}
return r;
}
static nlohmann::json toJson(const DebugStopReason& r) {
return {{"code", r.code}, {"summary", r.summary}};
}
};

View File

@@ -0,0 +1,76 @@
#pragma once
// Step 1450: root-cause clustering model.
#include <algorithm>
#include <map>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
#include "FailurePacket.h"
struct FailureCluster {
std::string clusterId;
std::string rootCauseCandidate;
std::string failureClass;
std::vector<std::string> memberPacketIds;
int blastRadius = 0;
bool fixFirst = false;
};
class FailureClusterer {
public:
static std::vector<FailureCluster> cluster(const std::vector<FailurePacket>& packets) {
std::map<std::string, FailureCluster> byKey;
for (const auto& p : packets) {
std::string key = p.failureClass + "|" + p.primaryFile + "|" + p.primarySymbol;
auto& c = byKey[key];
if (c.clusterId.empty()) {
c.failureClass = p.failureClass;
c.rootCauseCandidate = p.primarySymbol.empty() ? p.primaryFile : p.primarySymbol;
c.clusterId = stableId(key);
}
c.memberPacketIds.push_back(p.packetId);
}
std::vector<FailureCluster> out;
for (auto& kv : byKey) {
kv.second.blastRadius = static_cast<int>(kv.second.memberPacketIds.size());
std::sort(kv.second.memberPacketIds.begin(), kv.second.memberPacketIds.end());
out.push_back(kv.second);
}
std::sort(out.begin(), out.end(), [](const auto& a, const auto& b) {
if (a.blastRadius != b.blastRadius) return a.blastRadius > b.blastRadius;
return a.clusterId < b.clusterId;
});
if (!out.empty()) out[0].fixFirst = true;
return out;
}
static nlohmann::json toJson(const std::vector<FailureCluster>& c) {
nlohmann::json j = nlohmann::json::array();
for (const auto& x : c) {
j.push_back({
{"cluster_id", x.clusterId},
{"root_cause_candidate", x.rootCauseCandidate},
{"failure_class", x.failureClass},
{"member_packet_ids", x.memberPacketIds},
{"blast_radius", x.blastRadius},
{"fix_first", x.fixFirst}
});
}
return j;
}
private:
static std::string stableId(const std::string& key) {
uint64_t h = 2166136261u;
for (unsigned char c : key) {
h ^= c;
h *= 16777619u;
}
return "fc_" + std::to_string(h);
}
};

View File

@@ -0,0 +1,54 @@
#pragma once
// Step 1459: deterministic failure fixture catalog.
#include <algorithm>
#include <map>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
struct FailureFixture {
std::string id;
std::string failureClass;
std::string command;
std::string raw;
int exitCode = 1;
std::string expectedPrimaryFile;
};
class FailureFixtureCatalog {
public:
static std::vector<FailureFixture> defaults() {
std::vector<FailureFixture> v = {
{"compile_missing_include", "compile_error", "mock:error: editor/src/a.cpp:10:1 missing include", "error: editor/src/a.cpp:10:1 missing include", 1, "editor/src/a.cpp"},
{"test_assertion_fail", "test_assertion", "mock:alpha... PASS\nbeta... FAIL", "alpha... PASS\nbeta... FAIL", 1, ""},
{"tool_contract_missing", "tool_contract_error", "mock:Unknown tool: whetstone_x", "Unknown tool: whetstone_x", 1, ""},
{"schema_shape_mismatch", "schema_error", "mock:schema mismatch in packet", "schema mismatch in packet", 1, ""},
{"runtime_nonzero", "runtime_error", "mock:segmentation fault", "segmentation fault", 1, ""}
};
std::sort(v.begin(), v.end(), [](const auto& a, const auto& b){ return a.id < b.id; });
return v;
}
static const FailureFixture* find(const std::vector<FailureFixture>& fixtures,
const std::string& id) {
for (const auto& f : fixtures) if (f.id == id) return &f;
return nullptr;
}
static nlohmann::json toJson(const std::vector<FailureFixture>& fixtures) {
nlohmann::json arr = nlohmann::json::array();
for (const auto& f : fixtures) {
arr.push_back({
{"id", f.id},
{"failure_class", f.failureClass},
{"command", f.command},
{"raw", f.raw},
{"exit_code", f.exitCode},
{"expected_primary_file", f.expectedPrimaryFile}
});
}
return arr;
}
};

View File

@@ -0,0 +1,203 @@
#pragma once
// Step 1449: deterministic failure packet schema + normalizer.
#include <algorithm>
#include <cctype>
#include <cstdint>
#include <sstream>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
struct FailurePacket {
std::string packetId;
std::string failureClass = "unknown";
std::string primaryFile;
int primaryLine = 0;
std::string primarySymbol;
std::string normalizedMessage;
std::string reproCommand;
std::string firstFailingTest;
double confidence = 0.0;
std::string rawExcerpt;
};
class FailurePacketModel {
public:
static std::string canonicalizePath(const std::string& path) {
std::string p = path;
const std::string home = "/home/bill/Documents/CLionProjects/whetstone_DSL/";
auto pos = p.find(home);
if (pos != std::string::npos) p = p.substr(pos + home.size());
return p;
}
static std::string normalizeWhitespace(const std::string& in) {
std::string out;
bool ws = false;
for (char c : in) {
if (std::isspace(static_cast<unsigned char>(c))) {
ws = true;
} else {
if (ws && !out.empty()) out.push_back(' ');
out.push_back(c);
ws = false;
}
}
return out;
}
static std::string stableTruncate(const std::string& in, size_t maxBytes) {
if (in.size() <= maxBytes) return in;
if (maxBytes < 16) return in.substr(0, maxBytes);
return in.substr(0, maxBytes - 11) + "...[trunc]";
}
static std::string dedupLines(const std::string& in) {
std::vector<std::string> lines;
std::string cur;
for (char c : in) {
if (c == '\n') {
lines.push_back(cur);
cur.clear();
} else cur.push_back(c);
}
if (!cur.empty()) lines.push_back(cur);
std::vector<std::string> unique;
for (const auto& l : lines) {
if (l.empty()) continue;
if (std::find(unique.begin(), unique.end(), l) == unique.end()) unique.push_back(l);
}
std::string out;
for (size_t i = 0; i < unique.size(); ++i) {
if (i) out.push_back('\n');
out += unique[i];
}
return out;
}
static std::string classify(const std::string& text, int exitCode) {
const std::string s = text;
if (s.find("error:") != std::string::npos || s.find("undefined reference") != std::string::npos) return "compile_error";
if (s.find("FAIL") != std::string::npos || s.find("Assertion") != std::string::npos) return "test_assertion";
if (s.find("schema") != std::string::npos) return "schema_error";
if (s.find("Unknown tool") != std::string::npos) return "tool_contract_error";
if (exitCode != 0) return "runtime_error";
return "unknown";
}
static void extractPrimaryLocation(const std::string& text,
std::string* file,
int* line,
std::string* symbol) {
if (file) *file = "";
if (line) *line = 0;
if (symbol) *symbol = "";
std::string token;
std::istringstream iss(text);
while (iss >> token) {
auto p1 = token.find(':');
auto p2 = token.find(':', p1 == std::string::npos ? 0 : p1 + 1);
if (p1 == std::string::npos || p2 == std::string::npos) continue;
std::string f = canonicalizePath(token.substr(0, p1));
std::string lineStr = token.substr(p1 + 1, p2 - p1 - 1);
bool digits = !lineStr.empty();
for (char c : lineStr) if (!std::isdigit(static_cast<unsigned char>(c))) digits = false;
if (!digits) continue;
if (file) *file = f;
if (line) *line = std::stoi(lineStr);
break;
}
auto pos = text.find("undefined symbol ");
if (pos != std::string::npos && symbol) {
std::string rem = text.substr(pos + 17);
std::string s;
for (char c : rem) {
if (std::isalnum(static_cast<unsigned char>(c)) || c == '_') s.push_back(c);
else break;
}
*symbol = s;
}
}
static std::string extractFirstFailingTest(const std::string& text) {
std::istringstream iss(text);
std::string line;
while (std::getline(iss, line)) {
auto p = line.find("... FAIL");
if (p != std::string::npos) return normalizeWhitespace(line.substr(0, p));
}
return "";
}
static FailurePacket make(const std::string& command,
const std::string& rawOutput,
int exitCode,
size_t maxBytes = 8192) {
FailurePacket p;
p.reproCommand = command;
p.failureClass = classify(rawOutput, exitCode);
std::string deduped = dedupLines(rawOutput);
std::string norm = normalizeWhitespace(deduped);
p.normalizedMessage = stableTruncate(norm, maxBytes);
p.rawExcerpt = stableTruncate(deduped, maxBytes);
p.firstFailingTest = extractFirstFailingTest(rawOutput);
extractPrimaryLocation(rawOutput, &p.primaryFile, &p.primaryLine, &p.primarySymbol);
p.confidence = 0.5;
if (p.failureClass == "compile_error" || p.failureClass == "test_assertion") p.confidence = 0.9;
else if (p.failureClass == "runtime_error") p.confidence = 0.7;
p.packetId = stableId(p);
return p;
}
static nlohmann::json toJson(const FailurePacket& p) {
return {
{"packet_id", p.packetId},
{"failure_class", p.failureClass},
{"primary_file", p.primaryFile},
{"primary_line", p.primaryLine},
{"primary_symbol", p.primarySymbol},
{"normalized_message", p.normalizedMessage},
{"repro_command", p.reproCommand},
{"first_failing_test", p.firstFailingTest},
{"confidence", p.confidence},
{"raw_excerpt", p.rawExcerpt}
};
}
static FailurePacket fromJson(const nlohmann::json& j) {
FailurePacket p;
p.packetId = j.value("packet_id", "");
p.failureClass = j.value("failure_class", "unknown");
p.primaryFile = j.value("primary_file", "");
p.primaryLine = j.value("primary_line", 0);
p.primarySymbol = j.value("primary_symbol", "");
p.normalizedMessage = j.value("normalized_message", "");
p.reproCommand = j.value("repro_command", "");
p.firstFailingTest = j.value("first_failing_test", "");
p.confidence = j.value("confidence", 0.0);
p.rawExcerpt = j.value("raw_excerpt", "");
return p;
}
private:
static std::string stableId(const FailurePacket& p) {
std::string key = p.failureClass + "|" + p.primaryFile + "|" + std::to_string(p.primaryLine) + "|" + p.normalizedMessage;
uint64_t h = 1469598103934665603ull;
for (unsigned char c : key) {
h ^= c;
h *= 1099511628211ull;
}
std::ostringstream oss;
oss << "fp_" << std::hex << h;
return oss.str();
}
};

View File

@@ -0,0 +1,67 @@
#pragma once
// Step 1453: failure-class-driven patch proposer.
#include <string>
#include <vector>
#include "FailureClusterer.h"
#include "FixContextAssembler.h"
#include "PatchProposal.h"
struct FailurePatchSuggestion {
PatchProposal proposal;
double confidence = 0.0;
std::string rationale;
};
class FailurePatchProposer {
public:
static FailurePatchSuggestion propose(const FailureCluster& cluster,
const FixContextPacket& context,
const std::string& testTarget = "") {
FailurePatchSuggestion s;
s.proposal.failureClusterId = cluster.clusterId;
std::string target = context.slices.empty() ? "editor/src/unknown.h" : context.slices[0].path;
std::string strategy = strategyFor(cluster.failureClass);
s.rationale = "strategy=" + strategy + " class=" + cluster.failureClass;
s.proposal.patchPlan.push_back({strategy, target});
s.proposal.expectedTestsToRun = testTarget.empty() ? std::vector<std::string>{"step_smoke_test"}
: std::vector<std::string>{testTarget};
s.proposal.invariants = {
"do_not_edit_unrelated_files",
"preserve_schema_keys",
"keep_output_deterministic"
};
s.proposal.riskLevel = (cluster.failureClass == "compile_error") ? "low" : "medium";
s.proposal.candidateDiff =
"diff --git a/" + target + " b/" + target + "\n"
"--- a/" + target + "\n"
"+++ b/" + target + "\n"
"@@\n"
"+// auto-fix placeholder for " + strategy + "\n";
s.confidence = confidenceFor(cluster.failureClass);
s.proposal.proposalId = PatchProposalModel::stableId(s.proposal);
return s;
}
private:
static std::string strategyFor(const std::string& failureClass) {
if (failureClass == "compile_error") return "missing_include_or_symbol_fix";
if (failureClass == "schema_error") return "schema_key_alignment_fix";
if (failureClass == "test_assertion") return "assertion_behavior_adjustment_fix";
if (failureClass == "tool_contract_error") return "tool_input_contract_fix";
return "safe_fallback_review_fix";
}
static double confidenceFor(const std::string& failureClass) {
if (failureClass == "compile_error") return 0.85;
if (failureClass == "schema_error") return 0.8;
if (failureClass == "test_assertion") return 0.75;
if (failureClass == "tool_contract_error") return 0.78;
return 0.4;
}
};

View File

@@ -0,0 +1,42 @@
#pragma once
// Step 1469: failure trend tracker model.
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
struct FailureTrendPoint {
int iteration = 0;
int gapScore = 0;
};
struct FailureTrend {
std::vector<FailureTrendPoint> points;
bool worsening = false;
bool improving = false;
bool stagnant = false;
};
class FailureTrendTracker {
public:
static FailureTrend analyze(const std::vector<FailureTrendPoint>& points) {
FailureTrend t;
t.points = points;
if (points.size() < 2) {
t.stagnant = true;
return t;
}
int delta = points.back().gapScore - points.front().gapScore;
t.worsening = delta > 0;
t.improving = delta < 0;
t.stagnant = delta == 0;
return t;
}
static nlohmann::json toJson(const FailureTrend& t) {
nlohmann::json arr = nlohmann::json::array();
for (const auto& p : t.points) arr.push_back({{"iteration", p.iteration}, {"gap_score", p.gapScore}});
return {{"points", arr}, {"worsening", t.worsening}, {"improving", t.improving}, {"stagnant", t.stagnant}};
}
};

View File

@@ -0,0 +1,54 @@
#pragma once
// Step 1510: failure triage score model.
#include <algorithm>
#include <string>
#include <nlohmann/json.hpp>
struct FailureTriageScore {
std::string failureClass;
int severity = 0;
int reproducibility = 0;
int blastRadius = 0;
int score = 0;
std::string tier = "low";
};
class FailureTriageScorer {
public:
static FailureTriageScore compute(const std::string& failureClass,
int severity,
int reproducibility,
int blastRadius) {
FailureTriageScore s;
s.failureClass = failureClass;
s.severity = clamp10(severity);
s.reproducibility = clamp10(reproducibility);
s.blastRadius = clamp10(blastRadius);
s.score = s.severity * 5 + s.reproducibility * 3 + s.blastRadius * 2;
s.tier = tierFor(s.score);
return s;
}
static nlohmann::json toJson(const FailureTriageScore& s) {
return {
{"failure_class", s.failureClass},
{"severity", s.severity},
{"reproducibility", s.reproducibility},
{"blast_radius", s.blastRadius},
{"score", s.score},
{"tier", s.tier}
};
}
private:
static int clamp10(int v) { return std::max(0, std::min(10, v)); }
static std::string tierFor(int score) {
if (score >= 70) return "critical";
if (score >= 40) return "high";
if (score >= 20) return "medium";
return "low";
}
};

View File

@@ -0,0 +1,62 @@
#pragma once
// Step 1451: fix-context assembler with budget modes.
#include <algorithm>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
struct FixContextSlice {
std::string path;
int startLine = 1;
int endLine = 1;
std::string text;
};
struct FixContextPacket {
std::string mode;
int budgetChars = 0;
int totalChars = 0;
std::vector<FixContextSlice> slices;
};
class FixContextAssembler {
public:
static int budgetForMode(const std::string& mode) {
if (mode == "tiny") return 2000;
if (mode == "small") return 6000;
return 12000;
}
static FixContextPacket assemble(const std::string& mode,
const std::vector<FixContextSlice>& input) {
FixContextPacket out;
out.mode = mode;
out.budgetChars = budgetForMode(mode);
std::vector<FixContextSlice> sorted = input;
std::sort(sorted.begin(), sorted.end(), [](const auto& a, const auto& b) {
if (a.path != b.path) return a.path < b.path;
return a.startLine < b.startLine;
});
for (const auto& s : sorted) {
if (out.totalChars >= out.budgetChars) break;
FixContextSlice keep = s;
int remaining = out.budgetChars - out.totalChars;
if (static_cast<int>(keep.text.size()) > remaining) keep.text = keep.text.substr(0, remaining);
out.totalChars += static_cast<int>(keep.text.size());
out.slices.push_back(std::move(keep));
}
return out;
}
static nlohmann::json toJson(const FixContextPacket& p) {
nlohmann::json arr = nlohmann::json::array();
for (const auto& s : p.slices) {
arr.push_back({{"path", s.path}, {"start_line", s.startLine}, {"end_line", s.endLine}, {"text", s.text}});
}
return {{"mode", p.mode}, {"budget_chars", p.budgetChars}, {"total_chars", p.totalChars}, {"slices", arr}};
}
};

View File

@@ -0,0 +1,36 @@
#pragma once
// Step 1511: minimal repro reducer model.
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
struct MinimalReproResult {
std::string originalCommand;
std::string reducedCommand;
int removedFlags = 0;
};
class MinimalReproReducer {
public:
static MinimalReproResult reduce(const std::string& command,
const std::vector<std::string>& removableFlags) {
MinimalReproResult r;
r.originalCommand = command;
r.reducedCommand = command;
for (const auto& f : removableFlags) {
std::string needle = " " + f;
auto pos = r.reducedCommand.find(needle);
if (pos != std::string::npos) {
r.reducedCommand.erase(pos, needle.size());
++r.removedFlags;
}
}
return r;
}
static nlohmann::json toJson(const MinimalReproResult& r) {
return {{"original_command", r.originalCommand}, {"reduced_command", r.reducedCommand}, {"removed_flags", r.removedFlags}};
}
};

View File

@@ -0,0 +1,25 @@
#pragma once
// Step 1470: patch candidate ranking with history penalties.
#include <algorithm>
#include <string>
#include <vector>
struct RankedPatchCandidate {
std::string proposalId;
double confidence = 0.0;
int attempts = 0;
double score = 0.0;
};
class PatchCandidateRanker {
public:
static std::vector<RankedPatchCandidate> rank(std::vector<RankedPatchCandidate> in) {
for (auto& c : in) c.score = c.confidence - 0.1 * c.attempts;
std::sort(in.begin(), in.end(), [](const auto& a, const auto& b) {
if (a.score != b.score) return a.score > b.score;
return a.proposalId < b.proposalId;
});
return in;
}
};

View File

@@ -0,0 +1,53 @@
#pragma once
// Step 1460: patch dry-run applier.
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
struct PatchDryRunResult {
bool validDiff = false;
bool filesResolvable = false;
bool success = false;
std::vector<std::string> touchedFiles;
std::vector<std::string> errors;
};
class PatchDryRunApplier {
public:
static PatchDryRunResult run(const std::string& diffText,
const std::string& workspaceRoot = "") {
(void)workspaceRoot;
PatchDryRunResult r;
if (diffText.find("diff --git ") == std::string::npos) {
r.errors.push_back("diff_missing_header");
return r;
}
r.validDiff = true;
size_t p = 0;
while (true) {
p = diffText.find("diff --git a/", p);
if (p == std::string::npos) break;
size_t start = p + 13;
size_t end = diffText.find(" b/", start);
if (end == std::string::npos) break;
std::string file = diffText.substr(start, end - start);
if (!file.empty()) r.touchedFiles.push_back(file);
p = end + 3;
}
if (r.touchedFiles.empty()) {
r.errors.push_back("no_touched_files");
return r;
}
r.filesResolvable = true;
r.success = r.validDiff && r.filesResolvable;
return r;
}
static nlohmann::json toJson(const PatchDryRunResult& r) {
return {{"valid_diff", r.validDiff}, {"files_resolvable", r.filesResolvable}, {"success", r.success},
{"touched_files", r.touchedFiles}, {"errors", r.errors}};
}
};

View File

@@ -0,0 +1,33 @@
#pragma once
// Step 1487: patch execution audit bundle.
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
#include "PatchExecutionRecord.h"
struct PatchExecutionAuditBundle {
std::string sessionId;
std::vector<PatchExecutionRecord> records;
bool success = false;
};
class PatchExecutionAuditBundleModel {
public:
static PatchExecutionAuditBundle build(const std::string& sessionId,
const std::vector<PatchExecutionRecord>& records) {
PatchExecutionAuditBundle b;
b.sessionId = sessionId;
b.records = records;
b.success = !records.empty();
return b;
}
static nlohmann::json toJson(const PatchExecutionAuditBundle& b) {
nlohmann::json arr = nlohmann::json::array();
for (const auto& r : b.records) arr.push_back(PatchExecutionRecordModel::toJson(r));
return {{"session_id", b.sessionId}, {"records", arr}, {"success", b.success}};
}
};

View File

@@ -0,0 +1,43 @@
#pragma once
// Step 1479: patch execution record model.
#include <string>
#include <nlohmann/json.hpp>
struct PatchExecutionRecord {
std::string executionId;
std::string proposalId;
std::string phase; // validated|dry_run|applied|rolled_back
bool success = false;
std::string detail;
};
class PatchExecutionRecordModel {
public:
static PatchExecutionRecord make(const std::string& proposalId,
const std::string& phase,
bool success,
const std::string& detail) {
PatchExecutionRecord r;
r.proposalId = proposalId;
r.phase = phase;
r.success = success;
r.detail = detail;
r.executionId = stableId(proposalId, phase, detail);
return r;
}
static nlohmann::json toJson(const PatchExecutionRecord& r) {
return {{"execution_id", r.executionId}, {"proposal_id", r.proposalId}, {"phase", r.phase},
{"success", r.success}, {"detail", r.detail}};
}
private:
static std::string stableId(const std::string& p, const std::string& ph, const std::string& d) {
uint64_t h = 5381;
std::string key = p + "|" + ph + "|" + d;
for (unsigned char c : key) h = ((h << 5) + h) + c;
return "pe_" + std::to_string(h);
}
};

View File

@@ -0,0 +1,39 @@
#pragma once
// Step 1461: patch invariant checker.
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
#include "PatchProposal.h"
struct InvariantCheckResult {
bool success = false;
std::vector<std::string> violations;
};
class PatchInvariantChecker {
public:
static InvariantCheckResult check(const PatchProposal& p,
const std::vector<std::string>& allowedFiles) {
InvariantCheckResult r;
if (p.invariants.empty()) r.violations.push_back("invariants_missing");
if (p.expectedTestsToRun.empty()) r.violations.push_back("expected_tests_missing");
std::string err;
if (!PatchProposalModel::validate(p, allowedFiles, &err)) r.violations.push_back(err);
bool hasNoUnrelated = false;
for (const auto& i : p.invariants) if (i == "do_not_edit_unrelated_files") hasNoUnrelated = true;
if (!hasNoUnrelated) r.violations.push_back("scope_invariant_missing");
r.success = r.violations.empty();
return r;
}
static nlohmann::json toJson(const InvariantCheckResult& r) {
return {{"success", r.success}, {"violations", r.violations}};
}
};

View File

@@ -0,0 +1,86 @@
#pragma once
// Step 1452: patch proposal packet model.
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
struct PatchPlanItem {
std::string intent;
std::string targetFile;
};
struct PatchProposal {
std::string proposalId;
std::string failureClusterId;
std::vector<PatchPlanItem> patchPlan;
std::string candidateDiff;
std::string riskLevel = "medium";
std::vector<std::string> expectedTestsToRun;
std::vector<std::string> invariants;
};
class PatchProposalModel {
public:
static bool validate(const PatchProposal& p,
const std::vector<std::string>& allowedFiles,
std::string* error) {
if (error) *error = "";
if (p.invariants.empty()) {
if (error) *error = "invariants_missing";
return false;
}
if (p.candidateDiff.empty() || p.candidateDiff.find("diff --git") == std::string::npos) {
if (error) *error = "diff_invalid";
return false;
}
for (const auto& item : p.patchPlan) {
bool allowed = false;
for (const auto& f : allowedFiles) if (item.targetFile == f) allowed = true;
if (!allowed) {
if (error) *error = "out_of_scope_edit";
return false;
}
}
return true;
}
static nlohmann::json toJson(const PatchProposal& p) {
nlohmann::json plan = nlohmann::json::array();
for (const auto& i : p.patchPlan) plan.push_back({{"intent", i.intent}, {"target_file", i.targetFile}});
return {
{"proposal_id", p.proposalId},
{"failure_cluster_id", p.failureClusterId},
{"patch_plan", plan},
{"candidate_diff", p.candidateDiff},
{"risk_level", p.riskLevel},
{"expected_tests_to_run", p.expectedTestsToRun},
{"invariants", p.invariants}
};
}
static PatchProposal fromJson(const nlohmann::json& j) {
PatchProposal p;
p.proposalId = j.value("proposal_id", "");
p.failureClusterId = j.value("failure_cluster_id", "");
p.candidateDiff = j.value("candidate_diff", "");
p.riskLevel = j.value("risk_level", "medium");
p.expectedTestsToRun = j.value("expected_tests_to_run", std::vector<std::string>{});
p.invariants = j.value("invariants", std::vector<std::string>{});
for (const auto& it : j.value("patch_plan", nlohmann::json::array())) {
p.patchPlan.push_back({it.value("intent", ""), it.value("target_file", "")});
}
return p;
}
static std::string stableId(const PatchProposal& p) {
std::string key = p.failureClusterId + "|" + p.riskLevel + "|" + std::to_string(p.patchPlan.size());
uint64_t h = 1099511628211ull;
for (unsigned char c : key) {
h ^= c;
h *= 1469598103934665603ull;
}
return "pp_" + std::to_string(h);
}
};

View File

@@ -0,0 +1,37 @@
#pragma once
// Step 1512: patch risk labeler model.
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
struct PatchRiskLabel {
std::string level = "low";
std::vector<std::string> rationale;
};
class PatchRiskLabeler {
public:
static PatchRiskLabel label(int filesTouched,
int lineChanges,
bool corePathTouched,
bool addsUnsafePattern) {
PatchRiskLabel r;
if (filesTouched >= 5 || lineChanges >= 80 || corePathTouched || addsUnsafePattern) {
r.level = "high";
} else if (filesTouched >= 3 || lineChanges >= 30) {
r.level = "medium";
}
if (filesTouched >= 3) r.rationale.push_back("multi_file_change");
if (lineChanges >= 30) r.rationale.push_back("large_diff");
if (corePathTouched) r.rationale.push_back("core_path_touch");
if (addsUnsafePattern) r.rationale.push_back("unsafe_pattern");
if (r.rationale.empty()) r.rationale.push_back("contained_change");
return r;
}
static nlohmann::json toJson(const PatchRiskLabel& r) {
return {{"level", r.level}, {"rationale", r.rationale}};
}
};

View File

@@ -0,0 +1,52 @@
#pragma once
// Step 1455: deterministic regression guard planner.
#include <algorithm>
#include <set>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
struct RegressionGuardPlan {
std::vector<std::string> mustPass;
std::vector<std::string> optional;
std::vector<std::string> deferred;
};
class RegressionGuardPlanner {
public:
static RegressionGuardPlan plan(const std::vector<std::string>& touchedFiles,
int stepId,
const std::string& failingTarget) {
RegressionGuardPlan p;
if (!failingTarget.empty()) p.mustPass.push_back(failingTarget);
if (stepId > 1) p.mustPass.push_back("step" + std::to_string(stepId - 1) + "_test");
p.mustPass.push_back("step" + std::to_string(stepId) + "_test");
p.mustPass.push_back("step" + std::to_string(stepId + 1) + "_test");
bool mcpTouched = false;
for (const auto& f : touchedFiles) if (f.find("MCP") != std::string::npos || f.find("mcp/") != std::string::npos) mcpTouched = true;
if (mcpTouched) p.mustPass.push_back("mcp_tools_smoke_test");
p.optional = {"lint_smoke", "format_smoke"};
p.deferred = {"full_regression_suite"};
dedupSort(&p.mustPass);
dedupSort(&p.optional);
dedupSort(&p.deferred);
return p;
}
static nlohmann::json toJson(const RegressionGuardPlan& p) {
return {{"must_pass", p.mustPass}, {"optional", p.optional}, {"deferred", p.deferred}};
}
private:
static void dedupSort(std::vector<std::string>* xs) {
if (!xs) return;
std::sort(xs->begin(), xs->end());
xs->erase(std::unique(xs->begin(), xs->end()), xs->end());
}
};

View File

@@ -0,0 +1,35 @@
#pragma once
// Step 1471: repro command runner facade.
#include <string>
#include <nlohmann/json.hpp>
struct ReproRunResult {
std::string command;
int exitCode = 0;
std::string output;
bool mocked = true;
};
class ReproCommandRunner {
public:
static ReproRunResult run(const std::string& command) {
ReproRunResult r;
r.command = command;
if (command.rfind("mock:", 0) == 0) {
r.output = command.substr(5);
r.exitCode = (r.output.find("ok") != std::string::npos) ? 0 : 1;
r.mocked = true;
return r;
}
r.output = "runner_non_mock_mode_disabled";
r.exitCode = 1;
r.mocked = false;
return r;
}
static nlohmann::json toJson(const ReproRunResult& r) {
return {{"command", r.command}, {"exit_code", r.exitCode}, {"output", r.output}, {"mocked", r.mocked}};
}
};

View File

@@ -0,0 +1,80 @@
#pragma once
// Step 1454: repro packet archive + replay.
#include <filesystem>
#include <fstream>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
#include "FailurePacket.h"
struct ReproPacket {
FailurePacket failure;
std::string envHash;
nlohmann::json fileHashes = nlohmann::json::object();
nlohmann::json patchHistory = nlohmann::json::array();
std::string reproCommand;
};
class ReproPacketStore {
public:
static bool save(const std::string& path, const ReproPacket& p) {
std::ofstream out(path);
if (!out.good()) return false;
out << toJson(p).dump(2);
return true;
}
static bool load(const std::string& path, ReproPacket* out) {
if (!out) return false;
std::ifstream in(path);
if (!in.good()) return false;
nlohmann::json j;
in >> j;
*out = fromJson(j);
return true;
}
static bool appendHistory(const std::string& path, const nlohmann::json& historyItem) {
ReproPacket p;
if (!load(path, &p)) return false;
p.patchHistory.push_back(historyItem);
return save(path, p);
}
static bool replayClassMatches(const ReproPacket& archived,
const FailurePacket& current) {
return archived.failure.failureClass == current.failureClass &&
archived.failure.primaryFile == current.primaryFile;
}
static bool exportJsonl(const std::string& path,
const std::vector<ReproPacket>& packets) {
std::ofstream out(path);
if (!out.good()) return false;
for (const auto& p : packets) out << toJson(p).dump() << "\n";
return true;
}
static nlohmann::json toJson(const ReproPacket& p) {
return {
{"failure", FailurePacketModel::toJson(p.failure)},
{"env_hash", p.envHash},
{"file_hashes", p.fileHashes},
{"patch_history", p.patchHistory},
{"repro_command", p.reproCommand}
};
}
static ReproPacket fromJson(const nlohmann::json& j) {
ReproPacket p;
p.failure = FailurePacketModel::fromJson(j.value("failure", nlohmann::json::object()));
p.envHash = j.value("env_hash", "");
p.fileHashes = j.value("file_hashes", nlohmann::json::object());
p.patchHistory = j.value("patch_history", nlohmann::json::array());
p.reproCommand = j.value("repro_command", "");
return p;
}
};

View File

@@ -0,0 +1,38 @@
#pragma once
// Step 1480: rollback ledger model.
#include <fstream>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
#include "PatchExecutionRecord.h"
class RollbackLedger {
public:
static bool append(const std::string& path, const PatchExecutionRecord& rec) {
nlohmann::json arr = nlohmann::json::array();
std::ifstream in(path);
if (in.good()) in >> arr;
arr.push_back(PatchExecutionRecordModel::toJson(rec));
std::ofstream out(path);
if (!out.good()) return false;
out << arr.dump(2);
return true;
}
static nlohmann::json readAll(const std::string& path) {
std::ifstream in(path);
if (!in.good()) return nlohmann::json::array();
nlohmann::json arr;
in >> arr;
return arr.is_array() ? arr : nlohmann::json::array();
}
static nlohmann::json last(const std::string& path) {
auto arr = readAll(path);
if (arr.empty()) return nlohmann::json::object();
return arr.back();
}
};

View File

@@ -0,0 +1,33 @@
#pragma once
// Step 1476: SLM debug readiness scoring.
#include <string>
#include <nlohmann/json.hpp>
struct SLMDebugReadinessScore {
double score = 0.0;
std::string grade; // blocked|pilot|ready
std::string rationale;
};
class SLMDebugReadinessModel {
public:
static SLMDebugReadinessScore score(const nlohmann::json& metrics) {
SLMDebugReadinessScore out;
int acceptance = metrics.value("patch_acceptance_rate_pct", 0);
int regression = metrics.value("regression_escape_rate_pct", 100);
int compression = metrics.value("symptom_to_root_cause_compression", 0);
out.score = 0.5 * acceptance + 0.3 * (100 - regression) + 0.2 * std::min(100, compression / 4);
if (out.score >= 75) out.grade = "ready";
else if (out.score >= 50) out.grade = "pilot";
else out.grade = "blocked";
out.rationale = "acceptance=" + std::to_string(acceptance) + ", regression=" + std::to_string(regression);
return out;
}
static nlohmann::json toJson(const SLMDebugReadinessScore& r) {
return {{"score", r.score}, {"grade", r.grade}, {"rationale", r.rationale}};
}
};

View File

@@ -0,0 +1,37 @@
#pragma once
// Step 1491: safety envelope policy.
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
struct SafetyEnvelope {
int maxFailures = 3;
int maxRegressions = 1;
int maxTokensPerCampaign = 20000;
};
struct SafetyEnvelopeResult {
bool allowed = true;
std::vector<std::string> violations;
};
class SafetyEnvelopePolicy {
public:
static SafetyEnvelopeResult evaluate(const SafetyEnvelope& e,
int failures,
int regressions,
int tokenCost) {
SafetyEnvelopeResult r;
if (failures > e.maxFailures) r.violations.push_back("max_failures_exceeded");
if (regressions > e.maxRegressions) r.violations.push_back("max_regressions_exceeded");
if (tokenCost > e.maxTokensPerCampaign) r.violations.push_back("max_tokens_exceeded");
r.allowed = r.violations.empty();
return r;
}
static nlohmann::json toJson(const SafetyEnvelopeResult& r) {
return {{"allowed", r.allowed}, {"violations", r.violations}};
}
};