- content: POST nodes/{id}/quiz/complete and lessons/{id}/complete refuse a locked island
(403 node_locked, same rule as /api/home) and a user who has not answered every question
(409 answers_missing). Before, only a user with zero hearts and no answers was refused, so
anyone else finished any island, locked or not, and finishing a locked island's lessons
unlocked it. A node or lesson with no questions still passes when unlocked, with no XP.
The client can no longer understate totalQuestions to buy stars.
- throttle_scope on 15 routes that had none (question bank without login, intro, hearts,
notifications, tickets, plans, subscription status, orders, offers, affiliate, assistant
history and feedback, logout).
- admin: OTPCode and Session can only be added/changed by a superuser.
- deploy.sh: integrity-checked DB backup before migrate (keeps the last 2); no migrate without it.
- docs and CLAUDE.md updated to say what is verified and what is not.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
accounts.0008 drops the columns and refuses to run while guest rows exist.
Docs describe the current state; tests that only proved guest behaviour are gone.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
POST /api/auth/session is gone (404). Existing is_guest rows keep refresh and
upgrade-by-phone until the demo/guest rows are purged; the code that serves
them is listed in docs/security-before-production.md §10. Tests that used the
endpoint as a fixture now use accounts/testing.guest_session.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- accounts admin: phone/is_guest read-only for non-superusers, device_id filter lookups refused,
auth.User/Group admin writable only by superusers
- campaigns: delivery log needs view permission
- phone/OTP/birth date: any Unicode digit normalised or rejected with 400; unknown gradeId is 400
- shared demo guest is never turned into a person's account by logging in
- /api/home with unknown gradeId is no longer cached (LocMemCache flood reset throttle counters)
- /api/progress/nodes: one query, huge unit is 400
- BazaarCredential token filter lookups refused
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Admin: refund/cancel/mark-resolved/Bazaar-link actions need change permission; question and
curriculum import need add/delete; content browser needs view_question; guest device ids are
shown to superusers only.
- Accounts: registering a guest's phone revokes the guest's earlier sessions; self-deletion keeps
Redemption, UserOffer and the phone's OTP rows; the public demo guest cannot be deleted
(DEMO_GUEST_DEVICE_IDS); display-name contact filter covers any separator and social handles.
- Payment: callback without Authority is 400; a second Bazaar purchase on a paid order is 409;
orders snapshot credits/hearts/days at creation (payment.0011).
- Assistant: a broken diagram no longer makes a streamed answer free; a late off-topic marker
refuses the answer (not stored, not cached); a disconnect saves the partial answer; only
KNOWN_COLLECTIONS are searched.
- Infra: secret-key file is created atomically (workers no longer split keys); no default
INTERNAL_SERVICE_KEY; Kavenegar key and Bazaar token kept out of error logs; Swagger/ReDoc
pinned; schema throttled; docker compose bound to 127.0.0.1; non-ASCII digit ids are 400;
ticket text capped at 5000.
- docs/security-before-production.md: remaining open items 24-31 and the guard tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- profile publicId ("User482917", accounts/public_id.py); admin shows and searches it
- plans API reports aiDailyQuota per plan and freeAiDailyQuota; migration 0007 sets the
temporary prices (monthly 99,000 / yearly 990,000 toman, 5 tutor questions a day) unless
an admin already edited the yearly plan
- tutor tone follows the learner's age (18+ gets an adult tone at any grade); notation
still follows the grade's book; answer cache keyed by tone (prompt tutor-v12-age-tone)
- POST /api/user/delete: phone users confirm with a fresh SMS code (same checks as login,
now shared in verify_phone_otp), guests type "حذف"; payment records are kept, demo
accounts refused
- delete_users --skip-paid
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>