Private-Wealth Advisory Solver SOP
Executable procedure for producing the single JSON object each engagement asks for.
Inputs per task: input/prompt.txt, input/payloads/request_memo.md,
input/payloads/answer_template.json. Return ONLY a JSON object conforming to the
template — no prose. All USD numbers are JSON numbers rounded to cents; all dates are
ISO YYYY-MM-DD.
1. Remote API workflow
Base URL: <remote-env-url> (the harness may expose it as API_BASE; prefer
$API_BASE when set, otherwise the URL above). Use only this remote API.
Endpoint call order (GET, all take client_id except where noted):
GET /api/health -> sanity check {"ok": true}.
GET /api/clients/<client_id> -> base record: age, marital_status,
filing_status, planning_year, estate_value, liquid_assets.
GET /api/source-documents?client_id=<id> -> the CONFLICTING docs (each has
source_type in {CRM_NOTE, ATTORNEY_MEMO, SIGNED_PROFILE,
STALE_MARKETING_INTAKE} plus effective_date and a facts map).
GET /api/retirement-accounts?client_id=<id> -> retirement account(s), each tagged
source_type: CUSTODIAN_EXPORT: traditional_balance, roth_balance,
expected_return, rmd_start_age, recommended_conversion_years.
GET /api/life-insurance?client_id=<id> -> proposed ILIT policy: death_benefit,
annual_premium, planned_contribution_date, is_existing_policy_transfer.
GET /api/trust-candidates?client_id=<id> -> one trust case: asset_value,
expected_growth_rate, grat_term_years, grat_annuity_rate, crat_term_years,
crat_payout_rate.
GET /api/policies/tax -> constants (see §3).
GET /api/rmd-factors -> {age: divisor} (73->26.5, 74->25.5, ... 99->6.8).
Only fetch endpoints the analysis type needs, but always fetch clients,
source-documents, and the relevant detail endpoint(s).
2. Reading the engagement & planning horizon
analysis_type: determined by the memo engagement line. Four types:
roth_conversion_rmd, ilit_crummey_implementation, trust_comparison,
estate_liquidity_action_plan.
planning_year: take from the client base record / signed profile (e.g. 2026). It is
the base year for conversions, estate context, and contribution dates.
- RMD horizon (roth only): parse the request memo for the literal
Planning horizon year: YYYY (or a ... through YYYY phrase). That year is
rmd_projection.horizon_year and the RMD simulation runs through it inclusive. The
other three analysis types have no horizon concept.
task_id, client_id: copy from the prompt/template verbatim.
3. Tax / policy constants (/api/policies/tax)
annual_gift_exclusion keyed by year; use the entry for planning_year
(per-beneficiary Crummey exclusion).
estate_tax_exemption keyed by year; use the entry for planning_year.
estate_tax_rate (e.g. 0.40).
conversion_bracket_targets keyed by filing_status (MFJ/SINGLE/HOH) -> the
taxable-income ceiling to fill with conversions each year.
max_crat_term_years (e.g. 20) and charitable_deduction_rate (e.g. 0.35).
4. Source-resolution precedence
Conflict resolution is by FACT-FAMILY AUTHORITY, not a single global ranking. For each
family, the AUTHORITY source type controls both the value used and the reported
source_resolution field; if the authority is absent for a client, fall back by the
precedence chain SIGNED_PROFILE > ATTORNEY_MEMO > CUSTODIAN_EXPORT > CRM_NOTE > STALE_MARKETING_INTAKE (STALE_MARKETING_INTAKE is always lowest; treat it as
unreliable).
Authority by family:
- Personal profile, beneficiary count, planning goals (
family_transfer_priority,
philanthropic_intent), annual_non_ira_income, marginal_tax_rate, age,
planning_year, filing/marital status, liquid_assets -> SIGNED_PROFILE
(fall back to ATTORNEY_MEMO, then CRM_NOTE).
- Retirement-account balances,
expected_return, rmd_start_age,
recommended_conversion_years -> CUSTODIAN_EXPORT (these come from
/api/retirement-accounts, whose records are tagged CUSTODIAN_EXPORT).
- Estate / trust-asset valuation (
estate_value, trust asset_value,
GRAT/CRAT assumptions) -> ATTORNEY_MEMO (the attorney memo is the estate/trust
valuation authority; fall back to SIGNED_PROFILE, then CRM_NOTE).
- Insurance policy terms (
death_benefit, annual_premium,
planned_contribution_date, is_existing_policy_transfer) -> SIGNED_PROFILE
(the policy is authorized under the signed engagement; values are read from
/api/life-insurance).
Reported source_resolution (fields depend on analysis type):
- roth:
controlling_profile_source = SIGNED_PROFILE,
controlling_account_source = CUSTODIAN_EXPORT.
- ilit:
controlling_beneficiary_source = SIGNED_PROFILE,
controlling_policy_source = SIGNED_PROFILE.
- trust_comparison:
controlling_goal_source = SIGNED_PROFILE,
controlling_asset_source = ATTORNEY_MEMO.
- estate_liquidity:
controlling_goal_source = SIGNED_PROFILE,
controlling_policy_source = SIGNED_PROFILE.
5. Common calculation conventions
- Round every USD output to cents (round half up) as the LAST step; for derived
products (e.g. estate-tax-reduction = remainder * rate), compute from the UNROUNDED
intermediate, then round.
- Growth: compound annually as
(1+rate)**years (use full float precision, do not
round intermediate powers).
- Dates: ISO
YYYY-MM-DD; use true calendar-day arithmetic (Python date + timedelta).
marginal_tax_rate (SIGNED_PROFILE) is the tax rate applied to conversions AND to
each year's RMD amount.
6. Analysis A — roth_conversion_rmd
Inputs: /retirement-accounts (T0=traditional_balance, R0=roth_balance, r=
expected_return, rmd_start_age, conv_years=recommended_conversion_years),
SIGNED_PROFILE (mtr, inc=annual_non_ira_income, age0=age,
filing_status), /policies/tax bracket target, /rmd-factors, memo horizon.
Conversion plan:
first_conversion_year = planning_year.
conversion_years = conv_years (CUSTODIAN_EXPORT).
annual_conversion_amount = max(0, bracket_target[filing_status] - inc)
(flat each year; income does NOT grow).
conversion_years_positive = count of conversion years with a positive amount
(capped by remaining balance; equals conversion_years when bracket > income and
balance suffices).
total_converted = annual_conversion_amount * conversion_years_positive.
total_conversion_tax = total_converted * mtr.
first_rmd_year = planning_year + (rmd_start_age - age0)
(age0 is the client's age IN the planning year).
Year-by-year RMD projection (loop y from planning_year through horizon
INCLUSIVE; age = age0 + (y - planning_year)):
BASELINE (no conversion): trad = T0; base_tax = 0.
for each y:
if y >= first_rmd_year:
rmd_amt = trad / rmd_factor[age]
base_tax += rmd_amt * mtr
trad = (trad - rmd_amt) * (1 + r)
else:
trad = trad * (1 + r)
CONVERSION scenario: trad = T0; roth = R0; conv_tax = 0; conversion years =
{planning_year .. planning_year + conv_years - 1}.
for each y:
conv = annual_conversion_amount if y in conversion_years else 0
trad = trad - conv # conversion taken first, at start of year
roth = roth + conv # into Roth before this year's growth
if y >= first_rmd_year:
rmd_amt = trad / rmd_factor[age] # RMD base is AFTER conversion
conv_tax += rmd_amt * mtr
trad = (trad - rmd_amt) * (1 + r)
else:
trad = trad * (1 + r)
roth = roth * (1 + r) # Roth (incl. this year's conversion) grows
baseline_rmd_tax_through_horizon = base_tax.
conversion_rmd_tax_through_horizon = conv_tax.
rmd_tax_savings_through_horizon = base_tax - conv_tax.
projected_roth_balance_horizon = roth at end of horizon (CONVERSION scenario).
projected_traditional_balance_horizon = trad at end of horizon (CONVERSION
scenario).
horizon_year = memo horizon.
Recommendation:
primary_action = STAGED_ROTH_CONVERSION if annual_conversion_amount > 0 and
conv_years > 0, else NO_CONVERSION (or DEFER).
suitability = SUITABLE when positive conversion, else DEFER/BORDERLINE.
risk_flag: TAX_BRACKET_MANAGEMENT (default). Use LIQUIDITY_CONSTRAINT if
liquid_assets < total_conversion_tax; use RMD_NEAR_TERM if
first_rmd_year <= planning_year (already at/ past RMD age).
heir_tax_profile: MIXED_TAXABLE_AND_TAX_FREE if both projected balances > 0;
MOSTLY_TAX_FREE if traditional balance <= 0 and roth > 0; MOSTLY_TAXABLE if
roth balance <= 0.
7. Analysis B — ilit_crummey_implementation
Inputs: /life-insurance policy, SIGNED_PROFILE beneficiary_count,
/policies/tax annual_gift_exclusion[planning_year], estate_tax_rate.
gift_plan:
planning_year = client planning year.
annual_exclusion_per_beneficiary = annual_gift_exclusion[planning_year].
beneficiary_count = from SIGNED_PROFILE.
annual_exclusion_capacity = annual_exclusion_per_beneficiary * beneficiary_count.
annual_premium = policy annual_premium.
premium_gap = max(0, annual_premium - annual_exclusion_capacity).
administration (calendar-day arithmetic):
notices_required = beneficiary_count (one Crummey notice per beneficiary).
contribution_date = policy planned_contribution_date.
notice_due_date = contribution_date + 7 days.
withdrawal_window_end = notice_due_date + 30 days.
earliest_premium_payment_date = withdrawal_window_end + 1 day.
dedicated_bank_account_required = true (always for an ILIT Crummey cycle).
estate_result:
death_benefit = policy death_benefit.
estate_inclusion_risk = the risk_flag (see below).
projected_outside_estate_if_implemented = death_benefit (the ILIT removes the
full death benefit from the gross estate when formalities are met).
tax_liquidity_support = death_benefit * estate_tax_rate.
risk_flag / primary_action decision tree (from is_existing_policy_transfer +
premium_gap):
- not existing AND gap 0 ->
LOW_IF_FORMALITIES_MET, action
FUND_WITH_CRUMMEY_NOTICES, suitability SUITABLE_WITH_ADMINISTRATION.
- not existing AND gap > 0 ->
EXCLUSION_SHORTFALL, action
USE_LIFETIME_EXEMPTION_FOR_SHORTFALL.
- existing AND gap 0 ->
THREE_YEAR_LOOKBACK, action
USE_NEW_POLICY_OR_ACCEPT_LOOKBACK.
- existing AND gap > 0 ->
THREE_YEAR_LOOKBACK_AND_EXCLUSION_SHORTFALL, action
DISCLOSE_LOOKBACK_AND_USE_EXEMPTION.
8. Analysis C — trust_comparison
Inputs: /trust-candidates (asset_value V, expected_growth_rate g,
grat_term_years, grat_annuity_rate, crat_term_years, crat_payout_rate),
SIGNED_PROFILE goals (family_transfer_priority, philanthropic_intent) + estate
facts, estate_tax_rate, charitable_deduction_rate, max_crat_term_years.
estate_context:
planning_year = client planning year.
exemption_used = (2 if married/MFJ else 1) * estate_tax_exemption[planning_year].
taxable_estate = estate_value - exemption_used (estate_value from ATTORNEY_MEMO
authority, fallback SIGNED_PROFILE).
estate_tax_exposure = taxable_estate * estate_tax_rate.
liquid_assets_available = liquid_assets (SIGNED_PROFILE).
liquidity_gap_before_planning = max(0, estate_tax_exposure - liquid_assets_available).
Formulas (compute from UNROUNDED values, round outputs to cents):
- GRAT
projected_remainder_to_heirs =
V*(1+g)**grat_term_years - grat_annuity_rate*V*grat_term_years.
- GRAT
estimated_estate_tax_reduction =
round(projected_remainder_to_heirs_unrounded * estate_tax_rate, 2).
- GRAT
term_years = grat_term_years; mortality_inclusion_risk =
TERM_SURVIVAL_REQUIRED.
- CRAT
projected_charitable_remainder =
V*(1+g)**crat_term_years - crat_payout_rate*V*crat_term_years.
- CRAT
estimated_income_tax_deduction =
round(projected_charitable_remainder_unrounded * charitable_deduction_rate, 2).
- CRAT
term_years = crat_term_years (== max_crat_term_years).
Recommendation:
preferred_strategy = GRAT if family_transfer_priority == high; CRAT if
philanthropic_intent == high.
rationale_code = CHILDREN_TRANSFER_PRIORITY (GRAT) or PHILANTHROPIC_PRIORITY
(CRAT).
alternate_role = SECONDARY_CHARITABLE_TOOL (if GRAT preferred) or
SECONDARY_FAMILY_TRANSFER_TOOL (if CRAT preferred).
- CRAT
family_transfer_fit = LOW when GRAT is preferred; else MODERATE/HIGH.
9. Analysis D — estate_liquidity_action_plan
Combines an estate_context, a condensed ILIT block, a trust_transfer block, and an
action_set. Inputs: same as B + C for the same client.
estate_context: same formulas as §8.
ilit (condensed):
annual_exclusion_capacity = annual_gift_exclusion[planning_year] * beneficiary_count.
premium_gap = max(0, annual_premium - annual_exclusion_capacity).
estate_inclusion_risk = risk_flag from the same decision tree as §7.
projected_outside_estate_if_implemented = death_benefit.
trust_transfer:
preferred_strategy = GRAT if family_transfer_priority == high else CRAT.
projected_remainder_to_heirs = GRAT remainder formula (§8).
estimated_estate_tax_reduction = round(remainder_unrounded * estate_tax_rate, 2).
projected_charitable_remainder = CRAT remainder formula (§8).
recommendation:
primary_action = COMBINE_ILIT_AND_GRAT when ILIT is fundable (gap 0) and GRAT is
preferred; CRAT_WITH_LIQUIDITY_REVIEW when CRAT preferred with a liquidity gap;
ILIT_WITH_EXEMPTION_REVIEW when ILIT has a premium_gap and no trust driver.
sequencing = ILIT_FIRST_THEN_GRAT when combining ILIT + GRAT;
TRUST_DECISION_FIRST when CRAT; ILIT_FIRST_THEN_ATTORNEY_REVIEW when ILIT-only.
risk_flag = the ILIT risk_flag (decision tree §7).
action_set: choose from the enum set {ATTORNEY_DRAFT_REVIEW,
CRAT_FOR_CHARITABLE_REMAINDER, GRAT_FOR_APPRECIATING_SHARES,
ILIT_CRUMMEY_NOTICE_CYCLE, LIFETIME_EXEMPTION_ALLOCATION} and SORT ALPHABETICALLY.
Selection:
- Always include
ATTORNEY_DRAFT_REVIEW (combined plan needs attorney drafting).
- Include
ILIT_CRUMMEY_NOTICE_CYCLE when an ILIT is being funded.
- Include
GRAT_FOR_APPRECIATING_SHARES when preferred_strategy == GRAT.
- Include
CRAT_FOR_CHARITABLE_REMAINDER when preferred_strategy == CRAT.
- Include
LIFETIME_EXEMPTION_ALLOCATION when premium_gap > 0.
10. Common mistakes to avoid
- Wrong source winning: do NOT let CRM_NOTE or STALE_MARKETING_INTAKE override
SIGNED_PROFILE for profile/goal facts, and do NOT let SIGNED_PROFILE override
ATTORNEY_MEMO for estate/trust-asset valuation. Use the family-authority rule in §4.
- Wrong horizon year: read the horizon ONLY from the memo's
Planning horizon year: YYYY line (roth tasks). Do not infer it from age or RMD start.
- Off-by-one conversion years: conversions run
planning_year .. planning_year + recommended_conversion_years - 1; RMDs run first_rmd_year .. horizon INCLUSIVE. They may overlap (near-RMD client) — that is correct.
- RMD base wrong: in a year that is BOTH a conversion year and an RMD year, the RMD is
computed AFTER subtracting the conversion (
trad = trad - conv first).
- RMD factor lookup: use the client's age IN that year (
age0 + (y - planning_year)),
keyed on /rmd-factors (73->26.5, etc.). Do not recompute the table.
- Roth/ traditional balances at horizon: taken from the CONVERSION scenario, after
conversions AND RMDs both deplete the balance; baseline is only used for the
baseline_rmd_tax number.
- GRAT/CRAT rounding:
estimated_estate_tax_reduction and
estimated_income_tax_deduction MUST be computed from the UNROUNDED remainder, then
rounded — rounding the remainder first loses a cent.
- Non-ISO dates: all
administration dates must be YYYY-MM-DD strings produced by
calendar-day arithmetic (+7, +30, +1), not month-string math.
- action_set not sorted: emit the estate-liquidity
action_set in strict alphabetical
order.
- Numbers as strings: every USD figure must be a JSON number (e.g.
80000.0), never a
quoted string.
- Exemption multiplier: married/MFJ uses
2 * the per-person exemption; single uses
1 *. Wrong multiplier cascades into taxable_estate, exposure, and liquidity_gap.
1---2name: fewshot-attempt-03-53description: Private-Wealth Advisory Solver SOP4---5# Private-Wealth Advisory Solver SOP67Executable procedure for producing the single JSON object each engagement asks for.8Inputs per task: `input/prompt.txt`, `input/payloads/request_memo.md`,9`input/payloads/answer_template.json`. Return ONLY a JSON object conforming to the10template — no prose. All USD numbers are JSON numbers rounded to cents; all dates are11ISO `YYYY-MM-DD`.1213## 1. Remote API workflow1415Base URL: `<remote-env-url>` (the harness may expose it as `API_BASE`; prefer16`$API_BASE` when set, otherwise the URL above). Use only this remote API.1718Endpoint call order (GET, all take `client_id` except where noted):191. `GET /api/health` -> sanity check `{"ok": true}`.202. `GET /api/clients/<client_id>` -> base record: `age`, `marital_status`,21 `filing_status`, `planning_year`, `estate_value`, `liquid_assets`.223. `GET /api/source-documents?client_id=<id>` -> the CONFLICTING docs (each has23 `source_type` in {`CRM_NOTE`, `ATTORNEY_MEMO`, `SIGNED_PROFILE`,24 `STALE_MARKETING_INTAKE`} plus `effective_date` and a `facts` map).254. `GET /api/retirement-accounts?client_id=<id>` -> retirement account(s), each tagged26 `source_type: CUSTODIAN_EXPORT`: `traditional_balance`, `roth_balance`,27 `expected_return`, `rmd_start_age`, `recommended_conversion_years`.285. `GET /api/life-insurance?client_id=<id>` -> proposed ILIT policy: `death_benefit`,29 `annual_premium`, `planned_contribution_date`, `is_existing_policy_transfer`.306. `GET /api/trust-candidates?client_id=<id>` -> one trust case: `asset_value`,31 `expected_growth_rate`, `grat_term_years`, `grat_annuity_rate`, `crat_term_years`,32 `crat_payout_rate`.337. `GET /api/policies/tax` -> constants (see §3).348. `GET /api/rmd-factors` -> `{age: divisor}` (73->26.5, 74->25.5, ... 99->6.8).3536Only fetch endpoints the analysis type needs, but always fetch `clients`,37`source-documents`, and the relevant detail endpoint(s).3839## 2. Reading the engagement & planning horizon4041- `analysis_type`: determined by the memo engagement line. Four types:42 `roth_conversion_rmd`, `ilit_crummey_implementation`, `trust_comparison`,43 `estate_liquidity_action_plan`.44- `planning_year`: take from the client base record / signed profile (e.g. 2026). It is45 the base year for conversions, estate context, and contribution dates.46- RMD horizon (roth only): parse the request memo for the literal47 `Planning horizon year: YYYY` (or a `... through YYYY` phrase). That year is48 `rmd_projection.horizon_year` and the RMD simulation runs through it inclusive. The49 other three analysis types have no horizon concept.50- `task_id`, `client_id`: copy from the prompt/template verbatim.5152## 3. Tax / policy constants (`/api/policies/tax`)5354- `annual_gift_exclusion` keyed by year; use the entry for `planning_year`55 (per-beneficiary Crummey exclusion).56- `estate_tax_exemption` keyed by year; use the entry for `planning_year`.57- `estate_tax_rate` (e.g. 0.40).58- `conversion_bracket_targets` keyed by `filing_status` (MFJ/SINGLE/HOH) -> the59 taxable-income ceiling to fill with conversions each year.60- `max_crat_term_years` (e.g. 20) and `charitable_deduction_rate` (e.g. 0.35).6162## 4. Source-resolution precedence6364Conflict resolution is by FACT-FAMILY AUTHORITY, not a single global ranking. For each65family, the AUTHORITY source type controls both the value used and the reported66`source_resolution` field; if the authority is absent for a client, fall back by the67precedence chain `SIGNED_PROFILE > ATTORNEY_MEMO > CUSTODIAN_EXPORT > CRM_NOTE >68STALE_MARKETING_INTAKE` (STALE_MARKETING_INTAKE is always lowest; treat it as69unreliable).7071Authority by family:72- Personal profile, beneficiary count, planning goals (`family_transfer_priority`,73 `philanthropic_intent`), `annual_non_ira_income`, `marginal_tax_rate`, `age`,74 `planning_year`, filing/marital status, `liquid_assets` -> **SIGNED_PROFILE**75 (fall back to ATTORNEY_MEMO, then CRM_NOTE).76- Retirement-account balances, `expected_return`, `rmd_start_age`,77 `recommended_conversion_years` -> **CUSTODIAN_EXPORT** (these come from78 `/api/retirement-accounts`, whose records are tagged CUSTODIAN_EXPORT).79- Estate / trust-asset valuation (`estate_value`, trust `asset_value`,80 GRAT/CRAT assumptions) -> **ATTORNEY_MEMO** (the attorney memo is the estate/trust81 valuation authority; fall back to SIGNED_PROFILE, then CRM_NOTE).82- Insurance policy terms (`death_benefit`, `annual_premium`,83 `planned_contribution_date`, `is_existing_policy_transfer`) -> **SIGNED_PROFILE**84 (the policy is authorized under the signed engagement; values are read from85 `/api/life-insurance`).8687Reported `source_resolution` (fields depend on analysis type):88- roth: `controlling_profile_source` = SIGNED_PROFILE,89 `controlling_account_source` = CUSTODIAN_EXPORT.90- ilit: `controlling_beneficiary_source` = SIGNED_PROFILE,91 `controlling_policy_source` = SIGNED_PROFILE.92- trust_comparison: `controlling_goal_source` = SIGNED_PROFILE,93 `controlling_asset_source` = ATTORNEY_MEMO.94- estate_liquidity: `controlling_goal_source` = SIGNED_PROFILE,95 `controlling_policy_source` = SIGNED_PROFILE.9697## 5. Common calculation conventions9899- Round every USD output to cents (round half up) as the LAST step; for derived100 products (e.g. estate-tax-reduction = remainder * rate), compute from the UNROUNDED101 intermediate, then round.102- Growth: compound annually as `(1+rate)**years` (use full float precision, do not103 round intermediate powers).104- Dates: ISO `YYYY-MM-DD`; use true calendar-day arithmetic (Python `date + timedelta`).105- `marginal_tax_rate` (SIGNED_PROFILE) is the tax rate applied to conversions AND to106 each year's RMD amount.107108## 6. Analysis A — `roth_conversion_rmd`109110Inputs: `/retirement-accounts` (T0=`traditional_balance`, R0=`roth_balance`, `r`=111`expected_return`, `rmd_start_age`, `conv_years`=`recommended_conversion_years`),112SIGNED_PROFILE (`mtr`, `inc`=`annual_non_ira_income`, `age0`=`age`,113`filing_status`), `/policies/tax` bracket target, `/rmd-factors`, memo horizon.114115Conversion plan:116- `first_conversion_year` = `planning_year`.117- `conversion_years` = `conv_years` (CUSTODIAN_EXPORT).118- `annual_conversion_amount` = `max(0, bracket_target[filing_status] - inc)`119 (flat each year; income does NOT grow).120- `conversion_years_positive` = count of conversion years with a positive amount121 (capped by remaining balance; equals `conversion_years` when bracket > income and122 balance suffices).123- `total_converted` = `annual_conversion_amount * conversion_years_positive`.124- `total_conversion_tax` = `total_converted * mtr`.125- `first_rmd_year` = `planning_year + (rmd_start_age - age0)`126 (age0 is the client's age IN the planning year).127128Year-by-year RMD projection (loop `y` from `planning_year` through `horizon`129INCLUSIVE; `age = age0 + (y - planning_year)`):130131BASELINE (no conversion): `trad = T0`; `base_tax = 0`.132```133for each y:134 if y >= first_rmd_year:135 rmd_amt = trad / rmd_factor[age]136 base_tax += rmd_amt * mtr137 trad = (trad - rmd_amt) * (1 + r)138 else:139 trad = trad * (1 + r)140```141CONVERSION scenario: `trad = T0`; `roth = R0`; `conv_tax = 0`; conversion years =142`{planning_year .. planning_year + conv_years - 1}`.143```144for each y:145 conv = annual_conversion_amount if y in conversion_years else 0146 trad = trad - conv # conversion taken first, at start of year147 roth = roth + conv # into Roth before this year's growth148 if y >= first_rmd_year:149 rmd_amt = trad / rmd_factor[age] # RMD base is AFTER conversion150 conv_tax += rmd_amt * mtr151 trad = (trad - rmd_amt) * (1 + r)152 else:153 trad = trad * (1 + r)154 roth = roth * (1 + r) # Roth (incl. this year's conversion) grows155```156- `baseline_rmd_tax_through_horizon` = `base_tax`.157- `conversion_rmd_tax_through_horizon` = `conv_tax`.158- `rmd_tax_savings_through_horizon` = `base_tax - conv_tax`.159- `projected_roth_balance_horizon` = `roth` at end of horizon (CONVERSION scenario).160- `projected_traditional_balance_horizon` = `trad` at end of horizon (CONVERSION161 scenario).162- `horizon_year` = memo horizon.163164Recommendation:165- `primary_action` = `STAGED_ROTH_CONVERSION` if `annual_conversion_amount > 0` and166 `conv_years > 0`, else `NO_CONVERSION` (or `DEFER`).167- `suitability` = `SUITABLE` when positive conversion, else `DEFER`/`BORDERLINE`.168- `risk_flag`: `TAX_BRACKET_MANAGEMENT` (default). Use `LIQUIDITY_CONSTRAINT` if169 `liquid_assets < total_conversion_tax`; use `RMD_NEAR_TERM` if170 `first_rmd_year <= planning_year` (already at/ past RMD age).171- `heir_tax_profile`: `MIXED_TAXABLE_AND_TAX_FREE` if both projected balances > 0;172 `MOSTLY_TAX_FREE` if traditional balance <= 0 and roth > 0; `MOSTLY_TAXABLE` if173 roth balance <= 0.174175## 7. Analysis B — `ilit_crummey_implementation`176177Inputs: `/life-insurance` policy, SIGNED_PROFILE `beneficiary_count`,178`/policies/tax` `annual_gift_exclusion[planning_year]`, `estate_tax_rate`.179180gift_plan:181- `planning_year` = client planning year.182- `annual_exclusion_per_beneficiary` = `annual_gift_exclusion[planning_year]`.183- `beneficiary_count` = from SIGNED_PROFILE.184- `annual_exclusion_capacity` = `annual_exclusion_per_beneficiary * beneficiary_count`.185- `annual_premium` = policy `annual_premium`.186- `premium_gap` = `max(0, annual_premium - annual_exclusion_capacity)`.187188administration (calendar-day arithmetic):189- `notices_required` = `beneficiary_count` (one Crummey notice per beneficiary).190- `contribution_date` = policy `planned_contribution_date`.191- `notice_due_date` = `contribution_date + 7 days`.192- `withdrawal_window_end` = `notice_due_date + 30 days`.193- `earliest_premium_payment_date` = `withdrawal_window_end + 1 day`.194- `dedicated_bank_account_required` = `true` (always for an ILIT Crummey cycle).195196estate_result:197- `death_benefit` = policy `death_benefit`.198- `estate_inclusion_risk` = the risk_flag (see below).199- `projected_outside_estate_if_implemented` = `death_benefit` (the ILIT removes the200 full death benefit from the gross estate when formalities are met).201- `tax_liquidity_support` = `death_benefit * estate_tax_rate`.202203risk_flag / primary_action decision tree (from `is_existing_policy_transfer` +204`premium_gap`):205- not existing AND gap 0 -> `LOW_IF_FORMALITIES_MET`, action206 `FUND_WITH_CRUMMEY_NOTICES`, suitability `SUITABLE_WITH_ADMINISTRATION`.207- not existing AND gap > 0 -> `EXCLUSION_SHORTFALL`, action208 `USE_LIFETIME_EXEMPTION_FOR_SHORTFALL`.209- existing AND gap 0 -> `THREE_YEAR_LOOKBACK`, action210 `USE_NEW_POLICY_OR_ACCEPT_LOOKBACK`.211- existing AND gap > 0 -> `THREE_YEAR_LOOKBACK_AND_EXCLUSION_SHORTFALL`, action212 `DISCLOSE_LOOKBACK_AND_USE_EXEMPTION`.213214## 8. Analysis C — `trust_comparison`215216Inputs: `/trust-candidates` (`asset_value` V, `expected_growth_rate` g,217`grat_term_years`, `grat_annuity_rate`, `crat_term_years`, `crat_payout_rate`),218SIGNED_PROFILE goals (`family_transfer_priority`, `philanthropic_intent`) + estate219facts, `estate_tax_rate`, `charitable_deduction_rate`, `max_crat_term_years`.220221estate_context:222- `planning_year` = client planning year.223- `exemption_used` = `(2 if married/MFJ else 1) * estate_tax_exemption[planning_year]`.224- `taxable_estate` = `estate_value - exemption_used` (estate_value from ATTORNEY_MEMO225 authority, fallback SIGNED_PROFILE).226- `estate_tax_exposure` = `taxable_estate * estate_tax_rate`.227- `liquid_assets_available` = `liquid_assets` (SIGNED_PROFILE).228- `liquidity_gap_before_planning` = `max(0, estate_tax_exposure - liquid_assets_available)`.229230Formulas (compute from UNROUNDED values, round outputs to cents):231- GRAT `projected_remainder_to_heirs` =232 `V*(1+g)**grat_term_years - grat_annuity_rate*V*grat_term_years`.233- GRAT `estimated_estate_tax_reduction` =234 `round(projected_remainder_to_heirs_unrounded * estate_tax_rate, 2)`.235- GRAT `term_years` = `grat_term_years`; `mortality_inclusion_risk` =236 `TERM_SURVIVAL_REQUIRED`.237- CRAT `projected_charitable_remainder` =238 `V*(1+g)**crat_term_years - crat_payout_rate*V*crat_term_years`.239- CRAT `estimated_income_tax_deduction` =240 `round(projected_charitable_remainder_unrounded * charitable_deduction_rate, 2)`.241- CRAT `term_years` = `crat_term_years` (== `max_crat_term_years`).242243Recommendation:244- `preferred_strategy` = `GRAT` if `family_transfer_priority` == high; `CRAT` if245 `philanthropic_intent` == high.246- `rationale_code` = `CHILDREN_TRANSFER_PRIORITY` (GRAT) or `PHILANTHROPIC_PRIORITY`247 (CRAT).248- `alternate_role` = `SECONDARY_CHARITABLE_TOOL` (if GRAT preferred) or249 `SECONDARY_FAMILY_TRANSFER_TOOL` (if CRAT preferred).250- CRAT `family_transfer_fit` = `LOW` when GRAT is preferred; else `MODERATE`/`HIGH`.251252## 9. Analysis D — `estate_liquidity_action_plan`253254Combines an estate_context, a condensed ILIT block, a trust_transfer block, and an255action_set. Inputs: same as B + C for the same client.256257estate_context: same formulas as §8.258259ilit (condensed):260- `annual_exclusion_capacity` = `annual_gift_exclusion[planning_year] * beneficiary_count`.261- `premium_gap` = `max(0, annual_premium - annual_exclusion_capacity)`.262- `estate_inclusion_risk` = risk_flag from the same decision tree as §7.263- `projected_outside_estate_if_implemented` = `death_benefit`.264265trust_transfer:266- `preferred_strategy` = GRAT if `family_transfer_priority` == high else CRAT.267- `projected_remainder_to_heirs` = GRAT remainder formula (§8).268- `estimated_estate_tax_reduction` = `round(remainder_unrounded * estate_tax_rate, 2)`.269- `projected_charitable_remainder` = CRAT remainder formula (§8).270271recommendation:272- `primary_action` = `COMBINE_ILIT_AND_GRAT` when ILIT is fundable (gap 0) and GRAT is273 preferred; `CRAT_WITH_LIQUIDITY_REVIEW` when CRAT preferred with a liquidity gap;274 `ILIT_WITH_EXEMPTION_REVIEW` when ILIT has a premium_gap and no trust driver.275- `sequencing` = `ILIT_FIRST_THEN_GRAT` when combining ILIT + GRAT;276 `TRUST_DECISION_FIRST` when CRAT; `ILIT_FIRST_THEN_ATTORNEY_REVIEW` when ILIT-only.277- `risk_flag` = the ILIT risk_flag (decision tree §7).278279action_set: choose from the enum set {`ATTORNEY_DRAFT_REVIEW`,280`CRAT_FOR_CHARITABLE_REMAINDER`, `GRAT_FOR_APPRECIATING_SHARES`,281`ILIT_CRUMMEY_NOTICE_CYCLE`, `LIFETIME_EXEMPTION_ALLOCATION`} and SORT ALPHABETICALLY.282Selection:283- Always include `ATTORNEY_DRAFT_REVIEW` (combined plan needs attorney drafting).284- Include `ILIT_CRUMMEY_NOTICE_CYCLE` when an ILIT is being funded.285- Include `GRAT_FOR_APPRECIATING_SHARES` when `preferred_strategy` == GRAT.286- Include `CRAT_FOR_CHARITABLE_REMAINDER` when `preferred_strategy` == CRAT.287- Include `LIFETIME_EXEMPTION_ALLOCATION` when `premium_gap > 0`.288289## 10. Common mistakes to avoid290291- Wrong source winning: do NOT let CRM_NOTE or STALE_MARKETING_INTAKE override292 SIGNED_PROFILE for profile/goal facts, and do NOT let SIGNED_PROFILE override293 ATTORNEY_MEMO for estate/trust-asset valuation. Use the family-authority rule in §4.294- Wrong horizon year: read the horizon ONLY from the memo's `Planning horizon year:295 YYYY` line (roth tasks). Do not infer it from age or RMD start.296- Off-by-one conversion years: conversions run `planning_year ..297 planning_year + recommended_conversion_years - 1`; RMDs run `first_rmd_year ..298 horizon` INCLUSIVE. They may overlap (near-RMD client) — that is correct.299- RMD base wrong: in a year that is BOTH a conversion year and an RMD year, the RMD is300 computed AFTER subtracting the conversion (`trad = trad - conv` first).301- RMD factor lookup: use the client's age IN that year (`age0 + (y - planning_year)`),302 keyed on `/rmd-factors` (73->26.5, etc.). Do not recompute the table.303- Roth/ traditional balances at horizon: taken from the CONVERSION scenario, after304 conversions AND RMDs both deplete the balance; baseline is only used for the305 `baseline_rmd_tax` number.306- GRAT/CRAT rounding: `estimated_estate_tax_reduction` and307 `estimated_income_tax_deduction` MUST be computed from the UNROUNDED remainder, then308 rounded — rounding the remainder first loses a cent.309- Non-ISO dates: all `administration` dates must be `YYYY-MM-DD` strings produced by310 calendar-day arithmetic (`+7`, `+30`, `+1`), not month-string math.311- action_set not sorted: emit the estate-liquidity `action_set` in strict alphabetical312 order.313- Numbers as strings: every USD figure must be a JSON number (e.g. `80000.0`), never a314 quoted string.315- Exemption multiplier: married/MFJ uses `2 *` the per-person exemption; single uses316 `1 *`. Wrong multiplier cascades into taxable_estate, exposure, and liquidity_gap.