# Owl 2.0 configuration

This document describes the 2.0 configuration layout: clearer separation between **case goals** and **run-time options**, and how they map to the UI and solver.

## Goals and safety net

Goals and safety-net constraints live under **`[optimization_parameters]`** in TOML and on the **Goals & Safety Net** page in the UI. They are enforced as **hard constraints** by the optimizer (not penalty terms).

| Field | Description | TOML / UI |
|-------|-------------|-----------|
| `spending_floor` | Minimum real annual spending ($k) the plan aims to guarantee | Goals & Safety Net page; `optimization_parameters.spending_floor` |
| `spending_floor_percentile` | Target success percentile for the spending floor (0–1), for future MC use | Optional; same section |
| `bequest_target` | Desired bequest from savings accounts ($k, today’s dollars) | Goals & Safety Net page; `optimization_parameters.bequest_target` |
| `bequest_is_hard_constraint` | If true, treat bequest as a hard constraint | Optional; same section |

These fields are mapped internally to the existing solver constraints (`netSpending`, `bequest`) so optimization behavior is unchanged.

## Run-time configuration (RunConfig)

**How** to run a case (solver choice, time limit, MIP gap, verbosity, Monte Carlo path count and seed) is separated from **what** the case is. The UI builds a `RunConfig` from the Run Options (and related) pages and merges it into solver options before calling `plan.solve()`.

- **Solver**: backend (`default` / HiGHS / MOSEK), time limit (s), MIP gap, verbose flag.
- **Monte Carlo**: number of paths, optional seed (from rate seed when applicable).

Programmatic use:

```python
from owlplanner.config.schema import RunConfig, SolverConfig
from owlplanner.config.plan_bridge import run_config_to_solver_options

run_cfg = RunConfig(solver=SolverConfig(backend="HiGHS", time_limit=60.0))
options = run_config_to_solver_options(run_cfg, base={})
plan.solve("maxSpending", options=options)
# or pass run_config directly:
plan.solve("maxSpending", options={"bequest": 1.0}, run_config=run_cfg)
```

## Summary of changes from 1.x

- **Goals & Safety Net** page: spending floor and bequest target are configured here, not under Run Options.
- **Run Options** page: focused on execution (objective choice, solver, time limit, MIP gap, verbose, Roth/Medicare/ACA/expert options). No longer edits goal amounts.
- **TOML**: use `optimization_parameters.spending_floor` and `optimization_parameters.bequest_target` for goals; `solver_options` remains for low-level solver keys, but high-level run behavior can be driven by `RunConfig` in code.
- **API**: `Plan.solve(..., run_config=RunConfig(...))` is supported; goals continue to be applied from config when using `apply_config_to_plan()` (e.g. in the UI).
