#pragma once // Step 680: Score taskitem self-containment quality. #include "PrerequisiteOpResolver.h" #include #include #include struct ScoredTaskitem { std::string taskId; int score = 0; bool selfContained = false; std::vector issues; }; struct TaskitemInput { std::string taskId; std::string title; std::vector prerequisiteOps; std::vector reasons; int confidence = 0; std::vector dependencyTaskIds; }; class SelfContainmentScorer { public: static ScoredTaskitem score(const TaskitemInput& item, const std::string& workspaceRoot) { ScoredTaskitem out; out.taskId = item.taskId; int scoreValue = 100; if (item.prerequisiteOps.empty()) { scoreValue -= 30; out.issues.push_back("prerequisiteOps missing or empty"); } int unresolvedDeduction = 0; if (!item.prerequisiteOps.empty()) { auto resolved = PrerequisiteOpResolver::resolveAll(item.prerequisiteOps, workspaceRoot); for (const auto& op : resolved) { if (op.kind == OpKind::ReadFile && !op.fileExists) { unresolvedDeduction += 10; } } } unresolvedDeduction = std::min(unresolvedDeduction, 30); if (unresolvedDeduction > 0) { scoreValue -= unresolvedDeduction; out.issues.push_back("prerequisiteOps include unresolved file paths"); } if (item.reasons.empty()) { scoreValue -= 20; out.issues.push_back("reasons missing or empty"); } else if (item.reasons.size() < 3) { scoreValue -= 10; out.issues.push_back("reasons has fewer than 3 entries"); } if (item.confidence == 0) { scoreValue -= 10; out.issues.push_back("confidence not set"); } if (item.title.empty()) { scoreValue -= 20; out.issues.push_back("title empty"); } if (!item.dependencyTaskIds.empty()) { scoreValue -= 5; out.issues.push_back("dependencyTaskIds not empty"); } out.score = std::clamp(scoreValue, 0, 100); out.selfContained = out.score >= 80; return out; } };