Skip to content

Commit 394babb

Browse files
authored
Merge pull request #85 from constructive-io/devin/1776333417-membership-types-skill
docs: add constructive-membership-types skill for dynamic entity provisioning
2 parents c8223b0 + 7ffe77b commit 394babb

5 files changed

Lines changed: 645 additions & 2 deletions

File tree

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
---
2+
name: constructive-membership-types
3+
description: "Membership types and dynamic entity provisioning — how to create custom entity types (channels, departments, teams) via the ORM, CLI, or blueprint definitions. Covers the entity hierarchy, permissions per entity type, and the provisioning lifecycle."
4+
metadata:
5+
author: constructive-io
6+
version: "1.0.0"
7+
---
8+
9+
# Membership Types & Dynamic Entity Provisioning
10+
11+
Constructive has a hierarchical entity type system. Every scope of membership — app, org, channel, department, team — is a **membership type** with its own entity table, permissions, memberships, and security policies.
12+
13+
Types 1 (app) and 2 (org) are built-in. Types 3+ are **dynamic** — you define them at runtime via the ORM, CLI, or blueprint definitions.
14+
15+
Related skills:
16+
- **Blueprints:** `constructive`[blueprints.md](../constructive/references/blueprints.md) — how `constructBlueprint()` works
17+
- **Blueprint definition format:** `constructive`[blueprint-definition-format.md](../constructive/references/blueprint-definition-format.md) — table/relation/policy JSONB spec
18+
- **Safegres (security):** `constructive-safegres` — Authz* policy types for RLS
19+
- **SQL-level provisioning:** `entity-types-and-provisioning` skill in `constructive-db`
20+
21+
---
22+
23+
## Core Concepts
24+
25+
### Entity Type Hierarchy
26+
27+
| Type ID | Name | Prefix | Entity Table | Created By |
28+
|---------|------|--------|-------------|------------|
29+
| 1 | App Member | `app` | `users` | Built-in |
30+
| 2 | Organization Member | `org` | `users` (scoped) | Built-in |
31+
| 3+ | Dynamic | varies | auto-created | You provision these |
32+
33+
Every entity type gets:
34+
- An **entity table** (e.g. `channels`, `departments`)
35+
- A **permissions module** with bitmask-based permissions
36+
- A **memberships module** for tracking who belongs to what
37+
- **RLS security policies** on all tables
38+
- Optional modules: limits, profiles, levels, invites
39+
40+
### Permission Model
41+
42+
Each level has a standard set of permissions. The `create_entity` permission means **"create the next level down"**:
43+
44+
| Level | `create_entity` description | What it creates |
45+
|-------|---------------------------|-----------------|
46+
| App (type=1) | "Create organization entities." | Organizations |
47+
| Org (type=2) | "Create child entities." | Channels, departments, etc. |
48+
| Dynamic (type≥3) | "Create sub-entities." | Nested entity types |
49+
50+
Other standard permissions: `admin_members`, `create_invites`, `admin_invites`, `admin_limits`, `admin_permissions`, `admin_entity`.
51+
52+
### Parent-Child Relationships
53+
54+
Every dynamic entity type has a **parent type**. The parent defaults to `org` (type=2), but can be any previously-provisioned type:
55+
56+
```
57+
app (1)
58+
└── org (2)
59+
├── channel (3) ← parent_entity = 'org'
60+
├── department (4) ← parent_entity = 'org'
61+
│ └── team (5) ← parent_entity = 'department'
62+
└── ...
63+
```
64+
65+
Nested types must be provisioned **after** their parent type.
66+
67+
---
68+
69+
## Three Ways to Provision Entity Types
70+
71+
### 1. Blueprint Definition (Recommended)
72+
73+
Add `membership_types` to the blueprint `definition` JSONB. These are processed in **Phase 0** — before tables and relations — so blueprint tables can reference the entity tables they create.
74+
75+
See [blueprint-membership-types.md](./references/blueprint-membership-types.md) for the full spec and examples.
76+
77+
### 2. ORM / GraphQL Mutation
78+
79+
Use the `entityTypeProvision` table for direct provisioning outside of blueprints.
80+
81+
See [orm-provisioning.md](./references/orm-provisioning.md) for ORM examples.
82+
83+
### 3. CLI
84+
85+
```bash
86+
# Direct entity type provision (inserts into entity_type_provision trigger table)
87+
constructive public:entity-type-provision create \
88+
--databaseId <UUID> \
89+
--name "Channel Member" \
90+
--prefix "channel" \
91+
--description "Membership to a channel." \
92+
--parentEntity "org" \
93+
--isVisible true \
94+
--hasLimits false \
95+
--hasProfiles false \
96+
--hasLevels false \
97+
--skipEntityPolicies false
98+
```
99+
100+
---
101+
102+
## What Gets Created
103+
104+
When you provision a new entity type (e.g. prefix=`channel`), the system creates:
105+
106+
### Tables
107+
- `channels` — Entity table (with `id`, `name`, `owner_id`, `created_at`, `updated_at`)
108+
- `channel_permissions` — Permission bitmasks per member
109+
- `channel_permission_defaults` — Default permission values
110+
- `channel_limits` — Rate limits per member (if `has_limits`)
111+
- `channel_limit_defaults` — Default limit values (if `has_limits`)
112+
- `channel_members` — Member list (user_id + entity_id)
113+
- `channel_memberships` — Membership state (active, suspended, etc.)
114+
- `channel_membership_defaults` — Default membership values
115+
- `channel_grants` / `channel_admin_grants` / `channel_owner_grants` — Computed grants
116+
- `channel_acl` — Access control list
117+
118+
### Modules Registered
119+
- `permissions_module:channel`
120+
- `memberships_module:channel`
121+
- `limits_module:channel` (if `has_limits`)
122+
- `invites_module:channel` (auto-provisioned when `emails_module` exists)
123+
124+
### Optional Modules
125+
- `profiles_module:channel` (if `has_profiles`) — Named permission roles
126+
- `levels_module:channel` (if `has_levels`) — Gamification/achievements
127+
128+
---
129+
130+
## Querying Membership Types
131+
132+
### List all types
133+
134+
```typescript
135+
const types = await db.membershipType.findMany({
136+
select: {
137+
id: true,
138+
name: true,
139+
prefix: true,
140+
description: true,
141+
parentMembershipType: true,
142+
hasLimits: true,
143+
hasProfiles: true,
144+
hasLevels: true,
145+
}
146+
}).execute();
147+
// Returns: [{ id: 1, name: 'App Member', prefix: 'app', ... }, ...]
148+
```
149+
150+
### Find a specific type by prefix
151+
152+
```typescript
153+
const channelType = await db.membershipType.findMany({
154+
where: { prefix: { equalTo: 'channel' } },
155+
select: { id: true, name: true }
156+
}).execute();
157+
```
158+
159+
### CLI
160+
161+
```bash
162+
constructive public:membership-type list --select id,name,prefix,parentMembershipType
163+
constructive public:membership-type find --where.prefix channel --select id,name
164+
```
165+
166+
---
167+
168+
## Querying Membership Types Module
169+
170+
The `membershipTypesModule` tracks which databases have the membership types infrastructure installed:
171+
172+
```typescript
173+
const modules = await db.membershipTypesModule.findMany({
174+
where: { databaseId: { equalTo: dbId } },
175+
select: { id: true, tableName: true }
176+
}).execute();
177+
```
178+
179+
---
180+
181+
## Cross-References
182+
183+
- **Blueprint definition format:** [blueprint-definition-format.md](../constructive/references/blueprint-definition-format.md)`membership_types` is a top-level key alongside `tables`, `relations`, etc.
184+
- **ORM provisioning examples:** [orm-provisioning.md](./references/orm-provisioning.md)
185+
- **Blueprint membership_types spec:** [blueprint-membership-types.md](./references/blueprint-membership-types.md)
186+
- **SQL-level detail:** `entity-types-and-provisioning` skill in `constructive-db` repo

0 commit comments

Comments
 (0)