diff --git a/changelog/813.docs.md b/changelog/813.docs.md new file mode 100644 index 000000000..bb0086665 --- /dev/null +++ b/changelog/813.docs.md @@ -0,0 +1,3 @@ +Rewrites the architecture background page to describe the system as implemented, +with C4 context, container and component diagrams, +a package layering diagram and sequence diagrams for the main runtime flows. diff --git a/docs/background/architecture.md b/docs/background/architecture.md index 8bb72cece..4e220bb90 100644 --- a/docs/background/architecture.md +++ b/docs/background/architecture.md @@ -1,197 +1,395 @@ # Architecture -The CMIP7 Assessment Fast Track REF aims to provide a community tool for the benchmarking of Earth System Models (ESMs) -against observational datasets as they are submitted. -This near-real-time model evaluation will produce a set of scalar, -timeseries or gridded diagnostics in combination with static web pages and figures that will be made publicly available. - -The CMIP7 Model Benchmarking Task Team (MBTT) has identified an initial set of diagnostics -that will be implemented using existing benchmarking packages. -The REF will improve the availability of, -and global access to, -evaluated simulations as well as supporting community efforts to reduce the carbon footprint. -Certain emergent characteristics – such as climate sensitivity, ECS (Equilibrium Climate Sensitivity), -TCRE (Transient Climate Response to Cumulative Emissions), and others – -could be pre-computed in a standardised manner supporting users including IPCC author teams. - -This document outlines some key design principles for the Rapid Evaluation Framework (REF) for the CMIP Assessment Fast Track (AFT) process, -with an eye towards a scalable solution. - - -## System Architecture - -At the heart of the REF is the ability to perform an operation when a given input dataset is changed -and to track the results of that execution (Figure 1). -The benchmarking providers define a set of metrics, -each metric has an associated data request that defines the input datasets required to perform the operation. - -As new datasets are ingested into the system, -the REF will "solve" for which metrics need to be executed based on the data requirements of the metrics. -Each metric may produce multiple metric execution groups depending how it is configured. -The framework is agnostic to what operation is performed and (somewhat) what output is produced. -The external benchmarking packages provide the operations. - -The REF is responsible for tracking the state of the system and ensuring that the correct operations are performed. -The REF will also provide a set of CLI commands to allow users to interact with the system and to retrieve the results of the operations. - - -![Diagnostic execution](images/metric_execution.png) -/// caption -Figure 1: A metric in the REF context takes a set of input datasets and produces a set of outputs. -The output could include scalar values, timeseries, or gridded diagnostics as well as static figures or HTML. -/// - - -## Objectives - -The key objectives that determine the design of the system are as follows: - -* **Modular:** This is a community project. - It should be easy for existing and future benchmarking packages to integrate with the framework. - The developer experience for the benchmarking package providers should be paramount. -* **Extensible:** For AFT we are focusing on a small subset of possible derived metrics and diagnostics. - The metrics will scale over time as the science progresses in terms of data volume and complexity. -* **Reusable:** We are targeting multiple different deployment environments and a range of different potential users. - Where possible, we should preserve the ability to reuse components of the REF in different contexts. - -### Assumptions - -* Incoming CMIP source data has had basic quality assurance checks performed upon it. - No metadata fixes will be able to be performed on the incoming datasets - since this is intended to be run as new CMIP7 datasets are submitted. -* To speed up development, we are focussing on only monthly data. - This greatly reduces the data volumes that we need to deal with, lowering the systems requirement for using the REF. -* The benchmarking packages already provide the metrics that are targeted in the REF beta. - We are working with an understanding of what those packages require. - -### Quality - -* Transparency: Open source, well-documented code -* Auditablility: Results should include provenance information -* Robustness: comprehensive test suite identifies any inconsistencies early in development - -### Constraints - -* We cannot control the deployment environment in Modelling Centers, so we must provide multiple different ways of running the REF. -* Different users will have different areas of interest which drives the ability to run the REF locally, rather than a centralised application. -* The development should be Python-based to be accessible to the wider research community. - -### Design Principles -Some of the key design principles that were used when designing this system include: - -* **Separation of Concerns:** Dividing components based on distinct responsibilities. - The result should be a better experience for the benchmarking package developers as in-depth knowledge of only a subset of the system is required. -* **Resilience:** Designing for fault tolerance and graceful degradation. - This will be a public-facing system so care must be taken to ensure that one bad input dataset doesn’t cause a cascading failure. - -## System Context Diagram - -The context diagram below (Figure 2) provides a high-level overview of the REF system and how it interacts with the external world. -This particular diagram was drafted during a meeting with the MB TT as the initial concept of some of the key components in the REF. -For the CMIP7-FT implementation, -we are focussing on the delivery of the essential components (yellow). -There are some additional desirable components that may be touched on through the lifecycle of the project but are not described here. - - -![System Context Diagram](images/system_context_diagram.png) -/// caption -Figure 2: A metric in the REF context takes a set of input datasets and produces a set of outputs. -The output could include scalar values, timeseries, or gridded diagnostics as well as static figures or HTML. -/// - -A breakdown of the components that make up the REF is shown in Figure 3. -Each blue component describes a service that will be deployed separately -(either as a docker container or a separate process) when deploying the system in a production release. - -One of the aims of the system is to be flexible to the different needs of a wide userbase. -To that end, not all of the components would need to be installed for a usable system. -For example, some users may wish to run the system headless, i.e. without the data API and the portal. -Those users would still be able to access the artifacts directly from the file system. - -Each benchmarking package that is contributing to the REF (the term Provider is used in the codebase) -will operate a separate service exposing the metrics/diagnostics that are supported. -These metrics all share a common interface that exposes all the information needed to call their own benchmarking packages. -This provides flexibility to update provider services independently, -and for users to implement their own if they have particular use cases. - -Another differentiation to support would be where new local input data are ingested using the CLI, rather than from ESGF events. -This would be the likely mechanism for modelling centers wishing to test their data before submitting their data to ESGF. - -![Key Components](images/key_components.png) -/// caption -Figure 3: Key components identified that form the REF along with some potential touch points by users. -The touchpoints are not exhaustive and additional flows will be identified as activity diagrams as they are fleshed out. -/// - -## Component Diagram -The sections below provide some additional detail for the key components shown in the Systems Context Diagram (Figure 3). - -### Compute Engine - -![Compute Engine](images/container_compute_engine.png) -/// caption -Figure 4: Compute Engine - responsible for tracking input data and metric executions -/// - -The compute engine is responsible for orchestrating the workflow to execute metrics. -It will be executed via a CLI tool. -The compute engine will track the results that have been previously run in a database (SQLite by default). - -The solver at the core of the compute engine will use the requirements from the defined metrics, -the available data sources and existing metrics to determine what additional metrics need to be executed. -The actual execution of the metric will be delegated to the metrics services. - -This component will include a few key abstractions to ensure that multiple different target environments are able to be supported. -This allows the framework to retain some flexibility in places that we know will change between environments. - -These abstractions include: - -- [Executor][climate_ref_core.executor.Executor] - Allows for different task execution styles to be used -- [Database][climate_ref.database.Database] - Enables switching between different databases, and keeps the database layer separate from the rest of the code -- DatastoreManager - The underlying datastores may be POSIX file systems or S3-compatible object stores - -### Diagnostic service - - -/// admonition | Packaging - -The diagnostic provider packages for the AFT are included in the Climate-REF GitHub repository and are available on PyPi. -These REF-related packages are generally pretty lightweight and may not include the actual benchmarking code. -The benchmarking code is expected to be in a separate package that is installed as a dependency, -depending on how the REF will be deployed. - -The section below describes how a non-local deployment of the REF looks like. -For in-process execution, the metric can be called directly. -/// - -The diagnostic provider service will be responsible for performing metric calculations out-of-process -and returning the results to the compute engine. -Most of the business logic for the calculation of a metric will use the existing benchmarking packages. - -![Compute Engine](images/container_metric_service.png) -/// caption -Figure 5: Diagnostic Service for remotely executing diagnostics -/// - -The worker component is the entrypoint of the service and is responsible for routing the request to the appropriate function. -The form of the worker component will depend on which execution pattern is used. -Possible patterns include a long-lived worker which pulls jobs from a queue if Celery is selected -or a simple wrapper in the case where each execution is a Slurm job on an HPC. - -Each metrics service will be a separate docker container in the production deployment. -The virtual environment required to run each of the metrics packages will be different -and separating the environments provides greater control to the metric package maintainers. -Decoupling these environments makes it easy to update each metrics package independently. - -### Data API - -![Compute Engine](images/container_data_api.png) -/// caption -Figure 6: Data API - serves results to users -/// - -The Data API service is the main interface that the public will interact with. -This API will be read-only for CMIP7 FT implementation which greatly simplifies the complexity and controls that need to be in place. - -This API will report information about the executed metrics and the results of those metrics. -The API will be used by the portal to display the results of the metrics and to allow users to download the results. +This page describes how the Rapid Evaluation Framework (REF) is put together: +the systems involved, the packages and their layering, the components inside each package, +and the runtime flows that connect them. +It is a description of the system as implemented. +For the vocabulary and the ideas behind the workflow, start with [Concepts](../concepts.md). +For the stable extension points that providers depend on, see the [API surface](../api-surface.md). + +The REF was designed for the CMIP7 Assessment Fast Track (AFT): +near-real-time benchmarking of Earth System Models against observational datasets as they are submitted, +producing scalar, timeseries and gridded diagnostics along with figures and web pages. +The CMIP7 Model Benchmarking Task Team (MBTT) identified the initial set of diagnostics, +which are implemented by existing benchmarking packages (ESMValTool, ILAMB, PMP). + +## Design goals + +The key objectives that shaped the design: + +- **Modular.** This is a community project. + It should be easy for existing and future benchmarking packages to integrate with the framework, + and the developer experience for providers is paramount. +- **Extensible.** The AFT targets a small subset of possible diagnostics. + The set will grow over time in data volume and complexity. +- **Reusable.** The REF targets multiple deployment environments and a range of users, + so components should be reusable in different contexts. + Not every component needs to be installed for a usable system. + A headless deployment without the API and portal is fully supported. + +And the constraints: + +- Deployment environments at modelling centres cannot be controlled, + so the REF must support several ways of running (local process pool, task queue, HPC schedulers). +- Different users have different areas of interest, + which drives the ability to run the REF locally rather than only as a centralised service. +- The development is Python based to be accessible to the research community. + +The design principles that follow from these: + +- **Separation of concerns.** Providers focus on the science. + The framework tracks state and decides what to run. + A provider developer needs in-depth knowledge of only a small part of the system. +- **Resilience.** One bad input dataset must not cause a cascading failure. + Failures are recorded per execution and retried according to their failure class. + +## System context + +The REF sits between the data archives and the people who want evaluation results. + +```mermaid +C4Context + title Climate REF system context + Person(operator, "Operator", "Runs the REF at a modelling centre or evaluation hub") + Person(researcher, "Researcher", "Explores evaluation results") + System(ref, "Climate REF", "Ingests dataset metadata, solves for required diagnostics and executes them") + System(refapp, "REF web application", "Serves results through a REST API and a web portal") + System_Ext(esgf, "ESGF", "Published CMIP and obs4MIPs datasets") + System_Ext(refdata, "Reference data stores", "obs4REF and provider reference data registries") + System_Ext(science, "Diagnostic packages", "ESMValTool, ILAMB, PMP") + Rel(operator, ref, "Ingests data and triggers solves", "CLI") + Rel(ref, esgf, "Fetches datasets", "intake-esgf") + Rel(ref, refdata, "Fetches reference data", "HTTPS") + Rel(ref, science, "Executes diagnostics with") + Rel(refapp, ref, "Reads database and result files") + Rel(researcher, refapp, "Browses results", "HTTPS") +``` + +Two systems are under our control: + +- **Climate REF** (this repository) is the compute engine. + It indexes datasets, decides which diagnostics need to run, runs them, and records the results. +- The **[REF web application](https://github.com/Climate-REF/ref-app)** is a separate repository + containing a read-only REST API and a React frontend for browsing the results. + It is deployable at modelling centres as well as centrally. + +The science itself lives in the external benchmarking packages. +The REF does not perform any scientific calculations. + +## Containers + +Zooming in one level, these are the deployable pieces and stores that make up a full installation. + +```mermaid +C4Container + title Climate REF containers + Person(operator, "Operator") + Person(researcher, "Researcher") + System_Boundary(ref, "Climate REF") { + Container(cli, "ref CLI", "Python, typer", "Ingest, solve, providers, executions and test-cases commands") + ContainerDb(db, "Database", "SQLite or PostgreSQL", "Datasets, executions and metric values, owned by alembic migrations") + Container(results, "Results store", "Filesystem", "Execution output bundles, figures and logs") + Container(envs, "Provider environments", "conda (micromamba)", "One isolated environment per diagnostic provider") + Container(workers, "Celery workers", "Python, Redis broker", "Optional distributed execution, one queue per provider") + } + System_Boundary(app, "REF web application") { + Container(api, "Backend API", "FastAPI", "Read-only REST API over the REF database and results") + Container(spa, "Frontend", "React, TanStack", "Single page app using the generated OpenAPI client") + } + System_Ext(esgf, "ESGF and reference data stores", "Dataset sources") + Rel(operator, cli, "Runs", "shell") + Rel(cli, db, "Reads and writes", "SQLAlchemy") + Rel(cli, envs, "Executes diagnostics in", "subprocess") + Rel(cli, workers, "Dispatches executions to", "Celery over Redis") + Rel(workers, envs, "Execute diagnostics in") + Rel(workers, db, "Persist results") + Rel(cli, results, "Writes outputs") + Rel(cli, esgf, "Fetches data from") + Rel(api, db, "Reads", "SQLAlchemy, read-only") + Rel(api, results, "Streams files from") + Rel(spa, api, "Calls", "JSON over HTTPS") + Rel(researcher, spa, "Uses") +``` + +Notes on the individual containers: + +- The **`ref` CLI** is the single entry point for operating the compute engine. + All orchestration (ingest, solve, execute, inspect) happens through it. +- The **database** is SQLite by default and PostgreSQL in production deployments. + The schema is owned by alembic migrations in the `climate-ref` package. + It records what data is available, what has run, and every scalar and series metric value produced. +- The **results store** is a directory tree of execution outputs: + CMEC metric and output bundles, figures, data files and logs. +- Each **provider environment** is a conda environment built with micromamba, + so providers with incompatible dependencies can coexist. + See `ref providers setup`. +- **Celery workers** are optional. + The default local executor runs executions in a process pool without any extra services. + See [Executors](../how-to-guides/executors.md) for the full set of options. + +## Package layering + +The monorepo is a uv workspace with seven packages. +Source dependencies point in one direction, towards `climate-ref-core`. + +```mermaid +flowchart TD + celery["climate-ref-celery
task queue adapter"] + app["climate-ref
CLI, config, database, solver, executors, results"] + core["climate-ref-core
Diagnostic, DataRequirement, constraints, Executor, CMEC bundles"] + providers["climate-ref-example / -esmvaltool / -pmp / -ilamb
diagnostic providers"] + refapp["ref-app backend
FastAPI read layer"] + celery --> app + app --> core + providers --> core + refapp --> app + refapp --> core + app -. "entry points, FQN strings only" .-> providers +``` + +| Package | Role | Depends on | +| --- | --- | --- | +| `climate-ref-core` | Domain abstractions: `Diagnostic`, `DataRequirement`, constraints, the `Executor` protocol, CMEC bundle handling | third-party only | +| `climate-ref` | The application: CLI, configuration, database, solver, executors, results read layer | `climate-ref-core` | +| `climate-ref-celery` | Celery app and `CeleryExecutor` for distributed execution | `climate-ref`, `climate-ref-core` | +| provider packages | Thin wrappers exposing each benchmarking package's diagnostics | `climate-ref-core` | +| ref-app backend | Read-only API over the REF database and results (separate repository) | `climate-ref`, `climate-ref-core` | + +Three properties of this layering do most of the architectural work: + +- `climate-ref-core` is a dependency leaf. + It imports no other REF package and knows nothing about the database. + Providers can be written and tested against it alone. +- Providers are plugins. + Each registers itself through the `climate-ref.providers` entry point group, + and the application refers to providers only by name in configuration, never by import. + Installing a provider package makes it available. + Enabling it is a configuration decision. +- Executors are resolved from configuration by fully qualified name against the + [Executor][climate_ref_core.executor.Executor] protocol, + so new execution backends plug in without changes to the framework. + +## Inside the compute engine + +The `climate-ref` package contains the components that orchestrate the workflow. + +```mermaid +C4Component + title Compute engine components (climate-ref package) + Container_Boundary(app, "climate-ref") { + Component(cli, "cli", "typer", "Thin command layer that parses arguments and delegates") + Component(config, "config", "environs, TOML", "Configuration, executor and provider selection") + Component(adapters, "dataset adapters", "pandas", "Find, parse and validate CMIP6, CMIP7, obs4MIPs and PMP datasets") + Component(catalog, "data catalog", "pandas", "DataFrame view of the ingested datasets per source type") + Component(registry, "provider registry", "", "Loads providers from entry points and registers them") + Component(solver, "solver", "", "Matches catalogs against data requirements to produce executions") + Component(executors, "executors", "", "Local, synchronous and HPC execution backends") + Component(handling, "result handling", "", "Parses CMEC bundles, ingests metric values, copies outputs") + Component(reader, "results reader", "", "Read-layer facade for querying stored results") + Component(models, "models and database", "SQLAlchemy, alembic", "ORM models, migrations and session handling") + } + Rel(cli, adapters, "ingest") + Rel(cli, solver, "solve") + Rel(config, registry, "provider FQNs") + Rel(solver, catalog, "loads") + Rel(solver, registry, "iterates diagnostics of") + Rel(solver, executors, "submits executions to") + Rel(executors, handling, "hands results to") + Rel(adapters, models, "registers datasets") + Rel(solver, models, "creates execution groups") + Rel(handling, models, "writes metric values") + Rel(reader, models, "queries") +``` + +- **CLI** (`climate_ref.cli`): typer commands grouped by noun + (`datasets`, `solve`, `providers`, `executions`, `diagnostics`, `db`, `test-cases`). + The commands stay thin and delegate to the components below. +- **Configuration** (`climate_ref.config`): a TOML file under `REF_CONFIGURATION`, + overridable per field through `REF_*` environment variables. + It selects the executor and the enabled providers by fully qualified name. +- **Dataset adapters** (`climate_ref.datasets`): one adapter per source type + (CMIP6, CMIP7, obs4MIPs, PMP climatologies). + An adapter finds local files, parses their metadata into a catalog DataFrame, + validates it, and registers datasets and files in the database. + Reference datasets (obs4REF and ESMValTool reference data) are ingested through a separate + declarative path driven by the providers rather than through these adapters. + See [Datasets](datasets.md) for the parsers and the two-phase ingestion design. +- **Solver** (`climate_ref.solver`): the heart of the compute engine. + It loads the data catalog for each source type, + applies each diagnostic's [data requirements][climate_ref_core.diagnostics.DataRequirement] + (facet filters, then grouping, then group constraints), + and produces the set of executions that need to run. + Group hashing against previously recorded executions decides what is out of date. +- **Provider registry** (`climate_ref.provider_registry`): loads the configured providers + through their entry points and records them in the database. +- **Executors** (`climate_ref.executor`): local process pool (default), synchronous (in process), + and HPC schedulers. + The Celery executor lives in its own package. +- **Result handling** (`climate_ref.executor.result_handling`): parses the CMEC output bundles, + ingests scalar and series metric values, registers output files, and copies outputs into the results store. +- **Results reader** (`climate_ref.results`): a read-layer facade for querying stored results as DataFrames, + used for local analysis and notebooks. + See [Reading results locally](../how-to-guides/reading-results-locally.md). +- **Test cases and regression** (`climate_ref.cli.test_cases` with `climate_ref_core.regression`): + per-diagnostic regression baselines captured, compared and gated in CI. + See [Regression baselines](regression-baselines.md). + +## Runtime flows + +### Ingest + +Ingestion extracts metadata from locally available datasets and indexes it in the database. +The REF only knows about data that has been ingested. + +```mermaid +sequenceDiagram + autonumber + actor Op as Operator + participant CLI as ref datasets ingest + participant Ad as DatasetAdapter + participant DB as Database + Op->>CLI: ref datasets ingest --source-type cmip6 PATH + CLI->>Ad: find_local_datasets(PATH) + Ad-->>CLI: catalog DataFrame + CLI->>Ad: validate_data_catalog() + loop each dataset in the catalog + CLI->>Ad: register_dataset() + Ad->>DB: write Dataset and DatasetFile rows + end + CLI-->>Op: ingestion summary +``` + +### Solve and execute + +A solve compares the ingested datasets against every diagnostic's data requirements, +records the resulting execution groups and executions, +and hands the out-of-date executions to the configured executor. + +```mermaid +sequenceDiagram + autonumber + actor Op as Operator + participant CLI as ref solve + participant Solver as ExecutionSolver + participant Reg as ProviderRegistry + participant DB as Database + participant Ex as Executor + participant Diag as Diagnostic (provider env) + participant RH as Result handling + Op->>CLI: ref solve + CLI->>Solver: solve_required_executions() + Solver->>Reg: build_from_config() + Solver->>DB: load data catalogs + loop each provider / diagnostic + Solver->>Solver: apply filters, group_by, constraints + Solver->>DB: create or update ExecutionGroup / Execution + Solver->>Ex: run(definition) + end + Ex->>Diag: execute (conda env subprocess) + Diag-->>Ex: output bundle (CMEC JSON) + Ex->>RH: handle_execution_result() + RH->>DB: ingest scalar and series metric values + RH->>RH: copy outputs to results store + Ex-->>CLI: join() +``` + +The executor boundary is crossed with plain data structures. +The solver passes an [ExecutionDefinition][climate_ref_core.diagnostics.ExecutionDefinition] +describing the datasets and output location, +and receives an [ExecutionResult][climate_ref_core.diagnostics.ExecutionResult] +built from the CMEC bundles the diagnostic wrote. +Outputs follow the +[Earth System Metrics and Diagnostics Standards (EMDS)](https://github.com/Earth-System-Diagnostics-Standards/EMDS). + +### Distributed execution with Celery + +With the Celery executor, executions are dispatched to long-lived workers through a Redis broker. +Each provider gets its own queue and its own worker image, +so provider environments stay isolated and can be updated independently. + +```mermaid +sequenceDiagram + autonumber + participant Solver as Solver process + participant CE as CeleryExecutor + participant Redis as Redis broker + participant W as Provider worker + participant DB as Database + Solver->>CE: run(definition) + CE->>Redis: send_task("provider.diagnostic", definition) + Redis->>W: deliver task + W->>W: execute_locally() in provider environment + W-->>Redis: ExecutionResult + Redis->>W: handle_result callback + W->>DB: handle_execution_result() + Solver->>CE: join() +``` + +See [Docker deployment](../how-to-guides/docker_deployment.md) for the containerised form of this setup, +and [Running on HPC](../how-to-guides/hpc_executor.md) for scheduler-based execution. + +## The web application + +The [ref-app](https://github.com/Climate-REF/ref-app) repository provides the visualisation layer: +a FastAPI backend and a React frontend. +It is currently coupled to the AFT deployment of the REF and is best treated as a reference implementation. + +```mermaid +C4Component + title Web application backend components (ref-app) + Container_Boundary(api, "Backend API (ref_backend)") { + Component(routes, "api/routes", "FastAPI routers", "diagnostics, executions, datasets, explorer, aft, results, utils") + Component(deps, "api/deps", "FastAPI Depends", "AppContext bundling session, config, settings and provider registry") + Component(services, "core services", "Python", "Metric value filtering, outlier detection, collections, AFT index") + Component(dtos, "models", "pydantic", "Response DTOs built from ORM objects") + Component(static, "static metadata", "YAML", "AFT collection content and per-provider diagnostic overrides") + } + Container_Boundary(lib, "climate-ref library") { + Component(refmodels, "climate_ref.models", "SQLAlchemy", "Shared ORM models") + Component(refconfig, "climate_ref.config", "", "REF configuration and paths") + } + ContainerDb_Ext(db, "REF database", "SQLite or PostgreSQL", "Populated by the compute engine") + Container_Ext(results, "Results store", "Filesystem") + Rel(routes, deps, "resolves") + Rel(routes, services, "delegates to") + Rel(routes, dtos, "serialises with") + Rel(services, refmodels, "queries") + Rel(routes, refmodels, "queries") + Rel(dtos, static, "applies overrides from") + Rel(refmodels, db, "reads") + Rel(routes, results, "streams files from") + Rel(refconfig, results, "locates") +``` + +Key characteristics: + +- The backend is a **read-only consumer** of the REF database. + It opens the database without running migrations + (SQLite can be opened immutable so the state volume can be mounted read-only) + and reports the results the compute engine has already produced. +- It depends on the `climate-ref` library directly for configuration, models and the provider registry. + The database schema remains owned by `climate-ref` migrations. +- **AFT display metadata** (collection descriptions, plain-language summaries, explorer cards) + is maintained as YAML under `backend/static/` rather than in the database, + so scientific content can be edited without touching either the schema or the frontend. +- The frontend consumes a **TypeScript client generated from the OpenAPI schema**. + Changing the API means regenerating the client, which keeps the two sides honest. +- Result files (figures, logs, bundles, archives) are streamed straight from the results store on disk. + +## Known boundaries and debt + +The layering above is enforced by convention and review, not by tooling, +and a few boundaries are weaker than the diagrams suggest: + +- The solver both computes the required executions and reads and writes the database in the same module. + The pure solving logic already yields plain DTOs, so a cleaner split is available. +- `CondaDiagnosticProvider` places conda environment management inside `climate-ref-core`, + which drags process and download concerns into the innermost package. +- The ref-app backend queries the `climate-ref` ORM models directly rather than a published read contract, + which couples the two repositories through the database schema. + The `climate_ref.results` reader is the seam for narrowing that contract, + and adoption has started with the metric value endpoints + ([ref-app #39](https://github.com/Climate-REF/ref-app/pull/39)). + The remaining routes and DTO builders still query the ORM directly. + +These are recorded here so that readers do not mistake the target picture for the current one. diff --git a/docs/background/images/container_compute_engine.png b/docs/background/images/container_compute_engine.png deleted file mode 100644 index 20605bb5e..000000000 Binary files a/docs/background/images/container_compute_engine.png and /dev/null differ diff --git a/docs/background/images/container_data_api.png b/docs/background/images/container_data_api.png deleted file mode 100644 index 170705f29..000000000 Binary files a/docs/background/images/container_data_api.png and /dev/null differ diff --git a/docs/background/images/container_metric_service.png b/docs/background/images/container_metric_service.png deleted file mode 100644 index 01dbc7760..000000000 Binary files a/docs/background/images/container_metric_service.png and /dev/null differ diff --git a/docs/background/images/key_components.png b/docs/background/images/key_components.png deleted file mode 100644 index a70575cdc..000000000 Binary files a/docs/background/images/key_components.png and /dev/null differ diff --git a/docs/background/images/metric_execution.png b/docs/background/images/metric_execution.png deleted file mode 100644 index 648544907..000000000 Binary files a/docs/background/images/metric_execution.png and /dev/null differ diff --git a/docs/background/images/system_context_diagram.png b/docs/background/images/system_context_diagram.png deleted file mode 100644 index 8519cec52..000000000 Binary files a/docs/background/images/system_context_diagram.png and /dev/null differ