{# Inline explainer + suppress action for one verify finding. Context: `f: VerifyFinding`. Keep copy concise — one paragraph of "what this means" + one paragraph of "what to do". Numbers come from the row. #}
Overview market value disagrees with the sum of per-lot values.
We render ${{ '%.2f'|format(f.ours or 0) }} on the dashboard, but the
per-lot loop totals ${{ '%.2f'|format(f.theirs or 0) }}
(Δ ${{ '%.2f'|format(f.delta or 0) }}).
Most often this means a lot was added or split-adjusted but the aggregator wasn’t refreshed. Re-import the affected ticker’s CSV or re-run any pending corporate-action sync.
{% elif f.rule_id == "OV-2" %}Unrealized P&L ≠ market value − cost basis.
Components don’t reconcile (Δ ${{ '%.2f'|format(f.delta or 0) }}).
Usually a stale price snapshot or a lot whose adjusted basis was rewritten without recomputing P&L. Refresh prices on /portfolio and re-render.
{% elif f.rule_id == "OV-3" %}Realized YTD on the Overview disagrees with the closed-lot ledger. One side is missing a closure.
Check /imports — a partial Realized G/L import is the typical cause.
{% elif f.rule_id == "AL-1" %}Allocation slices don’t sum to 100%. The donut renders {{ '%.2f'|format(f.ours or 0) }}% across all slices (cash included).
This is almost always a rounding-tolerance miss; if persistent, a hidden slice (cash sleeve or excluded symbol) is the cause.
{% elif f.rule_id == "AL-2" %}Leaderboard $ weights don’t add up to total market value.
Drift ${{ '%.2f'|format(f.delta or 0) }}.
Open /positions and look for a symbol present in the donut but not the table (or vice versa) — usually an option lot that escaped the equity-only filter.
{% elif f.rule_id == "PL-1" %}A lot’s market value ≠ qty × current price.
Scope {{ f.scope }}, drift ${{ '%.2f'|format(f.delta or 0) }}.
Stale or zero quote, or a manually-edited row. Refresh quotes; if the price provider is offline, the lot will mark to zero.
{% elif f.rule_id == "PL-2" %}A lot’s unrealized P&L ≠ market value − adjusted basis.
Likely a wash-sale basis adjustment that ran after the position snapshot. Re-import the impacted account or click Run verification now after the engine catches up.
{% elif f.rule_id == "PL-3" %}Holding-period bucket (ST vs LT) doesn’t match the tacked acquired date.
Most often a §1223(4) wash-sale tack rewrite that didn’t propagate to the positions view. Refresh /positions.
{% elif f.rule_id == "XP-1" %}/positions doesn’t sum to the Overview total.
Cross-page drift of ${{ '%.2f'|format(f.delta or 0) }}.
One page was rendered against a stale price snapshot. Hard-refresh both tabs; if it persists, the lot adapters are out of sync.
{% elif f.rule_id == "RealizedRecon" %}Computed realized P&L disagrees with the broker’s Realized G/L file.
Scope {{ f.scope }}, Δ ${{ '%.2f'|format(f.delta or 0) }}.
Open the per-ticker reconciliation view to see the row-level diff. Common causes: split-adjustment timing, a manual trade with a stale basis, or a missing wash-sale rollover.
{% elif f.rule_id == "BasisRecon" %}Aggregate cost basis disagrees with the broker’s All-Positions file.
Scope {{ f.scope }}, Δ ${{ '%.2f'|format(f.delta or 0) }}.
Re-check the basis editor on the symbol page; spinoffs, partial rollovers, and DRIP shares are the usual suspects.
{% elif f.rule_id == "MarketValueRecon" %}Aggregate market value disagrees with the broker file.
Scope {{ f.scope }}, Δ ${{ '%.2f'|format(f.delta or 0) }}.
Almost always a price-snapshot timing difference (broker file captured pre-market, our quote is intraday). Re-export the broker CSV at the same time-of-day used in the previous comparison.
{% elif f.rule_id == "PositionsQty" %}Share quantity disagrees with the broker.
Scope {{ f.scope }}, ours {{ '%g'|format(f.ours or 0) }}
vs. broker {{ '%g'|format(f.theirs or 0) }}.
A trade is missing from one side — either a recent fill we haven’t imported, or a corporate-action share count the engine hasn’t applied.
{% elif f.rule_id == "PositionsMissingLocal" %}Broker shows a position we don’t have.
Scope {{ f.scope }}; broker qty
{{ '%g'|format(f.theirs or 0) }}.
Likely a missing trade import or an inbound transfer. Add the opening trade on the symbol page or upload the broker transactions CSV.
{% elif f.rule_id == "PositionsMissingBroker" %}We hold a position the broker file doesn’t show.
Scope {{ f.scope }}; our qty
{{ '%g'|format(f.ours or 0) }}.
The broker All-Positions file may be from a different account or a prior date. Re-export from the correct account and re-upload.
{% elif f.rule_id == "StaleReference" %}The broker All-Positions file is stale (or missing). Without a fresh reference, the broker reconciliation checks can’t run.
Go to /imports and drop in a fresh Schwab All-Positions CSV — anything inside the last 30 days clears this finding.
{% else %}Rule {{ f.rule_id }} failed.
Scope {{ f.scope }}{% if f.delta is not none %},
Δ {{ '%.4f'|format(f.delta) }}{% endif %}.
No specific guidance is wired up yet for this rule. Check the detail blob below for hints.
{% endif %} {% if f.detail_json %}{{ f.detail_json }}