Refine taskitem workflow guidance and sprint intake shaping

This commit is contained in:
Bill
2026-03-21 02:17:17 -06:00
parent 69243141dd
commit 017456a476
11 changed files with 1049 additions and 23 deletions

View File

@@ -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;