# w6 — Workspaces: Render workspace lifecycle parity (worker6)

**Worker:** worker6 Created 2026-07-08 from user request + deep-research report ([RESEARCH-workspaces.md](RESEARCH-workspaces.md)). w1/m9 mints the tenant substrate (one auto-created workspace per identity, OpenFGA enforced); w6 makes workspaces a real product surface: user-initiated lifecycle (create/rename/delete, multi-workspace per user, plan limits), the Render `owners` read API + MCP workspace tools, and the dashboard flows (`/new/workspace`, switcher, settings). Composes with existing authn (Kratos/Hydra), authz (OpenFGA `workspace:tea-<id>`), and the control-plane Postgres — no parallel workspace store. Ordered by dependency: model + verbs → API surface → dashboard UX.

## Milestones

- [x] **m1** — Workspace model & lifecycle verbs: create · rename · delete · plan limits (10 tasks) ← from RESEARCH-workspaces.md — done 2026-07-09 (backend shipped `b06e301`, verified vs real Postgres + OpenFGA), moved to `done/m1/`
- [x] **m2** — Render `owners` read API + MCP workspace tools (9 tasks) ← from RESEARCH-workspaces.md, needs m1; supersedes w2/002 — done, moved to `done/m2/`
- [x] **m3** — Dashboard workspace UX: `/new/workspace` flow · switcher · settings (9 tasks) ← from RESEARCH-workspaces.md, needs m1 — done 2026-07-09 (switcher/create/settings/delete shipped, verified end-to-end against the offline dev stub — no live cluster available this session, see `done/m3/README.md`), moved to `done/m3/`
- [x] **m4** — Workspace-scoped datastores: fix ownerId labeling + wire real delete purgers (10 tasks) ← from `/pm-brainstorm for w6` 2026-07-09, closing a gap m1 itself deferred ("OpenBao/Database purger concrete impls") plus a discovered Postgres label-mismatch bug and a KeyValue labeling gap — done 2026-07-10 (Postgres/KeyValue teardown + ownerId scoping live-verified against real Postgres+OpenFGA; OpenBao secrets purge implemented + unit-tested but not live-verified, see `done/m4/README.md`), moved to `done/m4/`
- [x] **m5** — Live-verify workspace dashboard UX against Render + real infrastructure (5 tasks) ← from `/pm-brainstorm for w6` ("more for w6") 2026-07-09, closing m1/t001's unmet live-capture acceptance criteria and m3's own "Follow-ups" (offline-stub-only verification, ungenerated codegen diff) — done 2026-07-11 (live Render capture → `docs/render-artifacts/workspace-lifecycle.md` revealed the `sudo delete workspace <name>` guard + Hobby-only delete; reconciled end-to-end with tests; codegen drift closed by source-diff; live-cluster _browser_ rerun blocked by a diagnosed local-cluster outage, see `done/m5/README.md`), moved to `done/m5/`
- [x] **m6** — Security review: RBAC, supply chain, injection surface, network isolation (10 tasks) ← from `/pm-brainstorm` 2026-07-11 (`GOAL.md` #7, "Security review"); placed here as the least-loaded worker (0 open milestones at brainstorm time), not for topical fit — done 2026-07-11 (`docs/ADR028-security-review.md` written; cosign + SBOM in `deploy.yml`; input validators + OAuth `iss` check added with tests; tenant-isolation matrix live-tested + CI structural guard; stale wildcard/unused RBAC deleted), six follow-up notes filed (`w6/002`–`w6/007`), moved to `done/m6/`
- [x] **m7** — User owners: opaque `own-` identity ids for member identifiers (6 tasks) ← from `/pm how to implement own- or owner feature` 2026-07-11 + design review that trimmed it — done 2026-07-11 (mint `id.Owner` + `owner_ids` table/migration 0009; members `userId` is now an opaque own- id instead of a raw Kratos subject, verified through the full REST stack + a real-Postgres round-trip; owner `type: user` and peer `own-` resolution recorded as deliberate non-goals — bex has only team workspaces, peer-resolve has no consumer), moved to `done/m7/`.
- [x] **m8** — Scope down bex-api's cluster-wide RBAC (7 tasks) ← promoted from `002` 2026-07-12 (`w6/m6`'s RBAC least-privilege audit top finding) — done 2026-07-12, shipped directly via `552130b` (ahead of its own task files); moved to `done/m8/`
- [x] **m9** — Security hygiene chores (9 tasks) ← from `/pm-brainstorm` 2026-07-12, groups `003`, `004`, `005`, `007`, `008` (each sub-hour; `008` is an urgent pre-existing test break) — done 2026-07-12, shipped directly via `552130b` (ahead of its own task files); moved to `done/m9/`
- [x] **m10** — Member identity: user{email} on the members surface (9 tasks) ← from `/pm-brainstorm for more w6` 2026-07-12 (ADR018 members-row ◐ shape divergence; reuses m7's `own-` ids + m2's Kratos-admin lookup, which the owners API already composes — the members CRUD surface + Team page never adopted it) — done 2026-07-12 (`userId`+`email` enrichment across REST/GraphQL/MCP/dashboard, verified via real-fragment tests + code-level wiring inspection — no live cluster available this session, see `done/m10/README.md`), moved to `done/m10/`
- [ ] **m11** — Live-verification sweep: close the m4/m5/m10 residuals on the restored cluster (7 tasks) ← from `/pm-brainstorm more for w6` 2026-07-12 (the "not live-verified" residuals recorded in `done/m4`, `done/m5`, `done/m10`; unblocked by the local-cluster Calico/CNPG fix)
- [ ] **m12** — Workspace plan change: upgrade · downgrade · plan-gated roles (9 tasks) ← from `/pm-brainstorm more for w6` 2026-07-12 (RESEARCH-workspaces.md findings 1/4/5; the last missing lifecycle verb — `workspaces` service has Create/Rename/Delete only)

## Inbox

_(empty)_

> `002.md` promoted to **m8**; `003.md`, `004.md`, `005.md`, `007.md`, `008.md` grouped into **m9**; `006.md` (sign CNB tenant images) also implemented directly in `552130b` as an opt-in `BEX_TENANT_SIGNING_KEY_SECRET` path — all six notes moved to `done/` 2026-07-12 (`006` earlier than its own "park until required" note anticipated; superseded by direct implementation).

## Not in w6 (deliberate)

- **Members & roles** (invite/change-role/remove, Team page) — that's `w4/m12`; w6/m1 only enforces the Hobby single-member guard w4/m12 must respect.
- **Billing/payments** — bex has no billing system; the plan field ships without a payment step (Render's flat-subscription + inactivity-waiver model is documented in RESEARCH-workspaces.md for whenever billing becomes roadmap-worthy).
- **Workspace-mutation REST endpoints** — parity means their _absence_ (research finding 9: Render's REST owners surface is read-only; lifecycle is dashboard-only).
