Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,11 @@ jobs:
- name: Tests
run: npm run test:run

- name: Install browser
run: npx playwright install --with-deps chromium

- name: Browser and accessibility tests
run: npm run test:e2e

- name: Build
run: npm run build
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ dist/

# Test coverage
coverage/
test-results/
playwright-report/

# IDE
.vscode/
Expand Down
65 changes: 65 additions & 0 deletions docs/DECISIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,41 @@ keep the literal `qfaf`/`quantinno` spelling, so no CalculatorInputs field was a
CSV/Excel round-trips are unaffected. **Standing requirement:** new user-visible
manager/fund text must be wrapped in `brandText()` (or come from `getPopupContent`).

### D-030 — Washington 2028 income-tax treatment
**Date:** 2026-07-11
**Context:** Washington enacted a 9.9% tax on income above the statutory deduction,
effective for income earned beginning in 2028. The existing Washington profile models
no general income tax and only the capital-gains excise, while projections can run as
long as 40 years. The new income tax overlaps with the capital-gains tax through a
credit, so a simple additional 9.9% rate would double-count some income.
**Decision (owner):** **Implement the enacted 2028 rules now.** Model the year-aware
Washington income tax and its capital-gains-tax credit rather than merely warning or
stopping Washington projections at 2027.
**Implications:** Tax parameters must be versioned by effective year and carry source,
verification-date, and provisional-status metadata. Washington projections use the
existing rules through 2027 and the enacted income-tax regime beginning in 2028, with
the capital-gains credit preventing double taxation. The UI and disclosures must clearly
mark implementation details that remain provisional pending agency regulations/forms.
Tests must cover the 2027/2028 boundary, the statutory deduction, overlapping capital
gains and credit, and multi-year projections. Apply the audit-complete directive to any
new per-year engine outputs and use the same year-aware tax path in liquidation analysis.

### D-031 — Results-first mobile Workspace
**Date:** 2026-07-11
**Context:** At phone width the Workspace avoids horizontal overflow but becomes an
extremely long, dense page. Responsive CSS alone does not preserve the results-first
information hierarchy already chosen for the Workspace.
**Decision (owner):** Build a **results-first mobile Workspace with an accessible input
drawer and selected-year detail**.
**Implications:** At mobile breakpoints, headline metrics and flags appear before the
input editor. Inputs move into an accessible drawer with focus containment, Escape/close
behavior, focus restoration, and touch-sized controls. The default year-by-year mobile
experience becomes a selected-year detail view rather than requiring the full dense
table; the audit-complete desktop table remains available and no engine outputs may be
silently omitted. Desktop Workspace behavior and the one-engine rule remain unchanged.
Browser coverage must verify representative phone widths, keyboard accessibility, dark
theme, input edits updating results, and selected-year navigation.

---

## Pending decision queue (next batches)
Expand Down Expand Up @@ -905,6 +940,36 @@ Clean exits skip the prompt. The Classic-tab Meeting Mode host remains un-sandbo
(live edits, as before) and is labeled accordingly in code. Workspace-level test proves
Discard leaves inputs bit-identical and Keep applies them.

### D-032 — Fast-learning client education layer
**Date:** 2026-07-11
**Context:** Owner directed implementation of all findings from a mock presentation to a
high-income California software engineer with moderate financial knowledge.
**Decision:** Keep the default results-first surface, then add progressive, engine-backed
explanations rather than a separate beginner mode or parallel projection.
**Status: IMPLEMENTED (2026-07-11)** — Workspace Overview now includes: a six-step
calculation trace; persistent capital/ordinary/NOL character map; federal-versus-CA
timing timeline; realized-benefit/tax-asset/deferral/liquidation grouping; selected-year
deterministic savings reconciliation; clearer income-utilization terminology; software-
compensation presets with character and AMT guidance; marginal-rate labeling; statutory
source provenance; and component attribution for pinned-scenario savings changes. All
displayed dollar explanations aggregate existing `CalculationResult` fields. Mixed IPO/
acquisition presets compose ordinary income and a capital-gain event through the existing
`YearOverride` engine path.

### D-033 — Meeting Mode education essentials
**Date:** 2026-07-11
**Context:** Workspace gained the full fast-learning client education layer in D-032;
owner asked for Meeting Mode to be complete enough for an advisor-led conversation
without requiring 100% Workspace parity.
**Decision:** Port five presentation-critical explanations, keeping construction,
provenance, presets, and the full audit workflow in Workspace.
**Status: IMPLEMENTED (2026-07-11)** — High Level adds client-outcome classification
(realized savings, unused tax assets, signed liquidation tax, net after liquidation) and
component attribution beneath the existing Was→Now comparison. Detail adds a selected-
year deterministic benefit/cost reconciliation. Mechanics adds the capital/ordinary/NOL
character map and a California SB 167 federal-versus-state timing callout. Every dollar
comes from existing `CalculationResult` / exit-analysis fields; no parallel projection.

---

## Bugs — no decision required
Expand Down
3 changes: 2 additions & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default tseslint.config(
'@typescript-eslint/no-non-null-assertion': 'warn',

// General code quality
'no-console': 'warn',
'no-console': ['warn', { allow: ['warn', 'error'] }],
'no-debugger': 'error',
'prefer-const': 'error',
'no-var': 'error',
Expand All @@ -54,6 +54,7 @@ export default tseslint.config(
files: ['**/*.test.ts', '**/*.test.tsx'],
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'no-console': 'off',
},
},
Expand Down
88 changes: 88 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
"preview": "vite preview",
"test": "vitest --config vitest.config.ts",
"test:run": "vitest run --config vitest.config.ts",
"test:e2e": "playwright test",
"test:coverage": "vitest run --config vitest.config.ts --coverage",
"lint": "eslint src/",
"lint": "eslint src/ --max-warnings 0",
"lint:fix": "eslint src/ --fix",
"format": "prettier --write src/",
"format:check": "prettier --check src/"
Expand All @@ -24,7 +25,9 @@
"xlsx": "^0.18.5"
},
"devDependencies": {
"@axe-core/playwright": "^4.12.1",
"@eslint/js": "^9.39.2",
"@playwright/test": "^1.61.1",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
Expand Down
24 changes: 24 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
testDir: './tests/e2e',
fullyParallel: true,
forbidOnly: Boolean(process.env.CI),
retries: process.env.CI ? 2 : 0,
reporter: process.env.CI ? 'github' : 'list',
use: {
baseURL: 'http://127.0.0.1:4173',
trace: 'retain-on-failure',
screenshot: 'only-on-failure',
},
projects: [
{ name: 'desktop', use: { ...devices['Desktop Chrome'] } },
{ name: 'mobile', use: { ...devices['iPhone 13'], browserName: 'chromium' } },
],
webServer: {
command: 'npm run dev -- --host 127.0.0.1 --port 4173',
env: { ...process.env, GITHUB_ACTIONS: '' },
url: 'http://127.0.0.1:4173',
reuseExistingServer: !process.env.CI,
},
});
8 changes: 7 additions & 1 deletion src/Calculator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ export function Calculator({ isActive = true }: CalculatorProps) {
);

const results = useMemo(() => {
// Custom strategy rates live outside React state; rateVersion is the
// explicit invalidation token that forces this memo to read them again.
void rateVersion;
// Priority: Year overrides > Sensitivity > Base calculation
// Note: Year overrides and sensitivity don't combine (would need a combined function)
if (hasActiveOverrides) {
Expand Down Expand Up @@ -207,7 +210,8 @@ export function Calculator({ isActive = true }: CalculatorProps) {
combinedLtRate,
advancedSettings.growthEnabled ? advancedSettings.defaultAnnualReturn : 0,
taxRates.stateProfile.ltcgExcise,
inputs.collateralCostBasis
inputs.collateralCostBasis,
{ stateCode: inputs.stateCode, ordinaryIncome: inputs.annualIncome }
),
[
results,
Expand All @@ -216,6 +220,8 @@ export function Calculator({ isActive = true }: CalculatorProps) {
advancedSettings.growthEnabled,
advancedSettings.defaultAnnualReturn,
inputs.collateralCostBasis,
inputs.stateCode,
inputs.annualIncome,
]
);

Expand Down
Loading
Loading