A push to `dev` now becomes a live demo, and a failed build never takes the
demo down.
- Four tables (pipeline_runs, pipeline_steps, releases, app_runtimes) and their
migration; alembic upgrade/downgrade/check all clean.
- DeliveryService implements DeliveryHooks: on_app_created provisions the demo
port + host + first build (idempotently); on_branch_pushed queues a run for
`dev` and supersedes older *queued* runs, leaving a running one alone.
- The 8-step pipeline. Blue/green is real: the replacement starts on a staging
port, is healthchecked, and only then does the route move — then the previous
container is stopped. A failure removes the replacement, releases its port and
leaves the old container serving.
- The demo port ledger is the DATABASE, not the router adapters' in-memory maps:
the API and the delivery worker are separate processes.
- Runs are claimed with a conditional UPDATE, so two workers never build the
same run.
- Live logs are batched (1 event / 500 ms, 200 lines) and redacted as a whole
tail — per-chunk redaction misses a secret split across pipe reads, and the
*stored* tail leaked until this was fixed.
- delivery worker: drain the queue, then poll remotes for pushes made outside
the engine.
- 7 routes; reads need membership, actions need projects.manage and are audited.
Tests: 415 API (+21, 1 docker-marked skipped); ruff, mypy, alembic check clean.
Report: docs/plan/reports/agent-3-phase-3.md
- users.is_platform_owner → users.platform_role (owner|manager|user) با مهاجرت
d4e5f6a7b8c9 و backfill. ایندکس یکتای جزئی uq_single_platform_owner تضمین
میکند دقیقاً یک مالک وجود داشته باشد — در سطح دیتابیس، نه فقط در کد.
- ثبتنام عمومی حذف شد (POST /auth/signup). حسابها فقط با POST /admin/users
(فقط مالک، با رمز موقت یکبارمصرف) یا با پذیرش لینک دعوت ساخته میشوند.
در مسیر دعوت، اگر حساب وجود نداشته باشد خودِ دعوتشده رمز تعیین میکند؛ اگر
وجود داشته باشد لینک هرگز جای reset رمز را نمیگیرد.
- ساخت سازمان فقط برای مالک و مدیران؛ کاربر عادی با دعوت عضو میشود.
- تنزل یا حذف مدیر، سازمانهایش را به مالک سامانه منتقل میکند (مالکیت + عضویت،
با audit). حساب مالک از API تغییرناپذیر است؛ جابهجایی صندلی فقط با
POST /admin/transfer-ownership و در یک تراکنش انجام میشود.
- افزودن POST /auth/change-password برای تبدیل رمز موقت به رمز خصوصی.
- فرانت: صفحهٔ /admin/users، صفحهٔ /account، بازنویسی /accept-invite، حذف /signup،
گارد /onboarding برای کاربر عادی، و نشان نقش پلتفرم در پوستهٔ اپ.
- PRAGMA foreign_keys=ON برای SQLite تا ON DELETE CASCADE در dev و prod یکسان
رفتار کند (پیشتر حذف کاربر، عضویتهایش را یتیم میگذاشت).
- تست: ۳۰۰ تست بکاند (شامل tests/test_platform_hierarchy.py با ۲۲ تست) و ۶۴ تست
فرانتاند؛ ruff، mypy، tsc، eslint و build تمیز.
- حذف کامل 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 مسیریابی میشود
مخزن اختصاصی برنامهٔ وب؛ محتوای پوشهٔ web در ریشهٔ مخزن قرار گرفت.
تاریخچهٔ پروژهٔ ربات (agy-bot) در این مخزن وجود ندارد و هیچ وابستگی runtime به آن نیست.