# Customer ERP → CFO acceptance matrix

This matrix is generated from the signed grant manifest and live discovery. Do not create a
facility/period/capability Cartesian product and do not report untested authority as passed.

| Case | Coverage | PASS condition |
| --- | --- | --- |
| Token and authority | once per credential | token 200; `/integrations/me` matches the manifest organization and zone |
| Discovery drift gate | all manifest destinations | live facility, period and capability sets equal the manifest |
| Dry run | every authorized tuple | terminal; one `would_create`; `created_rows=0` |
| First persist | every authorized tuple | terminal; exactly one `created`; no failed row |
| Same transport replay | every authorized tuple | same key + same canonical request returns the original job; no new row |
| Business duplicate | every authorized tuple | new key + same stable `external_ref` gives `skipped_duplicate`; no new row |
| New business row | once per credential | new key + new `external_ref` creates exactly one row |
| Lost response | once per connector build | accepted response is discarded; normal retry reloads the same key/request and returns the same job |
| Restart recovery | once per connector build | Odoo restarts after prepare/commit; the pending row keeps the same hashes and creates no duplicate |
| Supported-but-ungranted capability (subset grant) | once per credential | `403 AUTHORIZATION_DENIED`; classify `authorization_denial`; no job/write |
| Supported-but-ungranted capability (complete-catalog grant) | report only | `not_applicable` with exact supported/granted set equality; no outbox row/request/CFO call |
| Unsupported capability input (complete-catalog grant) | once per credential | `422 ACTIVITY_CAPABILITY_UNAVAILABLE`; classify `validation_rejection`; no job/write; never report as authorization denial |
| Ungranted destination | once per credential | `403 AUTHORIZATION_DENIED` or anti-enumeration `404 RESOURCE_NOT_FOUND`; classify `authorization_denial`; no job/write |
| CFO UI reconciliation | every persisted tuple | organization, facility, period, scope, activity, quantity, unit, emission and updater match |

DEV must pass before UAT. DEV and UAT use separate credentials, source-system suffixes, request
identities, business identities, data and reports.

The signed manifest must choose exactly one capability-probe branch. `subset` requires a real
supported-but-ungranted capability. `complete_catalog` requires schema/catalog version equality,
the source-supported inventory and exact equality with every target destination's granted
inventory. Any catalog drift stops preparation before a write-capable request is created.
