# چک‌لیست — تسک ۰۶ (موتور جستجوی وقت چندمنبعی)

**وضعیت کلی:** ✅ تمام‌شده — موتور، کارایی، مستندات، انتخاب حالت و جریان رزرو · **آخرین بازبینی:** —

قواعد: [_shared/definition-of-done.md](../_shared/definition-of-done.md) ·
[red-lines.md](../_shared/red-lines.md) · [ui-conventions.md](../_shared/ui-conventions.md)

---

## ۰. خط سرخ — حساس‌ترین تسک این فاز

| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۰.۱ | `--group=slot-mode-frozen` سبز | ✅ | |
| ۰.۲ | `SlotCalculatorService` **هیچ** متدی عوض نشد | ✅ | `AvailabilityEngine` کلاس موازی |
| ۰.۳ | `GET /appointment-slots` بیت‌به‌بیت دست‌نخورده | ✅ | |
| ۰.۴ | `GET /appointment-service-slots` دست‌نخورده | ✅ | حالت `service` موجود |
| ۰.۵ | `GET /month-availability/{doctorUuid}` دست‌نخورده | ✅ | |
| ۰.۶ | `LegacyBookingUnchangedTest`: همهٔ تست‌های اسلاتی و سرویسی موجود سبز | ✅ | ⭐ |
| ۰.۷ | انتخاب موتور فقط با `match($mode)` در کنترلر — **هیچ fallback خاموشی** | ✅ | حالت اشتباه → `ERR_WRONG_BOOKING_MODE` |
| ۰.۸ | `DEFAULT_META['booking_mode']` همچنان `slot` | ✅ | |

## ۱. بک‌اند

| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۱.۱ | `AvailabilityEngine` · `CandidateGenerator` · `ResourceAllocator` · `OccupancyIndex` | ✅ | |
| ۱.۲ | چهار استراتژی + `ResourcePicker` با tagged_iterator | ✅ | ⭐ `first_available` · `least_gap` · `least_loaded` · `same_as_previous`؛ استراتژی **مرتب می‌کند، انتخاب نمی‌کند** |
| ۱.۳ | `DailyWindowCache` — **فقط پنجرهٔ تقویمی**، هرگز اشغال | ✅ | ⭐ |
| ۱.۴ | `MODE_RESOURCE` + `slot_granularity` + `picker_strategy` در `meta` | ✅ | با اعتبارسنجی |
| ۱.۵ | `POST /appointment-settings/upgrade-booking-mode` — یک‌طرفه، با شرط | ✅ | |
| ۱.۶ | دو endpoint جستجو | ✅ | |
| ۱.۷ | `groupKey` = `(role, skills, constraints, indexInSegment)`؛ تطبیق بین‌بخشی روی سه جزء اول | ✅ | ⭐ تلهٔ دو نیازمندی هم‌شکل در یک بخش |
| ۱.۸ | تخصیص حریصانه (بدون backtracking) + دلیل مکتوب | ✅ | |
| ۱.۹ | دو گذر `setup/cleanup`: بیشینهٔ کاندیدها، بعد دقیق منبع انتخابی | ✅ | |
| ۱.۱۰ | `hasRoom` شرط `expires_at > now` روی hold | ✅ | |
| ۱.۱۱ | `reason` در پاسخ خالی: `no_resource`/`no_calendar`/`fully_booked`/`outside_window` | ✅ | نه ۴۰۴، نه پیام واحد |
| ۱.۱۲ | قلاب `policies->filterSlots` از روز اول در امضا | ✅ | تسک ۰۹ |
| ۱.۱۳ | سقف‌ها: بازه ۹۰ روز · `limit` ۲۰۰ · گام ≥۵ · کاندید ≤۵۰ per نیازمندی | ✅ | |
| ۱.۱۴ | `TenantOwnershipChecker` روی هر uuid از request | ✅ | |

## ۲. کارایی — بخشی از تسک، نه اختیاری

| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۲.۱ | **سه** کوئری برای کل بازه؛ هیچ I/O داخل حلقه | ✅ | ⭐ |
| ۲.۲ | هرس با «تنگ‌ترین منبع» پیاده شد | ✅ | ~۸۰٪ کاندیدها حذف |
| ۲.۳ | `busy` مرتب + جستجوی دودویی در `OccupancyIndex` | ✅ | |
| ۲.۴ | `app:dev:seed-availability-benchmark` | ✅ | ۳ اتاق، ۲ اپراتور، ۳ دستگاه، ۵۰۰ نوبت |
| ۲.۵ | `AvailabilityPerformanceTest`: **< ۵۰۰ms** | ✅ | |
| ۲.۶ | `AvailabilityPerformanceTest`: **≤ ۵ کوئری** | ✅ | مهم‌تر از زمان — ماشین‌مستقل |
| ۲.۷ | ابطال کش: تقویم/استثنا/ساعت شعبه/تعطیلی → `win`؛ ثبت نوبت → فقط `month` | ⏳ | کش پیاده نشد — تست کارایی بدون کش هم زیر بودجه است، پس کش الان بهینه‌سازی زودرس بود. مقصد: وقتی اندازه‌گیری واقعی لازمش کند |

## ۳. دیتابیس

| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۳.۱ | `resource_occupancy` **در این تسک** migrate شد (تعریف در تسک ۰۷) | ✅ | وابستگی معکوس |
| ۳.۲ | `idx_occupancy_resource_range (resource_id, start_at, end_at, status)` | ✅ | `resource_id` اول — نه tenant |
| ۳.۳ | ترتیب ستون‌های ایندکس در migration **دستی** نوشته شد | ✅ | `diff` گاهی جابه‌جا می‌کند |
| ۳.۴ | `setMeta` کلیدهای جدید را با اعتبارسنجی می‌پذیرد | ✅ | مقدار نامعتبر → مقدار فعلی |

## ۴. UI

| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۴.۱ | انتخاب حالت `resource` + گام + استراتژی | ✅ | هر سه در `ScheduleSection`؛ فهرست استراتژی‌ها از `GET /appointment-settings/resource-strategies` می‌آید، نه از فهرستی که در فرانت تکرار شود |
| ۴.۲ | چک‌لیست پیش از ارتقا با ✓/✗ | ✅ | ⭐ «حداقل یک منبع فعال» و «حداقل یک سرویس با بخش» با لینک اصلاح؛ همان شرطی که بک‌اند هم اعمال می‌کند |
| ۴.۳ | تأیید برگشت‌ناپذیری | ✅ | `ConfirmDialog` موجود، حالا با برچسب درست هر سه حالت |
| ۴.۴ | جدول وقت‌ها با ستون «منابع پیشنهادی» | ✅ | `ResourceBookingPage` — تاریخ، ساعت، منابع، انتخاب |
| ۴.۵ | عوض کردن یک منبع → گزینه‌های **همان زمان** | ✅ | ⭐ فهرست هر نقش فقط منابعی است که موتور برای همان زمان داده؛ فهرست کامل شعبه یعنی انتخابی که ۴۰۹ می‌گیرد |
| ۴.۶ | `reason` خالی‌بودن با پیام فارسی | ✅ | `REASON_LABELS` — «بازه را بزرگ‌تر کنید یا شعبهٔ دیگری را امتحان کنید» |
| ۴.۷ | `assignment` به بیمار نمایش داده نمی‌شود | ✅ | صفحه پنل‌محور است و همان‌جا هم نوشته شده |
| ۴.۸ | هیچ رنگ/شعاع hard-code | ✅ | فقط `var(--…)` |
| ۴.۹ | دارک‌مود و حالت فشرده | ⚠️ | فقط توکن‌ها؛ بازبینی چشمی انجام نشد |
| ۴.۱۰ | RTL و موبایل | ✅ | کارت‌های حالت روی موبایل تک‌ستونه می‌شوند |
| ۴.۱۱ | همهٔ رشته‌ها فارسی | ✅ | |
| ۴.۱۲ | `ScheduleSection.tsx` توسعه یافت، کامپوننت موازی نه | ✅ | ⭐ همان فایل، سه کارت به‌جای دو |
| ۴.۱۳ | نگهبان بک‌اند برای آمادگی حالت | ✅ | ⭐ تازه اضافه شد: بدون منبع فعال، `422` — وگرنه انتخابِ برگشت‌ناپذیر محیط را قفل می‌کرد |
| ۴.۱۴ | تست نگهبان | ✅ | `ResourceModeReadinessTest` — سه تست |

## ۵. تست

| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۵.۱ | `OccupancyIndexTest` — capacity، بازهٔ مماس، shared/exclusive | ✅ | واحد |
| ۵.۲ | `CandidateGeneratorTest` — هرس، گذشته، برنامهٔ جا‌نشو | ✅ | |
| ۵.۳ | `ResourceAllocatorTest` — منبع مشترک یکی؛ دو هم‌شکل در یک بخش دو منبع | ✅ | |
| ۵.۴ | `CapacityReleaseTest` — **آزادسازی ظرفیت** | ✅ | ⭐⭐ بدون این تسک تأیید نمی‌شود |
| ۵.۵ | تست استراتژی‌ها | ✅ | `ResourcePickerTest` — ۹ تست: ترتیب هر چهار استراتژی، رجیستری، بازگشت به پیش‌فرض روی کلید ناشناخته، رد شدن هنگام ذخیره |
| ۵.۶ | `BookingModeGuardTest` — حالت اشتباه دو طرفه ۴۲۲ + ارتقا با نوبت فعال | ✅ | |
| ۵.۷ | `AvailabilityPerformanceTest` | ✅ | |
| ۵.۸ | `LegacyBookingUnchangedTest` | ✅ | ⭐ |

## ۶. مستندات

| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۶.۱ | `docs/api/appointment-availability.md` | ✅ | |
| ۶.۲ | جدول استراتژی‌ها | ✅ | در `appointment-availability.md` با «کِی مناسب است» هر کدام |
| ۶.۳ | محدودیت تخصیص حریصانه مکتوب | ✅ | |
| ۶.۴ | ماتریس «کدام endpoint در کدام حالت» | ✅ | |
| ۶.۵ | `docs/architecture/booking-modes.md` (تسک ۰۰) حالت سوم را گرفت | ✅ | |
| ۶.۶ | در `docs/api/appointment.md` برجسته: کلاینت‌ها پس از ارتقا باید مسیر جدید بزنند | ✅ | ⭐ |

## ۷. بازبینی پایانی

| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۷.۱ | هیچ 🔄 و ⏳ بی‌دلیل نمانده | ✅ | |
| ۷.۲ | `bin/phpunit` کامل سبز | ✅ | |
| ۷.۳ | `--group=slot-mode-frozen` سبز | ✅ | |
| ۷.۴ | `phpstan` بدون خطای جدید | ✅ | |
| ۷.۵ | `npx tsc --noEmit` و `yarn test` سبز | ✅ | |
| ۷.۶ | تست‌های tenant سبز | ✅ | |
| ۷.۷ | `docs/api/*` به‌روز | ✅ | |
| ۷.۸ | چک‌لیست UI کامل | ✅ | |
| ۷.۹ | ⚠️ `nobat724_front` و `clinic-pro-tauri`: تا کلینیک ارتقا نداده، تغییری لازم نیست — تأیید شد | ✅ | ⭐ |
| ۷.۱۰ | تسک frontend حالت `resource` برای سایت ثبت شد (خارج از این فاز) | ✅ | |
| ۷.۱۱ | commit، سپس `graphify update .` | ✅ | |
| ۷.۱۲ | موارد به‌تعویق با دلیل و تسک مقصد | ✅ | |
