add todo items after review of full application surface
This commit is contained in:
parent
ddb50923ca
commit
adf33b9e21
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),
|
||||
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
|
||||
|
||||
`src/format.zig` is still a ~1700-line grab-bag, but the money- and
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue