Skip to main content

Overview

Step 3 allocates shares from the pool (determined in Step 2) to eligible participants, applying eligibility rules and ERISA caps.
This is where participants actually receive their annual allocation of ESOP shares based on their compensation.

Core Responsibilities

Eligibility Determination

Identify which employees qualify for allocation

Pro-Rata Allocation

Distribute shares proportional to eligible compensation

ERISA Compliance

Apply compensation and annual addition caps

Multi-Class Distribution

Allocate across multiple securities in proper proportions

Processing Phases

Phase 1: Determine Eligibility

Employees must meet all three criteria to receive allocations:
is_eligible = (
    employee.age >= eligibility_age              # e.g., 21
    AND employee.service_years >= eligibility_service_years  # e.g., 1.0
    AND employee.hours_worked >= eligibility_min_hours       # e.g., 1000
)
Example:
  • Eligibility Age: 21
  • Eligibility Service: 1 year
  • Eligibility Hours: 1,000 hours/year
Employee A: Age 35, 5 years service, 2,080 hours → ✅ Eligible
Employee B: Age 22, 0.5 years service, 2,080 hours → ❌ Not eligible (service)
Employee C: Age 24, 2 years service, 800 hours → ❌ Not eligible (hours)
Every employee gets an eligibility evaluation event in the compliance log, regardless of outcome.

Phase 2: Calculate Eligible Compensation

Apply the ERISA Compensation Cap (IRC §401(a)(17)):
max_compensation = 345_000  # 2025 ERISA limit (adjusts annually)

for employee in eligible_employees:
    if employee.compensation > max_compensation:
        capped_compensation = max_compensation
        # Log compensation cap event
    else:
        capped_compensation = employee.compensation
    
    total_eligible_compensation += capped_compensation
Example:
  • Employee A: 80,000compcappedat80,000 comp → capped at 80,000 (under limit)
  • Employee B: 250,000compcappedat250,000 comp → capped at 250,000 (under limit)
  • Employee C: 400,000compcappedat400,000 comp → capped at 345,000 (over limit) ⚠️
Total eligible comp = 80K+80K + 250K + 345K=345K = 675,000

Phase 3: Allocate Shares

Shares are distributed pro-rata by eligible compensation:
for employee in eligible_employees:
    allocation_ratio = employee.capped_compensation / total_eligible_compensation
    shares_to_allocate = total_share_pool * allocation_ratio
Example Allocation:
EmployeeCompensationCapped CompRatioShare PoolAllocation
A$80,000$80,00011.85%5,000593 shares
B$250,000$250,00037.04%5,0001,852 shares
C$400,000$345,00051.11%5,0002,555 shares
Total$730,000$675,000100%5,0005,000 shares
Notice Employee C’s allocation is based on 345K,nottheiractual345K, not their actual 400K compensation.

Phase 4: Apply Annual Addition Cap

ERISA Annual Addition Limit (IRC §415):
Maximum value that can be added to an employee’s account in a year: $69,000 (2025 limit).This includes employer contributions, forfeitures allocated, and certain other additions.
max_annual_addition = 69_000  # 2025 ERISA limit

for employee in eligible_employees:
    allocation_value = shares_to_allocate * share_price
    
    if allocation_value > max_annual_addition:
        # Scale down allocation to cap
        shares_to_allocate = max_annual_addition / share_price
        
        # Log annual addition cap event
Example:
  • Share Price: $500
  • Employee C allocated: 2,555 shares
  • Value: 2,555 * 500=500 = 1,277,500 ⚠️ WAY OVER
Capped Allocation:
  • Max value: $69,000
  • Capped shares: 69,000/69,000 / 500 = 138 shares
  • Employee C receives: 138 shares (not 2,555)

Multi-Class Allocation

When multi_class_mode=True, allocations are distributed per security:
1

Determine Per-Security Pools

From Step 2, each security has its own share pool:
share_pool_by_security = {
    "CLASS_A": 3_000.0,  # shares available
    "CLASS_B": 2_000.0   # shares available
}
2

Allocate Each Security Pro-Rata

for employee in eligible_employees:
    allocation_ratio = employee.capped_comp / total_eligible_comp
    
    for security_id, pool_quantity in share_pool_by_security.items():
        employee_allocation[security_id] = pool_quantity * allocation_ratio
3

Apply Annual Addition Cap

Calculate combined value and scale if necessary
4

Update Employee Holdings

for security_id, quantity in employee_allocation.items():
    if not employee.holdings.get(security_id):
        employee.holdings[security_id] = Holding()
    employee.holdings[security_id].shares += quantity
Example Multi-Class Allocation:
EmployeeComp RatioClass A PoolClass A AllocClass B PoolClass B Alloc
A20%3,0006002,000400
B35%3,0001,0502,000700
C45%3,0001,3502,000900

Data Flow

Inputs

{
  "total_shares_for_pool": 5000.0,           # Single-class mode
  "share_pool_by_security": {                # Multi-class mode
    "CLASS_A": 3000.0,
    "CLASS_B": 2000.0
  }
}

Outputs

{
  "allocated_shares": 1250.0,         # Total allocated (aggregate)
  "holdings": {                        # Multi-class mode
    "CLASS_A": {
      "shares": 750.0                 # Allocated Class A
    },
    "CLASS_B": {
      "shares": 500.0                 # Allocated Class B
    }
  }
}

Compliance Events

Every employee gets evaluated:
{
  "year": 2025,
  "phase": "eligibility",
  "event": "eligibility_evaluated",
  "entity_type": "employee",
  "entity_id": "EMP001",
  "inputs": {
    "age": 35,
    "service_years": 5.0,
    "hours_worked": 2080,
    "eligibility_age": 21,
    "eligibility_service_years": 1.0,
    "eligibility_min_hours": 1000
  },
  "outputs": {
    "eligible": true
  }
}
When compensation exceeds ERISA limit:
{
  "year": 2025,
  "phase": "allocation",
  "event": "compensation_capped",
  "entity_type": "employee",
  "entity_id": "EMP042",
  "details": {
    "original": 400000.0,
    "capped": 345000.0
  },
  "policy": "erisa_compensation_cap"
}
Company-level summary:
{
  "year": 2025,
  "phase": "allocation",
  "event": "covered_comp_summary",
  "entity_type": "company",
  "inputs": {
    "max_compensation": 345000.0
  },
  "outputs": {
    "total_capped_compensation": 2875000.0,
    "eligible_employee_count": 45
  }
}
When allocation value exceeds $69K:
{
  "year": 2025,
  "phase": "allocation",
  "event": "annual_addition_capped",
  "entity_type": "employee",
  "entity_id": "EMP105",
  "details": {
    "original_value": 1277500.0,
    "capped_value": 69000.0
  },
  "policy": "erisa_annual_addition_cap"
}
Complete allocation audit trail:
{
  "year": 2025,
  "phase": "allocation",
  "event": "allocation_computed",
  "entity_type": "employee",
  "entity_id": "EMP001",
  "inputs": {
    "capped_compensation": 125000.0,
    "total_eligible_compensation": 2875000.0,
    "share_pool_by_security": {
      "CLASS_A": 3000.0,
      "CLASS_B": 2000.0
    },
    "price_by_security": {
      "CLASS_A": 600.0,
      "CLASS_B": 400.0
    },
    "max_annual_addition": 69000.0
  },
  "outputs": {
    "shares_allocated_by_security": {
      "CLASS_A": 130.4,
      "CLASS_B": 86.96
    }
  },
  "policy": "erisa_annual_addition_cap"
}

Edge Cases

If no employees meet eligibility criteria:
  • Step 3 exits early
  • Share pool carries forward to next year
  • No allocations made
If all eligible employees have zero compensation:
  • Step 3 exits early
  • Division by zero avoided
  • Share pool carries forward
In high-share-price scenarios:
  • Cap may limit ALL allocations
  • Causes leftover shares in pool
  • Leftover shares carry to next year via forfeitures
Employees hired mid-year:
  • May not meet hours requirement
  • Excluded from allocation
  • Will be eligible next year if they meet criteria


Summary

Step 3 is the allocation engine that:
  • ✅ Determines employee eligibility (age, service, hours)
  • ✅ Applies ERISA compensation cap ($345K in 2025)
  • ✅ Distributes shares pro-rata by eligible compensation
  • ✅ Enforces annual addition cap ($69K in 2025)
  • ✅ Supports multi-class securities with per-security tracking
  • ✅ Emits comprehensive compliance events
Key Insight: ERISA caps can significantly constrain allocations for highly compensated employees and high-share-price companies, often causing shares to remain unallocated and carry forward to future years.