Complete Step 484: prior step context injector
This commit is contained in:
@@ -3235,4 +3235,13 @@ target_link_libraries(step483_test PRIVATE
|
||||
tree_sitter_javascript tree_sitter_typescript
|
||||
tree_sitter_java tree_sitter_rust tree_sitter_go)
|
||||
|
||||
add_executable(step484_test tests/step484_test.cpp)
|
||||
target_include_directories(step484_test PRIVATE src)
|
||||
target_link_libraries(step484_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)
|
||||
|
||||
125
editor/src/PriorStepContextInjector.h
Normal file
125
editor/src/PriorStepContextInjector.h
Normal file
@@ -0,0 +1,125 @@
|
||||
#pragma once
|
||||
|
||||
// Step 484: Prior-Step Context Injector
|
||||
// Builds a condensed API/context brief from prior step headers.
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct ApiMethodSummary {
|
||||
std::string signature;
|
||||
};
|
||||
|
||||
struct ApiTypeSummary {
|
||||
std::string kind; // class | struct
|
||||
std::string name;
|
||||
std::vector<ApiMethodSummary> methods;
|
||||
};
|
||||
|
||||
struct StepContext {
|
||||
std::vector<ApiTypeSummary> priorApis;
|
||||
std::map<std::string, std::vector<std::string>> includeDependencies;
|
||||
std::vector<std::string> namingPatterns;
|
||||
std::vector<std::string> warnings;
|
||||
|
||||
std::string condensedBrief() const {
|
||||
std::ostringstream out;
|
||||
out << "types:";
|
||||
for (const auto& t : priorApis) out << " " << t.name;
|
||||
out << " | patterns:";
|
||||
for (const auto& p : namingPatterns) out << " " << p;
|
||||
return out.str();
|
||||
}
|
||||
};
|
||||
|
||||
class PriorStepContextInjector {
|
||||
public:
|
||||
static StepContext build(const std::vector<std::string>& headerPaths) {
|
||||
StepContext out;
|
||||
std::set<std::string> patterns;
|
||||
|
||||
for (const auto& p : headerPaths) {
|
||||
std::string text = readAll(p);
|
||||
if (text.empty()) {
|
||||
out.warnings.push_back("Missing or unreadable header: " + p);
|
||||
continue;
|
||||
}
|
||||
|
||||
out.includeDependencies[p] = parseIncludes(text);
|
||||
auto types = parseTypesAndMethods(text);
|
||||
for (const auto& t : types) {
|
||||
out.priorApis.push_back(t);
|
||||
if (endsWith(t.name, "Report")) patterns.insert("Suffix:Report");
|
||||
if (endsWith(t.name, "Spec")) patterns.insert("Suffix:Spec");
|
||||
if (endsWith(t.name, "State")) patterns.insert("Suffix:State");
|
||||
}
|
||||
}
|
||||
|
||||
out.namingPatterns.assign(patterns.begin(), patterns.end());
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::string readAll(const std::string& path) {
|
||||
std::ifstream in(path);
|
||||
if (!in.is_open()) return "";
|
||||
std::ostringstream ss;
|
||||
ss << in.rdbuf();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
static bool endsWith(const std::string& v, const std::string& suffix) {
|
||||
return v.size() >= suffix.size() &&
|
||||
v.compare(v.size() - suffix.size(), suffix.size(), suffix) == 0;
|
||||
}
|
||||
|
||||
static std::vector<std::string> parseIncludes(const std::string& text) {
|
||||
std::vector<std::string> out;
|
||||
std::regex incRe(R"inc(#include\s+"([^"]+)")inc");
|
||||
auto begin = std::sregex_iterator(text.begin(), text.end(), incRe);
|
||||
auto end = std::sregex_iterator();
|
||||
for (auto it = begin; it != end; ++it) {
|
||||
out.push_back((*it)[1].str());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::vector<ApiTypeSummary> parseTypesAndMethods(const std::string& text) {
|
||||
std::vector<ApiTypeSummary> out;
|
||||
std::regex typeRe(R"(\b(class|struct)\s+([A-Za-z_][A-Za-z0-9_]*))");
|
||||
auto begin = std::sregex_iterator(text.begin(), text.end(), typeRe);
|
||||
auto end = std::sregex_iterator();
|
||||
for (auto it = begin; it != end; ++it) {
|
||||
ApiTypeSummary t;
|
||||
t.kind = (*it)[1].str();
|
||||
t.name = (*it)[2].str();
|
||||
out.push_back(std::move(t));
|
||||
}
|
||||
|
||||
std::regex methodRe(
|
||||
R"((?:static\s+)?[A-Za-z_][A-Za-z0-9_:<>\s,&\*]*\s+[A-Za-z_][A-Za-z0-9_]*\s*\([^)]*\)\s*(?:const\s*)?(?:\{|;))");
|
||||
auto mBegin = std::sregex_iterator(text.begin(), text.end(), methodRe);
|
||||
auto mEnd = std::sregex_iterator();
|
||||
|
||||
std::vector<ApiMethodSummary> methods;
|
||||
for (auto it = mBegin; it != mEnd; ++it) {
|
||||
ApiMethodSummary m;
|
||||
m.signature = std::regex_replace((*it).str(), std::regex(R"(\s+)"), " ");
|
||||
if (!m.signature.empty() &&
|
||||
(m.signature.back() == '{' || m.signature.back() == ';')) {
|
||||
m.signature.pop_back();
|
||||
}
|
||||
methods.push_back(std::move(m));
|
||||
}
|
||||
if (!out.empty()) {
|
||||
for (auto& t : out) t.methods = methods;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
120
editor/tests/step484_test.cpp
Normal file
120
editor/tests/step484_test.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
// Step 484: Prior-Step Context Injector Tests (unit, integration)
|
||||
|
||||
#include "PriorStepContextInjector.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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_extracts_type_names_from_prior_headers() {
|
||||
TEST(extracts_type_names_from_prior_headers);
|
||||
auto ctx = PriorStepContextInjector::build({
|
||||
"editor/src/StepSpecExpander.h",
|
||||
"editor/src/ProjectConventionAnalyzer.h"
|
||||
});
|
||||
bool foundStepSpec = false, foundConventions = false;
|
||||
for (const auto& t : ctx.priorApis) {
|
||||
if (t.name == "StepSpec") foundStepSpec = true;
|
||||
if (t.name == "ProjectConventions") foundConventions = true;
|
||||
}
|
||||
CHECK(foundStepSpec, "missing StepSpec");
|
||||
CHECK(foundConventions, "missing ProjectConventions");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_extracts_method_signatures_from_headers() {
|
||||
TEST(extracts_method_signatures_from_headers);
|
||||
auto ctx = PriorStepContextInjector::build({"editor/src/StepSpecExpander.h"});
|
||||
CHECK(!ctx.priorApis.empty(), "expected extracted APIs");
|
||||
bool hasExpand = false;
|
||||
for (const auto& m : ctx.priorApis[0].methods) {
|
||||
if (m.signature.find("expand(") != std::string::npos) hasExpand = true;
|
||||
}
|
||||
CHECK(hasExpand, "missing expand method signature");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_builds_include_dependency_graph() {
|
||||
TEST(builds_include_dependency_graph);
|
||||
auto path = std::string("editor/src/StepSpecExpander.h");
|
||||
auto ctx = PriorStepContextInjector::build({path});
|
||||
CHECK(ctx.includeDependencies.count(path) == 1, "missing include graph entry");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_detects_naming_patterns_from_prior_types() {
|
||||
TEST(detects_naming_patterns_from_prior_types);
|
||||
auto ctx = PriorStepContextInjector::build({
|
||||
"editor/src/StepSpecExpander.h",
|
||||
"editor/src/WorkflowState.h"
|
||||
});
|
||||
bool hasSpec = false, hasState = false;
|
||||
for (const auto& p : ctx.namingPatterns) {
|
||||
if (p == "Suffix:Spec") hasSpec = true;
|
||||
if (p == "Suffix:State") hasState = true;
|
||||
}
|
||||
CHECK(hasSpec, "expected Spec suffix pattern");
|
||||
CHECK(hasState, "expected State suffix pattern");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_condensed_brief_contains_type_and_pattern_summary() {
|
||||
TEST(condensed_brief_contains_type_and_pattern_summary);
|
||||
auto ctx = PriorStepContextInjector::build({"editor/src/StepSpecExpander.h"});
|
||||
auto brief = ctx.condensedBrief();
|
||||
CHECK(brief.find("types:") != std::string::npos, "missing types section");
|
||||
CHECK(brief.find("patterns:") != std::string::npos, "missing patterns section");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_method_signatures_are_declaration_level_not_implementation_blocks() {
|
||||
TEST(method_signatures_are_declaration_level_not_implementation_blocks);
|
||||
auto ctx = PriorStepContextInjector::build({"editor/src/StepSpecExpander.h"});
|
||||
CHECK(!ctx.priorApis.empty(), "missing api summaries");
|
||||
for (const auto& m : ctx.priorApis[0].methods) {
|
||||
CHECK(m.signature.find("{") == std::string::npos, "unexpected implementation brace");
|
||||
}
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_missing_header_path_reports_warning() {
|
||||
TEST(missing_header_path_reports_warning);
|
||||
auto ctx = PriorStepContextInjector::build({"editor/src/DOES_NOT_EXIST_484.h"});
|
||||
CHECK(!ctx.warnings.empty(), "expected missing file warning");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_integration_multiple_headers_yield_nonempty_context_brief() {
|
||||
TEST(integration_multiple_headers_yield_nonempty_context_brief);
|
||||
auto ctx = PriorStepContextInjector::build({
|
||||
"editor/src/StepSpecExpander.h",
|
||||
"editor/src/ProjectConventionAnalyzer.h",
|
||||
"editor/src/PriorStepContextInjector.h"
|
||||
});
|
||||
CHECK(ctx.priorApis.size() >= 3, "expected at least three API summaries");
|
||||
CHECK(!ctx.condensedBrief().empty(), "expected non-empty brief");
|
||||
PASS();
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Step 484: Prior-Step Context Injector Tests\n";
|
||||
|
||||
test_extracts_type_names_from_prior_headers(); // 1
|
||||
test_extracts_method_signatures_from_headers(); // 2
|
||||
test_builds_include_dependency_graph(); // 3
|
||||
test_detects_naming_patterns_from_prior_types(); // 4
|
||||
test_condensed_brief_contains_type_and_pattern_summary(); // 5
|
||||
test_method_signatures_are_declaration_level_not_implementation_blocks(); // 6
|
||||
test_missing_header_path_reports_warning(); // 7
|
||||
test_integration_multiple_headers_yield_nonempty_context_brief(); // 8
|
||||
|
||||
std::cout << "\nResults: " << passed << "/" << (passed + failed)
|
||||
<< " passed\n";
|
||||
return failed == 0 ? 0 : 1;
|
||||
}
|
||||
37
progress.md
37
progress.md
@@ -6951,3 +6951,40 @@ worker output conformance.
|
||||
- `editor/src/ProjectConventionAnalyzer.h` within header-size limit (`223` <= `600`)
|
||||
- `editor/tests/step483_test.cpp` within test-file size guidance (`146` lines)
|
||||
- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`
|
||||
|
||||
### Step 484: Prior-Step Context Injector
|
||||
**Status:** PASS (8/8 tests)
|
||||
|
||||
Adds prior-step API/context injection so worker-facing briefs can include
|
||||
condensed type/method summaries and include dependencies instead of full
|
||||
headers.
|
||||
|
||||
**Files added:**
|
||||
- `editor/src/PriorStepContextInjector.h` — context extraction layer:
|
||||
- `ApiTypeSummary`, `ApiMethodSummary`, `StepContext`
|
||||
- `build(headerPaths)`:
|
||||
- parses class/struct names from prior headers
|
||||
- extracts declaration/definition-level method signatures
|
||||
- records include dependency graph from `#include "..."` lines
|
||||
- infers naming patterns from type suffixes (`Spec`, `State`, `Report`)
|
||||
- emits warnings for missing/unreadable headers
|
||||
- `condensedBrief()` for prompt-sized worker context summaries
|
||||
- `editor/tests/step484_test.cpp` — 8 tests (unit/integration) covering:
|
||||
- type extraction from real prior-step headers
|
||||
- method signature extraction and declaration-only shape checks
|
||||
- include graph construction
|
||||
- naming pattern detection
|
||||
- condensed brief content
|
||||
- missing-header warning behavior
|
||||
- multi-header integration behavior
|
||||
- `editor/CMakeLists.txt` — `step484_test` target
|
||||
|
||||
**Verification run:**
|
||||
- `cmake --build editor/build-native --target step484_test step483_test` — PASS
|
||||
- `./editor/build-native/step484_test` — PASS (8/8)
|
||||
- `./editor/build-native/step483_test` — PASS (9/9) regression coverage
|
||||
|
||||
**Architecture gate check:**
|
||||
- `editor/src/PriorStepContextInjector.h` within header-size limit (`125` <= `600`)
|
||||
- `editor/tests/step484_test.cpp` within test-file size guidance (`120` lines)
|
||||
- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`
|
||||
|
||||
Reference in New Issue
Block a user