diff --git a/docs/README.md b/docs/README.md index 4ed2763..e18a419 100644 --- a/docs/README.md +++ b/docs/README.md @@ -61,6 +61,7 @@ shipped binary. - [`projections.srf`](reference/config/projections-srf.md) -- retirement projection inputs - [`watchlist.srf`](reference/config/watchlist-srf.md) -- price-only symbols - [`transaction_log.srf`](reference/config/transaction-log-srf.md) -- declared transfers + - [`acknowledgments.srf`](reference/config/acknowledgments-srf.md) -- acknowledged review findings - [`keys.srf`](reference/config/keys-srf.md) -- TUI keybindings - [`theme.srf`](reference/config/theme-srf.md) -- TUI colors - [Environment variables](reference/config/environment.md) -- API keys, `ZFIN_HOME`, `ZFIN_CACHE_DIR`, and more diff --git a/docs/guides/read-your-portfolio.md b/docs/guides/read-your-portfolio.md index 38c62d9..459bad4 100644 --- a/docs/guides/read-your-portfolio.md +++ b/docs/guides/read-your-portfolio.md @@ -144,6 +144,26 @@ outliers, and tiny positions against configurable thresholds. The status icons at the top summarize which checks fired. See [`zfin review`](../reference/cli/review.md). +### Acknowledging findings + +Findings are re-derived every run, so one you've already judged +acceptable would otherwise nag you forever. Acknowledge it to record +*why* and drop it from the active list. This is an interactive step -- +in the [TUI](../reference/tui.md) Review tab, move the cursor to a +finding and press: + +- **`a`** -- acknowledge it (you can type a short reason, saved as a note). +- **`U`** -- un-acknowledge (return it to the active list). +- **`v`** -- toggle whether already-acknowledged findings are shown. + +(Those are the defaults; `?` shows your current bindings.) +Acknowledgments persist in +[`acknowledgments.srf`](../reference/config/acknowledgments-srf.md) +beside your portfolio, so they survive across runs and are versioned +with the rest of your data. The CLI honors them -- `zfin review +--show-acked` includes acked findings in the table -- but only the TUI +can add or remove them. + ## `exposure`: look-through to a single symbol ```bash diff --git a/docs/reference/cli/review.md b/docs/reference/cli/review.md index 50f923b..c7f9709 100644 --- a/docs/reference/cli/review.md +++ b/docs/reference/cli/review.md @@ -41,9 +41,28 @@ ZFIN_HOME=examples/pre-retirement-both zfin review ❌️ Diversified sector at 85.7% (warn at 60.0%, flag at 75.0%) ``` +## Findings and acknowledgments + +The findings header counts findings by state -- `(2 active, 0 acked, 0 +resolved)`: + +- **active** -- live; shown by default. +- **acked** -- you acknowledged it; hidden unless `--show-acked`. +- **resolved** -- the condition no longer holds; set by the engine. + +Acknowledging a finding records your reasoning and drops it from the +active list. **That is a TUI action** -- `zfin review` only *displays* +findings and never writes acknowledgments. In the +[interactive TUI](../tui.md) Review tab, put the cursor on a finding and +press `a` to acknowledge (with an optional note), `U` to un-acknowledge, +and `v` to toggle showing acked findings. The decisions persist in +[`acknowledgments.srf`](../config/acknowledgments-srf.md) beside your +portfolio. + ## See also - [Read your portfolio](../../guides/read-your-portfolio.md#review-per-holding-performance-and-risk) +- [`acknowledgments.srf`](../config/acknowledgments-srf.md) -- where acknowledged findings persist. - [Returns and performance](../../explanation/returns-and-performance.md) -- the metric definitions. - [`analysis`](analysis.md) -- portfolio-level breakdowns. diff --git a/docs/reference/config/acknowledgments-srf.md b/docs/reference/config/acknowledgments-srf.md new file mode 100644 index 0000000..b21fe71 --- /dev/null +++ b/docs/reference/config/acknowledgments-srf.md @@ -0,0 +1,79 @@ +# `acknowledgments.srf` reference + +`acknowledgments.srf` is an optional sibling of `portfolio.srf` that +records which [`review`](../cli/review.md) **findings** you've +acknowledged -- and why. The observation engine re-derives findings +("Position concentration: NVDA at 18.2%") on every run; acknowledging +one suppresses it from the active list and saves your reasoning, so the +decision survives across runs instead of nagging you every time. + +Missing file -> every finding is simply "active"; nothing is suppressed. + +## Who writes it + +The **interactive TUI's Review tab** writes this file when you +acknowledge (`a`) or un-acknowledge (`U`) a finding. The CLI +[`zfin review`](../cli/review.md) is **read-only**: it shows findings +and honors existing acknowledgments (`--show-acked` to include them in +the table) but never edits the file. It is plain SRF, so you *can* edit +it by hand, but it's normally managed for you. + +## File format + +Compact-form SRF with two record types. Each `type::note` attaches to +the most recent `type::acknowledgment` above it (a note before any ack +is a parse error): + +```srf +#!srfv1 +type::acknowledgment,observation::position_concentration,target::NVDA,acknowledged_at::2026-06-08,state::acknowledged +type::note,line::Holding through the earnings cycle. +type::note,line::Will trim by Q3. +type::acknowledgment,observation::sector_dominance,target::VTI,SCHD,acknowledged_at::2026-06-08,state::acknowledged +``` + +Each acknowledgment is uniquely keyed by `(observation, target)` -- +there is never more than one record per pair; a state change mutates it +in place (git tracks the history of the file). + +### `type::acknowledgment` + +| Field | Type | Required | Description | +|---------------------|--------|----------|---------------------------------------------------------------------------------------------------------------| +| `observation` | string | Yes | The check that fired, e.g. `position_concentration`, `sector_dominance`. | +| `target` | string | Yes | What it's about: `NVDA` (single symbol), `sector:Technology` (sector), or `VTI,SCHD` (a pair, for dominance). | +| `acknowledged_at` | date | Yes | Date first acknowledged (`YYYY-MM-DD`). Immutable after creation. | +| `state` | enum | Yes | `active`, `acknowledged`, or `resolved` (see below). | +| `unacknowledged_at` | date | No | Breadcrumb: when you last un-acked. Persists across re-acks. | +| `resolved_at` | date | No | Breadcrumb: when the engine auto-resolved the finding. | + +### `type::note` + +| Field | Type | Required | Description | +|--------|--------|----------|---------------------------------------------------------------------------------------------------------------------| +| `line` | string | Yes | One line of your reasoning. Multi-line notes are several consecutive `note` records attached to the ack above them. | + +## State lifecycle + +| State | Meaning | +|----------------|---------------------------------------------------------------------------------------| +| `active` | Live and shown in the active findings list (effectively un-acked). | +| `acknowledged` | You've acked it; hidden from the active list unless you pass `--show-acked`. | +| `resolved` | The underlying condition no longer holds (e.g. you trimmed the position); engine-set. | + +Transitions -- driven from the TUI, except `resolved`, which the engine +sets on its own: + +- **acknowledge** (`active -> acknowledged`) -- clears `unacknowledged_at`. +- **un-acknowledge** (`acknowledged -> active`) -- stamps `unacknowledged_at`. +- **auto-resolve** (`* -> resolved`) -- stamps `resolved_at`. +- **reappear** (`resolved -> active`) -- clears `resolved_at` when the condition returns. + +## See also + +- [Read your portfolio](../../guides/read-your-portfolio.md#review-per-holding-performance-and-risk) -- acknowledging findings in the TUI. +- [`zfin review`](../cli/review.md) -- the findings dashboard (read-only for acks). + +--- + +[Documentation home](../../README.md#reference)