diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index cc9cb96..1a91144 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -2767,4 +2767,13 @@ target_link_libraries(step431_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step432_test tests/step432_test.cpp) +target_include_directories(step432_test PRIVATE src) +target_link_libraries(step432_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/step432_test.cpp b/editor/tests/step432_test.cpp new file mode 100644 index 0000000..9558b4c --- /dev/null +++ b/editor/tests/step432_test.cpp @@ -0,0 +1,182 @@ +// Step 432: Phase 19a Integration Tests (8 tests) + +#include "WorkflowTaskBoard.h" +#include "WorkflowTaskDetail.h" +#include "WorkflowDependencyView.h" +#include "WorkflowProgressDashboard.h" + +#include + +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 {} + +static WorkItem makeItem(const std::string& id, const std::string& status, + const std::string& worker, const std::vector& deps = {}) { + WorkItem wi; + wi.id = id; + wi.nodeId = id + "_node"; + wi.nodeName = id; + wi.nodeType = "Function"; + wi.bufferId = "src/" + id + ".cpp"; + wi.status = status; + wi.workerType = worker; + wi.priority = "high"; + wi.contextWidth = "file"; + wi.reviewRequired = (worker == "llm" || worker == "human"); + wi.dependencies = deps; + wi.result.generatedCode = "void " + id + "() {}"; + return wi; +} + +static WorkflowState makeWorkflow() { + WorkflowState wf("phase19a"); + wf.queue.enqueue(makeItem("taskA", WI_READY, "template")); + wf.queue.enqueue(makeItem("taskB", WI_IN_PROGRESS, "llm", {"taskA"})); + wf.queue.enqueue(makeItem("taskC", WI_REVIEW, "llm", {"taskB"})); + wf.queue.enqueue(makeItem("taskD", WI_COMPLETE, "deterministic")); + return wf; +} + +static WorkflowProgress makeProgress() { + WorkflowProgress p(4); + p.recordEvent({"routed", "taskA", {{"workerType", "template"}}, "2026-01-01T00:00:00Z"}); + p.recordEvent({"completed", "taskA", json::object(), "2026-01-01T00:01:00Z"}); + p.recordEvent({"routed", "taskB", {{"workerType", "llm"}}, "2026-01-01T00:02:00Z"}); + p.recordEvent({"blocked", "", {{"blockers", json::array({ + {{"type", "needs-review"}, {"itemIds", json::array({"taskC"})}, {"description", "review pending"}} + })}}, "2026-01-01T00:03:00Z"}); + return p; +} + +static int countColumn(const std::vector& cols, const std::string& key) { + for (const auto& c : cols) if (c.key == key) return (int)c.cards.size(); + return 0; +} + +void test_task_board_reflects_workflow_state() { + TEST(task_board_reflects_workflow_state); + auto board = WorkflowTaskBoard::build(makeWorkflow()); + CHECK(countColumn(board, "ready") >= 1, "ready column missing cards"); + CHECK(countColumn(board, "review") >= 1, "review column missing cards"); + PASS(); +} + +void test_dependency_graph_updates_after_completion() { + TEST(dependency_graph_updates_after_completion); + auto wf = makeWorkflow(); + auto g1 = WorkflowDependencyView::build(wf); + auto b1 = g1.getNode("taskB"); + CHECK(b1.has_value() && b1->colorHex == "#3498DB", "taskB should start blue"); + auto item = wf.queue.getItem("taskB"); + item->status = WI_COMPLETE; + wf.queue.updateItem("taskB", *item); + auto g2 = WorkflowDependencyView::build(wf); + auto b2 = g2.getNode("taskB"); + CHECK(b2.has_value() && b2->colorHex == "#2ECC71", "taskB should turn green"); + PASS(); +} + +void test_progress_dashboard_reflects_event_stream() { + TEST(progress_dashboard_reflects_event_stream); + auto d = WorkflowProgressDashboard::build(makeWorkflow(), makeProgress(), nullptr); + CHECK(!d.throughputSeries.empty(), "throughput series missing"); + CHECK(!d.blockerSummary.empty(), "blocker summary missing"); + PASS(); +} + +void test_review_approve_updates_board_and_detail() { + TEST(review_approve_updates_board_and_detail); + auto wf = makeWorkflow(); + CHECK(WorkflowTaskDetail::approve(wf, "taskC"), "approve should succeed"); + auto board = WorkflowTaskBoard::build(wf); + CHECK(countColumn(board, "review") == 0, "review column should be cleared"); + CHECK(countColumn(board, "complete") >= 2, "complete column should increase"); + auto detail = WorkflowTaskDetail::build(wf, "taskC"); + CHECK(detail.has_value() && detail->status == WI_COMPLETE, "detail should show complete"); + PASS(); +} + +void test_review_reject_requeues_and_visible_on_board() { + TEST(review_reject_requeues_and_visible_on_board); + auto wf = makeWorkflow(); + CHECK(WorkflowTaskDetail::reject(wf, "taskC", "needs changes"), "reject should succeed"); + auto board = WorkflowTaskBoard::build(wf); + CHECK(countColumn(board, "ready") >= 2, "rejected item should requeue to ready"); + PASS(); +} + +void test_dependency_click_navigates_to_task_detail() { + TEST(dependency_click_navigates_to_task_detail); + auto wf = makeWorkflow(); + auto graph = WorkflowDependencyView::build(wf); + std::string id = WorkflowDependencyView::navigateToTaskId(graph, "taskB"); + auto detail = WorkflowTaskDetail::build(wf, id); + CHECK(!id.empty(), "navigation id missing"); + CHECK(detail.has_value(), "detail should be openable from graph click"); + PASS(); +} + +void test_board_filter_and_detail_consistency() { + TEST(board_filter_and_detail_consistency); + auto wf = makeWorkflow(); + TaskBoardFilter f; + f.workerType = "llm"; + auto board = WorkflowTaskBoard::build(wf, f); + int cards = 0; + std::string firstId; + for (const auto& col : board) { + cards += (int)col.cards.size(); + if (firstId.empty() && !col.cards.empty()) firstId = col.cards[0].itemId; + } + auto detail = WorkflowTaskDetail::build(wf, firstId); + CHECK(cards >= 2, "llm filter should keep two cards"); + CHECK(detail.has_value() && detail->workerType == "llm", "detail worker mismatch"); + PASS(); +} + +void test_end_to_end_visual_workflow_sequence() { + TEST(end_to_end_visual_workflow_sequence); + auto wf = makeWorkflow(); + WorkflowProgress prog(4); + prog.recordEvent({"routed", "taskA", {{"workerType", "template"}}, "2026-01-01T00:00:00Z"}); + prog.recordEvent({"completed", "taskA", json::object(), "2026-01-01T00:01:00Z"}); + + auto a = wf.queue.getItem("taskB"); + a->status = WI_COMPLETE; + wf.queue.updateItem("taskB", *a); + prog.recordEvent({"completed", "taskB", json::object(), "2026-01-01T00:02:00Z"}); + + CHECK(WorkflowTaskDetail::approve(wf, "taskC"), "review approval failed"); + prog.recordEvent({"completed", "taskC", json::object(), "2026-01-01T00:03:00Z"}); + + auto board = WorkflowTaskBoard::build(wf); + auto graph = WorkflowDependencyView::build(wf); + auto dash = WorkflowProgressDashboard::build(wf, prog, nullptr); + + CHECK(countColumn(board, "review") == 0, "review should be empty"); + CHECK(graph.getNode("taskC").has_value() && + graph.getNode("taskC")->colorHex == "#2ECC71", + "taskC should be green in graph"); + CHECK(dash.completedItems >= 3, "dashboard completion should advance"); + PASS(); +} + +int main() { + std::cout << "Step 432: Phase 19a Integration Tests\n"; + + test_task_board_reflects_workflow_state(); // 1 + test_dependency_graph_updates_after_completion(); // 2 + test_progress_dashboard_reflects_event_stream(); // 3 + test_review_approve_updates_board_and_detail(); // 4 + test_review_reject_requeues_and_visible_on_board(); // 5 + test_dependency_click_navigates_to_task_detail(); // 6 + test_board_filter_and_detail_consistency(); // 7 + test_end_to_end_visual_workflow_sequence(); // 8 + + std::cout << "\nResults: " << passed << "/" << (passed + failed) + << " passed\n"; + return failed == 0 ? 0 : 1; +} diff --git a/progress.md b/progress.md index 622cd44..a899914 100644 --- a/progress.md +++ b/progress.md @@ -4944,6 +4944,56 @@ tracking into a panel-ready snapshot. - `editor/src/MCPServer.h` (`1940` > `600`) - `editor/src/HeadlessAgentRPCHandler.h` (`2768` > `600`) +### Step 432: Phase 19a Integration +**Status:** PASS (8/8 tests) + +Added Phase 19a integration coverage ensuring task board, task detail actions, +dependency view, and progress dashboard stay synchronized across workflow state +changes and review lifecycle transitions. + +**Files created:** +- `editor/tests/step432_test.cpp` — 8 integration tests covering: + 1. task board state sync + 2. dependency graph update after completion + 3. progress dashboard timeline/blocker sync + 4. review approve flow updates board+detail + 5. review reject flow requeues on board + 6. dependency-node click to detail navigation + 7. board filter/detail consistency + 8. end-to-end visible flow (route -> execute -> review -> complete) + +**Files modified:** +- `editor/CMakeLists.txt` — `step432_test` target + +**Verification run:** +- `step432_test` — PASS (8/8) new phase-integration coverage +- `step431_test` — PASS (12/12) regression coverage +- `step430_test` — PASS (12/12) regression coverage + +**Phase 19a completion snapshot (Steps 428-432):** +- Step 428: task board model (kanban columns, filters, moves) +- Step 429: task detail model (skeleton/result/diff/routing/review actions) +- Step 430: dependency graph visualization adapter +- Step 431: progress dashboard model +- Step 432: integration across board/detail/graph/dashboard + +**Architecture gate check (end of Phase 19a):** +- New Phase 19a headers within header-size limit: + - `editor/src/WorkflowTaskBoard.h` (`154` <= `600`) + - `editor/src/WorkflowTaskDetail.h` (`121` <= `600`) + - `editor/src/WorkflowDependencyView.h` (`94` <= `600`) + - `editor/src/WorkflowProgressDashboard.h` (`112` <= `600`) +- New Phase 19a tests within file-size guidance: + - `editor/tests/step428_test.cpp` (`186` lines) + - `editor/tests/step429_test.cpp` (`169` lines) + - `editor/tests/step430_test.cpp` (`155` lines) + - `editor/tests/step431_test.cpp` (`202` lines) + - `editor/tests/step432_test.cpp` (`182` lines) +- Legacy oversized headers persist: + - `editor/src/ast/Serialization.h` (`1427` > `600`) + - `editor/src/MCPServer.h` (`1940` > `600`) + - `editor/src/HeadlessAgentRPCHandler.h` (`2768` > `600`) + # Roadmap Planning — Sprints 12-25+ ## Status: Planning Complete (Sprints 12-19 detailed, 20-25 in roadmap.md)