ADR-0025: SaaS descriptors are imported into blueprints; the recipe path is the single generation contract¶
Decision
This page records a decision and its status. A Proposed decision is not binding; an Accepted decision is binding on the Factory until amended or superseded.
- Status: Accepted 2026-10-14 by the product owner (decision D55; closes Q-22, supersedes the provisional label of D11)
- Date: 2026-10-14
- Deciders: Dmitry Khaymov (product owner)
- Tags: generation, blueprints, SaaS templates, descriptors
Context¶
In September 2026 the five SaaS template repositories (Tenants, ProductsCatalog, Entitlements, Billing, Metering) each gained a <Context>.json descriptor, its .schema.json, .entitymodel.schema.json and Microservice.schema.json, and a build/regenerate.ps1 meant to call a Microservice Generator Agent endpoint. The Factory's generator specification (3,224 lines, section "SaaS one-aggregate-root descriptor") describes that interface: one descriptor per repository, a JSON run record, generated and preserved projects, regeneration semantics.
The 2026-10-09 verification (review folder handoffs/2026-10-09-independent-review/verification-record.md, rows V-D1…V-D3) established: the descriptors and schemas exist with one aggregate root per service and three empty entity lists; no script dispatches anything (three print "Invoking" and exit, Entitlements ignores the endpoint and throws, Billing throws in both branches); no run-record field exists; the consumers (2026-09-16) are newer than the specification; no generator service exists; the specification emits Orleans grain code while listing the Orleans actor project as preserved, and mandates MassTransit as the only transport against ADR-0012.
The 2026 design generates through blueprints: Service and Module blueprints (BC-02) feed a generation recipe (BC-15), the generate-product workflow scaffolds from the template packs in the sandbox, validates by build and test, records provenance and proposes the change set under approval (D21). Keeping the descriptor interface as a second contract would oblige the Factory to maintain two generation paths, reconcile the specification's contradictions and extend the descriptor beyond one aggregate root. The product owner deferred the question on 2026-10-08 (Q-22) and decided it on 2026-10-14.
Decision¶
- One generation contract. Generation is a Factory run: a Published Service or Module blueprint, a recipe validated against template manifests and the compatibility matrix (BC-15), the
generate-productworkflow, a reviewable change set with provenance. Nothing else generates. - The SaaS descriptor family is an import format.
BlueprintServiceimports a<Context>.jsonwith its entity-model schema into one Service Blueprint context and one Module Blueprint (rules SAD-001…SAD-003 in the blueprints catalog); the import records the descriptor's content hash as provenance of the blueprints it produced. A descriptor with several aggregates is several imports or a hand-authored blueprint. - No generator endpoint. The Factory exposes no "microservice generator" endpoint and no JSON run record of its own shape. The five consumer scripts are outside the Factory; when a consumer migrates, it submits a run (
run-submission-v1) that imports its descriptor and executesgenerate-product, through GW-01 or GW-02, under the consumer's project scope and approvals. - Regeneration follows the template manifest. Which projects are generated and which are preserved is the template's generated/preserved manifest in the template catalog, not the descriptor specification; transport is an installation profile of the messaging port (ADR-0012). The specification's contrary instructions are superseded.
- Scaffold engine as a service stays later and optional. The recipe path scaffolds with
dotnet newand the packed template installers in the sandbox;ScaffoldEngineServiceis taken up only if a need beyond that is measured. - Labels. The blueprint kind
saas-descriptorbecomes "SaaS descriptor import"; the provisional clauses on FR-11, XL-09, BC-15, the Studio launcher and the pack pages are removed; the Low-Code API Platform Agent produces Module blueprints for simple contexts from imported descriptors.
Consequences¶
- Positive: one contract to design, validate, evaluate and secure; the descriptors keep their value as a compact input for simple services; the consumers' existing files stay valid as import sources; nothing in the Control, Agent Mesh or Tools designs changes.
- Negative: the September consumer scripts do not work against the Factory until they are rewritten to submit runs; the generator specification's SaaS section becomes historical and is kept only as the description of the import source.
- Neutral: the canonical starter page
saas-factory-generation.mdin ConnectSoft.Documentation stays as context; the drift is recorded in the canonical drift register rather than edited there.
Revisit triggers¶
A consumer that needs regeneration faster than a Factory run can offer; a descriptor family that grows into a full product model (then it is a blueprint kind of its own, not an import); evidence that the import loses information the recipe needs.