## P0 - None. ## P1 1. **Economic no-ops are still reported as updates and create false wealth-refresh snapshots.** - `src/jarvis_finance/market_data/prices.py:526-532` correctly detects an identical observation and returns the existing observation, but `store_market_price()` discards that result and always returns the mutable `market_price_id` at `:597-612,660-661`. - `src/jarvis_finance/services/market_service.py:389-397` increments `updated` for every fresh provider response, regardless of whether the economic observation was a no-op. - `src/jarvis_finance/services/asset_price_refresh.py:388-413` creates an `aggregated_wealth_refresh_snapshots` row whenever this inflated `total_updated` is positive. - Adversarial reproduction: fetching the same price and provider timestamp twice after forcing cache staleness returned `updated_counts 1 1`, while `market_price_observations` remained at `1`. Two jobs would therefore create two wealth snapshots for one economic observation. - **Required fix:** propagate an inserted/no-op result from observation storage through batch/source results, and create a wealth snapshot only for genuinely new or corrected economic observations. 2. **Crypto refresh remains non-idempotent and has no correction lineage.** - `src/jarvis_finance/market/providers.py:164-176` always generates a new ID using fetch-time `now`, inserts a new `crypto_prices` row, and commits—even when provider timestamp and economic payload are unchanged. - `src/jarvis_finance/services/asset_price_refresh.py:234-240` then reports those writes as updates, feeding the same false wealth-snapshot condition. - There is no stable provider-timestamp identity, payload hash, predecessor, or correction audit for crypto observations. The hotfix therefore does not satisfy the asset-refresh requirement across all refreshed asset classes. 3. **The “canonical” payload is not numerically canonical.** - `src/jarvis_finance/market_data/prices.py:474-475` hashes `format(decimal, "f")`, which preserves insignificant trailing zeros. - Consequently, economically identical values such as `Decimal("10.0")` and `Decimal("10.00")` produce different payload hashes and append a false correction version. - Adversarial reproduction produced observations `[(1, '10.0'), (2, '10.00')]` and one `market_price_observation_corrected` audit. - Provider timestamp strings are likewise hashed without instant normalization at `:477,518-525`, so equivalent `Z` and `+00:00` timestamps can receive different identities. - **Required fix:** normalize decimal values and provider timestamps before identity/hash construction. 4. **Schema-52 migration does not preserve predecessor lineage for existing prices, and correction audits are mutable.** - `src/jarvis_finance/storage/migrations.py:2971-3003` creates an empty observation table but performs no deterministic backfill from existing `market_prices`. - On the first post-migration correction, `src/jarvis_finance/market_data/prices.py:533-567` therefore finds no predecessor, records version 1 rather than a correction version, and emits no `market_price_observation_corrected` audit. - The audit immutability triggers at `src/jarvis_finance/storage/migrations.py:2947-2954` cover only `manual_source_snapshot` and `asset_price_refresh_job`, not `market_price_observation`. - Adversarial reproduction successfully deleted a generated correction audit while both observation versions remained. - **Required fix:** backfill/reconcile schema-52 price rows into stable version-1 observations and make observation correction audits update/delete-proof. 5. **The cutoff and quality-count UI is materially misleading.** - `frontend/src/components/wealth/WealthCockpitPanel.vue:38-40` labels `modelled.current.date` as the “Verwendeter Kursstichtag.” That is the modelled row date, not necessarily the actual provider-price cutoff; carried components may have older `as_of` dates. - `:111` counts component categories—not valuations—and adds `unknown_accounts` to incomplete/unavailable components, allowing the same missing scope to be counted twice. The UI nevertheless labels these as “Bewertungen” at `:40`. - This fails the explicit cutoff/quality-count requirement and can overstate freshness. - **Required fix:** expose backend-derived effective price cutoff and disjoint valuation-quality counts. 6. **The repository’s required verification gate is broken by the new test import.** - `tests/unit/test_market_observation_idempotency.py:10` imports `tests.unit...`, but `tests` is not an importable package in the Makefile’s test invocation. - `Makefile:5-6,23` runs bare `pytest tests -q`; `make PYTHON=python3 verify` failed during collection with `ModuleNotFoundError: No module named 'tests'`. - Focused tests passed (`18 passed`), but the mandated full gate cannot complete. - **Required fix:** move shared fixtures to an importable support module, use a robust local import, or package the test tree consistently. ## P2 - None beyond the blocking issues above. ## Verdict **BLOCK** — P0=0, P1=6. The exact staged tree is `8ea8ad008f6af8431080306d6d6ff1ce45ac106d`; no files were modified.