Complete step475 architect review interface with tests

This commit is contained in:
Bill
2026-02-16 20:56:12 -07:00
parent fe490c1b1a
commit 07c629b88b
4 changed files with 450 additions and 0 deletions

View File

@@ -3154,4 +3154,13 @@ target_link_libraries(step474_test PRIVATE
tree_sitter_javascript tree_sitter_typescript
tree_sitter_java tree_sitter_rust tree_sitter_go)
add_executable(step475_test tests/step475_test.cpp)
target_include_directories(step475_test PRIVATE src)
target_link_libraries(step475_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,231 @@
#pragma once
// Step 475: Architect Review Interface
// Structured review state and mutation operations over proposed stack/skeleton.
#include "ArchitectSkeletonGenerator.h"
#include <algorithm>
#include <map>
#include <set>
#include <string>
#include <vector>
struct ModuleReviewSummary {
std::string moduleName;
std::string language;
int functionCount = 0;
int dependencyCount = 0;
std::vector<std::string> keyAnnotations;
};
struct ArchitectReviewSnapshot {
std::vector<ModuleReviewSummary> modules;
std::vector<ModuleEdge> dependencyGraph;
std::vector<std::string> stackRationales;
};
struct ArchitectReviewState {
SkeletonProjectSpec skeleton;
TechStackDecision stack;
ModuleGraph moduleGraph;
bool approved = false;
std::vector<std::string> changeLog;
};
class ArchitectReviewInterface {
public:
static ArchitectReviewState initialize(const SkeletonProjectSpec& skeleton,
const TechStackDecision& stack,
const ModuleGraph& graph) {
ArchitectReviewState st;
st.skeleton = skeleton;
st.stack = stack;
st.moduleGraph = graph;
st.approved = false;
return st;
}
static ArchitectReviewSnapshot snapshot(const ArchitectReviewState& st) {
ArchitectReviewSnapshot snap;
snap.dependencyGraph = st.moduleGraph.edges;
for (const auto& m : st.skeleton.modules) {
ModuleReviewSummary s;
s.moduleName = m.moduleName;
s.language = m.language;
s.functionCount = static_cast<int>(m.functions.size());
s.dependencyCount = static_cast<int>(m.dependencies.size());
s.keyAnnotations = collectKeyAnnotations(m);
snap.modules.push_back(std::move(s));
}
for (const auto& c : st.stack.choices) {
snap.stackRationales.push_back(c.moduleName + ": " + c.rationale);
}
return snap;
}
static bool changeModuleLanguage(ArchitectReviewState& st,
const std::string& moduleName,
const std::string& newLanguage) {
bool changed = false;
for (auto& m : st.skeleton.modules) {
if (m.moduleName == moduleName) {
m.language = newLanguage;
changed = true;
}
}
for (auto& c : st.stack.choices) {
if (c.moduleName == moduleName) {
c.language = newLanguage;
changed = true;
}
}
if (changed) st.changeLog.push_back("language:" + moduleName + "->" + newLanguage);
return changed;
}
static bool mergeModules(ArchitectReviewState& st,
const std::string& primary,
const std::string& secondary) {
auto pIdx = indexOf(st.skeleton.modules, primary);
auto sIdx = indexOf(st.skeleton.modules, secondary);
if (pIdx < 0 || sIdx < 0 || pIdx == sIdx) return false;
auto& p = st.skeleton.modules[static_cast<size_t>(pIdx)];
auto& s = st.skeleton.modules[static_cast<size_t>(sIdx)];
p.functions.insert(p.functions.end(), s.functions.begin(), s.functions.end());
p.dependencies.insert(p.dependencies.end(), s.dependencies.begin(), s.dependencies.end());
dedupe(p.dependencies);
st.skeleton.modules.erase(st.skeleton.modules.begin() + sIdx);
removeStackChoice(st.stack, secondary);
remapGraphAfterMerge(st.moduleGraph, primary, secondary);
st.changeLog.push_back("merge:" + primary + "<-" + secondary);
return true;
}
static bool addModule(ArchitectReviewState& st,
const std::string& moduleName,
const std::string& language,
const std::vector<std::string>& responsibilities = {}) {
if (st.skeleton.hasModule(moduleName)) return false;
SkeletonModuleSpec m;
m.moduleName = moduleName;
m.language = language;
m.functions.push_back(defaultFunctionFor(moduleName));
st.skeleton.modules.push_back(m);
st.stack.choices.push_back({moduleName, language, "", "", "Architect-added module", 0.70f});
st.moduleGraph.nodes.push_back({moduleName, responsibilities, 4, false});
st.changeLog.push_back("add_module:" + moduleName);
return true;
}
static bool adjustRouting(ArchitectReviewState& st,
const std::string& moduleName,
const std::string& functionName,
const std::string& automatability,
const std::string& contextWidth,
const std::string& complexity) {
for (auto& m : st.skeleton.modules) {
if (m.moduleName != moduleName) continue;
for (auto& f : m.functions) {
if (f.name != functionName) continue;
setAnnotation(f.annotations, "@Automatability", automatability);
setAnnotation(f.annotations, "@ContextWidth", contextWidth);
setAnnotation(f.annotations, "@Complexity", complexity);
st.changeLog.push_back("routing:" + moduleName + "." + functionName);
return true;
}
}
return false;
}
static void approve(ArchitectReviewState& st) {
st.approved = true;
st.changeLog.push_back("approved");
}
private:
static std::vector<std::string> collectKeyAnnotations(const SkeletonModuleSpec& m) {
std::set<std::string> keys;
for (const auto& f : m.functions) {
for (const auto& a : f.annotations) {
if (a.key == "@Intent" || a.key == "@Complexity" || a.key == "@Automatability" ||
a.key == "@ContextWidth" || a.key == "@Contract") {
keys.insert(a.key);
}
}
}
return std::vector<std::string>(keys.begin(), keys.end());
}
static int indexOf(const std::vector<SkeletonModuleSpec>& mods, const std::string& name) {
for (size_t i = 0; i < mods.size(); ++i) if (mods[i].moduleName == name) return static_cast<int>(i);
return -1;
}
static void dedupe(std::vector<std::string>& values) {
std::set<std::string> uniq(values.begin(), values.end());
values.assign(uniq.begin(), uniq.end());
}
static void removeStackChoice(TechStackDecision& stack, const std::string& moduleName) {
stack.choices.erase(
std::remove_if(stack.choices.begin(), stack.choices.end(),
[&](const TechChoice& c){ return c.moduleName == moduleName; }),
stack.choices.end());
}
static void remapGraphAfterMerge(ModuleGraph& g,
const std::string& primary,
const std::string& secondary) {
for (auto& e : g.edges) {
if (e.from == secondary) e.from = primary;
if (e.to == secondary) e.to = primary;
}
g.nodes.erase(std::remove_if(g.nodes.begin(), g.nodes.end(),
[&](const ModuleNode& n){ return n.name == secondary; }),
g.nodes.end());
// Remove self-loop duplicates introduced by merge remapping.
std::set<std::string> seen;
std::vector<ModuleEdge> out;
for (const auto& e : g.edges) {
if (e.from == e.to) continue;
std::string k = e.from + "->" + e.to + ":" + e.reason;
if (!seen.insert(k).second) continue;
out.push_back(e);
}
g.edges.swap(out);
}
static SkeletonFunctionSpec defaultFunctionFor(const std::string& moduleName) {
SkeletonFunctionSpec f;
f.name = "execute";
f.signature = "void execute()";
f.annotations = {
{"@Intent", moduleName + ":execute"},
{"@Complexity", "medium"},
{"@ContextWidth", "module"},
{"@Automatability", "llm"},
{"@Contract", "pre: valid input; post: deterministic output"}
};
return f;
}
static void setAnnotation(std::vector<SkeletonAnnotation>& anns,
const std::string& key,
const std::string& value) {
for (auto& a : anns) {
if (a.key == key) {
a.value = value;
return;
}
}
anns.push_back({key, value});
}
};

View File

@@ -0,0 +1,171 @@
// Step 475: Architect Review Interface Tests (12 tests)
#include "ArchitectReviewInterface.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 ArchitectReviewState makeState(const std::string& prompt) {
auto req = ArchitectProblemParser::parse(prompt);
auto graph = ArchitectModuleDecomposer::decompose(req);
auto stack = ArchitectTechStackSelector::select(req, graph);
auto skel = ArchitectSkeletonGenerator::generate(req, graph, stack);
return ArchitectReviewInterface::initialize(skel, stack, graph);
}
void test_snapshot_contains_module_summaries() {
TEST(snapshot_contains_module_summaries);
auto st = makeState("Build a web REST API with auth and PostgreSQL.");
auto snap = ArchitectReviewInterface::snapshot(st);
CHECK(!snap.modules.empty(), "expected module summaries");
CHECK(!snap.dependencyGraph.empty(), "expected dependency graph");
PASS();
}
void test_snapshot_includes_key_annotations() {
TEST(snapshot_includes_key_annotations);
auto st = makeState("Build a REST API with auth.");
auto snap = ArchitectReviewInterface::snapshot(st);
bool foundIntent = false;
for (const auto& m : snap.modules) {
for (const auto& a : m.keyAnnotations) if (a == "@Intent") foundIntent = true;
}
CHECK(foundIntent, "expected key annotation listing");
PASS();
}
void test_change_language_updates_skeleton_and_stack() {
TEST(change_language_updates_skeleton_and_stack);
auto st = makeState("Build a web dashboard.");
bool ok = ArchitectReviewInterface::changeModuleLanguage(st, "ui", "elm");
CHECK(ok, "language change should succeed");
CHECK(st.skeleton.getModule("ui").language == "elm", "skeleton language not updated");
CHECK(st.stack.getChoice("ui").language == "elm", "stack language not updated");
PASS();
}
void test_change_language_fails_for_missing_module() {
TEST(change_language_fails_for_missing_module);
auto st = makeState("Build an API.");
bool ok = ArchitectReviewInterface::changeModuleLanguage(st, "missing", "go");
CHECK(!ok, "language change should fail for missing module");
PASS();
}
void test_merge_modules_moves_functions_and_removes_secondary() {
TEST(merge_modules_moves_functions_and_removes_secondary);
auto st = makeState("Build API with auth.");
CHECK(st.skeleton.hasModule("api"), "missing api");
CHECK(st.skeleton.hasModule("auth"), "missing auth");
auto before = st.skeleton.getModule("api").functions.size();
bool ok = ArchitectReviewInterface::mergeModules(st, "api", "auth");
CHECK(ok, "merge should succeed");
CHECK(!st.skeleton.hasModule("auth"), "secondary module should be removed");
auto after = st.skeleton.getModule("api").functions.size();
CHECK(after >= before, "primary should have merged functions");
PASS();
}
void test_merge_modules_fails_for_invalid_pair() {
TEST(merge_modules_fails_for_invalid_pair);
auto st = makeState("Build API with auth.");
bool ok = ArchitectReviewInterface::mergeModules(st, "api", "missing");
CHECK(!ok, "merge should fail for missing secondary");
PASS();
}
void test_add_module_inserts_into_skeleton_stack_graph() {
TEST(add_module_inserts_into_skeleton_stack_graph);
auto st = makeState("Build API.");
bool ok = ArchitectReviewInterface::addModule(st, "billing", "python", {"payment processing"});
CHECK(ok, "add module should succeed");
CHECK(st.skeleton.hasModule("billing"), "missing new skeleton module");
CHECK(st.stack.hasChoiceFor("billing"), "missing new stack choice");
bool graphHas = false;
for (const auto& n : st.moduleGraph.nodes) if (n.name == "billing") graphHas = true;
CHECK(graphHas, "missing new graph node");
PASS();
}
void test_add_module_fails_when_already_present() {
TEST(add_module_fails_when_already_present);
auto st = makeState("Build API.");
bool ok = ArchitectReviewInterface::addModule(st, "api", "python");
CHECK(!ok, "adding existing module should fail");
PASS();
}
void test_adjust_routing_updates_annotations() {
TEST(adjust_routing_updates_annotations);
auto st = makeState("Build API with auth.");
auto api = st.skeleton.getModule("api");
CHECK(!api.functions.empty(), "missing api functions");
bool ok = ArchitectReviewInterface::adjustRouting(
st, "api", api.functions[0].name, "human", "project", "high");
CHECK(ok, "routing adjustment should succeed");
auto api2 = st.skeleton.getModule("api");
std::string autoV, ctxV, compV;
for (const auto& a : api2.functions[0].annotations) {
if (a.key == "@Automatability") autoV = a.value;
if (a.key == "@ContextWidth") ctxV = a.value;
if (a.key == "@Complexity") compV = a.value;
}
CHECK(autoV == "human" && ctxV == "project" && compV == "high",
"routing annotations not updated");
PASS();
}
void test_adjust_routing_fails_for_missing_function() {
TEST(adjust_routing_fails_for_missing_function);
auto st = makeState("Build API.");
bool ok = ArchitectReviewInterface::adjustRouting(
st, "api", "does_not_exist", "llm", "module", "low");
CHECK(!ok, "routing adjustment should fail for missing function");
PASS();
}
void test_approve_marks_state_approved() {
TEST(approve_marks_state_approved);
auto st = makeState("Build API.");
CHECK(!st.approved, "expected initial not-approved");
ArchitectReviewInterface::approve(st);
CHECK(st.approved, "approve should set approved flag");
PASS();
}
void test_change_log_records_modifications_and_approval() {
TEST(change_log_records_modifications_and_approval);
auto st = makeState("Build API with auth.");
ArchitectReviewInterface::changeModuleLanguage(st, "api", "go");
ArchitectReviewInterface::addModule(st, "billing", "python");
ArchitectReviewInterface::approve(st);
CHECK(st.changeLog.size() >= 3, "expected change log entries");
CHECK(st.changeLog.back() == "approved", "expected approval in changelog");
PASS();
}
int main() {
std::cout << "Step 475: Architect Review Interface Tests\n";
test_snapshot_contains_module_summaries(); // 1
test_snapshot_includes_key_annotations(); // 2
test_change_language_updates_skeleton_and_stack(); // 3
test_change_language_fails_for_missing_module(); // 4
test_merge_modules_moves_functions_and_removes_secondary(); // 5
test_merge_modules_fails_for_invalid_pair(); // 6
test_add_module_inserts_into_skeleton_stack_graph(); // 7
test_add_module_fails_when_already_present(); // 8
test_adjust_routing_updates_annotations(); // 9
test_adjust_routing_fails_for_missing_function(); // 10
test_approve_marks_state_approved(); // 11
test_change_log_records_modifications_and_approval(); // 12
std::cout << "\nResults: " << passed << "/" << (passed + failed)
<< " passed\n";
return failed == 0 ? 0 : 1;
}

View File

@@ -6581,3 +6581,42 @@ annotations suitable for workflow creation.
**Architecture gate check:**
- `editor/src/ArchitectSkeletonGenerator.h` within header-size limit (`198` <= `600`)
- `editor/tests/step474_test.cpp` within test-file size guidance (`187` lines)
### Step 475: Architect Review Interface
**Status:** PASS (12/12 tests)
Implements a structured architect review state with snapshot/summary generation
and concrete modification actions (language change, merge, add, routing
adjustment, approval).
**Files added:**
- `editor/src/ArchitectReviewInterface.h` — review interface model:
- review state/snapshot models:
- `ArchitectReviewState`
- `ArchitectReviewSnapshot`
- `ModuleReviewSummary`
- review actions:
- `changeModuleLanguage(...)`
- `mergeModules(...)`
- `addModule(...)`
- `adjustRouting(...)`
- `approve(...)`
- change-log tracking and graph remapping after merges
- key-annotation extraction for review summaries
- `editor/tests/step475_test.cpp` — 12 tests covering:
- summary snapshot content
- language change behavior
- module merge success/failure paths
- module add success/failure paths
- routing adjustment success/failure paths
- approval and change-log behavior
- `editor/CMakeLists.txt``step475_test` target
**Verification run:**
- `cmake --build editor/build-native --target step475_test` — PASS
- `./editor/build-native/step475_test` — PASS (12/12)
- `./editor/build-native/step474_test` — PASS (12/12) regression coverage
**Architecture gate check:**
- `editor/src/ArchitectReviewInterface.h` within header-size limit (`231` <= `600`)
- `editor/tests/step475_test.cpp` within test-file size guidance (`171` lines)