tutor answers step by step in plain text, islands finish, and the audit's money and login holes are closed
Tutor (prompt tutor-v3-stepwise-plain): plain text instead of LaTeX/markdown, with rag/services/mathtext.py converting whatever still slips through; full step-by-step solutions with a check before the final answer; three tones by grade; the last 6 messages of the conversation go to the model and follow-ups are no longer served from another user's cache; book solutions the checker disputes are kept out of the context; a questionId gives the tutor the practice question and the student's wrong answer. /api/health reports when the AI credit runs out. rag_model_benchmark compares chat models on verified book problems (run on the server; credit ran out mid-run). Map: only lessons that have questions count toward finishing an island, lessonIds[0] / nextLessonId is always the next lesson to play, and a finished island stays finished. The Seyar characters (sigma/delta/pi) are seeded. Audit fixes, each reproduced before fixing: - the plan is stored on the order, so paying a monthly order after starting a yearly checkout no longer grants a year - opening checkout no longer demotes an active subscription; renewals extend from the current expiry; expired subscriptions are not reported active - one discount code per order and only before the gateway; a 100% code completes the purchase; lost Zarinpal confirmations are re-verified - the unused /api/payment/webhook "mark paid" endpoint is removed - XP counts only answers recorded on the server - 6-digit OTP, each code dies after 5 wrong guesses; POST /api/auth/logout - a guest who signs up keeps their account and progress (test phones excluded) - affiliate commission on the first real payment; guests excluded - parents with several children no longer get a 500 - SQLite runs transactions IMMEDIATE, since it ignores select_for_update - admin login is rate-limited; display names drop phone numbers and links; userQuestion is capped at 2000 characters - server-side heart enforcement exists behind HEARTS_ENFORCED (off until the frontend handles 403 no_hearts) Not done on purpose: rotating the server's secret key and moving to Postgres. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
60
CLAUDE.md
60
CLAUDE.md
@@ -6,6 +6,22 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
Django + DRF backend for Tadrisino, an educational app (Persian/Farsi). Codebase and API responses are in Persian; comments in source are Persian too.
|
||||
|
||||
## Reviewing this codebase: comments are claims, not evidence
|
||||
|
||||
Comments here were mostly written by earlier agents to justify their own change. They
|
||||
explain intent; they do not prove the code is right. An audit on 2026-09-25 found real
|
||||
bugs directly under comments calling the same code "safe" or "race-free" (plan swap
|
||||
at checkout, `select_for_update` guards that SQLite silently ignores).
|
||||
|
||||
- **When asked to find problems, review adversarially.** Judge by behaviour, not by
|
||||
the comment above it: write a throwaway test or call the endpoint and watch what
|
||||
happens. Ask "how would a cheating student, an attacker, or a confused parent break
|
||||
this?" A finding counts once it is reproduced; say which ones were only read.
|
||||
- **When writing comments, report — don't vouch.** Say what the code does and why it
|
||||
was chosen, and name what was *not* checked. Avoid "this is safe / correct /
|
||||
race-free / the right architecture"; write "guards X; not tested under Y" instead.
|
||||
Record uncertainty plainly so the next reader re-checks rather than trusts.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
@@ -46,7 +62,7 @@ API docs: Swagger `/api/docs/`, Redoc `/api/redoc/`, admin `/admin/`.
|
||||
| `campaigns` | messaging campaign storage only, no send logic yet | `MessageCampaign` |
|
||||
| `config` | project settings, root urls, middleware | — |
|
||||
|
||||
**Auth is custom, not Django's.** `accounts.User` is deliberately *not* `AUTH_USER_MODEL` — it's an independent business model. Auth is a stateless signed bearer token (`django.core.signing`, no JWT library) issued/verified in `accounts/authentication.py` (`TadrisinoTokenAuthentication`, header `Authorization: Bearer <token>`), backed by `accounts.Session`. Missing/invalid header → anonymous request, not a hard failure; each view's permission class decides if auth is required.
|
||||
**Auth is custom, not Django's.** `accounts.User` is deliberately *not* `AUTH_USER_MODEL` — it's an independent business model. Auth is a stateless signed bearer token (`django.core.signing`, no JWT library) issued/verified in `accounts/authentication.py` (`TadrisinoTokenAuthentication`, header `Authorization: Bearer <token>`). The access token is only a signed `user_id` (1 h); `accounts.Session` holds refresh tokens, and `POST /api/auth/logout` revokes older access tokens via `User.tokens_valid_after`. Missing/invalid header → anonymous request, not a hard failure; each view's permission class decides if auth is required.
|
||||
|
||||
**Apps are decoupled by convention**: instead of real FKs across app boundaries, code uses loose refs (`user_id`, `object_id` + `label`) so any app can be split out later. Don't add cross-app FKs — follow the existing loose-reference pattern.
|
||||
|
||||
@@ -56,7 +72,7 @@ API docs: Swagger `/api/docs/`, Redoc `/api/redoc/`, admin `/admin/`.
|
||||
|
||||
**Structured logging**: `config.middleware.APIRequestLoggingMiddleware` (last in `MIDDLEWARE`, closest to the view) logs every `/api/` request as JSON (method/path/status/duration/user, no body/headers/secrets) via `config/log_formatters.py`.
|
||||
|
||||
**SQLite** is current DB (`OPTIONS.timeout=20` to tolerate concurrent-write locks, e.g. campaigns tests); README notes Postgres migration is still pending.
|
||||
**SQLite** is current DB (`OPTIONS.timeout=20`, and `transaction_mode: IMMEDIATE` so every `atomic()` takes the write lock up front — SQLite ignores `select_for_update`, so without it those guards locked nothing). Tests run on a file DB (`.test-db.sqlite3`). Postgres migration is still pending.
|
||||
|
||||
**SMS** goes through Kavenegar (`KAVENEGAR_API_KEY`/`KAVENEGAR_SENDER`); failures return explicit 503, not a fake success.
|
||||
|
||||
@@ -371,7 +387,45 @@ preferred setup, because there is then no key to leak. **Never let the server ru
|
||||
`django-insecure-…` fallback from `config/settings.py`: that string is committed here, so
|
||||
anyone who can read this repo can forge a token for any user.**
|
||||
|
||||
## وضعیت فعلی (۲۰۲۶-۰۹-۲۴) — این را اول بخوان
|
||||
## وضعیت ۲۰۲۶-۰۹-۲۵ — این را اول بخوان
|
||||
|
||||
کیفیتِ دستیار و گیرِ نقشه (جزئیات و دلیلها در `docs/architecture.md`). **۵۵۵ تست سبز.**
|
||||
این کارها روی سرور **هنوز دیپلوی نشدهاند**، مگر خلافش اینجا ثبت شده باشد.
|
||||
|
||||
- **پرامپت `tutor-v3-stepwise-plain`**: متنِ ساده (بی LaTeX/markdown، با تورِ ایمنیِ
|
||||
`rag/services/mathtext.py`)، حلِ کامل و مرحلهبهمرحله با وارسی و «✅ جواب»، لحنِ
|
||||
سهسطحی، حافظهی ۶ پیامِ آخرِ گفتگو، و حذفِ راهحلهای `disagrees` از context.
|
||||
- **جزیرهها**: فقط درسهای سوالدار شمرده میشوند، `nextLessonId` + `lessonIds[0]`
|
||||
همیشه درسِ بعدی است، و جزیرهی تمامشده تمام میماند. بدونِ این، فرانتِ فعلی
|
||||
(که همیشه `lessonIds[0]` را باز میکند) هیچ جزیرهای را تمام نمیکرد.
|
||||
- **سهیار** (`sigma`/`delta`/`pi`، باز شدن با ۰/۱۰۰/۳۰۰ امتیاز) با migration
|
||||
`progress.0004`؛ کاتالوگ روی سرور خالی بود.
|
||||
- **`/api/health` حالا تمام شدنِ اعتبارِ GapGPT را نشان میدهد** (`aiProvider`).
|
||||
- ⛔ **اعتبارِ GapGPT در ۲۰۲۶-۰۹-۲۵ تمام شد** (وسطِ `rag_model_benchmark`؛ مانده
|
||||
−$0.0016). تا شارژ، دستیار برای همه ۵۰۳ میدهد. انتخابِ مدلِ چت منتظرِ همین است:
|
||||
هزینهی واقعیِ هر پاسخ با پرامپتِ تازه — gpt-4o-mini ۰٫۰۴۴، gpt-4.1-mini ۰٫۱۱۶،
|
||||
gpt-5-mini ۰٫۱۷۱، gpt-5.4-mini ۰٫۲۹، o4-mini ۰٫۴۱ سنت. دقتشان هنوز اندازه نشده.
|
||||
**تصمیمِ صاحبِ پروژه (۰۹-۲۵): فعلاً gpt-4o-mini میماند** (ارزانترین). اولین
|
||||
کاندیدا برای سنجش بعد از شارژ: `deepseek-v4-flash` (قیمتِ رسمی از 4o-mini کمتر،
|
||||
ریاضیِ خیلی قویتر، ولی فارسی و حالتِ بدونِ thinkingاش اندازه نشده) و
|
||||
`gpt-4.1-mini`. تیمِ Rag_backend برای جوابِ ریاضی `o3-mini` استفاده میکند.
|
||||
پوشهی موقتِ سنجش روی سرور: `~/mathapp-v2/.bench/` (پس از اتمام پاک شود).
|
||||
- فرانت: PRِ ما (#3) بسته شد؛ هرچه فرانت باید بکند فقط در
|
||||
`~/Desktop/کارهای-فرانت-تدریسینو.html` است (نه PR). شاخهی محلیِ
|
||||
`zareian/ai-chat-readability` فقط مرجع است.
|
||||
|
||||
**بازبینیِ بدبینانهی همان روز و رفعها** (۵۷۹ تست سبز؛ جزئیات در `docs/architecture.md`):
|
||||
پلن روی سفارش (باگِ «ماهانه بده، سالانه بگیر»)، صفحهی خرید اشتراکِ فعال را
|
||||
دست نمیزند، تمدید از انقضای فعلی، یک کدِ تخفیف پیش از درگاه و کدِ ۱۰۰٪ = پرداخت،
|
||||
بازسنجیِ خودکارِ پرداختِ تأییدنشده، حذفِ `/api/payment/webhook`، پورسانتِ affiliate
|
||||
روی پرداختِ واقعی (مهمان بیرون)، امتیاز فقط از جوابِ ثبتشده، `HEARTS_ENFORCED`
|
||||
(خاموش)، OTPِ ۶ رقمی با ۵ حدس، خروج از حساب (`tokens_valid_after`)، مهمانِ
|
||||
ثبتنامکننده همان کاربر میماند، والدِ چندفرزندی، سقفِ ورودِ ادمین، پاکسازیِ نامِ
|
||||
نمایشی، `questionId` در دستیار، SQLite با `transaction_mode: IMMEDIATE`.
|
||||
**عمداً انجام نشد** (تصمیمِ صاحبِ پروژه: برای ارائه نقطهی شکست میسازند):
|
||||
عوض کردنِ `DJANGO_SECRET_KEY`ِ سرور (همه را بیرون میاندازد) و مهاجرت به Postgres.
|
||||
|
||||
## وضعیت ۲۰۲۶-۰۹-۲۴
|
||||
|
||||
سرور روی `81348ce` به بعد است (سرویس واقعاً ریاستارت شد)، **۵۳۰ تست سبز**، ۶۰ اندپوینت. **پروژه در این تاریخ بسته شد**؛ فایلهای تحویل (گزارشِ ۲۲، راهنمای ارائه، zipِ بکاند) در `~/Desktop/گزارشات Tadrisino/گزارش-۲۲/` بهروزند. آن روز کلِ
|
||||
فرانت (`MathApp_frontend`، upstream `main` = `d223336`) صفحهبهصفحه با API زنده
|
||||
|
||||
Reference in New Issue
Block a user