Complete Step 477: architect templates
This commit is contained in:
@@ -3172,4 +3172,13 @@ target_link_libraries(step476_test PRIVATE
|
||||
tree_sitter_javascript tree_sitter_typescript
|
||||
tree_sitter_java tree_sitter_rust tree_sitter_go)
|
||||
|
||||
add_executable(step477_test tests/step477_test.cpp)
|
||||
target_include_directories(step477_test PRIVATE src)
|
||||
target_link_libraries(step477_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)
|
||||
|
||||
182
editor/src/ArchitectTemplates.h
Normal file
182
editor/src/ArchitectTemplates.h
Normal file
@@ -0,0 +1,182 @@
|
||||
#pragma once
|
||||
|
||||
// Step 477: Architecture Templates
|
||||
// Prebuilt architecture skeleton patterns for common project types.
|
||||
|
||||
#include "ArchitectSkeletonGenerator.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
enum class ArchitectureTemplateKind {
|
||||
RestApi,
|
||||
CliTool,
|
||||
Library,
|
||||
Microservice,
|
||||
FullStack
|
||||
};
|
||||
|
||||
struct TemplateInput {
|
||||
std::string projectName;
|
||||
std::string primaryEntity; // e.g. "Book"
|
||||
std::vector<std::string> fields;
|
||||
std::string authMethod; // e.g. jwt/oauth/none
|
||||
};
|
||||
|
||||
struct TemplateOutput {
|
||||
ArchitectureTemplateKind kind = ArchitectureTemplateKind::RestApi;
|
||||
SkeletonProjectSpec skeleton;
|
||||
std::vector<std::string> notes;
|
||||
};
|
||||
|
||||
class ArchitectTemplates {
|
||||
public:
|
||||
static TemplateOutput instantiate(ArchitectureTemplateKind kind,
|
||||
const TemplateInput& in) {
|
||||
TemplateOutput out;
|
||||
out.kind = kind;
|
||||
switch (kind) {
|
||||
case ArchitectureTemplateKind::RestApi:
|
||||
out.skeleton = restApiTemplate(in);
|
||||
out.notes.push_back("REST API template applied");
|
||||
break;
|
||||
case ArchitectureTemplateKind::CliTool:
|
||||
out.skeleton = cliTemplate(in);
|
||||
out.notes.push_back("CLI template applied");
|
||||
break;
|
||||
case ArchitectureTemplateKind::Library:
|
||||
out.skeleton = libraryTemplate(in);
|
||||
out.notes.push_back("Library template applied");
|
||||
break;
|
||||
case ArchitectureTemplateKind::Microservice:
|
||||
out.skeleton = microserviceTemplate(in);
|
||||
out.notes.push_back("Microservice template applied");
|
||||
break;
|
||||
case ArchitectureTemplateKind::FullStack:
|
||||
out.skeleton = fullStackTemplate(in);
|
||||
out.notes.push_back("Full Stack template applied");
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
static SkeletonModuleSpec module(const std::string& name,
|
||||
const std::string& lang,
|
||||
std::initializer_list<SkeletonFunctionSpec> fns,
|
||||
std::initializer_list<std::string> deps = {}) {
|
||||
SkeletonModuleSpec m;
|
||||
m.moduleName = name;
|
||||
m.language = lang;
|
||||
m.dependencies.assign(deps.begin(), deps.end());
|
||||
m.functions.assign(fns.begin(), fns.end());
|
||||
return m;
|
||||
}
|
||||
|
||||
static SkeletonFunctionSpec fn(const std::string& name,
|
||||
const std::string& sig,
|
||||
const std::string& intent) {
|
||||
SkeletonFunctionSpec f;
|
||||
f.name = name;
|
||||
f.signature = sig;
|
||||
f.annotations = {
|
||||
{"@Intent", intent},
|
||||
{"@Complexity", "medium"},
|
||||
{"@ContextWidth", "module"},
|
||||
{"@Automatability", "llm"},
|
||||
{"@Contract", "pre: valid input; post: expected output"}
|
||||
};
|
||||
return f;
|
||||
}
|
||||
|
||||
static SkeletonProjectSpec restApiTemplate(const TemplateInput& in) {
|
||||
SkeletonProjectSpec sk;
|
||||
sk.modules.push_back(module("routes", "typescript", {
|
||||
fn("registerRoutes", "void registerRoutes(App app)", "register REST routes")
|
||||
}, {"handlers"}));
|
||||
sk.modules.push_back(module("handlers", "typescript", {
|
||||
fn("list" + in.primaryEntity + "s", "Response list(Request req)", "list entities"),
|
||||
fn("create" + in.primaryEntity, "Response create(Request req)", "create entity")
|
||||
}, {"models", "database", "auth"}));
|
||||
sk.modules.push_back(module("models", "typescript", {
|
||||
fn("validate" + in.primaryEntity, "bool validate(Entity e)", "validate entity")
|
||||
}));
|
||||
sk.modules.push_back(module("middleware", "typescript", {
|
||||
fn("authMiddleware", "bool authMiddleware(Request req)", "enforce auth")
|
||||
}));
|
||||
sk.modules.push_back(module("database", "sql", {
|
||||
fn("migrate", "void migrate()", "apply schema migrations")
|
||||
}));
|
||||
sk.modules.push_back(module("auth", "typescript", {
|
||||
fn("authenticate", "AuthResult authenticate(Request req)", "authenticate request")
|
||||
}));
|
||||
return sk;
|
||||
}
|
||||
|
||||
static SkeletonProjectSpec cliTemplate(const TemplateInput& in) {
|
||||
SkeletonProjectSpec sk;
|
||||
sk.modules.push_back(module("cli", "python", {
|
||||
fn("parseArgs", "Options parseArgs(List args)", "parse command-line arguments"),
|
||||
fn("runCommand", "int runCommand(Options opts)", "execute command")
|
||||
}, {"output"}));
|
||||
sk.modules.push_back(module("commands", "python", {
|
||||
fn("handle" + in.primaryEntity, "int handle(Options opts)", "handle primary command")
|
||||
}));
|
||||
sk.modules.push_back(module("output", "python", {
|
||||
fn("formatOutput", "str formatOutput(Result r)", "format command output")
|
||||
}));
|
||||
return sk;
|
||||
}
|
||||
|
||||
static SkeletonProjectSpec libraryTemplate(const TemplateInput& in) {
|
||||
SkeletonProjectSpec sk;
|
||||
sk.modules.push_back(module("public_api", "cpp", {
|
||||
fn("create" + in.primaryEntity, "Result create(Input in)", "public API entrypoint")
|
||||
}, {"internal"}));
|
||||
sk.modules.push_back(module("internal", "cpp", {
|
||||
fn("transform", "Data transform(Data in)", "internal transformation")
|
||||
}));
|
||||
sk.modules.push_back(module("tests", "cpp", {
|
||||
fn("testPublicApi", "void testPublicApi()", "library behavior verification")
|
||||
}));
|
||||
sk.modules.push_back(module("docs", "markdown", {
|
||||
fn("generateDocs", "void generateDocs()", "generate documentation stubs")
|
||||
}));
|
||||
return sk;
|
||||
}
|
||||
|
||||
static SkeletonProjectSpec microserviceTemplate(const TemplateInput& in) {
|
||||
SkeletonProjectSpec sk;
|
||||
sk.modules.push_back(module("service", "go", {
|
||||
fn("startService", "error startService(Config cfg)", "start microservice")
|
||||
}, {"transport", "storage"}));
|
||||
sk.modules.push_back(module("transport", "go", {
|
||||
fn("handleRequest", "Response handle(Request req)", "transport request handling")
|
||||
}));
|
||||
sk.modules.push_back(module("storage", "go", {
|
||||
fn("save" + in.primaryEntity, "error save(Entity e)", "persist entity")
|
||||
}));
|
||||
sk.modules.push_back(module("health", "go", {
|
||||
fn("healthCheck", "Status healthCheck()", "service health endpoint")
|
||||
}));
|
||||
return sk;
|
||||
}
|
||||
|
||||
static SkeletonProjectSpec fullStackTemplate(const TemplateInput& in) {
|
||||
SkeletonProjectSpec sk;
|
||||
sk.modules.push_back(module("frontend", "typescript", {
|
||||
fn("renderApp", "View renderApp(State s)", "render frontend app")
|
||||
}, {"backend"}));
|
||||
sk.modules.push_back(module("backend", "python", {
|
||||
fn("handleApi", "Response handleApi(Request req)", "backend API handling")
|
||||
}, {"database", "deploy"}));
|
||||
sk.modules.push_back(module("database", "sql", {
|
||||
fn("migrate", "void migrate()", "apply database migrations")
|
||||
}));
|
||||
sk.modules.push_back(module("deploy", "yaml", {
|
||||
fn("generateDeployConfig", "void generateDeployConfig()", "deployment config generation")
|
||||
}));
|
||||
return sk;
|
||||
}
|
||||
};
|
||||
166
editor/tests/step477_test.cpp
Normal file
166
editor/tests/step477_test.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
// Step 477: Architecture Templates Tests (12 tests)
|
||||
|
||||
#include "ArchitectTemplates.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 TemplateInput sampleInput() {
|
||||
TemplateInput in;
|
||||
in.projectName = "bookstore";
|
||||
in.primaryEntity = "Book";
|
||||
in.fields = {"id", "title", "author"};
|
||||
in.authMethod = "jwt";
|
||||
return in;
|
||||
}
|
||||
|
||||
void test_rest_api_template_contains_expected_modules() {
|
||||
TEST(rest_api_template_contains_expected_modules);
|
||||
auto out = ArchitectTemplates::instantiate(ArchitectureTemplateKind::RestApi, sampleInput());
|
||||
CHECK(out.skeleton.hasModule("routes"), "missing routes");
|
||||
CHECK(out.skeleton.hasModule("handlers"), "missing handlers");
|
||||
CHECK(out.skeleton.hasModule("models"), "missing models");
|
||||
CHECK(out.skeleton.hasModule("middleware"), "missing middleware");
|
||||
CHECK(out.skeleton.hasModule("database"), "missing database");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_cli_template_contains_cli_command_output_modules() {
|
||||
TEST(cli_template_contains_cli_command_output_modules);
|
||||
auto out = ArchitectTemplates::instantiate(ArchitectureTemplateKind::CliTool, sampleInput());
|
||||
CHECK(out.skeleton.hasModule("cli"), "missing cli");
|
||||
CHECK(out.skeleton.hasModule("commands"), "missing commands");
|
||||
CHECK(out.skeleton.hasModule("output"), "missing output");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_library_template_contains_api_internal_tests_docs() {
|
||||
TEST(library_template_contains_api_internal_tests_docs);
|
||||
auto out = ArchitectTemplates::instantiate(ArchitectureTemplateKind::Library, sampleInput());
|
||||
CHECK(out.skeleton.hasModule("public_api"), "missing public_api");
|
||||
CHECK(out.skeleton.hasModule("internal"), "missing internal");
|
||||
CHECK(out.skeleton.hasModule("tests"), "missing tests");
|
||||
CHECK(out.skeleton.hasModule("docs"), "missing docs");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_microservice_template_contains_service_transport_storage_health() {
|
||||
TEST(microservice_template_contains_service_transport_storage_health);
|
||||
auto out = ArchitectTemplates::instantiate(ArchitectureTemplateKind::Microservice, sampleInput());
|
||||
CHECK(out.skeleton.hasModule("service"), "missing service");
|
||||
CHECK(out.skeleton.hasModule("transport"), "missing transport");
|
||||
CHECK(out.skeleton.hasModule("storage"), "missing storage");
|
||||
CHECK(out.skeleton.hasModule("health"), "missing health");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_fullstack_template_contains_frontend_backend_database_deploy() {
|
||||
TEST(fullstack_template_contains_frontend_backend_database_deploy);
|
||||
auto out = ArchitectTemplates::instantiate(ArchitectureTemplateKind::FullStack, sampleInput());
|
||||
CHECK(out.skeleton.hasModule("frontend"), "missing frontend");
|
||||
CHECK(out.skeleton.hasModule("backend"), "missing backend");
|
||||
CHECK(out.skeleton.hasModule("database"), "missing database");
|
||||
CHECK(out.skeleton.hasModule("deploy"), "missing deploy");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_template_functions_are_annotated() {
|
||||
TEST(template_functions_are_annotated);
|
||||
auto out = ArchitectTemplates::instantiate(ArchitectureTemplateKind::RestApi, sampleInput());
|
||||
auto m = out.skeleton.getModule("handlers");
|
||||
CHECK(!m.functions.empty(), "missing handler functions");
|
||||
bool hasIntent = false, hasContract = false;
|
||||
for (const auto& a : m.functions[0].annotations) {
|
||||
if (a.key == "@Intent") hasIntent = true;
|
||||
if (a.key == "@Contract") hasContract = true;
|
||||
}
|
||||
CHECK(hasIntent && hasContract, "missing expected annotations");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_template_is_parameterized_by_primary_entity() {
|
||||
TEST(template_is_parameterized_by_primary_entity);
|
||||
auto out = ArchitectTemplates::instantiate(ArchitectureTemplateKind::RestApi, sampleInput());
|
||||
auto handlers = out.skeleton.getModule("handlers");
|
||||
bool hasCreateBook = false;
|
||||
for (const auto& f : handlers.functions) if (f.name == "createBook") hasCreateBook = true;
|
||||
CHECK(hasCreateBook, "expected entity-parameterized function name");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_template_module_dependencies_present() {
|
||||
TEST(template_module_dependencies_present);
|
||||
auto out = ArchitectTemplates::instantiate(ArchitectureTemplateKind::RestApi, sampleInput());
|
||||
auto routes = out.skeleton.getModule("routes");
|
||||
bool hasHandlersDep = false;
|
||||
for (const auto& d : routes.dependencies) if (d == "handlers") hasHandlersDep = true;
|
||||
CHECK(hasHandlersDep, "expected routes->handlers dependency");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_template_output_contains_template_note() {
|
||||
TEST(template_output_contains_template_note);
|
||||
auto out = ArchitectTemplates::instantiate(ArchitectureTemplateKind::CliTool, sampleInput());
|
||||
CHECK(!out.notes.empty(), "expected template note");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_template_module_languages_match_expected_defaults() {
|
||||
TEST(template_module_languages_match_expected_defaults);
|
||||
auto rest = ArchitectTemplates::instantiate(ArchitectureTemplateKind::RestApi, sampleInput());
|
||||
auto micro = ArchitectTemplates::instantiate(ArchitectureTemplateKind::Microservice, sampleInput());
|
||||
auto full = ArchitectTemplates::instantiate(ArchitectureTemplateKind::FullStack, sampleInput());
|
||||
CHECK(rest.skeleton.getModule("handlers").language == "typescript", "rest handlers language mismatch");
|
||||
CHECK(micro.skeleton.getModule("service").language == "go", "microservice language mismatch");
|
||||
CHECK(full.skeleton.getModule("backend").language == "python", "fullstack backend language mismatch");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_template_outputs_are_nonempty_across_all_kinds() {
|
||||
TEST(template_outputs_are_nonempty_across_all_kinds);
|
||||
auto in = sampleInput();
|
||||
auto a = ArchitectTemplates::instantiate(ArchitectureTemplateKind::RestApi, in);
|
||||
auto b = ArchitectTemplates::instantiate(ArchitectureTemplateKind::CliTool, in);
|
||||
auto c = ArchitectTemplates::instantiate(ArchitectureTemplateKind::Library, in);
|
||||
auto d = ArchitectTemplates::instantiate(ArchitectureTemplateKind::Microservice, in);
|
||||
auto e = ArchitectTemplates::instantiate(ArchitectureTemplateKind::FullStack, in);
|
||||
CHECK(!a.skeleton.modules.empty(), "rest template empty");
|
||||
CHECK(!b.skeleton.modules.empty(), "cli template empty");
|
||||
CHECK(!c.skeleton.modules.empty(), "library template empty");
|
||||
CHECK(!d.skeleton.modules.empty(), "microservice template empty");
|
||||
CHECK(!e.skeleton.modules.empty(), "fullstack template empty");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void test_templates_include_auth_module_for_rest_api() {
|
||||
TEST(templates_include_auth_module_for_rest_api);
|
||||
auto out = ArchitectTemplates::instantiate(ArchitectureTemplateKind::RestApi, sampleInput());
|
||||
CHECK(out.skeleton.hasModule("auth"), "expected auth module in REST template");
|
||||
PASS();
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Step 477: Architecture Templates Tests\n";
|
||||
|
||||
test_rest_api_template_contains_expected_modules(); // 1
|
||||
test_cli_template_contains_cli_command_output_modules(); // 2
|
||||
test_library_template_contains_api_internal_tests_docs(); // 3
|
||||
test_microservice_template_contains_service_transport_storage_health(); // 4
|
||||
test_fullstack_template_contains_frontend_backend_database_deploy(); // 5
|
||||
test_template_functions_are_annotated(); // 6
|
||||
test_template_is_parameterized_by_primary_entity(); // 7
|
||||
test_template_module_dependencies_present(); // 8
|
||||
test_template_output_contains_template_note(); // 9
|
||||
test_template_module_languages_match_expected_defaults(); // 10
|
||||
test_template_outputs_are_nonempty_across_all_kinds(); // 11
|
||||
test_templates_include_auth_module_for_rest_api(); // 12
|
||||
|
||||
std::cout << "\nResults: " << passed << "/" << (passed + failed)
|
||||
<< " passed\n";
|
||||
return failed == 0 ? 0 : 1;
|
||||
}
|
||||
37
progress.md
37
progress.md
@@ -6663,3 +6663,40 @@ skeleton generation → architect review/approval → workflow task materializat
|
||||
- annotated multi-module skeleton generation
|
||||
- architect review/modify/approve state model
|
||||
- end-to-end architect-mode flow readiness for phase 23b scaffolding
|
||||
|
||||
### Step 477: Architect Templates
|
||||
**Status:** PASS (12/12 tests)
|
||||
|
||||
Introduces reusable architecture templates that instantiate starter module
|
||||
layouts, dependencies, function stubs, and annotations for common product
|
||||
shapes (REST API, CLI, library, microservice, full stack).
|
||||
|
||||
**Files added:**
|
||||
- `editor/src/ArchitectTemplates.h` — template library:
|
||||
- `ArchitectureTemplateKind`, `TemplateInput`, `TemplateOutput`
|
||||
- `ArchitectTemplates::instantiate(...)`
|
||||
- template-specific module graphs and function stubs
|
||||
- default annotation emission for generated functions:
|
||||
- `@Intent`
|
||||
- `@Complexity`
|
||||
- `@ContextWidth`
|
||||
- `@Automatability`
|
||||
- `@Contract`
|
||||
- `editor/tests/step477_test.cpp` — 12 tests covering:
|
||||
- expected module sets for each template kind
|
||||
- annotation presence on generated function stubs
|
||||
- `primaryEntity` parameterization (e.g. `createBook`)
|
||||
- dependency expectations (e.g. `routes -> handlers`)
|
||||
- template metadata notes and language defaults
|
||||
- non-empty outputs across all template kinds
|
||||
- `editor/CMakeLists.txt` — `step477_test` target
|
||||
|
||||
**Verification run:**
|
||||
- `cmake --build editor/build-native --target step477_test step476_test` — PASS
|
||||
- `./editor/build-native/step477_test` — PASS (12/12)
|
||||
- `./editor/build-native/step476_test` — PASS (8/8) regression coverage
|
||||
|
||||
**Architecture gate check:**
|
||||
- `editor/src/ArchitectTemplates.h` within header-size limit (`182` <= `600`)
|
||||
- `editor/tests/step477_test.cpp` within test-file size guidance (`166` lines)
|
||||
- Naming/style aligned with `ARCHITECTURE.md` conventions (`PascalCase` types, `camelCase` functions)
|
||||
|
||||
Reference in New Issue
Block a user