# Broker Review Queue Hardening (Phase B4)

Use this reference when hardening PostFinance/True Wealth/Raiffeisen dry-run review workflows after real-file dry runs have produced runtime-only review items. Goal: make review items editable/auditable for later import readiness while still blocking all productive portfolio writes.

## Non-negotiables

- No productive `positions_snapshot`, `transactions`, `cash_balances`, ledger, or cash snapshot writes during review-queue hardening.
- Real broker/bank files may remain runtime/temp-only; never copy them into Git.
- Chat/reporting stays aggregate-only: counts/statuses/quality flags, no real quantities, prices, market values, rows, account balances, or raw labels if they reveal value.
- Runtime UI may display detail to the authenticated local user; Git/tests/fixtures/docs must use synthetic data.
- All manual review actions write audit-log entries.

## Schema/read model additions

Extend `broker_import_review_items` with metadata needed for manual review and readiness:

- detected identifiers: `isin`, `ticker`, `exchange`
- mapping IDs/status: `instrument_id`, `account_id`, `account_mapping_status`
- booleans: `detected_quantity_present`, `detected_market_value_present`, `reviewer_confirmed`, `snapshot_date_confirmed`, `ticker_exchange_confirmed`
- status: `review_status`, `import_readiness_status`
- notes/timestamps: `reviewer_note`, `updated_at`

Extend `broker_import_dry_runs` with session management:

- `session_status` (`active`, `archived`, `discarded`)
- `is_current`
- `updated_at`

Always keep old dry-run/review/audit history; archive/discard changes status only.

## Review Queue filters and detail

Filters to support:

- source platform: PostFinance / True Wealth / Raiffeisen
- dry run ID
- review status: open / blocked / mapped / ignored / resolved
- quality flag: `missing_isin`, `missing_ticker`, `snapshot_only`, `cost_basis_uncertain`, `market_value_legacy`, `missing_fx`, `aggregate_only`, `missing_instrument_details`, `cash_snapshot_candidate`, `needs_manual_review`
- asset class
- currency
- account mapping status
- import readiness

Detail view should show source, dry-run ID, source row reference, redacted/source label as appropriate, normalized name, detected asset class/currency/ISIN/ticker/exchange, account mapping, quality flags, review status, readiness, reviewer note, and audit history.

## Manual review actions

Implement as backend functions first; UI can call them later.

1. **Update ISIN**
   - Validate coarse ISIN shape (`^[A-Z]{2}[A-Z0-9]{9}[0-9]$`).
   - Remove `missing_isin` when valid.
   - Do not auto-map by name.
   - Write audit.

2. **Update ticker/exchange**
   - Normalize ticker/exchange to uppercase.
   - If ticker exists without exchange, keep item review-needed and add `ticker_without_exchange`/do not mark mapped.
   - Write audit.

3. **Confirm instrument mapping**
   - With ISIN: may map to an existing instrument.
   - With ticker+exchange: require explicit confirmation (`ticker_exchange_confirmed`).
   - Name-only must never become mapped/ready.
   - Write audit and refresh readiness.

4. **Confirm account mapping**
   - Map source account to internal account.
   - Keep cash and brokerage separate; reject cash review items mapped to brokerage-only accounts and equity/ETF items mapped to cash-only accounts.
   - Write audit and refresh readiness.

5. **Ignore with note**
   - Non-empty note is mandatory.
   - Set status/readiness to ignored.
   - Write audit.

6. **Mark resolved**
   - Only allowed when readiness is already `ready_for_import` or all mandatory problems are cleared by equivalent metadata.
   - Write audit.

7. **Dry run session management**
   - Archive/discard/mark-current must write audit.
   - Mark-current should clear current marker for other sessions from same source.
   - No productive import side effects.

## Import readiness rules

Per-item readiness values:

- `not_ready`
- `review_needed`
- `ready_for_import`
- `ignored`
- `blocked`

Equity/ETF can be `ready_for_import` only if all hold:

- source platform present
- account mapping present
- asset class is `equity` or `etf`
- ISIN present, or ticker+exchange explicitly confirmed
- currency present
- snapshot date confirmed or clearly detected
- quantity present
- no blocking quality flags
- reviewer confirmation present

Cash can be `ready_for_import` only if all hold:

- account mapping present
- currency present
- snapshot date confirmed or clearly detected
- amount/market-value presence detected
- reviewer confirmation present

Aggregate/Raiffeisen guard:

- `aggregate_only` is always blocked for instrument import.
- It may only become a cash/control snapshot if independently classified as cash and explicitly reviewed.

Blocking flags generally include: `aggregate_only`, `missing_instrument_details`, `missing_currency`, `missing_quantity`, `missing_fx` for CHF-required flows, and unresolved account/instrument mapping gaps.

## Data Quality Center additions

Show:

- review item counts per quality flag, including `open` and `blocked` items
- readiness counts (`ready_for_import`, `not_ready`, `review_needed`, `blocked`, `ignored`)
- per-source ready count and warning when a source has zero ready items
- warnings for missing snapshot date and high missing ISIN/ticker counts
- links/navigation hints to filtered Review Queue

Buttons may be present for recheck/resolve/refresh, but productive import buttons stay disabled until a later explicit phase.

## Test matrix

Minimum tests:

- filters by source, dry-run ID, quality flag
- ISIN update validates and writes audit
- ticker/exchange update writes audit
- name-only never maps automatically
- ISIN mapping can become ready for equity/ETF when all other gates are met
- ticker without exchange remains review-needed
- account mapping writes audit and rejects cash/brokerage mixing
- ignore requires note
- mark resolved refuses unresolved mandatory problems
- readiness for equity, ETF, cash
- aggregate-only remains blocked for instrument import
- dry-run archive/discard/mark-current writes audit
- Data Quality Center exposes readiness/status counts
- productive import remains disabled and productive tables unchanged
- Git-safety OK and no real files in repo

## Verification sequence

1. Apply migrations to runtime DB.
2. Refresh readiness for existing review items.
3. Verify aggregate status/readiness counts only.
4. Verify productive tables unchanged (`positions_snapshot`, `transactions`, `cash_balances`).
5. Run compile and pytest.
6. Remove `__pycache__`/`.pytest_cache` before Git-safety.
7. Run Git-safety, commit, push, and report only aggregate counts.
