# Provenance and review

> Every value keeps its source, author, time and evidence and is superseded rather than overwritten; declared data is reviewed before it enters a passport, with a second person when the organisation's four-eyes rule is on.

A passport is only as trustworthy as the values in it. BatteryPass therefore records where each value comes
from, who entered and approved it, and which documents support it, and it keeps every earlier state.

## A value is a revision

Every value is stored as a revision (`FieldValue`). The API returns these members:

| Member                                                       | Meaning                                                                                 |
| ------------------------------------------------------------ | --------------------------------------------------------------------------------------- |
| `value`, `unit`                                              | The value as JSON and its unit of measure; `value` is `null` when the field was cleared |
| `revision`                                                   | 1, 2, 3 … per field and target (model version or unit)                                  |
| `source`                                                     | Where the value came from, see below                                                    |
| `recorded_by`, `recorded_at`                                 | Who recorded this revision and when                                                     |
| `responsible`                                                | The member responsible for the value                                                    |
| `review_status`, `reviewed_by`, `reviewed_at`, `review_note` | The review decision                                                                     |
| `evidence[]`                                                 | The documents this revision rests on (kind, title, tier, revision)                      |
| `checks[]`                                                   | Results of the automatic checks: `pass`, `warn` or `fail` with a message                |
| `measured_at`                                                | For condition data: when the value was measured                                         |
| `superseded_at`                                              | When a newer revision replaced this one                                                 |
| `registry_version`                                           | The registry version the value was recorded under                                       |

| `source`   | Meaning                                                                     |
| ---------- | --------------------------------------------------------------------------- |
| `manual`   | A member entered it in the portal                                           |
| `import`   | It came from a spreadsheet import                                           |
| `supplier` | A supplier sent it through a request link, or a service partner reported it |
| `api`      | An API key reported it (condition data only)                                |
| `system`   | BatteryPass wrote it                                                        |

## Values are superseded, never overwritten

A change never edits a stored value. It inserts a new revision and marks the previous one as superseded, so
the full history stays available, including who changed what and when. A review decision is a revision too:
approving inserts a revision with the same value and the reviewer.

| Operation                                                                                                                                                                                                                                                                                | Returns                                                            |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| [`listModelVersionValues`](https://passport.kabin.sh/docs/en/api-reference/values/list-model-version-values), [`listBatteryValues`](https://passport.kabin.sh/docs/en/api-reference/values/list-battery-values)                                                                      | The current revision of each field of a model version or unit      |
| [`listModelVersionValueHistory`](https://passport.kabin.sh/docs/en/api-reference/values/list-model-version-value-history), [`listBatteryValueHistory`](https://passport.kabin.sh/docs/en/api-reference/values/list-battery-value-history)                                            | Every revision of one field, newest first                          |
| [`listEvidence`](https://passport.kabin.sh/docs/en/api-reference/evidence/list-evidence), [`getEvidence`](https://passport.kabin.sh/docs/en/api-reference/evidence/get-evidence), [`downloadEvidence`](https://passport.kabin.sh/docs/en/api-reference/evidence/download-evidence) | The documents, their revisions, where they are used, and the files |

```bash title="cURL"
curl -s "$BATTERYPASS_URL/api/v1/model-versions/$VERSION_ID/values/performance.nominal_voltage/history" \
  -H "Authorization: Bearer $BATTERYPASS_API_KEY" \
  | jq '.items[] | {revision, value, source, review_status, reviewed_at}'
```

Documents are versioned in the same way: a new revision of a document supersedes the old one, and a value
that rests on a superseded document is flagged by a failing check.

## Review status

| `review_status` | Meaning                                                                |
| --------------- | ---------------------------------------------------------------------- |
| `captured`      | Recorded; an automatic check failed or has not passed yet              |
| `auto_checked`  | The automatic checks passed; waiting for a reviewer                    |
| `approved`      | A reviewer approved it; only approved values enter a passport version  |
| `rejected`      | A reviewer rejected it with a note; the responsible member corrects it |

A reviewer (role `reviewer`, `admin` or `owner`) approves in the portal. An approval is refused while

- a check of the value fails,
- the registry requires evidence for the field and no current document supports the value, or
- the reviewer recorded the value themselves and the organisation has the four-eyes rule on (the default).

The values of a spreadsheet import can be approved in one action per import, under the same rules for each
value. Values that one of these rules holds back stay open for individual review.

> [!NOTE]
> Condition data is the exception: values reported as measurements are accepted without a reviewer and stored
> as `approved` with their `measured_at`. See [Condition data and lifecycle](https://passport.kabin.sh/docs/en/concepts/condition-and-lifecycle).

## Why API keys cannot approve or publish

An API key belongs to the organisation, not to a person. The four-eyes rule compares the author of a value
with its reviewer, and publication needs a reviewer who answers for it. A key that is nobody could not take
part in either without weakening them, so keys never write declared data, approve values, publish or withdraw
passports. They read, and with the `condition` scope they report measurements and lifecycle events, which
carry `source` `api`; the lifecycle event of each report names the key in `api_key`.

Declared data reaches BatteryPass through the portal, supplier requests or the spreadsheet import, and is
approved by members.

With the `import` scope a key stages a spreadsheet import: upload, mapping, validation. Only members commit it and
approve the imported values in the portal, so data from an ERP becomes declared data only through a member's decision
(ADR-026). See [Units from production](https://passport.kabin.sh/docs/en/integrations/workflows/units-from-production).

## Completeness

[`getModelVersionCompleteness`](https://passport.kabin.sh/docs/en/api-reference/values/get-model-version-completeness) and
[`getBatteryCompleteness`](https://passport.kabin.sh/docs/en/api-reference/values/get-battery-completeness) evaluate the registry for a
model version or unit and give each field a `display` status:

| `display`                 | Meaning                                                     |
| ------------------------- | ----------------------------------------------------------- |
| `required_missing`        | Required now, no current approved value; blocks publication |
| `required_pending_review` | A value exists but is not approved; blocks publication      |
| `ok`                      | An approved value is present                                |
| `not_applicable`          | Marked as not applicable, with a stored reason              |
| `later_required`          | The obligation starts later or is not triggered yet         |
| `optional_missing`        | Optional and empty                                          |

There is deliberately no percentage. The `summary` counts the statuses, and `summary.blocking` is the number
of fields that still block publication.
