- Drop first_seen_user_id; sample is anonymous by construction
- Rename sample_dummy → sample_row, store the upload's first real data
row verbatim (one row, no totals, no other positions, no link to a
user). Narrow, deliberate exception to the "no holdings persisted"
invariant — gives the operator material for hand-writing future
native parsers.
- Drop the cache self-heal behaviour; operator owns eviction. Reinforce
the non-goal of auto-promoting learned formats to code.
Transparent fallback after parse_t212_csv: LLM extracts a column-mapping
(not the data), result is cached globally by header fingerprint, replay
is deterministic Python. Stored dummy contains headers + synthetic row
only — no user holdings ever persisted.
Settings page tidy-up driven by user feedback that it had grown too busy:
- Each section (Import, Invite, Email digests, Cloud sync) is now a
native <details>/<summary> accordion. Import stays open by default
because /settings#import is the deep-link target from the dashboard
CTA; the others collapse so the page lands quiet.
- Manage subscription is a right-aligned gear-icon button instead of
a rectangular text button — the descriptive copy moves into the
tooltip. Frees up the Tier row of visual weight.
Auth + modal inputs were too small (verify code box, portfolio restore
PIN): the auth-card selector now covers text inputs as well, and a new
.modal-input class standardises 16px / 12px-padding fields used in the
cloud-sync enable modal and the portfolio restore prompt.
The verify page no longer carries the "Email me the digest" checkbox —
it was misleading on repeat logins (server-side it only applied on
first sign-up but rendered every time). Default-opt-in lives in the
User row at creation; per-user changes happen on /settings. First
successful verify now triggers a one-shot welcome email explaining the
digest cadence and pointing at /settings for opt-out; SMTP failure is
logged but does not block the login.
Tests rewritten to cover the new welcome-email path:
- first login sends exactly one welcome email
- returning user gets none
- SMTP failure does not break the redirect
- regression guard: returning user who opted out stays opted out
Also lands the paddle merchant-summary doc that was written earlier
during the Paddle → Polar → Stripe onboarding pivot.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Twelve-task plan covering the BETA chip, free-tier 6h news cap, daily
+ Sunday digest job, one-click unsubscribe, settings UI, sign-up
checkbox, pricing copy, and an admin send-test-digest CLI. Each task
is TDD where feasible.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Design doc for three coordinated closed-beta changes: a BETA chip in
the app header, a 6h news-window cap on the free tier, and email
digests (daily for paid Mon-Sat, Sunday weekly for everyone). Draft;
awaits implementation plan.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>