Scenarios
Plan-scenario tools: create, apply, and compare scenarios against a base plan.
A scenario is a named, ordered list of typed overrides (a delta) applied
against a base UserState (design dev_docs/tasks/scenario-storage-design.md).
These tools are thin wrappers over finplan_core.scenarios: the numbers are
always re-derived server-side by running the whole-plan projection per scenario —
never narrated between tool calls.
The base is identified by a BaseRef: a mandatory state_hash (the content
hash manage_state returns) plus an optional state_ref accelerator (the
st_… handle from the ephemeral state store) so the full document need not be
re-sent through model context mid-session. An inline state_json always works
and takes precedence.
create_scenario
Section titled “create_scenario”Create a plan scenario: a named, validated delta of typed overrides against a base plan.
| Parameter | Type | Default | Description |
|---|---|---|---|
base | object | required | BaseRef the scenario is authored against: {‘state_hash’: ‘sha256:…’ (required, from manage_state), ‘state_ref’: ‘st_…’ (optional in-session accelerator). The response returns base_state_ref for reuse as base.state_ref while live. |
name | string | required | Human label, e.g. ‘Retire at 60’. |
overrides | list | required | Ordered, typed overrides (the delta), each tagged by ‘kind’: retirement_age, return_assumption, inflation, tax_rate, monthly_contribution, account_balance, income_change, expense_change, goal_target. Amounts are in cents. E.g. [{‘kind’: ‘retirement_age’, ‘age’: 60}]. |
description | string | null | What question this scenario explores (e.g. ‘does retiring at 60 still fund college?’). Defaults to a generated summary of the overrides. |
state_json | object | null | The base UserState document inline. Optional when base.state_ref is still live; takes precedence when both are given. |
Returns: scenario, display_label, input_diff, base_state_hash, base_state_ref, and warnings.
apply_scenario
Section titled “apply_scenario”Resolve a scenario’s state-shaped overrides against the base and return a state_ref to the resolved (hypothetical) UserState — plus the resolved state_json inline when return_state_json=True — for inspection or handing to other tools.
| Parameter | Type | Default | Description |
|---|---|---|---|
base | object | required | BaseRef: {‘state_hash’: ‘sha256:…’ (required), ‘state_ref’: ‘st_…’ (optional)}. |
scenario | object | required | The scenario to apply: a create_scenario document, or a minimal {‘name’: …, ‘overrides’: […]} sketch. |
state_json | object | null | The base UserState document inline. Optional when base.state_ref is still live; takes precedence when both are given. |
return_state_json | bool | false | When True, also return the resolved hypothetical UserState inline as ‘state_json’. Use it with run_projection (which takes state_json); project_plan also accepts the returned state_ref directly. |
Returns: scenario_id, display_label, state_ref, state_hash, warnings — plus the resolved state_json inline when return_state_json=True.
compare_scenarios
Section titled “compare_scenarios”Compare plan scenarios against a base plan: one server-side operation that projects every scenario’s whole plan and diffs the outcomes — never narrated arithmetic.
| Parameter | Type | Default | Description |
|---|---|---|---|
base | object | null | BaseRef shared by the scenarios: {‘state_hash’: ‘sha256:…’ (required), ‘state_ref’: ‘st_…’ (optional)}. Required with ‘scenarios’; ignored in favor of the set-level base when ‘scenario_set’ is given. The response returns base_state_ref for reuse as base.state_ref while live. |
scenarios | list | null | Scenarios to compare against the base: create_scenario documents or minimal {‘name’: …, ‘overrides’: […]} sketches. Mutually exclusive with scenario_set. |
scenario_set | object | null | A portable finplan_scenario_set document ({‘base’: BaseRef, ‘scenarios’: […]}); its set-level base is authoritative. Mutually exclusive with scenarios. |
state_json | object | null | The base UserState document inline. Optional when the base’s state_ref is still live; takes precedence when both are given. |
time_horizon_months | int | 360 | Months to project (default 360 = 30 years). Shared by the base and every scenario so outcomes land on one comparable grid. |
assumptions_preset | string | "standard" | Capital-market assumptions preset the scenarios vary from: ‘standard’ (stocks 7%/15%), ‘conservative’ (5%/18%), or ‘optimistic’ (10%/15%). |
inflation | float | 0.025 | Baseline annual inflation rate as a decimal (e.g., 0.03 = 3%; default 2.5%); an inflation override in a scenario replaces it for that scenario. When > 0, values are in today’s purchasing power; 0 gives nominal dollars. |
percentiles | list | null | Percentiles to compute (default: [10, 25, 50, 75, 90]). |
marginal_ordinary_rate | float | 0.22 | Household marginal ordinary income tax rate for after-tax values; a tax_rate override in a scenario replaces it for that scenario. |
ltcg_rate | float | 0.15 | Household long-term capital gains tax rate for after-tax values; a tax_rate override in a scenario replaces it for that scenario. |
method | string | "closed_form" | Projection method (closed_form is recommended). |
iterations | int | 1000 | Monte Carlo iterations (only used for method=‘monte_carlo’). |
seed | int | null | Random seed, pinning which Monte Carlo draw runs (only used for method=‘monte_carlo’). Omitting it is still reproducible — monte_carlo falls back to a fixed default seed. |
compute_income_tax | bool | true | Compute each year’s federal, state, local and payroll tax from the base state’s person and each scenario’s income, and subtract it from the surplus (default true), as project_plan does, so a scenario’s delta includes its tax consequence. Computed tax replaces hand-entered TAXES expenses; false keeps them and invests the pre-tax surplus. |
Returns file URLs + a compact inline summary; the per-month timelines live in the data file only (see the returned schema URL and jq examples). The summary inputs include base_state_ref: reuse it as base.state_ref while it remains live instead of inlining the full state again.