| Testing & Quality |
@@ -576,7 +576,7 @@ Query find_library before writing any infrastructure.
Reference (separate site, mirrors the MCP bundle): the enforceable
-rules — Layering, Composition
+rules — Build Recipe, Layering, Composition
Root, Spec-First
Routing, CRUD
Template, Pipeline
@@ -593,8 +593,8 @@ Registry.
Library registry — the catalog of internal libraries behind every app
(libraries.json, served as registry://libraries)
Template — the runnable skeleton at standards/src/templates/app/
-Copy the repo's AGENTS.md-style build prompt from
- Build Recipe
+Start a build — invoke the start_app prompt on the standards server,
+ or read the Start an App guide
© Aetoskia Internal — standards
diff --git a/standards/wiki/search/search_index.json b/standards/wiki/search/search_index.json
index b8ab9db..3d999d6 100644
--- a/standards/wiki/search/search_index.json
+++ b/standards/wiki/search/search_index.json
@@ -1 +1 @@
-{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"\ud83e\udde9 Standards \u2014 the Aetos App Blueprint","text":"One reusable architecture for every backend application in the Aetos ecosystem: a spec-first HTTP edge, a framework-free domain core, a pluggable infrastructure layer, and a single composition root.
Fill in two axes only and a new app is done: your resource model, and (if needed) your pipeline family. The blueprint itself never names a business domain \u2014 expenses, health metrics, inventory, and analytics are all the same shape underneath.
Doc model: this wiki is written for humans \u2014 how-to guides, the build recipe, and quality gates. The enforceable rules (layering, composition root, CRUD template, pipeline scaffold) and the library registry live in the reference pages, mirrored by the machine-readable standards MCP bundle. Query find_library before writing any infrastructure.
"},{"location":"#key-features","title":"\ud83d\ude80 Key Features","text":" - \ud83e\udded Spec-first routing \u2014 the OpenAPI document, not decorator routers, is the source of truth (operationId lookup)
- \ud83c\udfd7\ufe0f One-way layering \u2014
interfaces \u2192 domain \u2192 infra, enforced - \ud83d\udd0c Single composition root \u2014 every dependency is wired in exactly one place
- \ud83e\udde9 Generic, not app-specific \u2014 reusable rules versus two authoring axes
- \ud83d\udcda Library-backed \u2014 DB, DI, pipelines, ingestion, auth, and docs come from internal libraries, never hand-written
- \ud83d\udea6 Explicit state machines \u2014 durable job statuses, progress, and human-in-the-loop pauses
"},{"location":"#documentation-structure","title":"\ud83d\udcc1 Documentation Structure","text":"Section Description Overview Philosophy, the one-way dependency rule, architecture App Components The component model and how the pieces wire together Build Recipe The ordered, agentic checklist for a new app Testing & Quality Test layout and acceptance gates Reference (separate site, mirrors the MCP bundle): the enforceable rules \u2014 Layering, Composition Root, Spec-First Routing, CRUD Template, Pipeline Scaffold, Worker & Progress, Human-in-the-Loop, Infra Adapters, Genericity Matrix, Library Registry.
"},{"location":"#related-resources","title":"\ud83d\udd17 Related Resources","text":" - MCP bundle \u2014
standards server on :8009: read doc://index, doc://nav, doc://modules/{module}; call find_library(capability) - Library registry \u2014 the catalog of internal libraries behind every app (
libraries.json, served as registry://libraries) - Template \u2014 the runnable skeleton at
standards/src/templates/app/ - Copy the repo's
AGENTS.md-style build prompt from Build Recipe
\u00a9 Aetoskia Internal \u2014 standards
"},{"location":"01_overview/","title":"Overview","text":"The blueprint is a hexagonal (ports & adapters) application with a spec-first web edge and a single composition root. It favors explicit wiring over implicit magic and existing internal libraries over hand-written infrastructure. Start here to build the mental model; the enforceable details live in the reference pages.
"},{"location":"01_overview/#architecture","title":"\ud83c\udfd7\ufe0f Architecture","text":"flowchart LR\n subgraph EDGE[\"interfaces/ \u2014 HTTP edge\"]\n OAS[\"openapi.yaml<br/>operationId contract\"]\n RT[\"<resource>/routes.py<br/>thin handlers\"]\n end\n\n subgraph CORE[\"domain/ \u2014 business core\"]\n MD[\"models\"]\n PT[\"ports (adapter binding seam)\"]\n SV[\"services\"]\n end\n\n subgraph INFRA[\"infra/ \u2014 adapters\"]\n RP[\"mongo repos (mongo-ops)\"]\n AD[\"cache \u00b7 stores \u00b7 llm \u00b7 mail\"]\n DB[(\"MongoDB\")]\n end\n\n CR[\"init.py<br/>composition root\"]\n\n OAS --> RT\n RT --> SV\n SV --> PT\n SV --> RP\n PT -. depends on .- RP\n CR --- RT\n CR --- SV\n CR --- RP\n RP --> DB\n AD --- CR
The rule holds at every edge: code in an outer ring may depend on a ring closer to the core, never the reverse. domain/ never imports a web framework; only its ports/ seam reaches into infra/ to bind adapters.
Legend \u2014 solid arrows are compile-time calls; dotted arrows are the port/adapter binding, performed by the composition root at startup.
"},{"location":"01_overview/#the-one-way-dependency-rule","title":"\ud83d\udd04 The one-way dependency rule","text":"Ring May import Never imports interfaces/ domain, framework glue (glue only) business logic, infra directly domain/ its own models + its ports/ seam (which binds infra/libraries) web frameworks, interfaces/ infra/ domain ports, drivers, libraries \u2014 init.py everything (the wiring point) \u2014"},{"location":"01_overview/#core-principles","title":"\ud83e\udded Core principles","text":" - One-way dependencies.
interfaces -> domain -> infra is allowed; the reverse is forbidden. - Ports in the domain, adapters in infra. A service depends on a port; the port's factory binds the concrete adapter, and the composition root calls those factories.
- One composition root. Nothing constructs its own dependencies. All wiring lives in one module (
init.py) exposed as a flat resource registry. - Spec-first. The HTTP contract is declared in
openapi.yaml; route handlers are looked up by operationId. There are no decorator routers. - Reuse the libraries. Orchestration, DI, Mongo repositories, ingestion, auth, and docs come from internal libraries \u2014 see the Library Registry.
- Explicit state machines. Long-running work moves through explicit, durable statuses and can pause for human input.
"},{"location":"01_overview/#runtime-processes","title":"\u2699\ufe0f Runtime processes","text":"A standard app may run as up to three processes that share the same composition root:
Process Entry point Responsibility API main.py serve HTTP; start/stop resources via lifespan Worker worker.py poll durable jobs, run pipelines, publish progress Scripts python -m scripts.<name> one-shot operator tasks"},{"location":"01_overview/#read-next","title":"\u27a1\ufe0f Read Next","text":" - 02 \u2013 App Components \u00b7 03 \u2013 Build Recipe
"},{"location":"02_components/","title":"App Components","text":"What ships in every standard app, and how the pieces relate. This is the blueprint's component model \u2014 templates/app/ is a runnable instance of it.
"},{"location":"02_components/#component-model","title":"\ud83d\uddc2\ufe0f Component model","text":"The blueprint has two orthogonal views: runtime flow (who calls whom) and composition-root wiring (who constructs whom). Keeping them separate makes the layering visible instead of hiding it behind a hub.
"},{"location":"02_components/#runtime-flow","title":"\ud83d\udd00 Runtime flow","text":"Who calls whom at request and job time:
%%{init: {\"flowchart\": {\"nodeSpacing\": 50, \"rankSpacing\": 80}}}%%\nflowchart TB\n subgraph REQ[\"Request path \u2014 API process\"]\n direction LR\n CL([\"Client<br/>operationId\"]) --> RT[\"interfaces/<resource>/routes.py\"]\n RT --> SVC[\"<resource>_svc\"]\n SVC --> REPO[\"<resource>_repo\"]\n end\n\n subgraph WRK[\"Background path \u2014 worker process\"]\n direction LR\n W[\"worker.py\"] --> JSVC[\"job_svc\"]\n W --> PIPE[\"pipelines/<family>\"]\n PIPE --> ISVC[\"item_svc\"]\n JSVC --> JREPO[\"job_repo\"]\n ISVC --> IREPO[\"item_repo\"]\n end\n\n subgraph INFRA[\"infra/ \u2014 adapters\"]\n MONGO[(\"MongoDB / mongo_ops\")]\n end\n\n REPO --> MONGO\n JREPO --> MONGO\n IREPO --> MONGO
Legend \u2014 solid arrows are runtime calls. Nothing is self-constructed.
"},{"location":"02_components/#composition-root-wiring","title":"\ud83e\uddf5 Composition-root wiring","text":"Who constructs what at startup:
%%{init: {\"flowchart\": {\"nodeSpacing\": 50, \"rankSpacing\": 80}}}%%\nflowchart TB\n INIT[\"init.py \u2014 composition root\"]\n SETUP[\"infra/db/setup.py\"]\n FACT[\"domain/*/ports/*<br/>factory seams\"]\n REPOS[\"repos<br/>infra/db/repos/mongo.py\"]\n SVCES[\"services<br/>CrudsServiceAdaptor\"]\n STATE[\"app.state.db_resources\"]\n HEXA[\"hexa.build\"]\n RT[\"routes.py\"]\n WK[\"worker.py\"]\n\n INIT --> SETUP\n INIT --> FACT\n FACT --> REPOS\n FACT --> SVCES\n REPOS --> SVCES\n HEXA -.-> SVCES\n INIT --> STATE\n STATE --> RT\n INIT --> WK
Legend \u2014 init.py is the only place that imports concrete adapters. The ports/ factory seams bind infra; the API reads resources from app.state.db_resources, the worker calls init() directly.
"},{"location":"02_components/#component-roles","title":"\ud83d\udce6 Component roles","text":""},{"location":"02_components/#1-interfaces-the-edge","title":"1. interfaces/ \u2014 the edge","text":"Thin handlers bound to openapi.yaml operationIds. They parse the request, call a service, and shape the response. No business rules, no I/O.
interfaces/openapi.py re-exports every route module so the OpenAPI-first app can resolve handlers. - Routes may mount the
cache decorator on read endpoints.
"},{"location":"02_components/#2-domain-the-core","title":"2. domain/ \u2014 the core","text":"Models, ports, and services (use cases). No framework imports. The ports/ package is the one seam allowed to reach into infra/: each port module declares the document shape and a factory that binds the adapter.
<aggregate>/models.py \u2014 dataclasses, enums, document shapes. <aggregate>/ports/db.py \u2014 document model + create_<aggregate>_repo(). <aggregate>/ports/service.py \u2014 service + get_<aggregate>_service(repo). - Services compose ports and enforce the business rules; the port factories bind the implementations, and the composition root calls them.
"},{"location":"02_components/#3-infra-the-adapters","title":"3. infra/ \u2014 the adapters","text":"Concrete implementations of the domain ports plus cross-cutting infrastructure:
db/ \u2014 mongo-ops repositories and the connection lifecycle. adapters/ \u2014 cache store, progress bus, storages, fetchers (library-backed). - Anything else the domain needs, implemented against its ports.
"},{"location":"02_components/#4-pipelines-data-driven-families-optional","title":"4. pipelines/ \u2014 data-driven families (optional)","text":"Extraction and enrichment families, each a deterministic step chain (abc.py / impl.py / pipeline.yaml / resources/). Roots are registered in the pattern registry and driven either by a worker job or a CLI.
"},{"location":"02_components/#5-workerpy-the-job-worker-optional","title":"5. worker.py \u2014 the job worker (optional)","text":"A separate process that polls durable job collections, runs pipeline roots, and publishes progress to the progress bus. It shares the same composition root as the API.
"},{"location":"02_components/#6-initpy-the-composition-root","title":"6. init.py \u2014 the composition root","text":"The only place anything is constructed: leaf repositories \u2192 dependent repositories \u2192 services \u2192 adapters. Exposes get_svc overloads and starts / stops resources on the app lifespan.
"},{"location":"02_components/#how-the-request-flows","title":"\ud83d\udd17 How the request flows","text":"sequenceDiagram\n participant C as Client\n participant R as route (interfaces)\n participant S as service (port factory / adapter)\n participant P as repo (infra)\n participant DB as MongoDB\n\n C->>R: POST /items (operationId)\n R->>S: svc.create(payload)\n S->>P: repo.insert(doc)\n P->>DB: insert\n DB-->>P: ack\n P-->>S: doc\n S-->>R: result\n R-->>C: 200 JSON
"},{"location":"02_components/#related","title":"Related","text":" - 01 \u2013 Overview \u00b7 03 \u2013 Build Recipe \u00b7 04 \u2013 Testing & Quality
"},{"location":"03_build_recipe/","title":"Build Recipe","text":"The ordered, deterministic checklist for generating a new application of the standard shape. Every step is fixed; only steps 2 and 3 need app-specific authoring. Give an AI agent the copyable prompt below (or read it from the standards MCP bundle as doc://modules/build_recipe) and it can produce a complete app in one pass.
"},{"location":"03_build_recipe/#quick-start","title":"\ud83d\ude80 Quick start","text":"# Copy the runnable skeleton (from the standards docs checkout)\ncp -r <docs>/standards/src/templates/app apps/<your-app>\ncd apps/<your-app>\n
Then follow the numbered steps in the prompt.
"},{"location":"03_build_recipe/#agent-prompt-copyable","title":"\ud83d\udccb Agent prompt (copyable)","text":"# Build a new application from the Aetos Standards blueprint\n\nYou are coding a complete new backend application conforming to the Aetos\nstandard app blueprint. Execute every step in order. Only steps 2 and 3 use the\napp-specific details provided by the user; everything else is deterministic.\n\nBefore writing any code, connect to the `standards` MCP server and read:\n- `doc://nav` (the module list)\n- `doc://modules/layering_rules` (the dependency rules)\n- `doc://modules/genericity_matrix` (what is generic vs app-specific)\nThen call `find_library` with capabilities like `db`, `di`, `routing`,\n`ingestion`, `auth`, `docs`, `pipeline`, and note the libraries that already\nexist. Never reimplement one.\n\n1. **Scaffold** \u2014 copy `src/templates/app/` into `apps/<your-app>/`. Keep the\n package tree: `domain/`, `infra/`, `interfaces/`, `pipelines/`, `tests/`,\n `scripts/`, plus `pyproject.toml`, `openapi.yaml`, `config.py`, `init.py`,\n `main.py`, `worker.py`, `Dockerfile`, `docker-compose.yaml`, `.env.example`.\n\n2. **Author the spec (app-specific)** \u2014 write `openapi.yaml`: resources,\n schemas, `operationId`s, security, and extension metadata. This is the\n contract and drives all routing.\n\n3. **Write each CRUD slice (app-specific)** \u2014 per resource create the\n four-file slice: `models.py`, `ports/db.py`, `ports/service.py`, and\n optional `converters.py`. Follow the CRUD template.\n\n4. **Wire the composition root** \u2014 in `init.py`, build leaf repositories, then\n dependent repositories, then services; add infra adapters (db, cache,\n stores, progress bus, fetchers). Expose `get_svc` overloads.\n\n5. **Add routes** \u2014 create `interfaces/<resource>/routes.py` thin handlers\n bound to `operationId`s and re-export them in `interfaces/openapi.py`. Add\n the cache decorator to read endpoints.\n\n6. **Bootstrap app and worker** \u2014 confirm `main.py` runs the OpenAPI-first app\n with its lifespan. If the app has long-running jobs, adapt `worker.py` to\n poll the job collection, run the pipeline, and publish progress.\n\n7. **Add pipeline families (only if data-driven)** \u2014 create\n `pipelines/<family>/` with `abc.py`, `impl.py`, `pipeline.yaml`,\n `resources/`, and register the root. Add fixtures and a parity test.\n\n8. **Add infrastructure only where needed** \u2014 prefer a library-backed adapter.\n Never hand-write what an internal library already provides.\n\n9. **Tests** \u2014 integration tests with fakes at the HTTP edge; unit tests for\n domain and pipeline logic; a config-consistency test for rule-driven\n pipelines.\n\n10. **Scripts and seeds** \u2014 operator scripts under `scripts/` using\n `init()` / `get_svc()` / `shutdown()`.\n\n11. **Docs** \u2014 author GSDFC docstrings, generate `.pyi` stubs, mark the package\n `py.typed`, and build the docs.\n\n**Definition of done:**\n- `python -c \"import main\"` succeeds.\n- `pytest` passes.\n- The app serves every operation in the spec; the worker drains its queue.\n- No domain module imports a web framework.\n- No infrastructure was reimplemented that a library already provides.\n
"},{"location":"03_build_recipe/#where-the-steps-land","title":"\ud83e\udded Where the steps land","text":"The steps map onto the layers as follows:
Step Package / file Depends on 1 \u00b7 Scaffold whole templates/app/ \u2014 2 \u00b7 Spec openapi.yaml \u2014 3 \u00b7 CRUD slices domain/<resource>/ spec, CRUD template 4 \u00b7 Wiring init.py every slice 5 \u00b7 Routes interfaces/<resource>/ services, openapi route registry 6 \u00b7 Bootstrap main.py, worker.py composition root 7 \u00b7 Pipelines pipelines/<family>/ library pipeline engine 8 \u00b7 Infra infra/ domain ports 9 \u00b7 Tests tests/ app + fixtures 10 \u00b7 Scripts scripts/ composition root 11 \u00b7 Docs docstrings + docs build code, Doc Forge"},{"location":"03_build_recipe/#definition-of-done","title":"\u26a0\ufe0f Definition of done","text":" python -c \"import main\" succeeds. pytest passes. - The app serves the spec's operations; the worker drains its queue.
- No domain module imports a web framework.
- No infrastructure was reimplemented that a library already provides.
"},{"location":"03_build_recipe/#related","title":"Related","text":" - 01 \u2013 Overview \u00b7 02 \u2013 App Components \u00b7 04 \u2013 Testing & Quality
"},{"location":"04_development/01_testing/","title":"Testing & Quality Gates","text":"Two suites run under pytest with asyncio auto mode: integration (the HTTP edge against fakes) and unit (pure domain and pipeline logic). Finish a blueprint app only when every gate below passes.
"},{"location":"04_development/01_testing/#layout","title":"\ud83d\uddc2\ufe0f Layout","text":"tests/\n integration/ # TestClient(app) + mocked resources\n unit/ # models, converters, pipeline stages/roots\n conftest.py # fixtures\n
"},{"location":"04_development/01_testing/#integration-tests","title":"\ud83d\ude80 Integration tests","text":" - Build the app with
TestClient. - Replace
app.state.db_resources with fake repositories/services so no database is required. - Patch the auth introspection call (
httpx.AsyncClient.post) to return a canned identity. - Assert status codes and bodies for list/get/create/update/delete, plus
400, 404, 204, and the streaming endpoint's fallback.
client = TestClient(app)\napp.state.db_resources = {\"item_svc\": FakeItemService(), ...}\n\ndef test_create_item():\n r = client.post(\"/items\", json={\"name\": \"x\"})\n assert r.status_code == 200\n
"},{"location":"04_development/01_testing/#unit-tests","title":"\ud83e\uddea Unit tests","text":" - Domain models and converters: pure, no I/O.
- Pipeline stages: feed a fixture state, assert the transformed state.
- Assembled pipeline roots: fixture parity \u2014 replay a stored fixture input and compare against a golden output file.
"},{"location":"04_development/01_testing/#config-consistency-tests","title":"\ud83d\udd10 Config-consistency tests","text":"For rule-driven pipelines, add a test that cross-references rules against the declared pattern registry: every flag emitted must be defined; every defined flag must be reachable. This catches drift without running the pipeline.
"},{"location":"04_development/01_testing/#quality-gates","title":"\u2705 Quality gates","text":"A definition of done for any standard app:
python -c \"import main\" succeeds. pytest passes. - The app serves the spec's operations; the worker drains its queue.
- No domain module imports a web framework.
- No infrastructure was reimplemented that a library already provides.
"},{"location":"04_development/01_testing/#conventions","title":"\ud83d\udca1 Conventions","text":" asyncio_mode = \"auto\"; no manual event loops. - Fixtures are checked into the repo (inputs + golden outputs).
- Tests skip (not fail) when a required fixture is absent.
- Do not mock the domain; mock only the infrastructure edge.
"},{"location":"04_development/01_testing/#related","title":"Related","text":" - 02 \u2013 App Components \u00b7 03 \u2013 Build Recipe \u00b7 Overview
"}]}
\ No newline at end of file
+{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"\ud83e\udde9 Standards \u2014 the Aetos App Blueprint","text":"One reusable architecture for every backend application in the Aetos ecosystem: a spec-first HTTP edge, a framework-free domain core, a pluggable infrastructure layer, and a single composition root.
Fill in two axes only and a new app is done: your resource model, and (if needed) your pipeline family. The blueprint itself never names a business domain \u2014 expenses, health metrics, inventory, and analytics are all the same shape underneath.
Doc model: this wiki is written for humans \u2014 how-to guides, the build recipe, and quality gates. The enforceable rules (layering, composition root, CRUD template, pipeline scaffold) and the library registry live in the reference pages, mirrored by the machine-readable standards MCP bundle. Query find_library before writing any infrastructure.
"},{"location":"#key-features","title":"\ud83d\ude80 Key Features","text":" - \ud83e\udded Spec-first routing \u2014 the OpenAPI document, not decorator routers, is the source of truth (operationId lookup)
- \ud83c\udfd7\ufe0f One-way layering \u2014
interfaces \u2192 domain \u2192 infra, enforced - \ud83d\udd0c Single composition root \u2014 every dependency is wired in exactly one place
- \ud83e\udde9 Generic, not app-specific \u2014 reusable rules versus two authoring axes
- \ud83d\udcda Library-backed \u2014 DB, DI, pipelines, ingestion, auth, and docs come from internal libraries, never hand-written
- \ud83d\udea6 Explicit state machines \u2014 durable job statuses, progress, and human-in-the-loop pauses
"},{"location":"#documentation-structure","title":"\ud83d\udcc1 Documentation Structure","text":"Section Description Overview Philosophy, the one-way dependency rule, architecture App Components The component model and how the pieces wire together Start an App The entry point: gather the two axes, then build Testing & Quality Test layout and acceptance gates Reference (separate site, mirrors the MCP bundle): the enforceable rules \u2014 Build Recipe, Layering, Composition Root, Spec-First Routing, CRUD Template, Pipeline Scaffold, Worker & Progress, Human-in-the-Loop, Infra Adapters, Genericity Matrix, Library Registry.
"},{"location":"#related-resources","title":"\ud83d\udd17 Related Resources","text":" - MCP bundle \u2014
standards server on :8009: read doc://index, doc://nav, doc://modules/{module}; call find_library(capability) - Library registry \u2014 the catalog of internal libraries behind every app (
libraries.json, served as registry://libraries) - Template \u2014 the runnable skeleton at
standards/src/templates/app/ - Start a build \u2014 invoke the
start_app prompt on the standards server, or read the Start an App guide
\u00a9 Aetoskia Internal \u2014 standards
"},{"location":"01_overview/","title":"Overview","text":"The blueprint is a hexagonal (ports & adapters) application with a spec-first web edge and a single composition root. It favors explicit wiring over implicit magic and existing internal libraries over hand-written infrastructure. Start here to build the mental model; the enforceable details live in the reference pages.
"},{"location":"01_overview/#architecture","title":"\ud83c\udfd7\ufe0f Architecture","text":"flowchart LR\n subgraph EDGE[\"interfaces/ \u2014 HTTP edge\"]\n OAS[\"openapi.yaml<br/>operationId contract\"]\n RT[\"<resource>/routes.py<br/>thin handlers\"]\n end\n\n subgraph CORE[\"domain/ \u2014 business core\"]\n MD[\"models\"]\n PT[\"ports (adapter binding seam)\"]\n SV[\"services\"]\n end\n\n subgraph INFRA[\"infra/ \u2014 adapters\"]\n RP[\"mongo repos (mongo-ops)\"]\n AD[\"cache \u00b7 stores \u00b7 llm \u00b7 mail\"]\n DB[(\"MongoDB\")]\n end\n\n CR[\"init.py<br/>composition root\"]\n\n OAS --> RT\n RT --> SV\n SV --> PT\n SV --> RP\n PT -. depends on .- RP\n CR --- RT\n CR --- SV\n CR --- RP\n RP --> DB\n AD --- CR
The rule holds at every edge: code in an outer ring may depend on a ring closer to the core, never the reverse. domain/ never imports a web framework; only its ports/ seam reaches into infra/ to bind adapters.
Legend \u2014 solid arrows are compile-time calls; dotted arrows are the port/adapter binding, performed by the composition root at startup.
"},{"location":"01_overview/#the-one-way-dependency-rule","title":"\ud83d\udd04 The one-way dependency rule","text":"Ring May import Never imports interfaces/ domain, framework glue (glue only) business logic, infra directly domain/ its own models + its ports/ seam (which binds infra/libraries) web frameworks, interfaces/ infra/ domain ports, drivers, libraries \u2014 init.py everything (the wiring point) \u2014"},{"location":"01_overview/#core-principles","title":"\ud83e\udded Core principles","text":" - One-way dependencies.
interfaces -> domain -> infra is allowed; the reverse is forbidden. - Ports in the domain, adapters in infra. A service depends on a port; the port's factory binds the concrete adapter, and the composition root calls those factories.
- One composition root. Nothing constructs its own dependencies. All wiring lives in one module (
init.py) exposed as a flat resource registry. - Spec-first. The HTTP contract is declared in
openapi.yaml; route handlers are looked up by operationId. There are no decorator routers. - Reuse the libraries. Orchestration, DI, Mongo repositories, ingestion, auth, and docs come from internal libraries \u2014 see the Library Registry.
- Explicit state machines. Long-running work moves through explicit, durable statuses and can pause for human input.
"},{"location":"01_overview/#runtime-processes","title":"\u2699\ufe0f Runtime processes","text":"A standard app may run as up to three processes that share the same composition root:
Process Entry point Responsibility API main.py serve HTTP; start/stop resources via lifespan Worker worker.py poll durable jobs, run pipelines, publish progress Scripts python -m scripts.<name> one-shot operator tasks"},{"location":"01_overview/#read-next","title":"\u27a1\ufe0f Read Next","text":" - 02 \u2013 App Components \u00b7 03 \u2013 Start an App
"},{"location":"02_components/","title":"App Components","text":"What ships in every standard app, and how the pieces relate. This is the blueprint's component model \u2014 templates/app/ is a runnable instance of it.
"},{"location":"02_components/#component-model","title":"\ud83d\uddc2\ufe0f Component model","text":"The blueprint has two orthogonal views: runtime flow (who calls whom) and composition-root wiring (who constructs whom). Keeping them separate makes the layering visible instead of hiding it behind a hub.
"},{"location":"02_components/#runtime-flow","title":"\ud83d\udd00 Runtime flow","text":"Who calls whom at request and job time:
%%{init: {\"flowchart\": {\"nodeSpacing\": 50, \"rankSpacing\": 80}}}%%\nflowchart TB\n subgraph REQ[\"Request path \u2014 API process\"]\n direction LR\n CL([\"Client<br/>operationId\"]) --> RT[\"interfaces/<resource>/routes.py\"]\n RT --> SVC[\"<resource>_svc\"]\n SVC --> REPO[\"<resource>_repo\"]\n end\n\n subgraph WRK[\"Background path \u2014 worker process\"]\n direction LR\n W[\"worker.py\"] --> JSVC[\"job_svc\"]\n W --> PIPE[\"pipelines/<family>\"]\n PIPE --> ISVC[\"item_svc\"]\n JSVC --> JREPO[\"job_repo\"]\n ISVC --> IREPO[\"item_repo\"]\n end\n\n subgraph INFRA[\"infra/ \u2014 adapters\"]\n MONGO[(\"MongoDB / mongo_ops\")]\n end\n\n REPO --> MONGO\n JREPO --> MONGO\n IREPO --> MONGO
Legend \u2014 solid arrows are runtime calls. Nothing is self-constructed.
"},{"location":"02_components/#composition-root-wiring","title":"\ud83e\uddf5 Composition-root wiring","text":"Who constructs what at startup:
%%{init: {\"flowchart\": {\"nodeSpacing\": 50, \"rankSpacing\": 80}}}%%\nflowchart TB\n INIT[\"init.py \u2014 composition root\"]\n SETUP[\"infra/db/setup.py\"]\n FACT[\"domain/*/ports/*<br/>factory seams\"]\n REPOS[\"repos<br/>infra/db/repos/mongo.py\"]\n SVCES[\"services<br/>CrudsServiceAdaptor\"]\n STATE[\"app.state.db_resources\"]\n HEXA[\"hexa.build\"]\n RT[\"routes.py\"]\n WK[\"worker.py\"]\n\n INIT --> SETUP\n INIT --> FACT\n FACT --> REPOS\n FACT --> SVCES\n REPOS --> SVCES\n HEXA -.-> SVCES\n INIT --> STATE\n STATE --> RT\n INIT --> WK
Legend \u2014 init.py is the only place that imports concrete adapters. The ports/ factory seams bind infra; the API reads resources from app.state.db_resources, the worker calls init() directly.
"},{"location":"02_components/#component-roles","title":"\ud83d\udce6 Component roles","text":""},{"location":"02_components/#1-interfaces-the-edge","title":"1. interfaces/ \u2014 the edge","text":"Thin handlers bound to openapi.yaml operationIds. They parse the request, call a service, and shape the response. No business rules, no I/O.
interfaces/openapi.py re-exports every route module so the OpenAPI-first app can resolve handlers. - Routes may mount the
cache decorator on read endpoints.
"},{"location":"02_components/#2-domain-the-core","title":"2. domain/ \u2014 the core","text":"Models, ports, and services (use cases). No framework imports. The ports/ package is the one seam allowed to reach into infra/: each port module declares the document shape and a factory that binds the adapter.
<aggregate>/models.py \u2014 dataclasses, enums, document shapes. <aggregate>/ports/db.py \u2014 document model + create_<aggregate>_repo(). <aggregate>/ports/service.py \u2014 service + get_<aggregate>_service(repo). - Services compose ports and enforce the business rules; the port factories bind the implementations, and the composition root calls them.
"},{"location":"02_components/#3-infra-the-adapters","title":"3. infra/ \u2014 the adapters","text":"Concrete implementations of the domain ports plus cross-cutting infrastructure:
db/ \u2014 mongo-ops repositories and the connection lifecycle. adapters/ \u2014 cache store, progress bus, storages, fetchers (library-backed). - Anything else the domain needs, implemented against its ports.
"},{"location":"02_components/#4-pipelines-data-driven-families-optional","title":"4. pipelines/ \u2014 data-driven families (optional)","text":"Extraction and enrichment families, each a deterministic step chain (abc.py / impl.py / pipeline.yaml / resources/). Roots are registered in the pattern registry and driven either by a worker job or a CLI.
"},{"location":"02_components/#5-workerpy-the-job-worker-optional","title":"5. worker.py \u2014 the job worker (optional)","text":"A separate process that polls durable job collections, runs pipeline roots, and publishes progress to the progress bus. It shares the same composition root as the API.
"},{"location":"02_components/#6-initpy-the-composition-root","title":"6. init.py \u2014 the composition root","text":"The only place anything is constructed: leaf repositories \u2192 dependent repositories \u2192 services \u2192 adapters. Exposes get_svc overloads and starts / stops resources on the app lifespan.
"},{"location":"02_components/#how-the-request-flows","title":"\ud83d\udd17 How the request flows","text":"sequenceDiagram\n participant C as Client\n participant R as route (interfaces)\n participant S as service (port factory / adapter)\n participant P as repo (infra)\n participant DB as MongoDB\n\n C->>R: POST /items (operationId)\n R->>S: svc.create(payload)\n S->>P: repo.insert(doc)\n P->>DB: insert\n DB-->>P: ack\n P-->>S: doc\n S-->>R: result\n R-->>C: 200 JSON
"},{"location":"02_components/#related","title":"Related","text":" - 01 \u2013 Overview \u00b7 03 \u2013 Start an App \u00b7 04 \u2013 Testing & Quality
"},{"location":"03_start_an_app/","title":"\ud83d\ude80 Start an App","text":"This is the entry point for building a new application from the blueprint. The goal is one deterministic pass: you supply only the two axes, and the agent does the rest by reading the standards MCP, importing Tier A libraries, and copying Tier B template components.
Doc model: this page is the human entry point. The deterministic, machine-facing checklist lives in the reference page Build Recipe, served as doc://modules/build_recipe.
"},{"location":"03_start_an_app/#what-the-agent-needs-from-you","title":"\ud83e\uddfe What the agent needs from you","text":"Gather these before starting \u2014 the start_app MCP prompt asks for them:
- App name + one-line purpose.
- Axis 1 \u2014 resources. Each resource, its fields, and relationships. Paste
openapi.yaml if you already have it. - Axis 2 \u2014 pipelines. Only if an input needs a multi-step transformation: the families and their input formats (or \"none\").
- Auth needed now? Default no; auth is deferred (
py-jwt only). - Integrations. Mongo, ingestion formats, email, LLM provider.
If you can state axes 1 and 2, the rest is mechanical.
"},{"location":"03_start_an_app/#how-to-start","title":"\u25b6\ufe0f How to start","text":"From an MCP client connected to the standards server:
- Invoke the
start_app prompt with no arguments to get the intake questions. - Answer them, then invoke
start_app(app_name=..., resources=..., pipelines=..., auth=...). - The prompt hands off to
doc://modules/build_recipe; the agent pulls exact files with get_app_component(kind, resource=..., family=...) and authors only the app-specific parts.
Manual alternative \u2014 copy the skeleton and follow the reference recipe:
cp -r <docs>/standards/src/templates/app apps/<your-app>\n
"},{"location":"03_start_an_app/#reference","title":"\ud83d\udcda Reference","text":" - Build Recipe \u2014 the deterministic checklist
- Genericity Matrix \u2014 Tier A / B / C
- Library Registry \u2014 the Tier A catalog
- App Components \u2014 the component model
"},{"location":"03_start_an_app/#related","title":"Related","text":" - 01 \u2013 Overview \u00b7 02 \u2013 App Components \u00b7 04 \u2013 Testing & Quality
"},{"location":"04_development/01_testing/","title":"Testing & Quality Gates","text":"Two suites run under pytest with asyncio auto mode: integration (the HTTP edge against fakes) and unit (pure domain and pipeline logic). Finish a blueprint app only when every gate below passes.
"},{"location":"04_development/01_testing/#layout","title":"\ud83d\uddc2\ufe0f Layout","text":"tests/\n integration/ # TestClient(app) + mocked resources\n unit/ # models, converters, pipeline stages/roots\n conftest.py # fixtures\n
"},{"location":"04_development/01_testing/#integration-tests","title":"\ud83d\ude80 Integration tests","text":" - Build the app with
TestClient. - Replace
app.state.db_resources with fake repositories/services so no database is required. - Patch the auth introspection call (
httpx.AsyncClient.post) to return a canned identity. - Assert status codes and bodies for list/get/create/update/delete, plus
400, 404, 204, and the streaming endpoint's fallback.
client = TestClient(app)\napp.state.db_resources = {\"item_svc\": FakeItemService(), ...}\n\ndef test_create_item():\n r = client.post(\"/items\", json={\"name\": \"x\"})\n assert r.status_code == 200\n
"},{"location":"04_development/01_testing/#unit-tests","title":"\ud83e\uddea Unit tests","text":" - Domain models and converters: pure, no I/O.
- Pipeline stages: feed a fixture state, assert the transformed state.
- Assembled pipeline roots: fixture parity \u2014 replay a stored fixture input and compare against a golden output file.
"},{"location":"04_development/01_testing/#config-consistency-tests","title":"\ud83d\udd10 Config-consistency tests","text":"For rule-driven pipelines, add a test that cross-references rules against the declared pattern registry: every flag emitted must be defined; every defined flag must be reachable. This catches drift without running the pipeline.
"},{"location":"04_development/01_testing/#quality-gates","title":"\u2705 Quality gates","text":"A definition of done for any standard app:
python -c \"import main\" succeeds. pytest passes. - The app serves the spec's operations; the worker drains its queue.
- No domain module imports a web framework.
- No infrastructure was reimplemented that a library already provides.
"},{"location":"04_development/01_testing/#conventions","title":"\ud83d\udca1 Conventions","text":" asyncio_mode = \"auto\"; no manual event loops. - Fixtures are checked into the repo (inputs + golden outputs).
- Tests skip (not fail) when a required fixture is absent.
- Do not mock the domain; mock only the infrastructure edge.
"},{"location":"04_development/01_testing/#related","title":"Related","text":" - 02 \u2013 App Components \u00b7 03 \u2013 Start an App \u00b7 Overview
"}]}
\ No newline at end of file