## Verdict: **DO NOT SHIP** **P0: 0 · P1: 7 · P2: 2** ### P0 None found. ### P1 1. **Recipient-name substring matches are incorrectly classified as secure.** `src/jarvis_finance/services/truewealth_productization.py:172-178,208-218,261` The supposed `recipient_name_exact` check uses substring containment. A transfer to `Example Recipient Scam` is therefore secure and confirmable for rule name `Example Recipient`, even when its counterparty hash differs. Reproduced locally. The negative test at `tests/unit/test_sprint20d_truewealth_productization.py:181-189` covers only a nonmatching partial name and misses suffix/prefix collisions. Require exact normalized recipient identity or a genuinely stable identifier; add adversarial collision cases. 2. **Manual-cashflow deduplication can suppress a legitimate payment.** `src/jarvis_finance/services/truewealth_productization.py:153-156,209-223` Any existing confirmed True Wealth deposit with the same date, absolute amount, and currency is treated as the same economic event, without bank transaction ID, raw fingerprint, recipient, source reference, or explicit manual linkage. Two legitimate equal payments on one day collapse. The test at `tests/unit/test_sprint20d_truewealth_productization.py:131-150` explicitly blesses this lossy match. Exact lineage should deduplicate; economic collisions should remain uncertain/manual-review items. 3. **“Internal wealth transfer / not expense” is preview text, not canonical overall-wealth behavior.** `src/jarvis_finance/services/truewealth_productization.py:190-204,273-305` Confirmation writes only a positive True Wealth `external_deposit`; it does not link or reclassify the originating Raiffeisen transaction as the other leg of an internal wealth transfer. The test at `tests/unit/test_sprint20d_truewealth_productization.py:125-128` asserts labels in the preview, not the actual overall-wealth reader. A probe confirmed the bank row remains an ordinary confirmed `transfer`. Add durable cross-leg lineage or canonical classification and exercise confirm → wealth cockpit/history to prove overall wealth and household income/expense remain neutral. 4. **True Wealth performance provenance and selected-period output are false.** `src/jarvis_finance/services/truewealth_valuation.py:472-500` `latest_confirmed_value` is assigned `points[-1]`, so a later modeled point is returned and displayed as confirmed by `frontend/src/components/performance/PortfolioPerformancePanel.vue:107-109,171`. Reproduced locally. The endpoint also returns every point rather than the requested/evaluated interval, and the chart consumes all of them at `PortfolioPerformancePanel.vue:192`, so a bounded period can render out-of-period history. Existing tests only check fallback dates (`tests/unit/test_sprint20d_truewealth_valuation.py:99-106`) and frontend mocks supply an already-correct contract (`PortfolioPerformancePanel.test.ts:34`). Filter period points and select confirmed/modelled values independently. 5. **Daily-source isolation does not survive a failed database transaction.** `src/jarvis_finance/services/truewealth_valuation.py:584-595` `src/jarvis_finance/cli/main.py:184-238` All workers share one SQLite connection, and the isolation wrapper catches exceptions without rollback or connection reset. A synthetic first worker that begins a transaction and fails caused the next worker to fail with `OperationalError`; the connection remained in a transaction. The test at `tests/unit/test_sprint20d_truewealth_valuation.py:122-132` uses stateless callbacks and therefore gives misleading isolation confidence. Use independent connections/transactions per source or mandatory rollback plus transaction-state verification. 6. **Recipient-rule activation is detached from the activated rule and does not automate future payment recognition.** `src/jarvis_finance/services/truewealth_productization.py:393-423` `src/jarvis_finance/services/truewealth_valuation.py:278-287` Activation only writes an audit record. No production path consumes the activated rule to classify future bank rows; the only consumer checks whether *any* historical `recipient_rule_activated` audit exists and uses that to enable modeled valuation. Thus an unrelated/stale rule can satisfy the valuation gate, while future payment capture remains manual. Bind activation to the exact current rule/account and add a future-row recognition acceptance test, or stop presenting it as future automation. 7. **The new payment/model path lacks end-to-end XIRR and TTWROR acceptance.** `tests/unit/test_sprint20d_truewealth_valuation.py:109-119` `tests/unit/test_sprint20d_truewealth_productization.py:153-168` Tests assert an arithmetic preview string and `xirr_readiness`, while the confirmation test stops at loaded activities. No test confirms bank payments and then executes the canonical performance reader to verify investment result and XIRR; no backend test proves provisional modeled values produce the intended TTWROR quality/value while XIRR remains unavailable where required. The frontend test uses mocked performance data. Add writer → canonical reader golden cases for XIRR, provisional TTWROR, and portfolio-level wealth neutrality. ### P2 1. **New GET contracts are unvalidated dictionaries.** `src/jarvis_finance/api/routers/overview.py:300-318` The True Wealth performance-view and model-preview endpoints return plain `dict` without response models. OpenAPI consequently exposes unrestricted `additionalProperties`, while the frontend assumes detailed structures at `frontend/src/api/portfolio.ts:43-44,145-146`. Add strict backend response schemas, bind them to both routes, and validate real service payloads/OpenAPI. 2. **Read-only acceptance covers only one of the new preview surfaces.** `tests/unit/test_sprint20d_truewealth_productization.py:192-226` The no-write sentinel covers bank-payment preview and a blocked confirm, but not activation-package preview, model preview, or performance view through HTTP. Add `total_changes`/logical-digest checks for every new preview route and verify all write endpoints remain blocked in read-only mode. ### Evidence - Focused Sprint 20B–20D backend tests: **46 passed** - Sprint 20D plus schema-49 focused tests: **19 passed** - Frontend performance tests: **11 passed** - `git diff --check`: passed - Reviewed base: `107de783afac36bf48c95a996fc920aec2a46270` - **Files modified by audit:** none