feat(hierarchy): M4-1b-ii hierarchy command family, grant evaluation, visibility (#1465)
ci/woodpecker/push/publish Pipeline was canceled

This commit was merged in pull request #1465.
This commit is contained in:
2026-08-29 16:54:39 +00:00
parent 5125fe21b0
commit 215faeda0a
20 changed files with 8407 additions and 132 deletions
+27 -9
View File
@@ -1063,13 +1063,24 @@ export const federationEnrollmentTokens = pgTable('federation_enrollment_tokens'
// command family only (§5.1), enforced by the writer-coverage assertion
// (§6.3b) — do not add writers outside that allowlist.
export const companies = pgTable('companies', {
id: uuid('id').primaryKey().defaultRandom(),
name: text('name').notNull(),
slug: text('slug').notNull().unique(),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
});
/** Company visibility classes (contract 1 §2.8, Ruling 4b): 'private' is the
* only creatable class (§5.5 — creation carries no visibility argument);
* 'directory' discloses existence/name/slug to all users and is entered only
* through the admin-gated visibility-change command. */
export const COMPANY_VISIBILITY = ['private', 'directory'] as const;
export const companies = pgTable(
'companies',
{
id: uuid('id').primaryKey().defaultRandom(),
name: text('name').notNull(),
slug: text('slug').notNull().unique(),
visibility: text('visibility').notNull().default('private'),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
},
() => [check('companies_visibility_check', sql`visibility IN ('private', 'directory')`)],
);
export const estates = pgTable(
'estates',
@@ -1110,6 +1121,10 @@ export const workspaces = pgTable(
(t) => [unique('workspaces_platform_project_slug_uniq').on(t.platformProjectId, t.slug)],
);
/** Grant role vocabulary (contract 2 §2): totally ordered, viewer ⊂ member ⊂
* owner. Order in this tuple IS the ordering — index = strength. */
export const HIERARCHY_GRANT_ROLES = ['viewer', 'member', 'owner'] as const;
export const hierarchyGrants = pgTable(
'hierarchy_grants',
{
@@ -1126,7 +1141,8 @@ export const hierarchyGrants = pgTable(
platformProjectId: uuid('platform_project_id').references(() => platformProjects.id, {
onDelete: 'cascade',
}),
// Role vocabulary and its CHECK constraint are contract 2 §2 (M4-2).
// Role vocabulary per contract 2 §2: exactly viewer ⊂ member ⊂ owner,
// totally ordered; CHECK below closes the column to that vocabulary.
role: text('role').notNull(),
grantedBy: text('granted_by')
.notNull()
@@ -1134,6 +1150,7 @@ export const hierarchyGrants = pgTable(
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
},
(t) => [
check('hierarchy_grants_role_check', sql`role IN ('viewer', 'member', 'owner')`),
check('hierarchy_grants_subject_check', sql`num_nonnulls(user_id, team_id) = 1`),
check(
'hierarchy_grants_target_check',
@@ -1170,6 +1187,7 @@ export const HIERARCHY_AUDIT_VERBS = [
'create',
'rename',
'transfer',
'visibility_change',
'delete',
'grant_create',
'grant_change',
@@ -1220,7 +1238,7 @@ export const hierarchyAuditEvents = pgTable(
index('hierarchy_audit_events_correlation_idx').on(t.correlationId),
check(
'hierarchy_audit_events_verb_check',
sql`verb IN ('create', 'rename', 'transfer', 'delete', 'grant_create', 'grant_change', 'grant_revoke')`,
sql`verb IN ('create', 'rename', 'transfer', 'visibility_change', 'delete', 'grant_create', 'grant_change', 'grant_revoke')`,
),
check(
'hierarchy_audit_events_target_kind_check',