# Agent 1 — final report (org platform) Branch: `dev`. Phases 1, 2 (previously merged) plus 3, 4 and 5 (implemented now, reconstructed from the sibling `agent1/org-platform` branch and reconciled onto `dev`'s shapes). ## Summary The org-platform workstream is complete on `dev`: a platform owner invites a manager, the manager builds one organization with one application through a five-step wizard, the owner decides which models that organization may use, the organization builds a memory of its own rules / preferences / lessons / code map, and an external AI agent can act inside it through a scoped MCP token the organization's own admin minted. Everything is tenant-scoped, audited, Persian/RTL on the UI side, and no secret reaches a response, a draft, an audit row, a log or an LLM prompt. ``` apps/api: pytest -q → 587 passed, 1 skipped apps/api: ruff check . → All checks passed! apps/api: mypy app → Success: no issues found in 147 source files apps/api: alembic upgrade head / downgrade -1 / upgrade head / check → clean, single head apps/web: tsc --noEmit → clean · next lint → No ESLint warnings or errors apps/web: vitest run → 99 passed (13 files) ``` Migrations added on `dev` (the branch's single `a1b2c3d4e5f1` was split into one owned migration per phase, chained off `dev`'s own head): | revision | phase | tables | |---|---|---| | `e6f7a8b9c0d1` | 3 | `default_model_grants`, `org_policies` | | `f7a8b9c0d1e2` | 4 | `org_memories` | | `a8b9c0d1e2f3` | 5 | `mcp_tokens` | (Phases 1 and 2 were already on `dev`: `a1b2c3d4e5f1` platform invites, `d5e6f7a8b9c0` org setup drafts.) ## Endpoints added (all under `/api`, roles as enforced by the router) ### Phase 1 — invites, roles, sharing `POST/GET /admin/platform-invites`, `POST /admin/platform-invites/{id}/revoke`, `GET /invites/{token}`, `POST /invites/accept`, `GET /auth/me` (`needs_onboarding`), `POST /organizations/{id}/transfer-ownership`, `POST /organizations/{id}/leave`, `GET/DELETE /organizations/{id}/invites[/{id}]`, and the `/admin/users` lifecycle. ### Phase 2 — wizard and app profile `GET /setup/catalog`, `GET/PUT /setup/draft`, `POST /setup/check-git`, `POST /setup/check-deploy`, `POST /setup/complete`, `GET/PATCH /organizations/{org}/app/profile`, `PUT /organizations/{org}/app/deploy-target`, `PUT /organizations/{org}/app/git`. ### Phase 3 — owner grants and policy `GET /admin/model-grants/{org}`, `PUT/DELETE /admin/model-grants/{org}/models/{model}`, `GET /admin/model-grants/{org}/active`, `GET/PUT/DELETE /admin/default-model-grants[/{model}]`, `GET/PUT /admin/org-policies/{org}`. ### Phase 4 — memory `GET /organizations/{org}/memory`, `POST /organizations/{org}/memory`, `PATCH /organizations/{org}/memory/{id}`, `POST /organizations/{org}/memory/{id}/pin`, `.../confirm`, `DELETE /organizations/{org}/memory/{id}`. ### Phase 5 — MCP `GET /organizations/{org}/mcp-tokens`, `POST .../mcp-tokens`, `DELETE .../mcp-tokens/{id}`, `GET .../mcp-tokens/settings`, `GET/PUT /admin/mcp`, `PUT /admin/mcp/organizations/{id}`, and the MCP server at `/mcp` (`python -m app.interfaces.mcp.server`, `pnpm mcp:dev`). MCP tools: `app_get_profile`, `tasks_list`, `tasks_get`, `tasks_create`, `memory_search`, `memory_write`, `knowledge_search`, `pipeline_status` (`{"available": false}` until Agent 3's tables are wired). ## Tables added (all in `db/models_org.py`) `platform_invites` (1) · `org_setup_drafts` (2) · `default_model_grants` (3) · `org_policies` (3) · `org_memories` (4) · `mcp_tokens` (5). `org_model_grants`, which the grants engine reads, is foundation-owned. ## Manual QA — the wizard 1. As the owner, `/admin/users` → «دعوت مدیر جدید» → copy the link. 2. Open it in a private window → «ساخت حساب مدیر» → set a password → land on `/onboarding`. 3. Step through the five steps; leave at step 2 and reload to confirm the draft resumes. Choose «Git پلتفرم (پیشنهادی)», skip production, create. 4. The success screen shows the demo URL; «برو به مدیر AI» → `/manager`. The DB has exactly one project with a full profile, and the Null hooks recorded one `bootstrap_team` and one `on_app_created`. 5. `/admin/model-grants`: grant a model to the new org, set its daily cap, revoke. 6. `/memory`: teach a rule, pin it, confirm a lesson, keep the page open while another tab changes memory — the list refreshes on `memory.changed`. 7. `/settings` → «یکپارچه‌سازی (MCP)»: mint a token, copy it once, revoke it. `/admin/mcp`: flip the global and per-org switches. ## CHANGE REQUESTs still open 1. **`ModelGrantPort` should expose `get_org_policy(organization_id)`** (and `.mcp_enabled`) so Agent 2 reads the policy through the port it already holds. 2. **`expire_memories()`** needs a worker hook (billing worker or a new tick in `workers/__main__.py`). 3. **`pipeline_status`** should be pointed at Agent 3's delivery/deploy tables. 4. **`@agy/contracts`** lacks `memory.changed` and the MCP event types; the web compares raw strings. 5. **Post-login redirect**: an existing manager with `needs_onboarding` still lands on `/dashboard` (`login/page.tsx` and `AppShell.tsx` are locked); `useNeedsOnboarding()` is ready to use. 6. **`build_memory_service` / `build_model_grant_service`** live in `container_org` only; re-export from `container.py` for one import surface. ## Known limitations - A reflection is not grant-checked (uses `pick_for_mode(CHEAP)` like the engine). - `expire_memories()` has no caller yet. - `pipeline_status` is a stub until Agent 3's tables are wired. - The MCP rate limiter is process-local. - `mcp_gateway_enabled` is a process-local setting mutation; a restart re-reads it from the environment.