Refine taskitem workflow guidance and sprint intake shaping
This commit is contained in:
@@ -230,6 +230,147 @@ void test_cross_project_repo_signal_grounds_target_files() {
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_project_requirement_generates_specific_task_title_and_files() {
|
||||
TEST(project_requirement_generates_specific_task_title_and_files);
|
||||
MCPServer mcp;
|
||||
json reqs = json::array({{
|
||||
{"requirementId", "goal-guidance-bridge"},
|
||||
{"kind", "goal"},
|
||||
{"normalizedText", "Implement GuidanceBridge formulas in WHIMP/software/cpp/src/guidance/GuidanceBridge.cpp and WHIMP/software/cpp/src/guidance/GuidanceBridge.h"},
|
||||
{"anchor", "goals"},
|
||||
{"sourceLine", 1},
|
||||
{"ambiguous", false}
|
||||
}});
|
||||
json out = callTool(mcp, "whetstone_generate_taskitems",
|
||||
{{"normalizedRequirements", reqs}, {"strictExecutionContract", true}});
|
||||
CHECK(out.value("success", false), "success should be true");
|
||||
CHECK(out["tasks"].is_array() && !out["tasks"].empty(), "tasks should be non-empty");
|
||||
const auto& task = out["tasks"][0];
|
||||
std::string title = task.value("title", "");
|
||||
std::string intent = task.value("intent", "");
|
||||
CHECK(title.find("GuidanceBridge") != std::string::npos, "title should mention GuidanceBridge");
|
||||
CHECK(title.find("Intake Foundation") == std::string::npos, "title should not fallback to generic milestone wording");
|
||||
CHECK(intent.find("GuidanceBridge") != std::string::npos, "intent should preserve requirement text");
|
||||
const auto& files = task["executionContract"]["targetFiles"];
|
||||
bool hasCpp = false;
|
||||
bool hasHeader = false;
|
||||
for (const auto& f : files) {
|
||||
if (!f.is_string()) continue;
|
||||
std::string v = f.get<std::string>();
|
||||
if (v == "WHIMP/software/cpp/src/guidance/GuidanceBridge.cpp") hasCpp = true;
|
||||
if (v == "WHIMP/software/cpp/src/guidance/GuidanceBridge.h") hasHeader = true;
|
||||
}
|
||||
CHECK(hasCpp, "expected cpp target file");
|
||||
CHECK(hasHeader, "expected header target file");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_single_project_product_name_does_not_trigger_cross_repo_fallback() {
|
||||
TEST(single_project_product_name_does_not_trigger_cross_repo_fallback);
|
||||
MCPServer mcp;
|
||||
json reqs = json::array({{
|
||||
{"requirementId", "goal-hivemind-shell-onboarding"},
|
||||
{"kind", "goal"},
|
||||
{"normalizedText", "Add onboarding plan for installing hivemind_shell in docs/onboarding/hivemind_shell_ubuntu.md"},
|
||||
{"anchor", "requirements"},
|
||||
{"sourceLine", 1},
|
||||
{"ambiguous", false}
|
||||
}});
|
||||
json out = callTool(mcp, "whetstone_generate_taskitems",
|
||||
{{"normalizedRequirements", reqs}, {"strictExecutionContract", true}});
|
||||
CHECK(out.value("success", false), "success should be true");
|
||||
CHECK(out["tasks"].is_array() && !out["tasks"].empty(), "tasks should be non-empty");
|
||||
const auto& files = out["tasks"][0]["executionContract"]["targetFiles"];
|
||||
bool hasLocalDoc = false;
|
||||
for (const auto& f : files) {
|
||||
if (!f.is_string()) continue;
|
||||
std::string v = f.get<std::string>();
|
||||
if (v == "docs/onboarding/hivemind_shell_ubuntu.md") hasLocalDoc = true;
|
||||
CHECK(v != "hivemind/README.md", "single-project product name should not trigger cross-repo fallback");
|
||||
}
|
||||
CHECK(hasLocalDoc, "expected local onboarding doc target");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_strict_contract_infers_test_files_and_verification_type_for_code_work() {
|
||||
TEST(strict_contract_infers_test_files_and_verification_type_for_code_work);
|
||||
MCPServer mcp;
|
||||
json reqs = json::array({{
|
||||
{"requirementId", "goal-guidance-bridge-tests"},
|
||||
{"kind", "goal"},
|
||||
{"normalizedText", "Implement GuidanceBridge formulas in WHIMP/software/cpp/src/guidance/GuidanceBridge.cpp and WHIMP/software/cpp/src/guidance/GuidanceBridge.h"},
|
||||
{"anchor", "goals"},
|
||||
{"sourceLine", 1},
|
||||
{"ambiguous", false}
|
||||
}});
|
||||
json out = callTool(mcp, "whetstone_generate_taskitems",
|
||||
{{"normalizedRequirements", reqs}, {"strictExecutionContract", true}});
|
||||
CHECK(out.value("success", false), "success should be true");
|
||||
CHECK(out["tasks"].is_array() && !out["tasks"].empty(), "tasks should be non-empty");
|
||||
const auto& contract = out["tasks"][0]["executionContract"];
|
||||
CHECK(contract.value("verificationType", "") == "unit", "expected unit verification type");
|
||||
const auto& tests = contract["testFiles"];
|
||||
CHECK(tests.is_array() && !tests.empty(), "expected inferred test files");
|
||||
bool hasGuidanceBridgeTest = false;
|
||||
for (const auto& testFile : tests) {
|
||||
if (!testFile.is_string()) continue;
|
||||
if (testFile.get<std::string>() == "WHIMP/software/cpp/tests/guidance/GuidanceBridge_test.cpp") {
|
||||
hasGuidanceBridgeTest = true;
|
||||
}
|
||||
}
|
||||
CHECK(hasGuidanceBridgeTest, "expected inferred GuidanceBridge C++ test path");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_rust_repo_contract_drops_wrong_language_commands_and_uses_real_test_file() {
|
||||
TEST(rust_repo_contract_drops_wrong_language_commands_and_uses_real_test_file);
|
||||
MCPServer mcp;
|
||||
json reqs = json::array({
|
||||
{
|
||||
{"requirementId", "goal-rust-1"},
|
||||
{"kind", "goal"},
|
||||
{"normalizedText", "Implement control-plane recomposition in crates/whimpwm/src/render.rs and crates/whimpwm/src/presentation.rs"},
|
||||
{"sourceText", "Implement control-plane recomposition in `crates/whimpwm/src/render.rs` and `crates/whimpwm/src/presentation.rs`"},
|
||||
{"anchor", "goals"},
|
||||
{"sourceLine", 1},
|
||||
{"ambiguous", false}
|
||||
},
|
||||
{
|
||||
{"requirementId", "constraint-rust-1"},
|
||||
{"kind", "constraint"},
|
||||
{"normalizedText", "Do not generate or verify go tasks for this sprint"},
|
||||
{"sourceText", "Do not generate or verify Go tasks for this sprint."},
|
||||
{"anchor", "constraints"},
|
||||
{"sourceLine", 2},
|
||||
{"ambiguous", false}
|
||||
}
|
||||
});
|
||||
json out = callTool(mcp, "whetstone_generate_taskitems",
|
||||
{{"normalizedRequirements", reqs},
|
||||
{"strictExecutionContract", true},
|
||||
{"workspace", "/home/bill/Documents/whimpwm"}});
|
||||
CHECK(out.value("success", false), "success should be true");
|
||||
CHECK(out["tasks"].is_array() && !out["tasks"].empty(), "tasks should be non-empty");
|
||||
const auto& contract = out["tasks"][0]["executionContract"];
|
||||
bool sawGo = false;
|
||||
bool sawCargo = false;
|
||||
for (const auto& commandJson : contract["acceptanceCommands"]) {
|
||||
if (!commandJson.is_string()) continue;
|
||||
const std::string command = commandJson.get<std::string>();
|
||||
if (command.find("go test") != std::string::npos) sawGo = true;
|
||||
if (command.find("cargo ") != std::string::npos) sawCargo = true;
|
||||
}
|
||||
CHECK(!sawGo, "go test should be removed for Rust repo");
|
||||
CHECK(sawCargo, "cargo command should be present for Rust repo");
|
||||
bool sawMainTest = false;
|
||||
for (const auto& testFileJson : contract["testFiles"]) {
|
||||
if (!testFileJson.is_string()) continue;
|
||||
if (testFileJson.get<std::string>() == "crates/whimpwm/tests/main_test.rs") sawMainTest = true;
|
||||
}
|
||||
CHECK(sawMainTest, "expected real whimpwm Rust test file");
|
||||
PASS();
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Step 615: whetstone_generate_taskitems MCP Tool\n";
|
||||
|
||||
@@ -246,6 +387,10 @@ int main() {
|
||||
test_rejects_empty_normalized_requirements(); // 11
|
||||
test_rejects_invalid_requirement_kind(); // 12
|
||||
test_cross_project_repo_signal_grounds_target_files(); // 13
|
||||
test_project_requirement_generates_specific_task_title_and_files(); // 14
|
||||
test_single_project_product_name_does_not_trigger_cross_repo_fallback(); // 15
|
||||
test_strict_contract_infers_test_files_and_verification_type_for_code_work(); // 16
|
||||
test_rust_repo_contract_drops_wrong_language_commands_and_uses_real_test_file(); // 17
|
||||
|
||||
std::cout << "\nResults: " << passed << "/" << (passed + failed) << " passed\n";
|
||||
return failed == 0 ? 0 : 1;
|
||||
|
||||
Reference in New Issue
Block a user