# ADR-0009: `obsvect` and `controlvect` derive their content from the `datavect` structure

| Field         | Value       |
|---------------|-------------|
| Kind          | ADR         |
| Status        | Accepted    |
| Decided       | historical  |
| Deciders      | Antoine Berchet |
| Supersedes    | —           |
| Superseded by | —           |

## Context

The observation vector (`obsvect`) and the control vector (`controlvect`) both need to
know, per component/tracer, which parameters exist and how they should be handled. Both
plugin types declare `datavect` as a hard requirement (`requirements = {"datavect": ...}`
in `pycif/plugins/obsvects/standard/__init__.py` and
`pycif/plugins/controlvects/standard/__init__.py`), and are built by walking the
`datavect` YAML block: `controlvect/standard`'s docstring states directly that "the
control vector is initialized according to the information specified in the data
vector," and for each datavect parameter, a set of primary/secondary YAML arguments on
that parameter decides whether and how it enters the control vector.
`obsvect/standard` organises `yobs`/`ysim`/`dy` by the same `component` / `tracer`
structure "as declared in the `datavect` YAML block."

## Decision

`obsvect` and `controlvect` do not maintain their own independent declaration of what
they contain. They fetch that information from `datavect`, which acts as the single,
centralised place where the user describes the data of the system.

## Consequences

- Centralising this in `datavect` means the user declares a component/tracer's data once,
  and both the observation side and the control side of the inversion derive their
  structure from that single declaration, rather than requiring the user to repeat or
  keep in sync two separate declarations.
- **Known weakness:** it is impossible to have an element in the control vector or the
  observation vector that is not defined in `datavect`. Anything obs/control-vector-only
  has no path into the system today.
- **Known weakness:** membership is implicit and error-prone. Whether a given `datavect`
  parameter ends up in the control vector (and how) is inferred from *which* optional
  primary/secondary arguments happen to be set on it, rather than from an explicit,
  readable declaration of "this parameter is/is not part of the control vector." The same
  applies on the observation side. A config author can misjudge what actually lands in
  either vector without an explicit error.
- These two weaknesses are expected to motivate a dedicated **RFC** in the near future
  (e.g. to make obs/control-vector membership explicit, and/or to allow elements that
  don't route through `datavect`). This ADR records the current, load-bearing state of
  affairs; it does not attempt to resolve it.
