open-standards/openpay/crates/op-connect
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Transaction Science fc5081ae3f
All checks were successful
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 10s
standards workspaces build + test / macOS energy backends (pull_request) Has been skipped
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 1m21s
standards workspaces build + test / openpay (openpay, openpay, linux-arm64) (pull_request) Successful in 6m0s
op-connect: an unrun PEP screen is not a clearance
`ConnectScreener` takes its PEP and adverse-media screeners as
`Option<Screener>`, documented so that "operators who only have a
sanctions feed can still use this crate (PEP / AM are then no-ops
returning empty hit-vectors)". Both screens are gated on
`if let Some(..)`, so with `pep: None` neither `pep_hits` nor
`any_review` is ever touched, and the fall-through returned
`Decision::Clear` — whose own doc comment read "No hits anywhere; safe
to proceed."

The module doc states that at each BeneficialOwners submission the
platform *must* "screen every person against a PEP watchlist ... per
FATF Recommendation 12 and EU AMLD5 Art. 20". So the crate emitted
"safe to proceed" for a screen it is required to run and did not run.
`ScreeningResult` recorded no field saying which screens had run, so
the persisted row was indistinguishable from one where a real PEP feed
was queried and returned nothing: a sitting head of state onboarded
under a sanctions-only configuration produced the same record as a
genuine three-screen clearance, and the gap was invisible on review.
The crate's own test `clear_when_lists_empty` built exactly that
configuration and asserted `Clear`, pinning the behaviour.

Unknown is now a distinct value rather than the best possible one:

- `ScreenCoverage { pep, adverse_media }` records which optional
  screens ran, and travels with the hits. Sanctions is absent from it
  because it cannot be absent from the run — `new` takes it by value,
  so a bool could never be false. `#[serde(default)]` makes a stored
  row that does not state its coverage default to "neither ran": a
  record that does not say cannot assert it.
- `Decision::Incomplete` reports a mandated screen that did not run.
  Precedence is `Block` > `Review` > `Incomplete` > `Clear`; a hit
  needing adjudication outranks a missing screen because the gap is
  reported independently in `coverage`.
- `Decision::may_proceed()` is true only for `Clear`, so a caller
  cannot read `Incomplete` as proceedable by forgetting a match arm.

The mandated/optional line is drawn from the crate's own doc: items
1-3 are "must", item 4 is "Optionally", so a missing adverse-media
screen is recorded but still clears, and a missing PEP screen does not.

`clear_when_lists_empty` is replaced by a discriminating pair with
identical empty hit-vectors and opposite decisions — unconfigured PEP
yields `Incomplete`, a configured-but-empty PEP index yields `Clear` —
which is the distinction the old single test could not express.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-10-17 04:13:10 -04:00
..
src op-connect: an unrun PEP screen is not a clearance 2026-10-17 04:13:10 -04:00
tests ingest batch 4: 9 crates · table-stakes completers across all three workspaces 2026-05-24 04:00:04 -04:00
Cargo.toml Repo-wide toolchain refresh + jouleclaw policy/sandbox updates 2026-06-07 17:04:45 -04:00