لینک دعوت هم کلیکپذیر نبود و هم کار نمیکرد؛ چهار نقص همزمان:
- `invite_url` نسبی بود (`/accept-invite?token=…`) و host نداشت → حالا از
`settings.app_url` آدرس مطلق ساخته میشود.
- صفحهٔ `/accept-invite` در اپ Next وجود نداشت → ۴۰۴.
- هیچ اندپوینت پذیرش دعوت در بکاند نبود؛ `InviteRepository.mark_accepted`
نوشته شده بود ولی هرگز صدا زده نمیشد، پس توکن مصرف نمیشد.
- در تنظیمات، لینک بهصورت متن ساده در `<p>` رندر میشد.
بکاند:
- روتر `app/interfaces/http/routers/invites.py`:
`GET /api/invites/{token}` (پیشنمایش) و `POST /api/invites/accept`.
عمداً زیر `/organizations/{org_id}` نیست: دعوتشونده هنوز عضو نیست و
`get_org_context` ردش میکند، پس خود توکن مجوز است. accept ایدمپوتنت است.
- `AuditAction.MEMBER_JOIN = "member.join"`.
- رفع باگ نهفته: `Invite.is_expired()` بین `expires_at` خواندهشده از SQLite
(naive) و `utcnow()` (aware) خطای مقایسه میداد. طبق قرارداد موجود پروژه
(`_aware` در `adapters/agy.py`)، در `repositories/identity.py` تابع `_aware`
اضافه و روی `expires_at`/`accepted_at` اعمال شد.
فرانت:
- صفحهٔ `app/accept-invite/page.tsx` با پیشنمایش سازمان/نقش و پذیرش؛ پس از
پذیرش `setActiveOrganization` میکند تا در همان سازمان لند کند.
- قراردادهای `InvitePreview`/`AcceptInviteResult` و کلاینت
`previewInvite`/`acceptInvite`.
- `login`/`signup` پارامتر `?next=` را میفهمند (فقط same-origin) تا توکن دعوت
در مسیر احراز هویت گم نشود.
- `settings`: لینک به `<a>` واقعی + دکمهٔ «کپی لینک».
تست: `tests/test_invites.py` (۶) و `src/test/accept-invite.test.tsx` (۵).
بکاند ۲۵۲ سبز + ruff/mypy تمیز؛ فرانت ۶۱ سبز + tsc/eslint تمیز.
- حذف کامل providerهای mock و openai_compatible: دامنه، آداپوترها، رجیستری، کانفیگ، seed و رابطٔ مدیریت
- بخش «ارائهدهندهها» از کنسول حذف شد؛ ورود اکانتها فقط از صفحهٔ اکانتهای agy
- صفحهٔ مدلها بازطراحی شد: ارائهدهنده → اکانت → مدل (کاتالوگ لحظهای و زندهٔ همان اکانت از Google) → افورت استدلال؛ بدون هیچ ورود دستی نام مدل
- مدلها ستون reasoning_effort گرفتند؛ آداپتور آن را به thinkingConfig (بودجهٔ ۱۰۲۴ تا ۳۲۷۶۸) ترجمه میکند و فقط به مدلهای استدلالی میفرستد
- مهاجرت c2d3e4f5b6a7: افزودن reasoning_effort و حذف ردیفهای providerهای قدیمی
- تستها: ۲۴۶ تست بکاند و ۵۱ تست فرانت سبز؛ ruff و mypy و tsc و eslint تمیز
- Adapter اکانتمحور با چرخش round-robin و بنچشدن خودکار روی ۴۲۹/۴۰۱
- ورود ادمین از طریق URL بازگشتی گوگل (PKCE) و ذخیرهٔ رمزنگاریشدهٔ refresh token
- صفحهٔ مدیریت اکانتهای agy در پنل ادمین به همراه تست و فعال/غیرفعالسازی
- مهاجرت Alembic b1c2d3e4f5a6 برای ستونهای اکانت agy
- seed همیشه کاتالوگ agy را میسازد و فقط با AI_DEFAULT_PROVIDER=agy مسیریابی میشود
واژگان مشترک حرکت در globals.css: شش @keyframes و شش کلاس .anim-*
بههمراه .anim-stagger برای ورود آبشاری. همهٔ مدتزمانها پشت custom
property هستند تا یک override کل اپ را تنظیم کند.
- packages/ui: Button، Card، Stat، Loading/Empty/ErrorState، UsageBar،
Timeline، Modal، Drawer، Table، ToastStack و ArtifactViewer
- ریشهٔ هر صفحهٔ (app) با .anim-stagger وارد میشود
- AppShell: محتوا fade، منوی کنار stagger، منوهای کشویی slide-up
- prefers-reduced-motion مدتزمانها را به ۰.۰۱ms میرساند تا
وضعیت پایانی حفظ شود
- قرارداد motion در src/test/motion.test.tsx تست میشود تا
بازنویسی بعدی آن را بیصدا حذف نکند
- تستهای فرانتاند: ۳۵ ← ۴۵
نام نمایشی و هویت اپلیکیشن در همهٔ سطوح کاربر-پسند از «AGY Web» به
«Smart Organization» تغییر کرد:
- UI: عنوان تب مرورگر، صفحهٔ ورود، برند و لوگوی پوستهٔ اپ (AG → SO)
- بکاند: مقدار پیشفرض app_name، نام و توضیح توزیع پایتون
- مانیفستها: package.json ریشه و چهار پکیج، pyproject.toml
- مستندات: README، PLAN، TODO و دو سند docs
شناسههای داخلی عمداً دستنخورده ماندهاند: scope پکیجهای @agy/*،
کلیدهای localStorage، نامهای logger، نام فایل پایگاهٔ داده، و اطلاعات
اتصال پایگاه داده در .env.example و infra/docker/docker-compose.yml.
مخزن اختصاصی برنامهٔ وب؛ محتوای پوشهٔ web در ریشهٔ مخزن قرار گرفت.
تاریخچهٔ پروژهٔ ربات (agy-bot) در این مخزن وجود ندارد و هیچ وابستگی runtime به آن نیست.