Step 418: add workflow MCP prompt templates

This commit is contained in:
Bill
2026-02-16 16:09:01 -07:00
parent 1be9b6102a
commit 29ae2aae7a
4 changed files with 358 additions and 0 deletions

View File

@@ -2641,4 +2641,13 @@ target_link_libraries(step417_test PRIVATE
tree_sitter_javascript tree_sitter_typescript
tree_sitter_java tree_sitter_rust tree_sitter_go)
add_executable(step418_test tests/step418_test.cpp)
target_include_directories(step418_test PRIVATE src)
target_link_libraries(step418_test PRIVATE
nlohmann_json::nlohmann_json
unofficial::tree-sitter::tree-sitter
tree_sitter_python tree_sitter_cpp tree_sitter_elisp
tree_sitter_javascript tree_sitter_typescript
tree_sitter_java tree_sitter_rust tree_sitter_go)
# Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies)

View File

@@ -0,0 +1,159 @@
#pragma once
#include <fstream>
#include <map>
#include <string>
#include <vector>
#include <filesystem>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
struct MCPWorkflowPromptTemplate {
std::string name;
std::string description;
std::string userPrompt;
std::vector<std::string> expectedTools;
};
class MCPWorkflowPrompts {
public:
static std::vector<MCPWorkflowPromptTemplate> templates() {
return {
{
"architect_project",
"Analyze codebase and create annotated project skeleton.",
"Protocol:\n"
"1. Run whetstone_infer_annotations over key modules.\n"
"2. Create structural scaffold with whetstone_create_skeleton.\n"
"3. Build initial task plan using whetstone_create_workflow.\n"
"4. Explain architecture invariants and routing assumptions.\n"
"Goal: Analyze {{project_name}} and produce an annotated skeleton.",
{"whetstone_infer_annotations", "whetstone_create_skeleton", "whetstone_create_workflow"}
},
{
"modernize_module",
"Modernize legacy module with workflow-guided safety.",
"Protocol:\n"
"1. Infer annotations and constraints for {{module_name}} with whetstone_infer_annotations.\n"
"2. Run automatic safe tasks via whetstone_orchestrate_run_deterministic.\n"
"3. Fetch unresolved items with whetstone_get_blockers.\n"
"4. Submit agent changes with whetstone_submit_result.\n"
"Goal: Modernize module while preserving behavior and auditability.",
{"whetstone_infer_annotations", "whetstone_orchestrate_run_deterministic",
"whetstone_get_blockers", "whetstone_submit_result"}
},
{
"port_to_language",
"Port module between languages through workflow routing.",
"Protocol:\n"
"1. Read source semantics and infer annotations with whetstone_infer_annotations.\n"
"2. Project using whetstone_project_language from {{source_language}} to {{target_language}}.\n"
"3. Plan remaining edits with whetstone_create_workflow.\n"
"4. Execute deterministic tasks and report blockers with whetstone_get_blockers.\n"
"Goal: Port code to target language with semantic parity.",
{"whetstone_infer_annotations", "whetstone_project_language",
"whetstone_create_workflow", "whetstone_get_blockers"}
},
{
"add_feature",
"Add feature through orchestrated skeleton-first workflow.",
"Protocol:\n"
"1. Add feature skeleton nodes with whetstone_create_skeleton.\n"
"2. Create execution plan via whetstone_create_workflow.\n"
"3. Execute deterministic tasks with whetstone_orchestrate_run_deterministic.\n"
"4. Track progress using whetstone_get_progress.\n"
"Goal: Implement {{feature_name}} with explicit review checkpoints.",
{"whetstone_create_skeleton", "whetstone_create_workflow",
"whetstone_orchestrate_run_deterministic", "whetstone_get_progress"}
},
{
"review_pending",
"Review and approve/reject pending workflow items.",
"Protocol:\n"
"1. Load pending review items using whetstone_get_review_queue.\n"
"2. For each item, open full details with whetstone_get_review_context.\n"
"3. Approve with whetstone_approve_item or reject with whetstone_reject_item.\n"
"4. Summarize accepted/rejected decisions and rationale.\n"
"Goal: Clear review queue with traceable decisions.",
{"whetstone_get_review_queue", "whetstone_get_review_context",
"whetstone_approve_item", "whetstone_reject_item"}
}
};
}
static bool hasTemplate(const std::string& name) {
for (const auto& t : templates()) if (t.name == name) return true;
return false;
}
static MCPWorkflowPromptTemplate getTemplate(const std::string& name) {
for (const auto& t : templates()) if (t.name == name) return t;
return {};
}
static std::string renderPrompt(const std::string& name,
const std::map<std::string, std::string>& params) {
auto t = getTemplate(name);
std::string out = t.userPrompt;
for (const auto& [k, v] : params) {
replaceAll(out, "{{" + k + "}}", v);
}
return out;
}
static bool validate(const MCPWorkflowPromptTemplate& t, std::string& error) {
if (t.name.empty()) { error = "template name is empty"; return false; }
if (t.userPrompt.find("Protocol:") == std::string::npos) {
error = "missing protocol section";
return false;
}
if (t.expectedTools.empty()) {
error = "expected tools list is empty";
return false;
}
for (const auto& tool : t.expectedTools) {
if (t.userPrompt.find(tool) == std::string::npos) {
error = "prompt does not reference required tool: " + tool;
return false;
}
}
return true;
}
static json manifestJson() {
json arr = json::array();
for (const auto& t : templates()) {
arr.push_back({
{"name", t.name},
{"description", t.description},
{"expectedTools", t.expectedTools}
});
}
return {{"templates", arr}};
}
static bool writeTemplateFiles(const std::string& outputDir) {
std::filesystem::create_directories(outputDir);
for (const auto& t : templates()) {
std::ofstream out(std::filesystem::path(outputDir) / (t.name + ".prompt"));
if (!out.is_open()) return false;
out << "# " << t.name << "\n\n";
out << "description:\n" << t.description << "\n\n";
out << "user:\n" << t.userPrompt << "\n\n";
out << "expected_tools:\n";
for (const auto& tool : t.expectedTools) out << "- " << tool << "\n";
}
return true;
}
private:
static void replaceAll(std::string& text, const std::string& from, const std::string& to) {
if (from.empty()) return;
size_t pos = 0;
while ((pos = text.find(from, pos)) != std::string::npos) {
text.replace(pos, from.size(), to);
pos += to.size();
}
}
};

View File

@@ -0,0 +1,146 @@
// Step 418: Workflow Prompt Templates Tests (12 tests)
#include "MCPWorkflowPrompts.h"
#include <filesystem>
#include <iostream>
#include <map>
#include <string>
static int passed = 0, failed = 0;
#define TEST(name) { std::cout << " " << #name << "... "; }
#define PASS() { std::cout << "PASS\n"; ++passed; }
#define FAIL(msg) { std::cout << "FAIL: " << msg << "\n"; ++failed; }
#define CHECK(cond, msg) if (!(cond)) { FAIL(msg); return; } else {}
void test_templates_include_required_names() {
TEST(templates_include_required_names);
CHECK(MCPWorkflowPrompts::hasTemplate("architect_project"), "missing architect_project");
CHECK(MCPWorkflowPrompts::hasTemplate("modernize_module"), "missing modernize_module");
CHECK(MCPWorkflowPrompts::hasTemplate("port_to_language"), "missing port_to_language");
CHECK(MCPWorkflowPrompts::hasTemplate("add_feature"), "missing add_feature");
CHECK(MCPWorkflowPrompts::hasTemplate("review_pending"), "missing review_pending");
PASS();
}
void test_architect_project_tools_present() {
TEST(architect_project_tools_present);
auto t = MCPWorkflowPrompts::getTemplate("architect_project");
CHECK(t.userPrompt.find("whetstone_infer_annotations") != std::string::npos, "missing infer tool");
CHECK(t.userPrompt.find("whetstone_create_skeleton") != std::string::npos, "missing skeleton tool");
CHECK(t.userPrompt.find("whetstone_create_workflow") != std::string::npos, "missing workflow tool");
PASS();
}
void test_modernize_module_tools_present() {
TEST(modernize_module_tools_present);
auto t = MCPWorkflowPrompts::getTemplate("modernize_module");
CHECK(t.userPrompt.find("whetstone_orchestrate_run_deterministic") != std::string::npos,
"missing deterministic run tool");
CHECK(t.userPrompt.find("whetstone_get_blockers") != std::string::npos, "missing blockers tool");
CHECK(t.userPrompt.find("whetstone_submit_result") != std::string::npos, "missing submit tool");
PASS();
}
void test_port_template_mentions_source_target() {
TEST(port_template_mentions_source_target);
auto rendered = MCPWorkflowPrompts::renderPrompt(
"port_to_language",
{{"source_language", "python"}, {"target_language", "rust"}});
CHECK(rendered.find("python") != std::string::npos, "missing source language replacement");
CHECK(rendered.find("rust") != std::string::npos, "missing target language replacement");
PASS();
}
void test_add_feature_template_progress_tools_present() {
TEST(add_feature_template_progress_tools_present);
auto t = MCPWorkflowPrompts::getTemplate("add_feature");
CHECK(t.userPrompt.find("whetstone_create_workflow") != std::string::npos, "missing workflow");
CHECK(t.userPrompt.find("whetstone_get_progress") != std::string::npos, "missing progress");
PASS();
}
void test_review_pending_template_review_tools_present() {
TEST(review_pending_template_review_tools_present);
auto t = MCPWorkflowPrompts::getTemplate("review_pending");
CHECK(t.userPrompt.find("whetstone_get_review_queue") != std::string::npos, "missing review queue");
CHECK(t.userPrompt.find("whetstone_get_review_context") != std::string::npos, "missing review context");
CHECK(t.userPrompt.find("whetstone_approve_item") != std::string::npos, "missing approve");
CHECK(t.userPrompt.find("whetstone_reject_item") != std::string::npos, "missing reject");
PASS();
}
void test_render_prompt_substitutes_project_name() {
TEST(render_prompt_substitutes_project_name);
std::string rendered = MCPWorkflowPrompts::renderPrompt(
"architect_project", {{"project_name", "AlphaRepo"}});
CHECK(rendered.find("AlphaRepo") != std::string::npos, "project_name not substituted");
PASS();
}
void test_render_prompt_keeps_unprovided_placeholders() {
TEST(render_prompt_keeps_unprovided_placeholders);
std::string rendered = MCPWorkflowPrompts::renderPrompt("add_feature", {});
CHECK(rendered.find("{{feature_name}}") != std::string::npos,
"unprovided placeholder should remain explicit");
PASS();
}
void test_validate_rejects_empty_tools() {
TEST(validate_rejects_empty_tools);
MCPWorkflowPromptTemplate t;
t.name = "bad";
t.userPrompt = "Protocol:\n1. do a thing";
std::string err;
CHECK(!MCPWorkflowPrompts::validate(t, err), "expected validation failure");
CHECK(!err.empty(), "expected error message");
PASS();
}
void test_manifest_json_contains_all_templates() {
TEST(manifest_json_contains_all_templates);
json m = MCPWorkflowPrompts::manifestJson();
CHECK(m.contains("templates"), "missing templates field");
CHECK(m["templates"].is_array(), "templates should be array");
CHECK(m["templates"].size() == 5, "expected five templates");
PASS();
}
void test_write_template_files_to_directory() {
TEST(write_template_files_to_directory);
auto outDir = std::filesystem::temp_directory_path() / "whetstone_step418_prompts";
CHECK(MCPWorkflowPrompts::writeTemplateFiles(outDir.string()), "writeTemplateFiles failed");
CHECK(std::filesystem::exists(outDir / "architect_project.prompt"), "missing architect prompt file");
CHECK(std::filesystem::exists(outDir / "review_pending.prompt"), "missing review prompt file");
PASS();
}
void test_all_templates_validate_successfully() {
TEST(all_templates_validate_successfully);
for (const auto& t : MCPWorkflowPrompts::templates()) {
std::string err;
CHECK(MCPWorkflowPrompts::validate(t, err), "template validation failed");
}
PASS();
}
int main() {
std::cout << "Step 418: Workflow Prompt Templates Tests\n";
test_templates_include_required_names(); // 1
test_architect_project_tools_present(); // 2
test_modernize_module_tools_present(); // 3
test_port_template_mentions_source_target(); // 4
test_add_feature_template_progress_tools_present(); // 5
test_review_pending_template_review_tools_present(); // 6
test_render_prompt_substitutes_project_name(); // 7
test_render_prompt_keeps_unprovided_placeholders(); // 8
test_validate_rejects_empty_tools(); // 9
test_manifest_json_contains_all_templates(); // 10
test_write_template_files_to_directory(); // 11
test_all_templates_validate_successfully(); // 12
std::cout << "\nResults: " << passed << "/" << (passed + failed)
<< " passed\n";
return failed == 0 ? 0 : 1;
}

View File

@@ -4278,6 +4278,50 @@ inspection without adding a custom plugin binary.
- `editor/src/MCPServer.h` (`1679` > `600`)
- `editor/src/HeadlessAgentRPCHandler.h` (`2629` > `600`)
### Step 418: Workflow Prompt Templates
**Status:** PASS (12/12 tests)
Added workflow-specific MCP prompt templates aligned to Sprint 18a's named
flows (`architect_project`, `modernize_module`, `port_to_language`,
`add_feature`, `review_pending`) with explicit tool references and rendering.
**Files created:**
- `editor/src/MCPWorkflowPrompts.h` — workflow prompt-template support:
- canonical template catalog (5 templates)
- parameterized prompt rendering (`{{...}}` substitution)
- template validation (protocol section + required tool mentions)
- JSON manifest export for client/tooling discovery
- prompt-file writer for emitting portable `.prompt` files
- `editor/tests/step418_test.cpp` — 12 tests covering:
1. required template-name presence
2. `architect_project` tool references
3. `modernize_module` tool references
4. `port_to_language` source/target substitution
5. `add_feature` progress workflow references
6. `review_pending` review queue/context/approve/reject references
7. parameter substitution behavior
8. unresolved placeholder behavior when param omitted
9. validation failure on empty tool list
10. manifest structure and template count
11. template-file write-out
12. validation success for all shipped templates
**Files modified:**
- `editor/CMakeLists.txt``step418_test` target
**Verification run:**
- `step418_test` — PASS (12/12) new step coverage
- `step417_test` — PASS (12/12) regression coverage
- `step416_test` — PASS (8/8) regression coverage
**Architecture gate check:**
- `editor/src/MCPWorkflowPrompts.h` within header-size limit (`159` <= `600`)
- `editor/tests/step418_test.cpp` within test-file size guidance (`146` lines)
- Legacy oversized headers persist:
- `editor/src/ast/Serialization.h` (`1427` > `600`)
- `editor/src/MCPServer.h` (`1679` > `600`)
- `editor/src/HeadlessAgentRPCHandler.h` (`2629` > `600`)
# Roadmap Planning — Sprints 12-25+
## Status: Planning Complete (Sprints 12-19 detailed, 20-25 in roadmap.md)