From 31cb1c8bff959bb9ccff518bebdfea012b668487 Mon Sep 17 00:00:00 2001 From: Bill Date: Tue, 17 Feb 2026 11:08:37 -0700 Subject: [PATCH] Step 578: integrate phase 32a markdown intake pipeline --- editor/CMakeLists.txt | 9 ++ editor/src/Phase32aIntegration.h | 104 +++++++++++++++++++++++ editor/tests/step578_test.cpp | 139 +++++++++++++++++++++++++++++++ progress.md | 37 ++++++++ 4 files changed, 289 insertions(+) create mode 100644 editor/src/Phase32aIntegration.h create mode 100644 editor/tests/step578_test.cpp diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index caceff7..a7ed83f 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -4081,4 +4081,13 @@ target_link_libraries(step577_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step578_test tests/step578_test.cpp) +target_include_directories(step578_test PRIVATE src) +target_link_libraries(step578_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/Phase32aIntegration.h b/editor/src/Phase32aIntegration.h new file mode 100644 index 0000000..1fc16ff --- /dev/null +++ b/editor/src/Phase32aIntegration.h @@ -0,0 +1,104 @@ +#pragma once +// Step 578: Phase 32a Integration + +#include "ArchitectReviewSurface.h" +#include "MarkdownSpecParser.h" +#include "RequirementNormalizationConflictDetector.h" +#include "ScopeMilestoneDecomposer.h" + +#include +#include + +struct Phase32aInput { + std::string markdown; + std::string reviewer = "architect"; + bool autoApproveConstraints = true; + bool forceRejectFirstConstraint = false; +}; + +struct Phase32aResult { + bool parseReady = false; + bool normalizationReady = false; + bool decompositionReady = false; + bool reviewReady = false; + bool phase32aPass = false; + std::size_t parsedSectionCount = 0; + std::size_t conflictCount = 0; + int overallUncertainty = 0; + ConstraintReviewSummary reviewSummary; + std::vector notes; +}; + +class Phase32aIntegration { +public: + static Phase32aResult run(const Phase32aInput& input) { + Phase32aResult result; + ParsedMarkdownSpec parsed; + std::string error; + if (!MarkdownSpecParser::parse(input.markdown, &parsed, &error)) { + result.notes.push_back("fail:markdown_parse"); + return finalize(result); + } + result.parseReady = true; + result.parsedSectionCount = parsed.sections.size(); + + RequirementNormalizationResult normalized; + if (!RequirementNormalizationConflictDetector::normalize(parsed, &normalized, &error)) { + result.notes.push_back("fail:normalization"); + return finalize(result); + } + result.normalizationReady = true; + result.conflictCount = normalized.conflicts.size(); + + DecomposedScopePlan plan; + if (!ScopeMilestoneDecomposer::decompose(normalized, &plan, &error)) { + result.notes.push_back("fail:decomposition"); + return finalize(result); + } + result.decompositionReady = true; + result.overallUncertainty = plan.overallUncertainty; + + ArchitectReviewSurface review; + if (!review.loadConstraints(normalized.requirements, &error)) { + result.notes.push_back("fail:review_load"); + return finalize(result); + } + + const auto items = review.items(); + for (std::size_t i = 0; i < items.size(); ++i) { + const auto& item = items[i]; + bool ok = false; + if (input.forceRejectFirstConstraint && i == 0) { + ok = review.reject(item.requirementId, input.reviewer, &error); + } else if (input.autoApproveConstraints) { + ok = review.approve(item.requirementId, input.reviewer, &error); + } else { + ok = review.modify(item.requirementId, item.originalText + " reviewed", input.reviewer, &error); + } + if (!ok) { + result.notes.push_back("fail:review_decision"); + return finalize(result); + } + } + + result.reviewSummary = review.summarize(); + result.reviewReady = result.reviewSummary.allDecided; + if (!result.reviewReady) { + result.notes.push_back("fail:review_incomplete"); + return finalize(result); + } + + result.notes.push_back("phase32a:markdown_intake_cycle_ready"); + return finalize(result); + } + +private: + static Phase32aResult finalize(Phase32aResult result) { + result.phase32aPass = result.parseReady && + result.normalizationReady && + result.decompositionReady && + result.reviewReady; + if (!result.phase32aPass) result.notes.push_back("phase32a:blocked"); + return result; + } +}; diff --git a/editor/tests/step578_test.cpp b/editor/tests/step578_test.cpp new file mode 100644 index 0000000..87b5be4 --- /dev/null +++ b/editor/tests/step578_test.cpp @@ -0,0 +1,139 @@ +// Step 578: Phase 32a Integration (8 tests) + +#include "Phase32aIntegration.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 hasNote(const Phase32aResult& r, const std::string& note) { + for (const auto& n : r.notes) if (n == note) return true; + return false; +} + +static std::string validMarkdown() { + return R"(## Goals +- Improve intake reliability + +## Constraints +- Enable deterministic parsing +- Do not enable deterministic parsing + +## Dependencies +- CMake + +## Acceptance Criteria +- Parse all required sections +)"; +} + +void test_full_cycle_passes_with_auto_approval() { + TEST(full_cycle_passes_with_auto_approval); + Phase32aInput input; + input.markdown = validMarkdown(); + auto result = Phase32aIntegration::run(input); + CHECK(result.phase32aPass, "phase should pass"); + CHECK(result.parseReady, "parse should be ready"); + CHECK(result.normalizationReady, "normalization should be ready"); + CHECK(result.decompositionReady, "decomposition should be ready"); + CHECK(result.reviewReady, "review should be ready"); + CHECK(hasNote(result, "phase32a:markdown_intake_cycle_ready"), "success note missing"); + PASS(); +} + +void test_parse_failure_blocks_phase() { + TEST(parse_failure_blocks_phase); + Phase32aInput input; + input.markdown = ""; + auto result = Phase32aIntegration::run(input); + CHECK(!result.phase32aPass, "phase should fail"); + CHECK(hasNote(result, "fail:markdown_parse"), "parse failure note expected"); + PASS(); +} + +void test_normalization_failure_blocks_phase() { + TEST(normalization_failure_blocks_phase); + Phase32aInput input; + input.markdown = "## Notes\ntext only"; + auto result = Phase32aIntegration::run(input); + CHECK(!result.phase32aPass, "phase should fail"); + CHECK(hasNote(result, "fail:normalization"), "normalization failure note expected"); + PASS(); +} + +void test_review_load_failure_when_no_constraints() { + TEST(review_load_failure_when_no_constraints); + Phase32aInput input; + input.markdown = R"(## Goals +- goal +## Acceptance Criteria +- done +)"; + auto result = Phase32aIntegration::run(input); + CHECK(!result.phase32aPass, "phase should fail"); + CHECK(hasNote(result, "fail:review_load"), "review load failure note expected"); + PASS(); +} + +void test_modify_mode_passes_and_counts_modified() { + TEST(modify_mode_passes_and_counts_modified); + Phase32aInput input; + input.markdown = validMarkdown(); + input.autoApproveConstraints = false; + auto result = Phase32aIntegration::run(input); + CHECK(result.phase32aPass, "phase should pass"); + CHECK(result.reviewSummary.modified == 2, "modified count mismatch"); + PASS(); +} + +void test_force_reject_first_constraint_counts_rejected() { + TEST(force_reject_first_constraint_counts_rejected); + Phase32aInput input; + input.markdown = validMarkdown(); + input.forceRejectFirstConstraint = true; + auto result = Phase32aIntegration::run(input); + CHECK(result.phase32aPass, "phase should still pass"); + CHECK(result.reviewSummary.rejected == 1, "one rejected expected"); + CHECK(result.reviewSummary.approved == 1, "one approved expected"); + PASS(); +} + +void test_conflict_count_exposed() { + TEST(conflict_count_exposed); + Phase32aInput input; + input.markdown = validMarkdown(); + auto result = Phase32aIntegration::run(input); + CHECK(result.phase32aPass, "phase should pass"); + CHECK(result.conflictCount >= 1, "conflict count should be exposed"); + PASS(); +} + +void test_parsed_section_count_exposed() { + TEST(parsed_section_count_exposed); + Phase32aInput input; + input.markdown = validMarkdown(); + auto result = Phase32aIntegration::run(input); + CHECK(result.phase32aPass, "phase should pass"); + CHECK(result.parsedSectionCount == 4, "section count mismatch"); + PASS(); +} + +int main() { + std::cout << "Step 578: Phase 32a Integration\n"; + + test_full_cycle_passes_with_auto_approval(); // 1 + test_parse_failure_blocks_phase(); // 2 + test_normalization_failure_blocks_phase(); // 3 + test_review_load_failure_when_no_constraints(); // 4 + test_modify_mode_passes_and_counts_modified(); // 5 + test_force_reject_first_constraint_counts_rejected(); // 6 + test_conflict_count_exposed(); // 7 + test_parsed_section_count_exposed(); // 8 + + std::cout << "\nResults: " << passed << "/" << (passed + failed) << " passed\n"; + return failed == 0 ? 0 : 1; +} diff --git a/progress.md b/progress.md index 319ba7c..f774d69 100644 --- a/progress.md +++ b/progress.md @@ -10696,3 +10696,40 @@ enabling approve/modify/reject decisions before task generation. - `editor/src/ArchitectReviewSurface.h` within header-size limit (`126` <= `600`) - `editor/tests/step577_test.cpp` within test-file size guidance (`178` lines) - Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md` + +### Step 578: Phase 32a Integration +**Status:** PASS (8/8 tests) + +Integrates the Phase 32a markdown intake pipeline across parser, +normalization/conflict detection, decomposition, and architect review workflow. + +**Files added:** +- `editor/src/Phase32aIntegration.h` - Phase 32a integration module: + - markdown parse + normalization orchestration + - conflict and uncertainty signal propagation + - decomposition and review-load gating + - review decision automation modes (approve/modify/reject-first) + - phase readiness and failure-note synthesis +- `editor/tests/step578_test.cpp` - 8 tests covering: + - full-cycle happy path behavior + - parse/normalize/review-load failure behavior + - modify/reject review decision behavior + - conflict count and parsed section count exposure behavior + +**Files modified:** +- `editor/CMakeLists.txt` - `step578_test` target + +**Verification run:** +- `cmake -S editor -B editor/build-native` - PASS +- `cmake --build editor/build-native --target step578_test step577_test` - PASS +- `./editor/build-native/step578_test` - PASS (8/8) +- `./editor/build-native/step577_test` - PASS (12/12) regression coverage + +**Architecture gate check:** +- `editor/src/Phase32aIntegration.h` within header-size limit (`104` <= `600`) +- `editor/tests/step578_test.cpp` within test-file size guidance (`139` lines) +- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md` + +**Phase 32a totals (574-578):** +- **Steps completed:** 5 +- **New tests in this phase plan:** 56/56 passing