#pragma once // Step 529: Pre-Apply Validation Gate #include #include #include #include #include #include "ConstraintViolationDiagnostics.h" #include "ConstraintSchemaFailurePacket.h" using json = nlohmann::json; struct CandidateEdit { std::string nodeKind; std::string operation; std::vector referencedSymbols; }; struct PreApplyValidationResult { bool allowed = false; bool mayMutateBuffer = false; TaskitemContract contract; ScopeSnapshot scopeSnapshot; ConstraintDiagnosticPacket diagnostics; }; class PreApplyValidationGate { public: static PreApplyValidationResult validate(const json& taskitemJson, const CandidateEdit& edit, const std::vector& visibleSymbols, const LegalOperationGraph& graph, const SymbolScopeExtractor& scopeExtractor) { PreApplyValidationResult result; auto schema = TypedTaskitemContractSchema::parseAndValidate(taskitemJson); if (!schema.valid) { result.diagnostics = makeUnderConstrainedContractPacket(schema.errors); return result; } result.contract = schema.contract; std::set forbidden(result.contract.forbiddenSymbols.begin(), result.contract.forbiddenSymbols.end()); result.scopeSnapshot = scopeExtractor.buildSnapshot(result.contract.language, edit.nodeKind, visibleSymbols, forbidden); ConstraintCheckInput input; input.contract = result.contract; input.language = result.contract.language; input.nodeKind = edit.nodeKind; input.requestedOp = edit.operation; input.requestedSymbols = edit.referencedSymbols; input.scopeSnapshot = result.scopeSnapshot; result.diagnostics = ConstraintViolationDiagnostics::evaluate(input, graph, scopeExtractor); if (!targetAllowed(result.contract, edit.nodeKind)) { result.diagnostics.violations.push_back({ "target_out_of_contract", "Target node kind is outside contract allowedTargets", edit.nodeKind, false }); result.diagnostics.ok = false; result.diagnostics.recommendedAction = "escalate"; } dedupe(result.diagnostics.violations); result.allowed = result.diagnostics.violations.empty(); result.mayMutateBuffer = result.allowed; result.diagnostics.ok = result.allowed; if (result.allowed) { result.diagnostics.recommendedAction = "proceed"; } else if (result.diagnostics.recommendedAction == "proceed") { result.diagnostics.recommendedAction = deriveAction(result.diagnostics.violations); } return result; } private: static bool targetAllowed(const TaskitemContract& contract, const std::string& nodeKind) { return std::find(contract.allowedTargets.begin(), contract.allowedTargets.end(), nodeKind) != contract.allowedTargets.end(); } static void dedupe(std::vector& violations) { std::vector out; for (const auto& v : violations) { bool exists = false; for (const auto& current : out) { if (current.code == v.code && current.field == v.field) { exists = true; break; } } if (!exists) out.push_back(v); } violations.swap(out); } static std::string deriveAction(const std::vector& violations) { for (const auto& v : violations) { if (!v.retryable) return "escalate"; } return violations.empty() ? "proceed" : "retry"; } };