docs(standards): split genericity matrix and infra adapters into Tier A/B/C

This commit is contained in:
2026-10-12 21:06:35 +05:30
parent 9390839d9b
commit 99fac53c2b
3 changed files with 70 additions and 36 deletions

View File

@@ -3,7 +3,9 @@
## Summary ## Summary
All external systems are reached through adapters in `infra/`. Prefer an 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. - Transactions use a session context manager provided by the library.
**Library:** the Mongo operations library (see registry). Do not write raw **Tier A — Library (Mongo Ops).** Do not write raw Motor calls in the domain;
Motor calls in the domain. consult the registry and the library's MCP before touching the driver.
## HTTP response cache ## HTTP response cache
**Tier B — template.** Copy from `templates/app`; not a standalone library.
A generic GET cache sits behind a decorator. A generic GET cache sits behind a decorator.
```python ```python
@@ -50,6 +54,8 @@ async def list_items(request): ...
## Event / progress bus ## Event / progress bus
**Tier B — template.** Copy from `templates/app`.
A port with an in-memory default adapter (`publish`, `get_events`). Swap for a A port with an in-memory default adapter (`publish`, `get_events`). Swap for a
durable broker in production (see Worker & Progress). 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 libraries. Construct them in the composition root and inject into
orchestrators. orchestrators.
**Libraries:** the ingestion library (formats) and the mail library (email). **Tier A — Libraries:** Omniread (formats) and Mail Intake (email).
## LLM clients ## 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 When a pipeline calls a model provider, route through a pool adaptor that
handles provider selection, retries, and client lifecycle: handles provider selection, retries, and client lifecycle:
@@ -75,10 +83,17 @@ Providers are selected by `LLM_PROVIDER` and configured by environment.
## Key-value store ## Key-value store
**Tier B — template.** Copy from `templates/app`.
For report/snapshot storage, provide a pluggable store port with a Redis For report/snapshot storage, provide a pluggable store port with a Redis
adapter and an in-memory fallback. adapter and an in-memory fallback.
## Rule ## Rule
If a capability already exists as a library (DB, DI, ingestion, mail, auth, - **Tier A (libraries: DB, DI, ingestion, mail, auth, docs, pipelines)** — use
docs), **use it**. Writing a private copy of a library is a defect. 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.

View File

@@ -2,50 +2,68 @@
## Summary ## Summary
This page states precisely what is **reusable as-is**, what is a **template to This page states precisely what is **library-backed**, what is
adapt**, and what is **app-specific and must be authored**. Use it to scope a **template-provided**, and what is **app-specific and must be authored**. Use it
new build. 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 | These capabilities already exist as internal libraries. Reach them through the
|---|---| library registry MCP (`find_library(capability)`) and read the library's own MCP
| Layering & dependency rule | the package layout and import rules | for its API. Writing a private copy of any of these is a defect.
| 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 |
## 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 | ## Tier B — Template-provided (copy from `templates/app`, then adapt)
|---|---|
| `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 |
## 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/<resource>/{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/<family>/` | 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 | | Artifact | Why |
|---|---| |---|---|
| Resource models | the business shape | | Resource models | the business shape |
| Repository populate/cache rules | the relationships | | Repository populate/cache rules | the relationships |
| Service business logic | domain behaviour | | Service business logic | domain behaviour |
| Pipeline families (if any) | the transformation graph | | Pipeline families | the transformation graph |
| Pattern/rule resources | data-driven policy | | Pattern/rule resources | data-driven policy |
| Fixtures & golden outputs | test data | | Fixtures & golden outputs | test data |
---
## The two axes ## The two axes
Everything app-specific reduces to **two axes**: Everything app-specific reduces to **two axes**:
@@ -54,4 +72,5 @@ Everything app-specific reduces to **two axes**:
the OpenAPI contract. the OpenAPI contract.
2. **Pipeline family** — only if inputs require multi-step transformation. 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.

View File

@@ -13,7 +13,7 @@ patterns, and the internal library catalog that agents consult while coding.
- **Worker & Progress** — queued work, progress reporting, and visibility. - **Worker & Progress** — queued work, progress reporting, and visibility.
- **Human-in-the-Loop** — guardrails and approvals inside automations. - **Human-in-the-Loop** — guardrails and approvals inside automations.
- **Infra Adapters** — how infrastructure connects without leaking into domain. - **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. - **Library Registry** — the catalog of internal libraries behind all of it.
Read the **Wiki** for the philosophy and rationale; these pages are the Read the **Wiki** for the philosophy and rationale; these pages are the