projectionlab-mcp 1.0.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/HOW-IT-WORKS.md +2 -2
- package/MANUAL-SETUP.md +64 -0
- package/README.md +34 -271
- package/REFERENCE.md +221 -0
- package/SETUP-FOR-AGENTS.md +37 -0
- package/dist/bootstrap.js +20 -3
- package/dist/bootstrap.js.map +1 -1
- package/dist/bridge.d.ts +2 -0
- package/dist/clonePlan.d.ts +9 -0
- package/dist/clonePlan.js +15 -14
- package/dist/clonePlan.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.js +41 -6
- package/dist/config.js.map +1 -1
- package/dist/currentFinances.d.ts +12 -1
- package/dist/currentFinances.js +60 -0
- package/dist/currentFinances.js.map +1 -1
- package/dist/dataModel.d.ts +58 -0
- package/dist/dataModel.js +1429 -0
- package/dist/dataModel.js.map +1 -0
- package/dist/deletePlan.d.ts +17 -0
- package/dist/deletePlan.js +72 -0
- package/dist/deletePlan.js.map +1 -0
- package/dist/diff.d.ts +288 -0
- package/dist/diff.js +744 -0
- package/dist/diff.js.map +1 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.js +14 -1
- package/dist/errors.js.map +1 -1
- package/dist/extensionBridge.d.ts +16 -2
- package/dist/extensionBridge.js +104 -23
- package/dist/extensionBridge.js.map +1 -1
- package/dist/index.js +27 -1
- package/dist/index.js.map +1 -1
- package/dist/instructions.d.ts +1 -1
- package/dist/instructions.js +3 -2
- package/dist/instructions.js.map +1 -1
- package/dist/largeOutput.js +25 -0
- package/dist/largeOutput.js.map +1 -1
- package/dist/logger.js +11 -4
- package/dist/logger.js.map +1 -1
- package/dist/pathPatch.d.ts +5 -2
- package/dist/pathPatch.js +16 -2
- package/dist/pathPatch.js.map +1 -1
- package/dist/planSettings.d.ts +1 -1
- package/dist/planSettings.js +3 -3
- package/dist/planSettings.js.map +1 -1
- package/dist/plans.d.ts +73 -10
- package/dist/plans.js +206 -42
- package/dist/plans.js.map +1 -1
- package/dist/prompts/index.js +7 -4
- package/dist/prompts/index.js.map +1 -1
- package/dist/realEstate.d.ts +22 -0
- package/dist/realEstate.js +118 -0
- package/dist/realEstate.js.map +1 -0
- package/dist/redact.d.ts +26 -0
- package/dist/redact.js +93 -1
- package/dist/redact.js.map +1 -1
- package/dist/resources/dataModel.d.ts +3 -0
- package/dist/resources/dataModel.js +66 -0
- package/dist/resources/dataModel.js.map +1 -0
- package/dist/resources/knowledge.js +189 -47
- package/dist/resources/knowledge.js.map +1 -1
- package/dist/resources/planData.js +3 -1
- package/dist/resources/planData.js.map +1 -1
- package/dist/resources/summary.js +4 -1
- package/dist/resources/summary.js.map +1 -1
- package/dist/snapshot.d.ts +21 -0
- package/dist/snapshot.js +116 -12
- package/dist/snapshot.js.map +1 -1
- package/dist/summary.js +19 -6
- package/dist/summary.js.map +1 -1
- package/dist/tools/clonePlan.js +17 -8
- package/dist/tools/clonePlan.js.map +1 -1
- package/dist/tools/connection.js +29 -4
- package/dist/tools/connection.js.map +1 -1
- package/dist/tools/deletePlan.d.ts +4 -0
- package/dist/tools/deletePlan.js +103 -0
- package/dist/tools/deletePlan.js.map +1 -0
- package/dist/tools/diff.d.ts +4 -0
- package/dist/tools/diff.js +417 -0
- package/dist/tools/diff.js.map +1 -0
- package/dist/tools/getCurrentFinances.js +27 -4
- package/dist/tools/getCurrentFinances.js.map +1 -1
- package/dist/tools/getPlan.js +4 -4
- package/dist/tools/getPlan.js.map +1 -1
- package/dist/tools/getPlanSection.js +7 -6
- package/dist/tools/getPlanSection.js.map +1 -1
- package/dist/tools/getPlanSettings.js +10 -6
- package/dist/tools/getPlanSettings.js.map +1 -1
- package/dist/tools/restore.js +18 -1
- package/dist/tools/restore.js.map +1 -1
- package/dist/tools/schema.js +28 -4
- package/dist/tools/schema.js.map +1 -1
- package/dist/tools/snapshots.d.ts +1 -1
- package/dist/tools/snapshots.js +53 -6
- package/dist/tools/snapshots.js.map +1 -1
- package/dist/tools/summary.js +3 -2
- package/dist/tools/summary.js.map +1 -1
- package/dist/tools/updateAccount.js +9 -7
- package/dist/tools/updateAccount.js.map +1 -1
- package/dist/tools/updatePlanEvent.js +71 -33
- package/dist/tools/updatePlanEvent.js.map +1 -1
- package/dist/tools/updatePlanSection.js +17 -32
- package/dist/tools/updatePlanSection.js.map +1 -1
- package/dist/tools/updatePlanSettings.js +11 -7
- package/dist/tools/updatePlanSettings.js.map +1 -1
- package/dist/types/projectionlab.d.ts +3 -1
- package/dist/types/wire.d.ts +27 -2
- package/dist/types/wire.js +6 -2
- package/dist/types/wire.js.map +1 -1
- package/dist/updateCheck.d.ts +22 -0
- package/dist/updateCheck.js +58 -0
- package/dist/updateCheck.js.map +1 -0
- package/dist/versionCompare.d.ts +5 -0
- package/dist/versionCompare.js +33 -0
- package/dist/versionCompare.js.map +1 -0
- package/dist/versionNotices.d.ts +9 -0
- package/dist/versionNotices.js +42 -0
- package/dist/versionNotices.js.map +1 -0
- package/package.json +6 -3
- package/skills/projectionlab/SKILL.md +329 -29
|
@@ -0,0 +1,1429 @@
|
|
|
1
|
+
import { ProjectionLabError } from "./errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* Verified field knowledge for the values this server reads and writes.
|
|
4
|
+
* Single source for the `projectionlab://data-model` resource, the
|
|
5
|
+
* `listPaths` annotations, and the enum refusals in the shared write path.
|
|
6
|
+
*
|
|
7
|
+
* Entries carry evidence:
|
|
8
|
+
* - "observed": a value was set through the UI and the stored value was
|
|
9
|
+
* read back (or matched field-for-field against a known plan).
|
|
10
|
+
* - "ui-label": the control was seen in the UI but not confirmed by a
|
|
11
|
+
* stored-value test.
|
|
12
|
+
* Only fields confirmed against ProjectionLab 4.6.0 are listed; fields known
|
|
13
|
+
* from names alone are left out entirely.
|
|
14
|
+
*/
|
|
15
|
+
export const DATA_MODEL_PROJECTIONLAB_VERSION = "4.6.0";
|
|
16
|
+
const PCT = "percentage points";
|
|
17
|
+
const FRAC = "decimal fraction";
|
|
18
|
+
const DOLLARS = "dollars";
|
|
19
|
+
const YEARS = "years";
|
|
20
|
+
// A one-time event (frequency "once") keeps the end reference it was created
|
|
21
|
+
// with: the UI hides it and the simulation ignores it. Confirmed in the Cash
|
|
22
|
+
// Flow view with one-time inheritances dated after a retirement milestone
|
|
23
|
+
// that their stored end ("exclude" "retirement") would otherwise cut off.
|
|
24
|
+
const ONE_TIME_END_NOTE = 'On a one-time event (frequency "once") the end reference is a leftover default: the UI hides it and the simulation ignores it, so it is not a user setting.';
|
|
25
|
+
function timingFields(prefix) {
|
|
26
|
+
const endNote = prefix === "end" ? ONE_TIME_END_NOTE : undefined;
|
|
27
|
+
const modifierNote = '"include" counts the milestone year itself; "exclude" leaves it out (an end of "exclude" "retirement" stops the year before retirement); an integer means that many years after it.';
|
|
28
|
+
return {
|
|
29
|
+
[`${prefix}.type`]: {
|
|
30
|
+
description: `Kind of the ${prefix} reference: a fixed date, a keyword, or a milestone.`,
|
|
31
|
+
allowedValues: ["date", "keyword", "milestone"],
|
|
32
|
+
evidence: "ui-label",
|
|
33
|
+
note: endNote,
|
|
34
|
+
},
|
|
35
|
+
[`${prefix}.value`]: {
|
|
36
|
+
description: `Target of the ${prefix} reference: "YYYY-MM-DD" when type is "date", a keyword such as "now", "beforeCurrentYear", "endOfPlan", or "never" when type is "keyword", or a built-in milestone keyword such as "retirement" or a milestone ID when type is "milestone".`,
|
|
37
|
+
evidence: "ui-label",
|
|
38
|
+
note: endNote,
|
|
39
|
+
},
|
|
40
|
+
[`${prefix}.modifier`]: {
|
|
41
|
+
description: `Offset applied to a milestone ${prefix} reference.`,
|
|
42
|
+
note: endNote ? `${modifierNote} ${endNote}` : modifierNote,
|
|
43
|
+
evidence: "ui-label",
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const EVENT_IDENTITY = {
|
|
48
|
+
name: { description: "Display label of the event.", evidence: "ui-label" },
|
|
49
|
+
hidden: {
|
|
50
|
+
description: "When true, the event is hidden from the timeline.",
|
|
51
|
+
evidence: "ui-label",
|
|
52
|
+
},
|
|
53
|
+
owner: {
|
|
54
|
+
description: "Which household member the event belongs to.",
|
|
55
|
+
allowedValues: ["me", "spouse", "joint"],
|
|
56
|
+
evidence: "ui-label",
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
const EVENT_MONEY = {
|
|
60
|
+
amount: {
|
|
61
|
+
description: "Amount of the event in the plan's base currency.",
|
|
62
|
+
units: DOLLARS,
|
|
63
|
+
evidence: "ui-label",
|
|
64
|
+
},
|
|
65
|
+
amountType: {
|
|
66
|
+
description: "Currency basis of the amount: real purchasing power, nominal dollars, or grown from today.",
|
|
67
|
+
allowedValues: ["today$", "$", "grow$"],
|
|
68
|
+
evidence: "ui-label",
|
|
69
|
+
note: '"today$" keeps pace with inflation; "$" is a nominal figure.',
|
|
70
|
+
},
|
|
71
|
+
frequency: {
|
|
72
|
+
description: "How often a recurring event repeats.",
|
|
73
|
+
allowedValues: ["yearly", "monthly", "once"],
|
|
74
|
+
evidence: "ui-label",
|
|
75
|
+
},
|
|
76
|
+
repeat: {
|
|
77
|
+
description: "When true, the event repeats on a cadence instead of occurring once.",
|
|
78
|
+
evidence: "ui-label",
|
|
79
|
+
},
|
|
80
|
+
"yearlyChange.type": {
|
|
81
|
+
description: "How the amount changes each year.",
|
|
82
|
+
allowedValues: [
|
|
83
|
+
"none",
|
|
84
|
+
"match-inflation",
|
|
85
|
+
"fixed-rate",
|
|
86
|
+
"fixed-amount",
|
|
87
|
+
"curve",
|
|
88
|
+
],
|
|
89
|
+
evidence: "ui-label",
|
|
90
|
+
},
|
|
91
|
+
"yearlyChange.amount": {
|
|
92
|
+
description: "Magnitude of the yearly change.",
|
|
93
|
+
note: 'Percentage points when yearlyChange.type is "fixed-rate"; a dollar amount when it is "fixed-amount".',
|
|
94
|
+
evidence: "ui-label",
|
|
95
|
+
},
|
|
96
|
+
"yearlyChange.amountType": {
|
|
97
|
+
description: "Currency basis of yearlyChange.amount.",
|
|
98
|
+
allowedValues: ["today$", "$"],
|
|
99
|
+
evidence: "ui-label",
|
|
100
|
+
},
|
|
101
|
+
"yearlyChange.limitEnabled": {
|
|
102
|
+
description: "When true, caps the yearly change at yearlyChange.limit.",
|
|
103
|
+
evidence: "ui-label",
|
|
104
|
+
},
|
|
105
|
+
"yearlyChange.limit": {
|
|
106
|
+
description: "Cap applied when yearlyChange.limitEnabled is true.",
|
|
107
|
+
units: PCT,
|
|
108
|
+
evidence: "ui-label",
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
function sharedEventFields() {
|
|
112
|
+
return {
|
|
113
|
+
...EVENT_IDENTITY,
|
|
114
|
+
...EVENT_MONEY,
|
|
115
|
+
...timingFields("start"),
|
|
116
|
+
...timingFields("end"),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
const EARLY_WITHDRAWAL_FIELDS = {
|
|
120
|
+
hasEWPenalty: {
|
|
121
|
+
description: "When true, early withdrawals before EWAge incur a penalty.",
|
|
122
|
+
evidence: "observed",
|
|
123
|
+
},
|
|
124
|
+
EWPenaltyRate: {
|
|
125
|
+
description: "Penalty charged on early withdrawals.",
|
|
126
|
+
units: PCT,
|
|
127
|
+
enabledBy: "hasEWPenalty",
|
|
128
|
+
note: "Commonly 10 for retirement accounts and 20 for an HSA.",
|
|
129
|
+
evidence: "observed",
|
|
130
|
+
},
|
|
131
|
+
EWAge: {
|
|
132
|
+
description: "Age after which the early withdrawal penalty no longer applies.",
|
|
133
|
+
units: "age",
|
|
134
|
+
enabledBy: "hasEWPenalty",
|
|
135
|
+
evidence: "observed",
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
export const DATA_MODEL_GROUPS = [
|
|
139
|
+
{
|
|
140
|
+
scope: "variables",
|
|
141
|
+
title: "Plan settings: variables (assumptions, tax, estate, flows)",
|
|
142
|
+
fields: {
|
|
143
|
+
assumptionsMode: {
|
|
144
|
+
description: "Return and inflation modeling mode: static baseline rates, a backtested historical sequence, or custom piecewise curves.",
|
|
145
|
+
allowedValues: ["fixed", "historical", "custom"],
|
|
146
|
+
evidence: "observed",
|
|
147
|
+
note: '"historical" enables startYear and loopYear; "custom" enables the *Custom curve objects.',
|
|
148
|
+
},
|
|
149
|
+
inflation: {
|
|
150
|
+
description: "Annual baseline inflation rate applied to living expenses and cost escalations.",
|
|
151
|
+
units: PCT,
|
|
152
|
+
evidence: "observed",
|
|
153
|
+
note: "Write 3.25 for 3.25%, not 0.0325.",
|
|
154
|
+
},
|
|
155
|
+
investmentReturn: {
|
|
156
|
+
description: "Expected annual capital appreciation of stock equity investments.",
|
|
157
|
+
units: PCT,
|
|
158
|
+
evidence: "observed",
|
|
159
|
+
note: "Write 5 for 5%.",
|
|
160
|
+
},
|
|
161
|
+
dividendRate: {
|
|
162
|
+
description: "Annual dividend yield on stock equity investments.",
|
|
163
|
+
units: PCT,
|
|
164
|
+
evidence: "observed",
|
|
165
|
+
},
|
|
166
|
+
bondInvestmentReturn: {
|
|
167
|
+
description: "Capital appreciation rate of fixed-income holdings.",
|
|
168
|
+
units: PCT,
|
|
169
|
+
evidence: "observed",
|
|
170
|
+
},
|
|
171
|
+
bondDividendRate: {
|
|
172
|
+
description: "Annual coupon or interest yield on bond holdings.",
|
|
173
|
+
units: PCT,
|
|
174
|
+
evidence: "observed",
|
|
175
|
+
},
|
|
176
|
+
benefitColaModifier: {
|
|
177
|
+
description: "Adjustment to the cost-of-living raise applied to Social Security benefits relative to baseline inflation.",
|
|
178
|
+
units: PCT,
|
|
179
|
+
evidence: "ui-label",
|
|
180
|
+
note: "0 applies baseline inflation unchanged.",
|
|
181
|
+
},
|
|
182
|
+
startYear: {
|
|
183
|
+
description: "Historical calendar year that anchors the projection start.",
|
|
184
|
+
units: "calendar year",
|
|
185
|
+
enabledBy: 'assumptionsMode "historical"',
|
|
186
|
+
evidence: "ui-label",
|
|
187
|
+
},
|
|
188
|
+
loopYear: {
|
|
189
|
+
description: "Historical year to jump back to when the projection outlasts the historical data range.",
|
|
190
|
+
units: "calendar year",
|
|
191
|
+
enabledBy: 'assumptionsMode "historical"',
|
|
192
|
+
evidence: "ui-label",
|
|
193
|
+
},
|
|
194
|
+
"dividendSettings.reinvestment.enabled": {
|
|
195
|
+
description: "When true, stock dividends are reinvested into the originating account instead of paid out.",
|
|
196
|
+
evidence: "ui-label",
|
|
197
|
+
},
|
|
198
|
+
"dividendSettings.composition.qualifiedPercent": {
|
|
199
|
+
description: "Share of stock dividends taxed at preferential capital gains rates; the remainder is ordinary income.",
|
|
200
|
+
units: PCT,
|
|
201
|
+
evidence: "ui-label",
|
|
202
|
+
},
|
|
203
|
+
"dividendSettings.composition.country": {
|
|
204
|
+
description: "Tax jurisdiction country as an ISO country string.",
|
|
205
|
+
evidence: "ui-label",
|
|
206
|
+
},
|
|
207
|
+
"bondSettings.allocationType": {
|
|
208
|
+
description: "How the target portfolio bond allocation is modeled.",
|
|
209
|
+
allowedValues: ["none", "target", "custom"],
|
|
210
|
+
evidence: "observed",
|
|
211
|
+
},
|
|
212
|
+
"bondSettings.allocation": {
|
|
213
|
+
description: "Timeline coordinate points defining the target bond allocation percentage over time.",
|
|
214
|
+
evidence: "ui-label",
|
|
215
|
+
note: "Array of { x, y } points where y is a percentage (0-100).",
|
|
216
|
+
},
|
|
217
|
+
"bondSettings.location.type": {
|
|
218
|
+
description: "How target bonds are placed across individual accounts.",
|
|
219
|
+
allowedValues: ["evenly", "account-order"],
|
|
220
|
+
evidence: "observed",
|
|
221
|
+
note: '"account-order" uses bondSettings.location.accountOrder.',
|
|
222
|
+
},
|
|
223
|
+
"bondSettings.location.accountOrder": {
|
|
224
|
+
description: "Ordered account UUIDs giving the priority in which accounts are filled with bonds.",
|
|
225
|
+
enabledBy: 'bondSettings.location.type "account-order"',
|
|
226
|
+
evidence: "observed",
|
|
227
|
+
},
|
|
228
|
+
"bondSettings.composition.municipalPercent": {
|
|
229
|
+
description: "Share of bond income that is federally tax-exempt municipal income; the remainder is fully taxable.",
|
|
230
|
+
units: PCT,
|
|
231
|
+
evidence: "ui-label",
|
|
232
|
+
},
|
|
233
|
+
"bondSettings.composition.treasuryPercent": {
|
|
234
|
+
description: "Share of bond income that is state tax-exempt treasury income.",
|
|
235
|
+
units: PCT,
|
|
236
|
+
evidence: "ui-label",
|
|
237
|
+
},
|
|
238
|
+
estimateTaxes: {
|
|
239
|
+
description: "Enables or disables algorithmic tax estimation for the plan.",
|
|
240
|
+
evidence: "ui-label",
|
|
241
|
+
},
|
|
242
|
+
filingStatus: {
|
|
243
|
+
description: "Tax filing status used by tax estimation.",
|
|
244
|
+
allowedValues: ["single", "joint", "head-of-household", "separate"],
|
|
245
|
+
evidence: "ui-label",
|
|
246
|
+
},
|
|
247
|
+
"withholding.taxDeferred": {
|
|
248
|
+
description: "Rate withheld at tax-deferred withdrawal to model interim cash flow before annual tax reconciliation.",
|
|
249
|
+
units: PCT,
|
|
250
|
+
evidence: "observed",
|
|
251
|
+
},
|
|
252
|
+
"withholding.taxable": {
|
|
253
|
+
description: "Rate withheld at taxable withdrawal.",
|
|
254
|
+
units: PCT,
|
|
255
|
+
evidence: "observed",
|
|
256
|
+
},
|
|
257
|
+
"withholding.conversions": {
|
|
258
|
+
description: "Rate withheld at Roth conversions.",
|
|
259
|
+
units: PCT,
|
|
260
|
+
evidence: "observed",
|
|
261
|
+
},
|
|
262
|
+
bbbSaltReversion: {
|
|
263
|
+
description: "When true, assumes the increased SALT deduction cap expires on its statutory schedule.",
|
|
264
|
+
evidence: "observed",
|
|
265
|
+
},
|
|
266
|
+
bbbSeniorReversion: {
|
|
267
|
+
description: "When true, assumes the senior bonus deduction expires on its statutory schedule.",
|
|
268
|
+
evidence: "ui-label",
|
|
269
|
+
},
|
|
270
|
+
incomeTaxModifier: {
|
|
271
|
+
description: "Shifts statutory ordinary income bracket rates up or down across the board.",
|
|
272
|
+
units: PCT,
|
|
273
|
+
evidence: "ui-label",
|
|
274
|
+
},
|
|
275
|
+
capGainsModifier: {
|
|
276
|
+
description: "Shifts statutory capital gains rates up or down across the board.",
|
|
277
|
+
units: PCT,
|
|
278
|
+
evidence: "ui-label",
|
|
279
|
+
},
|
|
280
|
+
"taxStrategy.type": {
|
|
281
|
+
description: "Tax strategy mode: fill a target bracket, optimize, or compare.",
|
|
282
|
+
allowedValues: ["bracket", "optimize", "compare"],
|
|
283
|
+
evidence: "ui-label",
|
|
284
|
+
},
|
|
285
|
+
"taxStrategy.bracketRate": {
|
|
286
|
+
description: "Marginal ordinary income bracket to fill with strategic Roth conversions.",
|
|
287
|
+
units: FRAC,
|
|
288
|
+
evidence: "observed",
|
|
289
|
+
note: "Write 0.22 for 22%, not 22. Typical choices: 0.1, 0.12, 0.22, 0.24, 0.32, 0.35.",
|
|
290
|
+
},
|
|
291
|
+
"taxStrategy.capGainsBracketRate": {
|
|
292
|
+
description: "Capital gains bracket to stay within.",
|
|
293
|
+
units: FRAC,
|
|
294
|
+
evidence: "observed",
|
|
295
|
+
note: "Write 0.15 for the 15% bracket or 0 for the tax-free threshold.",
|
|
296
|
+
},
|
|
297
|
+
"taxStrategy.niitCeiling": {
|
|
298
|
+
description: "When true, caps investment income below the net investment income tax threshold.",
|
|
299
|
+
evidence: "observed",
|
|
300
|
+
},
|
|
301
|
+
"taxStrategy.irmaaCeiling": {
|
|
302
|
+
description: "When true, caps MAGI below Medicare premium surcharge cliffs.",
|
|
303
|
+
evidence: "observed",
|
|
304
|
+
},
|
|
305
|
+
"taxStrategy.irmaaCliffIndex": {
|
|
306
|
+
description: "Which Medicare premium surcharge cliff to stay below.",
|
|
307
|
+
units: "count",
|
|
308
|
+
enabledBy: "taxStrategy.irmaaCeiling",
|
|
309
|
+
note: "0 is the first cliff (no surcharges); higher indexes are later cliffs.",
|
|
310
|
+
evidence: "observed",
|
|
311
|
+
},
|
|
312
|
+
"taxStrategy.acaCeiling": {
|
|
313
|
+
description: "When true, caps MAGI to protect ACA premium tax credits.",
|
|
314
|
+
evidence: "observed",
|
|
315
|
+
},
|
|
316
|
+
"taxStrategy.federalPovertyLevelMultiple": {
|
|
317
|
+
description: "Federal poverty level multiplier the ACA subsidy cap targets.",
|
|
318
|
+
units: "count",
|
|
319
|
+
enabledBy: "taxStrategy.acaCeiling",
|
|
320
|
+
note: "An integer multiplier: 4 means 400% of the federal poverty level.",
|
|
321
|
+
evidence: "observed",
|
|
322
|
+
},
|
|
323
|
+
"rothConversionStrategy.enabled": {
|
|
324
|
+
description: "Enables strategic Roth conversions.",
|
|
325
|
+
evidence: "ui-label",
|
|
326
|
+
},
|
|
327
|
+
"gainHarvestingStrategy.enabled": {
|
|
328
|
+
description: "Enables systematic harvesting within the zero capital gains bracket.",
|
|
329
|
+
evidence: "ui-label",
|
|
330
|
+
},
|
|
331
|
+
"drawdownBlendingStrategy.enabled": {
|
|
332
|
+
description: "Enables withdrawal shielding and blended account decumulation.",
|
|
333
|
+
evidence: "ui-label",
|
|
334
|
+
},
|
|
335
|
+
drawdownOrder: {
|
|
336
|
+
description: "Ordered account categories used to cover cash deficits, liquidated front to back.",
|
|
337
|
+
evidence: "ui-label",
|
|
338
|
+
note: 'Elements include "excess-cash", "taxable", "hsa-medical", "ira/401k", "free-roth-ira-contributions", "roth-ira", "non-qualified-ira/401k", "non-qualified-roth", "non-qualified-hsa", "all-cash".',
|
|
339
|
+
},
|
|
340
|
+
"estate.taxDeferredRate": {
|
|
341
|
+
description: "Ordinary income rate heirs pay on inherited pre-tax accounts.",
|
|
342
|
+
units: PCT,
|
|
343
|
+
evidence: "ui-label",
|
|
344
|
+
},
|
|
345
|
+
"estate.taxableGainsRate": {
|
|
346
|
+
description: "Capital gains rate on taxable accounts when no basis step-up is assumed.",
|
|
347
|
+
units: PCT,
|
|
348
|
+
enabledBy: 'estate.stepUpBasis false',
|
|
349
|
+
evidence: "ui-label",
|
|
350
|
+
},
|
|
351
|
+
"estate.stepUpBasis": {
|
|
352
|
+
description: "When true, taxable holdings are assumed to receive a full basis step-up at death.",
|
|
353
|
+
evidence: "ui-label",
|
|
354
|
+
},
|
|
355
|
+
"estate.assetLiquidationFee": {
|
|
356
|
+
description: "Transaction, legal, and broker fees on liquidating real property.",
|
|
357
|
+
units: PCT,
|
|
358
|
+
evidence: "ui-label",
|
|
359
|
+
},
|
|
360
|
+
"estate.charityRate": {
|
|
361
|
+
description: "Share of the gross estate bequeathed to charity.",
|
|
362
|
+
units: PCT,
|
|
363
|
+
evidence: "ui-label",
|
|
364
|
+
},
|
|
365
|
+
"estate.adminRate": {
|
|
366
|
+
description: "Probate, legal, and executor settlement costs.",
|
|
367
|
+
units: PCT,
|
|
368
|
+
evidence: "ui-label",
|
|
369
|
+
},
|
|
370
|
+
cashFlowDefault: {
|
|
371
|
+
description: "What happens to leftover annual cash after all priorities are funded.",
|
|
372
|
+
allowedValues: ["save", "spend"],
|
|
373
|
+
evidence: "observed",
|
|
374
|
+
note: '"save" adds it to cash holdings; "spend" treats it as discretionary spending.',
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
scope: "montecarlo",
|
|
380
|
+
title: "Plan settings: montecarlo (Chance of Success simulation)",
|
|
381
|
+
fields: {
|
|
382
|
+
trials: {
|
|
383
|
+
description: "Number of simulated trial paths to run.",
|
|
384
|
+
units: "count",
|
|
385
|
+
evidence: "observed",
|
|
386
|
+
},
|
|
387
|
+
iterations: {
|
|
388
|
+
description: "Concurrent simulation worker tasks.",
|
|
389
|
+
units: "count",
|
|
390
|
+
evidence: "ui-label",
|
|
391
|
+
},
|
|
392
|
+
sampling: {
|
|
393
|
+
description: "How historical market data is sampled for each trial.",
|
|
394
|
+
allowedValues: [
|
|
395
|
+
"random-start-random-restart",
|
|
396
|
+
"block-bootstrap",
|
|
397
|
+
"random-year",
|
|
398
|
+
],
|
|
399
|
+
evidence: "observed",
|
|
400
|
+
note: '"block-bootstrap" samples blocks of blockSize consecutive years.',
|
|
401
|
+
},
|
|
402
|
+
blockSize: {
|
|
403
|
+
description: "Length of the consecutive-year blocks sampled per trial.",
|
|
404
|
+
units: YEARS,
|
|
405
|
+
enabledBy: 'sampling "block-bootstrap"',
|
|
406
|
+
evidence: "ui-label",
|
|
407
|
+
},
|
|
408
|
+
mode: {
|
|
409
|
+
description: "Data sources feeding the simulation.",
|
|
410
|
+
allowedValues: ["backtest", "custom", "blended"],
|
|
411
|
+
evidence: "ui-label",
|
|
412
|
+
},
|
|
413
|
+
trialMetric: {
|
|
414
|
+
description: "Metric that decides success or failure of each trial.",
|
|
415
|
+
evidence: "ui-label",
|
|
416
|
+
note: 'Typically "netWorth".',
|
|
417
|
+
},
|
|
418
|
+
statsMetric: {
|
|
419
|
+
description: "Metric plotted as the primary percentile-band distribution.",
|
|
420
|
+
evidence: "ui-label",
|
|
421
|
+
note: 'Typically "liquidNetWorth".',
|
|
422
|
+
},
|
|
423
|
+
metrics: {
|
|
424
|
+
description: "Metrics monitored during the simulation.",
|
|
425
|
+
evidence: "ui-label",
|
|
426
|
+
note: 'Array of strings such as "netWorth", "liquidNetWorth", "expenses", "taxes", "effectiveTaxRate", "savingsRate", "wr".',
|
|
427
|
+
},
|
|
428
|
+
investmentReturn: {
|
|
429
|
+
description: "Source of the stock growth rate distribution.",
|
|
430
|
+
allowedValues: ["custom", "backtest"],
|
|
431
|
+
evidence: "observed",
|
|
432
|
+
},
|
|
433
|
+
investmentReturnMean: {
|
|
434
|
+
description: "Mean annual stock growth used when sampling is custom.",
|
|
435
|
+
units: PCT,
|
|
436
|
+
enabledBy: 'investmentReturn "custom"',
|
|
437
|
+
evidence: "observed",
|
|
438
|
+
},
|
|
439
|
+
investmentReturnStdDev: {
|
|
440
|
+
description: "Standard deviation of annual stock growth.",
|
|
441
|
+
units: PCT,
|
|
442
|
+
enabledBy: 'investmentReturn "custom"',
|
|
443
|
+
evidence: "observed",
|
|
444
|
+
},
|
|
445
|
+
dividendRate: {
|
|
446
|
+
description: "Source of the stock dividend distribution.",
|
|
447
|
+
allowedValues: ["custom", "backtest"],
|
|
448
|
+
evidence: "observed",
|
|
449
|
+
},
|
|
450
|
+
dividendRateMean: {
|
|
451
|
+
description: "Mean annual dividend yield used when sampling is custom.",
|
|
452
|
+
units: PCT,
|
|
453
|
+
enabledBy: 'dividendRate "custom"',
|
|
454
|
+
evidence: "observed",
|
|
455
|
+
},
|
|
456
|
+
dividendRateStdDev: {
|
|
457
|
+
description: "Standard deviation of the annual dividend yield.",
|
|
458
|
+
units: PCT,
|
|
459
|
+
enabledBy: 'dividendRate "custom"',
|
|
460
|
+
evidence: "observed",
|
|
461
|
+
},
|
|
462
|
+
inflation: {
|
|
463
|
+
description: "Source of the inflation distribution.",
|
|
464
|
+
allowedValues: ["custom", "backtest"],
|
|
465
|
+
evidence: "observed",
|
|
466
|
+
},
|
|
467
|
+
inflationMean: {
|
|
468
|
+
description: "Mean annual inflation used when sampling is custom.",
|
|
469
|
+
units: PCT,
|
|
470
|
+
enabledBy: 'inflation "custom"',
|
|
471
|
+
evidence: "observed",
|
|
472
|
+
},
|
|
473
|
+
inflationStdDev: {
|
|
474
|
+
description: "Standard deviation of annual inflation.",
|
|
475
|
+
units: PCT,
|
|
476
|
+
enabledBy: 'inflation "custom"',
|
|
477
|
+
evidence: "observed",
|
|
478
|
+
},
|
|
479
|
+
bondReturn: {
|
|
480
|
+
description: "Source of the bond return distribution.",
|
|
481
|
+
allowedValues: ["custom", "backtest"],
|
|
482
|
+
evidence: "observed",
|
|
483
|
+
},
|
|
484
|
+
bondReturnMean: {
|
|
485
|
+
description: "Mean annual bond return used when sampling is custom.",
|
|
486
|
+
units: PCT,
|
|
487
|
+
enabledBy: 'bondReturn "custom"',
|
|
488
|
+
evidence: "observed",
|
|
489
|
+
},
|
|
490
|
+
bondReturnStdDev: {
|
|
491
|
+
description: "Standard deviation of the annual bond return.",
|
|
492
|
+
units: PCT,
|
|
493
|
+
enabledBy: 'bondReturn "custom"',
|
|
494
|
+
evidence: "observed",
|
|
495
|
+
},
|
|
496
|
+
cryptoReturn: {
|
|
497
|
+
description: "Source of the crypto return distribution.",
|
|
498
|
+
allowedValues: ["custom", "backtest"],
|
|
499
|
+
evidence: "observed",
|
|
500
|
+
},
|
|
501
|
+
cryptoReturnMean: {
|
|
502
|
+
description: "Mean annual crypto return used when sampling is custom.",
|
|
503
|
+
units: PCT,
|
|
504
|
+
enabledBy: 'cryptoReturn "custom"',
|
|
505
|
+
evidence: "observed",
|
|
506
|
+
},
|
|
507
|
+
cryptoReturnStdDev: {
|
|
508
|
+
description: "Standard deviation of the annual crypto return.",
|
|
509
|
+
units: PCT,
|
|
510
|
+
enabledBy: 'cryptoReturn "custom"',
|
|
511
|
+
evidence: "observed",
|
|
512
|
+
},
|
|
513
|
+
},
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
scope: "withdrawalStrategy",
|
|
517
|
+
title: "Plan settings: withdrawalStrategy (textbook withdrawals)",
|
|
518
|
+
fields: {
|
|
519
|
+
enabled: {
|
|
520
|
+
description: "When true, overrides standard plan discretionary events and applies the active withdrawal rule from start onward.",
|
|
521
|
+
evidence: "observed",
|
|
522
|
+
},
|
|
523
|
+
strategy: {
|
|
524
|
+
description: "Active withdrawal rule.",
|
|
525
|
+
allowedValues: [
|
|
526
|
+
"initial-%",
|
|
527
|
+
"fixed-%",
|
|
528
|
+
"fixed-amount",
|
|
529
|
+
"kitces-ratchet",
|
|
530
|
+
"vpw",
|
|
531
|
+
"1/N",
|
|
532
|
+
"clyatt-95%",
|
|
533
|
+
"guyton-klinger",
|
|
534
|
+
],
|
|
535
|
+
evidence: "observed",
|
|
536
|
+
note: 'The parameter block for the active rule lives under a key of the same name, e.g. "fixed-%.amount".',
|
|
537
|
+
},
|
|
538
|
+
"start.type": {
|
|
539
|
+
description: "Kind of the begin-strategy reference.",
|
|
540
|
+
allowedValues: ["milestone"],
|
|
541
|
+
evidence: "ui-label",
|
|
542
|
+
},
|
|
543
|
+
"start.value": {
|
|
544
|
+
description: "Milestone ID at which the strategy begins.",
|
|
545
|
+
evidence: "ui-label",
|
|
546
|
+
},
|
|
547
|
+
"start.modifier": {
|
|
548
|
+
description: "Offset applied to the begin-strategy milestone.",
|
|
549
|
+
evidence: "ui-label",
|
|
550
|
+
},
|
|
551
|
+
income: {
|
|
552
|
+
description: "How planned income interacts with the withdrawal amount.",
|
|
553
|
+
allowedValues: ["decrease-drawdown", "add-to-drawdown", "ignore"],
|
|
554
|
+
evidence: "ui-label",
|
|
555
|
+
},
|
|
556
|
+
spendMode: {
|
|
557
|
+
description: 'Whether the strategy targets net withdrawals ("withdraw", spend is reduced when taxes are incurred) or gross annual spend ("spend", withdrawals are grossed up to cover taxes).',
|
|
558
|
+
allowedValues: ["withdraw", "spend"],
|
|
559
|
+
evidence: "ui-label",
|
|
560
|
+
},
|
|
561
|
+
"initial-%.amount": {
|
|
562
|
+
description: "Initial withdrawal rate of the starting portfolio.",
|
|
563
|
+
units: PCT,
|
|
564
|
+
evidence: "ui-label",
|
|
565
|
+
},
|
|
566
|
+
"initial-%.minType": {
|
|
567
|
+
description: "Basis of the minimum drawdown bound.",
|
|
568
|
+
allowedValues: ["today$", "future$", "%"],
|
|
569
|
+
evidence: "ui-label",
|
|
570
|
+
},
|
|
571
|
+
"initial-%.maxType": {
|
|
572
|
+
description: "Basis of the maximum drawdown bound.",
|
|
573
|
+
allowedValues: ["today$", "future$", "%"],
|
|
574
|
+
evidence: "ui-label",
|
|
575
|
+
},
|
|
576
|
+
"fixed-%.amount": {
|
|
577
|
+
description: "Withdrawal rate of the current portfolio balance each year.",
|
|
578
|
+
units: PCT,
|
|
579
|
+
evidence: "ui-label",
|
|
580
|
+
},
|
|
581
|
+
"fixed-%.minType": {
|
|
582
|
+
description: "Basis of the minimum drawdown bound.",
|
|
583
|
+
allowedValues: ["today$", "future$", "%"],
|
|
584
|
+
evidence: "ui-label",
|
|
585
|
+
},
|
|
586
|
+
"fixed-%.maxType": {
|
|
587
|
+
description: "Basis of the maximum drawdown bound.",
|
|
588
|
+
allowedValues: ["today$", "future$", "%"],
|
|
589
|
+
evidence: "ui-label",
|
|
590
|
+
},
|
|
591
|
+
"fixed-amount.amount": {
|
|
592
|
+
description: "Fixed dollar amount withdrawn each year.",
|
|
593
|
+
units: DOLLARS,
|
|
594
|
+
evidence: "ui-label",
|
|
595
|
+
},
|
|
596
|
+
"fixed-amount.amountType": {
|
|
597
|
+
description: "Currency basis of the fixed withdrawal amount.",
|
|
598
|
+
allowedValues: ["today$", "future$"],
|
|
599
|
+
evidence: "ui-label",
|
|
600
|
+
},
|
|
601
|
+
"fixed-amount.adjust": {
|
|
602
|
+
description: "When true, adjusts the fixed amount for inflation.",
|
|
603
|
+
evidence: "ui-label",
|
|
604
|
+
},
|
|
605
|
+
"kitces-ratchet.amount": {
|
|
606
|
+
description: "Initial withdrawal rate of the current portfolio.",
|
|
607
|
+
units: PCT,
|
|
608
|
+
evidence: "ui-label",
|
|
609
|
+
},
|
|
610
|
+
"kitces-ratchet.threshold": {
|
|
611
|
+
description: "Portfolio change that triggers a ratchet adjustment.",
|
|
612
|
+
units: PCT,
|
|
613
|
+
evidence: "ui-label",
|
|
614
|
+
},
|
|
615
|
+
"kitces-ratchet.ratchet": {
|
|
616
|
+
description: "Adjustment applied to the withdrawal when a ratchet fires.",
|
|
617
|
+
units: PCT,
|
|
618
|
+
evidence: "ui-label",
|
|
619
|
+
},
|
|
620
|
+
"kitces-ratchet.cooldown": {
|
|
621
|
+
description: "Years before the ratchet may adjust again.",
|
|
622
|
+
units: YEARS,
|
|
623
|
+
evidence: "ui-label",
|
|
624
|
+
},
|
|
625
|
+
"clyatt-95%.amount": {
|
|
626
|
+
description: "Initial withdrawal rate of the current portfolio.",
|
|
627
|
+
units: PCT,
|
|
628
|
+
evidence: "ui-label",
|
|
629
|
+
},
|
|
630
|
+
"clyatt-95%.percentOfPrevious": {
|
|
631
|
+
description: "Share of the previous year's withdrawal taken when the portfolio fell.",
|
|
632
|
+
units: PCT,
|
|
633
|
+
evidence: "ui-label",
|
|
634
|
+
note: "95 implements the 95% rule.",
|
|
635
|
+
},
|
|
636
|
+
"guyton-klinger.amount": {
|
|
637
|
+
description: "Initial withdrawal rate of the starting portfolio.",
|
|
638
|
+
units: PCT,
|
|
639
|
+
evidence: "ui-label",
|
|
640
|
+
},
|
|
641
|
+
"guyton-klinger.guardrail": {
|
|
642
|
+
description: "Deviation from the initial rate that trips a guardrail.",
|
|
643
|
+
units: PCT,
|
|
644
|
+
evidence: "ui-label",
|
|
645
|
+
},
|
|
646
|
+
"guyton-klinger.adjustment": {
|
|
647
|
+
description: "Adjustment applied to the withdrawal when a guardrail trips.",
|
|
648
|
+
units: PCT,
|
|
649
|
+
evidence: "ui-label",
|
|
650
|
+
},
|
|
651
|
+
},
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
scope: "income",
|
|
655
|
+
title: "Income events",
|
|
656
|
+
fields: {
|
|
657
|
+
...sharedEventFields(),
|
|
658
|
+
type: {
|
|
659
|
+
description: "Kind of income event.",
|
|
660
|
+
allowedValues: [
|
|
661
|
+
"salary",
|
|
662
|
+
"hourly",
|
|
663
|
+
"rsu",
|
|
664
|
+
"inheritance",
|
|
665
|
+
"side-hustle",
|
|
666
|
+
"tax-credit",
|
|
667
|
+
"tax-deduction",
|
|
668
|
+
"pension",
|
|
669
|
+
"social-security",
|
|
670
|
+
"old-age-security",
|
|
671
|
+
"canada-pension-plan",
|
|
672
|
+
"other",
|
|
673
|
+
],
|
|
674
|
+
evidence: "ui-label",
|
|
675
|
+
},
|
|
676
|
+
taxCharacter: {
|
|
677
|
+
description: "How the income is characterized for tax.",
|
|
678
|
+
allowedValues: ["auto", "ordinary", "capital-gains"],
|
|
679
|
+
evidence: "ui-label",
|
|
680
|
+
},
|
|
681
|
+
withholdingMode: {
|
|
682
|
+
description: "Whether tax withholding is estimated automatically or customized.",
|
|
683
|
+
allowedValues: ["auto", "custom"],
|
|
684
|
+
evidence: "ui-label",
|
|
685
|
+
},
|
|
686
|
+
taxExempt: {
|
|
687
|
+
description: "When true, the income is exempt from tax.",
|
|
688
|
+
evidence: "ui-label",
|
|
689
|
+
},
|
|
690
|
+
goPartTime: {
|
|
691
|
+
description: "When true, models a phased transition to part-time work.",
|
|
692
|
+
evidence: "ui-label",
|
|
693
|
+
},
|
|
694
|
+
partTimeRate: {
|
|
695
|
+
description: "Share of full salary earned while part-time.",
|
|
696
|
+
units: PCT,
|
|
697
|
+
enabledBy: "goPartTime",
|
|
698
|
+
note: "50 means half salary.",
|
|
699
|
+
evidence: "ui-label",
|
|
700
|
+
},
|
|
701
|
+
hasPension: {
|
|
702
|
+
description: "When true, employer-sponsored defined-benefit pension accrual is modeled.",
|
|
703
|
+
evidence: "ui-label",
|
|
704
|
+
},
|
|
705
|
+
pensionContributionType: {
|
|
706
|
+
description: "Basis of the pension contribution.",
|
|
707
|
+
allowedValues: ["%", "$"],
|
|
708
|
+
enabledBy: "hasPension",
|
|
709
|
+
evidence: "ui-label",
|
|
710
|
+
},
|
|
711
|
+
pensionPayoutType: {
|
|
712
|
+
description: "Basis of the pension payout.",
|
|
713
|
+
allowedValues: ["fap", "average", "today$", "$"],
|
|
714
|
+
enabledBy: "hasPension",
|
|
715
|
+
evidence: "ui-label",
|
|
716
|
+
note: '"fap" is a percentage of final pay; "average" a percentage of career average pay.',
|
|
717
|
+
},
|
|
718
|
+
pensionPayoutRate: {
|
|
719
|
+
description: "Pension payout as a percentage figure.",
|
|
720
|
+
units: PCT,
|
|
721
|
+
enabledBy: "hasPension",
|
|
722
|
+
evidence: "ui-label",
|
|
723
|
+
},
|
|
724
|
+
hoursPerWeek: {
|
|
725
|
+
description: "Hours worked per week (hourly wage events).",
|
|
726
|
+
units: "count",
|
|
727
|
+
evidence: "ui-label",
|
|
728
|
+
},
|
|
729
|
+
estimateIncome: {
|
|
730
|
+
description: "When true, the Social Security benefit is estimated from earnings history; when false, amount is entered manually.",
|
|
731
|
+
evidence: "observed",
|
|
732
|
+
},
|
|
733
|
+
primaryInsuranceAmount: {
|
|
734
|
+
description: "Monthly benefit at full retirement age used by the automatic estimate.",
|
|
735
|
+
units: DOLLARS,
|
|
736
|
+
enabledBy: "estimateIncome",
|
|
737
|
+
evidence: "observed",
|
|
738
|
+
},
|
|
739
|
+
expectedPercent: {
|
|
740
|
+
description: "Share of the statutory benefit expected to be paid.",
|
|
741
|
+
units: PCT,
|
|
742
|
+
enabledBy: "estimateIncome",
|
|
743
|
+
note: "100 assumes full benefits; a lower figure models trust fund exhaustion.",
|
|
744
|
+
evidence: "observed",
|
|
745
|
+
},
|
|
746
|
+
refundable: {
|
|
747
|
+
description: "When true, a tax credit is refundable beyond the tax owed.",
|
|
748
|
+
evidence: "ui-label",
|
|
749
|
+
},
|
|
750
|
+
},
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
scope: "expenses",
|
|
754
|
+
title: "Expense events",
|
|
755
|
+
fields: {
|
|
756
|
+
...sharedEventFields(),
|
|
757
|
+
type: {
|
|
758
|
+
description: "Kind of expense event.",
|
|
759
|
+
allowedValues: [
|
|
760
|
+
"living-expenses",
|
|
761
|
+
"rent",
|
|
762
|
+
"debt",
|
|
763
|
+
"dependent-support",
|
|
764
|
+
"education",
|
|
765
|
+
"health-care",
|
|
766
|
+
"medicare",
|
|
767
|
+
"vacation",
|
|
768
|
+
"wedding",
|
|
769
|
+
"charity",
|
|
770
|
+
"travel",
|
|
771
|
+
"medical",
|
|
772
|
+
"emergency",
|
|
773
|
+
"other",
|
|
774
|
+
],
|
|
775
|
+
evidence: "ui-label",
|
|
776
|
+
},
|
|
777
|
+
spendingType: {
|
|
778
|
+
description: "Whether the expense is essential or discretionary spending.",
|
|
779
|
+
allowedValues: ["essential", "discretionary"],
|
|
780
|
+
evidence: "observed",
|
|
781
|
+
},
|
|
782
|
+
healthcareType: {
|
|
783
|
+
description: "How health insurance premiums are modeled.",
|
|
784
|
+
allowedValues: ["standard", "out-of-pocket", "aca", "employer"],
|
|
785
|
+
evidence: "observed",
|
|
786
|
+
},
|
|
787
|
+
supplementalType: {
|
|
788
|
+
description: "Medicare supplemental coverage.",
|
|
789
|
+
allowedValues: ["none", "medigap", "advantage"],
|
|
790
|
+
evidence: "observed",
|
|
791
|
+
},
|
|
792
|
+
partAPremium: {
|
|
793
|
+
description: "Medicare Part A monthly premium.",
|
|
794
|
+
units: DOLLARS,
|
|
795
|
+
evidence: "observed",
|
|
796
|
+
},
|
|
797
|
+
partBPremium: {
|
|
798
|
+
description: "Medicare Part B monthly premium.",
|
|
799
|
+
units: DOLLARS,
|
|
800
|
+
evidence: "observed",
|
|
801
|
+
},
|
|
802
|
+
partDPremium: {
|
|
803
|
+
description: "Medicare Part D monthly premium.",
|
|
804
|
+
units: DOLLARS,
|
|
805
|
+
evidence: "observed",
|
|
806
|
+
},
|
|
807
|
+
supplementalPremium: {
|
|
808
|
+
description: "Medicare supplemental plan monthly premium.",
|
|
809
|
+
units: DOLLARS,
|
|
810
|
+
enabledBy: 'supplementalType "medigap" or "advantage"',
|
|
811
|
+
evidence: "observed",
|
|
812
|
+
},
|
|
813
|
+
healthcareInflationModifier: {
|
|
814
|
+
description: "Medical cost inflation above general inflation.",
|
|
815
|
+
units: PCT,
|
|
816
|
+
evidence: "observed",
|
|
817
|
+
},
|
|
818
|
+
fundWithAccounts: {
|
|
819
|
+
description: "Account IDs prioritized to pay this expense, such as an HSA for health care or a 529 for education.",
|
|
820
|
+
evidence: "ui-label",
|
|
821
|
+
},
|
|
822
|
+
deductFromIncomeId: {
|
|
823
|
+
description: "Income event ID whose pay models pre-tax payroll deductions for this expense.",
|
|
824
|
+
evidence: "ui-label",
|
|
825
|
+
},
|
|
826
|
+
monthlyPayment: {
|
|
827
|
+
description: "Scheduled monthly payment for a debt expense.",
|
|
828
|
+
units: DOLLARS,
|
|
829
|
+
evidence: "ui-label",
|
|
830
|
+
},
|
|
831
|
+
monthlyPaymentType: {
|
|
832
|
+
description: "Currency basis of monthlyPayment.",
|
|
833
|
+
allowedValues: ["today$", "$"],
|
|
834
|
+
evidence: "ui-label",
|
|
835
|
+
},
|
|
836
|
+
interestRate: {
|
|
837
|
+
description: "Annual percentage rate of a debt expense.",
|
|
838
|
+
units: PCT,
|
|
839
|
+
evidence: "ui-label",
|
|
840
|
+
},
|
|
841
|
+
interestType: {
|
|
842
|
+
description: "How interest accrues on a debt expense.",
|
|
843
|
+
allowedValues: ["simple", "compound"],
|
|
844
|
+
evidence: "ui-label",
|
|
845
|
+
},
|
|
846
|
+
compounding: {
|
|
847
|
+
description: "Frequency at which interest compounds.",
|
|
848
|
+
allowedValues: ["monthly", "yearly"],
|
|
849
|
+
enabledBy: 'interestType "compound"',
|
|
850
|
+
evidence: "ui-label",
|
|
851
|
+
},
|
|
852
|
+
hasForgiveness: {
|
|
853
|
+
description: "When true, the debt is forgiven at forgiveAt instead of amortizing to zero.",
|
|
854
|
+
evidence: "ui-label",
|
|
855
|
+
},
|
|
856
|
+
switchToMarried: {
|
|
857
|
+
description: "When true, the wedding switches tax filing status to married filing jointly at the wedding date.",
|
|
858
|
+
evidence: "ui-label",
|
|
859
|
+
},
|
|
860
|
+
donationType: {
|
|
861
|
+
description: "Whether a charitable donation is cash or appreciated assets.",
|
|
862
|
+
allowedValues: ["cash", "appreciated-assets"],
|
|
863
|
+
evidence: "ui-label",
|
|
864
|
+
},
|
|
865
|
+
taxDeductible: {
|
|
866
|
+
description: "When true, the expense reduces taxable income.",
|
|
867
|
+
evidence: "observed",
|
|
868
|
+
},
|
|
869
|
+
itemized: {
|
|
870
|
+
description: "When true, the deduction counts as an itemized deduction rather than above the line.",
|
|
871
|
+
enabledBy: "taxDeductible",
|
|
872
|
+
evidence: "observed",
|
|
873
|
+
},
|
|
874
|
+
},
|
|
875
|
+
},
|
|
876
|
+
{
|
|
877
|
+
scope: "accounts",
|
|
878
|
+
title: "Plan accounts",
|
|
879
|
+
fields: {
|
|
880
|
+
name: { description: "Display label of the account.", evidence: "ui-label" },
|
|
881
|
+
owner: {
|
|
882
|
+
description: "Which household member owns the account.",
|
|
883
|
+
allowedValues: ["me", "spouse", "joint"],
|
|
884
|
+
evidence: "ui-label",
|
|
885
|
+
},
|
|
886
|
+
type: {
|
|
887
|
+
description: "Account type.",
|
|
888
|
+
evidence: "ui-label",
|
|
889
|
+
note: 'US types include "taxable", "crypto", "401k", "roth-401k", "457b", "roth-457b", "401a", "ira", "roth-ira", "ira:inherited", "roth-ira:inherited", "hsa", "529"; other countries add types such as "stock-isa", "uk-pension", "sipp", "tfsa", "rrsp", "superannuation", "kh".',
|
|
890
|
+
},
|
|
891
|
+
balance: {
|
|
892
|
+
description: "Current balance of the account.",
|
|
893
|
+
units: DOLLARS,
|
|
894
|
+
evidence: "observed",
|
|
895
|
+
},
|
|
896
|
+
costBasis: {
|
|
897
|
+
description: "Unadjusted tax basis tracked for capital gains on taxable accounts.",
|
|
898
|
+
units: DOLLARS,
|
|
899
|
+
evidence: "observed",
|
|
900
|
+
},
|
|
901
|
+
investmentGrowthType: {
|
|
902
|
+
description: "How the account's growth rate is determined.",
|
|
903
|
+
allowedValues: ["plan", "fixed", "none"],
|
|
904
|
+
evidence: "observed",
|
|
905
|
+
note: '"plan" follows the global rate; a custom fixed rate is stored as "fixed", not "custom".',
|
|
906
|
+
},
|
|
907
|
+
investmentGrowthRate: {
|
|
908
|
+
description: "Custom annual growth rate for the account.",
|
|
909
|
+
units: PCT,
|
|
910
|
+
enabledBy: 'investmentGrowthType "fixed"',
|
|
911
|
+
evidence: "observed",
|
|
912
|
+
},
|
|
913
|
+
dividendType: {
|
|
914
|
+
description: "How the account's dividend yield is determined.",
|
|
915
|
+
evidence: "ui-label",
|
|
916
|
+
note: 'Observed values include "plan" and "none".',
|
|
917
|
+
},
|
|
918
|
+
dividendRate: {
|
|
919
|
+
description: "Custom annual dividend yield for the account.",
|
|
920
|
+
units: PCT,
|
|
921
|
+
evidence: "observed",
|
|
922
|
+
},
|
|
923
|
+
...EARLY_WITHDRAWAL_FIELDS,
|
|
924
|
+
yearlyFee: {
|
|
925
|
+
description: "Recurring account fee.",
|
|
926
|
+
note: 'Interpreted per yearlyFeeType: a percentage, a percentage of earnings, or a dollar amount.',
|
|
927
|
+
evidence: "ui-label",
|
|
928
|
+
},
|
|
929
|
+
yearlyFeeType: {
|
|
930
|
+
description: "Basis of yearlyFee.",
|
|
931
|
+
allowedValues: ["%", "%-earnings", "today$", "$"],
|
|
932
|
+
evidence: "ui-label",
|
|
933
|
+
},
|
|
934
|
+
liquid: {
|
|
935
|
+
description: "Whether the account counts toward liquid net worth.",
|
|
936
|
+
evidence: "observed",
|
|
937
|
+
note: 'true, false, or "EWAge" to count only once the penalty age has passed.',
|
|
938
|
+
},
|
|
939
|
+
withdraw: {
|
|
940
|
+
description: "Whether the account is eligible for automatic drawdown liquidation.",
|
|
941
|
+
evidence: "observed",
|
|
942
|
+
note: 'true, false, "EWAge", or "after" (see withdrawAge).',
|
|
943
|
+
},
|
|
944
|
+
withdrawAge: {
|
|
945
|
+
description: "Timing reference after which the account becomes eligible for drawdown.",
|
|
946
|
+
enabledBy: 'withdraw "after"',
|
|
947
|
+
evidence: "observed",
|
|
948
|
+
},
|
|
949
|
+
rmdType: {
|
|
950
|
+
description: "Statutory required minimum distribution regime.",
|
|
951
|
+
allowedValues: ["us", "none"],
|
|
952
|
+
evidence: "ui-label",
|
|
953
|
+
},
|
|
954
|
+
},
|
|
955
|
+
},
|
|
956
|
+
{
|
|
957
|
+
scope: "assets",
|
|
958
|
+
title: "Assets (plan asset events and Current Finances real assets)",
|
|
959
|
+
fields: {
|
|
960
|
+
...EVENT_IDENTITY,
|
|
961
|
+
type: {
|
|
962
|
+
description: "Kind of asset.",
|
|
963
|
+
evidence: "ui-label",
|
|
964
|
+
note: 'Known kinds: "real-estate", "land", "car", "motorcycle", "boat", "jewelry", "precious-metals", "furniture", "instrument", "machinery", "other".',
|
|
965
|
+
},
|
|
966
|
+
subtype: {
|
|
967
|
+
description: "Sub-kind for real property.",
|
|
968
|
+
evidence: "ui-label",
|
|
969
|
+
note: '"rental" for rental property; "rental-building" for commercial property.',
|
|
970
|
+
},
|
|
971
|
+
initialValue: {
|
|
972
|
+
description: "Acquisition purchase price.",
|
|
973
|
+
units: DOLLARS,
|
|
974
|
+
evidence: "observed",
|
|
975
|
+
},
|
|
976
|
+
initialValueType: {
|
|
977
|
+
description: "Currency basis of initialValue.",
|
|
978
|
+
evidence: "observed",
|
|
979
|
+
},
|
|
980
|
+
amount: {
|
|
981
|
+
description: "Current market value of the asset.",
|
|
982
|
+
units: DOLLARS,
|
|
983
|
+
evidence: "observed",
|
|
984
|
+
note: "Counts as an asset on the balance sheet.",
|
|
985
|
+
},
|
|
986
|
+
amountType: {
|
|
987
|
+
description: "Currency basis of amount.",
|
|
988
|
+
evidence: "observed",
|
|
989
|
+
},
|
|
990
|
+
paymentMethod: {
|
|
991
|
+
description: "Whether the asset was bought outright or financed.",
|
|
992
|
+
allowedValues: ["pay-in-full", "financed"],
|
|
993
|
+
evidence: "observed",
|
|
994
|
+
},
|
|
995
|
+
balance: {
|
|
996
|
+
description: "Outstanding loan principal on the asset.",
|
|
997
|
+
units: DOLLARS,
|
|
998
|
+
enabledBy: 'paymentMethod "financed"',
|
|
999
|
+
evidence: "observed",
|
|
1000
|
+
note: "Counts as a liability on the balance sheet; 0 when paid in full.",
|
|
1001
|
+
},
|
|
1002
|
+
balanceType: {
|
|
1003
|
+
description: "Currency basis of balance.",
|
|
1004
|
+
evidence: "observed",
|
|
1005
|
+
},
|
|
1006
|
+
downPayment: {
|
|
1007
|
+
description: "Cash down payment at acquisition.",
|
|
1008
|
+
units: DOLLARS,
|
|
1009
|
+
enabledBy: 'paymentMethod "financed"',
|
|
1010
|
+
evidence: "observed",
|
|
1011
|
+
},
|
|
1012
|
+
downPaymentType: {
|
|
1013
|
+
description: "Currency basis of downPayment.",
|
|
1014
|
+
evidence: "observed",
|
|
1015
|
+
},
|
|
1016
|
+
monthlyPayment: {
|
|
1017
|
+
description: "Scheduled monthly financing payment.",
|
|
1018
|
+
units: DOLLARS,
|
|
1019
|
+
enabledBy: 'paymentMethod "financed"',
|
|
1020
|
+
evidence: "observed",
|
|
1021
|
+
},
|
|
1022
|
+
monthlyPaymentType: {
|
|
1023
|
+
description: "Currency basis of monthlyPayment.",
|
|
1024
|
+
evidence: "observed",
|
|
1025
|
+
},
|
|
1026
|
+
interestRate: {
|
|
1027
|
+
description: "Annual percentage rate of the asset's loan.",
|
|
1028
|
+
units: PCT,
|
|
1029
|
+
enabledBy: 'paymentMethod "financed"',
|
|
1030
|
+
evidence: "observed",
|
|
1031
|
+
},
|
|
1032
|
+
interestType: {
|
|
1033
|
+
description: "How interest accrues on the asset's loan.",
|
|
1034
|
+
allowedValues: ["simple", "compound"],
|
|
1035
|
+
evidence: "observed",
|
|
1036
|
+
},
|
|
1037
|
+
compounding: {
|
|
1038
|
+
description: "Frequency at which loan interest compounds.",
|
|
1039
|
+
allowedValues: ["monthly", "yearly"],
|
|
1040
|
+
enabledBy: 'interestType "compound"',
|
|
1041
|
+
evidence: "observed",
|
|
1042
|
+
},
|
|
1043
|
+
assessedValue: {
|
|
1044
|
+
description: "Assessed value used for property tax.",
|
|
1045
|
+
units: DOLLARS,
|
|
1046
|
+
evidence: "observed",
|
|
1047
|
+
},
|
|
1048
|
+
taxRate: {
|
|
1049
|
+
description: "Annual property tax figure.",
|
|
1050
|
+
note: "A dollar amount or a percentage of assessedValue, per taxRateType.",
|
|
1051
|
+
evidence: "observed",
|
|
1052
|
+
},
|
|
1053
|
+
taxRateType: {
|
|
1054
|
+
description: "Basis of taxRate.",
|
|
1055
|
+
allowedValues: ["today$", "%"],
|
|
1056
|
+
evidence: "observed",
|
|
1057
|
+
note: '"%" is a percentage of assessedValue; "today$" is a dollar amount.',
|
|
1058
|
+
},
|
|
1059
|
+
maintenanceRate: {
|
|
1060
|
+
description: "Annual maintenance cost.",
|
|
1061
|
+
note: "A dollar amount or a percentage of market value, per maintenanceRateType.",
|
|
1062
|
+
evidence: "observed",
|
|
1063
|
+
},
|
|
1064
|
+
maintenanceRateType: {
|
|
1065
|
+
description: "Basis of maintenanceRate.",
|
|
1066
|
+
allowedValues: ["today$", "%"],
|
|
1067
|
+
evidence: "observed",
|
|
1068
|
+
},
|
|
1069
|
+
insuranceRate: {
|
|
1070
|
+
description: "Annual insurance cost.",
|
|
1071
|
+
note: "A dollar amount or a percentage of market value, per insuranceRateType.",
|
|
1072
|
+
evidence: "observed",
|
|
1073
|
+
},
|
|
1074
|
+
insuranceRateType: {
|
|
1075
|
+
description: "Basis of insuranceRate.",
|
|
1076
|
+
allowedValues: ["today$", "%"],
|
|
1077
|
+
evidence: "observed",
|
|
1078
|
+
},
|
|
1079
|
+
brokersFee: {
|
|
1080
|
+
description: "Broker or transaction fee charged at sale.",
|
|
1081
|
+
units: PCT,
|
|
1082
|
+
evidence: "observed",
|
|
1083
|
+
},
|
|
1084
|
+
"yearlyChange.type": {
|
|
1085
|
+
description: "How the asset's value moves each year.",
|
|
1086
|
+
allowedValues: ["inflation+", "inflation-", "depreciate", "appreciate"],
|
|
1087
|
+
evidence: "observed",
|
|
1088
|
+
},
|
|
1089
|
+
"yearlyChange.amount": {
|
|
1090
|
+
description: "Rate of the asset's yearly value change.",
|
|
1091
|
+
units: PCT,
|
|
1092
|
+
evidence: "observed",
|
|
1093
|
+
},
|
|
1094
|
+
excludeLoanFromLNW: {
|
|
1095
|
+
description: "When true, the asset's loan balance is excluded from liquid net worth.",
|
|
1096
|
+
evidence: "observed",
|
|
1097
|
+
},
|
|
1098
|
+
sellIfNeeded: {
|
|
1099
|
+
description: "When true, the asset is liquidated automatically if the portfolio depletes.",
|
|
1100
|
+
evidence: "observed",
|
|
1101
|
+
},
|
|
1102
|
+
cancelRent: {
|
|
1103
|
+
description: "When true, acquiring the asset cancels the rent expense.",
|
|
1104
|
+
evidence: "observed",
|
|
1105
|
+
},
|
|
1106
|
+
modifyAccount: {
|
|
1107
|
+
description: "Destination account for the net sale proceeds.",
|
|
1108
|
+
evidence: "observed",
|
|
1109
|
+
note: "false, true, or an account ID.",
|
|
1110
|
+
},
|
|
1111
|
+
repeat: {
|
|
1112
|
+
description: "When true, the asset is re-acquired on a cadence.",
|
|
1113
|
+
evidence: "observed",
|
|
1114
|
+
},
|
|
1115
|
+
repeatInterval: {
|
|
1116
|
+
description: "Years between replacements of the asset.",
|
|
1117
|
+
units: YEARS,
|
|
1118
|
+
enabledBy: "repeat",
|
|
1119
|
+
evidence: "observed",
|
|
1120
|
+
},
|
|
1121
|
+
repeatIntervalType: {
|
|
1122
|
+
description: "Cadence mode for replacements.",
|
|
1123
|
+
allowedValues: ["every"],
|
|
1124
|
+
evidence: "observed",
|
|
1125
|
+
},
|
|
1126
|
+
generateIncome: {
|
|
1127
|
+
description: "When true, the property generates modeled rental income.",
|
|
1128
|
+
evidence: "ui-label",
|
|
1129
|
+
},
|
|
1130
|
+
incomeRate: {
|
|
1131
|
+
description: "Gross rental yield of the property.",
|
|
1132
|
+
units: PCT,
|
|
1133
|
+
enabledBy: "generateIncome",
|
|
1134
|
+
evidence: "ui-label",
|
|
1135
|
+
},
|
|
1136
|
+
managementRate: {
|
|
1137
|
+
description: "Property management fee.",
|
|
1138
|
+
units: PCT,
|
|
1139
|
+
enabledBy: "generateIncome",
|
|
1140
|
+
evidence: "ui-label",
|
|
1141
|
+
},
|
|
1142
|
+
monthlyHOA: {
|
|
1143
|
+
description: "Monthly homeowners association dues.",
|
|
1144
|
+
units: DOLLARS,
|
|
1145
|
+
enabledBy: "generateIncome",
|
|
1146
|
+
evidence: "ui-label",
|
|
1147
|
+
},
|
|
1148
|
+
estimateRentalDeductions: {
|
|
1149
|
+
description: "When true, deducts depreciation, interest, and taxes from rental income.",
|
|
1150
|
+
enabledBy: "generateIncome",
|
|
1151
|
+
evidence: "ui-label",
|
|
1152
|
+
},
|
|
1153
|
+
estimateQBI: {
|
|
1154
|
+
description: "When true, models the qualified business income deduction on rental income.",
|
|
1155
|
+
enabledBy: "generateIncome",
|
|
1156
|
+
evidence: "ui-label",
|
|
1157
|
+
},
|
|
1158
|
+
...timingFields("start"),
|
|
1159
|
+
...timingFields("end"),
|
|
1160
|
+
},
|
|
1161
|
+
},
|
|
1162
|
+
{
|
|
1163
|
+
scope: "milestones",
|
|
1164
|
+
title: "Milestones",
|
|
1165
|
+
fields: {
|
|
1166
|
+
name: { description: "Display label of the milestone.", evidence: "observed" },
|
|
1167
|
+
id: {
|
|
1168
|
+
description: "Built-in key or generated ID of the milestone.",
|
|
1169
|
+
evidence: "observed",
|
|
1170
|
+
note: 'Built-ins: "retirement", "spouseRetirement", "lifeExpectancy", "spouseLifeExpectancy", "fi"; custom milestones get a generated ID.',
|
|
1171
|
+
},
|
|
1172
|
+
removable: {
|
|
1173
|
+
description: "True for user-created milestones; omitted for built-ins.",
|
|
1174
|
+
evidence: "observed",
|
|
1175
|
+
},
|
|
1176
|
+
"criteria.*.type": {
|
|
1177
|
+
description: "Kind of criterion that positions the milestone.",
|
|
1178
|
+
allowedValues: ["date", "year", "milestone", "netWorth", "totalDebt", "age"],
|
|
1179
|
+
evidence: "observed",
|
|
1180
|
+
},
|
|
1181
|
+
"criteria.*.value": {
|
|
1182
|
+
description: "Target of the criterion.",
|
|
1183
|
+
evidence: "observed",
|
|
1184
|
+
note: 'A "YYYY-MM-01" date for "date" and "year" types; a milestone ID for the "milestone" type; a numeric threshold for "netWorth" and "totalDebt".',
|
|
1185
|
+
},
|
|
1186
|
+
"criteria.*.modifier": {
|
|
1187
|
+
description: "Offset applied to a milestone criterion.",
|
|
1188
|
+
evidence: "observed",
|
|
1189
|
+
note: '"include" is the milestone year itself; an integer is that many years after it.',
|
|
1190
|
+
},
|
|
1191
|
+
"criteria.*.operator": {
|
|
1192
|
+
description: "Comparison for a netWorth or totalDebt criterion.",
|
|
1193
|
+
evidence: "observed",
|
|
1194
|
+
note: 'Observed values include "==" and ">=".',
|
|
1195
|
+
},
|
|
1196
|
+
"criteria.*.valueType": {
|
|
1197
|
+
description: "Basis of a numeric criterion value.",
|
|
1198
|
+
evidence: "observed",
|
|
1199
|
+
note: '"spending" expresses the value as a multiple of annual spending; "$" as dollars.',
|
|
1200
|
+
},
|
|
1201
|
+
"criteria.*.range": {
|
|
1202
|
+
description: "Tolerance band around a netWorth criterion value.",
|
|
1203
|
+
units: "count",
|
|
1204
|
+
evidence: "observed",
|
|
1205
|
+
},
|
|
1206
|
+
"criteria.*.age": {
|
|
1207
|
+
description: "Age at which an age criterion fires.",
|
|
1208
|
+
units: "age",
|
|
1209
|
+
evidence: "observed",
|
|
1210
|
+
},
|
|
1211
|
+
"criteria.*.householdMemberId": {
|
|
1212
|
+
description: "Whose age an age criterion tracks.",
|
|
1213
|
+
allowedValues: ["me", "spouse"],
|
|
1214
|
+
evidence: "observed",
|
|
1215
|
+
},
|
|
1216
|
+
},
|
|
1217
|
+
},
|
|
1218
|
+
{
|
|
1219
|
+
scope: "priorities",
|
|
1220
|
+
title: "Priorities (cash flow routing)",
|
|
1221
|
+
fields: {
|
|
1222
|
+
name: { description: "Display label of the cash flow.", evidence: "observed" },
|
|
1223
|
+
title: {
|
|
1224
|
+
description: "Group header the flow is listed under.",
|
|
1225
|
+
evidence: "observed",
|
|
1226
|
+
},
|
|
1227
|
+
type: {
|
|
1228
|
+
description: "Target account or priority type of the flow.",
|
|
1229
|
+
evidence: "observed",
|
|
1230
|
+
note: 'Examples: "taxable", "ira", "roth-ira", "401k", "hsa", "savings", "emergency-fund", "debt", "asset", "transfer".',
|
|
1231
|
+
},
|
|
1232
|
+
goalIntent: {
|
|
1233
|
+
description: "Whether the flow invests surplus cash or transfers between accounts.",
|
|
1234
|
+
allowedValues: ["invest", "transfer"],
|
|
1235
|
+
evidence: "observed",
|
|
1236
|
+
},
|
|
1237
|
+
accountId: {
|
|
1238
|
+
description: "Destination account ID the flow routes to.",
|
|
1239
|
+
evidence: "observed",
|
|
1240
|
+
},
|
|
1241
|
+
desiredContribution: {
|
|
1242
|
+
description: "How much the flow receives from surplus cash flow.",
|
|
1243
|
+
allowedValues: ["max", "fixed"],
|
|
1244
|
+
evidence: "observed",
|
|
1245
|
+
note: '"max" routes all remaining surplus; "fixed" takes amount.',
|
|
1246
|
+
},
|
|
1247
|
+
amount: {
|
|
1248
|
+
description: "Fixed contribution taken when desiredContribution is \"fixed\".",
|
|
1249
|
+
units: DOLLARS,
|
|
1250
|
+
evidence: "observed",
|
|
1251
|
+
},
|
|
1252
|
+
amountType: {
|
|
1253
|
+
description: "Currency basis of amount.",
|
|
1254
|
+
evidence: "observed",
|
|
1255
|
+
},
|
|
1256
|
+
frequency: {
|
|
1257
|
+
description: "How often the flow is funded.",
|
|
1258
|
+
evidence: "observed",
|
|
1259
|
+
note: 'Examples: "yearly", "monthly".',
|
|
1260
|
+
},
|
|
1261
|
+
owner: {
|
|
1262
|
+
description: "Household member the flow belongs to.",
|
|
1263
|
+
allowedValues: ["me", "spouse", "joint"],
|
|
1264
|
+
evidence: "ui-label",
|
|
1265
|
+
},
|
|
1266
|
+
...timingFields("start"),
|
|
1267
|
+
...timingFields("end"),
|
|
1268
|
+
},
|
|
1269
|
+
},
|
|
1270
|
+
{
|
|
1271
|
+
scope: "savingsAccounts",
|
|
1272
|
+
title: "Current Finances: savings accounts",
|
|
1273
|
+
fields: {
|
|
1274
|
+
name: { description: "Display label of the account.", evidence: "ui-label" },
|
|
1275
|
+
balance: {
|
|
1276
|
+
description: "Cash balance of the account.",
|
|
1277
|
+
units: DOLLARS,
|
|
1278
|
+
evidence: "ui-label",
|
|
1279
|
+
},
|
|
1280
|
+
interestRate: {
|
|
1281
|
+
description: "Annual percentage yield paid on the balance.",
|
|
1282
|
+
units: PCT,
|
|
1283
|
+
evidence: "ui-label",
|
|
1284
|
+
},
|
|
1285
|
+
owner: {
|
|
1286
|
+
description: "Which household member owns the account.",
|
|
1287
|
+
allowedValues: ["me", "spouse", "joint"],
|
|
1288
|
+
evidence: "ui-label",
|
|
1289
|
+
},
|
|
1290
|
+
},
|
|
1291
|
+
},
|
|
1292
|
+
{
|
|
1293
|
+
scope: "investmentAccounts",
|
|
1294
|
+
title: "Current Finances: investment accounts",
|
|
1295
|
+
fields: {
|
|
1296
|
+
name: { description: "Display label of the account.", evidence: "ui-label" },
|
|
1297
|
+
type: {
|
|
1298
|
+
description: "Account type.",
|
|
1299
|
+
evidence: "ui-label",
|
|
1300
|
+
note: 'For example "taxable", "ira", "401k", "roth-ira", "hsa".',
|
|
1301
|
+
},
|
|
1302
|
+
balance: {
|
|
1303
|
+
description: "Current balance of the account.",
|
|
1304
|
+
units: DOLLARS,
|
|
1305
|
+
evidence: "ui-label",
|
|
1306
|
+
},
|
|
1307
|
+
costBasis: {
|
|
1308
|
+
description: "Unadjusted tax basis, required for taxable brokerage accounts.",
|
|
1309
|
+
units: DOLLARS,
|
|
1310
|
+
evidence: "ui-label",
|
|
1311
|
+
},
|
|
1312
|
+
owner: {
|
|
1313
|
+
description: "Which household member owns the account.",
|
|
1314
|
+
allowedValues: ["me", "spouse", "joint"],
|
|
1315
|
+
evidence: "ui-label",
|
|
1316
|
+
},
|
|
1317
|
+
...EARLY_WITHDRAWAL_FIELDS,
|
|
1318
|
+
rmdType: {
|
|
1319
|
+
description: "Statutory required minimum distribution regime.",
|
|
1320
|
+
allowedValues: ["us", "none"],
|
|
1321
|
+
evidence: "ui-label",
|
|
1322
|
+
},
|
|
1323
|
+
},
|
|
1324
|
+
},
|
|
1325
|
+
{
|
|
1326
|
+
scope: "debts",
|
|
1327
|
+
title: "Current Finances: unsecured debts",
|
|
1328
|
+
fields: {
|
|
1329
|
+
name: { description: "Display label of the debt.", evidence: "ui-label" },
|
|
1330
|
+
balance: {
|
|
1331
|
+
description: "Outstanding principal of the debt.",
|
|
1332
|
+
units: DOLLARS,
|
|
1333
|
+
evidence: "ui-label",
|
|
1334
|
+
},
|
|
1335
|
+
interestRate: {
|
|
1336
|
+
description: "Annual percentage rate of the debt.",
|
|
1337
|
+
units: PCT,
|
|
1338
|
+
evidence: "ui-label",
|
|
1339
|
+
},
|
|
1340
|
+
monthlyPayment: {
|
|
1341
|
+
description: "Minimum monthly payment.",
|
|
1342
|
+
units: DOLLARS,
|
|
1343
|
+
evidence: "ui-label",
|
|
1344
|
+
},
|
|
1345
|
+
owner: {
|
|
1346
|
+
description: "Which household member owes the debt.",
|
|
1347
|
+
allowedValues: ["me", "spouse", "joint"],
|
|
1348
|
+
evidence: "ui-label",
|
|
1349
|
+
},
|
|
1350
|
+
},
|
|
1351
|
+
},
|
|
1352
|
+
];
|
|
1353
|
+
const compiledByScope = new Map();
|
|
1354
|
+
for (const group of DATA_MODEL_GROUPS) {
|
|
1355
|
+
const entries = [];
|
|
1356
|
+
for (const [pattern, field] of Object.entries(group.fields)) {
|
|
1357
|
+
entries.push({ pattern, segments: pattern.split("."), field });
|
|
1358
|
+
}
|
|
1359
|
+
compiledByScope.set(group.scope, entries);
|
|
1360
|
+
}
|
|
1361
|
+
function entryMatches(segments, pathSegments) {
|
|
1362
|
+
if (segments.length !== pathSegments.length)
|
|
1363
|
+
return false;
|
|
1364
|
+
for (let i = 0; i < segments.length; i++) {
|
|
1365
|
+
if (segments[i] !== "*" && segments[i] !== pathSegments[i])
|
|
1366
|
+
return false;
|
|
1367
|
+
}
|
|
1368
|
+
return true;
|
|
1369
|
+
}
|
|
1370
|
+
/** Field entry for a scope-prefixed concrete path, or undefined. */
|
|
1371
|
+
export function matchDataModelField(scope, path) {
|
|
1372
|
+
const entries = compiledByScope.get(scope);
|
|
1373
|
+
if (!entries)
|
|
1374
|
+
return undefined;
|
|
1375
|
+
const exact = entries.find((entry) => entry.pattern === path);
|
|
1376
|
+
if (exact)
|
|
1377
|
+
return exact.field;
|
|
1378
|
+
const pathSegments = path.split(".");
|
|
1379
|
+
for (const entry of entries) {
|
|
1380
|
+
if (!entry.pattern.includes("*"))
|
|
1381
|
+
continue;
|
|
1382
|
+
if (entryMatches(entry.segments, pathSegments))
|
|
1383
|
+
return entry.field;
|
|
1384
|
+
}
|
|
1385
|
+
return undefined;
|
|
1386
|
+
}
|
|
1387
|
+
/**
|
|
1388
|
+
* Copy listed values, attaching description, units, and allowedValues for
|
|
1389
|
+
* paths with a registry entry. Entries for unknown paths are returned
|
|
1390
|
+
* unchanged, so existing listPaths fields never change.
|
|
1391
|
+
*/
|
|
1392
|
+
export function annotateAddressableValues(scope, values) {
|
|
1393
|
+
return values.map((value) => {
|
|
1394
|
+
const field = scope ? matchDataModelField(scope, value.path) : undefined;
|
|
1395
|
+
if (!field)
|
|
1396
|
+
return value;
|
|
1397
|
+
const annotated = { ...value };
|
|
1398
|
+
annotated.description = field.description;
|
|
1399
|
+
if (field.units)
|
|
1400
|
+
annotated.units = field.units;
|
|
1401
|
+
if (field.allowedValues)
|
|
1402
|
+
annotated.allowedValues = field.allowedValues;
|
|
1403
|
+
return annotated;
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
/**
|
|
1407
|
+
* Choices to refuse a value over: an observed enum's list when value is a
|
|
1408
|
+
* string outside it. undefined when no refusal applies. Numeric and boolean
|
|
1409
|
+
* values are never refused; ui-label enums are annotated but not enforced.
|
|
1410
|
+
*/
|
|
1411
|
+
export function refusedEnumChoices(scope, path, value) {
|
|
1412
|
+
if (scope === undefined || typeof value !== "string")
|
|
1413
|
+
return undefined;
|
|
1414
|
+
const field = matchDataModelField(scope, path);
|
|
1415
|
+
if (!field?.allowedValues || field.evidence !== "observed")
|
|
1416
|
+
return undefined;
|
|
1417
|
+
if (field.allowedValues.includes(value))
|
|
1418
|
+
return undefined;
|
|
1419
|
+
return field.allowedValues;
|
|
1420
|
+
}
|
|
1421
|
+
/** Refusal for a value a write sets outside an observed enum's choices. */
|
|
1422
|
+
export function enumRefusalError(scope, path, value) {
|
|
1423
|
+
const choices = refusedEnumChoices(scope, path, value);
|
|
1424
|
+
if (!choices)
|
|
1425
|
+
return undefined;
|
|
1426
|
+
const shown = typeof value === "string" ? JSON.stringify(value) : String(value);
|
|
1427
|
+
return new ProjectionLabError("INVALID_INPUT", `Invalid value ${shown} for "${scope}.${path}". Allowed values: ${choices.join(", ")}.`, `Write one of the listed values, or inspect projectionlab://data-model for what "${path}" means and what enables it.`);
|
|
1428
|
+
}
|
|
1429
|
+
//# sourceMappingURL=dataModel.js.map
|