Private Wealth Advisory — Structured Planning Output SOP
Use this for personal-financial-advisory engagement tasks that ask for a single JSON
object conforming to a per-task answer_template.json. Domain covers Roth conversion +
RMD projection, ILIT Crummey implementation, GRAT-vs-CRAT trust comparison, and integrated
estate-liquidity action planning. The advisory API serves client records plus CONFLICTING
source documents (CRM note, attorney memo, custodian export, signed profile, stale
marketing intake). You must resolve which source controls each fact before computing.
0. Output contract (all task types)
- Return ONLY a JSON object matching the task's
answer_template.json required top-level
keys. No prose, no markdown fences.
- All USD amounts are JSON numbers rounded to cents (2 decimals), e.g.
469504.00.
- Dates are ISO
YYYY-MM-DD.
task_id = the task identifier supplied by the harness, formatted train_NNN /
test_NNN (derive from the task folder name if not explicit, e.g. folder 005 +
harness phase train -> train_005).
client_id = the stable client id from the request memo (see section 2).
analysis_type = the enum string from the answer template's analysis_type field.
1. Read engagement inputs (memo vs prompt)
- Read
input/payloads/request_memo.md FIRST. It is the authoritative engagement record.
- Extract from the memo:
Client ID: line -> client_id (USE THIS, not the client name in prompt.txt).
Engagement: line -> confirms which analysis type.
Planning horizon year: NNNN line -> the RMD projection horizon (Roth tasks only).
If absent, the task is point-in-time / term-based (ILIT, GRAT/CRAT, estate-liquidity)
and needs no multi-year horizon.
input/prompt.txt is generic boilerplate; its client name line can be STALE and may
disagree with the memo. Always trust the memo's Client ID: over the prompt prose.
input/payloads/answer_template.json defines the exact required keys, enums, and any
ordering constraint (e.g. action_set sorted alphabetically). Build the JSON to match.
2. Remote API workflow + endpoint order
Base URL is in environment_access.md (e.g. http://HOST:PORT). GET only.
Fetch in this order per engagement:
GET /api/health -> confirm {"ok": true}.
GET /api/policies/tax -> planning constants (cache once, reuse for every client):
annual_gift_exclusion (by year, e.g. {"2025":19000,"2026":20000})
estate_tax_exemption (by year, e.g. {"2025":13990000,"2026":13610000})
estate_tax_rate (e.g. 0.4)
conversion_bracket_targets by filing status (MFJ/SINGLE/HOH)
max_crat_term_years (e.g. 20)
charitable_deduction_rate (e.g. 0.35)
GET /api/rmd-factors -> map of age (string) -> IRS Uniform Lifetime Table divisor
(73->26.5, 74->25.5, ...). Used only for Roth RMD projection.
GET /api/clients/<client_id> -> base client record (age, filing_status, marital_status,
planning_year, estate_value, liquid_assets).
GET /api/source-documents?client_id=<id> -> array of conflicting source docs. Each has
source_type, effective_date, and a facts object. THIS IS THE CONFLICT SURFACE.
- Analysis-type-specific records:
- Roth:
GET /api/retirement-accounts?client_id=<id>
- ILIT:
GET /api/life-insurance?client_id=<id>
- GRAT/CRAT or estate-liquidity:
GET /api/trust-candidates?client_id=<id> AND
GET /api/life-insurance?client_id=<id> (estate-liquidity needs both).
3. Source-resolution precedence (inferred rule)
For any fact, resolve conflicts using this authority ladder, HIGHEST to LOWEST:
- SIGNED_PROFILE — client-signed, newest effective_date. Controls personal/demographic
profile AND stated client goals:
age, annual_non_ira_income, marginal_tax_rate,
filing_status, marital_status, liquid_assets, estate_value, beneficiary_count,
planning_year, philanthropic_intent, family_transfer_priority.
- ATTORNEY_MEMO — attorney planning-call notes. Controls LEGAL trust/estate STRUCTURE
characterization: ILIT trust design, GRAT/CRAT vehicle choice, and which asset is placed
in trust. Wins for
controlling_asset_source (trust assets are legal structures, not
custodian holdings).
- CUSTODIAN_EXPORT — account/policy holdings of record. Controls money-on-hand facts:
traditional_balance, roth_balance, expected_return, rmd_start_age,
recommended_conversion_years, and policy death_benefit/annual_premium (the
retirement-accounts and life-insurance records are custodian-type data).
- CRM_NOTE — older CRM import. Fallback only; never controls when a higher source
states the same fact.
- STALE_MARKETING_INTAKE — lowest; marketing-only. Never controls a material fact.
Per-task source_resolution output mapping
| Output field |
Controlling source |
Why |
controlling_profile_source (Roth) |
SIGNED_PROFILE |
demographics + tax rate |
controlling_account_source (Roth) |
CUSTODIAN_EXPORT |
retirement account record |
controlling_beneficiary_source (ILIT) |
SIGNED_PROFILE |
beneficiary_count appears in signed vs CRM -> signed wins |
controlling_policy_source (ILIT, estate) |
CUSTODIAN_EXPORT |
life-insurance record is custodian-type; override to ATTORNEY_MEMO/SIGNED_PROFILE only if that higher source explicitly documents the policy |
controlling_goal_source (GRAT/CRAT, estate) |
SIGNED_PROFILE |
client-signed philanthropic_intent / family_transfer_priority supersede attorney/CRM |
controlling_asset_source (GRAT/CRAT) |
ATTORNEY_MEMO |
trust is an attorney-drafted legal structure; enum excludes CUSTODIAN, confirming trust assets are legal not custodian |
Note the enum composition is itself a hint: controlling_asset_source never includes
CUSTODIAN_EXPORT (a trust asset is not a brokerage holding), while
controlling_policy_source does include it (an insurance policy is a financial product).
4. Shared formulas
first_rmd_year = planning_year + max(0, rmd_start_age - age) (RMD begins at age 73).
taxable_estate = max(0, estate_value - estate_tax_exemption[planning_year])
(estate_value from controlling source, typically signed profile).
estate_tax_exposure = taxable_estate * estate_tax_rate.
liquidity_gap_before_planning = max(0, estate_tax_exposure - liquid_assets).
annual_gift_exclusion for the plan = annual_gift_exclusion[planning_year].
5. Analysis type A — roth_conversion_rmd
Resolved inputs: age, annual_non_ira_income, marginal_tax_rate, filing_status
(from SIGNED_PROFILE); traditional_balance (T0), roth_balance (R0), expected_return
(g), rmd_start_age, recommended_conversion_years (from CUSTODIAN_EXPORT);
conversion_bracket_targets[filing_status] (bt); RMD factor table; horizon_year (memo).
conversion_plan (BRACKET-FILL method — conversion_bracket_targets is load-bearing)
first_conversion_year = planning_year.
conversion_years = recommended_conversion_years.
annual_conversion_amount = max(0, bt - annual_non_ira_income) (fill remaining bracket
room each year), capped in the final year at the remaining traditional balance.
total_converted = sum of per-year conversions across the window (<= T0). Round cents.
total_conversion_tax = total_converted * marginal_tax_rate. Round cents.
conversion_years_positive = count of conversion years falling BEFORE first_rmd_year
(the pre-RMD window), capped at conversion_years:
max(0, min(conversion_years, first_rmd_year - first_conversion_year)).
These are the years conversion is unambiguously beneficial.
rmd_projection (year-by-year simulation, planning_year..horizon_year)
Define factor(age_in_year) from the RMD table (age attained that year).
BASELINE (no conversion):
For each year Y, beginning balance B_Y:
- if Y >= first_rmd_year:
rmd_Y = B_Y / factor(age_Y); tax_Y = rmd_Y * mtr;
B_{Y+1} = (B_Y - rmd_Y) * (1+g).
- else:
rmd_Y = 0; B_{Y+1} = B_Y * (1+g).
baseline_rmd_tax_through_horizon = sum of tax_Y for Y in [first_rmd_year, horizon].
CONVERSION scenario: same loop, but in each conversion year reduce the traditional balance
by that year's conversion amount (after taking any RMD first in overlap years), and credit
the conversion to the Roth balance which then grows at g. Take RMDs on the reduced balance.
conversion_rmd_tax_through_horizon = sum of tax_Y under conversion.
horizon_year = memo horizon.
first_rmd_year = as above.
rmd_tax_savings_through_horizon =
baseline_rmd_tax_through_horizon - conversion_rmd_tax_through_horizon. Round cents.
legacy_projection (end of horizon_year, conversion scenario)
projected_roth_balance_horizon = R0 grown to horizon + each conversion grown the
remaining years at g. Round cents.
projected_traditional_balance_horizon = remaining traditional balance at end of horizon
(after conversions and RMDs). Round cents.
heir_tax_profile: let r = projected_roth_balance_horizon / (roth + trad) at horizon.
r > 0.6 -> MOSTLY_TAX_FREE; r < 0.4 -> MOSTLY_TAXABLE; else MIXED_TAXABLE_AND_TAX_FREE.
recommendation
primary_action: STAGED_ROTH_CONVERSION if T0 > 0 and pre-RMD window >= 1; DEFER if
RMD imminent and bracket/liquidity poor; NO_CONVERSION if T0 == 0.
suitability: SUITABLE if window >= ~3 yrs and liquid_assets cover conversion tax;
BORDERLINE if window 1-2 yrs or tax exceeds liquid_assets; DEFER if no viable window.
risk_flag (most salient wins, priority order): RMD_NEAR_TERM if
first_rmd_year - planning_year <= 2; else LIQUIDITY_CONSTRAINT if
total_conversion_tax > liquid_assets; else TAX_BRACKET_MANAGEMENT (bracket-fill
inherently involves managing the bracket).
source_resolution
controlling_profile_source = SIGNED_PROFILE; controlling_account_source = CUSTODIAN_EXPORT.
6. Analysis type B — ilit_crummey_implementation
Resolved inputs: beneficiary_count (SIGNED_PROFILE); planning_year (client record);
policy death_benefit, annual_premium, planned_contribution_date,
is_existing_policy_transfer (life-insurance record); annual_gift_exclusion[planning_year].
gift_plan
planning_year = client.planning_year.
annual_exclusion_per_beneficiary = annual_gift_exclusion[planning_year] (e.g. 20000 for 2026).
beneficiary_count = from SIGNED_PROFILE.
annual_exclusion_capacity = annual_exclusion_per_beneficiary * beneficiary_count.
annual_premium = policy.annual_premium.
premium_gap = annual_premium - annual_exclusion_capacity (raw; positive => shortfall).
administration (Crummey notice formalities)
notices_required = beneficiary_count (one notice per beneficiary).
contribution_date = policy.planned_contribution_date (ISO).
notice_due_date = contribution_date (notice delivered at/with the contribution).
withdrawal_window_end = contribution_date + 30 days (30-day Crummey withdrawal right).
ISO date arithmetic: add 30 days to the contribution date.
earliest_premium_payment_date = withdrawal_window_end (pay premium after the
withdrawal right lapses so funds are clearly trust property).
dedicated_bank_account_required = true (ILIT must hold gifts in a dedicated trust account).
estate_result
death_benefit = policy.death_benefit.
estate_inclusion_risk = same value as recommendation.risk_flag.
projected_outside_estate_if_implemented = death_benefit when no 3-year lookback
(is_existing_policy_transfer == false); 0 when lookback applies within 3 years.
tax_liquidity_support = death_benefit (liquidity the ILIT provides to the estate).
recommendation
risk_flag: if is_existing_policy_transfer AND premium_gap > 0 ->
THREE_YEAR_LOOKBACK_AND_EXCLUSION_SHORTFALL; elif lookback -> THREE_YEAR_LOOKBACK;
elif premium_gap > 0 -> EXCLUSION_SHORTFALL; else LOW_IF_FORMALITIES_MET.
primary_action: if lookback and shortfall -> DISCLOSE_LOOKBACK_AND_USE_EXEMPTION;
elif lookback -> USE_NEW_POLICY_OR_ACCEPT_LOOKBACK; elif shortfall ->
USE_LIFETIME_EXEMPTION_FOR_SHORTFALL; else FUND_WITH_CRUMMEY_NOTICES.
suitability: SUITABLE_WITH_ADMINISTRATION (no lookback, no shortfall);
BORDERLINE (shortfall only); NOT_SUITABLE (lookback present).
source_resolution
controlling_beneficiary_source = SIGNED_PROFILE; controlling_policy_source = CUSTODIAN_EXPORT
(default; override to a higher source only if that source explicitly documents the policy).
7. Analysis type C — trust_comparison (GRAT vs CRAT)
Resolved inputs: goals philanthropic_intent, family_transfer_priority (SIGNED_PROFILE);
estate_value (SIGNED_PROFILE); trust candidate asset_value (A), expected_growth_rate (g),
grat_term_years (nG), grat_annuity_rate (aG), crat_term_years (= max_crat_term_years,
e.g. 20), crat_payout_rate (aC); estate_tax_rate, charitable_deduction_rate.
estate_context
taxable_estate = max(0, estate_value - exemption[planning_year]).
estate_tax_exposure = taxable_estate * estate_tax_rate.
liquidity_gap_before_planning = max(0, estate_tax_exposure - liquid_assets).
grat (fixed-amount annuity on initial value)
term_years = grat_term_years.
- annuity payment P =
A * aG per year.
FV_asset = A * (1+g)^nG.
FV_annuity = P * ((1+g)^nG - 1) / g.
projected_remainder_to_heirs = max(0, FV_asset - FV_annuity). Round cents.
estimated_estate_tax_reduction = projected_remainder_to_heirs * estate_tax_rate.
(remainder passes to heirs free of estate tax via the GRAT).
mortality_inclusion_risk = TERM_SURVIVAL_REQUIRED (always; grantor must survive term).
crat
term_years = crat_term_years (== max_crat_term_years).
- charity payout C =
A * aC per year.
projected_charitable_remainder = C * ((1+g)^term - 1) / g (FV of charitable payouts).
Round cents.
estimated_income_tax_deduction = A * charitable_deduction_rate. Round cents.
family_transfer_fit = LOW (CRAT is charity-first; family transfer is incidental).
recommendation
- If
family_transfer_priority == "high" -> preferred_strategy = GRAT,
rationale_code = CHILDREN_TRANSFER_PRIORITY, alternate_role = SECONDARY_CHARITABLE_TOOL.
- Elif
philanthropic_intent == "high" -> preferred_strategy = CRAT,
rationale_code = PHILANTHROPIC_PRIORITY, alternate_role = SECONDARY_FAMILY_TRANSFER_TOOL.
- Default (no "high") -> GRAT (family transfer is the harder wealth-transfer problem).
source_resolution
controlling_goal_source = SIGNED_PROFILE; controlling_asset_source = ATTORNEY_MEMO.
8. Analysis type D — estate_liquidity_action_plan
Combines ILIT + trust transfer. Resolved inputs as in B and C, plus estate context.
estate_context
Same as section 7 (taxable_estate, estate_tax_exposure, liquidity_gap_before_planning).
ilit
annual_exclusion_capacity, premium_gap as in section 6.
estate_inclusion_risk = the ILIT risk_flag (section 6 logic).
projected_outside_estate_if_implemented = death_benefit (no lookback) or 0.
trust_transfer
preferred_strategy = GRAT or CRAT (section 7 goal logic).
projected_remainder_to_heirs = GRAT remainder (section 7) if GRAT chosen.
estimated_estate_tax_reduction = projected_remainder_to_heirs * estate_tax_rate.
projected_charitable_remainder = CRAT charitable FV (section 7) — the alternate
charitable tool's figure, reported for the combined plan.
action_set (sorted alphabetically; include applicable subset)
ATTORNEY_DRAFT_REVIEW — always (trust docs need drafting).
ILIT_CRUMMEY_NOTICE_CYCLE — if ILIT funded via Crummey (no lookback-blocking issue).
LIFETIME_EXEMPTION_ALLOCATION — if premium_gap > 0 (shortfall).
GRAT_FOR_APPRECIATING_SHARES — if preferred_strategy == GRAT.
CRAT_FOR_CHARITABLE_REMAINDER — if preferred_strategy == CRAT.
Sort the included values alphabetically before emitting.
recommendation
primary_action: if ILIT viable and GRAT preferred -> COMBINE_ILIT_AND_GRAT;
if CRAT preferred -> CRAT_WITH_LIQUIDITY_REVIEW;
if ILIT shortfall dominates -> 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 = ILIT risk_flag (section 6 logic).
source_resolution
controlling_goal_source = SIGNED_PROFILE; controlling_policy_source = CUSTODIAN_EXPORT.
9. Formatting & rounding rules
- Round every USD amount to cents (2 decimals). Apply rounding only at the final emitted
value, not at intermediate simulation steps, to avoid drift.
- Years are integers; ISO dates are
YYYY-MM-DD.
action_set must be sorted alphabetically (estate-liquidity task).
- Numbers are JSON numbers, never quoted strings.
- Use the controlling-source values for every computation; never average conflicting sources.
10. Common mistakes / pitfalls
- Trusting
prompt.txt client name over the memo's Client ID: line. The prompt can be
stale; the memo is authoritative.
- Forgetting to read
Planning horizon year: from the memo (Roth tasks) and defaulting to
a wrong horizon.
- Using CRM_NOTE
beneficiary_count / annual_non_ira_income instead of the SIGNED_PROFILE
values (they disagree by design).
- Computing
taxable_estate as gross estate_value instead of
max(0, estate_value - exemption).
- Treating
conversion_years_positive as equal to conversion_years by default; for a
near-RMD client the pre-RMD window is much shorter than the recommended conversion window.
- Ignoring that bracket-fill makes
conversion_bracket_targets the conversion-sizing driver
(annual = bracket_target - non_ira_income), not an even split.
- Forgetting RMD-must-come-out-first in years that are both conversion and RMD years.
- Setting
earliest_premium_payment_date to the contribution date instead of after the
30-day Crummey withdrawal window closes.
- Setting
dedicated_bank_account_required to false (an ILIT always needs one).
- Not flagging
THREE_YEAR_LOOKBACK when is_existing_policy_transfer == true.
- Putting CRAT/GRAT numbers from the non-preferred strategy in the preferred-strategy
fields, or forgetting
projected_charitable_remainder in the combined estate plan.
- Emitting
action_set unsorted.
- Rounding intermediate values and propagating error; round only final emitted amounts.
1---2name: self-attempt-02-153description: Private Wealth Advisory — Structured Planning Output SOP4---5# Private Wealth Advisory — Structured Planning Output SOP67Use this for personal-financial-advisory engagement tasks that ask for a single JSON8object conforming to a per-task `answer_template.json`. Domain covers Roth conversion +9RMD projection, ILIT Crummey implementation, GRAT-vs-CRAT trust comparison, and integrated10estate-liquidity action planning. The advisory API serves client records plus CONFLICTING11source documents (CRM note, attorney memo, custodian export, signed profile, stale12marketing intake). You must resolve which source controls each fact before computing.1314## 0. Output contract (all task types)15- Return ONLY a JSON object matching the task's `answer_template.json` required top-level16 keys. No prose, no markdown fences.17- All USD amounts are JSON numbers rounded to cents (2 decimals), e.g. `469504.00`.18- Dates are ISO `YYYY-MM-DD`.19- `task_id` = the task identifier supplied by the harness, formatted `train_NNN` /20 `test_NNN` (derive from the task folder name if not explicit, e.g. folder `005` +21 harness phase `train` -> `train_005`).22- `client_id` = the stable client id from the request memo (see section 2).23- `analysis_type` = the enum string from the answer template's `analysis_type` field.2425## 1. Read engagement inputs (memo vs prompt)261. Read `input/payloads/request_memo.md` FIRST. It is the authoritative engagement record.272. Extract from the memo:28 - `Client ID:` line -> `client_id` (USE THIS, not the client name in `prompt.txt`).29 - `Engagement:` line -> confirms which analysis type.30 - `Planning horizon year: NNNN` line -> the RMD projection horizon (Roth tasks only).31 If absent, the task is point-in-time / term-based (ILIT, GRAT/CRAT, estate-liquidity)32 and needs no multi-year horizon.333. `input/prompt.txt` is generic boilerplate; its client name line can be STALE and may34 disagree with the memo. Always trust the memo's `Client ID:` over the prompt prose.354. `input/payloads/answer_template.json` defines the exact required keys, enums, and any36 ordering constraint (e.g. `action_set` sorted alphabetically). Build the JSON to match.3738## 2. Remote API workflow + endpoint order39Base URL is in `environment_access.md` (e.g. `http://HOST:PORT`). GET only.4041Fetch in this order per engagement:421. `GET /api/health` -> confirm `{"ok": true}`.432. `GET /api/policies/tax` -> planning constants (cache once, reuse for every client):44 - `annual_gift_exclusion` (by year, e.g. `{"2025":19000,"2026":20000}`)45 - `estate_tax_exemption` (by year, e.g. `{"2025":13990000,"2026":13610000}`)46 - `estate_tax_rate` (e.g. `0.4`)47 - `conversion_bracket_targets` by filing status (`MFJ`/`SINGLE`/`HOH`)48 - `max_crat_term_years` (e.g. `20`)49 - `charitable_deduction_rate` (e.g. `0.35`)503. `GET /api/rmd-factors` -> map of age (string) -> IRS Uniform Lifetime Table divisor51 (73->26.5, 74->25.5, ...). Used only for Roth RMD projection.524. `GET /api/clients/<client_id>` -> base client record (age, filing_status, marital_status,53 planning_year, estate_value, liquid_assets).545. `GET /api/source-documents?client_id=<id>` -> array of conflicting source docs. Each has55 `source_type`, `effective_date`, and a `facts` object. THIS IS THE CONFLICT SURFACE.566. Analysis-type-specific records:57 - Roth: `GET /api/retirement-accounts?client_id=<id>`58 - ILIT: `GET /api/life-insurance?client_id=<id>`59 - GRAT/CRAT or estate-liquidity: `GET /api/trust-candidates?client_id=<id>` AND60 `GET /api/life-insurance?client_id=<id>` (estate-liquidity needs both).6162## 3. Source-resolution precedence (inferred rule)63For any fact, resolve conflicts using this authority ladder, HIGHEST to LOWEST:64651. **SIGNED_PROFILE** — client-signed, newest effective_date. Controls personal/demographic66 profile AND stated client goals: `age`, `annual_non_ira_income`, `marginal_tax_rate`,67 `filing_status`, `marital_status`, `liquid_assets`, `estate_value`, `beneficiary_count`,68 `planning_year`, `philanthropic_intent`, `family_transfer_priority`.692. **ATTORNEY_MEMO** — attorney planning-call notes. Controls LEGAL trust/estate STRUCTURE70 characterization: ILIT trust design, GRAT/CRAT vehicle choice, and which asset is placed71 in trust. Wins for `controlling_asset_source` (trust assets are legal structures, not72 custodian holdings).733. **CUSTODIAN_EXPORT** — account/policy holdings of record. Controls money-on-hand facts:74 `traditional_balance`, `roth_balance`, `expected_return`, `rmd_start_age`,75 `recommended_conversion_years`, and policy `death_benefit`/`annual_premium` (the76 retirement-accounts and life-insurance records are custodian-type data).774. **CRM_NOTE** — older CRM import. Fallback only; never controls when a higher source78 states the same fact.795. **STALE_MARKETING_INTAKE** — lowest; marketing-only. Never controls a material fact.8081### Per-task `source_resolution` output mapping82| Output field | Controlling source | Why |83|---|---|---|84| `controlling_profile_source` (Roth) | `SIGNED_PROFILE` | demographics + tax rate |85| `controlling_account_source` (Roth) | `CUSTODIAN_EXPORT` | retirement account record |86| `controlling_beneficiary_source` (ILIT) | `SIGNED_PROFILE` | beneficiary_count appears in signed vs CRM -> signed wins |87| `controlling_policy_source` (ILIT, estate) | `CUSTODIAN_EXPORT` | life-insurance record is custodian-type; override to `ATTORNEY_MEMO`/`SIGNED_PROFILE` only if that higher source explicitly documents the policy |88| `controlling_goal_source` (GRAT/CRAT, estate) | `SIGNED_PROFILE` | client-signed philanthropic_intent / family_transfer_priority supersede attorney/CRM |89| `controlling_asset_source` (GRAT/CRAT) | `ATTORNEY_MEMO` | trust is an attorney-drafted legal structure; enum excludes CUSTODIAN, confirming trust assets are legal not custodian |9091Note the enum composition is itself a hint: `controlling_asset_source` never includes92`CUSTODIAN_EXPORT` (a trust asset is not a brokerage holding), while93`controlling_policy_source` does include it (an insurance policy is a financial product).9495## 4. Shared formulas96- `first_rmd_year` = `planning_year + max(0, rmd_start_age - age)` (RMD begins at age 73).97- `taxable_estate` = `max(0, estate_value - estate_tax_exemption[planning_year])`98 (`estate_value` from controlling source, typically signed profile).99- `estate_tax_exposure` = `taxable_estate * estate_tax_rate`.100- `liquidity_gap_before_planning` = `max(0, estate_tax_exposure - liquid_assets)`.101- `annual_gift_exclusion` for the plan = `annual_gift_exclusion[planning_year]`.102103## 5. Analysis type A — `roth_conversion_rmd`104Resolved inputs: `age`, `annual_non_ira_income`, `marginal_tax_rate`, `filing_status`105(from SIGNED_PROFILE); `traditional_balance` (T0), `roth_balance` (R0), `expected_return`106(g), `rmd_start_age`, `recommended_conversion_years` (from CUSTODIAN_EXPORT);107`conversion_bracket_targets[filing_status]` (bt); RMD factor table; `horizon_year` (memo).108109### conversion_plan (BRACKET-FILL method — `conversion_bracket_targets` is load-bearing)110- `first_conversion_year` = `planning_year`.111- `conversion_years` = `recommended_conversion_years`.112- `annual_conversion_amount` = `max(0, bt - annual_non_ira_income)` (fill remaining bracket113 room each year), capped in the final year at the remaining traditional balance.114- `total_converted` = sum of per-year conversions across the window (<= T0). Round cents.115- `total_conversion_tax` = `total_converted * marginal_tax_rate`. Round cents.116- `conversion_years_positive` = count of conversion years falling BEFORE `first_rmd_year`117 (the pre-RMD window), capped at `conversion_years`:118 `max(0, min(conversion_years, first_rmd_year - first_conversion_year))`.119 These are the years conversion is unambiguously beneficial.120121### rmd_projection (year-by-year simulation, planning_year..horizon_year)122Define `factor(age_in_year)` from the RMD table (age attained that year).123BASELINE (no conversion):124 For each year Y, beginning balance B_Y:125 - if Y >= first_rmd_year: `rmd_Y = B_Y / factor(age_Y)`; `tax_Y = rmd_Y * mtr`;126 `B_{Y+1} = (B_Y - rmd_Y) * (1+g)`.127 - else: `rmd_Y = 0`; `B_{Y+1} = B_Y * (1+g)`.128 `baseline_rmd_tax_through_horizon` = sum of `tax_Y` for Y in [first_rmd_year, horizon].129CONVERSION scenario: same loop, but in each conversion year reduce the traditional balance130by that year's conversion amount (after taking any RMD first in overlap years), and credit131the conversion to the Roth balance which then grows at g. Take RMDs on the reduced balance.132 `conversion_rmd_tax_through_horizon` = sum of `tax_Y` under conversion.133- `horizon_year` = memo horizon.134- `first_rmd_year` = as above.135- `rmd_tax_savings_through_horizon` =136 `baseline_rmd_tax_through_horizon - conversion_rmd_tax_through_horizon`. Round cents.137138### legacy_projection (end of horizon_year, conversion scenario)139- `projected_roth_balance_horizon` = R0 grown to horizon + each conversion grown the140 remaining years at g. Round cents.141- `projected_traditional_balance_horizon` = remaining traditional balance at end of horizon142 (after conversions and RMDs). Round cents.143- `heir_tax_profile`: let `r = projected_roth_balance_horizon / (roth + trad)` at horizon.144 `r > 0.6` -> `MOSTLY_TAX_FREE`; `r < 0.4` -> `MOSTLY_TAXABLE`; else `MIXED_TAXABLE_AND_TAX_FREE`.145146### recommendation147- `primary_action`: `STAGED_ROTH_CONVERSION` if T0 > 0 and pre-RMD window >= 1; `DEFER` if148 RMD imminent and bracket/liquidity poor; `NO_CONVERSION` if T0 == 0.149- `suitability`: `SUITABLE` if window >= ~3 yrs and liquid_assets cover conversion tax;150 `BORDERLINE` if window 1-2 yrs or tax exceeds liquid_assets; `DEFER` if no viable window.151- `risk_flag` (most salient wins, priority order): `RMD_NEAR_TERM` if152 `first_rmd_year - planning_year <= 2`; else `LIQUIDITY_CONSTRAINT` if153 `total_conversion_tax > liquid_assets`; else `TAX_BRACKET_MANAGEMENT` (bracket-fill154 inherently involves managing the bracket).155156### source_resolution157`controlling_profile_source = SIGNED_PROFILE`; `controlling_account_source = CUSTODIAN_EXPORT`.158159## 6. Analysis type B — `ilit_crummey_implementation`160Resolved inputs: `beneficiary_count` (SIGNED_PROFILE); `planning_year` (client record);161policy `death_benefit`, `annual_premium`, `planned_contribution_date`,162`is_existing_policy_transfer` (life-insurance record); `annual_gift_exclusion[planning_year]`.163164### gift_plan165- `planning_year` = client.planning_year.166- `annual_exclusion_per_beneficiary` = `annual_gift_exclusion[planning_year]` (e.g. 20000 for 2026).167- `beneficiary_count` = from SIGNED_PROFILE.168- `annual_exclusion_capacity` = `annual_exclusion_per_beneficiary * beneficiary_count`.169- `annual_premium` = policy.annual_premium.170- `premium_gap` = `annual_premium - annual_exclusion_capacity` (raw; positive => shortfall).171172### administration (Crummey notice formalities)173- `notices_required` = `beneficiary_count` (one notice per beneficiary).174- `contribution_date` = policy.planned_contribution_date (ISO).175- `notice_due_date` = `contribution_date` (notice delivered at/with the contribution).176- `withdrawal_window_end` = `contribution_date + 30 days` (30-day Crummey withdrawal right).177 ISO date arithmetic: add 30 days to the contribution date.178- `earliest_premium_payment_date` = `withdrawal_window_end` (pay premium after the179 withdrawal right lapses so funds are clearly trust property).180- `dedicated_bank_account_required` = `true` (ILIT must hold gifts in a dedicated trust account).181182### estate_result183- `death_benefit` = policy.death_benefit.184- `estate_inclusion_risk` = same value as `recommendation.risk_flag`.185- `projected_outside_estate_if_implemented` = `death_benefit` when no 3-year lookback186 (`is_existing_policy_transfer == false`); `0` when lookback applies within 3 years.187- `tax_liquidity_support` = `death_benefit` (liquidity the ILIT provides to the estate).188189### recommendation190- `risk_flag`: if `is_existing_policy_transfer` AND `premium_gap > 0` ->191 `THREE_YEAR_LOOKBACK_AND_EXCLUSION_SHORTFALL`; elif lookback -> `THREE_YEAR_LOOKBACK`;192 elif `premium_gap > 0` -> `EXCLUSION_SHORTFALL`; else `LOW_IF_FORMALITIES_MET`.193- `primary_action`: if lookback and shortfall -> `DISCLOSE_LOOKBACK_AND_USE_EXEMPTION`;194 elif lookback -> `USE_NEW_POLICY_OR_ACCEPT_LOOKBACK`; elif shortfall ->195 `USE_LIFETIME_EXEMPTION_FOR_SHORTFALL`; else `FUND_WITH_CRUMMEY_NOTICES`.196- `suitability`: `SUITABLE_WITH_ADMINISTRATION` (no lookback, no shortfall);197 `BORDERLINE` (shortfall only); `NOT_SUITABLE` (lookback present).198199### source_resolution200`controlling_beneficiary_source = SIGNED_PROFILE`; `controlling_policy_source = CUSTODIAN_EXPORT`201(default; override to a higher source only if that source explicitly documents the policy).202203## 7. Analysis type C — `trust_comparison` (GRAT vs CRAT)204Resolved inputs: goals `philanthropic_intent`, `family_transfer_priority` (SIGNED_PROFILE);205`estate_value` (SIGNED_PROFILE); trust candidate `asset_value` (A), `expected_growth_rate` (g),206`grat_term_years` (nG), `grat_annuity_rate` (aG), `crat_term_years` (= `max_crat_term_years`,207e.g. 20), `crat_payout_rate` (aC); `estate_tax_rate`, `charitable_deduction_rate`.208209### estate_context210- `taxable_estate` = `max(0, estate_value - exemption[planning_year])`.211- `estate_tax_exposure` = `taxable_estate * estate_tax_rate`.212- `liquidity_gap_before_planning` = `max(0, estate_tax_exposure - liquid_assets)`.213214### grat (fixed-amount annuity on initial value)215- `term_years` = `grat_term_years`.216- annuity payment P = `A * aG` per year.217- `FV_asset = A * (1+g)^nG`.218- `FV_annuity = P * ((1+g)^nG - 1) / g`.219- `projected_remainder_to_heirs` = `max(0, FV_asset - FV_annuity)`. Round cents.220- `estimated_estate_tax_reduction` = `projected_remainder_to_heirs * estate_tax_rate`.221 (remainder passes to heirs free of estate tax via the GRAT).222- `mortality_inclusion_risk` = `TERM_SURVIVAL_REQUIRED` (always; grantor must survive term).223224### crat225- `term_years` = `crat_term_years` (== `max_crat_term_years`).226- charity payout C = `A * aC` per year.227- `projected_charitable_remainder` = `C * ((1+g)^term - 1) / g` (FV of charitable payouts).228 Round cents.229- `estimated_income_tax_deduction` = `A * charitable_deduction_rate`. Round cents.230- `family_transfer_fit` = `LOW` (CRAT is charity-first; family transfer is incidental).231232### recommendation233- If `family_transfer_priority == "high"` -> `preferred_strategy = GRAT`,234 `rationale_code = CHILDREN_TRANSFER_PRIORITY`, `alternate_role = SECONDARY_CHARITABLE_TOOL`.235- Elif `philanthropic_intent == "high"` -> `preferred_strategy = CRAT`,236 `rationale_code = PHILANTHROPIC_PRIORITY`, `alternate_role = SECONDARY_FAMILY_TRANSFER_TOOL`.237- Default (no "high") -> GRAT (family transfer is the harder wealth-transfer problem).238239### source_resolution240`controlling_goal_source = SIGNED_PROFILE`; `controlling_asset_source = ATTORNEY_MEMO`.241242## 8. Analysis type D — `estate_liquidity_action_plan`243Combines ILIT + trust transfer. Resolved inputs as in B and C, plus estate context.244245### estate_context246Same as section 7 (`taxable_estate`, `estate_tax_exposure`, `liquidity_gap_before_planning`).247248### ilit249- `annual_exclusion_capacity`, `premium_gap` as in section 6.250- `estate_inclusion_risk` = the ILIT risk_flag (section 6 logic).251- `projected_outside_estate_if_implemented` = `death_benefit` (no lookback) or `0`.252253### trust_transfer254- `preferred_strategy` = GRAT or CRAT (section 7 goal logic).255- `projected_remainder_to_heirs` = GRAT remainder (section 7) if GRAT chosen.256- `estimated_estate_tax_reduction` = `projected_remainder_to_heirs * estate_tax_rate`.257- `projected_charitable_remainder` = CRAT charitable FV (section 7) — the alternate258 charitable tool's figure, reported for the combined plan.259260### action_set (sorted alphabetically; include applicable subset)261- `ATTORNEY_DRAFT_REVIEW` — always (trust docs need drafting).262- `ILIT_CRUMMEY_NOTICE_CYCLE` — if ILIT funded via Crummey (no lookback-blocking issue).263- `LIFETIME_EXEMPTION_ALLOCATION` — if `premium_gap > 0` (shortfall).264- `GRAT_FOR_APPRECIATING_SHARES` — if `preferred_strategy == GRAT`.265- `CRAT_FOR_CHARITABLE_REMAINDER` — if `preferred_strategy == CRAT`.266Sort the included values alphabetically before emitting.267268### recommendation269- `primary_action`: if ILIT viable and GRAT preferred -> `COMBINE_ILIT_AND_GRAT`;270 if CRAT preferred -> `CRAT_WITH_LIQUIDITY_REVIEW`;271 if ILIT shortfall dominates -> `ILIT_WITH_EXEMPTION_REVIEW`.272- `sequencing`: `COMBINE_ILIT_AND_GRAT` -> `ILIT_FIRST_THEN_GRAT`;273 `CRAT_WITH_LIQUIDITY_REVIEW` -> `TRUST_DECISION_FIRST`;274 `ILIT_WITH_EXEMPTION_REVIEW` -> `ILIT_FIRST_THEN_ATTORNEY_REVIEW`.275- `risk_flag` = ILIT risk_flag (section 6 logic).276277### source_resolution278`controlling_goal_source = SIGNED_PROFILE`; `controlling_policy_source = CUSTODIAN_EXPORT`.279280## 9. Formatting & rounding rules281- Round every USD amount to cents (2 decimals). Apply rounding only at the final emitted282 value, not at intermediate simulation steps, to avoid drift.283- Years are integers; ISO dates are `YYYY-MM-DD`.284- `action_set` must be sorted alphabetically (estate-liquidity task).285- Numbers are JSON numbers, never quoted strings.286- Use the controlling-source values for every computation; never average conflicting sources.287288## 10. Common mistakes / pitfalls289- Trusting `prompt.txt` client name over the memo's `Client ID:` line. The prompt can be290 stale; the memo is authoritative.291- Forgetting to read `Planning horizon year:` from the memo (Roth tasks) and defaulting to292 a wrong horizon.293- Using CRM_NOTE `beneficiary_count` / `annual_non_ira_income` instead of the SIGNED_PROFILE294 values (they disagree by design).295- Computing `taxable_estate` as gross `estate_value` instead of296 `max(0, estate_value - exemption)`.297- Treating `conversion_years_positive` as equal to `conversion_years` by default; for a298 near-RMD client the pre-RMD window is much shorter than the recommended conversion window.299- Ignoring that bracket-fill makes `conversion_bracket_targets` the conversion-sizing driver300 (annual = bracket_target - non_ira_income), not an even split.301- Forgetting RMD-must-come-out-first in years that are both conversion and RMD years.302- Setting `earliest_premium_payment_date` to the contribution date instead of after the303 30-day Crummey withdrawal window closes.304- Setting `dedicated_bank_account_required` to false (an ILIT always needs one).305- Not flagging `THREE_YEAR_LOOKBACK` when `is_existing_policy_transfer == true`.306- Putting CRAT/GRAT numbers from the non-preferred strategy in the preferred-strategy307 fields, or forgetting `projected_charitable_remainder` in the combined estate plan.308- Emitting `action_set` unsorted.309- Rounding intermediate values and propagating error; round only final emitted amounts.