Skip to content

feat(db): add workspace-scoped department foundation - #86

Open
Umoren wants to merge 2 commits into
mainfrom
feat/42-department-foundation
Open

feat(db): add workspace-scoped department foundation#86
Umoren wants to merge 2 commits into
mainfrom
feat/42-department-foundation

Conversation

@Umoren

@Umoren Umoren commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Part of #42.

This PR adds the database foundation for departments inside one Garden workspace. It adds department records, department membership, four department roles, workspace-isolation constraints, shared validation schemas, and verification tests.

The main system effect is that future department APIs can rely on PostgreSQL to reject cross-workspace membership and duplicate membership. This PR does not add those APIs or any user interface.

Scope

  • In scope:
    • Add workspace-scoped department and department_member tables.
    • Add viewer, member, lead, and admin department roles.
    • Enforce workspace isolation with composite foreign keys.
    • Enforce unique slugs per workspace and unique department membership.
    • Add strict shared Zod schemas for department and membership payloads.
    • Add database integration tests and validation tests.
  • Out of scope:
    • Department management API routes.
    • Department settings or other user interface work.
    • Assignment of issues, agents, documents, or connectors to departments.
    • Department-based visibility rules.
    • Billing, budgets, chat mentions, CSV export, and WorkStream integration.

Invariants

  • Canonical identity is defined where records/events are involved. Departments use a UUID primary key. A department slug is unique within its workspace. Membership identity is the workspace, department, and member tuple.
  • Re-running the operation does not duplicate effects. Database uniqueness rejects duplicate department slugs and duplicate memberships.
  • Partial failure cannot silently corrupt state. Each relationship insert is one database statement, and foreign keys reject invalid workspace relationships.
  • Public outputs do not expose private or internal data. This PR adds no public API output.

Design

Garden stores workspaces in the organization table. Each department belongs to one workspace. A department member points to an existing Better Auth workspace member.

erDiagram
    ORGANIZATION_WORKSPACE ||--o{ DEPARTMENT : contains
    ORGANIZATION_WORKSPACE ||--o{ MEMBER : contains
    DEPARTMENT ||--o{ DEPARTMENT_MEMBER : has
    MEMBER ||--o{ DEPARTMENT_MEMBER : joins
Loading

department_member stores workspace_id with both relationship identifiers. Two composite foreign keys then enforce the tenant boundary:

(workspace_id, department_id) -> department(workspace_id, id)
(workspace_id, member_id)     -> member(organization_id, id)

This design makes PostgreSQL the source of truth for workspace isolation. A caller cannot add a member from workspace B to a department in workspace A, even if application code misses that check.

The validation layer uses drizzle-zod so request shapes stay aligned with the Drizzle schema. Department updates accept only name and slug. They cannot change id or move a department to another workspace. Membership updates accept only a supported role.

The migration was generated from the Drizzle schema. The generated statements were reordered so PostgreSQL creates the composite unique indexes before the foreign keys that reference them.

Duplicate and idempotency review

  • Stable idempotency/dedupe key:
    • Department: (workspace_id, slug)
    • Membership: (workspace_id, department_id, member_id)
  • Persisted-data duplicate check: PostgreSQL unique indexes reject existing duplicates.
  • Same-batch duplicate check: The same unique indexes reject duplicates in one transaction or batch.
  • Retry behavior: A repeated insert returns a unique-constraint conflict and cannot create a second row.
  • Conflict behavior: Callers must handle the database conflict. This PR does not add an API error mapping.

Privacy and security review

  • Data classification: Internal workspace structure and membership metadata.
  • Public fields explicitly allowlisted: Not applicable. This PR adds no public output.
  • Private links/identifiers suppressed: Not applicable. This PR adds no response serializer.
  • Secrets and permissions reviewed: No secrets or permission grants changed. Composite foreign keys enforce the workspace boundary at the database layer.

Data, migration, and rollback

  • Schema/data changes: Adds department and department_member, their constraints and indexes, plus one composite unique index on member.
  • Backup completed or not applicable: Not applicable for local verification. The migration adds empty tables and does not rewrite existing rows.
  • Dry-run/preview result: drizzle-kit check passed.
  • Expected affected-record count: Zero existing records. The migration creates schema objects only.
  • Rollback procedure: Create a reverse migration that removes the new foreign keys, indexes, and tables. Check that no department data must be retained before rollback.

Verification

  • Syntax/build/compile
  • Formatting/lint
  • Type checks
  • Unit tests
  • Integration tests
  • Repeat-run/idempotency test
  • Malformed-input test
  • Partial-failure test: Not applicable because this PR adds no multi-step write operation.
  • Privacy-output test: Not applicable because this PR adds no public output.
  • Before/after reconciliation: Not applicable because this migration does not transform existing data.

Commands and actual results:

pnpm --filter @garden/db exec vitest run \
  'src/__tests__/department-validation.test.ts'
Test Files  1 passed (1)
Tests       5 passed (5)

pnpm --filter @garden/db exec vitest run \
  'src/__tests__/departments.test.ts'
Test Files  1 passed (1)
Tests       6 passed (6)

pnpm --filter @garden/db exec vitest run --maxWorkers=1
Test Files  4 passed (4)
Tests       16 passed (16)

pnpm --filter @garden/db typecheck
Passed

pnpm --filter @garden/db db:check
Everything's fine

pnpm --filter @garden/db exec oxlint -c ../../.oxlintrc.json \
  src/validation/index.ts src/__tests__/department-validation.test.ts
Found 0 warnings and 0 errors

git diff --check origin/main...HEAD
Passed

The first full test run started three PostgreSQL containers in parallel. All three timed out while waiting for their readiness log. No test assertion failed. The focused department suite passed, and the full suite then passed with --maxWorkers=1.

Operations

  • Configuration changes: None.
  • Deployment/restart steps: Apply migration 0045_department_foundation through the normal database migration process.
  • Health checks: Confirm the migration completes and the two new tables exist.
  • Monitoring/alerts: No new runtime monitoring in this PR.
  • Post-deploy verification: Confirm valid membership inserts succeed and PostgreSQL rejects a cross-workspace membership in a non-production verification environment.

Agent declaration

  • I read the applicable AGENTS.md instructions.
  • I did not perform unapproved destructive production actions.
  • I have clearly stated any checks I could not run.
  • The patch does not include unrelated refactoring.

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Caution

Review failed

An error occurred during the review process. Please try again later.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Umoren
Umoren requested a review from Konan69 August 17, 2026 15:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant