# Hierarchy Schema Contract (D2) Status: DRAFT — awaiting ratification (webui-audit S2, contract 1 of 9). Authority: PRD D2/D9/D13 (Part I §4) and the native-kanban SOT Amendment A1 (`docs/requirements/native-kanban-sot.md` §8, ratified 2026-08-25). This document turns the ratified hierarchy into a concrete schema contract: tables, cardinalities, constraints, and ownership/transfer semantics. It is the prerequisite for the D8 rank-1 tool (hierarchy command family) and for the RBAC grant model (contract 2). Scope: the tenancy/authorization structure record class — companies, estates, platform-projects, workspaces, their parentage, and the grant attachment points. Out of scope: the RBAC grant vocabulary and evaluation semantics (contract 2), roll-up projection semantics (contract 8), kanban planning entities inside workspaces (SOT §5), migration of legacy flat data (SOT REQ-MIG-001). ## 1. Record class and placement 1. The four tables below form the **tenancy/authorization structure record class** defined by Amendment A1 §8.1.2. They carry parentage, naming, and grant data only — never task, plan, or any business/orchestration payload. No business/orchestration row may reference a hierarchy record as a dependency, claim target, or work subject. 2. Hierarchy records are NOT workspace-scoped rows: REQ-TEN-001's `workspace_id` obligation binds business/orchestration rows and does not apply to this class (A1 §8.1.2). The `workspaces` table itself is the anchor the obligation points at. 3. The legacy flat tables (`teams`, `projects` in `packages/db/src/schema.ts` — the Brain planning table, not the hierarchy level) are not part of this class and are not repurposed. The SOT forbids reusing the flat model for the hierarchy; their eventual migration or retirement is REQ-MIG-001 work, out of scope here. ## 2. Tables and cardinalities Naming: the level above workspaces is `platform_projects`, per A1 §8.1.4 — the existing `projects` table already means workspace-internal planning entities, and the schema MUST NOT merge the two. (A rename of either remains an implementation-PR decision under A1; this contract pins only that they stay distinct tables.) 1. `companies` — id (uuid pk), name, slug (unique per deployment), created_at, updated_at. N per deployment (D2). 2. `estates` — id, name, slug, `company_id` NOT NULL → `companies.id` ON DELETE RESTRICT. Exactly one company per estate. 3. `platform_projects` — id, name, slug, `estate_id` NOT NULL → `estates.id` ON DELETE RESTRICT. Exactly one estate per platform-project. 4. `workspaces` — id, name, slug, `platform_project_id` NOT NULL → `platform_projects.id` ON DELETE RESTRICT. Exactly one platform-project per workspace. This table is the referent of every `workspace_id` column the SOT requires on canonical rows. 5. **Chain resolution is by construction.** Because every parent FK is NOT NULL and single-valued, each workspace resolves to exactly one platform-project → estate → company chain (A1 §8.3 acceptance 1). There are no parentage edge tables, no multi-parent forms, and no nullable "detached" states. 6. **Slug scoping.** `estates.slug` is unique within its company, `platform_projects.slug` within its estate, `workspaces.slug` within its platform-project (composite unique constraints). Display names are unconstrained. 7. No hierarchy table carries a `metadata` jsonb column or any free-form payload field. Parentage, naming, timestamps, and the audit linkage of §5 — nothing else (A1 §8.1.2). ## 3. Grant attachment points The grant vocabulary (which roles exist, what each permits, how inheritance and revocation evaluate) is contract 2. This contract pins only the schema shape contract 2 attaches to: 1. `hierarchy_grants` — id, subject (exactly one of `user_id` → `users.id`, `team_id` → `teams.id`; CHECK-enforced exactly-one-of), target (exactly one of `company_id`, `estate_id`, `platform_project_id`; CHECK-enforced exactly-one-of), `role` (text; vocabulary owned by contract 2), granted_by → `users.id`, created_at. 2. Uniqueness: at most one grant row per (subject, target, role). 3. Deleting a hierarchy record deletes its grants (the one permitted cascade in this class — a grant on a deleted node is meaningless and fail-open if retained). Deleting a user or team follows the platform rule for principal deletion (identity contract §7; deletion is currently gated). 4. Workspace-level access is evaluated, not stored here: a grant at any of the three levels evaluates down the chain to workspace-scoped authorization (A1 §8.1.3). No `workspace_id` column exists on `hierarchy_grants` — workspace membership (REQ-ID-001) remains its own mechanism inside the SOT schema, and the chain adds where grants can be declared, never a bypass. ## 4. Ownership and transfer "Assets are transferable subject to the structure" (PRD Part I §4): 1. A transfer is an audited UPDATE of exactly one parent FK on exactly one hierarchy row: workspace → new platform-project, platform-project → new estate, estate → new company. Nothing else changes: business and orchestration rows inside affected workspaces are untouched, keep their `workspace_id`, and never cross a workspace boundary (A1 §8.1.3 "chain maintenance"). 2. Transfer authorization requires authority over BOTH the source and the destination parent (the vocabulary for "authority" is contract 2; the both-sides requirement is structural and binds here). 3. A transfer is a single-row, single-statement mutation; there are no multi-row transfer batches at the schema level. Bulk moves are N audited transfers. 4. Hierarchy records have no `owner_id`. Ownership in the hierarchy IS the grant structure (§3) — a "company owner" is a subject with the top role on that company, not a column. This avoids the dual-write failure mode of the legacy `teams.owner_id`/`teams.manager_id` columns, which encode authority outside any evaluable grant model. ## 5. Mutation path, audit, and deletion 1. All hierarchy mutations flow through the same sole-writable-SOT, fail-closed, audited Gateway command path as everything else (A1 §8.2.3, REQ-API-001). No direct-DB writers, no raw CRUD endpoints. 2. Every create, rename, transfer, grant change, and delete emits a semantic audit event carrying actor, verb, target record, and (for transfers) source and destination parents. Hierarchy audit events reference hierarchy records; they are not workspace-scoped rows and do not ride the workspace outbox (REQ-AUD-001 binds business rows; the implementing PR defines the hierarchy audit store under the same append-only rules). 3. Deletion is fail-closed bottom-up: a hierarchy record with children cannot be deleted (RESTRICT FKs, §2). Deleting a workspace is a SOT-side operation subject to the kanban SOT's own rules and is not granted any new semantics by this contract. 4. Roll-up reads (A1 §8.1.3) touch none of these tables' write paths and are specified by contract 8; this contract only guarantees the chain they aggregate over is unique and non-null. ## 6. Verification requirements Binding on the implementing PRs (extends A1 §8.3): 1. Schema tests: chain uniqueness by construction (insert attempts with null/duplicate parents fail); composite slug uniqueness per parent; CHECK-enforced exactly-one-of on grant subject and target; grant uniqueness per (subject, target, role). 2. Transfer tests: parent-FK update moves the subtree resolution and modifies zero business/orchestration rows (row-count and content assertions on workspace contents before/after); transfer without authority on either side is refused. 3. Deletion tests: delete with children refused at the database level; delete of a leaf cascades its grants and nothing else. 4. Authorization tests (with contract 2): a grant at each level yields exactly the workspace permissions the chain implies; revocation up the chain propagates (A1 §8.3 acceptance 2). 5. Negative tests: no business/orchestration table accepts a hierarchy record id in any dependency/reference position; roll-up endpoints cannot mutate hierarchy state; readers see aggregates only over workspaces they are authorized on, with no cross-tenant existence oracles (A1 §8.3 acceptance 3). 6. Real-PostgreSQL coverage for the constraint-witness tests (unique/CHECK/ RESTRICT behavior), using the `ci-postgres` service in the `test` CI step; mocked specs cannot witness database constraints. ## Ruling request Ratify sections 1–6 as written, with one decision embedded: hierarchy records carry no owner column — ownership is expressed solely through grants (§4.4) — say "agreed" or name the ownership model you want.