feat: add team columns to group and jct_user_group

Schema foundations for teams. Ships dark — nothing reads these columns yet.

group:          kind, name, handle, plan_id, deleted_at
jct_user_group: org_owned
user:           requires_password_change

`kind = 'team'` marks a workspace; the seeded system groups keep it NULL so a
team query can never surface them. `org_owned` distinguishes accounts the
workspace created from the master account, deciding who pays rather than who
may read. `requires_password_change` is a fourth `requires_*` flag for
assertVerifiedAccount, needed by the phase 5 reset flow but shipped here to
avoid a second three-dialect migration.

`handle` uniqueness is case-insensitive, and the dialects disagree by default:
mysql gets it from utf8mb4_unicode_ci, sqlite needs COLLATE NOCASE (as
0055 does for usernames), and postgres indexes lower(handle). Without this the
same migration would accept `Design-Team` next to `design-team` on sqlite and
postgres while mysql rejected it. Postgres handle lookups must therefore
compare lower(handle) to use the index.

idx_group_owner is sqlite-only: mysql and postgres already index that column.
idx_jct_user_group_group is composite, unlike the existing single-column keys.
This commit is contained in:
Juan Castro
2026-09-03 12:10:40 -04:00
parent 55fa5cadcb
commit 2a5c089dbe
5 changed files with 203 additions and 1 deletions
@@ -106,6 +106,7 @@ const AVAILABLE_MIGRATIONS: [number, string[]][] = [
[69, ['0074_add_user_home.sql']],
[70, ['0075_event-subscriptions.sql']],
[71, ['0076_event-handlers.sql']],
[72, ['0077_teams.sql']],
];
export class SqliteDatabaseClient extends AbstractDatabaseClient {