# Agent 1 · Phase 2 — Org creation wizard + app profile (v2 A1-2) **Status:** done. Backend + frontend + tests all green. **Branch:** `dev` (Gitea auto-deploys `dev`). **Checks:** `ruff` clean on new `.py`; `test_org_wizard.py` 16/16 pass; web `tsc --noEmit` clean; `next lint` clean; `org-wizard.test.tsx` 6/6 pass; `org-sharing.test.tsx` 5/5 pass. --- ## Endpoints added (method + path + role) | Method | Path | Who | |---|---|---| | GET | `/api/setup/catalog` | any authenticated user | | GET | `/api/setup/draft` | any authenticated user (row is user-scoped) | | PUT | `/api/setup/draft` | any authenticated user (204, no secrets persisted) | | POST | `/api/setup/check-git` | any authenticated user (URL format + SSRF check server-side) | | POST | `/api/setup/check-deploy` | any authenticated user (TOFU fingerprint on success) | | POST | `/api/setup/complete` | any authenticated user; **403** for role `user`, **409** if the caller already owns an app | | GET | `/api/organizations/{org}/app/profile` | org member | | PATCH | `/api/organizations/{org}/app/profile` | `CAN_MANAGE_PROJECTS` (owner/admin/manager) | | PUT | `/api/organizations/{org}/app/deploy-target` | `CAN_MANAGE_PROJECTS` | | PUT | `/api/organizations/{org}/app/git` | `CAN_MANAGE_PROJECTS` (credential rotation only — git source is fixed after creation) | Audit action `organization.wizard_complete` (`ORG_WIZARD_COMPLETE`) is written on complete. ## Tables / migrations - New `org_setup_drafts` table via migration `d5e6f7a8b9c0_org_setup_drafts.py` (`down_revision="a1b2c3d4e5f1"`, the Phase 1 head). Columns: `id, user_id (FK users CASCADE), step, data_json, created_at, updated_at`, index `ix_org_setup_drafts_user_id`. - App-profile columns on `projects` (`app_type, language, framework, runtime_version, package_manager, setup/build/test/start_command, app_port, healthcheck_path, git_source, external_repo_url, wizard_completed_at`) — added in the v2 foundation (TODO-0), not this phase. - `UNIQUE(organization_id)` on `projects` enforces 1 org = 1 app; the service returns a friendly **409** Persian message (`این سازمان قبلاً یک اپلیکیشن دارد.`) instead of a raw constraint error. ## Files Backend - `app/domain/projects/catalog.py` — pure-Python catalog (2.1): `APP_TYPES`, `GIT_SOURCES`, `LANGUAGES` → frameworks → `ProfileDefaults`; `defaults_for(...)`. - `app/infrastructure/db/models_org.py` — `OrgSetupDraftRow`. - `alembic/versions/d5e6f7a8b9c0_org_setup_drafts.py` — migration. - `app/application/services/org_setup_service.py` — `OrgSetupService` (`catalog, get_draft, save_draft, check_git, check_deploy, complete, get_profile, update_profile, update_deploy_target, update_git_credentials`) + `WizardPayload`. - `app/interfaces/http/routers/app_setup.py` — all routes above (was a stub, pre-registered in `rpc_app.py`). - `app/domain/audit/models.py` — `ORG_WIZARD_COMPLETE`. - `tests/test_org_wizard.py` — 16 tests. Frontend - `packages/contracts/src/index.ts` — `CatalogPayload`, `CatalogLanguage/Framework/Defaults`, `WizardCompleteResult`, `AppProfile`, `DeployTargetSummary`, and request bodies (`SetupDraft`, `CheckGitBody`, `CheckDeployBody`, `WizardCompleteBody`, `DeployTargetPutBody`, `GitCredentialsPutBody`, `AppProfileUpdate`). - `apps/web/src/lib/api-org.ts` — `setupApi` (wizard) + `appProfileApi` (org-scoped profile/deploy/git). - `apps/web/src/app/onboarding/page.tsx` — rebuilt as a 5-step wizard (stepper, back/next, draft auto-save + resume, secrets stripped from the draft). - `apps/web/src/app/(app)/settings/page.tsx` — new "اپلیکیشن" tab (`AppTab`): profile editing, git credential rotation, deploy-target edit + test. - `apps/web/src/test/org-wizard.test.tsx` — 6 tests (catalog load, slug auto-suggest, step navigation, **secrets never in draft payload**, manager-with-org redirect, plain-user waiting screen). ## Contract consumption (frozen — unchanged) - `RepoWorkspacePort.test_remote` (Agent 3) — external git check. - `DeployTargetTester.test_connection` (Agent 3) — production deploy check. - `WorkforceBootstrapPort.bootstrap_team` (Agent 2) — called after commit. - `DeliveryHooks.on_app_created` (Agent 3) — called after commit; each hook called once, failure logged, never rolls back the org. ## Manual QA steps 1. As the owner, invite a manager (Phase 1 flow); log in as that manager → redirected to `/onboarding`. 2. Step through: org name/slug → app type/language/framework (advanced accordion prefilled) → git (platform or own repo, "بررسی اتصال") → optional production deploy ("تست اتصال") → review → create. 3. Success screen → "برو به مدیر AI" → `/manager`. `GET /api/auth/me` now returns `needs_onboarding: false`; exactly one `projects` row exists with a full profile. 4. In the new org, open Settings → "اپلیکیشن": edit commands/port/healthcheck, rotate git credentials, set + test a deploy target. Secrets never appear in responses (only `has_secret` / masked values). 5. Confirm a second `POST /setup/complete` returns **409**. ## Known limitations / open items - No `GET` for the deploy target exists yet (the service `update_deploy_target` only writes). The settings tab therefore shows post-save feedback from the `PUT` response (`has_secret`, `last_test_ok`, `last_test_message`) rather than a pre-filled form. A read endpoint can be added later without touching the contract. - Default model grants (Phase 3) are **not** copied on `complete` yet — `complete` passes `deploy_target`/git through; grant copy lands in Phase 3.3. - CHANGE REQUESTs: none blocking this phase.