Staging org linked to parent
Epic 6714. Child C11 of the architecture. Covers PRD ask F7 and its S.1 gate. `depends_on` the SSO child only to serialize schema migrations.
## Original requirement (epic body, verbatim)
"Missing features: ... staging org ..." Done means: "Every feature above runs." Decision D-F7: one staging org per parent, sharing the parent's plan with no separate billing.
## Acceptance criteria (PRD, verbatim)
**Environment:** dev = `dev.jaah.ai` (browser face `https://app.dev.jaah.ai`); browser user flows; unit tests required but never sufficient; ON by default on dev; the verifying org is on Enterprise.
- **F7.1** An admin of an org on a plan with the staging-org switch clicks "Create staging org". A linked org is created with the parent's plan and switches, no separate billing, and the parent's admins as admins. Members switch between the two from the org switcher. Deleting the parent's plan entitlement removes the staging org's entitlement. At most one staging org per parent (D-F7).
- **S.1** Each of F1–F8 has its own plan switch, per-org overridable, default OFF. With the switch off, the feature's settings show an upgrade prompt and its APIs return 403 naming the plan feature.
- **Quality** Each new setting appears on the page where peers live. Labels follow `docs/standards/ui-copy-standards.md`. Nothing truncated or overflowing. Every refusal names what to do next. A cramped, unlabeled or raw-key UI fails.
## Approach (architecture C11, settled)
- Schema: `orgs.parent_org_id uuid NULL REFERENCES orgs(id)` + partial unique index (one staging org per parent).
- Create: `POST /api/org/staging` (owner/admin, `require_switch(..., "staging_org_enabled")`) → new org "<Parent> (staging)", `plan_key` copied, parent's owners/admins added as owners/admins, no Stripe customer.
- Terms: add `plan_terms.terms_org(session, org) -> Org` (pinned seam S4) at the top of `effective`/`switches_on`/`caps_of` so a staging org resolves through its parent (parent's plan row + parent's overrides).
- Billing: `DispatchBudget` (`org_limits.py`) keys its ledger by `billing_org_id = parent_org_id or id` (shared agent pool). In-service: `pool_state`'s clause also requires the parent's `staging_org_enabled` (losing it stops staging dispatch; data kept).
- Rollover: add `AND orgs.parent_org_id IS NULL` to `plan_rollover`'s select (a staging org follows its parent, never rolls over on its own).
- Switcher shows "Staging · <Parent>" (`OrgSwitcherList.tsx`); the parent's billing page lists staging usage under the parent. `PlanUpgradeNotice` when the switch is off.
- One expand-only migration; re-point `down_revision` at rebase.
Credentials & reach:
- Console oracles on dev: `jaah/debug/main/operator-login` on `app.dev.jaah.ai` — verified.
Out of scope: Do NOT build multi-org (N4). No separate Stripe customer or billing for the staging org. Do NOT allow more than one staging org per parent. Do NOT touch the marketing site or `docs/compliance/public/` (N2).
Done means:
- F7.1 → verify: on dev, create one and observe it in the switcher labelled as staging of the parent. Launch a task there and observe it is not billed separately on the parent's billing page (usage listed under the parent, and it counts against the parent's agent limit). Try to create a second and observe the refusal naming the one-per-parent rule. Remove the parent's switch/entitlement (override off) and observe staging dispatch stops while its data remains.
- Unit tests: staging terms resolve through the parent; rollover skips staging orgs; shared dispatch budget.
- S.1 → verify: on dev, view the staging-org control on a Business org (upgrade prompt; API 403 naming "Staging org") and on an Enterprise org (available).
- Quality → verify: the switcher entry and the create control at desktop and phone width: readable labels, the staging relationship clear, nothing truncated, no raw keys.
Outcome
Staging org shipped: linked org on parent's plan/bill, shared agent cap, switch-gated (403), one per parent (409), switcher + Staging tab. Quick gate green; PR #6148 queued in merge lane. Dev oracles pending deploy; follow-ups draft 7689.
Artifacts
Activity
- Draft
- Ready
- Runner started
- Runner ended (done)