Private Wealth Advisory Structured Output Skill
SOP for producing the JSON planning object for an unseen advisory client.
Base API URL is in the staged environment_access.md (hereafter API_BASE).
All amounts are USD rounded to cents (2 decimals). All dates are ISO YYYY-MM-DD.
Return ONLY the JSON object; no prose.
0. Determine analysis type from the engagement text
Map the memo "Engagement:" line (or prompt title) to analysis_type:
- contains "Roth conversion" / "RMD" / "near-RMD Roth" ->
roth_conversion_rmd
- contains "ILIT" / "Crummey" ->
ilit_crummey_implementation
- contains "GRAT" and "CRAT" / "GRAT versus CRAT" ->
trust_comparison
- contains "estate liquidity action plan" ->
estate_liquidity_action_plan
Set task_id = "test_NNN" where NNN is the zero-padded task folder number.
Set client_id = the CLT-XXXX from the memo.
1. Remote API workflow (endpoint order)
GET {API_BASE}/api/health — confirm {"ok": true}.
GET {API_BASE}/api/policies/tax — cache the planning constants (gift exclusion by year, estate exemption by year, estate_tax_rate, conversion_bracket_targets by filing status, max_crat_term_years, charitable_deduction_rate).
GET {API_BASE}/api/rmd-factors — cache the age->divisor map (keys 73..99).
GET {API_BASE}/api/clients/{client_id} — base record (age, filing_status, planning_year, estate_value, liquid_assets).
GET {API_BASE}/api/source-documents?client_id={client_id} — the CONFLICTING sources (CRM_NOTE, ATTORNEY_MEMO, SIGNED_PROFILE, possibly CUSTODIAN_EXPORT / STALE_MARKETING_INTAKE). Each carries source_type, effective_date, facts{}.
- Optionally fetch the data endpoints the analysis needs:
- roth:
GET /api/retirement-accounts?client_id=
- ilit / estate-liquidity:
GET /api/life-insurance?client_id=
- trust-comparison / estate-liquidity:
GET /api/trust-candidates?client_id=
- Read the local
request_memo.md for the planning horizon year and engagement framing.
- Read
answer_template.json for the required keys / enums for THIS task.
2. Source-resolution precedence (infer the controlling source)
Each source has an effective_date and source_type. Resolve every fact from the
conflicting sources using this precedence (highest first):
- SIGNED_PROFILE — most recent, client-signed. Controls PROFILE facts:
annual_non_ira_income, marginal_tax_rate, beneficiary_count, age,
filing_status, marital_status, planning_year, liquid_assets,
estate_value, and GOAL facts (philanthropic_intent, family_transfer_priority).
- ATTORNEY_MEMO — controls ASSET identification for trust transfers (which
appreciating asset funds the GRAT/CRAT). Also confirms
estate_value.
- CUSTODIAN_EXPORT — controls ACCOUNT balances (traditional_balance,
roth_balance, expected_return on retirement accounts). Always wins for
account balances even if a profile states a different number.
- CRM_NOTE — older import; superseded by any of the above.
- STALE_MARKETING_INTAKE — lowest; never controls any scored fact.
Resulting source_resolution output fields by analysis type:
- roth_conversion_rmd:
controlling_profile_source = SIGNED_PROFILE;
controlling_account_source = CUSTODIAN_EXPORT.
- ilit_crummey_implementation:
controlling_beneficiary_source = SIGNED_PROFILE;
controlling_policy_source = SIGNED_PROFILE (the signed plan documents the policy).
- trust_comparison:
controlling_goal_source = SIGNED_PROFILE;
controlling_asset_source = ATTORNEY_MEMO.
- estate_liquidity_action_plan:
controlling_goal_source = SIGNED_PROFILE;
controlling_policy_source = SIGNED_PROFILE.
When SIGNED_PROFILE and ATTORNEY_MEMO agree on a goal fact, emit SIGNED_PROFILE.
Only emit a different enum when the signed profile is genuinely absent for that
fact category.
3. Reading the planning horizon from the memo
- For
roth_conversion_rmd: the memo states Planning horizon year: YYYY.
Use that year for rmd_projection.horizon_year and project balances to that year.
- For the other three analysis types there is NO horizon field; the trust term is
read from
trust-candidates (grat_term_years / crat_term_years) and the ILIT
dates from life-insurance.planned_contribution_date.
4. Type A — roth_conversion_rmd (tasks 1, 5 template)
Fetch retirement-accounts; you get traditional_balance, roth_balance,
expected_return, rmd_start_age (=73), recommended_conversion_years.
From SIGNED_PROFILE read annual_non_ira_income, marginal_tax_rate,
filing_status, age, planning_year. From tax policy read
conversion_bracket_targets[filing_status].
4a. conversion_plan
first_conversion_year = planning_year.
conversion_years = recommended_conversion_years.
first_rmd_year = planning_year + (rmd_start_age - age) (year owner turns 73).
annual_conversion_amount = conversion_bracket_targets[filing_status] MINUS
annual_non_ira_income. (Fill the bracket: non-IRA income fills first, conversion
fills the rest to the bracket ceiling.) Round to cents.
total_converted = annual_conversion_amount * conversion_years (cents).
total_conversion_tax = total_converted * marginal_tax_rate (cents).
conversion_years_positive = number of conversion years that fall STRICTLY BEFORE
first_rmd_year = max(0, min(conversion_years, first_rmd_year - first_conversion_year)).
4b. rmd_projection
horizon_year = memo planning-horizon year.
first_rmd_year = as above.
Baseline model (no conversion):
B = traditional_balance
for Y in [planning_year .. horizon_year]:
ageY = age + (Y - planning_year)
rmd = B / rmd_factors[ageY] if Y >= first_rmd_year else 0
tax = rmd * marginal_tax_rate if Y >= first_rmd_year else 0
B = (B - rmd) * (1 + expected_return) # withdraw RMD, then grow remainder
baseline_rmd_tax_through_horizon = sum(tax over Y)
Conversion model (with conversion):
Bc = traditional_balance
Rc = roth_balance
for Y in [planning_year .. horizon_year]:
ageY = age + (Y - planning_year)
rmd = Bc / rmd_factors[ageY] if Y >= first_rmd_year else 0
conv = annual_conversion_amount if Y is a conversion year else 0
tax = rmd * marginal_tax_rate if Y >= first_rmd_year else 0
Bc = (Bc - rmd - conv) * (1 + expected_return)
Rc = (Rc + conv) * (1 + expected_return)
conversion_rmd_tax_through_horizon = sum(tax over Y)
rmd_tax_savings_through_horizon = baseline_rmd_tax_through_horizon MINUS
conversion_rmd_tax_through_horizon (positive = savings; cents).
4c. legacy_projection
projected_traditional_balance_horizon = Bc at horizon (conversion model).
projected_roth_balance_horizon = Rc at horizon (conversion model).
heir_tax_profile: ratio = Rc / (Rc + Bc).
- ratio >= 0.60 ->
MOSTLY_TAX_FREE
- ratio <= 0.30 ->
MOSTLY_TAXABLE
- else ->
MIXED_TAXABLE_AND_TAX_FREE
4d. recommendation
- If
conversion_years_positive <= 1 (RMD imminent): primary_action=DEFER,
suitability=DEFER, risk_flag=RMD_NEAR_TERM.
- Else if
liquid_assets < total_conversion_tax: risk_flag=LIQUIDITY_CONSTRAINT,
suitability=BORDERLINE, primary_action=DEFER.
- Else:
primary_action=STAGED_ROTH_CONVERSION, suitability=SUITABLE,
risk_flag=TAX_BRACKET_MANAGEMENT.
5. Type B — ilit_crummey_implementation (task 2 template)
Fetch life-insurance; you get death_benefit, annual_premium,
planned_contribution_date, is_existing_policy_transfer. From SIGNED_PROFILE
read beneficiary_count; from the client record read planning_year.
5a. gift_plan
planning_year = client planning_year (2026 for train data).
annual_exclusion_per_beneficiary = annual_gift_exclusion[planning_year].
beneficiary_count = from SIGNED_PROFILE (resolves CRM conflict).
annual_exclusion_capacity = beneficiary_count * annual_exclusion_per_beneficiary.
annual_premium = from life-insurance record.
premium_gap = max(0, annual_premium - annual_exclusion_capacity) (cents).
5b. administration
notices_required = beneficiary_count.
contribution_date = planned_contribution_date (ISO).
notice_due_date = contribution_date (Crummey notice served on contribution).
withdrawal_window_end = contribution_date + 30 days (ISO).
earliest_premium_payment_date = withdrawal_window_end + 1 day (trustee pays
premium only after the 30-day withdrawal right closes).
dedicated_bank_account_required = true.
Date arithmetic: add 30 calendar days to the ISO date; + 1 day after.
E.g. 2026-03-10 -> window end 2026-04-09 -> earliest premium 2026-04-10.
5c. estate_result
death_benefit = from life-insurance record.
estate_inclusion_risk = same value as recommendation.risk_flag (see 5d).
projected_outside_estate_if_implemented = death_benefit (ILIT-owned policy
passes outside the taxable estate when formalities are met).
tax_liquidity_support = death_benefit (liquidity the ILIT supplies).
5d. recommendation (matrix on two axes)
Axes: is_existing_policy_transfer (T/F) and premium_gap > 0 (T/F).
- F / F:
FUND_WITH_CRUMMEY_NOTICES, SUITABLE_WITH_ADMINISTRATION, LOW_IF_FORMALITIES_MET.
- F / T:
USE_LIFETIME_EXEMPTION_FOR_SHORTFALL, BORDERLINE, EXCLUSION_SHORTFALL.
- T / F:
USE_NEW_POLICY_OR_ACCEPT_LOOKBACK, BORDERLINE, THREE_YEAR_LOOKBACK.
- T / T:
DISCLOSE_LOOKBACK_AND_USE_EXEMPTION, NOT_SUITABLE, THREE_YEAR_LOOKBACK_AND_EXCLUSION_SHORTFALL.
estate_result.estate_inclusion_risk takes the same risk_flag value.
6. Type C — trust_comparison (task 3 template)
Fetch trust-candidates; you get asset_value, expected_growth_rate,
grat_term_years, grat_annuity_rate, crat_term_years, crat_payout_rate.
From SIGNED_PROFILE read estate_value, liquid_assets, filing_status,
planning_year, and goals (family_transfer_priority, philanthropic_intent).
6a. estate_context (shared formula for types C and D)
taxable_estate = estate_value from SIGNED_PROFILE.
estate_tax_exposure = max(0, taxable_estate - estate_tax_exemption[planning_year]) * estate_tax_rate.
liquidity_gap_before_planning = max(0, estate_tax_exposure - liquid_assets).
6b. grat
term_years = grat_term_years.
- annuity_payment =
asset_value * grat_annuity_rate (fixed each year).
projected_remainder_to_heirs =
asset_value * (1+growth)^term - annuity_payment * (((1+growth)^term - 1) / growth) (cents).
(Grow the asset each year, pay the fixed annuity at year-end; remainder passes to heirs.)
estimated_estate_tax_reduction = projected_remainder_to_heirs * estate_tax_rate (cents).
mortality_inclusion_risk = TERM_SURVIVAL_REQUIRED (always; grantor must survive the term).
6c. crat
term_years = min(crat_term_years, max_crat_term_years).
- annuity_payment =
asset_value * crat_payout_rate (fixed each year).
projected_charitable_remainder =
asset_value * (1+growth)^term - annuity_payment * (((1+growth)^term - 1) / growth) (cents).
estimated_income_tax_deduction = asset_value * charitable_deduction_rate (cents).
family_transfer_fit based on family_transfer_priority:
high -> LOW; moderate -> MODERATE; low -> HIGH.
6d. recommendation
preferred_strategy: GRAT if family_transfer_priority == "high" and
philanthropic_intent != "high"; else CRAT if philanthropic_intent == "high";
else default GRAT.
rationale_code: GRAT -> CHILDREN_TRANSFER_PRIORITY; CRAT -> PHILANTHROPIC_PRIORITY.
alternate_role: GRAT preferred -> SECONDARY_CHARITABLE_TOOL;
CRAT preferred -> SECONDARY_FAMILY_TRANSFER_TOOL.
7. Type D — estate_liquidity_action_plan (task 4 template)
Combines estate_context + ILIT + trust transfer. Fetch life-insurance AND
trust-candidates. Use SIGNED_PROFILE for estate/liquid goals, beneficiary_count.
7a. estate_context — same formula as 6a.
7b. ilit
annual_exclusion_capacity = beneficiary_count * annual_gift_exclusion[planning_year].
premium_gap = max(0, annual_premium - annual_exclusion_capacity).
estate_inclusion_risk = risk_flag from the 5d matrix.
projected_outside_estate_if_implemented = death_benefit.
7c. trust_transfer
preferred_strategy: GRAT/CRAT using the same goal rule as 6d.
projected_remainder_to_heirs = GRAT remainder (formula 6b).
estimated_estate_tax_reduction = projected_remainder_to_heirs * estate_tax_rate.
projected_charitable_remainder = CRAT remainder (formula 6c, computed regardless
of which strategy is preferred).
7d. recommendation
primary_action:
- GRAT preferred + ILIT funding (no shortfall) ->
COMBINE_ILIT_AND_GRAT.
- CRAT preferred (philanthropic_intent high) ->
CRAT_WITH_LIQUIDITY_REVIEW.
- ILIT has
premium_gap > 0 -> ILIT_WITH_EXEMPTION_REVIEW.
sequencing:
COMBINE_ILIT_AND_GRAT -> ILIT_FIRST_THEN_GRAT.
CRAT_WITH_LIQUIDITY_REVIEW -> TRUST_DECISION_FIRST.
ILIT_WITH_EXEMPTION_REVIEW -> ILIT_FIRST_THEN_ATTORNEY_REVIEW.
risk_flag = the ILIT risk from the 5d matrix.
7e. action_set (sorted alphabetically, subset of the 5 enum values)
Always include: ATTORNEY_DRAFT_REVIEW.
Include GRAT_FOR_APPRECIATING_SHARES when GRAT is preferred.
Include CRAT_FOR_CHARITABLE_REMAINDER when CRAT is preferred.
Include ILIT_CRUMMEY_NOTICE_CYCLE when the ILIT is being funded.
Include LIFETIME_EXEMPTION_ALLOCATION when taxable_estate > estate_tax_exemption
(always true for these high-net-worth clients) OR when premium_gap > 0.
Sort the resulting list alphabetically and output as a JSON array of strings.
8. Pitfalls
- Do NOT use CRM_NOTE values for income, beneficiary_count, or goals when a
SIGNED_PROFILE exists — the CRM is the stale import that disagrees.
- Do NOT use the signed-profile stated balance for retirement accounts; use the
CUSTODIAN_EXPORT balance from
/api/retirement-accounts.
conversion_years is the RECOMMENDED count from the account record, NOT a value
you derive.
annual_conversion_amount is the bracket fill (target MINUS non-IRA income), not
the balance divided by years.
first_rmd_year = year the owner turns 73 = planning_year + (73 - age).
Patel (age 72) starts RMD the very next year.
- RMD factor for year Y uses the age IN that year (
age + (Y - planning_year)),
not the current age. Guard against age > 99 (no factor) by clamping.
premium_gap is max(0, ...), never negative.
- GRAT/CRAT annuity payment =
asset_value * rate EACH YEAR (fixed), and the
remainder formula grows the starting asset and subtracts the FV of the annuity
stream at expected_growth_rate.
estimated_income_tax_deduction for CRAT = asset_value * charitable_deduction_rate
(a flat fraction of the contribution), NOT the future charitable remainder.
- All numeric outputs must be JSON numbers (not strings) rounded to 2 decimals.
- All date outputs must be ISO
YYYY-MM-DD strings.
action_set MUST be sorted alphabetically.
- Use the
estate_tax_exemption and annual_gift_exclusion for the client's
planning_year (2026 -> exemption 13,610,000; gift exclusion 20,000), not 2025.
task_id must be the literal test_NNN for the current test task, not a train id.
1---2name: self-attempt-03-123description: Private Wealth Advisory Structured Output Skill4---5# Private Wealth Advisory Structured Output Skill67SOP for producing the JSON planning object for an unseen advisory client.8Base API URL is in the staged `environment_access.md` (hereafter `API_BASE`).9All amounts are USD rounded to cents (2 decimals). All dates are ISO `YYYY-MM-DD`.10Return ONLY the JSON object; no prose.1112## 0. Determine analysis type from the engagement text13Map the memo "Engagement:" line (or prompt title) to `analysis_type`:14- contains "Roth conversion" / "RMD" / "near-RMD Roth" -> `roth_conversion_rmd`15- contains "ILIT" / "Crummey" -> `ilit_crummey_implementation`16- contains "GRAT" and "CRAT" / "GRAT versus CRAT" -> `trust_comparison`17- contains "estate liquidity action plan" -> `estate_liquidity_action_plan`18Set `task_id` = `"test_NNN"` where NNN is the zero-padded task folder number.19Set `client_id` = the `CLT-XXXX` from the memo.2021## 1. Remote API workflow (endpoint order)221. `GET {API_BASE}/api/health` — confirm `{"ok": true}`.232. `GET {API_BASE}/api/policies/tax` — cache the planning constants (gift exclusion by year, estate exemption by year, estate_tax_rate, conversion_bracket_targets by filing status, max_crat_term_years, charitable_deduction_rate).243. `GET {API_BASE}/api/rmd-factors` — cache the age->divisor map (keys 73..99).254. `GET {API_BASE}/api/clients/{client_id}` — base record (age, filing_status, planning_year, estate_value, liquid_assets).265. `GET {API_BASE}/api/source-documents?client_id={client_id}` — the CONFLICTING sources (CRM_NOTE, ATTORNEY_MEMO, SIGNED_PROFILE, possibly CUSTODIAN_EXPORT / STALE_MARKETING_INTAKE). Each carries `source_type`, `effective_date`, `facts{}`.276. Optionally fetch the data endpoints the analysis needs:28 - roth: `GET /api/retirement-accounts?client_id=`29 - ilit / estate-liquidity: `GET /api/life-insurance?client_id=`30 - trust-comparison / estate-liquidity: `GET /api/trust-candidates?client_id=`317. Read the local `request_memo.md` for the planning horizon year and engagement framing.328. Read `answer_template.json` for the required keys / enums for THIS task.3334## 2. Source-resolution precedence (infer the controlling source)35Each source has an `effective_date` and `source_type`. Resolve every fact from the36conflicting sources using this precedence (highest first):37381. **SIGNED_PROFILE** — most recent, client-signed. Controls PROFILE facts:39 `annual_non_ira_income`, `marginal_tax_rate`, `beneficiary_count`, `age`,40 `filing_status`, `marital_status`, `planning_year`, `liquid_assets`,41 `estate_value`, and GOAL facts (`philanthropic_intent`, `family_transfer_priority`).422. **ATTORNEY_MEMO** — controls ASSET identification for trust transfers (which43 appreciating asset funds the GRAT/CRAT). Also confirms `estate_value`.443. **CUSTODIAN_EXPORT** — controls ACCOUNT balances (traditional_balance,45 roth_balance, expected_return on retirement accounts). Always wins for46 account balances even if a profile states a different number.474. **CRM_NOTE** — older import; superseded by any of the above.485. **STALE_MARKETING_INTAKE** — lowest; never controls any scored fact.4950Resulting `source_resolution` output fields by analysis type:51- roth_conversion_rmd: `controlling_profile_source` = SIGNED_PROFILE;52 `controlling_account_source` = CUSTODIAN_EXPORT.53- ilit_crummey_implementation: `controlling_beneficiary_source` = SIGNED_PROFILE;54 `controlling_policy_source` = SIGNED_PROFILE (the signed plan documents the policy).55- trust_comparison: `controlling_goal_source` = SIGNED_PROFILE;56 `controlling_asset_source` = ATTORNEY_MEMO.57- estate_liquidity_action_plan: `controlling_goal_source` = SIGNED_PROFILE;58 `controlling_policy_source` = SIGNED_PROFILE.5960When SIGNED_PROFILE and ATTORNEY_MEMO agree on a goal fact, emit SIGNED_PROFILE.61Only emit a different enum when the signed profile is genuinely absent for that62fact category.6364## 3. Reading the planning horizon from the memo65- For `roth_conversion_rmd`: the memo states `Planning horizon year: YYYY`.66 Use that year for `rmd_projection.horizon_year` and project balances to that year.67- For the other three analysis types there is NO horizon field; the trust term is68 read from `trust-candidates` (grat_term_years / crat_term_years) and the ILIT69 dates from `life-insurance.planned_contribution_date`.7071## 4. Type A — `roth_conversion_rmd` (tasks 1, 5 template)7273Fetch retirement-accounts; you get `traditional_balance`, `roth_balance`,74`expected_return`, `rmd_start_age` (=73), `recommended_conversion_years`.75From SIGNED_PROFILE read `annual_non_ira_income`, `marginal_tax_rate`,76`filing_status`, `age`, `planning_year`. From tax policy read77`conversion_bracket_targets[filing_status]`.7879### 4a. conversion_plan80- `first_conversion_year` = `planning_year`.81- `conversion_years` = `recommended_conversion_years`.82- `first_rmd_year` = `planning_year + (rmd_start_age - age)` (year owner turns 73).83- `annual_conversion_amount` = `conversion_bracket_targets[filing_status]` MINUS84 `annual_non_ira_income`. (Fill the bracket: non-IRA income fills first, conversion85 fills the rest to the bracket ceiling.) Round to cents.86- `total_converted` = `annual_conversion_amount * conversion_years` (cents).87- `total_conversion_tax` = `total_converted * marginal_tax_rate` (cents).88- `conversion_years_positive` = number of conversion years that fall STRICTLY BEFORE89 `first_rmd_year` = `max(0, min(conversion_years, first_rmd_year - first_conversion_year))`.9091### 4b. rmd_projection92- `horizon_year` = memo planning-horizon year.93- `first_rmd_year` = as above.9495Baseline model (no conversion):96```97B = traditional_balance98for Y in [planning_year .. horizon_year]:99 ageY = age + (Y - planning_year)100 rmd = B / rmd_factors[ageY] if Y >= first_rmd_year else 0101 tax = rmd * marginal_tax_rate if Y >= first_rmd_year else 0102 B = (B - rmd) * (1 + expected_return) # withdraw RMD, then grow remainder103baseline_rmd_tax_through_horizon = sum(tax over Y)104```105106Conversion model (with conversion):107```108Bc = traditional_balance109Rc = roth_balance110for Y in [planning_year .. horizon_year]:111 ageY = age + (Y - planning_year)112 rmd = Bc / rmd_factors[ageY] if Y >= first_rmd_year else 0113 conv = annual_conversion_amount if Y is a conversion year else 0114 tax = rmd * marginal_tax_rate if Y >= first_rmd_year else 0115 Bc = (Bc - rmd - conv) * (1 + expected_return)116 Rc = (Rc + conv) * (1 + expected_return)117conversion_rmd_tax_through_horizon = sum(tax over Y)118```119- `rmd_tax_savings_through_horizon` = `baseline_rmd_tax_through_horizon` MINUS120 `conversion_rmd_tax_through_horizon` (positive = savings; cents).121122### 4c. legacy_projection123- `projected_traditional_balance_horizon` = `Bc` at horizon (conversion model).124- `projected_roth_balance_horizon` = `Rc` at horizon (conversion model).125- `heir_tax_profile`: ratio = `Rc / (Rc + Bc)`.126 - ratio >= 0.60 -> `MOSTLY_TAX_FREE`127 - ratio <= 0.30 -> `MOSTLY_TAXABLE`128 - else -> `MIXED_TAXABLE_AND_TAX_FREE`129130### 4d. recommendation131- If `conversion_years_positive` <= 1 (RMD imminent): `primary_action`=`DEFER`,132 `suitability`=`DEFER`, `risk_flag`=`RMD_NEAR_TERM`.133- Else if `liquid_assets < total_conversion_tax`: `risk_flag`=`LIQUIDITY_CONSTRAINT`,134 `suitability`=`BORDERLINE`, `primary_action`=`DEFER`.135- Else: `primary_action`=`STAGED_ROTH_CONVERSION`, `suitability`=`SUITABLE`,136 `risk_flag`=`TAX_BRACKET_MANAGEMENT`.137138## 5. Type B — `ilit_crummey_implementation` (task 2 template)139140Fetch life-insurance; you get `death_benefit`, `annual_premium`,141`planned_contribution_date`, `is_existing_policy_transfer`. From SIGNED_PROFILE142read `beneficiary_count`; from the client record read `planning_year`.143144### 5a. gift_plan145- `planning_year` = client planning_year (2026 for train data).146- `annual_exclusion_per_beneficiary` = `annual_gift_exclusion[planning_year]`.147- `beneficiary_count` = from SIGNED_PROFILE (resolves CRM conflict).148- `annual_exclusion_capacity` = `beneficiary_count * annual_exclusion_per_beneficiary`.149- `annual_premium` = from life-insurance record.150- `premium_gap` = `max(0, annual_premium - annual_exclusion_capacity)` (cents).151152### 5b. administration153- `notices_required` = `beneficiary_count`.154- `contribution_date` = `planned_contribution_date` (ISO).155- `notice_due_date` = `contribution_date` (Crummey notice served on contribution).156- `withdrawal_window_end` = `contribution_date + 30 days` (ISO).157- `earliest_premium_payment_date` = `withdrawal_window_end + 1 day` (trustee pays158 premium only after the 30-day withdrawal right closes).159- `dedicated_bank_account_required` = `true`.160161Date arithmetic: add 30 calendar days to the ISO date; `+ 1 day` after.162E.g. 2026-03-10 -> window end 2026-04-09 -> earliest premium 2026-04-10.163164### 5c. estate_result165- `death_benefit` = from life-insurance record.166- `estate_inclusion_risk` = same value as `recommendation.risk_flag` (see 5d).167- `projected_outside_estate_if_implemented` = `death_benefit` (ILIT-owned policy168 passes outside the taxable estate when formalities are met).169- `tax_liquidity_support` = `death_benefit` (liquidity the ILIT supplies).170171### 5d. recommendation (matrix on two axes)172Axes: `is_existing_policy_transfer` (T/F) and `premium_gap > 0` (T/F).173- F / F: `FUND_WITH_CRUMMEY_NOTICES`, `SUITABLE_WITH_ADMINISTRATION`, `LOW_IF_FORMALITIES_MET`.174- F / T: `USE_LIFETIME_EXEMPTION_FOR_SHORTFALL`, `BORDERLINE`, `EXCLUSION_SHORTFALL`.175- T / F: `USE_NEW_POLICY_OR_ACCEPT_LOOKBACK`, `BORDERLINE`, `THREE_YEAR_LOOKBACK`.176- T / T: `DISCLOSE_LOOKBACK_AND_USE_EXEMPTION`, `NOT_SUITABLE`, `THREE_YEAR_LOOKBACK_AND_EXCLUSION_SHORTFALL`.177`estate_result.estate_inclusion_risk` takes the same risk_flag value.178179## 6. Type C — `trust_comparison` (task 3 template)180181Fetch trust-candidates; you get `asset_value`, `expected_growth_rate`,182`grat_term_years`, `grat_annuity_rate`, `crat_term_years`, `crat_payout_rate`.183From SIGNED_PROFILE read `estate_value`, `liquid_assets`, `filing_status`,184`planning_year`, and goals (`family_transfer_priority`, `philanthropic_intent`).185186### 6a. estate_context (shared formula for types C and D)187- `taxable_estate` = `estate_value` from SIGNED_PROFILE.188- `estate_tax_exposure` = `max(0, taxable_estate - estate_tax_exemption[planning_year]) * estate_tax_rate`.189- `liquidity_gap_before_planning` = `max(0, estate_tax_exposure - liquid_assets)`.190191### 6b. grat192- `term_years` = `grat_term_years`.193- annuity_payment = `asset_value * grat_annuity_rate` (fixed each year).194- `projected_remainder_to_heirs` =195 `asset_value * (1+growth)^term - annuity_payment * (((1+growth)^term - 1) / growth)` (cents).196 (Grow the asset each year, pay the fixed annuity at year-end; remainder passes to heirs.)197- `estimated_estate_tax_reduction` = `projected_remainder_to_heirs * estate_tax_rate` (cents).198- `mortality_inclusion_risk` = `TERM_SURVIVAL_REQUIRED` (always; grantor must survive the term).199200### 6c. crat201- `term_years` = `min(crat_term_years, max_crat_term_years)`.202- annuity_payment = `asset_value * crat_payout_rate` (fixed each year).203- `projected_charitable_remainder` =204 `asset_value * (1+growth)^term - annuity_payment * (((1+growth)^term - 1) / growth)` (cents).205- `estimated_income_tax_deduction` = `asset_value * charitable_deduction_rate` (cents).206- `family_transfer_fit` based on `family_transfer_priority`:207 high -> `LOW`; moderate -> `MODERATE`; low -> `HIGH`.208209### 6d. recommendation210- `preferred_strategy`: `GRAT` if `family_transfer_priority == "high"` and211 `philanthropic_intent != "high"`; else `CRAT` if `philanthropic_intent == "high"`;212 else default `GRAT`.213- `rationale_code`: GRAT -> `CHILDREN_TRANSFER_PRIORITY`; CRAT -> `PHILANTHROPIC_PRIORITY`.214- `alternate_role`: GRAT preferred -> `SECONDARY_CHARITABLE_TOOL`;215 CRAT preferred -> `SECONDARY_FAMILY_TRANSFER_TOOL`.216217## 7. Type D — `estate_liquidity_action_plan` (task 4 template)218219Combines estate_context + ILIT + trust transfer. Fetch life-insurance AND220trust-candidates. Use SIGNED_PROFILE for estate/liquid goals, beneficiary_count.221222### 7a. estate_context — same formula as 6a.223224### 7b. ilit225- `annual_exclusion_capacity` = `beneficiary_count * annual_gift_exclusion[planning_year]`.226- `premium_gap` = `max(0, annual_premium - annual_exclusion_capacity)`.227- `estate_inclusion_risk` = risk_flag from the 5d matrix.228- `projected_outside_estate_if_implemented` = `death_benefit`.229230### 7c. trust_transfer231- `preferred_strategy`: GRAT/CRAT using the same goal rule as 6d.232- `projected_remainder_to_heirs` = GRAT remainder (formula 6b).233- `estimated_estate_tax_reduction` = `projected_remainder_to_heirs * estate_tax_rate`.234- `projected_charitable_remainder` = CRAT remainder (formula 6c, computed regardless235 of which strategy is preferred).236237### 7d. recommendation238- `primary_action`:239 - GRAT preferred + ILIT funding (no shortfall) -> `COMBINE_ILIT_AND_GRAT`.240 - CRAT preferred (philanthropic_intent high) -> `CRAT_WITH_LIQUIDITY_REVIEW`.241 - ILIT has `premium_gap > 0` -> `ILIT_WITH_EXEMPTION_REVIEW`.242- `sequencing`:243 - `COMBINE_ILIT_AND_GRAT` -> `ILIT_FIRST_THEN_GRAT`.244 - `CRAT_WITH_LIQUIDITY_REVIEW` -> `TRUST_DECISION_FIRST`.245 - `ILIT_WITH_EXEMPTION_REVIEW` -> `ILIT_FIRST_THEN_ATTORNEY_REVIEW`.246- `risk_flag` = the ILIT risk from the 5d matrix.247248### 7e. action_set (sorted alphabetically, subset of the 5 enum values)249Always include: `ATTORNEY_DRAFT_REVIEW`.250Include `GRAT_FOR_APPRECIATING_SHARES` when GRAT is preferred.251Include `CRAT_FOR_CHARITABLE_REMAINDER` when CRAT is preferred.252Include `ILIT_CRUMMEY_NOTICE_CYCLE` when the ILIT is being funded.253Include `LIFETIME_EXEMPTION_ALLOCATION` when `taxable_estate > estate_tax_exemption`254 (always true for these high-net-worth clients) OR when `premium_gap > 0`.255Sort the resulting list alphabetically and output as a JSON array of strings.256257## 8. Pitfalls258- Do NOT use CRM_NOTE values for income, beneficiary_count, or goals when a259 SIGNED_PROFILE exists — the CRM is the stale import that disagrees.260- Do NOT use the signed-profile stated balance for retirement accounts; use the261 CUSTODIAN_EXPORT balance from `/api/retirement-accounts`.262- `conversion_years` is the RECOMMENDED count from the account record, NOT a value263 you derive.264- `annual_conversion_amount` is the bracket fill (target MINUS non-IRA income), not265 the balance divided by years.266- `first_rmd_year` = year the owner turns 73 = `planning_year + (73 - age)`.267 Patel (age 72) starts RMD the very next year.268- RMD factor for year Y uses the age IN that year (`age + (Y - planning_year)`),269 not the current age. Guard against age > 99 (no factor) by clamping.270- `premium_gap` is `max(0, ...)`, never negative.271- GRAT/CRAT annuity payment = `asset_value * rate` EACH YEAR (fixed), and the272 remainder formula grows the starting asset and subtracts the FV of the annuity273 stream at `expected_growth_rate`.274- `estimated_income_tax_deduction` for CRAT = `asset_value * charitable_deduction_rate`275 (a flat fraction of the contribution), NOT the future charitable remainder.276- All numeric outputs must be JSON numbers (not strings) rounded to 2 decimals.277- All date outputs must be ISO `YYYY-MM-DD` strings.278- `action_set` MUST be sorted alphabetically.279- Use the `estate_tax_exemption` and `annual_gift_exclusion` for the client's280 `planning_year` (2026 -> exemption 13,610,000; gift exclusion 20,000), not 2025.281- `task_id` must be the literal `test_NNN` for the current test task, not a train id.