From 6536d913e510084bb2e46c08d5a1b1eaf634cf32 Mon Sep 17 00:00:00 2001 From: Bill Date: Tue, 17 Feb 2026 10:39:14 -0700 Subject: [PATCH] Step 562: add workflow-aware debug hooks model --- editor/CMakeLists.txt | 9 ++ editor/src/WorkflowAwareDebugHooks.h | 77 ++++++++++++++++ editor/tests/step562_test.cpp | 133 +++++++++++++++++++++++++++ progress.md | 34 +++++++ 4 files changed, 253 insertions(+) create mode 100644 editor/src/WorkflowAwareDebugHooks.h create mode 100644 editor/tests/step562_test.cpp diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 0c34f5a..1f1a6a6 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -3937,4 +3937,13 @@ target_link_libraries(step561_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step562_test tests/step562_test.cpp) +target_include_directories(step562_test PRIVATE src) +target_link_libraries(step562_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/src/WorkflowAwareDebugHooks.h b/editor/src/WorkflowAwareDebugHooks.h new file mode 100644 index 0000000..3feee77 --- /dev/null +++ b/editor/src/WorkflowAwareDebugHooks.h @@ -0,0 +1,77 @@ +#pragma once +// Step 562: Workflow-Aware Debug Hooks + +#include +#include +#include + +struct WorkflowDebugBinding { + std::string workflowItemId; + std::string debugSessionId; + std::vector diagnosticIds; + bool active = true; +}; + +class WorkflowAwareDebugHooks { +public: + bool attach(const std::string& workflowItemId, + const std::string& debugSessionId, + const std::vector& diagnosticIds) { + if (workflowItemId.empty() || debugSessionId.empty()) return false; + if (bindings_.count(workflowItemId) != 0) return false; + bindings_[workflowItemId] = {workflowItemId, debugSessionId, diagnosticIds, true}; + return true; + } + + bool detach(const std::string& workflowItemId) { + auto it = bindings_.find(workflowItemId); + if (it == bindings_.end()) return false; + bindings_.erase(it); + return true; + } + + bool setActive(const std::string& workflowItemId, bool active) { + auto* b = find(workflowItemId); + if (!b) return false; + b->active = active; + return true; + } + + bool addDiagnostic(const std::string& workflowItemId, + const std::string& diagnosticId) { + auto* b = find(workflowItemId); + if (!b || diagnosticId.empty()) return false; + for (const auto& d : b->diagnosticIds) { + if (d == diagnosticId) return false; + } + b->diagnosticIds.push_back(diagnosticId); + return true; + } + + std::vector activeBindings() const { + std::vector out; + for (const auto& kv : bindings_) { + if (kv.second.active) out.push_back(kv.second); + } + return out; + } + + bool isAttached(const std::string& workflowItemId) const { + return bindings_.count(workflowItemId) != 0; + } + + std::vector diagnosticsFor(const std::string& workflowItemId) const { + auto it = bindings_.find(workflowItemId); + if (it == bindings_.end()) return {}; + return it->second.diagnosticIds; + } + +private: + std::map bindings_; + + WorkflowDebugBinding* find(const std::string& workflowItemId) { + auto it = bindings_.find(workflowItemId); + if (it == bindings_.end()) return nullptr; + return &it->second; + } +}; diff --git a/editor/tests/step562_test.cpp b/editor/tests/step562_test.cpp new file mode 100644 index 0000000..fa78e15 --- /dev/null +++ b/editor/tests/step562_test.cpp @@ -0,0 +1,133 @@ +// Step 562: Workflow-Aware Debug Hooks (12 tests) + +#include "WorkflowAwareDebugHooks.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 {} + +void test_attach_binding_succeeds() { + TEST(attach_binding_succeeds); + WorkflowAwareDebugHooks h; + CHECK(h.attach("wi-1", "ds-1", {"d1"}), "attach should succeed"); + CHECK(h.isAttached("wi-1"), "binding should exist"); + PASS(); +} + +void test_attach_rejects_duplicate_workflow_item() { + TEST(attach_rejects_duplicate_workflow_item); + WorkflowAwareDebugHooks h; + CHECK(h.attach("wi-1", "ds-1", {"d1"}), "first attach should succeed"); + CHECK(!h.attach("wi-1", "ds-2", {"d2"}), "duplicate workflow item should fail"); + PASS(); +} + +void test_attach_rejects_missing_ids() { + TEST(attach_rejects_missing_ids); + WorkflowAwareDebugHooks h; + CHECK(!h.attach("", "ds-1", {}), "missing workflow id should fail"); + CHECK(!h.attach("wi-1", "", {}), "missing session id should fail"); + PASS(); +} + +void test_detach_removes_binding() { + TEST(detach_removes_binding); + WorkflowAwareDebugHooks h; + CHECK(h.attach("wi-1", "ds-1", {"d1"}), "attach should succeed"); + CHECK(h.detach("wi-1"), "detach should succeed"); + CHECK(!h.isAttached("wi-1"), "binding should be removed"); + PASS(); +} + +void test_detach_fails_for_unknown_item() { + TEST(detach_fails_for_unknown_item); + WorkflowAwareDebugHooks h; + CHECK(!h.detach("missing"), "detach unknown should fail"); + PASS(); +} + +void test_set_active_toggles_binding_state() { + TEST(set_active_toggles_binding_state); + WorkflowAwareDebugHooks h; + CHECK(h.attach("wi-1", "ds-1", {"d1"}), "attach should succeed"); + CHECK(h.setActive("wi-1", false), "set inactive should succeed"); + CHECK(h.activeBindings().empty(), "no active bindings expected"); + CHECK(h.setActive("wi-1", true), "set active should succeed"); + CHECK(h.activeBindings().size() == 1, "one active binding expected"); + PASS(); +} + +void test_set_active_fails_for_unknown_item() { + TEST(set_active_fails_for_unknown_item); + WorkflowAwareDebugHooks h; + CHECK(!h.setActive("missing", true), "set active unknown should fail"); + PASS(); +} + +void test_add_diagnostic_appends_unique_diagnostic() { + TEST(add_diagnostic_appends_unique_diagnostic); + WorkflowAwareDebugHooks h; + CHECK(h.attach("wi-1", "ds-1", {"d1"}), "attach should succeed"); + CHECK(h.addDiagnostic("wi-1", "d2"), "add diagnostic should succeed"); + auto d = h.diagnosticsFor("wi-1"); + CHECK(d.size() == 2, "two diagnostics expected"); + PASS(); +} + +void test_add_diagnostic_rejects_duplicate() { + TEST(add_diagnostic_rejects_duplicate); + WorkflowAwareDebugHooks h; + CHECK(h.attach("wi-1", "ds-1", {"d1"}), "attach should succeed"); + CHECK(!h.addDiagnostic("wi-1", "d1"), "duplicate diagnostic should fail"); + PASS(); +} + +void test_add_diagnostic_fails_for_unknown_binding() { + TEST(add_diagnostic_fails_for_unknown_binding); + WorkflowAwareDebugHooks h; + CHECK(!h.addDiagnostic("missing", "d1"), "unknown binding should fail"); + PASS(); +} + +void test_active_bindings_returns_only_active() { + TEST(active_bindings_returns_only_active); + WorkflowAwareDebugHooks h; + CHECK(h.attach("wi-1", "ds-1", {"d1"}), "attach wi-1"); + CHECK(h.attach("wi-2", "ds-2", {"d2"}), "attach wi-2"); + CHECK(h.setActive("wi-2", false), "deactivate wi-2"); + auto active = h.activeBindings(); + CHECK(active.size() == 1, "only one active expected"); + CHECK(active[0].workflowItemId == "wi-1", "active item mismatch"); + PASS(); +} + +void test_diagnostics_for_unknown_item_empty() { + TEST(diagnostics_for_unknown_item_empty); + WorkflowAwareDebugHooks h; + CHECK(h.diagnosticsFor("missing").empty(), "unknown diagnostics should be empty"); + PASS(); +} + +int main() { + std::cout << "Step 562: Workflow-Aware Debug Hooks\n"; + + test_attach_binding_succeeds(); // 1 + test_attach_rejects_duplicate_workflow_item(); // 2 + test_attach_rejects_missing_ids(); // 3 + test_detach_removes_binding(); // 4 + test_detach_fails_for_unknown_item(); // 5 + test_set_active_toggles_binding_state(); // 6 + test_set_active_fails_for_unknown_item(); // 7 + test_add_diagnostic_appends_unique_diagnostic(); // 8 + test_add_diagnostic_rejects_duplicate(); // 9 + test_add_diagnostic_fails_for_unknown_binding(); // 10 + test_active_bindings_returns_only_active(); // 11 + test_diagnostics_for_unknown_item_empty(); // 12 + + std::cout << "\nResults: " << passed << "/" << (passed + failed) << " passed\n"; + return failed == 0 ? 0 : 1; +} diff --git a/progress.md b/progress.md index e7a6514..92b1c55 100644 --- a/progress.md +++ b/progress.md @@ -10044,3 +10044,37 @@ state transitions with session/type/window query support. - `editor/src/TraceTimelineModel.h` within header-size limit (`58` <= `600`) - `editor/tests/step561_test.cpp` within test-file size guidance (`144` lines) - Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md` + +### Step 562: Workflow-Aware Debug Hooks +**Status:** PASS (12/12 tests) + +Implements workflow-aware debug hooks that bind debug sessions to active +workflow items and diagnostic IDs for coordinated runtime/debug context. + +**Files added:** +- `editor/src/WorkflowAwareDebugHooks.h` - workflow debug hook model: + - workflow-item to debug-session attachment lifecycle + - active/inactive binding state toggles + - diagnostic linkage management per workflow item + - active-binding and diagnostic query helpers +- `editor/tests/step562_test.cpp` - 12 tests covering: + - attach/detach lifecycle behavior + - duplicate/invalid attach guards + - active state toggle behavior + - diagnostic add/duplicate/unknown behavior + - active-binding filtering behavior + - unknown diagnostic query behavior + +**Files modified:** +- `editor/CMakeLists.txt` - `step562_test` target + +**Verification run:** +- `cmake -S editor -B editor/build-native` - PASS +- `cmake --build editor/build-native --target step562_test step561_test` - PASS +- `./editor/build-native/step562_test` - PASS (12/12) +- `./editor/build-native/step561_test` - PASS (12/12) regression coverage + +**Architecture gate check:** +- `editor/src/WorkflowAwareDebugHooks.h` within header-size limit (`77` <= `600`) +- `editor/tests/step562_test.cpp` within test-file size guidance (`133` lines) +- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`