ThatAIguyA world of ideas

THE CONSCIOUSNESS ATLAS
Read. Experiment. Follow the evidence.

ConsciousnessLearn · Explore · Joseph Anady

Janady13/missionmatch / db/migrations/0088_teams_and_record_scope.sql

Source identity, revision, and recovered contents.

GitHub sourceSource text recovered

Source identity

FieldRecorded value
Source typeGitHub source
Original recordJanady13/missionmatch / db/migrations/0088_teams_and_record_scope.sql
DateNot specified in source
RepositoryJanady13/missionmatch
Revision720d47fae476b0d38477ae4401697d372b1ea2bc
Branchfeature/opportunity-to-outcome
Path or archive memberdb/migrations/0088_teams_and_record_scope.sql
Original SHA-25692fa57ef0293d4e5398e270f402fb6630f32e51ab3858018f17f2f4ad4af5e2a
Git blob SHA493816799d99e96c464f03099539c41eb9998ed4
Reading-copy SHA-25692fa57ef0293d4e5398e270f402fb6630f32e51ab3858018f17f2f4ad4af5e2a

Original source ↗

Recovered source transcript

-- +goose Up -- ACCESS LEVELS, THE THIRD AXIS. Directive: build access out to what business and industry standard are. -- -- A recruiting platform's access model has three axes and this schema had one and a half. -- -- 1. WHAT ACTIONS may I take. The capability matrix, six roles and two dozen capabilities. Already correct. -- 2. WHICH FIELDS may I see on a record I can read. Field sensitivity, migration 0087. Done. -- 3. WHICH RECORDS may I see at all. own, my team's, or the whole organization. THIS IS THE MISSING ONE, -- and it is the axis every established product in this market has: Bullhorn, Greenhouse, Lever and -- Salesforce all express it as owner, team and organization. -- -- Two things were missing under it. There was no notion of a TEAM, so "my team's records" could not be -- expressed, and CANDIDATES HAD NO OWNER, so the single most important record type in the product could not -- be scoped at all. Both are added here. -- -- THE DEFAULT IS ORGANIZATION, WHICH IS EXACTLY TODAY'S BEHAVIOUR. Nothing narrows until an owner chooses to -- narrow it. A migration that silently hid records from people who could see them yesterday would be a -- support incident on the morning it shipped, not a feature. -- -- OWNER AND ADMIN ARE ALWAYS ORGANIZATION WIDE AND THAT IS NOT CONFIGURABLE. Somebody has to be able to see -- everything, and it is the person accountable for the account. A product that lets an administrator lock -- themselves out of their own data is a product that generates a support call it cannot resolve. -- ============================================================================ -- 1. teams, the unit a desk belongs to -- ============================================================================ CREATE TABLE teams ( organization_id uuid NOT NULL REFERENCES organizations(id), id uuid NOT NULL DEFAULT gen_random_uuid(), name text NOT NULL, -- The lead is a convenience for display and for routing, NOT an authority. Authority comes from the role -- on the ladder, so making somebody a team lead does not quietly promote them. lead_user_id uuid, status text NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'archived')), created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (organization_id, id), CONSTRAINT fk_teams_lead FOREIGN KEY (organization_id, lead_user_id) REFERENCES users (organization_id, id) ON DELETE SET NULL, CONSTRAINT ck_teams_name CHECK (length(btrim(name)) BETWEEN 1 AND 120) ); -- Archived teams keep their name out of the way without deleting the history of who was on which desk. CREATE UNIQUE INDEX uq_teams_name ON teams (organization_id, lower(btrim(name))) WHERE status = 'active'; ALTER TABLE teams ENABLE ROW LEVEL SECURITY; ALTER TABLE teams FORCE ROW LEVEL SECURITY; CREATE POLICY teams_isolation ON teams USING (organization_id = NULLIF(current_setting('app.current_org_id', true), '')::uuid) WITH CHECK (organization_id = NULLIF(current_setting('app.current_org_id', true), '')::uuid); GRANT SELECT, INSERT, UPDATE ON teams TO missionmatch_app; -- ============================================================================ -- 2. team_members -- ============================================================================ -- A person may belong to MORE THAN ONE team, deliberately. Agencies run people across desks, and a model -- that forces one team per person makes the common case a lie. Team scope therefore means the union of every -- team the caller belongs to. CREATE TABLE team_members ( organization_id uuid NOT NULL REFERENCES organizations(id), team_id uuid NOT NULL, user_id uuid NOT NULL, added_at timestamptz NOT NULL DEFAULT now(), added_by uuid, PRIMARY KEY (organization_id, team_id, user_id), CONSTRAINT fk_team_members_team FOREIGN KEY (organization_id, team_id) REFERENCES teams (organization_id, id) ON DELETE CASCADE, CONSTRAINT fk_team_members_user FOREIGN KEY (organization_id, user_id) REFERENCES users (organization_id, id) ON DELETE CASCADE, CONSTRAINT fk_team_members_adder FOREIGN KEY (organization_id, added_by) REFERENCES users (organization_id, id) ON DELETE SET NULL ); CREATE INDEX idx_team_members_user ON team_members (organization_id, user_id); ALTER TABLE team_members ENABLE ROW LEVEL SECURITY; ALTER TABLE team_members FORCE ROW LEVEL SECURITY; CREATE POLICY team_members_isolation ON team_members USING (organization_id = NULLIF(current_setting('app.current_org_id', true), '')::uuid) WITH CHECK (organization_id = NULLIF(current_setting('app.current_org_id', true), '')::uuid); -- DELETE is granted because removing somebody from a desk is not a historical claim, it is a correction, and -- the audit trail records the change. This matches the reasoning already recorded for org_secrets. GRANT SELECT, INSERT, UPDATE, DELETE ON team_members TO missionmatch_app; -- ============================================================================ -- 3. candidates get an owner -- ============================================================================ -- The gap that made record scope impossible for the record type that matters most. NULLABLE, because the -- entire existing bench is unowned and inventing an owner for it would be a fabrication. An unowned candidate -- is visible at every scope, which is the correct reading: nobody has claimed responsibility for them, so -- nobody is excluded from picking them up. ALTER TABLE candidates ADD COLUMN owner_user_id uuid; ALTER TABLE candidates ADD CONSTRAINT fk_candidates_owner FOREIGN KEY (organization_id, owner_user_id) REFERENCES users (organization_id, id) ON DELETE SET NULL; CREATE INDEX idx_candidates_owner ON candidates (organization_id, owner_user_id) WHERE owner_user_id IS NOT NULL; -- ============================================================================ -- 4. the scope settings -- ============================================================================ -- TWO settings rather than one per role, because the ladder already has the answer for the other four. -- Owner and admin are organization wide and not configurable. Manager either sees their team or everything. -- Everyone below manager shares one setting, since recruiter, member and viewer are all "staff on a desk" -- from a data scoping point of view even though their capabilities differ sharply. ALTER TABLE org_settings ADD COLUMN staff_record_scope text NOT NULL DEFAULT 'organization' CHECK (staff_record_scope IN ('own', 'team', 'organization')); ALTER TABLE org_settings ADD COLUMN manager_record_scope text NOT NULL DEFAULT 'organization' CHECK (manager_record_scope IN ('team', 'organization')); -- Whether a record with no owner is visible to everyone. Default true, because an unowned candidate that -- nobody can see is a candidate nobody will ever work, which is worse than one everybody can see. ALTER TABLE org_settings ADD COLUMN unowned_records_visible boolean NOT NULL DEFAULT true; -- ============================================================================ -- 5. the resolver, in the database, so one definition serves every caller -- ============================================================================ -- +goose StatementBegin -- mm_team_peer_ids returns every user who shares at least one team with the given user, INCLUDING that user. -- Including them matters: a person on no team must still see their own records under team scope, or joining -- an organization before being assigned a desk would make your own work disappear. CREATE OR REPLACE FUNCTION mm_team_peer_ids(p_user uuid) RETURNS TABLE (user_id uuid) AS $$ SELECT p_user UNION SELECT tm2.user_id FROM team_members tm1 JOIN team_members tm2 ON tm2.organization_id = tm1.organization_id AND tm2.team_id = tm1.team_id WHERE tm1.user_id = p_user; $$ LANGUAGE sql STABLE; -- +goose StatementEnd GRANT EXECUTE ON FUNCTION mm_team_peer_ids(uuid) TO missionmatch_app; SELECT mm_mirror_staging_role_privileges(); -- +goose Down DROP FUNCTION IF EXISTS mm_team_peer_ids(uuid); ALTER TABLE org_settings DROP COLUMN IF EXISTS unowned_records_visible; ALTER TABLE org_settings DROP COLUMN IF EXISTS manager_record_scope; ALTER TABLE org_settings DROP COLUMN IF EXISTS staff_record_scope; DROP INDEX IF EXISTS idx_candidates_owner; ALTER TABLE candidates DROP CONSTRAINT IF EXISTS fk_candidates_owner; ALTER TABLE candidates DROP COLUMN IF EXISTS owner_user_id; DROP TABLE IF EXISTS team_members; DROP TABLE IF EXISTS teams;

Search equations, builds, manuscript text, and recovered sources.