TODO cleanup

This commit is contained in:
Emil Lerch 2026-06-24 11:08:11 -07:00
parent bd0483b26e
commit 545fda4c52
Signed by: lobo
GPG key ID: A7B62D657EF764F8

120
TODO.md
View file

@ -539,80 +539,6 @@ Once decisions are made, sweep all four sites + add a regression
alignment test per table that mixes a fully-populated row with
an em-dash-heavy row and verifies `displayCols` matches.
## TUI: numeric keypad input not handled — priority LOW
Numeric-keypad keys (Num0-Num9, decimal point, minus) don't reach
the modal text-input handlers. Reproduced on the projections tab
as-of date prompt (`d`): typing the date with the numpad produces
no input, while the digit keys on the main keyboard row work fine.
Affects every modal that takes numeric input — symbol-input is
unaffected because it's letters.
Likely cause: the modal handlers route through `vaxis.Key.codepoint`
matching, but vaxis emits keypad keys with a distinct keycode (kitty
keyboard protocol) rather than the codepoint of the equivalent ASCII
digit. The fix is in the modal key paths (`handleDateInputKey` in
`src/tui/projections_tab.zig` and the symbol-input handler in
`src/tui.zig` — though that one is letters-only in practice) and
possibly the shared `input_buffer.zig` if that's where character
gathering lives. Worth surveying both files plus any other tab that
will grow numeric input (the CLI options command's near-the-money
strike count would be a candidate if migrated to a modal).
Verification: open the TUI, press `d` on projections, try to type a
date with the keypad. Then try the keyboard row. Both should commit
identical input.
## TUI: memory leaks somewhere — priority MEDIUM
User reported leaks while doing a detailed TUI walkthrough; no
specific tab or interaction yet identified. The TUI uses a mix
of arena allocators (frame-scoped) and persistent tab state, so
likely culprits:
- Per-tab `State` structs that hold `[]const u8` slices duped
from a long-lived allocator but not freed when the tab
reloads or the symbol changes.
- Cached service-fetch results stored in tab state that aren't
`result.deinit()`-ed before being replaced.
- ArrayList accumulators that get appended to across multiple
draw cycles without an intervening clear.
- Vaxis event/dialog closures that capture strings into
arena-allocated lambdas but escape the arena's lifetime.
### Investigation plan
1. Run the TUI under `std.testing.allocator` (a debug
allocator that panics on leak). The current binary uses a
gpa, which silently tolerates leaks. A test-mode TUI run
with a leak-detecting allocator would surface the
offending alloc sites with file/line info.
2. Walk each tab's `State.deinit` (and `tab.deactivate` /
`tab.reload` hooks) against the `State` field list — every
owned field needs a free path on every state-change boundary.
3. Pay specific attention to `classification_map` and any
per-symbol caches (option chains, candle snapshots) — those
are the biggest fixed-size strings.
No reproducer yet. When the user has a more specific lead
(which tab, which interaction), this entry should narrow.
### `etf <SYMBOL>` warns `failed to serialize ETF profile: WriteFailed` — priority LOW
Every `zfin etf VTI` invocation prints
`warning(cache): VTI: failed to serialize ETF profile: WriteFailed`
to stderr before the foreground output. The ETF profile renders
correctly; just the cache write fails.
`src/cache/store.zig:209` calls `serializeEtfProfile` which fails
on the `aw.writer.print(...)` call inside it (line 1007). Likely
a Zig 0.16 stdlib quirk in the SRF writer path or a missing
`flush()` somewhere in the writer chain.
Investigate by replacing the `print` with a manual `print` +
`flush` to see if it's a buffer-not-flushed issue, or by
serializing a known-good fixture in isolation.
## Analysis: dividend equity / income-shaped equity — think about it
Dividend-equity ETFs (SCHD, VYM, DGRO, NOBL, SDY, VIG, etc.)
@ -660,52 +586,6 @@ If a fix lands, it's probably a separate analysis section (yield
breakdown, income coverage) — not a change to the asset-class
taxonomy.
## Analysis: umbrella-insurance exposure — future enhancements — priority LOW
**v1 shipped (May 2026)**: `analysis` command and TUI tab now show an
"Umbrella exposure" section that splits the liquid portfolio into
Shielded vs Exposed dollars based on per-account `tax_type` from
`accounts.srf`, with an optional per-account `shielded:bool:false`
(or `:true`) override for cases the tax-type default gets wrong
(e.g. pre-tax deferred-comp accounts that aren't ERISA-protected).
The shielding decision is intentionally simple: tax_type != taxable
defaults to shielded; the `shielded` field overrides per-account.
That covers the realistic cases (401k vs taxable brokerage; DCP-style
non-ERISA pre-tax accounts) without a state-by-state lookup table.
### What's deferred
These were in the original v1 design but skipped to keep the
shipping scope tight. Pick up only if real user demand surfaces.
- **State-by-state IRA protection lookup table**. Civil-judgment IRA
shielding varies by state (TX/FL full, some none, most partial).
v1 punts to the manual override; users in weak-state IRA
jurisdictions add `shielded:bool:false` on their IRA accounts
themselves. A built-in state table would automate this — needs a
`state` field at the user level (per-portfolio-file or global
config) and a maintained taxonomy. Doable but high-friction relative
to the manual override.
- **`account_type` distinction**. v1 uses `tax_type` (taxable / roth
/ traditional / hsa) as the umbrella proxy because that's what
exists. A more granular `account_type` (`401k`, `403b`, `roth_ira`,
`traditional_ira`, `sep_ira`, `inherited_ira`, ...) would let the
default shielding decision be more nuanced (e.g. inherited_ira
defaults to NOT shielded post-Clark v. Rameker). Not necessary
while the override exists.
- **Joint-account / community-property nuance**. State-specific.
v1 treats each account holistically. Probably never needed.
- **Inherited-IRA flag**. Currently the user adds
`shielded:bool:false` on inherited IRAs as the workaround. A
dedicated flag would let the section call them out by name in
the output. Cosmetic.
The following items are acknowledged but not prioritized. Listed here
so they don't get lost; pick up opportunistically.