BEGIN; ALTER TABLE tenant ADD COLUMN policy_version varchar(64) NOT NULL DEFAULT 'local-v1'; ALTER TABLE user_account RENAME COLUMN identity_subject TO oidc_subject; ALTER TABLE user_account ADD COLUMN oidc_issuer varchar(300) NOT NULL DEFAULT 'urn:legacy:local', ADD COLUMN last_login_at timestamptz; ALTER TABLE user_account DROP CONSTRAINT user_account_tenant_id_identity_subject_key; ALTER TABLE user_account ADD CONSTRAINT user_account_tenant_oidc_identity_key UNIQUE (tenant_id, oidc_issuer, oidc_subject), ADD CONSTRAINT user_account_id_tenant_key UNIQUE (id, tenant_id); CREATE INDEX user_account_oidc_identity_idx ON user_account (oidc_issuer, oidc_subject) WHERE status = 'active'; CREATE TABLE household ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), public_id uuid NOT NULL UNIQUE DEFAULT gen_random_uuid(), tenant_id uuid NOT NULL REFERENCES tenant(id), name varchar(200) NOT NULL, owner_user_id uuid NOT NULL, status varchar(32) NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'archived', 'deleted')), version bigint NOT NULL DEFAULT 1 CHECK (version > 0), created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), deleted_at timestamptz, CONSTRAINT household_owner_same_tenant_fk FOREIGN KEY (owner_user_id, tenant_id) REFERENCES user_account (id, tenant_id), CONSTRAINT household_id_tenant_key UNIQUE (id, tenant_id) ); CREATE INDEX household_tenant_status_idx ON household (tenant_id, status); CREATE TABLE household_member ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), public_id uuid NOT NULL UNIQUE DEFAULT gen_random_uuid(), tenant_id uuid NOT NULL REFERENCES tenant(id), household_id uuid NOT NULL, user_id uuid NOT NULL, role varchar(32) NOT NULL CHECK (role IN ('owner', 'admin', 'editor', 'viewer')), status varchar(32) NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'removed')), joined_at timestamptz NOT NULL DEFAULT now(), removed_at timestamptz, version bigint NOT NULL DEFAULT 1 CHECK (version > 0), created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), CONSTRAINT household_member_household_tenant_fk FOREIGN KEY (household_id, tenant_id) REFERENCES household (id, tenant_id), CONSTRAINT household_member_user_tenant_fk FOREIGN KEY (user_id, tenant_id) REFERENCES user_account (id, tenant_id) ); CREATE UNIQUE INDEX household_member_active_user_key ON household_member (household_id, user_id) WHERE status = 'active'; CREATE INDEX household_member_tenant_user_status_idx ON household_member (tenant_id, user_id, status); CREATE INDEX household_member_household_status_idx ON household_member (household_id, status); CREATE TABLE member_invitation ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), public_id uuid NOT NULL UNIQUE DEFAULT gen_random_uuid(), tenant_id uuid NOT NULL REFERENCES tenant(id), household_id uuid NOT NULL, invitee_hint varchar(255), token_digest char(64) NOT NULL UNIQUE, role varchar(32) NOT NULL CHECK (role IN ('admin', 'editor', 'viewer')), expires_at timestamptz NOT NULL, used_at timestamptz, revoked_at timestamptz, created_by uuid NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), CONSTRAINT member_invitation_household_tenant_fk FOREIGN KEY (household_id, tenant_id) REFERENCES household (id, tenant_id), CONSTRAINT member_invitation_creator_tenant_fk FOREIGN KEY (created_by, tenant_id) REFERENCES user_account (id, tenant_id) ); CREATE INDEX member_invitation_household_expiry_idx ON member_invitation (household_id, expires_at) WHERE used_at IS NULL AND revoked_at IS NULL; COMMIT;