# چک‌لیست — تسک ۰۹ (موتور قوانین شش‌دسته‌ای) **وضعیت کلی:** ⏳ شروع نشده · **آخرین بازبینی:** — قواعد: [_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` سبز | ⏳ | | | ۰.۲ | **هیچ قانونی روی حالت `slot` اعمال نمی‌شود** | ⏳ | ⭐ حتی اگر منطقی به نظر برسد | | ۰.۳ | `DiscountRule` مهاجرت نکرد و دست‌نخورده ماند | ⏳ | | | ۰.۴ | `NoPolicyRegressionTest`: بدون هیچ قانون، خروجی‌ها بیت‌به‌بیت مثل تسک ۰۸ | ⏳ | ⭐ | | ۰.۵ | کد دلخواه در قانون **ممنوع** — فقط فهرست بسته | ⏳ | مستند بند ۸ | | ۰.۶ | تودرتویی شرط ممنوع — فقط `all`/`any` یک‌سطحی | ⏳ | | ## ۱. بک‌اند | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۱.۱ | `Policy` · `PolicyVersionLog` | ⏳ | | | ۱.۲ | `active = false` پیش‌فرض | ⏳ | تسک ۱۰ آزمایش را اجبار می‌کند | | ۱.۳ | `FieldRegistry` — سه مسئولیت روی یک آرایه (schema/extract/assert) | ⏳ | ⭐ فیلد نمایشیِ بی‌ارزیابی ممکن نشود | | ۱.۴ | `OperatorRegistry` با یازده عملگر شامل `days_since` | ⏳ | | | ۱.۵ | `EffectRegistry` — اثر خارج از دسته → ۴۲۲ | ⏳ | | | ۱.۶ | `Combiner` — جدول ترکیب مستند بند ۸، خالص و بدون I/O | ⏳ | | | ۱.۷ | `PolicyResolver` — اولویت → اختصاصی‌بودن → قدمت | ⏳ | | | ۱.۸ | `specificity` هنگام **ذخیره** محاسبه می‌شود، نه اجرا | ⏳ | | | ۱.۹ | شش موتور جدا، هر کدام یک کلاس | ⏳ | نه یک `PolicyEngine` بزرگ | | ۱.۱۰ | `evaluateIsolated()` روی هر شش موتور | ⏳ | ⭐ تسک ۱۰ به آن نیاز دارد — اینجا اضافه شود | | ۱.۱۱ | `SpacingPolicyEngine::forbiddenRanges()` — کوئری، **نه حلقه per slot** | ⏳ | ⭐ | | ۱.۱۲ | بازهٔ ممنوعه **پیش از** تولید کاندید به `CandidateGenerator` می‌رود | ⏳ | نه فیلتر بعدی | | ۱.۱۳ | `combinable=false` → short-circuit؛ `deny` همیشه short-circuit | ⏳ | | | ۱.۱۴ | فیلد بی‌مقدار → `false` **با لاگ**، نه سکوت | ⏳ | ⭐ قانون خاموش بی‌صدا | | ۱.۱۵ | `PATCH` محتوای قانون وجود ندارد؛ فقط `name` و `active` | ⏳ | نسخه‌بندی | | ۱.۱۶ | `policy_version_log` snapshot **کامل** نگه می‌دارد، نه diff | ⏳ | | | ۱.۱۷ | `valid_from` گذشته در نسخهٔ جدید → ۴۲۲ | ⏳ | قانون پنجم | | ۱.۱۸ | شش endpoint شامل `GET /policy-schema` | ⏳ | | | ۱.۱۹ | `PricingPolicyEngine` هر دو منبع (`DiscountRule` + `Policy`) را ترکیب می‌کند | ⏳ | | | ۱.۲۰ | `TenantOwnershipChecker` روی هر uuid از request | ⏳ | | ## ۲. پر کردن قلاب‌های تسک‌های قبل | # | قلاب | وضعیت | یادداشت | |---|---|---|---| | ۲.۱ | تسک ۰۴ — `ServiceSelectionValidator` → `SelectionPolicyEngine` | ⏳ | | | ۲.۲ | تسک ۰۵ — `AppointmentPlanBuilder` مرحلهٔ ۷ → `Resource` + `Timing` | ⏳ | | | ۲.۳ | تسک ۰۶ — `AvailabilityEngine` مرحلهٔ ۶ → `Spacing` | ⏳ | | | ۲.۴ | تسک ۰۷ — `BookingService::confirm` مرحلهٔ ۳ → `Eligibility` | ⏳ | | | ۲.۵ | تسک ۰۸ — `PricingEngine` مرحلهٔ ۳ → `Pricing` | ⏳ | | | ۲.۶ | **هیچ امضایی عوض نشد** | ⏳ | ⭐ دلیل گذاشتن قلاب‌ها از روز اول | ## ۳. دیتابیس | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۳.۱ | `policies` + `policy_version_log` | ⏳ | | | ۳.۲ | `idx_policies_lookup (entity_type, entity_id, category, active, valid_from)` | ⏳ | | | ۳.۳ | `appointments.applied_policies` (JSON تهی‌پذیر) | ⏳ | | | ۳.۴ | قرارداد `applied_policy_ids` با `{id, version, name}` | ⏳ | `name` کپی متنی | | ۳.۵ | `policy_version_log` در `AGGREGATE_CHILDREN` | ⏳ | | | ۳.۶ | `app:policy:seed-examples` — پنج نمونه، همه `active=false` | ⏳ | | | ۳.۷ | `TenantSchemaCoverageTest` سبز | ⏳ | | ## ۴. کارایی | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۴.۱ | `AvailabilityPerformanceTest` **با قوانین فعال** سبز است | ⏳ | ⭐⭐ اگر قرمز شد، `spacing` حلقه می‌زند | | ۴.۲ | `SpacingPolicyEngine` تعداد کوئری ثابت دارد، مستقل از تعداد اسلات | ⏳ | | ## ۵. UI این تسک صفحه نمی‌سازد (تسک ۱۰ می‌سازد). فقط: | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۵.۱ | پیام‌های خطای `deny` فارسی و قابل فهم بیمار | ⏳ | نه نام قانون خام | | ۵.۲ | خطای `add_requirement` بدون منبع شامل **نام قانون** | ⏳ | «قانون X جراح می‌خواهد ولی…» | ## ۶. تست | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۶.۱ | `ConditionEvaluatorTest` — همهٔ عملگرها × نوع‌ها، `all`/`any`، فیلد ناموجود | ⏳ | واحد | | ۶.۲ | `CombinerTest` — شش قاعدهٔ جدول مستند | ⏳ | واحد | | ۶.۳ | `PolicyResolverTest` — سه سناریوی حل تناقض + short-circuit | ⏳ | | | ۶.۴ | `SpacingPolicyEngineTest` — بازهٔ ممنوعه + تعداد کوئری ثابت | ⏳ | | | ۶.۵ | `PolicyVersioningTest` — قانون پنجم | ⏳ | ⭐ | | ۶.۶ | `PolicyIntegrationTest` — چهار دسته end-to-end | ⏳ | | | ۶.۷ | `PolicySchemaTest` — هر فیلد schema قابل extract است | ⏳ | ⭐ | | ۶.۸ | `NoPolicyRegressionTest` | ⏳ | ⭐ | ## ۷. مستندات | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۷.۱ | `docs/api/policy.md` با فهرست کامل فیلد/عملگر/اثر | ⏳ | | | ۷.۲ | قاعدهٔ «`DiscountRule` یا `Policy`؟» صریح | ⏳ | ⭐ | | ۷.۳ | `docs/architecture/policy-engine.md` — حل تناقض، ترکیب، دلیل ممنوعیت کد دلخواه، دلیل عدم مهاجرت | ⏳ | | ## ۸. بازبینی پایانی | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۸.۱ | هیچ 🔄 و ⏳ بی‌دلیل نمانده | ⏳ | | | ۸.۲ | `bin/phpunit` کامل سبز | ⏳ | | | ۸.۳ | `--group=slot-mode-frozen` سبز | ⏳ | | | ۸.۴ | `AvailabilityPerformanceTest` با قوانین فعال سبز | ⏳ | | | ۸.۵ | `phpstan` بدون خطای جدید | ⏳ | | | ۸.۶ | `npx tsc --noEmit` و `yarn test` سبز | ⏳ | | | ۸.۷ | تست‌های tenant سبز | ⏳ | | | ۸.۸ | `docs/api/*` به‌روز | ⏳ | | | ۸.۹ | دو کلاینت دیگر بررسی شدند | ⏳ | پیام‌های `deny` در سایت درست نمایش داده می‌شوند؟ | | ۸.۱۰ | commit، سپس `graphify update .` | ⏳ | | | ۸.۱۱ | موارد به‌تعویق با دلیل و تسک مقصد | ⏳ | |