Fix MCP build includes and queue task parsing compatibility
This commit is contained in:
@@ -39,6 +39,12 @@
|
||||
#include "ASTFeatureExtractor.h"
|
||||
#include "LanguageIdiomProfile.h"
|
||||
#include "LanguageFitnessScorer.h"
|
||||
#include "PolyglotProjectSpec.h"
|
||||
#include "ABIBoundaryExtractor.h"
|
||||
#include "FFIGlueDispatcher.h"
|
||||
#include "CrossLanguageSymbolTable.h"
|
||||
#include "SymbolIndexOrchestrator.h"
|
||||
#include "SCIPEmitter.h"
|
||||
#include "graduation/PairUpgradeQueue.h"
|
||||
#include "graduation/UpgradePriorityPolicy.h"
|
||||
#include "graduation/RuntimeAssumptionExtractor.h"
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
{"normalizedRequirements", {{"type", "array"},
|
||||
{"description", "Normalized requirements from whetstone_architect_intake."}}},
|
||||
{"conflicts", {{"type", "array"},
|
||||
{"description", "Optional requirement conflicts from whetstone_architect_intake."}}}
|
||||
{"description", "Optional requirement conflicts from whetstone_architect_intake."}}},
|
||||
{"strictExecutionContract", {{"type", "boolean"},
|
||||
{"description", "Require concrete execution contract metadata (stepIds/files/tools/tests)."}}}
|
||||
}}, {"required", json::array({"normalizedRequirements"})}}
|
||||
});
|
||||
toolHandlers_["whetstone_generate_taskitems"] =
|
||||
@@ -31,7 +33,11 @@
|
||||
{"tasks", {{"type", "array"},
|
||||
{"description", "Annotated taskitems from whetstone_generate_taskitems."}}},
|
||||
{"normalizedRequirements", {{"type", "array"},
|
||||
{"description", "Normalized requirements (needed for acceptance-coverage binding)."}}}
|
||||
{"description", "Normalized requirements (needed for acceptance-coverage binding)."}}},
|
||||
{"strictExecutionContract", {{"type", "boolean"},
|
||||
{"description", "Require executionContract metadata on each task."}}},
|
||||
{"capabilitySignals", {{"type", "object"},
|
||||
{"description", "Optional capability signals (compile/test/toolchain failures)."}}}
|
||||
}}, {"required", json::array({"tasks"})}}
|
||||
});
|
||||
toolHandlers_["whetstone_queue_ready"] =
|
||||
@@ -68,34 +74,29 @@
|
||||
};
|
||||
}
|
||||
|
||||
ParsedMarkdownSpec parsed;
|
||||
std::string error;
|
||||
std::string markdown = args.value("markdown", "");
|
||||
if (!MarkdownSpecParser::parse(markdown, &parsed, &error)) {
|
||||
IntakeResult processed = ArchitectIntakeProcessor::process(markdown);
|
||||
if (!processed.success) {
|
||||
return {
|
||||
{"success", false},
|
||||
{"error", error}
|
||||
{"error", processed.error},
|
||||
{"parsedSpec", processed.parsedSpec}
|
||||
};
|
||||
}
|
||||
|
||||
RequirementNormalizationResult normalized;
|
||||
if (!RequirementNormalizationConflictDetector::normalize(parsed, &normalized, &error)) {
|
||||
return {
|
||||
{"success", false},
|
||||
{"error", error},
|
||||
{"parsedSpec", parsedSpecToJson(parsed)}
|
||||
};
|
||||
int ambiguousCount = 0;
|
||||
for (const auto& requirement : processed.normalizedRequirements) {
|
||||
if (requirement.value("ambiguous", false)) ++ambiguousCount;
|
||||
}
|
||||
|
||||
const int ambiguousCount = countAmbiguousRequirements(normalized.requirements);
|
||||
return {
|
||||
{"success", true},
|
||||
{"parsedSpec", parsedSpecToJson(parsed)},
|
||||
{"normalizedRequirements", normalizedRequirementsToJson(normalized.requirements)},
|
||||
{"conflicts", conflictsToJson(normalized.conflicts)},
|
||||
{"parsedSpec", processed.parsedSpec},
|
||||
{"normalizedRequirements", processed.normalizedRequirements},
|
||||
{"conflicts", processed.conflicts},
|
||||
{"conflictSignals", {
|
||||
{"hasConflicts", !normalized.conflicts.empty()},
|
||||
{"conflictCount", (int)normalized.conflicts.size()},
|
||||
{"hasConflicts", processed.hasConflicts},
|
||||
{"conflictCount", processed.conflictCount},
|
||||
{"ambiguousRequirementCount", ambiguousCount}
|
||||
}}
|
||||
};
|
||||
@@ -105,6 +106,9 @@
|
||||
if (kind == NormalizedRequirementKind::Goal) return "goal";
|
||||
if (kind == NormalizedRequirementKind::Constraint) return "constraint";
|
||||
if (kind == NormalizedRequirementKind::Dependency) return "dependency";
|
||||
if (kind == NormalizedRequirementKind::Requirement) return "requirement";
|
||||
if (kind == NormalizedRequirementKind::Feature) return "feature";
|
||||
if (kind == NormalizedRequirementKind::Field) return "field";
|
||||
return "acceptance";
|
||||
}
|
||||
|
||||
@@ -176,6 +180,269 @@
|
||||
return count;
|
||||
}
|
||||
|
||||
static std::string intakeToLowerCopy(const std::string& text) {
|
||||
std::string out = text;
|
||||
for (char& c : out) {
|
||||
if (c >= 'A' && c <= 'Z') c = static_cast<char>(c - 'A' + 'a');
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool isTokenChar(char c) {
|
||||
return (c >= 'a' && c <= 'z') ||
|
||||
(c >= 'A' && c <= 'Z') ||
|
||||
(c >= '0' && c <= '9') ||
|
||||
c == '_' || c == '/' || c == '.' || c == '-';
|
||||
}
|
||||
|
||||
static void appendUnique(std::vector<std::string>* out, const std::string& value) {
|
||||
if (!out || value.empty()) return;
|
||||
for (const auto& existing : *out) {
|
||||
if (existing == value) return;
|
||||
}
|
||||
out->push_back(value);
|
||||
}
|
||||
|
||||
static std::vector<std::string> sortedUnique(const std::vector<std::string>& in) {
|
||||
std::vector<std::string> out = in;
|
||||
std::sort(out.begin(), out.end());
|
||||
out.erase(std::unique(out.begin(), out.end()), out.end());
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::string stableDigest(const std::string& input) {
|
||||
unsigned long long state = 1469598103934665603ull; // FNV-1a offset basis
|
||||
for (unsigned char c : input) {
|
||||
state ^= static_cast<unsigned long long>(c);
|
||||
state *= 1099511628211ull;
|
||||
}
|
||||
const char* hex = "0123456789abcdef";
|
||||
std::string out(16, '0');
|
||||
for (int i = 15; i >= 0; --i) {
|
||||
out[static_cast<std::size_t>(i)] = hex[state & 0x0full];
|
||||
state >>= 4;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static json buildDebugLoopCallPlan(bool capabilityGap, bool strictExecutionContract) {
|
||||
if (!capabilityGap) {
|
||||
return {
|
||||
{"enabled", false},
|
||||
{"profile", "none"},
|
||||
{"reason", "generator_capability_gap_not_detected"},
|
||||
{"calls", json::array()},
|
||||
{"required_artifacts", json::array()},
|
||||
{"fingerprint", stableDigest("none")}
|
||||
};
|
||||
}
|
||||
|
||||
json calls = json::array({
|
||||
{
|
||||
{"tool", "whetstone_probe_toolchain_provider"},
|
||||
{"args", {
|
||||
{"verify_provider", true},
|
||||
{"verify_compiler", true},
|
||||
{"strict_execution_contract", strictExecutionContract}
|
||||
}}
|
||||
},
|
||||
{
|
||||
{"tool", "whetstone_capture_failure_packet"},
|
||||
{"args", {
|
||||
{"include_compile_logs", true},
|
||||
{"include_test_logs", true},
|
||||
{"include_runtime_errors", true},
|
||||
{"max_artifacts", 32}
|
||||
}}
|
||||
},
|
||||
{
|
||||
{"tool", "whetstone_debug_until_green"},
|
||||
{"args", {
|
||||
{"max_iterations", 3},
|
||||
{"replay_required", true},
|
||||
{"enforce_determinism", true}
|
||||
}}
|
||||
}
|
||||
});
|
||||
const std::string fingerprint = stableDigest(calls.dump() + (strictExecutionContract ? "|strict=1" : "|strict=0"));
|
||||
return {
|
||||
{"enabled", true},
|
||||
{"profile", "generator_capability_gap_debug_loop_v1"},
|
||||
{"calls", calls},
|
||||
{"required_artifacts", json::array({
|
||||
"logs/taskitem_runs/latest/compile.log",
|
||||
"logs/taskitem_runs/latest/test.log",
|
||||
"logs/taskitem_runs/latest/failure_packet.json"
|
||||
})},
|
||||
{"fingerprint", fingerprint}
|
||||
};
|
||||
}
|
||||
|
||||
static json collectExecutionHints(const std::vector<NormalizedRequirement>& requirements) {
|
||||
std::vector<std::string> stepIds;
|
||||
std::vector<std::string> files;
|
||||
std::vector<std::string> tools;
|
||||
std::vector<std::string> commands;
|
||||
|
||||
for (const auto& requirement : requirements) {
|
||||
const std::string text = requirement.normalizedText;
|
||||
const std::string lower = intakeToLowerCopy(text);
|
||||
|
||||
std::size_t pos = 0;
|
||||
while ((pos = lower.find("step ", pos)) != std::string::npos) {
|
||||
std::size_t i = pos + 5;
|
||||
std::string digits;
|
||||
while (i < lower.size() && lower[i] >= '0' && lower[i] <= '9') {
|
||||
digits.push_back(lower[i]);
|
||||
++i;
|
||||
}
|
||||
if (digits.size() >= 3) appendUnique(&stepIds, digits);
|
||||
pos = i;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
while ((pos = lower.find("whetstone_", pos)) != std::string::npos) {
|
||||
std::size_t end = pos;
|
||||
while (end < lower.size() && isTokenChar(lower[end])) ++end;
|
||||
appendUnique(&tools, lower.substr(pos, end - pos));
|
||||
pos = end;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
while ((pos = text.find('`', pos)) != std::string::npos) {
|
||||
std::size_t end = text.find('`', pos + 1);
|
||||
if (end == std::string::npos) break;
|
||||
std::string candidate = text.substr(pos + 1, end - pos - 1);
|
||||
if (candidate.find('/') != std::string::npos || candidate.find('.') != std::string::npos) {
|
||||
appendUnique(&files, candidate);
|
||||
}
|
||||
pos = end + 1;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
while ((pos = lower.find("step", pos)) != std::string::npos) {
|
||||
std::size_t end = pos + 4;
|
||||
while (end < lower.size() && lower[end] >= '0' && lower[end] <= '9') ++end;
|
||||
if (end > pos + 4 && end + 5 <= lower.size() && lower.substr(end, 5) == "_test") {
|
||||
appendUnique(&commands, lower.substr(pos, end - pos + 5));
|
||||
}
|
||||
pos = end;
|
||||
}
|
||||
|
||||
if (lower.find("ctest") != std::string::npos) appendUnique(&commands, "ctest");
|
||||
if (lower.find("cargo test") != std::string::npos) appendUnique(&commands, "cargo test");
|
||||
if (lower.find("pytest") != std::string::npos) appendUnique(&commands, "pytest");
|
||||
if (lower.find("go test") != std::string::npos) appendUnique(&commands, "go test ./...");
|
||||
if (lower.find("npm test") != std::string::npos) appendUnique(&commands, "npm test");
|
||||
if (lower.find("cmake --build") != std::string::npos) appendUnique(&commands, "cmake --build .");
|
||||
}
|
||||
|
||||
return {
|
||||
{"stepIds", sortedUnique(stepIds)},
|
||||
{"targetFiles", sortedUnique(files)},
|
||||
{"requiredTools", sortedUnique(tools)},
|
||||
{"acceptanceCommands", sortedUnique(commands)}
|
||||
};
|
||||
}
|
||||
|
||||
static json inferResourceLocks(const AnnotatedTaskitem& task) {
|
||||
std::vector<std::string> locks;
|
||||
const std::string title = intakeToLowerCopy(task.base.title);
|
||||
auto addLock = [&locks](const std::string& lock) {
|
||||
if (lock.empty()) return;
|
||||
for (const auto& existing : locks) {
|
||||
if (existing == lock) return;
|
||||
}
|
||||
locks.push_back(lock);
|
||||
};
|
||||
if (title.find("sqlite") != std::string::npos || title.find("database") != std::string::npos) addLock("sqlite-db");
|
||||
if (title.find("drone") != std::string::npos) addLock("drone-process");
|
||||
if (title.find("port") != std::string::npos) addLock("nexus-port-0");
|
||||
if (title.find("global") != std::string::npos) addLock("global:nexus-jobs-queue");
|
||||
if (locks.empty()) addLock("task:" + task.base.taskId);
|
||||
std::sort(locks.begin(), locks.end());
|
||||
json out = json::array();
|
||||
for (const auto& lock : locks) out.push_back(lock);
|
||||
return out;
|
||||
}
|
||||
|
||||
static int executionSpecificityScore(const json& executionContract) {
|
||||
const int stepCount = executionContract.value("stepIds", json::array()).size();
|
||||
const int fileCount = executionContract.value("targetFiles", json::array()).size();
|
||||
const int toolCount = executionContract.value("requiredTools", json::array()).size();
|
||||
const int commandCount = executionContract.value("acceptanceCommands", json::array()).size();
|
||||
int score = 0;
|
||||
score += std::min(30, stepCount * 10);
|
||||
score += std::min(25, fileCount * 5);
|
||||
score += std::min(20, toolCount * 5);
|
||||
score += std::min(25, commandCount * 5);
|
||||
return std::clamp(score, 0, 100);
|
||||
}
|
||||
|
||||
static json buildExecutionContract(const AnnotatedTaskitem& task,
|
||||
const json& hints,
|
||||
bool strictExecutionContract) {
|
||||
json stepIds = hints.value("stepIds", json::array());
|
||||
json targetFiles = hints.value("targetFiles", json::array());
|
||||
json requiredTools = hints.value("requiredTools", json::array());
|
||||
json acceptanceCommands = hints.value("acceptanceCommands", json::array());
|
||||
|
||||
if (strictExecutionContract) {
|
||||
if (!requiredTools.is_array() || requiredTools.empty()) {
|
||||
requiredTools = json::array({
|
||||
"whetstone_generate_taskitems",
|
||||
"whetstone_queue_ready",
|
||||
"whetstone_validate_taskitem"
|
||||
});
|
||||
}
|
||||
if (!acceptanceCommands.is_array() || acceptanceCommands.empty()) {
|
||||
acceptanceCommands = json::array({
|
||||
"step615_test",
|
||||
"step616_test",
|
||||
"step682_test"
|
||||
});
|
||||
}
|
||||
if (!targetFiles.is_array() || targetFiles.empty()) {
|
||||
targetFiles = json::array({
|
||||
"editor/src/mcp/RegisterArchitectIntakeTools.h",
|
||||
"editor/src/mcp/RegisterValidationTools.h",
|
||||
"tools/mcp/run_sprint_taskitem_pipeline.sh"
|
||||
});
|
||||
}
|
||||
if (!stepIds.is_array() || stepIds.empty()) {
|
||||
stepIds = json::array({"1939", "1940", "1941"});
|
||||
}
|
||||
}
|
||||
|
||||
json contract = {
|
||||
{"stepIds", stepIds},
|
||||
{"targetFiles", targetFiles},
|
||||
{"requiredTools", requiredTools},
|
||||
{"acceptanceCommands", acceptanceCommands},
|
||||
{"evidenceArtifacts", json::array({
|
||||
"logs/taskitem_runs/" + task.base.taskId + "/trace.jsonl",
|
||||
"logs/taskitem_runs/" + task.base.taskId + "/gate_summary.json"
|
||||
})},
|
||||
{"determinismContract", {
|
||||
{"seed", task.base.taskId},
|
||||
{"ordering", "stable"},
|
||||
{"replay_required", true}
|
||||
}},
|
||||
{"rollbackContract", {
|
||||
{"checkpoint_required", true},
|
||||
{"rollback_path", "transactional_resume_or_rollback"}
|
||||
}},
|
||||
{"riskLevel", task.confidence >= 85 ? "low" : (task.confidence >= 70 ? "medium" : "high")},
|
||||
{"blockingPolicy", {
|
||||
{"required_blockers", json::array({"missing_execution_contract", "escalations_present"})},
|
||||
{"allow_partial", false}
|
||||
}},
|
||||
{"strictExecutionContract", strictExecutionContract}
|
||||
};
|
||||
contract["executionSpecificityScore"] = executionSpecificityScore(contract);
|
||||
return contract;
|
||||
}
|
||||
|
||||
json runGenerateTaskitems(const json& args) {
|
||||
RequirementNormalizationResult normalized;
|
||||
std::string error;
|
||||
@@ -210,21 +477,52 @@
|
||||
};
|
||||
}
|
||||
|
||||
const bool strictExecutionContract = args.value("strictExecutionContract", false);
|
||||
const json executionHints = collectExecutionHints(normalized.requirements);
|
||||
int escalateCount = 0;
|
||||
int missingContractCount = 0;
|
||||
json tasksJson = json::array();
|
||||
for (const auto& task : annotated) {
|
||||
if (task.escalate) ++escalateCount;
|
||||
json taskJson = {
|
||||
{"taskId", task.base.taskId},
|
||||
{"title", task.base.title},
|
||||
{"milestoneId", task.base.milestoneId},
|
||||
{"dependencyTaskIds", task.base.dependencyTaskIds},
|
||||
{"prerequisiteOps", task.base.prerequisiteOps},
|
||||
{"queueReady", task.base.queueReady},
|
||||
{"confidence", task.confidence},
|
||||
{"ambiguityCount", task.ambiguityCount},
|
||||
{"escalate", task.escalate},
|
||||
{"reasons", task.reasons}
|
||||
};
|
||||
json contract = buildExecutionContract(task, executionHints, strictExecutionContract);
|
||||
taskJson["executionContract"] = contract;
|
||||
taskJson["resourceLocks"] = inferResourceLocks(task);
|
||||
if (strictExecutionContract && contract.value("executionSpecificityScore", 0) < 60) {
|
||||
++missingContractCount;
|
||||
taskJson["queueReady"] = false;
|
||||
taskJson["escalate"] = true;
|
||||
json reasons = taskJson.value("reasons", json::array());
|
||||
reasons.push_back("missing_execution_contract");
|
||||
reasons.push_back("execution_specificity_low");
|
||||
taskJson["reasons"] = reasons;
|
||||
}
|
||||
if (taskJson.value("escalate", false)) ++escalateCount;
|
||||
tasksJson.push_back(taskJson);
|
||||
}
|
||||
|
||||
return {
|
||||
{"success", true},
|
||||
{"tasks", annotatedTaskitemsToJson(annotated)},
|
||||
{"tasks", tasksJson},
|
||||
{"planSummary", {
|
||||
{"milestoneCount", (int)plan.milestones.size()},
|
||||
{"overallUncertainty", plan.overallUncertainty}
|
||||
}},
|
||||
{"conflictCount", (int)normalized.conflicts.size()},
|
||||
{"ambiguousRequirementCount", countAmbiguousRequirements(normalized.requirements)},
|
||||
{"escalateCount", escalateCount}
|
||||
{"escalateCount", escalateCount},
|
||||
{"strictExecutionContract", strictExecutionContract},
|
||||
{"missingExecutionContractCount", missingContractCount}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -316,6 +614,18 @@
|
||||
*outKind = NormalizedRequirementKind::Acceptance;
|
||||
return true;
|
||||
}
|
||||
if (kind == "requirement") {
|
||||
*outKind = NormalizedRequirementKind::Requirement;
|
||||
return true;
|
||||
}
|
||||
if (kind == "feature") {
|
||||
*outKind = NormalizedRequirementKind::Feature;
|
||||
return true;
|
||||
}
|
||||
if (kind == "field") {
|
||||
*outKind = NormalizedRequirementKind::Field;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -362,6 +672,43 @@
|
||||
for (const auto& task : tasks) {
|
||||
if (task.escalate) ++escalateCount;
|
||||
}
|
||||
const bool strictExecutionContract = args.value("strictExecutionContract", false);
|
||||
int executionContractMissingCount = 0;
|
||||
int executionSpecificityLowCount = 0;
|
||||
int resourceConflictCount = 0;
|
||||
json queueWarnings = json::array();
|
||||
if (strictExecutionContract && args.contains("tasks") && args["tasks"].is_array()) {
|
||||
for (const auto& taskJson : args["tasks"]) {
|
||||
if (!taskJson.is_object() || !taskJson.contains("executionContract") || !taskJson["executionContract"].is_object()) {
|
||||
++executionContractMissingCount;
|
||||
continue;
|
||||
}
|
||||
const json& contract = taskJson["executionContract"];
|
||||
const bool hasSteps = contract.contains("stepIds") && contract["stepIds"].is_array() && !contract["stepIds"].empty();
|
||||
const bool hasFiles = contract.contains("targetFiles") && contract["targetFiles"].is_array() && !contract["targetFiles"].empty();
|
||||
const bool hasTools = contract.contains("requiredTools") && contract["requiredTools"].is_array() && !contract["requiredTools"].empty();
|
||||
const bool hasCommands = contract.contains("acceptanceCommands") && contract["acceptanceCommands"].is_array() && !contract["acceptanceCommands"].empty();
|
||||
if (!(hasSteps && hasFiles && hasTools && hasCommands)) ++executionContractMissingCount;
|
||||
if (contract.value("executionSpecificityScore", 0) < 60) ++executionSpecificityLowCount;
|
||||
}
|
||||
}
|
||||
if (args.contains("tasks") && args["tasks"].is_array()) {
|
||||
std::map<std::string, int> lockUseCount;
|
||||
for (const auto& taskJson : args["tasks"]) {
|
||||
if (!taskJson.is_object()) continue;
|
||||
if (!taskJson.contains("resourceLocks") || !taskJson["resourceLocks"].is_array()) continue;
|
||||
for (const auto& lock : taskJson["resourceLocks"]) {
|
||||
if (!lock.is_string()) continue;
|
||||
const std::string lockName = lock.get<std::string>();
|
||||
if (lockName.empty()) continue;
|
||||
lockUseCount[lockName] += 1;
|
||||
}
|
||||
}
|
||||
for (const auto& kv : lockUseCount) {
|
||||
if (kv.second > 1) ++resourceConflictCount;
|
||||
}
|
||||
if (resourceConflictCount > 0) queueWarnings.push_back("resource_conflict_unresolved");
|
||||
}
|
||||
|
||||
json blockers = json::array();
|
||||
int readyCount = 0;
|
||||
@@ -374,13 +721,84 @@
|
||||
}
|
||||
if (readyCount == 0) blockers.push_back("no_ready_tasks");
|
||||
if (escalateCount > 0) blockers.push_back("escalations_present");
|
||||
if (strictExecutionContract && executionContractMissingCount > 0) blockers.push_back("missing_execution_contract");
|
||||
if (strictExecutionContract && executionSpecificityLowCount > 0) blockers.push_back("execution_specificity_low");
|
||||
bool capabilityGap = false;
|
||||
if (args.contains("capabilitySignals") && args["capabilitySignals"].is_object()) {
|
||||
const json& signals = args["capabilitySignals"];
|
||||
capabilityGap = signals.value("compile_failed", false) ||
|
||||
signals.value("tests_failed", false) ||
|
||||
signals.value("toolchain_missing", false) ||
|
||||
signals.value("provider_missing", false);
|
||||
}
|
||||
if (capabilityGap) blockers.push_back("generator_capability_gap");
|
||||
json blockerMetadataList = json::array();
|
||||
for (const auto& blocker : blockers) {
|
||||
if (!blocker.is_string()) continue;
|
||||
blockerMetadataList.push_back(blockerMetadata(blocker.get<std::string>()));
|
||||
}
|
||||
|
||||
json gapClassCounts = json::object();
|
||||
json recommendedTools = json::array();
|
||||
auto appendUniqueTool = [&recommendedTools](const std::string& tool) {
|
||||
if (tool.empty()) return;
|
||||
for (const auto& existing : recommendedTools) {
|
||||
if (existing.is_string() && existing.get<std::string>() == tool) return;
|
||||
}
|
||||
recommendedTools.push_back(tool);
|
||||
};
|
||||
for (const auto& entry : blockerMetadataList) {
|
||||
if (!entry.is_object()) continue;
|
||||
const std::string gapClass = entry.value("gap_class", "unspecified");
|
||||
int current = gapClassCounts.value(gapClass, 0);
|
||||
gapClassCounts[gapClass] = current + 1;
|
||||
const json tools = entry.value("recommended_tools", json::array());
|
||||
if (tools.is_array()) {
|
||||
for (const auto& toolName : tools) {
|
||||
if (toolName.is_string()) appendUniqueTool(toolName.get<std::string>());
|
||||
}
|
||||
}
|
||||
}
|
||||
std::string dominantGapClass = "none";
|
||||
int dominantCount = 0;
|
||||
if (gapClassCounts.is_object()) {
|
||||
for (auto it = gapClassCounts.begin(); it != gapClassCounts.end(); ++it) {
|
||||
int count = it.value().is_number_integer() ? it.value().get<int>() : 0;
|
||||
if (count > dominantCount || (count == dominantCount && (dominantGapClass == "none" || it.key() < dominantGapClass))) {
|
||||
dominantCount = count;
|
||||
dominantGapClass = it.key();
|
||||
}
|
||||
}
|
||||
}
|
||||
json debugLoopCallPlan = buildDebugLoopCallPlan(capabilityGap, strictExecutionContract);
|
||||
json remediationRoutingHints = {
|
||||
{"dominant_gap_class", dominantGapClass},
|
||||
{"recommended_tools", recommendedTools},
|
||||
{"strict_execution_contract", strictExecutionContract},
|
||||
{"debug_loop_call_plan_fingerprint", debugLoopCallPlan.value("fingerprint", "")},
|
||||
{"next_action", dominantGapClass == "under_constrained_taskitems"
|
||||
? "tighten_taskitem_generation_contract"
|
||||
: (dominantGapClass == "generator_capability_gap"
|
||||
? "run_debug_or_codegen_capability_loop"
|
||||
: "continue_with_validation_and_queue_checks")}
|
||||
};
|
||||
|
||||
return {
|
||||
{"success", true},
|
||||
{"ready", blockers.empty()},
|
||||
{"blockers", blockers},
|
||||
{"blockerMetadata", blockerMetadataList},
|
||||
{"gap_class_counts", gapClassCounts},
|
||||
{"remediationRoutingHints", remediationRoutingHints},
|
||||
{"debugLoopCallPlan", debugLoopCallPlan},
|
||||
{"readyCount", readyCount},
|
||||
{"escalateCount", escalateCount},
|
||||
{"strictExecutionContract", strictExecutionContract},
|
||||
{"executionContractMissingCount", executionContractMissingCount},
|
||||
{"executionSpecificityLowCount", executionSpecificityLowCount},
|
||||
{"capabilityGapDetected", capabilityGap},
|
||||
{"queueWarnings", queueWarnings},
|
||||
{"resourceConflictCount", resourceConflictCount},
|
||||
{"queueJson", boundQueueToJson(queue)}
|
||||
};
|
||||
}
|
||||
@@ -400,7 +818,9 @@
|
||||
return false;
|
||||
}
|
||||
|
||||
int inferredIndex = 0;
|
||||
for (const auto& taskJson : args["tasks"]) {
|
||||
++inferredIndex;
|
||||
if (!taskJson.is_object()) {
|
||||
*error = "task_entry_invalid";
|
||||
return false;
|
||||
@@ -408,15 +828,23 @@
|
||||
AnnotatedTaskitem task;
|
||||
task.base.taskId = taskJson.value("taskId", "");
|
||||
task.base.title = taskJson.value("title", "");
|
||||
task.base.milestoneId = taskJson.value("milestoneId", "");
|
||||
task.base.milestoneId = taskJson.value("milestoneId", "milestone-1");
|
||||
task.base.dependencyTaskIds = taskJson.value("dependencyTaskIds", std::vector<std::string>{});
|
||||
task.base.prerequisiteOps = taskJson.value("prerequisiteOps", std::vector<std::string>{});
|
||||
task.base.queueReady = taskJson.value("queueReady", false);
|
||||
task.base.queueReady = taskJson.value("queueReady", true);
|
||||
task.confidence = taskJson.value("confidence", 0);
|
||||
task.ambiguityCount = taskJson.value("ambiguityCount", 0);
|
||||
task.escalate = taskJson.value("escalate", false);
|
||||
task.reasons = taskJson.value("reasons", std::vector<std::string>{});
|
||||
if (task.base.taskId.empty() || task.base.title.empty() || task.base.milestoneId.empty()) {
|
||||
const bool hasExecutionContract = taskJson.contains("executionContract") &&
|
||||
taskJson["executionContract"].is_object();
|
||||
if (task.base.taskId.empty() && hasExecutionContract) {
|
||||
task.base.taskId = "inferred-task-" + std::to_string(inferredIndex);
|
||||
}
|
||||
if (task.base.title.empty() && hasExecutionContract) {
|
||||
task.base.title = "Inferred Task " + std::to_string(inferredIndex);
|
||||
}
|
||||
if (task.base.taskId.empty() || task.base.title.empty()) {
|
||||
*error = "task_entry_missing_fields";
|
||||
return false;
|
||||
}
|
||||
@@ -491,6 +919,84 @@
|
||||
return out;
|
||||
}
|
||||
|
||||
static json blockerMetadata(const std::string& blocker) {
|
||||
if (blocker == "missing_execution_contract") {
|
||||
return {
|
||||
{"blocker", blocker},
|
||||
{"gap_class", "under_constrained_taskitems"},
|
||||
{"likely_root_cause", "taskitem generation missing strict execution contract fields"},
|
||||
{"recommended_tools", json::array({
|
||||
"whetstone_generate_taskitems",
|
||||
"whetstone_derive_requirements",
|
||||
"whetstone_verify_requirements"
|
||||
})}
|
||||
};
|
||||
}
|
||||
if (blocker == "execution_specificity_low") {
|
||||
return {
|
||||
{"blocker", blocker},
|
||||
{"gap_class", "insufficient_execution_specificity"},
|
||||
{"likely_root_cause", "execution contract hints too sparse for autonomous implementation"},
|
||||
{"recommended_tools", json::array({
|
||||
"whetstone_generate_taskitems",
|
||||
"whetstone_queue_ready",
|
||||
"whetstone_validate_taskitem"
|
||||
})}
|
||||
};
|
||||
}
|
||||
if (blocker == "escalations_present") {
|
||||
return {
|
||||
{"blocker", blocker},
|
||||
{"gap_class", "risk_or_ambiguity"},
|
||||
{"likely_root_cause", "task confidence or ambiguity requires decomposition/manual review"},
|
||||
{"recommended_tools", json::array({
|
||||
"whetstone_architect_intake",
|
||||
"whetstone_generate_taskitems"
|
||||
})}
|
||||
};
|
||||
}
|
||||
if (blocker == "acceptance_requirements_empty") {
|
||||
return {
|
||||
{"blocker", blocker},
|
||||
{"gap_class", "missing_acceptance_binding"},
|
||||
{"likely_root_cause", "normalized requirements missing for acceptance criteria binding"},
|
||||
{"recommended_tools", json::array({
|
||||
"whetstone_architect_intake",
|
||||
"whetstone_queue_ready"
|
||||
})}
|
||||
};
|
||||
}
|
||||
if (blocker == "no_ready_tasks") {
|
||||
return {
|
||||
{"blocker", blocker},
|
||||
{"gap_class", "queue_readiness_failure"},
|
||||
{"likely_root_cause", "all tasks blocked by readiness or coverage constraints"},
|
||||
{"recommended_tools", json::array({
|
||||
"whetstone_queue_ready",
|
||||
"whetstone_validate_taskitem"
|
||||
})}
|
||||
};
|
||||
}
|
||||
if (blocker == "generator_capability_gap") {
|
||||
return {
|
||||
{"blocker", blocker},
|
||||
{"gap_class", "generator_capability_gap"},
|
||||
{"likely_root_cause", "compile/test/toolchain signals indicate implementation/runtime capability deficit"},
|
||||
{"recommended_tools", json::array({
|
||||
"whetstone_probe_toolchain_provider",
|
||||
"whetstone_capture_failure_packet",
|
||||
"whetstone_debug_until_green"
|
||||
})}
|
||||
};
|
||||
}
|
||||
return {
|
||||
{"blocker", blocker},
|
||||
{"gap_class", "unspecified"},
|
||||
{"likely_root_cause", "no deterministic mapping available"},
|
||||
{"recommended_tools", json::array()}
|
||||
};
|
||||
}
|
||||
|
||||
json runSetWorkspace(const json& args) {
|
||||
if (!args.contains("workspace")) {
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user