test(booking): freeze slot-mode contract before service-mode work

Adds SlotModeFrozenTest (#[Group('slot-mode-frozen')]) locking three things
against the multi-resource booking phase:
  - GET /api/v1/appointment-slots response shape
  - GET /api/v1/appointment-settings/month-availability/{uuid} response shape
  - public method signatures of SlotCalculatorService

Fixtures are structural, not raw snapshots: a fixed past date is rejected by
isWithinBookingWindow so an empty snapshot would prove nothing. Instead a
deterministic schedule on a computed near-future date, with epoch/uuid values
normalized to placeholders. What stays locked is the contract itself: keys,
ordering, types and local times.

No production code touched.

Task: docs/new_feture/taskes/task-00-service-mode-completion/
Slot-mode contract: unchanged (--group=slot-mode-frozen green, 3 tests)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
hamed
2026-07-30 12:31:57 +03:30
co-authored by Claude Opus 5
parent f2ecaf201a
commit c37c0cee39
6 changed files with 931 additions and 11 deletions
@@ -0,0 +1,569 @@
# راهبر اجرای تسک‌های موتور نوبت‌دهی — یک تسک در هر اجرا
## پروژه
`clinicpro` (بک‌اند + پنل ادمین)
پرامپت همتا برای سایت عمومی: `nobat724_front/.claude/prompt/booking-engine-task-00b-service-mode.md`
(همین راهبر وقتی به تسک ۰۰ب برسد، تحویلش می‌دهد و می‌ایستد.)
---
## زمینه
`docs/new_feture/taskes/` شانزده تسک برای پیاده‌سازی
[مستند موتور نوبت‌دهی](../../docs/new_feture/clinic-pro-mostanad-sade.md) دارد. هر تسک یک
پوشه با شش فایل است و **خودش یک پرامپت کامل است**: `task.md` (دامنه و معیار پذیرش)،
`architecture.md` (فایل‌ها و سرویس‌ها)، `database.md` (جداول و migration)،
`implementation_notes.md` (نکات و edge case و تست)، `checklist.md` (وضعیت)، و برای
تسک‌های پیچیده `user_flow.md`.
جمع زمان تخمینی ۲۱۴ تا ۲۵۶ ساعت است. هیچ اجرایی نمی‌تواند همه را در یک نشست تمام کند.
**پس این پرامپت خودش قابلیت نیست — راهبر است.** هر اجرا:
```
وضعیت را از checklist.md ها بخوان
→ تسک بعدی واجد شرایط را انتخاب کن
→ همان یک تسک را کامل انجام بده (پیاده‌سازی + تست + مستند + چک‌لیست)
→ commit + graphify update
→ بایست و بگو تسک بعدی چیست
```
همین پرامپت را دوباره اجرا کن تا تسک بعدی برود. تا وقتی همهٔ چک‌لیست‌ها ✅ نشده‌اند، کار
تمام نیست.
---
## مشکل / هدف
**هدف:** یک مسیر اجرای تکرارشونده و ایمن که شانزده تسک را به ترتیب وابستگی، یکی‌یکی و
بدون از دست دادن وضعیت پیش ببرد — طوری که هر اجرا مستقل باشد و قطع شدن وسط کار،
کار انجام‌شده را از بین نبرد.
**چرا این شکل و نه یک پرامپت غول:** وضعیت در `checklist.md` روی دیسک زندگی می‌کند، نه در
context. اجرای بعدی همان فایل را می‌خواند و می‌فهمد کجا مانده. هزینهٔ این طراحی یک
مرحلهٔ «خواندن وضعیت» در ابتدای هر اجراست؛ سودش این است که هیچ اجرایی به context اجرای
قبلی وابسته نیست.
---
## معیار پذیرش
معیار پذیرش این راهبر، **رفتار خودِ راهبر** است. معیار پذیرش هر تسک در `task.md` همان تسک
است و اینجا تکرار نمی‌شود.
-**موفق:** اجرا با شناسایی وضعیت شروع می‌شود (جدول ۱۶ تسک با ✅/🔄/⏳)، دقیقاً یک تسک
انتخاب می‌شود، همهٔ ردیف‌های `checklist.md` آن تسک به ✅ می‌رسند، `bin/phpunit` و
`--group=slot-mode-frozen` سبزند، `docs/api/*` به‌روز است، یک commit زده می‌شود،
`graphify update .` اجرا می‌شود، و اجرا با اعلام تسک بعدی تمام می‌شود.
-**موفق (ازسرگیری):** اجرای دوم روی همان مخزن، تسک تمام‌شده را **دوباره انجام نمی‌دهد**
و مستقیم سراغ تسک بعدی می‌رود.
-**موفق (نیمه‌کاره):** اگر چک‌لیستی ردیف 🔄 دارد، همان تسک ادامه داده می‌شود، نه تسک بعدی.
-**خطا:** اگر تسکی وابستگی‌اش ✅ نیست → اجرا شروع نمی‌شود؛ پیام روشن با نام تسک
پیش‌نیاز و توقف.
-**خطا:** اگر `--group=slot-mode-frozen` قرمز شد → **توقف کامل**، rollback تغییرات آن
مرحله، گزارش دقیق کدام fixture شکست. fixture هرگز به‌روز نمی‌شود.
-**خطا:** اگر تستی سبز نشد → تسک `completed` نمی‌شود؛ ردیف چک‌لیست 🔄 می‌ماند و اجرا
با گزارش خطا تمام می‌شود. تسک بعدی شروع نمی‌شود.
- ⚠️ **مرزی:** همهٔ ۱۶ چک‌لیست ✅ → پیام «همهٔ تسک‌ها تمام شده‌اند» و توقف بدون تغییر.
- ⚠️ **مرزی:** تسک انتخاب‌شده ۰۰ب است (پروژهٔ دیگر) → اجرا **کد نمی‌زند**؛ دستور اجرای
پرامپت `nobat724_front` را می‌دهد و می‌ایستد.
- ⚠️ **مرزی:** کاربر شمارهٔ تسک را صریح داد (`/run-prompt … --task=05`) → همان تسک، ولی
وابستگی‌ها همچنان بررسی می‌شوند و اگر ناقص بودند توقف.
- ⚠️ **مرزی:** ردیفی از چک‌لیست به دلیل موجه قابل انجام نیست → ⏳ می‌ماند **با دلیل مکتوب و
تسک مقصد** در ستون یادداشت. ⏳ بی‌دلیل = تسک تمام نشده.
---
## فایل‌های مرتبط
| فایل | نقش |
|------|-----|
| `docs/new_feture/taskes/README.md` | فهرست ۱۶ تسک، وابستگی‌ها، ترتیب اجرا |
| `docs/new_feture/taskes/00-current-state-report.md` | تحلیل شکاف وضعیت فعلی در برابر مستند |
| `docs/new_feture/taskes/_shared/red-lines.md` | ⛔ قواعد قفل‌شده — منطق اسلاتی دست‌کاری نمی‌شود |
| `docs/new_feture/taskes/_shared/ui-conventions.md` | 🎨 توکن‌ها و کامپوننت‌های اجباری |
| `docs/new_feture/taskes/_shared/definition-of-done.md` | ☑️ نمادها و بازبینی پایانی |
| `docs/new_feture/taskes/task-XX-*/task.md` | دامنه و معیار پذیرش هر تسک |
| `docs/new_feture/taskes/task-XX-*/architecture.md` | فایل‌ها، سرویس‌ها، قواعد UI |
| `docs/new_feture/taskes/task-XX-*/database.md` | جداول، ایندکس، migration، backfill |
| `docs/new_feture/taskes/task-XX-*/implementation_notes.md` | edge case و فهرست تست |
| `docs/new_feture/taskes/task-XX-*/checklist.md` | **وضعیت پایدار — منبع حقیقت پیشرفت** |
| `docs/new_feture/taskes/task-XX-*/user_flow.md` | جریان کاربری (۰۰، ۰۰ب، ۰۵، ۰۶، ۰۷، ۱۲) |
| `CLAUDE.md` · `docs/architecture/tenancy.md` | قواعد ثابت پروژه |
---
## وضعیت فعلی
هر شانزده `checklist.md` ساخته شده و **همهٔ ردیف‌هایشان `⏳` است**. هیچ تسکی شروع نشده.
ساختار ثابت هر چک‌لیست:
```markdown
# چک‌لیست — تسک XX (عنوان)
**وضعیت کلی:** ⏳ شروع نشده · **آخرین بازبینی:**
## ۰. خط سرخ
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۰.۱ | `--group=slot-mode-frozen` سبز | ⏳ | |
...
## ۱. بک‌اند
## ۲. دیتابیس
## ۳. UI
## ۴. تست
## ۵. مستندات
## ۶. بازبینی پایانی
```
نمادها (از `_shared/definition-of-done.md`):
| نماد | معنی | اجازهٔ باقی‌ماندن در پایان تسک |
|---|---|---|
| ✅ | انجام‌شده و تأییدشده | بله |
| 🔄 | در حال انجام | **نه** |
| ⏳ | انجام‌نشده | **نه** — مگر با دلیل مکتوب و تسک مقصد |
| ⚠️ | نیازمند بررسی یا تست | **نه** — باید تعیین تکلیف شود |
ترتیب و وابستگی (از `README.md`):
```
۰۰ ── ۰۰ب
├─ ۰۱ ─┬─ ۰۲ ── ۰۳ ─┐
│ └─ ۰۴ ── ۰۵ ─┴─ ۰۶ ── ۰۷ ─┬─ ۰۸ ─┬─ ۰۹ ── ۱۰
│ │ └─ ۱۱ ── ۱۲
│ ├─ ۱۳
│ └─ ۱۴
```
| تسک | پروژه | وابستگی |
|---|---|---|
| ۰۰ تکمیل نوبت‌دهی سرویسی | `clinicpro` | — |
| ۰۰ب سازگارسازی سایت | `nobat724_front` | ۰۰ |
| ۰۱ شعبه و اتاق | `clinicpro` | ۰۰ |
| ۰۲ منبع، مهارت، استخر | `clinicpro` | ۰۱ |
| ۰۳ تقویم منبع | `clinicpro` | ۰۱، ۰۲ |
| ۰۴ کاتالوگ خدمات v2 | `clinicpro` | ۰۱ |
| ۰۵ بخش‌های نوبت | `clinicpro` | ۰۲، ۰۴ |
| ۰۶ جستجوی وقت چندمنبعی | `clinicpro` | ۰۳، ۰۵ |
| ۰۷ رزرو موقت و ثبت | `clinicpro` | ۰۶ |
| ۰۸ قیمت و snapshot | `clinicpro` | ۰۴، ۰۷ |
| ۰۹ موتور قوانین | `clinicpro` | ۰۵، ۰۶، ۰۸ |
| ۱۰ فرم و sandbox قانون | `clinicpro` | ۰۹ |
| ۱۱ پکیج و دفتر اعتبار | `clinicpro` | ۰۸ |
| ۱۲ دوره درمان | `clinicpro` | ۰۷، ۱۱ |
| ۱۳ لغو، عدم حضور، انتظار | `clinicpro` | ۰۷ |
| ۱۴ رویدادها و بهره‌وری | `clinicpro` | ۰۷ |
---
## وظایف
> ⚠️ این هفت وظیفه **مراحل یک اجرا**ی راهبرند، نه هفت قابلیت مستقل. `todo` را از روی
> ردیف‌های `checklist.md` تسکِ انتخاب‌شده بساز، نه از روی این هفت مرحله.
### ۱. خواندن قواعد حاکم — پیش از هر چیز
سه سند را کامل بخوان و در همین اجرا اعمال کن:
```
docs/new_feture/taskes/_shared/red-lines.md
docs/new_feture/taskes/_shared/ui-conventions.md
docs/new_feture/taskes/_shared/definition-of-done.md
```
در تناقض با متن هر تسک، **این سه برنده‌اند**.
مهم‌ترین بندشان:
- ⛔ منطق اسلاتی (`booking_mode = 'slot'`) در **هیچ تسکی** دست‌کاری نمی‌شود. فهرست فایل‌ها و
متدهای قفل‌شده در `red-lines.md` است.
- 🎨 هر صفحه یا کامپوننت جدید عیناً با دیزاین‌سیستم موجود — توکن‌های `assets/admin/styles.css`،
کامپوننت‌های `assets/admin/components/ui/`، پنج قاعدهٔ غیرقابل‌مذاکره.
- ☑️ هیچ تسکی بدون تکمیل چک‌لیستش تمام نیست.
**نحوه تست:** بعد از خواندن، در گزارش شروع بنویس کدام سه سند خوانده شد و مهم‌ترین قید
مربوط به تسک انتخاب‌شده چیست (یک جمله).
---
### ۲. شناسایی وضعیت و انتخاب تسک
همهٔ چک‌لیست‌ها را بخوان و وضعیت هر تسک را از **ردیف‌هایش** استنتاج کن، نه از خط
«وضعیت کلی» (که ممکن است به‌روز نشده باشد):
```bash
# فهرست چک‌لیست‌ها به ترتیب
ls -1 docs/new_feture/taskes/*/checklist.md | sort
# شمارش نمادها per تسک — دیدِ سریع وضعیت
for f in docs/new_feture/taskes/*/checklist.md; do
echo "$(dirname "$f" | xargs basename): ✅=$(grep -c '| ✅ |' "$f") 🔄=$(grep -c '| 🔄 |' "$f") ⏳=$(grep -c '| ⏳ |' "$f") ⚠️=$(grep -c '| ⚠️ |' "$f")"
done
```
قاعدهٔ استنتاج:
```
تمام‌شده = هیچ ردیف 🔄 · هیچ ردیف ⚠️ · هیچ ردیف ⏳ بدون یادداشتِ دلیل
در جریان = حداقل یک ردیف 🔄 یا ⚠️
شروع نشده = همهٔ ردیف‌ها ⏳ و هیچ یادداشتی
```
قاعدهٔ انتخاب، به همین ترتیب:
1. اگر تسکی **در جریان** است → همان. (تسک نیمه‌کاره رها نمی‌شود.)
2. وگرنه اولین تسکِ **شروع نشده** که همهٔ وابستگی‌هایش **تمام‌شده** اند.
3. اگر کاربر `--task=XX` داد → همان، ولی وابستگی‌ها همچنان بررسی می‌شوند.
4. اگر همه تمام‌شده‌اند → پیام و توقف.
5. اگر تسک انتخاب‌شده وابستگی ناقص دارد → توقف با نام تسک پیش‌نیاز.
جدول وضعیت را به کاربر نشان بده و تسک انتخاب‌شده را اعلام کن:
```
📊 وضعیت تسک‌ها
۰۰ تکمیل سرویسی ✅ تمام‌شده
۰۰ب سازگارسازی سایت ✅ تمام‌شده
۰۱ شعبه و اتاق 🔄 در جریان (۱۲/۳۸)
۰۲ منبع و مهارت ⏳ شروع نشده
🎯 تسک انتخاب‌شده: ۰۱ — شعبه و اتاق (ادامهٔ کار نیمه‌کاره)
```
**نحوه تست:** دستور شمارش بالا را اجرا کن و خروجی واقعی‌اش را در گزارش بگذار. جدول
نمایش‌داده‌شده باید با آن خروجی بخواند.
---
### ۳. تحویل به پرامپت همتا اگر تسک ۰۰ب انتخاب شد
تسک ۰۰ب روی `nobat724_front` است و در این پروژه هیچ کدی ندارد.
اگر انتخاب ۰۰ب شد، **هیچ فایلی را تغییر نده** و این را چاپ کن و بایست:
```
🔀 تسک ۰۰ب روی پروژهٔ nobat724_front است.
اجرا کن: /run-prompt nobat724_front/.claude/prompt/booking-engine-task-00b-service-mode.md
بعد از تمام شدنش، همین راهبر را دوباره اجرا کن تا تسک ۰۱ برود.
```
**نحوه تست:** موقتاً همهٔ ردیف‌های `task-00-service-mode-completion/checklist.md` را ✅ کن،
راهبر را اجرا کن، و ببین همین پیام چاپ می‌شود و هیچ فایلی در `src/` عوض نمی‌شود.
بعد تغییر موقت را برگردان.
---
### ۴. اجرای تسک انتخاب‌شده — یکی، کامل
چهار (یا پنج) فایل تسک را **کامل** بخوان:
```
task-XX-*/task.md ← دامنه، endpoint ها، معیار پذیرش
task-XX-*/architecture.md ← فایل‌ها، سرویس‌ها، الگوها، قواعد UI
task-XX-*/database.md ← جداول، ایندکس، migration، backfill
task-XX-*/implementation_notes.md ← edge case ها، فهرست تست، تله‌های مشخص
task-XX-*/user_flow.md ← اگر وجود دارد
```
بعد کد واقعی مرتبط را بخوان — **هیچ فرضی از حافظه** (guidelines §۱).
سپس `todo` بساز: **یک آیتم به ازای هر ردیفِ غیر-✅ چک‌لیست**، مرتب بر اساس بخش
(۰ خط سرخ → ۱ بک‌اند → ۲ دیتابیس → ۳ UI → ۴ تست → ۵ مستندات → ۶ بازبینی پایانی).
اجرای هر ردیف با همان هفت‌مرحلهٔ `run-prompt`:
```
① تحلیل ② طراحی ③ پیاده‌سازی ④ تست ⑤ رفع خطا ⑥ مستندسازی ⑦ چک‌لیست + گزارش
```
قواعدی که در این مرحله شکستنی نیستند:
- **بخش ۰ چک‌لیست اول از همه.** خط سرخ‌ها پیش از هر کد بررسی می‌شوند. در تسک ۰۰ این یعنی
ساختن `SlotModeFrozenTest` و سه fixture **قبل** از هر تغییر — وگرنه snapshot وضعیت
تغییریافته گرفته می‌شود و چیزی را تضمین نمی‌کند.
- **یک ردیف در هر لحظه `in_progress`.**
- **ردیف فقط با هر سه ✅ می‌شود:** پیاده‌سازی + تست سبز + مستند به‌روز.
- **خطا = توقف کامل.** هیچ ردیفی روی خرابهٔ ردیف قبلی ساخته نمی‌شود.
- **کشف کار جدید** (باگ جانبی، وابستگی پنهان) → ردیف جدید به همان `checklist.md` **اضافه
کن** و به کاربر بگو. انجام بی‌صدا ممنوع.
**نحوه تست:** دستورهای هر پروژه بعد از هر ردیفِ منطق‌دار:
```bash
ddev exec php bin/console cache:clear
ddev exec php bin/console doctrine:migrations:diff --no-interaction # اگر entity عوض شد
ddev exec php bin/console doctrine:migrations:migrate --no-interaction
ddev exec php bin/phpunit tests/<Domain> # تست همان تسک
ddev exec php bin/phpunit --group=slot-mode-frozen # ⛔ بعد از هر ردیف
ddev exec php vendor/bin/phpstan analyse src/<Domain>
npx tsc --noEmit --project tsconfig.json # اگر tsx عوض شد
yarn dev # بررسی خطای build
yarn test
```
برای هر endpoint، تست واقعی با داده واقعی — نه فقط `php -l`. کاربر تست:
`09390039833 / 09390039833` (اگر کار نکرد، اعتبارهای per-نقش با `QaTest@1234`).
---
### ۵. به‌روزرسانی زندهٔ `checklist.md`
چک‌لیست **حافظهٔ بین‌اجرایی** است. اگر به‌روز نشود، اجرای بعدی کار انجام‌شده را دوباره
انجام می‌دهد.
قواعد نوشتن:
- ردیف را **همان لحظه** که شروع می‌کنی `⏳ → 🔄` کن، نه در پایان تسک
- تمام شد → `🔄 → ✅` با یادداشت کوتاه اگر چیزی برای گفتن هست (مسیر فایل، عدد اندازه‌گیری‌شده)
- شکست خورد یا بلاک شد → `⚠️` با **دلیل** در ستون یادداشت
- به تعویق افتاد → `⏳` با **دلیل + تسک مقصد** در یادداشت. `⏳` بی‌دلیل = تسک تمام نشده.
- خط سربرگ را هم به‌روز کن:
```markdown
**وضعیت کلی:** 🔄 در حال انجام · **آخرین بازبینی:** ۱۴۰۵/۰۵/۰۳
```
پیش از رفتن به وظیفهٔ ۶، **بخش ۶ (یا ۷/۸ بسته به تسک) بازبینی پایانی** را ردیف‌به‌ردیف
اجرا کن. ردیف‌های مشترک همهٔ تسک‌ها:
```
همهٔ ردیف‌های بالا وضعیت نهایی دارند (هیچ 🔄 و ⏳ بی‌دلیل)
ddev exec php bin/phpunit کامل سبز
ddev exec php bin/phpunit --group=slot-mode-frozen سبز
ddev exec php vendor/bin/phpstan analyse بدون خطای جدید
npx tsc --noEmit بدون خطا
yarn test سبز
TenantSchemaCoverageTest + TenantLookupInventoryTest سبز
docs/api/* به‌روز شد
چک‌لیست UI کامل شد (اگر تسک صفحه دارد)
nobat724_front و clinic-pro-tauri دستی بررسی شدند
commit شد، سپس graphify update .
موارد به‌تعویق‌افتاده با دلیل و تسک مقصد ثبت شدند
```
ردیف «دو کلاینت دیگر» در build هیچ‌کدام خطا نمی‌دهد — بررسی **فقط دستی** ممکن است
(guidelines §۳). نتیجه را با ذکر فایل‌های بررسی‌شده گزارش بده، نه با «بررسی شد».
**نحوه تست:** بعد از به‌روزرسانی، دستور شمارش وظیفهٔ ۲ را دوباره اجرا کن. تسک باید
`✅=<همه> 🔄=0 ⏳=0 ⚠️=0` بدهد.
---
### ۶. Commit و به‌روزرسانی گراف
**ترتیب مهم است:** اول commit، بعد `graphify update` — وگرنه گراف وضعیت کامیت‌نشده را
ایندکس می‌کند.
```bash
git add -A
git commit -m "$(cat <<'EOF'
feat(booking): <عنوان انگلیسی تسک>
<دو-سه خط: چه چیزی اضافه شد، چه چیزی عمداً دست‌نخورده ماند>
Task: docs/new_feture/taskes/task-XX-<name>/
Slot-mode contract: unchanged (--group=slot-mode-frozen green)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
EOF
)"
graphify update .
```
پیام کامیت انگلیسی (قاعدهٔ پروژه). خط `Slot-mode contract` اجباری است — رد مکتوبِ
اینکه خط سرخ رعایت شده.
اگر روی برنچ `main` هستی، **اول برنچ بساز**:
```bash
git checkout -b feat/booking-engine-task-XX
```
**نحوه تست:** `git log --oneline -1` و `git status` را در گزارش بگذار.
---
### ۷. گزارش پایانی و اعلام تسک بعدی
اجرا با این قالب تمام می‌شود و **تسک بعدی شروع نمی‌شود**:
```
✅ تسک XX — <عنوان> تمام شد
پیاده‌سازی
• <سه تا پنج خط: چه ساخته شد، با مسیر فایل>
تست
• bin/phpunit : <N> تست، سبز
• --group=slot-mode-frozen : سبز
• phpstan / tsc / yarn test : سبز
• تست‌های جدید این تسک : <فهرست کوتاه>
مستندات
• <فایل‌های docs/api/* و docs/architecture/* که عوض شدند>
خط سرخ
• منطق اسلاتی دست‌نخورده — <یک جمله شاهد، مثلاً: هیچ متد SlotCalculatorService عوض نشد>
کلاینت‌های دیگر
• nobat724_front : <چه بررسی شد و نتیجه>
• clinic-pro-tauri: <همان>
به تعویق افتاد (اگر هست)
• <ردیف> — دلیل: <…> — تسک مقصد: <XX>
commit: <hash کوتاه> · graphify: به‌روز شد
────────────────────────────────
🎯 تسک بعدی: XX — <عنوان> (وابستگی‌ها: ✅)
اجرا کن: /run-prompt clinicpro/.claude/prompt/booking-engine-task-runner.md
```
اگر تسک ناتمام ماند، به‌جای `✅` بنویس:
```
⛔ تسک XX ناتمام ماند
بلاک‌کننده: <چه چیزی و کجا>
ردیف‌های باقی: <فهرست با وضعیت>
چک‌لیست به‌روز شد؛ اجرای بعدی از همین‌جا ادامه می‌دهد.
```
**نحوه تست:** گزارش باید با خروجی واقعی دستورها بخواند. عدد تست ساختگی ننویس.
---
## نکات مهم
### خط سرخ — تنها چیزی که هیچ تسکی نمی‌تواند نقض کند
`booking_mode = 'slot'` منطق تولیدیِ زنده است. `_shared/red-lines.md` فهرست دقیق دارد؛
خلاصه‌اش:
| قفل | یعنی |
|---|---|
| هفت متد عمومی `SlotCalculatorService` | امضا و رفتار عوض نمی‌شود |
| `GET /api/v1/appointment-slots` | قرارداد request/response |
| `GET /api/v1/appointment-settings/month-availability/{doctorUuid}` | قرارداد |
| `Appointment::active_slot_key` و `refreshActiveSlotKey()` | مکانیزم یکتایی موجود |
| `AppointmentRepository::isSlotTaken` | امضا و معنا |
| `WeeklySchedule::DEFAULT_META['booking_mode']` | مقدار `slot` می‌ماند |
**مجاز:** افزودن متد جدید · کلاس موازی · ستون تهی‌پذیر · کلید جدید در `meta` با حفظ پیش‌فرض
**ممنوع:** تغییر امضای متد موجود · refactor «یکدست‌سازی» · اعمال قوانین جدید روی حالت `slot`
fixture های `tests/Appointment/fixtures/slot-mode-contract.json` و
`month-availability-contract.json` و `slot-calculator-signatures.php` **read-only** اند.
اگر تست قرمز شد، **کد برمی‌گردد، نه fixture**. تسک ۰۰ آن‌ها را می‌سازد؛ هیچ تسک دیگری
اجازهٔ به‌روزرسانی‌شان را ندارد.
### فاز ۰ اختیاری نیست
تسک ۰۰ و ۰۰ب پیش‌نیاز واقعی‌اند، نه توصیه. حالت `service` امروز پنج شکاف در چرخهٔ عمر
نوبت دارد (ویرایش، جابه‌جایی، رزرو، پنل بیمار، دیزاین‌سیستم سایت — جزئیات در
`00-current-state-report.md` بخش ۲-۵ب). اگر حالت `resource` (تسک ۰۶) روی این پایه ساخته
شود، هر باگ موجود به موتور جدید ارث می‌رسد و تشخیص منبعش غیرممکن می‌شود.
### دیزاین‌سیستم — هر صفحهٔ جدید
`_shared/ui-conventions.md` کامل است. پنج قاعده‌ای که رعایت نکردنشان یعنی رد شدن ردیف:
1. `SearchableSelect`، هرگز `<select>` بومی
2. زیرصفحه‌ها `backTo` یا `<BackButton fallback="…" />` — دکمهٔ دست‌ساز نه
3. وضعیت لیست (جستجو/فیلتر/صفحه) در URL با `hooks/useUrlState.ts`، نه `useState`
4. داده فقط TanStack Query از `lib/api.ts`؛ استخراج: paginated → `data?.data` +
`data?.meta?.totalRecords` · single → `data?.data` · Category → `data?.data?.data ?? []`
5. فرم با React Hook Form + Zod
و: هیچ رنگ/شعاع/سایهٔ hard-code — همه از توکن‌های `:root` در `assets/admin/styles.css`.
دارک‌مود (`[data-theme="dark"]`) و حالت فشرده (`[data-density="compact"]`) بررسی شوند.
مقدار hex در دارک‌مود می‌شکند.
پیش از ساختن هر کامپوننت، `assets/admin/components/ui/` را بگرد. ساختن نسخهٔ موازیِ
`DataTable`/`Modal`/`ConfirmDialog`/`PageHeader`/`StatCard`/`StatusBadge`/`Pagination`/
`PersianDatePicker`/`PriceInput` رد می‌شود.
### قواعد ثابت پروژه در هر تسک
از `CLAUDE.md` و `docs/architecture/tenancy.md`:
- entity جدید یا `TenantOwnedTrait` می‌گیرد یا با دلیل در `App\Shared\Tenant\GlobalTables`
ثبت می‌شود؛ `TenantSchemaCoverageTest` هر دو حالت را اجبار می‌کند
- `entity_type, entity_id` **ستون‌های اول** هر ایندکس ترکیبیِ لیست — وگرنه MariaDB برای
شرط `TenantFilter` از ایندکس استفاده نمی‌کند
- هر uuid که از request می‌آید با `App\Shared\Tenant\TenantOwnershipChecker` سنجیده
می‌شود؛ `TenantLookupInventoryTest` شمارنده per-file دارد و `findByUuid` تازه تست را
قرمز می‌کند
- timestamp ها `int` یونیکس، نه `DateTime`؛ نمایش شمسی فقط در UI با `formatDate`
- کنترلر نازک · `extends BaseController` · `$this->success()/paginated()/error()`
- خطا با `throw new AppException(ErrorCodes::ERR_XXX, null, $status)`؛ کد و پیام فارسی در
`src/Shared/Constant/ErrorCodes.php`
- لیست‌های ادمین با `->getArrayResult()`
- **API جدید فقط وقتی هیچ endpoint موجودی — حتی با توسعه — کافی نباشد؛ دلیلش نوشته شود**
- هر endpoint جدید یا تغییریافته → `docs/api/*.md` در **همان** نشست
### الگوهای معماری که تسک‌ها تصریح کرده‌اند
| الگو | تسک | دلیل انتخاب (در همان تسک مکتوب است) |
|---|---|---|
| Strategy + tagged_iterator | ۰۶ `ResourcePickerInterface` | افزودن استراتژی پنجم نباید کلاس موجود را عوض کند (OCP) |
| Repository + سرویس واحدِ نویسنده | ۰۷ `OccupancyWriter` | تنها نقطهٔ نوشتن در `resource_occupancy` |
| Registry بسته | ۰۹ `FieldRegistry`/`OperatorRegistry`/`EffectRegistry` | مستند بند ۸: کد دلخواه در قانون ممنوع، وگرنه جستجوی وقت کند می‌شود |
| Outbox | ۱۴ `DomainEventPublisher` | commit اتمی رویداد با کار؛ نه «پیامک رفته و نوبت نیست» |
| Ledger (دفتر) نه شمارنده | ۱۱ `SessionCreditLedger` | مستند بند ۱۲: با شمارنده، اولین اشتباه غیرقابل‌ردیابی است |
| کلید یکتای سطل زمانی | ۰۷ `resource_occupancy_slot` | MariaDB `EXCLUDE` ندارد؛ تضمین از دیتابیس نه از کد |
این‌ها تصمیم گرفته‌شده‌اند، نه پیشنهاد. اگر در اجرا به مشکل خوردی، **قبل از تغییر
الگو** دلیل را به کاربر بگو و تعیین تکلیف بخواه (guidelines §۶).
### تسک‌های پرخطر — جای کندی و دقت
| تسک | ریسک | کاری که باید بکنی |
|---|---|---|
| ۰۰ | fixture بعد از تغییرات ساخته شود | fixture **اول**، بعد کد |
| ۰۶ | I/O داخل حلقه → جستجو کند | `AvailabilityPerformanceTest` تعداد کوئری را قفل می‌کند: ≤۵ |
| ۰۷ | تست همزمانی با mock بی‌ارزش است | دو اتصال DBAL واقعی، `assertSame(1, ok1+ok2)` |
| ۰۹ | `spacing` per-slot حلقه بزند | `forbiddenRanges()` کوئری‌محور؛ `AvailabilityPerformanceTest` باید با قوانین فعال هم سبز بماند |
| ۱۰ | شبیه‌سازی داده واقعی بنویسد | rollback در `finally` + `em->clear()` + تست شمارش ردیف |
| ۱۱ | `quote` اعتبار مصرف کند | مصرف **فقط** در `confirm`؛ تست: ده `quote` → مانده بی‌تغییر |
| ۱۲ | `book-all` نیمه‌کاره | همه یا هیچ در یک تراکنش |
### cross-repo — بررسی دستی اجباری
`nobat724_front/services/response.js` و `clinic-pro-tauri/src/service/response.js` هر دو
مصرف‌کنندهٔ همان `/api/v1/...` اند. تغییر قرارداد در build هیچ‌کدام خطا نمی‌دهد و در
runtime می‌شکند (guidelines §۳).
تسک‌هایی که قرارداد را واقعاً عوض می‌کنند و بررسی دستی‌شان بحرانی است:
- **۰۰** — `service_item` تکی باید با `service_items` هم‌گام بماند (چهار مصرف‌کننده رویش خوانده‌اند)
- **۰۴** — مدت نوبت‌های چندسرویسی **عوض می‌شود** (فرمول «زمان تنها/زمان اضافه»)
- **۰۶** — پس از ارتقای یک کلینیک به `resource`، کلاینت باید مسیر جدید صدا بزند
- **۰۸** — مبلغ نمایشی رزرو ممکن است عوض شود
- **۱۳** — سایت باید `cancellation-preview` را نشان دهد
در گزارش هر تسک، **فایل‌های بررسی‌شده را نام ببر**؛ «بررسی شد» بی‌ارزش است.
### وقتی متن تسک با کد واقعی نمی‌خواند
تسک‌ها از خواندن کد در تاریخ نوشتنشان ساخته شده‌اند. اگر جایی متن با کد امروز نخواند:
1. **کد واقعی مرجع است**، نه متن تسک
2. اختلاف را به کاربر بگو
3. متن تسک را اصلاح کن (فایل تسک هم سند است — guidelines §۴)
4. بعد پیاده‌سازی کن
موارد شناخته‌شده‌ای که تسک‌ها خودشان علامت زده‌اند و باید **پیش از کدنویسی** تأیید شوند:
- تسک ۱۴ ردیف ۲.۹ — وجود `patient_sessions.started_at/ended_at`؛ اگر نبود، گزارش
`plan-accuracy` به تسک جدا موکول می‌شود، **نه** ساخته شدن با داده حدسی
- تسک ۰۰ب بخش ۱ — وجود `service_items` و `service_total_minutes` در پاسخ
`GET /api/v1/appointments/user`؛ اگر نبود، به تسک ۰۰ برمی‌گردد
- تسک ۰۷ database.md — وابستگی معکوس: جدول `resource_occupancy` در تسک **۰۶** migrate
می‌شود، تعریفش در ۰۷ است