compliance: flag-gate AI portfolio + cloud sync + Stripe; de-risk prompts; harden reviewer

Implements docs/read-markets-compliance-changes.md as flag-gated changes
(no deletions) so paused features stay in the tree for future re-enable.
All four flags default False so a fresh deploy is compliance-safe.

- New env flags: PORTFOLIO_AI_ENABLED, PORTFOLIO_SYNC_ENABLED,
  TICKER_UNIVERSE_AGGREGATE_ENABLED, SUBSCRIPTIONS_ENABLED.
- Gates: /api/analyze, /api/portfolio/sync*, /api/stripe/*, /pricing,
  ticker_universe writes, portfolio_analysis.analyse(). is_paid_active()
  returns True for any auth'd user when subscriptions are paused.
- Prompts (PROMPT_VERSION 10): universal _COMPLIANCE_RIDER prepended to
  every system prompt; watch list removed; price-target / close-above-below
  / trigger / forward-state-as-description rules added; SPECULATIVE
  pivoted to regime-only scenarios; daily + weekly digests tightened.
- Reviewer: deterministic regex/lexicon pre-check fail-closed under the
  Haiku call; portfolio rider gated by PORTFOLIO_AI_ENABLED; base prompt
  sharpened for forward-state and MAR forward-opinion patterns;
  ReviewerVerdict audit table; generate_with_review retry helper.
- Migration 0026: purge portfolio_sync + ticker_universe; create
  reviewer_verdicts.
- Copy: MAR cite fixed to Art 3(1)(35) + Art 20 + Del Reg 2016/958;
  portfolio reframed as browser-only viewer in disclaimer / privacy /
  terms / about / pricing / landing (en + it). TODO(legal) marker for
  lawyer sign-off on disclaimer.
- Tests: 13 lexicon + 6 reviewer compliance regressions; conftest enables
  all flags so existing 402 tests still cover their code paths.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Giorgio Gilestro 2026-05-29 19:57:12 +02:00
parent ee8384f1ba
commit 47dce1a1a4
38 changed files with 1188 additions and 279 deletions

View file

@ -10,9 +10,9 @@
6-hour news feed, the cross-asset indicator panels, and a strategic
log refreshed every six hours. Paid stretches the news feed to a
full 24 hours, runs the strategic log hourly, unlocks the follow-up
chat against past logs, adds portfolio import with AI analysis, and
turns on the daily email digest on top of the Sunday recap everyone
gets.
chat against past logs, adds a browser-only portfolio composition
viewer, and turns on the daily email digest on top of the Sunday
recap everyone gets.
</p>
</section>
@ -33,7 +33,7 @@
<li><strong>Sunday weekly digest</strong> by email &mdash; week behind + week ahead, one-click unsubscribe</li>
</ul>
<div class="tier-card__more">
Need the full-day news feed, hourly strategic log, follow-up chat, daily digests, or portfolio analysis? See <strong>Paid</strong> &rarr;
Need the full-day news feed, hourly strategic log, follow-up chat, daily digests, or the portfolio composition viewer? See <strong>Paid</strong> &rarr;
</div>
<div class="tier-card__cta">
{% if cu and (cu.user or cu.is_admin) %}
@ -47,7 +47,7 @@
<div class="tier-card tier-card--featured">
<div class="tier-card__badge">Best value</div>
<h2 class="tier-card__name">Paid</h2>
<div class="tier-card__tagline">Full-day news feed, hourly strategic log, follow-up chat, and AI portfolio analysis.</div>
<div class="tier-card__tagline">Full-day news feed, hourly strategic log, follow-up chat, and the browser-only portfolio composition viewer.</div>
<div class="tier-card__price">&pound;7<span class="tier-card__price-unit"> / month</span></div>
<div class="tier-card__price-hint">
Or <strong>&pound;70 / year</strong> &mdash; two months free, and
@ -62,16 +62,8 @@
<li><strong>Strategic log refreshed every hour</strong> instead of every six &mdash; track intraday moves as they unfold</li>
<li><strong>Follow-up chat on any past log</strong> &mdash; ask the model a question against the day&rsquo;s full context</li>
<li><strong>Daily email digest</strong> (Mon&ndash;Sat) &mdash; ~600-word read of the session ahead, on top of the Sunday recap</li>
<li><strong>Portfolio import</strong> from any broker CSV &mdash; Trading 212 natively, other formats auto-detected</li>
<li><strong>AI portfolio read</strong> &mdash; diversification, sector and currency concentration, macro-regime fit on your holdings</li>
<li><strong>Optional encrypted cloud sync</strong> &mdash; PIN-derived encryption in your browser, second-layer wrap on the server, no plaintext holdings server-side</li>
<li><strong>Browser-only portfolio composition viewer</strong> &mdash; drop a broker CSV and see your sector, currency, and concentration breakdown, computed entirely in your browser</li>
</ul>
<p class="tier-card__more" style="font-style: italic;">
The portfolio feature does not produce buy, sell or hold
recommendations and does not consider your wider finances, debts,
tax position or objectives. It is not regulated investment advice
or a personal recommendation under FSMA / FCA COBS.
</p>
<div class="tier-card__cta">
{% if paid %}
<a class="btn-secondary btn-block" href="/settings">Manage subscription</a>
@ -190,17 +182,7 @@
<td class="compare-table__paid"><strong>Sunday + daily Mon&ndash;Sat</strong></td>
</tr>
<tr>
<th scope="row">Portfolio import (broker CSV)</th>
<td class="compare-table__none">&mdash;</td>
<td class="compare-table__paid"><strong>Included</strong></td>
</tr>
<tr>
<th scope="row">AI portfolio read</th>
<td class="compare-table__none">&mdash;</td>
<td class="compare-table__paid"><strong>Included</strong></td>
</tr>
<tr>
<th scope="row">Encrypted cloud sync</th>
<th scope="row">Browser-only portfolio composition viewer</th>
<td class="compare-table__none">&mdash;</td>
<td class="compare-table__paid"><strong>Included</strong></td>
</tr>
@ -265,18 +247,10 @@
<section class="public-section">
<h2 class="public-section__head">How the data is handled</h2>
<p>
Your portfolio holdings live in your browser&rsquo;s local storage by
default. The server only learns which Yahoo tickers appear across the
user base &mdash; an anonymous union, with no link back to any specific
user.
</p>
<p>
If you opt in to <strong>encrypted cloud sync</strong>, your pie is
encrypted in your browser with a PIN you choose, then sent to the
server. We add a second layer of encryption with a key only the
server holds. We never see your holdings as plaintext, and forgetting
the PIN means we can&rsquo;t recover it for you. Full details on the
<a href="/privacy">privacy page</a>.
Your portfolio holdings live in your browser&rsquo;s local storage.
The CSV is parsed in your browser, the resulting pie is kept there,
and nothing about your holdings is sent to or stored on the server.
Full details on the <a href="/privacy">privacy page</a>.
</p>
</section>