The cancellation policy page showed only the tenant policy, so nothing said which services opt out of it. Service policies do not blend with the tenant one — a service that has its own follows it completely — and without the table an operator cannot tell why one service's penalty differs. It lists them with a link to each service. The waitlist had the matches endpoint and no way to reach it. The list answers "who is waiting"; the question asked when capacity frees up is "who is waiting for this slot", so the page now takes a service and a date and answers that. The note says plainly that cancelling notifies them anyway — this is for looking before deciding, not a second notification path. Spacing is enforced at hold time rather than during candidate generation, which costs one slot being shown and then refused, and saves a patient-history query per candidate. That trade had no test; now a booking five days after the last one is refused and one thirty days later goes through. Checklists across all sixteen tasks are final: no pending rows, and the warnings that remain are recorded decisions — one resolver instead of six engines, a closed list instead of a registry, sample size three instead of ten — each with the reason it was taken. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
8.5 KiB
8.5 KiB
چکلیست — تسک ۱۰ (فرم ساخت قانون و محیط آزمایش)
وضعیت کلی: ✅ تمامشده با انحرافهای ثبتشده · آخرین بازبینی: ۱۴۰۵/۰۵/۰۹
قواعد: _shared/definition-of-done.md · red-lines.md · ui-conventions.md
۰. خط سرخ
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۰.۱ | --group=slot-mode-frozen سبز |
✅ | |
| ۰.۲ | شبیهسازی هیچ ردیفی نمینویسد (جز policy_simulation_runs) |
✅ | ⭐⭐ testSimulationWritesNothingButItsOwnRun پنج جدول را قبل/بعد میشمارد |
| ۰.۳ | نوبتهای واقعی بیماران تغییر نکردند | ✅ | ارزیابی روی حقایق است، نه entity |
۱. بکاند — شبیهساز
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۱.۱ | PolicySimulator · SimulationSampler · PolicySimulationRun |
✅ | بهعلاوهٔ SimulationFacts |
| ۱.۲ | سه لایهٔ تضمین | ✅ | ⭐ حقایق (نه entity) · تراکنش با rollback در finally · تست شمارش |
| ۱.۳ | $this->em->clear() بعد از rollback |
✅ | ⭐ قانون بعد از clear دوباره خوانده میشود |
| ۱.۴ | ثبت نتیجه بعد از rollback و در تراکنش جدا | ✅ | |
| ۱.۵ | ارزیابی جدا — فقط همان قانون | ⚠️ | PolicyResolver::evaluateOne() بهجای evaluateIsolated() روی شش موتور؛ شش موتوری وجود ندارد که متد بگیرد (انحراف تسک ۰۹) |
| ۱.۶ | فیلتر شعبه/سرویس/دسته از دامنهٔ قانون | ⚠️ | از دامنهٔ قانون استخراج میشود، نه از داخل condition؛ شرطها فیلدِ id ندارند که به کوئری تبدیل شوند |
| ۱.۷ | سقف نمونه ۵۰؛ درخواست بیشتر → ۴۲۲ | ✅ | testSampleSizeAboveTheCapIsRejected |
| ۱.۸ | PolicyTemplateRegistry با پنج الگو |
✅ | شش الگو |
| ۱.۹ | activate شرط simulate همان نسخه |
✅ | ⭐ testSimulationOfTheOldVersionDoesNotUnlockTheNewOne |
| ۱.۱۰ | محیط بدون نوبت → آزمایش خالی موفق، فعالسازی مجاز | ✅ | ⭐ warning: دادهای برای آزمایش نیست |
| ۱.۱۱ | چهار سطح شدت با آستانههای مستند | ✅ | PolicySimulationRun::severityFor() — ۰٪ · ≤۲۰٪ · ≤۶۰٪ · >۶۰٪ |
| ۱.۱۲ | دو endpoint | ✅ | سه تا: policy-templates · simulate · simulations |
| ۱.۱۳ | app:policy:prune-simulations — آخرین اجرا per (قانون، نسخه) حذف نمیشود |
✅ | --days و --dry-run؛ SQL با MAX(id) GROUP BY policy_id, policy_version |
۲. دیتابیس
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۲.۱ | policy_simulation_runs با جفت tenant |
✅ | Version20260731065427 + دو ایندکس |
| ۲.۲ | report سقف ۵۰ ردیف |
✅ | از سقف نمونه میآید: بیش از ۵۰ نوبت اصلاً خوانده نمیشود |
| ۲.۳ | TenantSchemaCoverageTest سبز |
✅ |
۳. UI
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۳.۱ | PoliciesPage · PolicyFormPage · PolicySimulationPage |
✅ | + ورودی «قوانین» در منوی تنظیمات |
| ۳.۲ | PolicyConditionBuilder از GET /policy-schema |
✅ | ⭐ تست با فیلد ساختگی: بدون تغییر فرانت در UI ظاهر میشود |
| ۳.۳ | عملگرها per فیلد فیلتر میشوند | ✅ | ⭐ field_meta[].operators — تست جداگانه |
| ۳.۴ | نوع ورودی مقدار از schema | ✅ | int · enum · bool · list |
| ۳.۵ | همهٔ selectها SearchableSelect |
✅ | هیچ <select> بومی |
| ۳.۶ | انتخاب الگو → فرم کوتاه مقدارها | ✅ | حالت پیشفرض صفحه همین است |
| ۳.۷ | ستون «وضعیت فعلی ← با این قانون» | ✅ | ⭐ |
| ۳.۸ | درصد + شدت با رنگ توکنمحور | ✅ | کلاسهای badge green/amber/red موجود |
| ۳.۹ | شدت none هشدار میدهد |
✅ | «احتمالاً شرطش هرگز برقرار نمیشود» |
| ۳.۱۰ | شدت high → تأیید دوم |
✅ | |
| ۳.۱۱ | ConfirmDialog موجود |
✅ | نه window.confirm |
| ۳.۱۲ | DataTable + فیلتر دسته در URL |
✅ | useUrlState |
| ۳.۱۳ | backTo روی هر سه صفحه |
✅ | |
| ۳.۱۴ | هیچ رنگ/شعاع hard-code | ✅ | فقط var(--…) |
| ۳.۱۵ | دارکمود و حالت فشرده | ✅ | اسکرینشات واقعی در دارکمود و حالت فشرده؛ ایرادی نماند |
| ۳.۱۶ | RTL و موبایل — اسکرول افقی جدول | ✅ | overflow-x: auto دور جدول گزارش |
| ۳.۱۷ | تاریخها شمسی | ✅ | formatDate |
| ۳.۱۸ | همهٔ رشتهها فارسی | ✅ | |
| ۳.۱۹ | تاریخچهٔ نسخهها با diff | ✅ | PolicyVersionDiff — هر نسخه در برابر نسخهٔ پیش، فیلدبهفیلد؛ «بدون تغییرِ معنادار» هم صریح گفته میشود |
۴. تست
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۴.۱ | شمارش ردیف قبل/بعد | ✅ | ⭐⭐ |
| ۴.۲ | استثنا → rollback + clear | ✅ | ⭐ خودِ خاصیت سنجیده میشود: entityِ در انتظار flush بعد از آزمایش ثبت نمیشود و تراکنش بسته است |
| ۴.۳ | نمونهگیری — فقط confirmed/completed، سقف | ✅ | سقف تست شد؛ فیلتر وضعیت غیرمستقیم (نوبتهای نمونه completed اند) |
| ۴.۴ | دروازهٔ فعالسازی | ✅ | ⭐ بدون آزمایش، نسخهٔ قدیمی، نسخهٔ درست، محیط خالی |
| ۴.۵ | هر الگو قانون معتبر میسازد | ✅ | هر شش الگو تا فعالسازی رفتند. یادداشت قبلی: dataProvider ششتایی نوشته نشد |
| ۴.۶ | چهار آستانهٔ شدت | ✅ | هر چهار آستانه روی مرزِ خودش، بهعلاوهٔ حالت کوتاهتر |
| ۴.۷ | فیلد ساختگی از mock schema در UI | ✅ | ⭐ PolicyFormPage.test.tsx |
| ۴.۸ | عملگر نامعتبر نمایش داده نمیشود | ✅ | |
| ۴.۹ | صفحهٔ آزمایش — قفل فعالسازی روی نسخهٔ قدیمی | ✅ | PolicySimulationPage.test.tsx |
اجرا: ddev exec php bin/phpunit tests/Policy → ۳۰ تست · npx vitest run → ۶۲۸ تست.
۵. مستندات
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۵.۱ | docs/api/policy.md — simulate، policy-templates، شرط تازهٔ activate |
✅ | JSON واقعی |
| ۵.۲ | policy-engine.md بخش «چرا آزمایش اجباری است» |
✅ | + سه لایهٔ تضمین و دو حالت مرزی |
۶. بازبینی پایانی
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۶.۱ | هیچ 🔄 و ⏳ بیدلیل نمانده | ✅ | ۷ مورد ⚠️ همه با دلیل |
| ۶.۲ | bin/phpunit کامل سبز |
✅ | ۱۲۵۰ تست |
| ۶.۳ | --group=slot-mode-frozen سبز |
✅ | |
| ۶.۴ | phpstan بدون خطای جدید |
✅ | ۱۴ = baseline |
| ۶.۵ | npx tsc --noEmit و تستهای فرانت سبز |
✅ | ۶۲۸ تست؛ yarn test داخل ddev اجرا نمیشود (باینری esbuild برای darwin نصب شده) — روی هاست اجرا شد |
| ۶.۶ | تستهای tenant سبز | ✅ | |
| ۶.۷ | docs/api/* بهروز |
✅ | |
| ۶.۸ | چکلیست UI کامل | ✅ | جز ۳.۱۵ و ۳.۱۹ |
| ۶.۹ | دو کلاینت دیگر بررسی شدند | ✅ | این تسک هیچ قرارداد عمومیای عوض نکرد؛ همهٔ اندپوینتها پنلمحورند |
| ۶.۱۰ | commit، سپس graphify update . |
✅ | دو کامیت جدا |
| ۶.۱۱ | موارد بهتعویق با دلیل | ✅ | ۳.۱۵ (بازبینی چشمی) · ۳.۱۹ (diff بصری) · ۴.۲/۴.۵/۴.۶ (پوشش تست) — همه در همین فایل ثبتاند |