Files
hamedandClaude Opus 5 e5b74ebab4 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>
2026-08-01 16:40:55 +03:30

7.6 KiB
Raw Permalink Blame History

انحراف‌ها از نقشهٔ تسک‌ها

هر ردیفی که در چک‌لیست‌ها ⚠️ مانده، اینجا با سه چیز ثبت است: نقشه چه گفت، چه شد، و چرا. هیچ‌کدام «انجام نشده» نیست؛ همه تصمیم‌اند.

قاعدهٔ خواندن: اگر روزی یکی از این تصمیم‌ها اشتباه از آب درآمد، همین‌جا عوضش کن و دلیل تازه را بنویس. سندی که فقط توجیه جمع کند به درد نمی‌خورد.


چیزهایی که نباید به نقشه برگردند

هیچ‌کدام. سه موردی که قبلاً اینجا بودند (idempotency با catch، تراکنش سراسری لغو، و مانده تجمعی در UI) به نقشه برگردانده شدند — هر کدام با بستنِ باگی که دعوتش می‌کردند. جزئیات در کامیت refactor: take the three risky rows back to the plan.


تسک ۰۴ — کاتالوگ سرویس

ردیف نقشه چه شد چرا
۴.۳ جدول آیتم‌ها با نام/زمان/قیمت جدول در پیش‌نمایش انتخاب است همان ستون‌ها، ولی کنار عددی که کاربر دربارهٔ آن تصمیم می‌گیرد؛ جدول دوم یعنی دو جای خواندن یک چیز
۴.۶ قیمت با PriceInput فقط نمایش با formatRial این تب قیمت را ویرایش نمی‌کند؛ ویرایشش در ServiceItemFormModal است که از قبل PriceInput دارد
۴.۱۰ فرم با RHF + Zod ذخیرهٔ inline per تغییر RHF برای فرمی است که دکمهٔ submit دارد. این تب ندارد — هر تغییر مستقل و فوری ذخیره می‌شود
۴.۱۲ خطاها زیر همان گروه همهٔ خطاها زیر پیش‌نمایش قرارداد پروژه «همهٔ خطاها با هم» است؛ نگاشت group_uuid به کارت، خطا را از کنار عددِ نتیجه دور می‌کرد

تسک ۰۵ — برنامهٔ نوبت

ردیف نقشه چه شد چرا
۱ پنج سرویس جدا یک AppointmentPlanBuilder پنج کلاس وقتی معنا دارد که هرکدام مصرف‌کنندهٔ مستقل داشته باشند؛ اینجا هر پنج فقط از build() صدا زده می‌شوند
۱ build() تابع خالص از دیتابیس می‌خواند تا تسک ۰۸ خالص بود؛ تسک ۰۹ قوانین را وصل کرد و قانون در دیتابیس است. خلوص را فقط با کپی‌کردن قوانین در حافظه می‌شد نگه داشت
۱ اشغال جدا از offset نمایشی setup/cleanup روی PlannedRequirement اثر عملی یکی است و تسک ۰۷ همان را می‌خواند؛ دو offset جدا یعنی دو عدد که باید هم‌زمان درست بمانند
۲.۲ service_item_id یا service_option_id فقط ServiceItem مفهوم service_option در این محصول وجود ندارد. ستونی برای چیزی که نیست، تهی می‌ماند و بعداً کسی فکر می‌کند باگ است
۲.۳ fixed_minutes یا duration_share duration_sourcefixed|items «مدت از آیتم‌ها» همان نیاز واقعی است؛ سهم درصدی هیچ مصرف‌کننده‌ای نداشت
۲.۵ required_skills به‌صورت JSON یک Skill با FK چند مهارت هم‌زمان نیاز واقعی نداشت، و FK اعتبار ارجاعی می‌دهد که JSON نمی‌دهد — مهارتِ حذف‌شده در JSON بی‌صدا می‌ماند

تسک ۰۹ — موتور قوانین

رجیستری‌ها، شش موتور و specificity ذخیره‌شده ساخته شدند (کامیت refactor(policy): build the registries and six engines). آنچه مانده:

ردیف نقشه چه شد چرا
۱.۱۱ forbiddenRanges() — کوئری نه حلقه spacing در لحظهٔ رزرو موقت هزینه‌اش یک اسلات است که نمایش داده و بعد رد می‌شود؛ سودش این است که جستجوی وقت per کاندید یک کوئری تاریخچهٔ بیمار نزند
۲.۳ تسک ۰۶ → Spacing BookingPolicyGuard همان نتیجه، در همان نقطه‌ای که تصمیم واقعی گرفته می‌شود
۲.۴ Eligibility در confirm در hold رد کردن بعد از گرفتن صندلی هم وقت بیمار را تلف می‌کند هم صندلی را
۲.۶ هیچ امضایی عوض نشد سه سرویس یک وابستگی سازنده گرفتند امضای هیچ متد عمومی‌ای عوض نشد؛ سازنده تنها راه رسیدن قانون به آن سه است
نام فیلدها patient.age نقطه‌دار patient_age شرط‌های ذخیره‌شده روی قانون‌های زندهٔ کلینیک‌ها به نام‌های فعلی اشاره می‌کنند؛ تغییرشان مهاجرت داده است و نگاشت یک‌به‌یک هم ندارد

تسک ۱۰ — آزمایشگاه قانون

ردیف نقشه چه شد چرا
۱ فیلتر از داخل condition از دامنهٔ قانون شرط‌ها فیلد id ندارند که به کوئری ترجمه شوند؛ دامنه دقیقاً همان چیزی است که قابل ترجمه است

تسک ۱۱ — دفتر اعتبار

ردیف نقشه چه شد چرا
۴.۴ مانده هرگز منفی نمی‌شود با مصرف پشت‌سرهم تست شد تست هم‌زمانی واقعی حالا هست (testAConcurrentConsumeIsAbsorbedWithoutBurningTheRequest)

تسک ۱۲ — دورهٔ درمان

ردیف نقشه چه شد چرا
۱.۱۲ سخت‌گیرانه‌تر برنده در هر دو جهت فقط max(min) قانون spacing اثر «حداکثر» ندارد، پس min(max) چیزی برای انتخاب کردن ندارد

تسک ۱۳ — لغو و لیست انتظار

ردیف نقشه چه شد چرا
۳.۱ Service + Matcher جدا یک WaitlistNotifier تطبیق یک کوئری در repository است؛ کلاس جدا فقط یک لایهٔ اسم‌گذاری می‌شد

تسک ۱۴ — رویدادها

ردیف نقشه چه شد چرا
۱.۱ کلاس پایه + چهارده زیرکلاس فهرست بستهٔ نام + یک entity زیرکلاسِ خالی که فقط نام را در تایپ نگه می‌دارد، همان کاری را می‌کند که const — با چهارده فایل بیشتر

چندزبانگی (i18n)

پروژه فایل i18n ندارد و همهٔ رشته‌ها inline اند. محصول تک‌زبانه است و لایهٔ ترجمه بدون زبان دوم فقط یک واسطهٔ اضافه است.

روزی که زبان دوم لازم شد، ترتیبش این است: اول assets/admin/lib/i18n.ts با یک t() ساده و کلیدهای تخت؛ بعد صفحه‌به‌صفحه مهاجرت، نه یک‌باره. مهاجرت هم‌زمانِ هزاران رشته یعنی رگرسیون متنی که هیچ تستی نمی‌گیردش.