Add step435 workflow status overlay and tests

This commit is contained in:
Bill
2026-02-16 17:24:41 -07:00
parent 8129e7a320
commit 142e826318
4 changed files with 304 additions and 0 deletions

View File

@@ -2794,4 +2794,13 @@ target_link_libraries(step434_test PRIVATE
tree_sitter_javascript tree_sitter_typescript
tree_sitter_java tree_sitter_rust tree_sitter_go)
add_executable(step435_test tests/step435_test.cpp)
target_include_directories(step435_test PRIVATE src)
target_link_libraries(step435_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,94 @@
#pragma once
#include "WorkflowState.h"
#include <algorithm>
#include <map>
#include <string>
#include <vector>
struct WorkflowStatusMarker {
std::string itemId;
std::string nodeId;
std::string displayName;
std::string bufferId;
int line = 0;
std::string status;
std::string statusIcon;
std::string statusColor;
std::string boardColumn;
std::string detailRoute;
};
class WorkflowStatusOverlay {
public:
static std::vector<WorkflowStatusMarker> build(const WorkflowState& wf,
const std::map<std::string, int>& nodeLines,
const std::string& bufferFilter = "") {
std::vector<WorkflowStatusMarker> out;
for (const auto& status : {WI_PENDING, WI_READY, WI_ASSIGNED, WI_IN_PROGRESS,
WI_REVIEW, WI_COMPLETE, WI_REJECTED}) {
for (const auto& wi : wf.queue.getByStatus(status)) {
if (!bufferFilter.empty() && wi.bufferId != bufferFilter) continue;
WorkflowStatusMarker marker;
marker.itemId = wi.id;
marker.nodeId = wi.nodeId;
marker.displayName = wi.nodeName.empty() ? wi.nodeId : wi.nodeName;
marker.bufferId = wi.bufferId;
marker.line = lineForNode(wi.nodeId, nodeLines);
marker.status = wi.status;
marker.statusIcon = iconForStatus(wi.status);
marker.statusColor = colorForStatus(wi.status);
marker.boardColumn = boardColumnForStatus(wi.status);
marker.detailRoute = detailRoute(wi.id);
out.push_back(marker);
}
}
std::sort(out.begin(), out.end(), [](const WorkflowStatusMarker& a,
const WorkflowStatusMarker& b) {
if (a.bufferId != b.bufferId) return a.bufferId < b.bufferId;
if (a.line != b.line) return a.line < b.line;
return a.itemId < b.itemId;
});
return out;
}
static std::string iconForStatus(const std::string& status) {
if (status == WI_PENDING || status == WI_READY || status == WI_REJECTED) return "outline";
if (status == WI_ASSIGNED || status == WI_IN_PROGRESS) return "spinner";
if (status == WI_REVIEW) return "eye";
if (status == WI_COMPLETE) return "check";
return "outline";
}
static std::string colorForStatus(const std::string& status) {
if (status == WI_PENDING || status == WI_REJECTED) return "#7F8C8D";
if (status == WI_READY) return "#3498DB";
if (status == WI_ASSIGNED || status == WI_IN_PROGRESS) return "#2980B9";
if (status == WI_REVIEW) return "#F39C12";
if (status == WI_COMPLETE) return "#27AE60";
return "#7F8C8D";
}
static std::string boardColumnForStatus(const std::string& status) {
if (status == WI_PENDING || status == WI_REJECTED) return "pending";
if (status == WI_READY) return "ready";
if (status == WI_ASSIGNED || status == WI_IN_PROGRESS) return "in-progress";
if (status == WI_REVIEW) return "review";
if (status == WI_COMPLETE) return "complete";
return "pending";
}
static std::string detailRoute(const std::string& itemId) {
return "task://detail/" + itemId;
}
private:
static int lineForNode(const std::string& nodeId,
const std::map<std::string, int>& nodeLines) {
auto it = nodeLines.find(nodeId);
if (it == nodeLines.end()) return 0;
return it->second;
}
};

View File

@@ -0,0 +1,155 @@
// Step 435: Workflow Status Overlay Tests (12 tests)
#include "WorkflowStatusOverlay.h"
#include <iostream>
#include <map>
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& nodeId,
const std::string& name,
const std::string& status,
const std::string& file) {
WorkItem wi;
wi.id = id;
wi.nodeId = nodeId;
wi.nodeName = name;
wi.status = status;
wi.bufferId = file;
wi.workerType = "llm";
wi.priority = "medium";
return wi;
}
static WorkflowState sampleState() {
WorkflowState wf("overlay");
wf.queue.enqueue(makeItem("w1", "n1", "parseTokens", WI_PENDING, "src/a.cpp"));
wf.queue.enqueue(makeItem("w2", "n2", "routeTask", WI_READY, "src/a.cpp"));
wf.queue.enqueue(makeItem("w3", "n3", "emitCode", WI_IN_PROGRESS, "src/a.cpp"));
wf.queue.enqueue(makeItem("w4", "n4", "reviewOutput", WI_REVIEW, "src/b.cpp"));
wf.queue.enqueue(makeItem("w5", "n5", "finalize", WI_COMPLETE, "src/b.cpp"));
return wf;
}
void test_build_overlay_returns_marker_per_item() {
TEST(build_overlay_returns_marker_per_item);
auto wf = sampleState();
std::map<std::string, int> lines = {{"n1", 10}, {"n2", 20}, {"n3", 30}, {"n4", 40}, {"n5", 50}};
auto markers = WorkflowStatusOverlay::build(wf, lines);
CHECK(markers.size() == 5, "expected one marker per work item");
PASS();
}
void test_buffer_filter_limits_markers() {
TEST(buffer_filter_limits_markers);
auto wf = sampleState();
std::map<std::string, int> lines = {{"n1", 10}, {"n2", 20}, {"n3", 30}, {"n4", 40}, {"n5", 50}};
auto markers = WorkflowStatusOverlay::build(wf, lines, "src/a.cpp");
CHECK(markers.size() == 3, "expected only src/a.cpp markers");
PASS();
}
void test_missing_line_mapping_defaults_to_zero() {
TEST(missing_line_mapping_defaults_to_zero);
auto wf = sampleState();
std::map<std::string, int> lines = {{"n1", 10}};
auto markers = WorkflowStatusOverlay::build(wf, lines);
bool hasZero = false;
for (const auto& m : markers) if (m.nodeId == "n2" && m.line == 0) hasZero = true;
CHECK(hasZero, "missing line map should produce line 0");
PASS();
}
void test_icon_mapping_for_pending_is_outline() {
TEST(icon_mapping_for_pending_is_outline);
CHECK(WorkflowStatusOverlay::iconForStatus(WI_PENDING) == "outline", "pending icon mismatch");
PASS();
}
void test_icon_mapping_for_in_progress_is_spinner() {
TEST(icon_mapping_for_in_progress_is_spinner);
CHECK(WorkflowStatusOverlay::iconForStatus(WI_IN_PROGRESS) == "spinner", "in-progress icon mismatch");
PASS();
}
void test_icon_mapping_for_review_is_eye() {
TEST(icon_mapping_for_review_is_eye);
CHECK(WorkflowStatusOverlay::iconForStatus(WI_REVIEW) == "eye", "review icon mismatch");
PASS();
}
void test_icon_mapping_for_complete_is_check() {
TEST(icon_mapping_for_complete_is_check);
CHECK(WorkflowStatusOverlay::iconForStatus(WI_COMPLETE) == "check", "complete icon mismatch");
PASS();
}
void test_color_mapping_matches_board_statuses() {
TEST(color_mapping_matches_board_statuses);
CHECK(WorkflowStatusOverlay::colorForStatus(WI_REVIEW) == "#F39C12", "review color mismatch");
CHECK(WorkflowStatusOverlay::colorForStatus(WI_COMPLETE) == "#27AE60", "complete color mismatch");
PASS();
}
void test_board_column_mapping_for_assigned() {
TEST(board_column_mapping_for_assigned);
CHECK(WorkflowStatusOverlay::boardColumnForStatus(WI_ASSIGNED) == "in-progress", "assigned column mismatch");
PASS();
}
void test_click_route_points_to_task_detail() {
TEST(click_route_points_to_task_detail);
CHECK(WorkflowStatusOverlay::detailRoute("work-9") == "task://detail/work-9", "detail route mismatch");
PASS();
}
void test_display_name_prefers_node_name() {
TEST(display_name_prefers_node_name);
WorkflowState wf("overlay");
wf.queue.enqueue(makeItem("x1", "node-x", "prettyName", WI_READY, "src/c.cpp"));
std::map<std::string, int> lines = {{"node-x", 12}};
auto markers = WorkflowStatusOverlay::build(wf, lines);
CHECK(markers.size() == 1, "expected one marker");
CHECK(markers[0].displayName == "prettyName", "display name should prefer nodeName");
PASS();
}
void test_markers_sort_by_file_then_line() {
TEST(markers_sort_by_file_then_line);
WorkflowState wf("overlay");
wf.queue.enqueue(makeItem("z2", "n2", "b", WI_READY, "src/z.cpp"));
wf.queue.enqueue(makeItem("a1", "n1", "a", WI_READY, "src/a.cpp"));
std::map<std::string, int> lines = {{"n1", 40}, {"n2", 5}};
auto markers = WorkflowStatusOverlay::build(wf, lines);
CHECK(markers.size() == 2, "expected two markers");
CHECK(markers[0].bufferId == "src/a.cpp", "file order mismatch");
CHECK(markers[1].bufferId == "src/z.cpp", "file order mismatch");
PASS();
}
int main() {
std::cout << "Step 435: Workflow Status Overlay Tests\n";
test_build_overlay_returns_marker_per_item(); // 1
test_buffer_filter_limits_markers(); // 2
test_missing_line_mapping_defaults_to_zero(); // 3
test_icon_mapping_for_pending_is_outline(); // 4
test_icon_mapping_for_in_progress_is_spinner(); // 5
test_icon_mapping_for_review_is_eye(); // 6
test_icon_mapping_for_complete_is_check(); // 7
test_color_mapping_matches_board_statuses(); // 8
test_board_column_mapping_for_assigned(); // 9
test_click_route_points_to_task_detail(); // 10
test_display_name_prefers_node_name(); // 11
test_markers_sort_by_file_then_line(); // 12
std::cout << "\nResults: " << passed << "/" << (passed + failed)
<< " passed\n";
return failed == 0 ? 0 : 1;
}

View File

@@ -5083,6 +5083,52 @@ visualization in workflow overlays, including category weighting and color mappi
- `editor/src/MCPServer.h` (`1940` > `600`)
- `editor/src/HeadlessAgentRPCHandler.h` (`2768` > `600`)
### Step 435: Workflow Status Overlay
**Status:** PASS (12/12 tests)
Added a workflow status overlay model to project per-function/class lifecycle
state into editor views, with status icon/color mapping and task-detail routes.
**Files created:**
- `editor/src/WorkflowStatusOverlay.h` — overlay marker support:
- work-item -> status marker projection
- optional buffer filter for multi-file views
- node-id -> line mapping with safe fallback
- status -> icon mapping (outline/spinner/eye/check)
- status -> color mapping aligned with board states
- status -> task-board column normalization
- click route helper to task-detail URI
- stable sorting by file/line/item for deterministic rendering
- `editor/tests/step435_test.cpp` — 12 tests covering:
1. marker generation count parity with work items
2. file-scope buffer filtering behavior
3. missing node-line fallback behavior
4. pending icon mapping
5. in-progress icon mapping
6. review icon mapping
7. complete icon mapping
8. color mapping for review/complete
9. board-column mapping for assigned/in-progress
10. task-detail click route format
11. node-name display preference
12. deterministic sorting by file then line
**Files modified:**
- `editor/CMakeLists.txt``step435_test` target
**Verification run:**
- `step435_test` — PASS (12/12) new step coverage
- `step434_test` — PASS (12/12) regression coverage
- `step433_test` — PASS (12/12) regression coverage
**Architecture gate check:**
- `editor/src/WorkflowStatusOverlay.h` within header-size limit (`94` <= `600`)
- `editor/tests/step435_test.cpp` within test-file size guidance (`155` 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)