Files
whetstone_DSL/editor/src/ConstraintViolationDiagnostics.h

170 lines
5.4 KiB
C++

#pragma once
// Step 527: Constraint Violation Diagnostics
#include <algorithm>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
#include "LegalOperationGraph.h"
#include "SymbolScopeExtractor.h"
#include "TypedTaskitemContractSchema.h"
using json = nlohmann::json;
struct ConstraintViolation {
std::string code;
std::string message;
std::string field;
bool retryable = false;
};
struct ConstraintDiagnosticPacket {
bool ok = true;
std::string recommendedAction = "proceed";
std::vector<ConstraintViolation> violations;
json toJson() const {
json out;
out["ok"] = ok;
out["recommendedAction"] = recommendedAction;
out["violations"] = json::array();
for (const auto& v : violations) {
out["violations"].push_back({
{"code", v.code},
{"message", v.message},
{"field", v.field},
{"retryable", v.retryable}
});
}
return out;
}
};
struct ConstraintCheckInput {
TaskitemContract contract;
std::string language;
std::string nodeKind;
std::string requestedOp;
std::vector<std::string> requestedSymbols;
ScopeSnapshot scopeSnapshot;
};
class ConstraintViolationDiagnostics {
public:
static ConstraintDiagnosticPacket evaluate(
const ConstraintCheckInput& input,
const LegalOperationGraph& graph,
const SymbolScopeExtractor& scopeExtractor) {
ConstraintDiagnosticPacket packet;
addOperationViolations(packet, input, graph);
addSymbolViolations(packet, input, scopeExtractor);
deduplicate(packet.violations);
packet.ok = packet.violations.empty();
packet.recommendedAction = decideAction(packet.violations);
return packet;
}
private:
static void addOperationViolations(ConstraintDiagnosticPacket& packet,
const ConstraintCheckInput& input,
const LegalOperationGraph& graph) {
auto legal = graph.allowedOps(input.language, input.nodeKind);
if (!legal.supported) {
packet.violations.push_back({
"unsupported_context",
"No legal operation mapping for language/nodeKind",
input.language + "::" + input.nodeKind,
false
});
return;
}
if (!graph.opAllowed(input.language, input.nodeKind, input.requestedOp)) {
packet.violations.push_back({
"illegal_operation",
"Requested operation is illegal for this AST node",
input.requestedOp,
false
});
}
if (!TypedTaskitemContractSchema::opAllowed(input.contract, input.requestedOp)) {
packet.violations.push_back({
"contract_forbidden_operation",
"Requested operation is outside taskitem contract",
input.requestedOp,
false
});
}
}
static void addSymbolViolations(ConstraintDiagnosticPacket& packet,
const ConstraintCheckInput& input,
const SymbolScopeExtractor& scopeExtractor) {
auto unresolved = scopeExtractor.unresolvedSymbols(
input.scopeSnapshot, input.requestedSymbols);
for (const auto& symbol : unresolved) {
packet.violations.push_back({
"out_of_scope_symbol",
"Requested symbol is not in extracted scope snapshot",
symbol,
true
});
}
for (const auto& symbol : input.requestedSymbols) {
if (isForbidden(input.contract, symbol)) {
packet.violations.push_back({
"forbidden_symbol",
"Requested symbol is explicitly forbidden by contract",
symbol,
false
});
continue;
}
if (!TypedTaskitemContractSchema::symbolAllowed(input.contract, symbol)) {
packet.violations.push_back({
"contract_disallowed_symbol",
"Requested symbol is outside contract allowedSymbols",
symbol,
false
});
}
}
}
static bool isForbidden(const TaskitemContract& contract,
const std::string& symbol) {
return std::find(contract.forbiddenSymbols.begin(),
contract.forbiddenSymbols.end(),
symbol) != contract.forbiddenSymbols.end();
}
static void deduplicate(std::vector<ConstraintViolation>& violations) {
std::vector<ConstraintViolation> out;
for (const auto& v : violations) {
bool exists = false;
for (const auto& existing : out) {
if (existing.code == v.code && existing.field == v.field) {
exists = true;
break;
}
}
if (!exists) out.push_back(v);
}
violations.swap(out);
}
static std::string decideAction(const std::vector<ConstraintViolation>& violations) {
if (violations.empty()) return "proceed";
for (const auto& v : violations) {
if (!v.retryable) return "escalate";
}
return "retry";
}
};