# تسک ۱۰ — فرم ساخت قانون و محیط آزمایش

**فاز:** ۲ (قوانین) · **وابستگی:** ۰۹ · **زمان:** ۱۰-۱۲ ساعت

---

## هدف

مستند بند ۱۷، ریسک دوم: «کاربر غیرفنی نمی‌تواند قانون درست تعریف کند → قانون‌های اشتباه،
رفتار عجیب». راه‌حل مستند: **فرم آماده، الگوهای از پیش تعریف‌شده، آزمایش اجباری قبل از
فعال شدن.**

بدون این تسک، تسک ۰۹ یک API قدرتمند است که هیچ‌کس نمی‌تواند از آن استفادهٔ درست کند.

## دامنه

**هست:**
- فرم ساخت قانون که از `GET /api/v1/policy-schema` ساخته می‌شود (نه hard-code در فرانت)
- الگوهای آماده (`policy templates`) — کاربر الگو را انتخاب و مقدار پر می‌کند
- محیط آزمایش (`dry-run`): اجرای قانون روی داده واقعی بدون ثبت هیچ چیز
- **آزمایش اجباری**: `activate` تا وقتی یک اجرای آزمایشی موفق ثبت نشده، رد می‌شود
- نمایش تاریخچهٔ نسخه‌ها با diff

**نیست:** موتور قانون (تسک ۰۹).

## Endpoint ها

| متد | مسیر | توضیح |
|---|---|---|
| POST | `/api/v1/policy/{uuid}/simulate` | اجرای آزمایشی روی نوبت‌های واقعی گذشته |
| GET | `/api/v1/policy-templates` | الگوهای آماده |

`POST /policy/{uuid}/activate` (تسک ۰۹) یک شرط جدید می‌گیرد: وجود یک `simulate` موفق
برای نسخهٔ جاری.

## معیار پذیرش

- ✅ موفق: کاربر الگوی «حداقل فاصله بین جلسات» را انتخاب می‌کند، سرویس و تعداد روز را
  پر می‌کند، `simulate` می‌زند → گزارشی از ۵۰ نوبت اخیر: چند تا تحت تأثیر قرار می‌گرفتند و
  دقیقاً چه تغییری می‌کردند.
- ✅ موفق: `simulate` هیچ ردیفی در دیتابیس نمی‌نویسد (به‌جز `policy_simulation_runs`).
  تست باید تعداد ردیف‌های `appointments`, `price_snapshots`, `resource_occupancy` را
  قبل و بعد مقایسه کند.
- ✅ موفق: بعد از `simulate` موفق، `activate` کار می‌کند.
- ✅ موفق: فرم ساخت قانون بدون هیچ تغییر کد فرانت، فیلد جدیدی که به `FieldRegistry`
  اضافه شود را نشان می‌دهد.
- ❌ خطا: `activate` بدون `simulate` → `422` با پیام «ابتدا قانون را آزمایش کنید».
- ❌ خطا: `activate` بعد از تغییر محتوای قانون (نسخهٔ جدید) → `simulate` قبلی معتبر نیست
  → `422`.
- ⚠️ مرزی: محیطی که هیچ نوبت گذشته‌ای ندارد → `simulate` با گزارش خالی و
  `warning: 'داده‌ای برای آزمایش نیست'` موفق شود (وگرنه کلینیک جدید هرگز نمی‌تواند
  قانون فعال کند).
- ⚠️ مرزی: قانون `deny` که همهٔ ۵۰ نوبت را رد می‌کند → `simulate` موفق ولی با
  `severity: 'high'` و پیام «این قانون همهٔ نوبت‌های نمونه را رد می‌کند».
- ⚠️ مرزی: `simulate` روی قانون دستهٔ `pricing` → تفاوت مبلغ per نوبت نمایش داده شود.

## خروجی

- `src/Policy/Simulation/`
- `assets/admin/pages/PoliciesPage.tsx` + `PolicyFormPage.tsx` + `PolicySimulationPage.tsx`
- `docs/api/policy.md` به‌روزرسانی
