diff --git a/standards/src/lib/09_infra_adaptors.md b/standards/src/lib/09_infra_adaptors.md index da7694b..ea1d2e9 100644 --- a/standards/src/lib/09_infra_adaptors.md +++ b/standards/src/lib/09_infra_adaptors.md @@ -3,7 +3,9 @@ ## Summary All external systems are reached through adapters in `infra/`. Prefer an -existing internal library over writing a new adapter. +existing internal library over writing a new adapter; where no library exists, +copy the Tier B scaffold from `templates/app`. See the Genericity Matrix for the +Tier A / B / C split. --- @@ -31,11 +33,13 @@ existing internal library over writing a new adapter. - Transactions use a session context manager provided by the library. -**Library:** the Mongo operations library (see registry). Do not write raw -Motor calls in the domain. +**Tier A — Library (Mongo Ops).** Do not write raw Motor calls in the domain; +consult the registry and the library's MCP before touching the driver. ## HTTP response cache +**Tier B — template.** Copy from `templates/app`; not a standalone library. + A generic GET cache sits behind a decorator. ```python @@ -50,6 +54,8 @@ async def list_items(request): ... ## Event / progress bus +**Tier B — template.** Copy from `templates/app`. + A port with an in-memory default adapter (`publish`, `get_events`). Swap for a durable broker in production (see Worker & Progress). @@ -59,10 +65,12 @@ External fetchers (files, mail, HTTP) are adapters that wrap ingestion libraries. Construct them in the composition root and inject into orchestrators. -**Libraries:** the ingestion library (formats) and the mail library (email). +**Tier A — Libraries:** Omniread (formats) and Mail Intake (email). ## LLM clients +**Tier B — template.** Copy the pool scaffold from `templates/app`. + When a pipeline calls a model provider, route through a pool adaptor that handles provider selection, retries, and client lifecycle: @@ -75,10 +83,17 @@ Providers are selected by `LLM_PROVIDER` and configured by environment. ## Key-value store +**Tier B — template.** Copy from `templates/app`. + For report/snapshot storage, provide a pluggable store port with a Redis adapter and an in-memory fallback. ## Rule -If a capability already exists as a library (DB, DI, ingestion, mail, auth, -docs), **use it**. Writing a private copy of a library is a defect. +- **Tier A (libraries: DB, DI, ingestion, mail, auth, docs, pipelines)** — use + the library; writing a private copy is a defect. Query `find_library` first. +- **Tier B (cache, progress bus, LLM pool, KV store, composition shape, worker, + tests)** — these are template code, not libraries: copy them from + `templates/app` rather than searching the registry. +- **Tier C (models, business logic, pipeline families, rules)** — author them + yourself. diff --git a/standards/src/lib/12_genericity_matrix.md b/standards/src/lib/12_genericity_matrix.md index bbb5ede..4e65b32 100644 --- a/standards/src/lib/12_genericity_matrix.md +++ b/standards/src/lib/12_genericity_matrix.md @@ -2,50 +2,68 @@ ## Summary -This page states precisely what is **reusable as-is**, what is a **template to -adapt**, and what is **app-specific and must be authored**. Use it to scope a -new build. +This page states precisely what is **library-backed**, what is +**template-provided**, and what is **app-specific and must be authored**. Use it +to scope a new build and to know, for any given capability, whether to call a +library, copy template code, or write it yourself. --- -## Reusable as-is (do not rewrite) +## Tier A — Library-backed (use; never reimplement) -| Area | Artifact | -|---|---| -| Layering & dependency rule | the package layout and import rules | -| Composition root | the `init.py` shape, idempotency, `get_svc` overloading | -| Web edge | `OpenAPIFirstApp` bootstrap, handler-name = `operationId`, thin handlers | -| CRUD slice | the four-file pattern (model / ports-db / ports-service / converters) | -| Generic CRUD service | the base service adaptor | -| Repository factory | base / populating / cached selection | -| Worker | the poll loop, backoff, retry, status machine | -| Progress bus | the bus port + in-memory adapter + streaming endpoint | -| Human-in-the-loop | the durable pause + question document + resolve endpoint + resume map | -| Infra adaptors | HTTP cache decorator, KV store port, LLM pool scaffold | -| Testing | integration harness with fakes, fixture-parity pattern | -| Docs | GSDFC + generated stubs + MCP bundle pipeline | +These capabilities already exist as internal libraries. Reach them through the +library registry MCP (`find_library(capability)`) and read the library's own MCP +for its API. Writing a private copy of any of these is a defect. -## Template to adapt (copy, then change names) +| Capability | Library | MCP | +|---|---|---| +| Persistence: repositories, cache, populate, transactions | Mongo Ops (`mongo_ops`) | `:8007` | +| Dependency injection / composition | Hexa (`hexa`) | `:8008` | +| Deterministic multi-step transformation engine | DAG Pipe (`dagpipe`) | `:8006` | +| Spec-first HTTP edge, client, codegen | OpenAPI First (`openapi_first`) | `:8004` | +| Format-agnostic fetch + parse (HTML, PDF, CSV, XLSX) | Omniread (`omniread`) | `:8003` | +| Read-only email ingestion | Mail Intake (`mail_intake`) | `:8002` | +| JWT auth primitives (register, login, introspect) | Py JWT (`jwtlib`) | `:8005` | +| Documentation build (MkDocs + MCP) | Doc Forge (`docforge`) | `:8001` | -| Artifact | Change | -|---|---| -| `pyproject.toml` | project name, dependencies | -| `Dockerfile` / `docker-compose.yaml` | service name, ports, services | -| `.env.example` | connection strings, provider keys | -| `openapi.yaml` | every path/schema/operationId | -| `scripts/` | seed and operator tasks | +## Tier B — Template-provided (copy from `templates/app`, then adapt) -## App-specific (must be authored) +These are reusable internal components shipped **inside** the template — they are +not independent libraries. Copy them as-is, then rename/populate only what is +app-specific. They give every app the same wiring shape as the reference app. + +| Component | Template location | Action | +|---|---|---| +| Layering & dependency rule | the package layout + import rules | keep | +| Composition root shape | `init.py` (`get_svc`/`get_db`, idempotency) | keep | +| Spec-first web edge | `interfaces/openapi.py`, thin handlers bound to `operationId` | keep | +| CRUD slice pattern | `domain//{models.py, ports/db.py, ports/service.py, converters.py}` | rename + fill | +| Generic CRUD service | `infra/adapters/service.py` (`CrudsServiceAdaptor`) | keep | +| Repository factory | `infra/db/repos/mongo.py` (`_repo_factory`) | keep | +| Worker poll + status loop | `worker.py` | keep | +| Progress bus + streaming | bus port + in-memory adapter + SSE endpoint | keep | +| Human-in-the-loop | durable pause + question document + resolve endpoint + resume map | keep | +| HTTP response cache | `cache_response` decorator + generation invalidation | keep | +| Key-value store | KV store port + Redis adapter + in-memory fallback | keep | +| LLM client pool | provider-select + retry pool scaffold | keep | +| Pipeline scaffold | `pipelines/shared/{ports,loader,registry}.py` + `pipelines//` | rename + fill | +| Operator scripts | `scripts/` (`init()` / `get_svc()` / `shutdown()`) | rename | +| Testing harness | integration fakes + fixture-parity pattern | keep | +| Project scaffolding | `pyproject.toml`, `Dockerfile`, `docker-compose.yaml`, `.env.example`, `openapi.yaml` | rename + fill | + +## Tier C — App-specific (must be authored) | Artifact | Why | |---|---| | Resource models | the business shape | | Repository populate/cache rules | the relationships | | Service business logic | domain behaviour | -| Pipeline families (if any) | the transformation graph | +| Pipeline families | the transformation graph | | Pattern/rule resources | data-driven policy | | Fixtures & golden outputs | test data | +--- + ## The two axes Everything app-specific reduces to **two axes**: @@ -54,4 +72,5 @@ Everything app-specific reduces to **two axes**: the OpenAPI contract. 2. **Pipeline family** — only if inputs require multi-step transformation. -If you can specify those two axes, the rest of the app follows mechanically. +If you can specify those two axes, the rest of the app follows mechanically: +Tier A is imported, Tier B is copied, and Tier C is the only thing you author. diff --git a/standards/src/lib/index.md b/standards/src/lib/index.md index da99396..fbc72ff 100644 --- a/standards/src/lib/index.md +++ b/standards/src/lib/index.md @@ -13,7 +13,7 @@ patterns, and the internal library catalog that agents consult while coding. - **Worker & Progress** — queued work, progress reporting, and visibility. - **Human-in-the-Loop** — guardrails and approvals inside automations. - **Infra Adapters** — how infrastructure connects without leaking into domain. -- **Genericity Matrix** — what is generic vs app-specific. +- **Genericity Matrix** — what is library-backed, template-provided, or authored. - **Library Registry** — the catalog of internal libraries behind all of it. Read the **Wiki** for the philosophy and rationale; these pages are the