Step 584: add value forward onboarding flow

This commit is contained in:
Bill
2026-02-17 11:18:18 -07:00
parent 7de00387cc
commit 6d608e41b9
4 changed files with 302 additions and 0 deletions

View File

@@ -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)

View File

@@ -0,0 +1,105 @@
#pragma once
// Step 584: Value-Forward Onboarding Flow
#include <string>
#include <vector>
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<OnboardingCard> 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<float>(completed) / static_cast<float>(state.cards.size());
}
static std::vector<std::string> valueHighlights(const OnboardingFlowState& state) {
std::vector<std::string> 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<OnboardingCard> 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}
};
}
};

View File

@@ -0,0 +1,154 @@
// Step 584: Value-Forward Onboarding Flow (12 tests)
#include "ValueForwardOnboardingFlow.h"
#include <iostream>
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;
}

View File

@@ -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`