## P0 — 0 None. ## P1 — 6 1. **Partial market-job retries destroy “immutable” valuation history** `src/jarvis_finance/services/portfolio_analytics.py:610-649` Partial runs are retried with the same deterministic snapshot IDs, but `INSERT OR REPLACE` deletes and recreates existing snapshots. A synthetic retry changed two snapshots from version 1 to version 2 while the row count and IDs stayed unchanged, bypassing the schema-49 update/delete immutability contract. **Fix:** use append-only IDs per version, plain `INSERT`, and explicit supersession lineage; make an identical partial retry a true no-op. 2. **Market-run fingerprint does not bind valuation inputs** `src/jarvis_finance/services/portfolio_analytics.py:423-432` The fingerprint includes positions, policy, and cash, but excludes accepted market prices, FX rates, provider mappings, and their provenance. Once a run is complete, corrected provider data is never consulted. A probe changed a CHF quote from 100 to 101; the second run returned idempotently and retained 100. **Fix:** resolve cutoff-safe price/FX/mapping inputs before idempotency lookup and include their complete provenance and values in the fingerprint. 3. **Backfill preview fingerprint is not bound to required historical inputs** `src/jarvis_finance/services/performance_activation.py:348-405,443-475` The fingerprint covers accounts, coverage, and missing days only. Adding the exact historical crypto price after Preview left the fingerprint unchanged, so Confirm can consume inputs that were never previewed. PostFinance provider/FX inputs are similarly unbound. **Fix:** preview and fingerprint the exact per-day transaction/position, price, FX, mapping, and provenance inputs; Confirm must consume only that frozen input set. 4. **Incomplete backfills are finalized as confirmed and permanently idempotent** `src/jarvis_finance/services/performance_activation.py:454-511` Confirm does not require every child run to be complete. A two-day probe produced `['complete', 'partial']`, recorded `performance_backfill_confirmed`, and subsequent use of the same confirmation ID returned idempotent success while one day remained missing. **Fix:** do not record a successful confirmation unless every requested day is complete; persist an auditable resumable/incomplete state and allow safe continuation. 5. **Confirmation IDs are not bound to their original payloads** `src/jarvis_finance/services/performance_activation.py:188-213,414-450` Both activation and backfill return idempotent success solely by `confirmation_id`, before validating source, period, preview ID, or fingerprint. A different payload with bogus preview/fingerprint was accepted as an idempotent repeat. **Fix:** store a unique confirmation ID plus payload hash/fingerprint and reject reuse with any different payload; enforce uniqueness transactionally. 6. **Crypto activation accepts semantically incompatible pre-existing stable IDs** `src/jarvis_finance/services/performance_activation.py:219-260` Existing `performance-crypto-*` objects are checked only for existence. A synthetic EUR bank/cash account was accepted and classified as `crypto_portfolio`. This can attach CHF crypto valuations to the wrong account role and currency. **Fix:** validate every pre-existing platform/account field—type, currency, linkage, active state, ownership, portfolio bucket, and performance semantics—and fail closed on drift. ## P2 — 3 1. **Daily-job status does not report the timer service’s actual activation state** `src/jarvis_finance/services/performance_activation.py:30-68` It reads the API process environment, while activation is configured in a different systemd service. The API can therefore report disabled while the timer job is enabled, or vice versa; `configured` is also hard-coded. **Fix:** use a shared authoritative activation configuration or inspect a sanitized systemd/runtime status artifact. 2. **Coverage endpoint turns incomplete query pairs into an internal error** `src/jarvis_finance/api/routers/overview.py:158-167` and `src/jarvis_finance/services/portfolio_performance.py:67-68` Supplying only `from` or only `to` raises an uncaught `ValueError`, yielding a 500 rather than a controlled client-validation response. **Fix:** validate the pair in the route/schema and return 400/422. 3. **Unexplained 335 KB `uv.lock` is included in the untracked candidate** `uv.lock` The repository currently uses `pyproject.toml`, `requirements-ci.txt`, and npm’s lockfile, with no tracked `uv` workflow. Committing this generated lock would add unrelated dependency/toolchain scope without corresponding dependency review. **Fix:** remove it from the candidate or explicitly adopt and verify the locked runtime workflow. **Counts:** P0: 0 · P1: 6 · P2: 3 · **Total: 9**