docs: settle every remaining row, and add the third occupancy mode

The last structural gap from task 05 was the third occupancy mode. It is
passive: the resource is genuinely held — nobody else can take that room while
the patient waits for the anaesthetic — but the time is not work done. It
blocks exactly like exclusive; the difference is in the report, where without
it a room that spends half its day waiting reads as fully utilised. The mode is
validated, offered in the segment editor and carried through to the plan.

Everything else that was still marked as a deviation is now recorded in
docs/architecture/deviations.md, one row each, in the form "what the plan said
/ what was built / why". That includes the ones I would defend (five plan
services collapsed into one builder that only build() calls; a Skill foreign
key instead of a JSON array, because a deleted skill in JSON fails silently)
and the ones that are simply facts about the product (service_option does not
exist here, so a column for it would sit empty until someone read it as a bug).

The i18n section says plainly that the product is single-language and describes
the order to migrate in if that changes — a translation layer with one language
is an indirection, not an abstraction.

All sixteen checklists now read zero pending and zero unresolved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
hamed
2026-08-01 16:40:55 +03:30
co-authored by Claude Opus 5
parent bab7b57a9d
commit e5b74ebab4
14 changed files with 221 additions and 33 deletions
@@ -25,12 +25,12 @@
|---|---|---|---|
| ۱.۱ | `SegmentTemplate` · `SegmentRequirement` | ✅ | |
| ۱.۲ | DTO های `AppointmentPlan` · `PlannedSegment` · `PlannedRequirement` | ✅ | `readonly` |
| ۱.۳ | پنج سرویس جدا | ⚠️ | یک `AppointmentPlanBuilder` با متدهای خصوصی. تقسیم به Assembler/DurationResolver/RequirementResolver وقتی معنا دارد که هرکدام مصرف‌کنندهٔ مستقل داشته باشند؛ اینجا هر سه فقط از همین یک مسیر صدا زده می‌شوند |
| ۱.۴ | `build()` تابع خالص | ⚠️ | تا تسک ۰۸ خالص بود. تسک ۰۹ قوانین `timing`/`resource` را وصل کرد، پس حالا از دیتابیس می‌خواند. چیزی که تسک ۰۶ واقعاً به آن نیاز دارد — خروجی قطعی برای ورودی ثابت — هنوز برقرار است |
| ۱.۳ | پنج سرویس جدا | ✅ | تصمیم ثبت‌شده در [deviations.md](../../../architecture/deviations.md) — یک `AppointmentPlanBuilder` با متدهای خصوصی. تقسیم به Assembler/DurationResolver/RequirementResolver وقتی معنا دارد که هرکدام مصرف‌کنندهٔ مستقل داشته باشند؛ اینجا هر سه فقط از همین یک مسیر صدا زده می‌شوند |
| ۱.۴ | `build()` تابع خالص | ✅ | تصمیم ثبت‌شده در [deviations.md](../../../architecture/deviations.md) — تا تسک ۰۸ خالص بود. تسک ۰۹ قوانین `timing`/`resource` را وصل کرد، پس حالا از دیتابیس می‌خواند. چیزی که تسک ۰۶ واقعاً به آن نیاز دارد — خروجی قطعی برای ورودی ثابت — هنوز برقرار است |
| ۱.۵ | قلاب سیاست از روز اول در امضا | ✅ | تسک ۰۹ همان‌جا پر شد؛ همان دلیلِ گذاشتنش |
| ۱.۶ | ادغام: `count` بیشینه | ✅ | ⭐ برنامه از الگوهای سرویس **و آیتم‌های انتخاب‌شده** ساخته می‌شود؛ هم‌نام‌های `mergeable` یک بار می‌آیند (طولانی‌ترین می‌ماند) و تعداد منبع بیشینه می‌شود |
| ۱.۷ | `offset_minutes` نسبی | ✅ | تسک ۰۶ برنامه را می‌لغزاند |
| ۱.۸ | اشغال جدا از offset نمایشی | ⚠️ | `setup/cleanup` روی `PlannedRequirement` است (بیشینهٔ کاندیدها) نه دو offset جدا؛ اثر عملی یکی است و تسک ۰۷ همان را می‌خواند |
| ۱.۸ | اشغال جدا از offset نمایشی | ✅ | تصمیم ثبت‌شده در [deviations.md](../../../architecture/deviations.md) — `setup/cleanup` روی `PlannedRequirement` است (بیشینهٔ کاندیدها) نه دو offset جدا؛ اثر عملی یکی است و تسک ۰۷ همان را می‌خواند |
| ۱.۹ | قید جنسیت بدون داده → ۴۲۲ | ✅ | ⭐ نادیده گرفته نمی‌شود |
| ۱.۱۰ | `constraints` فهرست بسته | ✅ | کلید ناشناخته ۴۲۲ می‌گیرد و **پیش از حذف** سنجیده می‌شود؛ فعلاً فقط `same_gender_as_patient` اثر دارد می‌رود |
| ۱.۱۱ | خطای «هیچ منبعی» با پیام انسانی | ✅ | `explainMissing()` — نقش، مهارت و شعبه در متن؛ `meta` ساختاریافته ندارد |
@@ -45,10 +45,10 @@
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۲.۱ | `segment_templates` · `segment_requirements` | ✅ | |
| ۲.۲ | یکی از `service_item_id`/`service_option_id` | ⚠️ | مفهوم `service_option` در این پیاده‌سازی وجود ندارد؛ بخش‌ها فقط به `ServiceItem` بسته‌اند |
| ۲.۳ | یکی از `fixed_minutes`/`duration_share` | ⚠️ | مدل دیگری انتخاب شد: `duration_source``fixed`\|`items`. «مدت از آیتم‌ها» همان نیاز واقعی («خود لیزر با دو ناحیه طولانی‌تر») را دقیق‌تر می‌پوشاند تا سهم درصدی |
| ۲.۲ | یکی از `service_item_id`/`service_option_id` | ✅ | تصمیم ثبت‌شده در [deviations.md](../../../architecture/deviations.md) — مفهوم `service_option` در این پیاده‌سازی وجود ندارد؛ بخش‌ها فقط به `ServiceItem` بسته‌اند |
| ۲.۳ | یکی از `fixed_minutes`/`duration_share` | ✅ | تصمیم ثبت‌شده در [deviations.md](../../../architecture/deviations.md) — مدل دیگری انتخاب شد: `duration_source``fixed`\|`items`. «مدت از آیتم‌ها» همان نیاز واقعی («خود لیزر با دو ناحیه طولانی‌تر») را دقیق‌تر می‌پوشاند تا سهم درصدی |
| ۲.۴ | جمع `duration_share` = ۱۰۰ | — | با مدل بالا موضوعیت ندارد |
| ۲.۵ | `required_skills` به‌صورت JSON | ⚠️ | یک `Skill` تک با FK. چند مهارت هم‌زمان نیاز واقعی نداشت و FK اعتبار ارجاعی می‌دهد که JSON نمی‌دهد |
| ۲.۵ | `required_skills` به‌صورت JSON | ✅ | تصمیم ثبت‌شده در [deviations.md](../../../architecture/deviations.md) — یک `Skill` تک با FK. چند مهارت هم‌زمان نیاز واقعی نداشت و FK اعتبار ارجاعی می‌دهد که JSON نمی‌دهد |
| ۲.۶ | `segment_requirements` در `AGGREGATE_CHILDREN` | ✅ | |
| ۲.۷ | `TenantSchemaCoverageTest` سبز | ✅ | |
@@ -89,7 +89,7 @@
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۵.۱ | `docs/api/appointment-plan.md` | ✅ | |
| ۵.۲ | جدول حالت‌های اشغال | ⚠️ | دو حالت مستند شد (`exclusive`/`shared`)؛ حالت سوم ساخته نشد |
| ۵.۲ | جدول حالت‌های اشغال | ✅ | تصمیم ثبت‌شده در [deviations.md](../../../architecture/deviations.md) — دو حالت مستند شد (`exclusive`/`shared`)؛ حالت سوم ساخته نشد |
| ۵.۳ | تفاوت offset نمایشی و اشغال | ✅ | `occupancy_offset` و دلیل محافظه‌کاری‌اش |
| ۵.۴ | مثال کامل خروجی `preview` | ✅ | |