add todo items after review of full application surface
This commit is contained in:
parent
b0ef25f1e0
commit
7fac66f00b
1 changed files with 248 additions and 0 deletions
248
TODO.md
248
TODO.md
|
|
@ -160,6 +160,254 @@ Out of scope for V1: file-format alternatives (SVG, PDF), themed
|
||||||
color overrides for export (always uses the active terminal theme),
|
color overrides for export (always uses the active terminal theme),
|
||||||
non-chart command output as PNG.
|
non-chart command output as PNG.
|
||||||
|
|
||||||
|
## CLI architecture overhaul — priority MEDIUM
|
||||||
|
|
||||||
|
The CLI surface has grown to 21 top-level commands and the dispatch
|
||||||
|
machinery in `src/main.zig` is starting to bloat. Three related gaps,
|
||||||
|
all best addressed together because they share the same refactor
|
||||||
|
vehicle:
|
||||||
|
|
||||||
|
1. **No per-command `--help`.** `zfin help` shows a wall of every
|
||||||
|
command and every flag. There's no way to ask "what does
|
||||||
|
`projections` accept?" without grepping the top-level usage.
|
||||||
|
Every command should support `zfin <command> --help` printing
|
||||||
|
just that command's synopsis, flags, and a few examples.
|
||||||
|
2. **`usage` text is roughly chronological-by-feature.** Re-group it
|
||||||
|
into workflow sections so users can scan for what they need:
|
||||||
|
- **Per-symbol lookups:** `perf`, `quote`, `history` (symbol
|
||||||
|
mode), `divs`, `splits`, `options`, `earnings`, `etf`
|
||||||
|
- **Portfolio analysis:** `portfolio`, `analysis`, `history`
|
||||||
|
(portfolio mode), `projections`, `milestones`
|
||||||
|
- **Time-series & journaling:** `snapshot`, `compare`,
|
||||||
|
`contributions`
|
||||||
|
- **Data hygiene:** `audit`, `enrich`, `lookup`
|
||||||
|
- **Infra:** `cache`, `version`, `interactive`
|
||||||
|
3. **Per-command flag parsing lives inline in `runCli`.** It's
|
||||||
|
~500 lines now. The `projections`, `contributions`, and `compare`
|
||||||
|
branches each carry ~50 lines of inline flag parsing plus their
|
||||||
|
own conflict-detection. Each command module should own its own
|
||||||
|
`parseArgs()`; `runCli` should be pure dispatch.
|
||||||
|
|
||||||
|
The natural mechanism for all three is a **command registry table**
|
||||||
|
mirroring the `tab_modules` registry in `src/tui.zig`:
|
||||||
|
|
||||||
|
```zig
|
||||||
|
// src/main.zig
|
||||||
|
const command_registry = .{
|
||||||
|
.perf = @import("commands/perf.zig"),
|
||||||
|
.quote = @import("commands/quote.zig"),
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
|
||||||
|
// each command module exposes:
|
||||||
|
// pub const meta = .{
|
||||||
|
// .help = "perf <SYMBOL> Show trailing returns ...",
|
||||||
|
// .group = .symbol_lookup,
|
||||||
|
// .takes_symbol = true,
|
||||||
|
// .needs_portfolio_path = false,
|
||||||
|
// };
|
||||||
|
// pub fn parseArgs(allocator, args, today) !ParsedArgs
|
||||||
|
// pub fn run(io, allocator, *DataService, parsed, today, color, *Writer) !void
|
||||||
|
```
|
||||||
|
|
||||||
|
A comptime walk over the registry produces:
|
||||||
|
- The grouped `usage` text (sort by `meta.group`, render section
|
||||||
|
headers).
|
||||||
|
- Per-command help dispatch (`zfin <cmd> --help` prints
|
||||||
|
`meta.help` plus the parsed flag descriptors).
|
||||||
|
- The dispatch chain itself (replace the giant `if-else if`).
|
||||||
|
- The "does this command take a symbol?" / "does it need a
|
||||||
|
portfolio path?" decisions currently scattered across `runCli`.
|
||||||
|
|
||||||
|
**Driver:** the per-command `--help` is the loudest user-facing gap;
|
||||||
|
the registry refactor is the cleanest way to land it without
|
||||||
|
scattering help text across more files. Also unblocks `zfin doctor`
|
||||||
|
(below) and the unified time-range parser (below) by giving them
|
||||||
|
a clean module shape to plug into.
|
||||||
|
|
||||||
|
**Open question for when this is picked up:** how to handle the
|
||||||
|
`history` command's two modes (symbol vs portfolio) under a single
|
||||||
|
registry entry. Probably one entry with a parser that branches on
|
||||||
|
`args[0]`, same as today — but the registry shape should accommodate
|
||||||
|
that without ceremony.
|
||||||
|
|
||||||
|
## `zfin doctor` health-check command — priority LOW
|
||||||
|
|
||||||
|
Front-door command for the file constellation and environment.
|
||||||
|
Answers "is my zfin setup sane?" without making any changes.
|
||||||
|
|
||||||
|
The configuration surface has grown organically and only the README
|
||||||
|
explains the file layout:
|
||||||
|
|
||||||
|
- `portfolio.srf` — lots
|
||||||
|
- `metadata.srf` — classifications
|
||||||
|
- `accounts.srf` — tax types
|
||||||
|
- `projections.srf` — retirement config
|
||||||
|
- `transaction_log.srf` — internal transfers
|
||||||
|
- `imported_values.srf` — back-history
|
||||||
|
- `history/*-portfolio.srf` — snapshots
|
||||||
|
- `~/.config/zfin/keys.srf` — keybinds
|
||||||
|
- `~/.config/zfin/theme.srf` — colors
|
||||||
|
|
||||||
|
Plus 5 API keys, a cache directory, and an optional `ZFIN_SERVER`.
|
||||||
|
|
||||||
|
### v1 scope (full health check)
|
||||||
|
|
||||||
|
- **File inventory + parse check.** For each of the files above:
|
||||||
|
does it exist, where was it resolved from (cwd vs `ZFIN_HOME` vs
|
||||||
|
`~/.config/zfin`), and does it parse cleanly. No fixes.
|
||||||
|
- **Cross-reference checks.** Every account in `portfolio.srf`
|
||||||
|
has an `accounts.srf` entry. Every held symbol has a
|
||||||
|
`metadata.srf` entry (or is opted out). Every snapshot file
|
||||||
|
parses as a portfolio. `transaction_log.srf` records reference
|
||||||
|
real account names.
|
||||||
|
- **Environment audit.** Which API keys are set (presence only,
|
||||||
|
never the value). Cache size + symbol count. `ZFIN_SERVER`
|
||||||
|
reachability if set (HEAD/OPTIONS, low timeout). Staleness of
|
||||||
|
hand-maintained data files (T-bill rates, Shiller `ie_data.csv`)
|
||||||
|
— same registry as `data/staleness.zig`.
|
||||||
|
- **Output shape.** Sectioned, color-coded. Every check is
|
||||||
|
`OK` / `WARN` / `FAIL` with a one-line context. Exit code 0 on
|
||||||
|
all-OK or warnings only; non-zero only on FAIL. Suitable for
|
||||||
|
CI / cron / pre-commit.
|
||||||
|
|
||||||
|
### Driver
|
||||||
|
|
||||||
|
The file constellation has grown to nine files in three locations,
|
||||||
|
plus five API keys, plus the cache, plus the optional server.
|
||||||
|
Today only the README explains the structure. A `doctor` command
|
||||||
|
surfaces it, catches regressions, and is the obvious place to point
|
||||||
|
new users (or to point future-self after a long break).
|
||||||
|
|
||||||
|
### Open question for when this is picked up
|
||||||
|
|
||||||
|
Does this *replace* the portfolio-hygiene portion of `audit`, or
|
||||||
|
live alongside it? Probably alongside — `audit` is reconciliation
|
||||||
|
against external broker exports, `doctor` is internal-state
|
||||||
|
validation. But worth confirming the boundary before implementing
|
||||||
|
to avoid duplicated checks.
|
||||||
|
|
||||||
|
## TUI tab re-order — priority LOW
|
||||||
|
|
||||||
|
Current tab order interleaves the two natural categories:
|
||||||
|
|
||||||
|
```
|
||||||
|
Portfolio Quote Performance Options Earnings Analysis History Projections
|
||||||
|
P S S S S P P P
|
||||||
|
```
|
||||||
|
|
||||||
|
That's `P S S S S P P P` — the per-portfolio tabs (P) are split
|
||||||
|
across the bar (slot 1, then slots 6-8). Reflects history of feature
|
||||||
|
additions rather than current shape.
|
||||||
|
|
||||||
|
### Suggested re-order to consider
|
||||||
|
|
||||||
|
```
|
||||||
|
Portfolio Analysis History Projections | Quote Performance Options Earnings
|
||||||
|
```
|
||||||
|
|
||||||
|
"Your money" tabs first (1-4), "research a symbol" tabs second
|
||||||
|
(5-8). Categorical split is clean; the 4↔5 boundary is a natural
|
||||||
|
divider in the tab bar.
|
||||||
|
|
||||||
|
### Alternative orderings worth weighing
|
||||||
|
|
||||||
|
- `Portfolio Analysis Projections History | Quote Performance Earnings Options`
|
||||||
|
— within each category, ordered by frequency of use rather than
|
||||||
|
by category cohesion.
|
||||||
|
- Status quo with just Analysis moved up next to Portfolio (minimum
|
||||||
|
churn).
|
||||||
|
|
||||||
|
### Mechanics
|
||||||
|
|
||||||
|
The `tab_modules` registry in `src/tui.zig` makes the actual reorder
|
||||||
|
a one-edit change (reorder fields in the registry literal). Cost is
|
||||||
|
in retraining muscle memory and updating the README's keybinding
|
||||||
|
table + screenshots.
|
||||||
|
|
||||||
|
### Doc drift to fix while we're there
|
||||||
|
|
||||||
|
README still says "six tabs," actual count is eight (Portfolio,
|
||||||
|
Quote, Performance, Options, Earnings, Analysis, History,
|
||||||
|
Projections).
|
||||||
|
|
||||||
|
## Unified time-range flag parser — priority LOW
|
||||||
|
|
||||||
|
`compare`, `contributions`, `projections --vs`, and `projections
|
||||||
|
--as-of` all accept overlapping but slightly different time-range
|
||||||
|
inputs. Today there are two helpers in `src/commands/common.zig`
|
||||||
|
(`parseAsOfDate`, `parseCommitSpec`) and the conflict-detection
|
||||||
|
logic (`since != null and before_spec != null`, etc.) is duplicated
|
||||||
|
across each command's flag-parsing block in `main.zig`.
|
||||||
|
|
||||||
|
### Sketch
|
||||||
|
|
||||||
|
```zig
|
||||||
|
// src/commands/common.zig
|
||||||
|
pub const TimeRange = struct {
|
||||||
|
before: Endpoint,
|
||||||
|
after: Endpoint,
|
||||||
|
pub const Endpoint = union(enum) {
|
||||||
|
date: Date,
|
||||||
|
commit_spec: CommitSpec,
|
||||||
|
live,
|
||||||
|
head,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
pub fn parseTimeRange(args: ...) !TimeRange { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
Each command consumes a `TimeRange` and decides which endpoint
|
||||||
|
combinations make sense:
|
||||||
|
- `compare` rejects `live`-on-both-sides.
|
||||||
|
- `contributions` rejects `live` entirely (no meaningful "live
|
||||||
|
contributions diff").
|
||||||
|
- `projections --vs` accepts `live` on either side.
|
||||||
|
|
||||||
|
### Naturally folds into the CLI architecture overhaul
|
||||||
|
|
||||||
|
Once each command has its own `parseArgs()` (per the CLI overhaul
|
||||||
|
entry above), the time-range parser becomes a shared utility those
|
||||||
|
parsers call. Could either land standalone or be pulled in as part
|
||||||
|
of the overhaul. If the overhaul lands first, this is one of its
|
||||||
|
follow-ups; if this lands first, the overhaul inherits it for free.
|
||||||
|
|
||||||
|
## Split `audit.zig` into per-broker reconcilers — priority LOW
|
||||||
|
|
||||||
|
`src/commands/audit.zig` is 3438 lines — the largest command file
|
||||||
|
by ~2x. It bundles four logically distinct responsibilities:
|
||||||
|
|
||||||
|
- Portfolio hygiene check (no-flag mode)
|
||||||
|
- Fidelity positions CSV reconciler (`--fidelity`)
|
||||||
|
- Schwab per-account positions CSV reconciler (`--schwab`)
|
||||||
|
- Schwab account-summary stdin parser (`--schwab-summary`)
|
||||||
|
|
||||||
|
### Sketch
|
||||||
|
|
||||||
|
```
|
||||||
|
src/commands/audit/
|
||||||
|
mod.zig ← thin dispatcher; current public `run()` lives here
|
||||||
|
hygiene.zig ← portfolio hygiene check (no-flag mode)
|
||||||
|
fidelity.zig ← --fidelity CSV reconciler
|
||||||
|
schwab.zig ← --schwab CSV + --schwab-summary stdin reconciler
|
||||||
|
common.zig ← shared types (Discrepancy, ReconcileResult), formatters
|
||||||
|
```
|
||||||
|
|
||||||
|
Adding a new broker (Vanguard, Robinhood, etc.) becomes a one-file
|
||||||
|
add against a documented contract. The hygiene check can be
|
||||||
|
referenced from `zfin doctor` (above) without pulling in CSV-parser
|
||||||
|
baggage.
|
||||||
|
|
||||||
|
### Driver
|
||||||
|
|
||||||
|
Maintenance friction. The next person adding a broker reconciler
|
||||||
|
— likely future-you — has to navigate 3438 lines to find the
|
||||||
|
pattern. The split also makes the audit-bug investigations already
|
||||||
|
in this TODO file (phantom discrepancy on freshly-added lots) easier
|
||||||
|
to localize.
|
||||||
|
|
||||||
|
Pure internal refactor; no user-visible change.
|
||||||
|
|
||||||
## Refactor: trim `src/format.zig` once Money / Date have absorbed their helpers — priority LOW
|
## Refactor: trim `src/format.zig` once Money / Date have absorbed their helpers — priority LOW
|
||||||
|
|
||||||
`src/format.zig` is still a ~1700-line grab-bag, but the money- and
|
`src/format.zig` is still a ~1700-line grab-bag, but the money- and
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue