diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index ace51e9..62924ab 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -3775,4 +3775,13 @@ target_link_libraries(step543_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step544_test tests/step544_test.cpp) +target_include_directories(step544_test PRIVATE src) +target_link_libraries(step544_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/ConstrainedRoutingRulesetExtension.h b/editor/src/ConstrainedRoutingRulesetExtension.h new file mode 100644 index 0000000..d9998db --- /dev/null +++ b/editor/src/ConstrainedRoutingRulesetExtension.h @@ -0,0 +1,60 @@ +#pragma once +// Step 544: Constrained Routing Ruleset Extension + +#include +#include + +struct ConstrainedRoutingInput { + std::string taskClass; + double legalChoiceConfidence = 0.0; + bool deterministicTemplateAvailable = false; + bool constrainedMode = true; + bool hasRecentPostApplyFailures = false; +}; + +struct ConstrainedRoutingDecision { + std::string route; // deterministic_template/constrained_worker/general_worker/escalate + double confidence = 0.0; + std::vector reasons; +}; + +class ConstrainedRoutingRulesetExtension { +public: + static ConstrainedRoutingDecision decide(const ConstrainedRoutingInput& input) { + ConstrainedRoutingDecision out; + + if (!input.constrainedMode) { + out.route = "general_worker"; + out.confidence = 0.70; + out.reasons.push_back("constrained_mode_disabled"); + return out; + } + + if (input.hasRecentPostApplyFailures) { + out.route = "escalate"; + out.confidence = 0.55; + out.reasons.push_back("recent_post_apply_failures"); + return out; + } + + if (input.deterministicTemplateAvailable && input.legalChoiceConfidence >= 0.85) { + out.route = "deterministic_template"; + out.confidence = input.legalChoiceConfidence; + out.reasons.push_back("high_legal_choice_confidence"); + out.reasons.push_back("template_available"); + return out; + } + + if (input.legalChoiceConfidence >= 0.60) { + out.route = "constrained_worker"; + out.confidence = input.legalChoiceConfidence; + out.reasons.push_back("moderate_legal_choice_confidence"); + return out; + } + + out.route = "general_worker"; + out.confidence = input.legalChoiceConfidence; + out.reasons.push_back("low_legal_choice_confidence"); + return out; + } +}; diff --git a/editor/tests/step544_test.cpp b/editor/tests/step544_test.cpp new file mode 100644 index 0000000..2be9a90 --- /dev/null +++ b/editor/tests/step544_test.cpp @@ -0,0 +1,126 @@ +// Step 544: Constrained Routing Ruleset Extension (12 tests) + +#include "ConstrainedRoutingRulesetExtension.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 bool hasReason(const ConstrainedRoutingDecision& d, const std::string& reason) { + for (const auto& r : d.reasons) if (r == reason) return true; + return false; +} + +void test_non_constrained_mode_routes_general_worker() { + TEST(non_constrained_mode_routes_general_worker); + auto d = ConstrainedRoutingRulesetExtension::decide({"rename", 0.9, true, false, false}); + CHECK(d.route == "general_worker", "should route general when constrained off"); + CHECK(hasReason(d, "constrained_mode_disabled"), "reason expected"); + PASS(); +} + +void test_recent_failures_force_escalation() { + TEST(recent_failures_force_escalation); + auto d = ConstrainedRoutingRulesetExtension::decide({"rename", 0.95, true, true, true}); + CHECK(d.route == "escalate", "recent failures should escalate"); + CHECK(hasReason(d, "recent_post_apply_failures"), "reason expected"); + PASS(); +} + +void test_high_confidence_with_template_promotes_deterministic_route() { + TEST(high_confidence_with_template_promotes_deterministic_route); + auto d = ConstrainedRoutingRulesetExtension::decide({"rename", 0.92, true, true, false}); + CHECK(d.route == "deterministic_template", "high confidence + template should promote deterministic route"); + PASS(); +} + +void test_high_confidence_without_template_routes_constrained_worker() { + TEST(high_confidence_without_template_routes_constrained_worker); + auto d = ConstrainedRoutingRulesetExtension::decide({"rename", 0.92, false, true, false}); + CHECK(d.route == "constrained_worker", "without template should route constrained worker"); + PASS(); +} + +void test_moderate_confidence_routes_constrained_worker() { + TEST(moderate_confidence_routes_constrained_worker); + auto d = ConstrainedRoutingRulesetExtension::decide({"update", 0.70, true, true, false}); + CHECK(d.route == "constrained_worker", "moderate confidence should route constrained worker"); + PASS(); +} + +void test_low_confidence_routes_general_worker() { + TEST(low_confidence_routes_general_worker); + auto d = ConstrainedRoutingRulesetExtension::decide({"update", 0.40, true, true, false}); + CHECK(d.route == "general_worker", "low confidence should route general worker"); + CHECK(hasReason(d, "low_legal_choice_confidence"), "reason expected"); + PASS(); +} + +void test_template_threshold_is_inclusive_at_085() { + TEST(template_threshold_is_inclusive_at_085); + auto d = ConstrainedRoutingRulesetExtension::decide({"update", 0.85, true, true, false}); + CHECK(d.route == "deterministic_template", "0.85 should qualify for template route"); + PASS(); +} + +void test_constrained_worker_threshold_is_inclusive_at_060() { + TEST(constrained_worker_threshold_is_inclusive_at_060); + auto d = ConstrainedRoutingRulesetExtension::decide({"update", 0.60, false, true, false}); + CHECK(d.route == "constrained_worker", "0.60 should qualify for constrained worker"); + PASS(); +} + +void test_escalation_precedence_over_template_promotion() { + TEST(escalation_precedence_over_template_promotion); + auto d = ConstrainedRoutingRulesetExtension::decide({"update", 0.99, true, true, true}); + CHECK(d.route == "escalate", "escalation should take precedence over template promotion"); + PASS(); +} + +void test_confidence_propagates_for_constrained_worker_route() { + TEST(confidence_propagates_for_constrained_worker_route); + auto d = ConstrainedRoutingRulesetExtension::decide({"update", 0.73, false, true, false}); + CHECK(d.route == "constrained_worker", "route mismatch"); + CHECK(d.confidence > 0.72 && d.confidence < 0.74, "confidence should match input for constrained route"); + PASS(); +} + +void test_confidence_propagates_for_template_route() { + TEST(confidence_propagates_for_template_route); + auto d = ConstrainedRoutingRulesetExtension::decide({"update", 0.89, true, true, false}); + CHECK(d.route == "deterministic_template", "route mismatch"); + CHECK(d.confidence > 0.88 && d.confidence < 0.90, "confidence should match input for template route"); + PASS(); +} + +void test_reasons_include_template_and_confidence_tags() { + TEST(reasons_include_template_and_confidence_tags); + auto d = ConstrainedRoutingRulesetExtension::decide({"update", 0.89, true, true, false}); + CHECK(hasReason(d, "high_legal_choice_confidence"), "high confidence reason expected"); + CHECK(hasReason(d, "template_available"), "template reason expected"); + PASS(); +} + +int main() { + std::cout << "Step 544: Constrained Routing Ruleset Extension\n"; + + test_non_constrained_mode_routes_general_worker(); // 1 + test_recent_failures_force_escalation(); // 2 + test_high_confidence_with_template_promotes_deterministic_route(); // 3 + test_high_confidence_without_template_routes_constrained_worker(); // 4 + test_moderate_confidence_routes_constrained_worker(); // 5 + test_low_confidence_routes_general_worker(); // 6 + test_template_threshold_is_inclusive_at_085(); // 7 + test_constrained_worker_threshold_is_inclusive_at_060(); // 8 + test_escalation_precedence_over_template_promotion(); // 9 + test_confidence_propagates_for_constrained_worker_route(); // 10 + test_confidence_propagates_for_template_route(); // 11 + test_reasons_include_template_and_confidence_tags(); // 12 + + std::cout << "\nResults: " << passed << "/" << (passed + failed) << " passed\n"; + return failed == 0 ? 0 : 1; +} diff --git a/progress.md b/progress.md index 7e8cd10..1f497e4 100644 --- a/progress.md +++ b/progress.md @@ -9360,3 +9360,39 @@ re-validate architecture constraints without behavior changes. - `./editor/build-native/step541_test` - PASS (12/12) - `./editor/build-native/step542_test` - PASS (12/12) - `./editor/build-native/step543_test` - PASS (8/8) + +### Step 544: Constrained Routing Ruleset Extension +**Status:** PASS (12/12 tests) + +Implements constrained-mode routing policy extensions with deterministic +template promotion at high legal-choice confidence and explicit precedence for +post-apply failure escalation. + +**Files added:** +- `editor/src/ConstrainedRoutingRulesetExtension.h` - constrained routing module: + - constrained/non-constrained mode branching + - failure-first escalation for unstable recent outcomes + - deterministic template promotion for high-confidence legal-choice paths + - constrained-worker routing for moderate confidence + - low-confidence fallback to general worker +- `editor/tests/step544_test.cpp` - 12 tests covering: + - constrained-mode and non-constrained routing behavior + - escalation precedence behavior + - template promotion thresholds + - constrained-worker thresholds + - confidence propagation behavior + - rationale-tag emission behavior + +**Files modified:** +- `editor/CMakeLists.txt` - `step544_test` target + +**Verification run:** +- `cmake -S editor -B editor/build-native` - PASS +- `cmake --build editor/build-native --target step544_test step543_test` - PASS +- `./editor/build-native/step544_test` - PASS (12/12) +- `./editor/build-native/step543_test` - PASS (8/8) regression coverage + +**Architecture gate check:** +- `editor/src/ConstrainedRoutingRulesetExtension.h` within header-size limit (`60` <= `600`) +- `editor/tests/step544_test.cpp` within test-file size guidance (`126` lines) +- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`