From 9e68ef07074e9b553b3f04566bbfc461fa2a6b48 Mon Sep 17 00:00:00 2001 From: Bill Date: Mon, 16 Feb 2026 13:49:26 -0700 Subject: [PATCH] Step 393: add Phase 15c integration tests and Sprint 15 summary Co-Authored-By: Claude Opus 4.6 --- editor/CMakeLists.txt | 9 ++ editor/tests/step393_test.cpp | 250 ++++++++++++++++++++++++++++++++++ 2 files changed, 259 insertions(+) create mode 100644 editor/tests/step393_test.cpp diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 996d995..4c54628 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -2416,4 +2416,13 @@ target_link_libraries(step392_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step393_test tests/step393_test.cpp) +target_include_directories(step393_test PRIVATE src) +target_link_libraries(step393_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) diff --git a/editor/tests/step393_test.cpp b/editor/tests/step393_test.cpp new file mode 100644 index 0000000..090473d --- /dev/null +++ b/editor/tests/step393_test.cpp @@ -0,0 +1,250 @@ +// Step 393: Phase 15c Integration + Sprint 15 Summary (8 tests) + +#include +#include +#include +#include "RoutingRules.h" +#include "CostEstimator.h" +#include "WorkflowTemplates.h" +#include "DependencyGraph.h" +#include "HeadlessEditorState.h" +#include "HeadlessAgentRPCHandler.h" +#include "WorkflowOrchestrator.h" +#include "WorkflowProtocol.h" +#include "EventStream.h" + +static WorkItem makeItem(const std::string& id, + const std::string& name = "doWork", + const std::string& workerType = "", + const std::string& contextWidth = "local", + const std::string& priority = "medium", + const std::vector& deps = {}) { + WorkItem item; + item.id = id; + item.nodeId = id + "_node"; + item.nodeName = name; + item.nodeType = "Function"; + item.bufferId = "main.py"; + item.workerType = workerType; + item.contextWidth = contextWidth; + item.priority = priority; + item.status = WI_READY; + item.createdAt = workItemTimestamp(); + item.dependencies = deps; + return item; +} + +int main() { + int passed = 0; + + // Test 1: Custom routing rules override default to use template for simple functions + { + RulesEngine engine; + // Add custom rule: all local functions → template + RoutingRule custom; + custom.name = "local-template"; + custom.priority = 1; + custom.conditions.push_back({"contextWidth", "", "==", "local"}); + custom.action.workerType = "template"; + engine.addRule(custom); + + auto item = makeItem("t1", "processData", "", "local"); + auto decision = engine.evaluate(item); + assert(decision.workerType == "template"); + assert(decision.reasoning.find("local-template") != std::string::npos); + std::cout << "PASS: test 1 — custom routing rules override\n"; + passed++; + } + + // Test 2: Cost estimation for 10-function workflow with optimization suggestions + { + WorkflowState wf("test"); + // 3 getters in project context (should suggest template) + wf.queue.enqueue(makeItem("t1", "getName", "", "project")); + wf.queue.enqueue(makeItem("t2", "getValue", "", "project")); + wf.queue.enqueue(makeItem("t3", "isActive", "", "project")); + // 7 normal functions + for (int i = 4; i <= 10; ++i) { + wf.queue.enqueue(makeItem("t" + std::to_string(i), "func" + std::to_string(i), "", "local")); + } + + RoutingEngine routing; + CostEstimator estimator; + auto est = estimator.estimate(wf, routing); + + assert(est.totalContextTokens > 0); + assert(est.totalOutputTokens > 0); + + // Should have use-template suggestion for the getters + bool hasTemplateSuggestion = false; + for (const auto& s : est.optimizationSuggestions) { + if (s.action == "use-template") { + hasTemplateSuggestion = true; + } + } + assert(hasTemplateSuggestion); + std::cout << "PASS: test 2 — cost estimation with suggestions\n"; + passed++; + } + + // Test 3: CRUD template → route → deterministic completes + { + TemplateParams params; + params.entityName = "Task"; + auto wf = WorkflowTemplates::applyTemplate("crud-api", params); + + RoutingEngine routing; + WorkerRegistry workers = WorkerRegistry::getDefaultRegistry(); + ContextAssembler assembler; + ReviewGate gate; + + WorkflowOrchestrator orchestrator(wf, routing, workers, assembler, gate); + auto stats = orchestrator.runToCompletion(); + + // Template (getTask) and deterministic should auto-complete + // LLM/SLM tasks will block at needs-external-model + assert(stats.complete > 0 || stats.inProgress > 0); + std::cout << "PASS: test 3 — CRUD template workflow execution\n"; + passed++; + } + + // Test 4: Dependency graph 5-function chain → critical path = 5 + { + WorkflowState wf("test"); + wf.queue.enqueue(makeItem("t1", "step1")); + wf.queue.enqueue(makeItem("t2", "step2", "", "local", "medium", {"t1"})); + wf.queue.enqueue(makeItem("t3", "step3", "", "local", "medium", {"t2"})); + wf.queue.enqueue(makeItem("t4", "step4", "", "local", "medium", {"t3"})); + wf.queue.enqueue(makeItem("t5", "step5", "", "local", "medium", {"t4"})); + + auto graph = buildDependencyGraph(wf); + auto path = getCriticalPath(graph); + assert(path.size() == 5); + assert(path[0] == "t1"); + assert(path[4] == "t5"); + std::cout << "PASS: test 4 — dependency graph critical path\n"; + passed++; + } + + // Test 5: Event stream captures workflow transitions + { + WorkflowState wf("test"); + wf.queue.enqueue(makeItem("t1", "getName", "template", "local")); + + RoutingEngine routing; + WorkerRegistry workers = WorkerRegistry::getDefaultRegistry(); + ContextAssembler assembler; + ReviewGate gate; + EventStream stream; + + WorkflowOrchestrator orchestrator(wf, routing, workers, assembler, gate); + auto events = orchestrator.advance(); + + // Emit events to stream + for (const auto& ev : events) { + stream.emit(ev); + } + + assert(stream.getVersion() > 0); + auto recent = stream.getRecent(10); + assert(!recent.empty()); + + // Check that we see routing and execution events + bool hasRouted = false, hasExecuted = false; + for (const auto& se : recent) { + if (se.event.type == "routed") hasRouted = true; + if (se.event.type == "executed") hasExecuted = true; + } + assert(hasRouted); + assert(hasExecuted); + std::cout << "PASS: test 5 — event stream captures transitions\n"; + passed++; + } + + // Test 6: Full MCP client simulation through protocol phases + { + WorkflowSession session; + session.sessionId = "test-session"; + session.projectName = "integration-test"; + + // Phase transitions + assert(validateTransition("init", "model")); + assert(validateTransition("model", "annotate")); + assert(validateTransition("annotate", "plan")); + assert(validateTransition("plan", "route")); + assert(validateTransition("route", "execute")); + assert(validateTransition("execute", "assist")); + assert(validateTransition("assist", "review")); + assert(validateTransition("review", "complete")); + assert(!validateTransition("complete", "init")); // can't go backward + + // getNextAction suggests correct phase + auto action = getNextAction(session); + assert(action.phase == "model" || action.phase == "init"); + std::cout << "PASS: test 6 — protocol phase validation\n"; + passed++; + } + + // Test 7: Optimization: apply suggestion to narrow context → lower cost + { + WorkflowState wf("test"); + // Getter at project width (wasteful) + wf.queue.enqueue(makeItem("t1", "getName", "", "project")); + + RoutingEngine routing; + CostEstimator estimator; + + auto est1 = estimator.estimate(wf, routing); + int originalCost = est1.totalContextTokens; + + // Apply optimization: narrow to local + auto item = wf.queue.getItem("t1"); + WorkItem updated = item.value(); + updated.contextWidth = "local"; + wf.queue.updateItem("t1", updated); + + auto est2 = estimator.estimate(wf, routing); + int optimizedCost = est2.totalContextTokens; + + assert(optimizedCost < originalCost); + std::cout << "PASS: test 7 — optimization reduces cost\n"; + passed++; + } + + // Test 8: Sprint 15 totals verification + { + // Verify all new components exist and work together + // RoutingRules + auto defaults = RulesEngine::getDefaultRules(); + assert(defaults.size() >= 5); + + // CostEstimator + CostEstimator estimator; + WorkflowState emptyWf("empty"); + RoutingEngine routing; + auto est = estimator.estimate(emptyWf, routing); + assert(est.totalContextTokens == 0); + + // WorkflowTemplates + auto templates = WorkflowTemplates::getTemplates(); + assert(templates.size() == 5); + + // DependencyGraph + auto graph = buildDependencyGraph(emptyWf); + assert(graph.nodes.empty()); + + // EventStream + EventStream stream; + assert(stream.getVersion() == 0); + + // WorkflowProtocol + auto phases = workflowProtocolPhases(); + assert(phases.size() == 9); + + std::cout << "PASS: test 8 — Sprint 15 totals verification\n"; + passed++; + } + + std::cout << "\nStep 393 result: " << passed << "/8 tests passed\n"; + return (passed == 8) ? 0 : 1; +}