- technical-handbook: question packs instead of subscriptions, real
hearts with the new wait screen (screenshot), math-only tutor and what
is charged (new 9-7), admin cancel/refund, Bazaar planId, account
deletion, 59 endpoints, 779 tests, 14 apps, status and open decisions.
- Diagrams redrawn: use-case (buy a pack, refunds), data model
(QuestionCredit, HeartsPass, Profile.birth_date), pack payment sequence.
- The PDF is now built from the Markdown by docs/build_handbook.py
(pandoc + Chrome, font in docs/assets); the hand-made HTML it used to
come from was not in the repo and had been lost.
- architecture, operations, security-before-production (item 13 closed),
ai-cost-and-guardrails and README follow the same changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The claim that the user-row lock plus SQLite IMMEDIATE stops one credit
being spent twice was untested. Two threads on separate connections now
ask at the same time: one answer, one credit spent; the same
Idempotency-Key arriving twice at once also costs one credit. Removing
transaction_mode IMMEDIATE turns both red (500, database is locked).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bazaar orders are only created through POST /api/payment/orders, which
took no planId, so a pack order had plan_id=None: Bazaar verified the
purchase, the order became paid, and grant_pack never ran. The endpoint
now takes planId (rejected when unknown) and snapshots the plan title.
The verify response also returned the pre-payment status ('pending')
because apply_order_payment_result updates its own copy of the order.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Five a day was too close to the free three. Pricing keeps the tutor under ~40% of net revenue
at typical use (a third of the cap); full numbers in the cost report and docs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- remove the family app from the root (separate parent accounts, invite codes, parental
settings); progress.0006 drops its tables, permissions and migration history, and
Profile.parent_phone, the two family notification types and ?userId= on
/api/progress/nodes go with it
- GET /api/user/weekly-report: last 7 days next to the 7 before (estimated minutes,
questions, accuracy, islands, tutor questions) for the "parent report" page
- profile birthDate in Jalali (accounts/jalali.py), ages 5-99, birthYear derived from it
- GET /api/payment/invoices; orders now snapshot list price and plan title
- tutor answers only math: server pre-filter, scope rule in the prompt (tutor-v11),
refusal marker replaced with fixed text; self-harm gets a help line instead
- streamed answers now record token usage (it was always 0)
- daily tutor quota: AI_DAILY_QUOTA_FREE (default 3) vs Plan.ai_daily_quota
- delete_users management command for wiping test accounts across every app
- docs/ai-cost-and-guardrails.md: measured cost per question and guardrail results
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>