From 58066d6f46d8516adcd7d21319e99c704bf6714d Mon Sep 17 00:00:00 2001 From: Bill Date: Mon, 16 Feb 2026 20:59:47 -0700 Subject: [PATCH] Complete Step 477: architect templates --- editor/CMakeLists.txt | 9 ++ editor/src/ArchitectTemplates.h | 182 ++++++++++++++++++++++++++++++++ editor/tests/step477_test.cpp | 166 +++++++++++++++++++++++++++++ progress.md | 37 +++++++ 4 files changed, 394 insertions(+) create mode 100644 editor/src/ArchitectTemplates.h create mode 100644 editor/tests/step477_test.cpp diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 4d2851d..a93f659 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -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) diff --git a/editor/src/ArchitectTemplates.h b/editor/src/ArchitectTemplates.h new file mode 100644 index 0000000..ecb6c23 --- /dev/null +++ b/editor/src/ArchitectTemplates.h @@ -0,0 +1,182 @@ +#pragma once + +// Step 477: Architecture Templates +// Prebuilt architecture skeleton patterns for common project types. + +#include "ArchitectSkeletonGenerator.h" + +#include +#include +#include + +enum class ArchitectureTemplateKind { + RestApi, + CliTool, + Library, + Microservice, + FullStack +}; + +struct TemplateInput { + std::string projectName; + std::string primaryEntity; // e.g. "Book" + std::vector fields; + std::string authMethod; // e.g. jwt/oauth/none +}; + +struct TemplateOutput { + ArchitectureTemplateKind kind = ArchitectureTemplateKind::RestApi; + SkeletonProjectSpec skeleton; + std::vector 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 fns, + std::initializer_list 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; + } +}; diff --git a/editor/tests/step477_test.cpp b/editor/tests/step477_test.cpp new file mode 100644 index 0000000..dabfd47 --- /dev/null +++ b/editor/tests/step477_test.cpp @@ -0,0 +1,166 @@ +// Step 477: Architecture Templates Tests (12 tests) + +#include "ArchitectTemplates.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 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; +} diff --git a/progress.md b/progress.md index 7e18364..2badbee 100644 --- a/progress.md +++ b/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)