# قانون تغییرناپذیری ۱۰ روزه

پیاده‌سازی: `backend/src/modules/articles/service.ts`
آزمون: `backend/tests/critical.test.ts` → suite «قانون ۱۰ روزه»

## ۱. قاعده

> هر خبر منتشرشده، **۱۰ روز پس از نخستین انتشار**، تغییرناپذیر می‌شود.
> پس از آن، اصلاح فقط از راه **اصلاحیه رسمی** با دلیل مکتوب ممکن است.

مقدار ۱۰ روز از پیکربندی خوانده می‌شود: `config.editorial.editWindowDays`
(قابل تنظیم با متغیر محیطی و کلید `editorial.edit_window_days` در جدول `settings`).

## ۲. مکانیزم

```
انتشار (نخستین بار)
  └── articles.published_at   = now()
  └── articles.editable_until = now() + editWindowDays

هر تلاش برای ویرایش
  └── isWithinEditWindow(article)
        ├── article.is_locked === true            → مسدود
        ├── editable_until === null               → مجاز (هنوز منتشر نشده)
        └── now() > editable_until                → مسدود
```

هنگام مسدود شدن:
- پاسخ `HTTP 423 Locked` با کد `CONTENT_LOCKED`
- ثبت رویداد حسابرسی `article.edit_denied_locked` (چه کسی، چه زمانی، از چه IP تلاش کرد)

## ۳. راه‌های دور زدن که بسته شده‌اند

| مسیر حمله | نتیجه |
|---|---|
| `PATCH /api/editorial/articles/:id` روی خبر قفل‌شده | `423 CONTENT_LOCKED` |
| `DELETE` سپس ایجاد مجدد با همان نامک | حذف خبر منتشرشده مسدود است (`423`) — فقط بایگانی ممکن است |
| تغییر مستقیم `is_locked` یا `editable_until` از بدنه‌ی درخواست | این فیلدها در اسکیمای Zod ورودی وجود ندارند و نادیده گرفته می‌شوند |
| ورود با نقش `super_admin` | همان بررسی اجرا می‌شود؛ نقش تفاوتی ایجاد نمی‌کند |
| انتشار دوباره برای بازنشانی مهلت | `published_at` فقط اگر `null` باشد مقداردهی می‌شود |

### آزمون واقعی (اجراشده روی همین نسخه)

```bash
$ curl -X PATCH .../api/editorial/articles/<locked-id> -d '{"title":"..."}'
423 {"error":"CONTENT_LOCKED","message":"مهلت ۱۰ روزه ویرایش این خبر پایان یافته است. …"}

$ curl -X DELETE .../api/editorial/articles/<locked-id>
423 {"error":"CONTENT_LOCKED","message":"خبر منتشرشده حذف نمی‌شود؛ فقط قابل بایگانی یا اصلاح است."}

$ curl -X POST .../api/editorial/articles/<locked-id>/corrections \
       -d '{"reason":"تصحیح عدد نادرست در پاراگراف دوم","note":"عدد مصرف آب اصلاح شد."}'
200 → نسخه جدید + رکورد اصلاحیه ساخته شد
```

## ۴. اصلاحیه رسمی

```
POST /api/editorial/articles/:id/corrections
{
  "title":   "عنوان اصلاح‌شده (اختیاری)",
  "body":    "متن اصلاح‌شده (اختیاری)",
  "reason":  "دلیل اصلاح — الزامی، حداقل ۵ نویسه",
  "note":    "توضیح عمومی برای خواننده — الزامی"
}
```

اثرها:
1. یک ردیف در `article_corrections` با دلیل، توضیح، و شناسه‌ی ثبت‌کننده.
2. یک نسخه‌ی تازه در `article_revisions` با `change_kind = 'correction'`.
3. رکورد در `audit_logs`.
4. نمایش **بالای متن خبر** در صفحه‌ی عمومی، با کادر متمایز (نه در پاورقی، نه پنهان).

نبود `reason` یا `note` باعث خطای `422` می‌شود؛ یعنی اصلاح بی‌دلیل ممکن نیست.

## ۵. چرا این قاعده؟

اعتبار یک خبرگزاری به این وابسته است که نتواند گذشته را بی‌سروصدا بازنویسی کند.
اگر ویرایش نامحدود باشد، آرشیو ارزش استنادی ندارد. این سامانه به‌جای «اعتماد به
سیاست داخلی»، محدودیت را در پایگاه داده و API نهادینه می‌کند.
