Files
docs/standards/wiki/search/search_index.json
Vishesh 'ironeagle' Bangotra fced40af59 feat(standards): codify codegen, ship generic Tier-B infra, add get_app_template + prompts; fix docs:// regressions
P0: fix doc:// -> docs:// across standards src/server/registry and regenerate stale mongo-ops bundle. P1: build recipe + spec-first routing now document the openapi-first codegen fast path (models/routes with --inject-request/--package). P4: CRUD template documents the model-vs-document id rule (BaseDocument owns id). P5: authless guidance (omit security keys). P6: template gains generic infra (http_cache, keyed SSE bus, KV port+in-memory+Redis), interfaces/openapi.py + .dockerignore + generic build test registered as components, and a get_app_template tool; genericity matrix marks LLM pool + human-in-loop as reference-app patterns. P7: intake/build/start_app/trial prompts. P8: template ships tests/integration/test_openapi_app_builds.py. Rebuilt standards + openapi-first bundles and re-collected.
2026-10-13 03:26:51 +05:30

1 line
19 KiB
JSON

{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"\ud83e\udde9 Standards \u2014 the Aetos App Blueprint","text":"<p>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.</p> <p>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.</p> <p>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 <code>standards</code> MCP bundle. Query <code>find_library</code> before writing any infrastructure.</p>"},{"location":"#key-features","title":"\ud83d\ude80 Key Features","text":"<ul> <li>\ud83e\udded Spec-first routing \u2014 the OpenAPI document, not decorator routers, is the source of truth (operationId lookup)</li> <li>\ud83c\udfd7\ufe0f One-way layering \u2014 <code>interfaces \u2192 domain \u2192 infra</code>, enforced</li> <li>\ud83d\udd0c Single composition root \u2014 every dependency is wired in exactly one place</li> <li>\ud83e\udde9 Generic, not app-specific \u2014 reusable rules versus two authoring axes</li> <li>\ud83d\udcda Library-backed \u2014 DB, DI, pipelines, ingestion, auth, and docs come from internal libraries, never hand-written</li> <li>\ud83d\udea6 Explicit state machines \u2014 durable job statuses, progress, and human-in-the-loop pauses</li> </ul>"},{"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 &amp; Quality Test layout and acceptance gates <p>Reference (separate site, mirrors the MCP bundle): the enforceable rules \u2014 Build Recipe, Layering, Composition Root, Spec-First Routing, CRUD Template, Pipeline Scaffold, Worker &amp; Progress, Human-in-the-Loop, Infra Adapters, Genericity Matrix, Library Registry.</p>"},{"location":"#related-resources","title":"\ud83d\udd17 Related Resources","text":"<ul> <li>MCP bundle \u2014 <code>standards</code> server on <code>:8009</code>: read <code>docs://index</code>, <code>docs://nav</code>, <code>docs://modules/{module}</code>; call <code>find_library(capability)</code></li> <li>Library registry \u2014 the catalog of internal libraries behind every app (<code>libraries.json</code>, served as <code>registry://libraries</code>)</li> <li>Template \u2014 the runnable skeleton at <code>standards/src/templates/app/</code></li> <li>Start a build \u2014 invoke the <code>start_app</code> prompt on the <code>standards</code> server, or read the Start an App guide</li> </ul> <p>\u00a9 Aetoskia Internal \u2014 <code>standards</code></p>"},{"location":"01_overview/","title":"Overview","text":"<p>The blueprint is a hexagonal (ports &amp; 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.</p>"},{"location":"01_overview/#architecture","title":"\ud83c\udfd7\ufe0f Architecture","text":"<pre><code>flowchart LR\n subgraph EDGE[\"interfaces/ \u2014 HTTP edge\"]\n OAS[\"openapi.yaml&lt;br/&gt;operationId contract\"]\n RT[\"&lt;resource&gt;/routes.py&lt;br/&gt;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&lt;br/&gt;composition root\"]\n\n OAS --&gt; RT\n RT --&gt; SV\n SV --&gt; PT\n SV --&gt; RP\n PT -. depends on .- RP\n CR --- RT\n CR --- SV\n CR --- RP\n RP --&gt; DB\n AD --- CR</code></pre> <p>The rule holds at every edge: code in an outer ring may depend on a ring closer to the core, never the reverse. <code>domain/</code> never imports a web framework; only its <code>ports/</code> seam reaches into <code>infra/</code> to bind adapters.</p> <p>Legend \u2014 solid arrows are compile-time calls; dotted arrows are the port/adapter binding, performed by the composition root at startup.</p>"},{"location":"01_overview/#the-one-way-dependency-rule","title":"\ud83d\udd04 The one-way dependency rule","text":"Ring May import Never imports <code>interfaces/</code> <code>domain</code>, framework glue (glue only) business logic, <code>infra</code> directly <code>domain/</code> its own models + its <code>ports/</code> seam (which binds <code>infra</code>/libraries) web frameworks, <code>interfaces/</code> <code>infra/</code> <code>domain</code> ports, drivers, libraries \u2014 <code>init.py</code> everything (the wiring point) \u2014"},{"location":"01_overview/#core-principles","title":"\ud83e\udded Core principles","text":"<ol> <li>One-way dependencies. <code>interfaces -&gt; domain -&gt; infra</code> is allowed; the reverse is forbidden.</li> <li>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.</li> <li>One composition root. Nothing constructs its own dependencies. All wiring lives in one module (<code>init.py</code>) exposed as a flat resource registry.</li> <li>Spec-first. The HTTP contract is declared in <code>openapi.yaml</code>; route handlers are looked up by <code>operationId</code>. There are no decorator routers.</li> <li>Reuse the libraries. Orchestration, DI, Mongo repositories, ingestion, auth, and docs come from internal libraries \u2014 see the Library Registry.</li> <li>Explicit state machines. Long-running work moves through explicit, durable statuses and can pause for human input.</li> </ol>"},{"location":"01_overview/#runtime-processes","title":"\u2699\ufe0f Runtime processes","text":"<p>A standard app may run as up to three processes that share the same composition root:</p> Process Entry point Responsibility API <code>main.py</code> serve HTTP; start/stop resources via lifespan Worker <code>worker.py</code> poll durable jobs, run pipelines, publish progress Scripts <code>python -m scripts.&lt;name&gt;</code> one-shot operator tasks"},{"location":"01_overview/#read-next","title":"\u27a1\ufe0f Read Next","text":"<ul> <li>02 \u2013 App Components \u00b7 03 \u2013 Start an App</li> </ul>"},{"location":"02_components/","title":"App Components","text":"<p>What ships in every standard app, and how the pieces relate. This is the blueprint's component model \u2014 <code>templates/app/</code> is a runnable instance of it.</p>"},{"location":"02_components/#component-model","title":"\ud83d\uddc2\ufe0f Component model","text":"<p>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.</p>"},{"location":"02_components/#runtime-flow","title":"\ud83d\udd00 Runtime flow","text":"<p>Who calls whom at request and job time:</p> <pre><code>%%{init: {\"flowchart\": {\"nodeSpacing\": 50, \"rankSpacing\": 80}}}%%\nflowchart TB\n subgraph REQ[\"Request path \u2014 API process\"]\n direction LR\n CL([\"Client&lt;br/&gt;operationId\"]) --&gt; RT[\"interfaces/&amp;lt;resource&amp;gt;/routes.py\"]\n RT --&gt; SVC[\"&amp;lt;resource&amp;gt;_svc\"]\n SVC --&gt; REPO[\"&amp;lt;resource&amp;gt;_repo\"]\n end\n\n subgraph WRK[\"Background path \u2014 worker process\"]\n direction LR\n W[\"worker.py\"] --&gt; JSVC[\"job_svc\"]\n W --&gt; PIPE[\"pipelines/&amp;lt;family&amp;gt;\"]\n PIPE --&gt; ISVC[\"item_svc\"]\n JSVC --&gt; JREPO[\"job_repo\"]\n ISVC --&gt; IREPO[\"item_repo\"]\n end\n\n subgraph INFRA[\"infra/ \u2014 adapters\"]\n MONGO[(\"MongoDB / mongo_ops\")]\n end\n\n REPO --&gt; MONGO\n JREPO --&gt; MONGO\n IREPO --&gt; MONGO</code></pre> <p>Legend \u2014 solid arrows are runtime calls. Nothing is self-constructed.</p>"},{"location":"02_components/#composition-root-wiring","title":"\ud83e\uddf5 Composition-root wiring","text":"<p>Who constructs what at startup:</p> <pre><code>%%{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/*&lt;br/&gt;factory seams\"]\n REPOS[\"repos&lt;br/&gt;infra/db/repos/mongo.py\"]\n SVCES[\"services&lt;br/&gt;CrudsServiceAdaptor\"]\n STATE[\"app.state.db_resources\"]\n HEXA[\"hexa.build\"]\n RT[\"routes.py\"]\n WK[\"worker.py\"]\n\n INIT --&gt; SETUP\n INIT --&gt; FACT\n FACT --&gt; REPOS\n FACT --&gt; SVCES\n REPOS --&gt; SVCES\n HEXA -.-&gt; SVCES\n INIT --&gt; STATE\n STATE --&gt; RT\n INIT --&gt; WK</code></pre> <p>Legend \u2014 <code>init.py</code> is the only place that imports concrete adapters. The <code>ports/</code> factory seams bind <code>infra</code>; the API reads resources from <code>app.state.db_resources</code>, the worker calls <code>init()</code> directly.</p>"},{"location":"02_components/#component-roles","title":"\ud83d\udce6 Component roles","text":""},{"location":"02_components/#1-interfaces-the-edge","title":"1. <code>interfaces/</code> \u2014 the edge","text":"<p>Thin handlers bound to <code>openapi.yaml</code> <code>operationId</code>s. They parse the request, call a service, and shape the response. No business rules, no I/O.</p> <ul> <li><code>interfaces/openapi.py</code> re-exports every route module so the OpenAPI-first app can resolve handlers.</li> <li>Routes may mount the <code>cache</code> decorator on read endpoints.</li> </ul>"},{"location":"02_components/#2-domain-the-core","title":"2. <code>domain/</code> \u2014 the core","text":"<p>Models, ports, and services (use cases). No framework imports. The <code>ports/</code> package is the one seam allowed to reach into <code>infra/</code>: each port module declares the document shape and a factory that binds the adapter.</p> <ul> <li><code>&lt;aggregate&gt;/models.py</code> \u2014 dataclasses, enums, document shapes.</li> <li><code>&lt;aggregate&gt;/ports/db.py</code> \u2014 document model + <code>create_&lt;aggregate&gt;_repo()</code>.</li> <li><code>&lt;aggregate&gt;/ports/service.py</code> \u2014 service + <code>get_&lt;aggregate&gt;_service(repo)</code>.</li> <li>Services compose ports and enforce the business rules; the port factories bind the implementations, and the composition root calls them.</li> </ul>"},{"location":"02_components/#3-infra-the-adapters","title":"3. <code>infra/</code> \u2014 the adapters","text":"<p>Concrete implementations of the domain ports plus cross-cutting infrastructure:</p> <ul> <li><code>db/</code> \u2014 mongo-ops repositories and the connection lifecycle.</li> <li><code>adapters/</code> \u2014 cache store, progress bus, storages, fetchers (library-backed).</li> <li>Anything else the domain needs, implemented against its ports.</li> </ul>"},{"location":"02_components/#4-pipelines-data-driven-families-optional","title":"4. <code>pipelines/</code> \u2014 data-driven families (optional)","text":"<p>Extraction and enrichment families, each a deterministic step chain (<code>abc.py</code> / <code>impl.py</code> / <code>pipeline.yaml</code> / <code>resources/</code>). Roots are registered in the pattern registry and driven either by a worker job or a CLI.</p>"},{"location":"02_components/#5-workerpy-the-job-worker-optional","title":"5. <code>worker.py</code> \u2014 the job worker (optional)","text":"<p>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.</p>"},{"location":"02_components/#6-initpy-the-composition-root","title":"6. <code>init.py</code> \u2014 the composition root","text":"<p>The only place anything is constructed: leaf repositories \u2192 dependent repositories \u2192 services \u2192 adapters. Exposes <code>get_svc</code> overloads and starts / stops resources on the app lifespan.</p>"},{"location":"02_components/#how-the-request-flows","title":"\ud83d\udd17 How the request flows","text":"<pre><code>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-&gt;&gt;R: POST /items (operationId)\n R-&gt;&gt;S: svc.create(payload)\n S-&gt;&gt;P: repo.insert(doc)\n P-&gt;&gt;DB: insert\n DB--&gt;&gt;P: ack\n P--&gt;&gt;S: doc\n S--&gt;&gt;R: result\n R--&gt;&gt;C: 200 JSON</code></pre>"},{"location":"02_components/#related","title":"Related","text":"<ul> <li>01 \u2013 Overview \u00b7 03 \u2013 Start an App \u00b7 04 \u2013 Testing &amp; Quality</li> </ul>"},{"location":"03_start_an_app/","title":"\ud83d\ude80 Start an App","text":"<p>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 <code>standards</code> MCP, importing Tier A libraries, and copying Tier B template components.</p> <p>Doc model: this page is the human entry point. The deterministic, machine-facing checklist lives in the reference page Build Recipe, served as <code>docs://modules/build_recipe</code>.</p>"},{"location":"03_start_an_app/#what-the-agent-needs-from-you","title":"\ud83e\uddfe What the agent needs from you","text":"<p>Gather these before starting \u2014 the <code>start_app</code> MCP prompt asks for them:</p> <ol> <li>App name + one-line purpose.</li> <li>Axis 1 \u2014 resources. Each resource, its fields, and relationships. Paste <code>openapi.yaml</code> if you already have it.</li> <li>Axis 2 \u2014 pipelines. Only if an input needs a multi-step transformation: the families and their input formats (or \"none\").</li> <li>Auth needed now? Default no. Auth is deferred (<code>py-jwt</code> primitives only), and an authless app is just a spec with no <code>security</code> keys \u2014 see Spec-First Routing \u2192 Authless apps.</li> <li>Integrations. Mongo, ingestion formats, email, LLM provider.</li> </ol> <p>If you can state axes 1 and 2, the rest is mechanical.</p>"},{"location":"03_start_an_app/#how-to-start","title":"\u25b6\ufe0f How to start","text":"<p>From an MCP client connected to the <code>standards</code> server:</p> <ol> <li>Invoke the <code>intake</code> prompt (or <code>start_app</code> with no arguments) to get the intake questions.</li> <li>Answer them, then invoke <code>build</code> (<code>build(app_name=..., resources=..., pipelines=..., auth=...)</code>), or use <code>start_app</code> to intake-and-build in one call.</li> <li>The prompt hands off to <code>docs://modules/build_recipe</code>; the agent scaffolds with <code>get_app_template()</code> and pulls exact files with <code>get_app_component(kind, resource=..., family=...)</code>, authoring only the app-specific parts.</li> <li>To validate the standard without touching a real app, invoke <code>trial</code> (<code>trial(app_name=..., build_path=...)</code>) to build into a scratch directory, prove it builds, and report findings.</li> </ol> <p>Manual alternative \u2014 copy the skeleton and follow the reference recipe:</p> <pre><code>cp -r &lt;docs&gt;/standards/src/templates/app apps/&lt;your-app&gt;\n</code></pre>"},{"location":"03_start_an_app/#reference","title":"\ud83d\udcda Reference","text":"<ul> <li>Build Recipe \u2014 the deterministic checklist</li> <li>Genericity Matrix \u2014 Tier A / B / C</li> <li>Library Registry \u2014 the Tier A catalog</li> <li>App Components \u2014 the component model</li> </ul>"},{"location":"03_start_an_app/#related","title":"Related","text":"<ul> <li>01 \u2013 Overview \u00b7 02 \u2013 App Components \u00b7 04 \u2013 Testing &amp; Quality</li> </ul>"},{"location":"04_development/01_testing/","title":"Testing &amp; Quality Gates","text":"<p>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.</p>"},{"location":"04_development/01_testing/#layout","title":"\ud83d\uddc2\ufe0f Layout","text":"<pre><code>tests/\n integration/ # TestClient(app) + mocked resources\n unit/ # models, converters, pipeline stages/roots\n conftest.py # fixtures\n</code></pre>"},{"location":"04_development/01_testing/#integration-tests","title":"\ud83d\ude80 Integration tests","text":"<ul> <li>Build the app with <code>TestClient</code>.</li> <li>Replace <code>app.state.db_resources</code> with fake repositories/services so no database is required.</li> <li>Patch the auth introspection call (<code>httpx.AsyncClient.post</code>) to return a canned identity.</li> <li>Assert status codes and bodies for list/get/create/update/delete, plus <code>400</code>, <code>404</code>, <code>204</code>, and the streaming endpoint's fallback.</li> </ul> <pre><code>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</code></pre>"},{"location":"04_development/01_testing/#unit-tests","title":"\ud83e\uddea Unit tests","text":"<ul> <li>Domain models and converters: pure, no I/O.</li> <li>Pipeline stages: feed a fixture state, assert the transformed state.</li> <li>Assembled pipeline roots: fixture parity \u2014 replay a stored fixture input and compare against a golden output file.</li> </ul>"},{"location":"04_development/01_testing/#config-consistency-tests","title":"\ud83d\udd10 Config-consistency tests","text":"<p>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.</p>"},{"location":"04_development/01_testing/#quality-gates","title":"\u2705 Quality gates","text":"<p>A definition of done for any standard app:</p> <ul> <li><code>python -c \"import main\"</code> succeeds.</li> <li><code>pytest</code> passes.</li> <li>The app serves the spec's operations; the worker drains its queue.</li> <li>No domain module imports a web framework.</li> <li>No infrastructure was reimplemented that a library already provides.</li> </ul>"},{"location":"04_development/01_testing/#conventions","title":"\ud83d\udca1 Conventions","text":"<ul> <li><code>asyncio_mode = \"auto\"</code>; no manual event loops.</li> <li>Fixtures are checked into the repo (inputs + golden outputs).</li> <li>Tests skip (not fail) when a required fixture is absent.</li> <li>Do not mock the domain; mock only the infrastructure edge.</li> </ul>"},{"location":"04_development/01_testing/#related","title":"Related","text":"<ul> <li>02 \u2013 App Components \u00b7 03 \u2013 Start an App \u00b7 Overview</li> </ul>"}]}