Skip to content

v2.1.1 — signup self-heals a dangling identity link

Choose a tag to compare

@chiam-ck chiam-ck released this 22 Jul 21:22
· 16 commits to main since this release

Signup no longer dead-ends when a login points at a customer that no longer exists. An identity whose linked crm.customer row is gone made the returning-customer branch fail on CRM's 422 customer.kyc.not_found — a rule with no copy registered, so it rendered the generic "Sorry, something went wrong". The identity was bricked permanently, and because reserve-at-pick runs before the customer lookup, every retry burned another MSISDN hold.

Highlights

  • Self-heal instead of dead-end. Signup now treats that rule as a stale link: ERROR log portal.signup.identity_link_dangling, a portal_action audit row, drop the link, fall through to the normal create-customer path, re-bind. The customer completes signup unaided; the underlying data fault stays loudly visible rather than papered over.
  • One narrow carve-out, no takeover surface. bss_portal_auth::relink_orphaned_to_customer is the single exception to link_to_customer's "links are 1:1 and not reassignable from this surface" rule. The UPDATE is a compare-and-swap on the ghost customer id, so the only reachable transition is ghost → new customer. An identity bound to a live customer can't be moved through it — the CAS misses and returns AlreadyLinked naming the real holder — even though the caller picks new_customer_id.
  • Copy for customer.kyc.not_found. Signup repairs the state, but the CSR portal and the customer.get_kyc_status tool read a linked customer without repairing it, so the rule needed real copy rather than the generic fallback.
  • Mock-mode PAN guard re-applied on the fall-through, since it's skipped upstream while the identity still looks like a returning customer.

Why it happened

Not a signup regression — no code change caused it. The trigger was a data state. The v2.1.1 notes originally said the cause was unknown / done out-of-band; that was wrong, and v2.1.2 corrects it: make scenarios-hero truncated the customers during v2.0.0 release verification.

Guards

The self-heal keys off CRM's exact rule string across a service boundary — a rename in services/crm/src/service.rs would silently restore the original defect — so dangling_link_rule_is_pinned_and_has_copy pins the literal and its copy-map entry in CI. CAS semantics are pinned by three cases in the live audit_link_live round-trip: CAS miss refuses and leaves the link untouched, CAS hit repairs, unknown identity errors rather than silently no-opping.

Compatibility

SemVer patch. No new verb, no wire contract, event, payload, or DB schema change. relink_orphaned_to_customer is internal-crate API with no external surface. customer_self_serve untouched. BSS_RELEASE 2.1.0 → 2.1.1.

Gate

cargo fmt --all --check · clippy -D warnings (0) · 14 doctrine guards · live audit_link_live run against a real DB.

Known defect in this cut: it was tagged with chunker_matches_python_oracle failing (96 chunks vs 95). The full suite was run before the DECISIONS.md entry was appended and only the two changed crates re-run afterward, so this tag's commit message claim of "540 pass / 0 fail" did not describe the tree that shipped. Fixed in v2.1.2.