Skip to main content

Foundational Philosophy

The Repurchase Engine is built upon three core design principles that ensure its robustness, auditability, and extensibility. These principles inform every architectural decision and distinguish this system from traditional financial modeling tools.

Separation of Concerns

Distinguish legal rules from business strategy

Immutability

Never overwrite; always append

Abstraction

High-level intents, not low-level functions

Principle 1: Separation of Concerns

Plan Rules vs. Operating Assumptions

The engine’s input framework makes a crucial distinction between two types of information:

PlanRules

The “Constitution”The stable, legal framework of the ESOP as defined in the official plan document.Characteristics:
  • Non-discretionary
  • Legally binding
  • Changes infrequently
  • Requires plan amendments
Examples:
  • Vesting schedule
  • Distribution timing
  • Diversification eligibility
  • Cash usage hierarchy

OperatingAssumptions

The “Annual Strategy”The discretionary, variable financial decisions made by the company on an annual basis.Characteristics:
  • Discretionary
  • Business decisions
  • Changes annually
  • No legal filing required
Examples:
  • Contribution amounts
  • Share valuations
  • Repurchase timing
  • Growth assumptions

Why This Matters

This separation directly reflects how plan sponsors and fiduciaries actually think and operate. The plan document is the unchanging foundation, while annual business decisions vary based on financial conditions.
To compare scenarios, you simply swap out OperatingAssumptions while keeping PlanRules constant. No need to duplicate or modify legal configurations.
# Scenario 1: Conservative
conservative = engine.simulate(
    plan_rules=legal_framework,
    operating_assumptions=conservative_strategy
)

# Scenario 2: Aggressive
aggressive = engine.simulate(
    plan_rules=legal_framework,  # Same rules!
    operating_assumptions=aggressive_strategy
)
By structurally separating legal requirements from business decisions, the system prevents common errors like accidentally modifying vesting schedules when you meant to change contribution levels.

Implementation Example

{
  "plan_name": "Acme Corp ESOP",
  "plan_year_end": "12/31",
  "vesting_schedule": {
    "type": "graded",
    "schedule": [0, 0, 20, 40, 60, 80, 100]
  },
  "distribution_policy": {
    "timing": "termination_plus_1_year",
    "form": "lump_sum",
    "in_service_allowed": false
  },
  "cash_usage_policy": [
    "unallocated_company_contributions",
    "unallocated_forfeiture_cash",
    "participant_cash_accounts"
  ]
}
☝️ These rarely change

Principle 2: Immutability

The Database as a System of Record

The data layer is a temporal, stateful, and immutable system of record.
First Principle: A model’s credibility is tied to its reproducibility. We never overwrite data; every input scenario and simulation run is a permanent, versioned record.

The Problem with Traditional Approaches

Most financial models overwrite results:
❌ Traditional Model:
   Run 1 (June) → results.xlsx → ✓
   Run 2 (Sept) → results.xlsx → ✓ [Run 1 data lost!]
   
   Questions:
   - What changed between runs?
   - Can we reproduce June's results?
   - Which assumptions drove the differences?
   
   Answer: 🤷 Unknown

The Immutable Approach

The Repurchase Engine appends, never overwrites:
✅ Immutable System:
   Run 1 (June) → Scenario A v1 → SimRun #123 → Results archived
   Run 2 (Sept) → Scenario A v2 → SimRun #456 → Results archived
   
   Questions:
   - What changed? → Diff Scenario v1 vs v2
   - Reproduce June? → Replay SimRun #123
   - Compare results? → Query both runs
   
   Answer: ✅ Fully auditable

Implementation

Every simulation creates permanent, timestamped records:
1

Input Versioning

scenarios
  id: "scenario_2024_06_15_001"
  created_at: "2024-06-15T10:30:00Z"
  plan_rules: {...}
  operating_assumptions: {...}
  version: 1
2

Execution Logging

simulation_runs
  id: "run_123"
  scenario_id: "scenario_2024_06_15_001"
  run_at: "2024-06-15T10:31:22Z"
  status: "completed"
  duration_ms: 1842
3

State Archival

annual_trust_states
  simulation_run_id: "run_123"
  year: 2025
  cash_balance: 425000
  allocated_shares: 45000
  source_type: "simulated"

Benefits

Perfect Reproducibility

Rerun any historical simulation and get identical results

Temporal Analysis

Compare how forecasts evolved over time

Audit Trail

Complete record of all modeling decisions

Debugging

Trace back to exact inputs that produced any output

Real-World Application

# September 2024: Review June's forecast
june_run = engine.get_run("run_123")
sept_run = engine.get_run("run_456")

# Compare assumptions
diff = engine.compare_scenarios(
    june_run.scenario_id,
    sept_run.scenario_id
)

# Analyze: "Why did repurchase obligations increase?"
print(diff.changes)
# Output:
# - share_valuation.annual_growth_rate: 0.05 → 0.08
# - contribution_policy.amount: 500000 → 450000

Principle 3: Abstraction

The Agent Toolkit as a User Intent Layer

The engine is designed for deep integration with AI agents like Kelso (or Claude, GPT, etc.).
First Principle: The tools exposed to an agent represent high-level user intents (e.g., “compare two scenarios”), not a direct mapping to internal engine functions.

The Problem with Tight Coupling

❌ Tight Coupling:
   Agent Tool: get_share_pool()
   Engine Function: _calculate_share_pool_with_loan_releases()
   
   Problem: If engine refactors internal logic, agent breaks

The Abstraction Layer

✅ Abstraction:
   Agent Tool: compare_scenarios(scenario_a, scenario_b)
   Intent: "User wants to understand differences"
   
   Engine: Can completely change internal implementation
   Result: Agent still works because intent is preserved

Agent Toolkit Design

The toolkit exposes user-facing intents, not technical functions:
# High-level, stable intent-based API

agent_toolkit = [
    "create_scenario",           # "I want to model a plan"
    "run_simulation",            # "Show me the forecast"
    "compare_scenarios",         # "How do these differ?"
    "analyze_repurchase_risk",   # "Where are the danger zones?"
    "optimize_contribution",     # "What's the ideal amount?"
    "export_results"             # "Give me a report"
]
☝️ Stable, semantic, user-oriented

Benefits

The agent’s core conversational and analytical abilities remain intact even if the engine’s internal logic is completely overhauled.
Agent tools map directly to how users think and speak:
User: "Can you compare my June and September forecasts?"
Agent: compare_scenarios("june_forecast", "sept_forecast")
Agent developers don’t need deep knowledge of ESOP mechanics or engine internals. They work with high-level concepts that match user needs.

Real-World Example

User: "Kelso, what happens if we reduce our contribution by 20%?"

Agent (Internal):
  1. create_scenario(base_scenario, modify={contribution: -20%})
  2. run_simulation(new_scenario)
  3. compare_scenarios(base_scenario, new_scenario)
  4. analyze_repurchase_risk(new_scenario)

Agent (Response):
  "If you reduce contributions by 20%, your 10-year repurchase 
   obligation increases by $1.2M due to slower loan paydown and 
   reduced cash reserves. Peak cash years shift from 2028 to 2030..."

How These Principles Work Together

The three principles create a powerful, resilient system:
┌──────────────────────────────────────────────────────┐
│ Agent Layer (Abstraction)                            │
│ High-level user intents                              │
└─────────────────┬────────────────────────────────────┘


┌──────────────────────────────────────────────────────┐
│ Engine Layer (Separation of Concerns)                │
│ PlanRules + OperatingAssumptions → Processing        │
└─────────────────┬────────────────────────────────────┘


┌──────────────────────────────────────────────────────┐
│ Data Layer (Immutability)                            │
│ Versioned, append-only system of record              │
└──────────────────────────────────────────────────────┘

The Compound Effect

  • Separation makes configuration intuitive
  • Immutability makes results reproducible
  • Abstraction makes integration stable
→ Result: A system that’s both powerful and maintainable

Next Steps