Imported from Jacoby6000/Smithplates (
AGENTS.md). Install upstream withnpx skills add Jacoby6000/Smithplates. Copyright stays with the author.
Smithplates
Smithy codegen plugin for SQL schema/migration and HTTP service/client output. Bundled templates today: Python (SQL + FastAPI server + HTTPX/HTTPX2 client) and TypeScript (HTTP client via axios or fetch).
Conventions
Follow .cursor/rules/ — start with smithplates-build.mdc and code-design.mdc. Update user docs under docs/usage/ and contributor docs under docs/contributing/ when behavior or integration steps change.
Keep Smithplates self-contained and domain-neutral. Do not reference downstream repositories, their names, or their issue/PR numbers in Smithplates code, tests, documentation, changelogs, commit messages, or pull requests; describe motivations and examples as generic consumer requirements instead.
Reference docs (fetch on demand)
module-layout.md—modules/responsibilities, precompilation summary, template roots; read when adding or moving codegolden-template-tests.md— golden fixture layout, variant registration, refresh commands; read when changing codegen output or template testsplugin-consumer-config.md—smithy-build.jsonlanguage-first config; read when wiring consumers or plugin settingscodegen-output-deck.md—outputs.jsonartifact-deck JSON contract (schema, bindings, path placeholders, verbatim-copy rule); read when adding/editing bundled artifacts or a new language's deckcodegen-core.md— neutral IR, planner, strategies, and templateTemplateViewadapters; read when changingsmithplates-codegen-coreor renderer codecad-contract.md— Conceptual Architecture Document schema for architecture skills
Human docs
docs/usage/— plugin configuration and generated-code usagedocs/contributing/— validate commands, CI, architecture (CONTRIBUTING.mdfor./validatetargets)CHANGELOG.md— release history and migration notes (keep current with user-visible changes)
Decisions log
SqlTableTreeskips self-referential@sqlForeignKeyedges when computing DDL render order so a table can reference itself inline inCREATE TABLE- Language-neutral codegen epic (
#34, closed via#35–#42):smithplates-codegen-coreownsNeutralType/Model/ModelSet/ServiceModel/OperationModel,TypeUsageAnalyzer, declarativeNamingStrategy→Conventions,TypeRenderer, andCodegenPlanneroveroutputs.jsondecks. Feature extractors (HttpCoreModelExtractor,SqlCoreModelExtractor) lower Smithy into parametric feature metadata;SystemValidatoris the holistic post-extraction gate (model-set + service validators, duplicate ids, cyclic aliases, unresolved operation refs). - Domain-neutral applied traits: neutral metadata carries every effective Smithy trait on represented services, operations, models, fields, union variants, and enum values as
AppliedTrait/ recursiveSmithyNodeValue. Populate it withShape.getAllTraits; consumer-specific trait interpretation stays in consumer SSP, never in Smithplates. - HTTP core extraction (
#36):HttpCoreModelExtractor.extractalways runsSystemValidator(includingHttpCoreMetaValidatorfor response status codes and@websocketshape rules). Operation-bound shapes getHttpRequestMeta/HttpResponseMetafrom legacy HTTP binding IR viaHttpCoreMetaBuilder; nested shapes getHttpNestedField. - SQL core extraction (
#36):SqlCoreModelExtractorlowers member types andSqlTableMeta(table name) for structuralSystemValidatorchecks. DDL and derived-query rendering still read legacySqlSchema/SqlServiceIralongside the neutral model set. - SQL cutover (
#39/#42): SQL bundled output expansion usesCodegenOutputdecks andCodegenPlanner; all db SSP templates render throughSqlNeutralServiceTemplateAttributes(TemplateView[ServiceModel[SqlServiceMeta, SqlOperationMeta], SqlMeta]enriched at render time). Python-specific Scala helpers (SqlCodegenPythonImports, snake-case helpers, etc.) were removed; import formatting and casing live in SSP preambles.SqlShapeIrremains in context enrichment (for exampleSqlCodegenUuidTypeNames). Exception: Python SQLenum/intEnumartifacts are still rendered by a Scala side path (string_enum/int_enuminSqlServiceCodegenRenderer.renderEnumArtifacts), not byoutputs.jsonmodel bindings. - HTTP cutover (
#40/#42): HTTP server/client/route-group expansion usesCodegenOutputdecks andCodegenPlanner; all bundled HTTP SSP templates render through neutralTemplateViewwithHttpNeutralModelTemplateAttributes,HttpNeutralServiceTemplateAttributes, orHttpNeutralRouteGroupTemplateAttributes.TemplateViewcarriestypeRenderer: TypeRendererwired frombase_config.jsonviaConfigurableTypeRenderer. The legacy HTTP bridge (HttpCodegenTemplateView,HttpModelTypeNames, package-name helpers) is removed. - Consumer-declarable outputs (
#41, closed):additionalTemplatesDirectoryonsmithplates.<lang>.{sql,http.server,http.client}loads a consumeroutputs.jsondeck appended to bundled defaults;overridesreplaces bundled outputs by id;enableExternalTemplatesgates filesystem template directories. Consumer-deck static file copy (CodegenStaticOutput) and filesystem static copy are not wired yet. Duplicate resolved output paths are detected at codegen (plan/render) time. - Artifact decks are JSON, not Scala (HTTP and SQL service artifacts): every bundled output deck is an
outputs.jsonresource sitting beside that language's templates (templates/python/src/http/{server,client,models}/outputs.json,templates/python/src/db/outputs.json,templates/typescript/src/http/**/outputs.json), decoded byplanning/config/CodegenOutputDecoders+CodegenOutputDeckin codegen-core. Deck composers load JSON rather than hardcoding per-language artifact tables;CodegenOutputDeckLoader.load(templateDirectory, classLoader)derives the resource path from the (language-encoding) template directory (<dir>/outputs.json), and the default template dir comes fromlanguageId. A missing deck is a validation error ("missing codegen output deck: …"); a present deck whose referenced.ssptemplates are absent is a separate "missing required templates" error. Copy-verbatim resources are identified language-neutrally by not ending in.ssp. A customtemplateDirectorymust ship its ownoutputs.jsondeck. Caveat: SQL string/int enum files remain outside the deck (see SQL cutover note above). - HTTP
@httpProblembase model: smithplates emits one sharedHttpProblemmodel under{rootNamespace}/smithplates/codegen/http/(Pythonhttp_problem.py, TypeScripthttpProblem.ts; Smithy namespacesmithplates.codegen.http, aligned with the trait).@httpProblemerror structures extend it; consumer structures namedProblemare allowed when unrelated. - TypeScript HTTP clients: bundled under
templates/typescript/withhttpLibraryfetchoraxios. Client-only — no bundled TypeScript SQL or HTTP server. Seeexample/typescript/and golden cases undertemplates/typescript/tests/. - Python HTTP clients: bundled
httpLibraryvalues arehttpx(default) andhttpx2; both supportasync,sync, andbothmodes. Templates render the selected package name directly, so client, response, exception, and transport objects must come from the same library. - WebSockets (
@websocket): bidirectional endpoints on@httpServiceoperations (requires@httpURI +@tags). Python FastAPI emitswebsocket_routes.py; Python and TypeScript clients emit websocket client modules. REST route/client generation skips@websocketoperations. @sqlAutoIncrement: Integer table members map to SQLiteINTEGER PRIMARY KEY AUTOINCREMENT/ PostgresGENERATED ALWAYS AS IDENTITY; omitted from derived inserts; synthesized PRIMARY KEY clauses skip columns that already declare PK inline.@nestedPropertiesbody binding: a single@httpPayloadmember with Smithy@nestedPropertiesbecomesHttpOperationBodyBinding.NestedDocument— wire body is the payload target; the outer input shape is reconstructed for service dispatch.- Implementation helpers live in public nested
object internalcompanions, notprivate(seecode-design.mdc). Some legacyprivatehelpers remain in codegen-core and related modules; preferobject internalfor new code. - Toolchain: Scala/Java target JDK 17 (
flake.nixsuppliesjdk17_headless). - PR snapshot publishing: invoke
sbtn ci-releasewith the same PGP and Sonatype secrets as tagged releases. Directpublishbypassessbt-ci-release's secure credential setup and does not publish the root aggregate's complete transitive module graph. Test changes toissue_commentworkflows withworkflow_dispatchon the PR branch; comment events always load the workflow definition from the default branch. resolve-smithy-build-versions.sh: parsesbtn print smithplatesPlugin/versionas a whole-line dynver string, including bare release semvers (0.4.2on an exact tag). Do not require a-/+suffix — that filter broke example CI whenever main landed on a release tag.{{serviceModuleName}}in service-scoped paths (v0.5.0): service-scoped artifacts (app_factory,app_services,apis/,clients/, etc.) use{{serviceModuleName}}/in their output path, producing a per-service subdirectory under the namespace dir. This eliminates the need for per-servicepackageNameoverrides insmithy-build.jsonor post-generation directory merging — a single output entry with noservicesfilter produces the correct layout naturally. Model artifacts (shared across services) stay at{{smithyNamespaceDir}}(noserviceModuleName) viabinding: { type: "model" }. Template helpersservicePackageName(ctx)/serviceModuleDir(ctx)wrap the service-scoped package/module logic;packageSeparatoronConventionsdelegates tostrategy.packageSeparator.