diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 4f557ee..e999f55 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -4135,4 +4135,13 @@ target_link_libraries(step583_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step584_test tests/step584_test.cpp) +target_include_directories(step584_test PRIVATE src) +target_link_libraries(step584_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/ValueForwardOnboardingFlow.h b/editor/src/ValueForwardOnboardingFlow.h new file mode 100644 index 0000000..2e441ad --- /dev/null +++ b/editor/src/ValueForwardOnboardingFlow.h @@ -0,0 +1,105 @@ +#pragma once +// Step 584: Value-Forward Onboarding Flow + +#include +#include + +enum class OnboardingCardType { + MCPDepth, + ConstrainedExecution, + VerificationLoop, + WorkflowRouting +}; + +struct OnboardingCard { + std::string cardId; + OnboardingCardType type = OnboardingCardType::MCPDepth; + std::string title; + std::string message; + bool completed = false; +}; + +struct OnboardingFlowState { + std::string profileId; + bool active = false; + std::size_t currentIndex = 0; + std::vector cards; +}; + +class ValueForwardOnboardingFlow { +public: + static bool start(const std::string& profileId, + OnboardingFlowState* state, + std::string* error) { + if (!state || !error) return false; + error->clear(); + if (profileId.empty()) { + *error = "profile_id_missing"; + return false; + } + + state->profileId = profileId; + state->active = true; + state->currentIndex = 0; + state->cards = defaultCards(); + return true; + } + + static bool completeCurrent(OnboardingFlowState* state, std::string* error) { + if (!state || !error) return false; + error->clear(); + if (!state->active) return fail(error, "flow_inactive"); + if (state->currentIndex >= state->cards.size()) return fail(error, "card_index_invalid"); + state->cards[state->currentIndex].completed = true; + return true; + } + + static bool next(OnboardingFlowState* state, std::string* error) { + if (!state || !error) return false; + error->clear(); + if (!state->active) return fail(error, "flow_inactive"); + if (state->currentIndex >= state->cards.size()) return fail(error, "card_index_invalid"); + if (!state->cards[state->currentIndex].completed) return fail(error, "current_card_not_completed"); + + if (state->currentIndex + 1 >= state->cards.size()) { + state->active = false; + return true; + } + ++state->currentIndex; + return true; + } + + static float progress(const OnboardingFlowState& state) { + if (state.cards.empty()) return 0.0f; + int completed = 0; + for (const auto& card : state.cards) if (card.completed) ++completed; + return static_cast(completed) / static_cast(state.cards.size()); + } + + static std::vector valueHighlights(const OnboardingFlowState& state) { + std::vector highlights; + for (const auto& card : state.cards) { + if (!card.completed) continue; + if (card.type == OnboardingCardType::MCPDepth) highlights.push_back("mcp_tool_depth"); + if (card.type == OnboardingCardType::ConstrainedExecution) highlights.push_back("safe_constrained_ops"); + if (card.type == OnboardingCardType::VerificationLoop) highlights.push_back("verify_before_merge"); + if (card.type == OnboardingCardType::WorkflowRouting) highlights.push_back("transparent_routing_gates"); + } + return highlights; + } + +private: + static bool fail(std::string* error, const char* code) { + *error = code; + return false; + } + + static std::vector defaultCards() { + return { + {"onboarding-1", OnboardingCardType::MCPDepth, "Deep Tool Reach", "Discover multi-tool MCP depth.", false}, + {"onboarding-2", OnboardingCardType::ConstrainedExecution, "Safe Execution", "Apply constrained operations with clear guardrails.", false}, + {"onboarding-3", OnboardingCardType::VerificationLoop, "Built-in Verification", "Run build/test loops before finalization.", false}, + {"onboarding-4", OnboardingCardType::WorkflowRouting, "Explainable Routing", "Understand review gates and decision routing.", false} + }; + } +}; diff --git a/editor/tests/step584_test.cpp b/editor/tests/step584_test.cpp new file mode 100644 index 0000000..2dec0ab --- /dev/null +++ b/editor/tests/step584_test.cpp @@ -0,0 +1,154 @@ +// Step 584: Value-Forward Onboarding Flow (12 tests) + +#include "ValueForwardOnboardingFlow.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_start_success() { + TEST(start_success); + OnboardingFlowState state; + std::string error; + CHECK(ValueForwardOnboardingFlow::start("profile-1", &state, &error), "start should succeed"); + CHECK(state.active, "flow should be active"); + CHECK(state.cards.size() == 4, "default card count mismatch"); + PASS(); +} + +void test_start_fails_without_profile_id() { + TEST(start_fails_without_profile_id); + OnboardingFlowState state; + std::string error; + CHECK(!ValueForwardOnboardingFlow::start("", &state, &error), "start should fail"); + CHECK(error == "profile_id_missing", "wrong error"); + PASS(); +} + +void test_complete_current_marks_card_completed() { + TEST(complete_current_marks_card_completed); + OnboardingFlowState state; + std::string error; + CHECK(ValueForwardOnboardingFlow::start("profile-1", &state, &error), "start failed"); + CHECK(ValueForwardOnboardingFlow::completeCurrent(&state, &error), "complete current failed"); + CHECK(state.cards[0].completed, "current card should be completed"); + PASS(); +} + +void test_complete_current_fails_when_inactive() { + TEST(complete_current_fails_when_inactive); + OnboardingFlowState state; + std::string error; + CHECK(!ValueForwardOnboardingFlow::completeCurrent(&state, &error), "complete should fail"); + CHECK(error == "flow_inactive", "wrong error"); + PASS(); +} + +void test_next_requires_current_completion() { + TEST(next_requires_current_completion); + OnboardingFlowState state; + std::string error; + CHECK(ValueForwardOnboardingFlow::start("profile-1", &state, &error), "start failed"); + CHECK(!ValueForwardOnboardingFlow::next(&state, &error), "next should fail"); + CHECK(error == "current_card_not_completed", "wrong error"); + PASS(); +} + +void test_next_advances_index_after_completion() { + TEST(next_advances_index_after_completion); + OnboardingFlowState state; + std::string error; + CHECK(ValueForwardOnboardingFlow::start("profile-1", &state, &error), "start failed"); + CHECK(ValueForwardOnboardingFlow::completeCurrent(&state, &error), "complete failed"); + CHECK(ValueForwardOnboardingFlow::next(&state, &error), "next failed"); + CHECK(state.currentIndex == 1, "index should advance"); + PASS(); +} + +void test_next_on_last_card_finishes_flow() { + TEST(next_on_last_card_finishes_flow); + OnboardingFlowState state; + std::string error; + CHECK(ValueForwardOnboardingFlow::start("profile-1", &state, &error), "start failed"); + for (int i = 0; i < 4; ++i) { + CHECK(ValueForwardOnboardingFlow::completeCurrent(&state, &error), "complete failed"); + CHECK(ValueForwardOnboardingFlow::next(&state, &error), "next failed"); + } + CHECK(!state.active, "flow should be inactive at end"); + PASS(); +} + +void test_progress_reflects_completion_fraction() { + TEST(progress_reflects_completion_fraction); + OnboardingFlowState state; + std::string error; + CHECK(ValueForwardOnboardingFlow::start("profile-1", &state, &error), "start failed"); + CHECK(ValueForwardOnboardingFlow::completeCurrent(&state, &error), "complete failed"); + const float p = ValueForwardOnboardingFlow::progress(state); + CHECK(p > 0.24f && p < 0.26f, "progress should be 0.25"); + PASS(); +} + +void test_progress_zero_for_empty_state() { + TEST(progress_zero_for_empty_state); + OnboardingFlowState state; + CHECK(ValueForwardOnboardingFlow::progress(state) == 0.0f, "empty progress should be zero"); + PASS(); +} + +void test_value_highlights_include_completed_card_signals() { + TEST(value_highlights_include_completed_card_signals); + OnboardingFlowState state; + std::string error; + CHECK(ValueForwardOnboardingFlow::start("profile-1", &state, &error), "start failed"); + CHECK(ValueForwardOnboardingFlow::completeCurrent(&state, &error), "complete card 1 failed"); + CHECK(ValueForwardOnboardingFlow::next(&state, &error), "next failed"); + CHECK(ValueForwardOnboardingFlow::completeCurrent(&state, &error), "complete card 2 failed"); + const auto highlights = ValueForwardOnboardingFlow::valueHighlights(state); + CHECK(highlights.size() == 2, "two highlights expected"); + CHECK(highlights[0] == "mcp_tool_depth", "first highlight mismatch"); + CHECK(highlights[1] == "safe_constrained_ops", "second highlight mismatch"); + PASS(); +} + +void test_value_highlights_empty_when_nothing_completed() { + TEST(value_highlights_empty_when_nothing_completed); + OnboardingFlowState state; + std::string error; + CHECK(ValueForwardOnboardingFlow::start("profile-1", &state, &error), "start failed"); + CHECK(ValueForwardOnboardingFlow::valueHighlights(state).empty(), "highlights should be empty"); + PASS(); +} + +void test_next_fails_when_inactive() { + TEST(next_fails_when_inactive); + OnboardingFlowState state; + std::string error; + CHECK(!ValueForwardOnboardingFlow::next(&state, &error), "next should fail"); + CHECK(error == "flow_inactive", "wrong error"); + PASS(); +} + +int main() { + std::cout << "Step 584: Value-Forward Onboarding Flow\n"; + + test_start_success(); // 1 + test_start_fails_without_profile_id(); // 2 + test_complete_current_marks_card_completed(); // 3 + test_complete_current_fails_when_inactive(); // 4 + test_next_requires_current_completion(); // 5 + test_next_advances_index_after_completion(); // 6 + test_next_on_last_card_finishes_flow(); // 7 + test_progress_reflects_completion_fraction(); // 8 + test_progress_zero_for_empty_state(); // 9 + test_value_highlights_include_completed_card_signals();// 10 + test_value_highlights_empty_when_nothing_completed();// 11 + test_next_fails_when_inactive(); // 12 + + std::cout << "\nResults: " << passed << "/" << (passed + failed) << " passed\n"; + return failed == 0 ? 0 : 1; +} diff --git a/progress.md b/progress.md index 28953ba..0b4a254 100644 --- a/progress.md +++ b/progress.md @@ -10951,3 +10951,37 @@ normalization, and acceptance test-skeleton generation. - `editor/src/RequirementNormalizationConflictDetector.h` within header-size limit (`153` <= `600`) - `editor/src/AcceptanceCriteriaBinding.h` within header-size limit (`68` <= `600`) - Shared normalization centralization reduces duplicated string-normalization logic and stays aligned with `ARCHITECTURE.md` + +### Step 584: Value-Forward Onboarding Flow +**Status:** PASS (12/12 tests) + +Implements a first-run onboarding flow that explicitly surfaces MCP depth, +constrained execution safeguards, verification, and routing transparency. + +**Files added:** +- `editor/src/ValueForwardOnboardingFlow.h` - onboarding flow module: + - profile-based first-run flow initialization + - card completion and gated forward navigation + - completion progress reporting + - completed-card value highlight extraction + - inactive/index guard behavior +- `editor/tests/step584_test.cpp` - 12 tests covering: + - flow start success/failure behavior + - completion/navigation guard behavior + - end-of-flow behavior + - progress computation behavior + - value highlight extraction behavior + +**Files modified:** +- `editor/CMakeLists.txt` - `step584_test` target + +**Verification run:** +- `cmake -S editor -B editor/build-native` - PASS +- `cmake --build editor/build-native --target step584_test step583_test` - PASS +- `./editor/build-native/step584_test` - PASS (12/12) +- `./editor/build-native/step583_test` - PASS (8/8) regression coverage + +**Architecture gate check:** +- `editor/src/ValueForwardOnboardingFlow.h` within header-size limit (`105` <= `600`) +- `editor/tests/step584_test.cpp` within test-file size guidance (`154` lines) +- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`