Step 578: integrate phase 32a markdown intake pipeline
This commit is contained in:
@@ -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)
|
||||
|
||||
104
editor/src/Phase32aIntegration.h
Normal file
104
editor/src/Phase32aIntegration.h
Normal file
@@ -0,0 +1,104 @@
|
||||
#pragma once
|
||||
// Step 578: Phase 32a Integration
|
||||
|
||||
#include "ArchitectReviewSurface.h"
|
||||
#include "MarkdownSpecParser.h"
|
||||
#include "RequirementNormalizationConflictDetector.h"
|
||||
#include "ScopeMilestoneDecomposer.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<std::string> 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;
|
||||
}
|
||||
};
|
||||
139
editor/tests/step578_test.cpp
Normal file
139
editor/tests/step578_test.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
// Step 578: Phase 32a Integration (8 tests)
|
||||
|
||||
#include "Phase32aIntegration.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 {}
|
||||
|
||||
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;
|
||||
}
|
||||
37
progress.md
37
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
|
||||
|
||||
Reference in New Issue
Block a user