Files
whetstone_DSL/editor/src/WorkerDispatchValidator.h

146 lines
5.2 KiB
C++

#pragma once
// Step 485: Worker Dispatch + Result Validation
// Models architect->worker handoff, result validation, retry budget, and escalation.
#include "PriorStepContextInjector.h"
#include "ProjectConventionAnalyzer.h"
#include "StepSpecExpander.h"
#include <sstream>
#include <string>
#include <vector>
struct WorkerSubmission {
std::vector<SourceFileDraft> files;
bool buildPassed = false;
bool testsPassed = false;
std::string errorOutput;
};
struct WorkerDispatchInput {
std::string stepMarkdown;
std::string stepNumber;
std::string componentName;
std::string buildSystem = "cmake";
std::string repoRoot = ".";
std::vector<std::string> priorHeaders;
};
struct DispatchAttemptReport {
int attempt = 0;
bool accepted = false;
bool buildPassed = false;
bool testsPassed = false;
ConventionValidationReport conventionReport;
std::string status;
};
struct WorkerDispatchResult {
StepSpec spec;
StepContext context;
ProjectConventions conventions;
std::vector<DispatchAttemptReport> attempts;
bool completed = false;
bool escalated = false;
int retryBudget = 3;
int attemptsUsed = 0;
std::string finalStatus;
};
class WorkerDispatchValidator {
public:
static WorkerDispatchResult run(const WorkerDispatchInput& in,
const std::vector<WorkerSubmission>& submissions,
int retryBudget = 3) {
WorkerDispatchResult out;
out.retryBudget = retryBudget <= 0 ? 1 : retryBudget;
out.spec = StepSpecExpander::expand({
in.stepMarkdown,
in.stepNumber,
in.componentName,
in.buildSystem,
{}, // conventions are extracted from repo state
in.priorHeaders
});
out.context = PriorStepContextInjector::build(in.priorHeaders);
out.conventions = ProjectConventionAnalyzer::extract(in.repoRoot);
if (submissions.empty()) {
out.escalated = true;
out.finalStatus = "No worker submissions provided";
return out;
}
const int maxAttempts = out.retryBudget < (int)submissions.size()
? out.retryBudget : (int)submissions.size();
for (int i = 0; i < maxAttempts; ++i) {
DispatchAttemptReport report;
report.attempt = i + 1;
report.buildPassed = submissions[i].buildPassed;
report.testsPassed = submissions[i].testsPassed;
report.conventionReport =
ProjectConventionAnalyzer::validate(out.conventions, submissions[i].files);
const bool conventionsOk = report.conventionReport.violations.empty();
report.accepted = report.buildPassed && report.testsPassed && conventionsOk;
report.status = classifyAttemptStatus(report, conventionsOk, submissions[i].errorOutput);
out.attempts.push_back(report);
out.attemptsUsed = i + 1;
if (report.accepted) {
out.completed = true;
out.finalStatus = "Step completed";
return out;
}
}
out.escalated = true;
out.finalStatus = out.attemptsUsed >= out.retryBudget
? "Retry budget exhausted; escalate to architect"
: "All submissions consumed without completion";
return out;
}
static std::string buildWorkerBrief(const WorkerDispatchInput& in,
const StepSpec& spec,
const StepContext& context,
const ProjectConventions& conventions) {
std::ostringstream out;
out << "Step " << in.stepNumber << ": " << in.componentName << "\n";
out << "Complexity: " << complexityLabel(spec.complexity) << "\n";
out << "Tests planned: " << spec.testPlan.size() << "\n";
out << "Header target: " << spec.headerSkeleton.fileName << "\n";
out << "Build target: " << spec.buildEntry.targetName << "\n";
out << "Conventions: " << conventions.classNaming << ", "
<< conventions.methodNaming << ", " << conventions.fileNaming << "\n";
out << "Context brief: " << context.condensedBrief() << "\n";
return out.str();
}
private:
static std::string classifyAttemptStatus(const DispatchAttemptReport& report,
bool conventionsOk,
const std::string& errorOutput) {
if (!report.buildPassed) {
return "build_failed" + (errorOutput.empty() ? "" : ": " + errorOutput);
}
if (!report.testsPassed) {
return "tests_failed" + (errorOutput.empty() ? "" : ": " + errorOutput);
}
if (!conventionsOk) return "convention_warnings";
return "accepted";
}
static std::string complexityLabel(StepComplexity c) {
switch (c) {
case StepComplexity::Trivial: return "trivial";
case StepComplexity::Standard: return "standard";
case StepComplexity::Complex: return "complex";
case StepComplexity::Pipeline: return "pipeline";
}
return "standard";
}
};