{# 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. #}
{% if f.rule_id == "OV-1" %}

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 %}
Raw detail
{{ f.detail_json }}
{% endif %}