> ## Documentation Index
> Fetch the complete documentation index at: https://village-docs.villagelabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Step 8: Year-End Closing

> Finalize annual results, evolve employee data, and prepare for next year

## Overview

Step 8 is the **final step** in the annual processing cycle. It rolls account balances forward, applies yearly evolution to employee data (age, service, compensation), and captures the complete year's results.

<Info>
  This step transforms **in-progress allocations** into **opening balances** for the next year and advances the simulation forward in time.
</Info>

## Core Responsibilities

<CardGroup cols={2}>
  <Card title="Balance Rollover" icon="rotate-right">
    Move allocated/diversified amounts to opening balances
  </Card>

  <Card title="Yearly Evolution" icon="calendar-plus">
    Age employees by 1 year, grow compensation, increment service
  </Card>

  <Card title="Remove Paid-Out Participants" icon="user-minus">
    Clean up fully distributed accounts
  </Card>

  <Card title="Capture Results" icon="camera">
    Store year-end snapshots and KPIs
  </Card>
</CardGroup>

***

## Processing Phases

### Phase 1: Roll Balances Forward

For **active employees** (not terminated), consolidate the year's activity:

```python theme={null}
for employee in active_employees:
    if not employee.termination_date:  # Active employees only
        # Roll shares forward
        employee.opening_shares = (
            employee.opening_shares +      # What they started with
            employee.allocated_shares -    # Plus this year's allocation
            employee.diversified_shares    # Minus diversification
        )
        
        # Roll cash forward
        employee.opening_cash = (
            employee.opening_cash +
            employee.allocated_cash -
            employee.diversified_cash
        )
        
        # Reset annual accumulators
        employee.allocated_shares = 0
        employee.allocated_cash = 0
```

**Example:**

| Field                | Beginning | Step 3 Allocation | Step 6 Diversification | Year-End      |
| -------------------- | --------- | ----------------- | ---------------------- | ------------- |
| `opening_shares`     | 1,500     | N/A               | N/A                    | **2,000**     |
| `allocated_shares`   | 0         | +500              | N/A                    | **0** (reset) |
| `diversified_shares` | 0         | N/A               | 0                      | 0             |

**Result:** Employee starts next year with 2,000 opening shares.

<Note>
  **Terminated employees** do NOT roll balances—their accounts are frozen and distributions are handled via `pending_distributions` in Step 7.
</Note>

***

### Phase 2: Identify Fully Paid-Out Participants

Participants who have received all scheduled distributions are marked for removal:

```python theme={null}
fully_paid_out_threshold = 0.01  # e.g., $0.01 or 0.01 shares

for employee in active_employees:
    if employee.termination_date:  # Only check terminated employees
        if (employee.remaining_payout_shares < threshold AND
            employee.remaining_payout_cash < threshold):
            employee.is_fully_paid_out = True
```

**Cleanup:** At the end of Step 8, fully paid-out employees are **removed from the active list** to improve performance in future years.

```python theme={null}
active_employee_list.remove_fully_paid_out()
```

**Example:**

* Employee terminated in 2020
* 5-year distribution schedule completed in 2025
* Remaining payout: \$0.00
* **Result:** Removed from active list after 2025

<Info>
  Removing fully paid-out participants keeps the simulation efficient. They're preserved in historical snapshots but don't participate in future processing.
</Info>

***

### Phase 3: Capture Annual Results

Create a comprehensive year-end snapshot:

```python theme={null}
annual_results[year] = YearResult(
    year=year,
    share_price=current_share_price,
    total_shares_allocated=sum(emp.allocated_shares for emp in active),
    total_cash_allocated=sum(emp.allocated_cash for emp in active),
    total_forfeitures=...,  # Aggregated from Step 7
    total_distributions=..., # Aggregated from Step 7
    participant_count=len([emp for emp in active if not emp.termination_date]),
    compliance_events=[...]  # All events from this year
)
```

**Included Metrics:**

* ✅ Share price at year-end
* ✅ Total shares allocated this year
* ✅ Total cash allocated
* ✅ Forfeitures captured
* ✅ Distributions paid
* ✅ Active participant count
* ✅ All compliance events (audit trail)

***

### Phase 4: Apply Yearly Evolution

For **remaining active employees**, advance time by one year:

<Tabs>
  <Tab title="Age Increment">
    ```python theme={null}
    for employee in active_employees:
        employee.age = employee.age + 1
    ```

    **Example:**

    * Beginning: Age 35
    * End: Age 36

    <Note>
      This affects eligibility for diversification (age 55+) and RMD (age 73+).
    </Note>
  </Tab>

  <Tab title="Service Increment">
    ```python theme={null}
    for employee in active_employees:
        employee.service_years = employee.service_years + 1.0
    ```

    **Example:**

    * Beginning: 3.5 years service
    * End: 4.5 years service

    <Note>
      This affects vesting percentage in Step 5 of next year.
    </Note>
  </Tab>

  <Tab title="Compensation Growth">
    ```python theme={null}
    comp_growth_rate = financial_assumptions["combined_comp_growth_rate"]  # e.g., 0.03 for 3%

    for employee in active_employees:
        employee.compensation = employee.compensation * (1 + comp_growth_rate)
    ```

    **Example:**

    * Beginning: \$100,000
    * Growth: 3%
    * End: $100,000 * 1.03 = **$103,000\*\*

    <Note>
      This affects pro-rata allocation in Step 3 of next year.
    </Note>
  </Tab>

  <Tab title="Structured Event">
    Each employee evolution emits an event:

    ```json theme={null}
    {
      "year": 2025,
      "phase": "year_end",
      "event": "yearly_evolution_applied",
      "entity_type": "employee",
      "entity_id": "EMP001",
      "inputs": {
        "prev_age": 35,
        "prev_service_years": 3.5,
        "prev_compensation": 100000.0,
        "comp_growth_rate": 0.03
      },
      "outputs": {
        "age": 36,
        "service_years": 4.5,
        "compensation": 103000.0
      }
    }
    ```
  </Tab>
</Tabs>

<Warning>
  **Timing:** Yearly evolution happens **at year-end** (Step 8), not at the beginning (Step 1).

  This means that all processing for year N uses the employee data from the **beginning** of year N, and evolution is applied only after all steps complete.
</Warning>

***

### Phase 5: TrustCashLedger Reconciliation

Emit a complete reconciliation of the **TrustCashLedger** (no investment earnings in v0.3):

```python theme={null}
ledger_summary = {
    "boy": {
        "participant_cash_accounts": 150_000,
        "unallocated_company_contributions": 50_000,
        "unallocated_forfeiture_cash": 25_000
    },
    "deposits": {
        "unallocated_company_contributions": 500_000
    },
    "draws": [
        {"source": "unallocated_company_contributions", "amount": 200_000},
        {"source": "unallocated_forfeiture_cash", "amount": 100_000},
        {"source": "participant_cash_accounts", "amount": 50_000}
    ],
    "transfers": [
        {"from": "unallocated_forfeiture_cash", "to": "participant_cash_accounts", "amount": 25_000}
    ],
    "eoy": {
        "participant_cash_accounts": 200_000,
        "unallocated_company_contributions": 200_000,
        "unallocated_forfeiture_cash": 0
    }
}
```

**Formula:**

```
EOY per source = BOY + Deposits - Draws + Transfers(net)
```

After emitting the summary, **reset any year-accumulators** for next year.

***

## Data Flow

### Inputs

<Tabs>
  <Tab title="Employee Data (Pre-Evolution)">
    ```python theme={null}
    {
      "employee_id": "EMP001",
      "age": 35,
      "service_years": 3.5,
      "compensation": 100000.0,
      "opening_shares": 1500.0,
      "allocated_shares": 500.0,
      "diversified_shares": 0.0,
      "opening_cash": 5000.0,
      "allocated_cash": 0.0,
      "diversified_cash": 0.0,
      "termination_date": null
    }
    ```
  </Tab>

  <Tab title="Financial Assumptions">
    ```python theme={null}
    {
      "combined_comp_growth_rate": 0.03  # 3% annual pay increase
    }
    ```
  </Tab>

  <Tab title="Carry-Over State">
    ```python theme={null}
    {
      "ledger_boy": {
        "participant_cash_accounts": 150000.0,
        "unallocated_company_contributions": 50000.0,
        "unallocated_forfeiture_cash": 25000.0
      }
    }
    ```
  </Tab>
</Tabs>

### Outputs

<Tabs>
  <Tab title="Employee Data (Post-Evolution)">
    ```python theme={null}
    {
      "employee_id": "EMP001",
      "age": 36,                    # +1 year
      "service_years": 4.5,         # +1 year
      "compensation": 103000.0,     # Grown by 3%
      "opening_shares": 2000.0,     # Rolled forward
      "allocated_shares": 0.0,      # Reset
      "diversified_shares": 0.0,
      "opening_cash": 5000.0,       # Rolled forward
      "allocated_cash": 0.0,        # Reset
      "diversified_cash": 0.0
    }
    ```
  </Tab>

  <Tab title="Year Result">
    ```python theme={null}
    {
      "year": 2025,
      "share_price": 525.00,
      "total_shares_allocated": 45000.0,
      "total_cash_allocated": 0.0,
      "total_forfeitures": 2500.0,
      "total_distributions": 15000.0,
      "participant_count": 89,
      "compliance_events": [...]  # Full audit log
    }
    ```
  </Tab>

  <Tab title="Carry-Over State (Reset)">
    ```python theme={null}
    {
      "ledger_boy": {
        "participant_cash_accounts": 200000.0,
        "unallocated_company_contributions": 200000.0,
        "unallocated_forfeiture_cash": 0.0
      }
    }
    ```
  </Tab>
</Tabs>

***

## Compliance Events

<Tabs>
  <Tab title="yearly_evolution_applied">
    Per employee:

    ```json theme={null}
    {
      "year": 2025,
      "phase": "year_end",
      "event": "yearly_evolution_applied",
      "entity_type": "employee",
      "entity_id": "EMP042",
      "inputs": {
        "prev_age": 45,
        "prev_service_years": 12.0,
        "prev_compensation": 150000.0,
        "comp_growth_rate": 0.03
      },
      "outputs": {
        "age": 46,
        "service_years": 13.0,
        "compensation": 154500.0
      }
    }
    ```
  </Tab>

  <Tab title="ledger_reconciliation">
    Company-level TrustCashLedger reconciliation:

    ```json theme={null}
    {
      "year": 2025,
      "phase": "ledger",
      "event": "ledger_reconciliation",
      "entity_type": "company",
      "entity_id": null,
      "inputs": {
        "boy": {
          "participant_cash_accounts": 150000.0,
          "unallocated_company_contributions": 50000.0,
          "unallocated_forfeiture_cash": 25000.0
        },
        "deposits": {"unallocated_company_contributions": 500000.0},
        "draws": [
          {"source": "unallocated_company_contributions", "amount": 200000.0},
          {"source": "unallocated_forfeiture_cash", "amount": 100000.0}
        ]
      },
      "outputs": {
        "eoy": {
          "participant_cash_accounts": 200000.0,
          "unallocated_company_contributions": 200000.0,
          "unallocated_forfeiture_cash": 0.0
        }
      }
    }
    ```
  </Tab>
</Tabs>

***

## Edge Cases

<AccordionGroup>
  <Accordion title="Terminated Employees" icon="user-xmark">
    **Do NOT roll balances or evolve:**

    * Balances frozen at termination
    * Age/service/comp don't increment
    * Distributions handled via `pending_distributions`

    Only **active** employees are evolved.
  </Accordion>

  <Accordion title="Mid-Year Terminations" icon="calendar-days">
    Employees who terminated during year:

    * Participated in Steps 1-7 with beginning-of-year data
    * Do NOT evolve in Step 8
    * Marked as terminated for future years
  </Accordion>

  <Accordion title="Negative Balances" icon="triangle-exclamation">
    In rare cases, corrections might create negative balances:

    ```python theme={null}
    opening_shares = max(0, opening + allocated - diversified)
    ```

    Floor at zero to prevent negative opening balances.
  </Accordion>

  <Accordion title="Zero Compensation Growth" icon="0">
    If no compensation growth assumption:

    ```python theme={null}
    comp_growth_rate = financial_assumptions.get("combined_comp_growth_rate", 0.0)
    employee.compensation = employee.compensation * (1 + comp_growth_rate)
    ```

    Compensation stays flat year-over-year.
  </Accordion>
</AccordionGroup>

***

## Impact on Next Year

Step 8 **sets the stage** for the next year's simulation:

<Tabs>
  <Tab title="Opening Balances">
    What was `allocated_shares` this year becomes `opening_shares` next year:

    **Year 2025 End:**

    * opening\_shares: 2,000
    * allocated\_shares: 0

    **Year 2026 Start:**

    * opening\_shares: 2,000 ← from 2025 rollover
    * allocated\_shares: 0 ← fresh accumulator
  </Tab>

  <Tab title="Vesting Percentage">
    Service increment affects vesting:

    **Year 2025:**

    * Service: 3.5 years → 60% vested (year 3)

    **Year 2026:**

    * Service: 4.5 years → 80% vested (year 4)

    More shares vest automatically due to service increase.
  </Tab>

  <Tab title="Diversification Eligibility">
    Age increment may trigger eligibility:

    **Year 2025:**

    * Age: 54 → Not eligible (need 55+)

    **Year 2026:**

    * Age: 55 → **Eligible** for diversification!
  </Tab>

  <Tab title="RMD Enforcement">
    Age increment may trigger Required Minimum Distributions:

    **Year 2025:**

    * Age: 72 → No RMD yet

    **Year 2026:**

    * Age: 73 → **RMD enforced** in Step 7!
  </Tab>
</Tabs>

***

## Performance Optimization

<AccordionGroup>
  <Accordion title="Remove Fully Paid-Out Participants" icon="user-minus">
    **Purpose:** Reduce active employee list size over time

    **Impact:**

    * Fewer employees to iterate in Steps 1-8
    * Faster projections in later years
    * No loss of data (preserved in snapshots)

    **Threshold:** 0.01 shares or \$0.01 cash
  </Accordion>

  <Accordion title="Reset Accumulators" icon="rotate-left">
    Clear year-specific accumulators to prepare for next year:

    * `allocated_shares = 0`
    * `allocated_cash = 0`
    * OIA tracking variables reset

    Prevents carryover of stale data.
  </Accordion>

  <Accordion title="Batch Evolution" icon="gauge-high">
    All employees evolved in single loop for efficiency:

    ```python theme={null}
    for emp in active_employees:
        emp.age += 1
        emp.service_years += 1.0
        emp.compensation *= (1 + growth_rate)
    ```

    Vectorization opportunities if using NumPy/Pandas.
  </Accordion>
</AccordionGroup>

***

## Related Steps

<CardGroup cols={2}>
  <Card title="Step 1: Initialization" icon="play" href="/simulation/step-1-initialization">
    Uses evolved data from Step 8 of previous year
  </Card>

  <Card title="Step 7: Repurchase" icon="money-bill-transfer" href="/simulation/step-7-repurchase">
    Generates distribution data captured in Step 8
  </Card>

  <Card title="Simulation Core" icon="gears" href="/architecture/simulation-core">
    How Step 8 fits into the annual cycle
  </Card>

  <Card title="TrustCashLedger" icon="wallet" href="/models/trust-cash-ledger">
    Ledger reconciliation details
  </Card>
</CardGroup>

***

## Summary

Step 8 is the **year-end finalization step** that:

* ✅ Rolls share/cash balances forward for active employees
* ✅ Identifies and removes fully paid-out participants
* ✅ Captures comprehensive year-end results and KPIs
* ✅ Applies yearly evolution (age +1, service +1, comp growth)
* ✅ Reconciles OIA ledger with sources/uses/earnings
* ✅ Reconciles TrustCashLedger movements (no earnings in v0.3)
* ✅ Resets accumulators for next year
* ✅ Emits evolution events for audit trail

**Key Insight:** Step 8 is the **bridge between years**. It transforms the current year's processing results into the starting state for the next year, ensuring continuity and accuracy across multi-year projections.

<Note>
  After Step 8 completes, the engine is ready to **loop back to Step 1** for the next year, repeating the 8-step cycle until the projection period ends.
</Note>
