Widen the accept gate, obfuscate masked contact info, let free-for-all leads be claimed

Featurekamo-shared-library
Shipped
September 3, 2026 at 8:02 PM UTC
Author
Kamo
Commit
be5d937

Three related changes to the lead-assignment core. **The accept gate now asks two questions, over every assigned lead.** LeadAcceptEligibilityService replaces the comment gate that lived inside CreditLeadAssignmentService. A lead blocks its holder from taking another when either it has no note from them written SINCE it was assigned to them, or it is still sitting at Fresh > New Lead / Farm > Farm Fresh. Both halves close a hole. The old rule counted any note ever written, so a lead that came back to a member passed on a note from their previous stint with it. And it looked only at leads acquired through the auto-assignment system, which left every other route — a manager's hand-assignment, an import, a self-claim — as a way to accumulate untouched leads with the pool still open. The rule is two set-based queries whatever the size of the member's book: the current assignments, then one grouped MediaObj aggregate over their stream guids. Widening the scope to every assignment made the old query-per-lead loop unaffordable — it is the shape that made the /leads grid slow, and this runs on every render of the leads nav for every signed-in member. The blocking statuses are named explicitly in LeadStatusType rather than derived from isAllowSwitchBack. Those two happen to be the only statuses carrying that flag, but it answers a different question, and a future status marked the same way must not silently start blocking accepts. Matching is on the stored system descriptions, so an org's custom status labels never affect the rule. **Masked contact info is obfuscated rather than blanked.** ContactObfuscator turns (555) 555-5555 into (555) 555-**55, snadjafinia@gmail.com into s**********@gmail.com, and 123 Main St into *** Main St. A blank field is indistinguishable from one nobody filled in, so a restricted viewer could not tell "there is a number here you may not read" from "this lead has no number" — and staff chased the second reading. City, postal code and country now survive on the same basis the state always did: coarse enough to identify a duplicate, useless for reaching anybody once the street number is gone. Date of birth stays absent; no partial form of it is both recognisable and safe. The browser mirrors this exactly **************** because LeadRealtimePublisher fans a full unmasked DTO out to every subscriber in the org with no per-recipient projection possible. The Java and vitest suites assert the same strings so the two cannot drift apart unnoticed. **Unassigned free-for-all leads are visible and claimable.** **************** claims one specific unassigned lead. Free-for-all is the open door and carries the accept gate with it — otherwise it would be the way around the discipline it exists alongside — and mints/spends its own credit exactly as acceptNextLead does, so Max Daily Spend still caps it. ASSIGN_LEADS is the elevated door: any unassigned lead, no gate, no credit. Licensing is a legal constraint and neither door waives it. LeadGridQuery gains **************** turning the grid's permission floor from "leads assigned to me" into "leads assigned to me, plus anything genuinely up for grabs". Hiding unclaimed free-for-all leads from everyone without VIEW_UNASSIGNED_LEADS left them claimable in theory and unreachable in practice.

All changes

Like what you see shipping?

Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.

Start Free ForeverView Pricing