Skip to main content

ADR-0001: CRM Settlement Identity Boundary

  • Status: Proposed
  • Date: 2026-04-03
  • Author: @digiwedge/engineering

Context

The POS partner settlement flow accepts CRM-linked requests that may contain either quoteId, reservationId, or both. The reconciliation expansion under #13319 and the identity resolver work under #14033 separated trusted cross-ID identity from settlement admission by introducing a first-class SettlementAlias model and a shared SettlementIdentityResolver.

That change exposed a hard boundary that was previously blurred by acceptance-anchor backfill:

  • SettlementAcceptanceAnchor rows are safe as workflow ownership and idempotency state.
  • They are not safe as an authority for quote-to-reservation identity inference.
  • Later ingress requests can repeat a known identifier while supplying a mistyped secondary identifier.
  • Persisting that secondary identifier onto an existing anchor poisons future admission and replay behavior.

We removed anchor backfill from conflicting ingress for exactly that reason. That removal restores the trust boundary, but it also leaves an explicit operational window:

  • request A can arrive with quoteId only
  • request B can arrive with reservationId only for the same booking
  • without trusted alias evidence or an upstream canonical booking identifier, the system cannot prove that both requests refer to the same booking at admission time
  • exact-only admission can therefore create two separate anchors and two separate settlement attempts until reconciliation later promotes a trusted alias from a durable outcome

This is not a local implementation bug. It is an information-boundary problem. Preventing that double-admission window requires authoritative upstream identity proof.

The design options considered are:

  1. Add a canonical booking identifier to the partner/CRM settlement payload.
  2. Perform a synchronous upstream CRM lookup in the settlement hot path.
  3. Keep exact-only admission until a trusted alias exists, then let reconciliation catch up and normalize future traffic.

The current runtime also keeps reconciliation sync inside the API process. That is acceptable only as a temporary single-replica bridge and is already tracked separately in #14009.

Decision

We will treat CRM settlement identity as a two-tier model with an explicit trust boundary.

1. Trusted identity authority

SettlementAlias is the only trusted quote-to-reservation identity surface.

  • Cross-ID enrichment must read only from SettlementAlias.
  • Trust lifecycle remains explicit: ACTIVE_TRUSTED and INVALIDATED.
  • Reconciliation and admission must share the same resolver semantics through SettlementIdentityResolver.

2. Acceptance-anchor role

SettlementAcceptanceAnchor is an ownership and idempotency surface only.

  • It may store identifiers that were present on the request that created the anchor.
  • It must not be mutated later with secondary identifiers copied from subsequent ingress.
  • It must not be treated as trusted alias evidence.
  • It must not perform cross-column identity inference.

3. Admission behavior before trust exists

Until authoritative upstream booking identity exists, CRM-linked settlement admission remains exact-only.

  • quoteId-only requests match only exact quoteId.
  • reservationId-only requests match only exact reservationId.
  • Cross-ID normalization happens only when a trusted alias already exists.
  • Reconciliation remains responsible for catch-up after a durable dual-ID outcome proves the mapping.

This means the double-debit window for one-sided requests is explicit and accepted as the current production-safe posture.

4. Long-term target

The preferred long-term design is a canonical booking identifier in the payload.

  • This avoids adding CRM availability to the payment hot path.
  • It removes the need for local heuristic identity inference.
  • It lets admission dedupe the same booking without mutating anchors or depending on later reconciliation.

We do not choose synchronous CRM lookup as the long-term default because it adds a hard runtime dependency to the money-moving path. It remains a fallback option only if the payload contract cannot be evolved.

5. Evidence versus authority

If the business needs observability for unresolved or one-sided CRM settlements, we will add a separate evidence surface rather than reusing anchors as a trust store.

  • Observed pairs and one-sided admissions may be recorded for analysis.
  • That evidence must not directly drive admission, alias promotion, or replay normalization.

6. Reconciliation runtime follow-up

The current in-process reconciliation scheduler remains a bridge only.

  • Same-process coalescing reduces local races.
  • Cross-pod safety still requires externalizing or leasing the writer.
  • That work stays tracked under #14009.

Consequences

Positive

  • The trust boundary is explicit and defensible.
  • Later malformed requests cannot poison trusted identity by mutating existing anchors.
  • Admission, replay matching, and reconciliation all converge on one trusted alias model.
  • The architecture is honest about what the system can and cannot prove locally.
  • The long-term path is clear: canonical booking identity in the payload.

Negative

  • One-sided CRM admissions still have a real double-debit window until trusted alias promotion occurs.
  • Some duplicate prevention moves from admission time to reconciliation time.
  • Operators need visibility into unresolved identity cases and alias invalidation history.
  • The platform still carries a single-writer reconciliation runtime constraint until #14009 lands.

Alternatives Considered

AlternativeProsConsWhy not chosen
Canonical booking identifier in payloadZero hot-path lookup latency, no runtime CRM dependency, strongest admission-time identityRequires CRM and partner contract changeChosen as the long-term target, not immediately available
Synchronous CRM lookup before admissionPrevents one-sided double-admission once CRM can answer both directionsAdds CRM latency and availability coupling to payment flow, harder failure modesNot chosen as the default long-term design
Continue anchor backfill from ingressBlocks some duplicate-anchor cases without upstream changesPersists unverified secondary identifiers and poisons future admissionRejected as unsafe
Infer trust from durable anchorsReuses existing data already in the ledgerHistorical anchors contain backfilled data with unverifiable provenanceRejected as unsafe
Exact-only admission plus reconciliation catch-upPreserves trust boundary, no new upstream dependencyLeaves explicit double-debit window for one-sided requestsChosen as the current production-safe posture until canonical identity exists

Follow-ups

  • Keep SettlementAlias as the only trusted cross-ID authority in #14033.
  • Remove ingress-driven anchor backfill that mutates existing anchors with unverified secondary identifiers in #13993.
  • Externalize reconciliation sync out of the API process in #14009.
  • Add one-sided CRM settlement metrics and dashboards to measure the real size of the double-debit window in #14049.
  • Add operator-grade alias review, invalidation, and audit tooling in #14051.
  • Define and land the canonical booking identity contract change for CRM-linked settlements in #14050.
  • Add unresolved-identity reporting that is observational only and does not change admission semantics in #14052.