ThatAIguyA world of ideas

THE CONSCIOUSNESS ATLAS
Read. Experiment. Follow the evidence.

ConsciousnessLearn · Explore · Joseph Anady

Janady13/missionmatch / db/migrations/0086_proposal_operations.sql

Source identity, revision, and recovered contents.

GitHub sourceSource text recovered

Source identity

FieldRecorded value
Source typeGitHub source
Original recordJanady13/missionmatch / db/migrations/0086_proposal_operations.sql
DateNot specified in source
RepositoryJanady13/missionmatch
Revision720d47fae476b0d38477ae4401697d372b1ea2bc
Branchfeature/opportunity-to-outcome
Path or archive memberdb/migrations/0086_proposal_operations.sql
Original SHA-25674383bb5e337d66b9314432ccdffcf67c5ea7c2fb51f6ca4e9342e05be29f087
Git blob SHA2db0701720ef706b629663426b5b7475570dd339
Reading-copy SHA-25674383bb5e337d66b9314432ccdffcf67c5ea7c2fb51f6ca4e9342e05be29f087

Original source ↗

Recovered source transcript

-- +goose Up -- PHASE 3, PROPOSAL OPERATIONS. Directive Section 3.7, plus the recorded scope amendment of 30 July 2026 that -- adds pricing and recorded wage floors. -- -- TENANCY IS IN THE KEY, not merely in a column. Every table carries organization_id, every cross table -- reference is a COMPOSITE foreign key on (organization_id, target_id), and every index leads with -- organization_id. That is the Phase 2 pattern and it is what makes a mis scoped join impossible rather than -- merely unlikely: a row cannot reference a parent belonging to another tenant, because the key includes the -- tenant. -- -- POLICIES CARRY NO "TO" CLAUSE, DELIBERATELY. Of the 57 policies in this schema, 6 name a role and 51 do not, -- and the 6 are exactly the ones that broke Gate A tonight: a policy addressed only to missionmatch_app does -- not apply to missionmatch_staging_app, and with NO applicable policy RLS denies everything. An unaddressed -- policy applies to every role, which is both simpler and correct in every environment. Migration 0085 exists -- to repair that damage; these tables are written so it cannot happen to them. -- -- NO DELETE GRANTS ANYWHERE. Withdrawal, rejection, and supersession are STATUSES. A proposal that was -- withdrawn is a fact about the record, and a record that can be deleted cannot be audited. The one exception -- is proposal_team_members, where removing a person from a team is not a historical claim, and even there the -- grant is withheld and removal is a status, because the alternative is an audit trail with holes in it. -- ============================================================================ -- 1. proposals -- ============================================================================ CREATE TABLE proposals ( organization_id uuid NOT NULL REFERENCES organizations(id), id uuid NOT NULL DEFAULT gen_random_uuid(), opportunity_id uuid NOT NULL, title text NOT NULL DEFAULT '', -- stage is where the proposal is in the capture lifecycle; status is whether it is live, so a proposal can -- be at gold_team AND cancelled. Conflating them would lose the second fact. stage text NOT NULL DEFAULT 'intake' CHECK (stage IN ('intake','pink_team','red_team','gold_team','final_review', 'upload','submitted','awarded','lost','no_bid')), status text NOT NULL DEFAULT 'active' CHECK (status IN ('active','on_hold','cancelled','archived')), proposal_owner_user_id uuid, capture_owner_user_id uuid, customer_deadline timestamptz, internal_deadline timestamptz, submitted_at timestamptz, outcome_recorded_at timestamptz, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (id), CONSTRAINT uq_proposals_org_id UNIQUE (organization_id, id), FOREIGN KEY (organization_id, opportunity_id) REFERENCES tracked_opportunities (organization_id, id), FOREIGN KEY (organization_id, proposal_owner_user_id) REFERENCES users (organization_id, id) ON DELETE SET NULL (proposal_owner_user_id), FOREIGN KEY (organization_id, capture_owner_user_id) REFERENCES users (organization_id, id) ON DELETE SET NULL (capture_owner_user_id), -- SUBMISSION IS A ONE WAY DOOR IN THE SCHEMA. submitted_at may only be set on a submitted or later stage, -- so no code path can stamp a submission on a proposal still in review, whatever the API believes. CONSTRAINT ck_proposals_submitted_stage CHECK ( submitted_at IS NULL OR stage IN ('submitted','awarded','lost') ), -- An internal deadline after the customer deadline is a planning error worth refusing at the schema. CONSTRAINT ck_proposals_deadline_order CHECK ( internal_deadline IS NULL OR customer_deadline IS NULL OR internal_deadline <= customer_deadline ) ); CREATE INDEX idx_proposals_org_opp ON proposals (organization_id, opportunity_id, stage); CREATE INDEX idx_proposals_org_deadline ON proposals (organization_id, customer_deadline) WHERE status = 'active'; ALTER TABLE proposals ENABLE ROW LEVEL SECURITY; ALTER TABLE proposals FORCE ROW LEVEL SECURITY; CREATE POLICY proposals_isolation ON proposals 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 proposals TO missionmatch_app; -- ============================================================================ -- 2 and 3. proposal_sections and proposal_section_versions -- ============================================================================ CREATE TABLE proposal_sections ( organization_id uuid NOT NULL REFERENCES organizations(id), id uuid NOT NULL DEFAULT gen_random_uuid(), proposal_id uuid NOT NULL, section_code text NOT NULL, title text NOT NULL DEFAULT '', owner_user_id uuid, required boolean NOT NULL DEFAULT true, page_or_word_limit text NOT NULL DEFAULT '', due_at timestamptz, draft_status text NOT NULL DEFAULT 'not_started' CHECK (draft_status IN ('not_started','in_progress','drafted','final')), review_status text NOT NULL DEFAULT 'not_reviewed' CHECK (review_status IN ('not_reviewed','in_review','changes_requested','approved')), approved_by_user_id uuid, approved_at timestamptz, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (id), CONSTRAINT uq_proposal_sections_org_id UNIQUE (organization_id, id), CONSTRAINT uq_proposal_sections_code UNIQUE (organization_id, proposal_id, section_code), FOREIGN KEY (organization_id, proposal_id) REFERENCES proposals (organization_id, id), FOREIGN KEY (organization_id, owner_user_id) REFERENCES users (organization_id, id) ON DELETE SET NULL (owner_user_id), FOREIGN KEY (organization_id, approved_by_user_id) REFERENCES users (organization_id, id) ON DELETE SET NULL (approved_by_user_id), -- An approval must name who gave it. An approved section with no approver is the shape of an audit hole. CONSTRAINT ck_proposal_sections_approval CHECK ( review_status <> 'approved' OR (approved_by_user_id IS NOT NULL AND approved_at IS NOT NULL) ) ); CREATE INDEX idx_proposal_sections_org_prop ON proposal_sections (organization_id, proposal_id, draft_status); ALTER TABLE proposal_sections ENABLE ROW LEVEL SECURITY; ALTER TABLE proposal_sections FORCE ROW LEVEL SECURITY; CREATE POLICY proposal_sections_isolation ON proposal_sections 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 proposal_sections TO missionmatch_app; -- Versions are APPEND ONLY. There is no UPDATE grant, so a version cannot be edited after it exists: a new -- version is written instead. That is what makes "what did the red team actually review" answerable later. CREATE TABLE proposal_section_versions ( organization_id uuid NOT NULL REFERENCES organizations(id), id uuid NOT NULL DEFAULT gen_random_uuid(), section_id uuid NOT NULL, version_number integer NOT NULL, content_ref text NOT NULL DEFAULT '', content_sha256 text NOT NULL DEFAULT '', authored_by_user_id uuid, created_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (id), CONSTRAINT uq_proposal_section_versions_org_id UNIQUE (organization_id, id), CONSTRAINT uq_proposal_section_versions_num UNIQUE (organization_id, section_id, version_number), CONSTRAINT ck_proposal_section_versions_num CHECK (version_number >= 1), FOREIGN KEY (organization_id, section_id) REFERENCES proposal_sections (organization_id, id), FOREIGN KEY (organization_id, authored_by_user_id) REFERENCES users (organization_id, id) ON DELETE SET NULL (authored_by_user_id) ); CREATE INDEX idx_proposal_section_versions_org_section ON proposal_section_versions (organization_id, section_id, version_number DESC); ALTER TABLE proposal_section_versions ENABLE ROW LEVEL SECURITY; ALTER TABLE proposal_section_versions FORCE ROW LEVEL SECURITY; CREATE POLICY proposal_section_versions_isolation ON proposal_section_versions 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 ON proposal_section_versions TO missionmatch_app; -- ============================================================================ -- 4. proposal_compliance_items -- ============================================================================ CREATE TABLE proposal_compliance_items ( organization_id uuid NOT NULL REFERENCES organizations(id), id uuid NOT NULL DEFAULT gen_random_uuid(), proposal_id uuid NOT NULL, requirement_text text NOT NULL DEFAULT '', source_document_id uuid, source_page_number integer, source_section_label text NOT NULL DEFAULT '', response_location text NOT NULL DEFAULT '', owner_user_id uuid, due_at timestamptz, required boolean NOT NULL DEFAULT true, status text NOT NULL DEFAULT 'open' CHECK (status IN ('open','in_progress','answered','verified','waived')), evidence_ref text NOT NULL DEFAULT '', reviewer_user_id uuid, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (id), CONSTRAINT uq_proposal_compliance_org_id UNIQUE (organization_id, id), FOREIGN KEY (organization_id, proposal_id) REFERENCES proposals (organization_id, id), FOREIGN KEY (organization_id, source_document_id) REFERENCES opportunity_documents (organization_id, id) ON DELETE SET NULL (source_document_id), FOREIGN KEY (organization_id, owner_user_id) REFERENCES users (organization_id, id) ON DELETE SET NULL (owner_user_id), FOREIGN KEY (organization_id, reviewer_user_id) REFERENCES users (organization_id, id) ON DELETE SET NULL (reviewer_user_id), -- A WAIVER MUST BE ACCOUNTABLE. Waiving a required compliance item is the one status that removes a hard -- blocker, so it may not be anonymous. CONSTRAINT ck_proposal_compliance_waiver CHECK ( status <> 'waived' OR reviewer_user_id IS NOT NULL ) ); CREATE INDEX idx_proposal_compliance_org_prop ON proposal_compliance_items (organization_id, proposal_id, status, required); ALTER TABLE proposal_compliance_items ENABLE ROW LEVEL SECURITY; ALTER TABLE proposal_compliance_items FORCE ROW LEVEL SECURITY; CREATE POLICY proposal_compliance_isolation ON proposal_compliance_items 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 proposal_compliance_items TO missionmatch_app; -- ============================================================================ -- 5. proposal_milestones -- ============================================================================ CREATE TABLE proposal_milestones ( organization_id uuid NOT NULL REFERENCES organizations(id), id uuid NOT NULL DEFAULT gen_random_uuid(), proposal_id uuid NOT NULL, kind text NOT NULL CHECK (kind IN ('question_deadline','pink_team','red_team','gold_team','final_review', 'upload','submission')), due_at timestamptz, completed_at timestamptz, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (id), CONSTRAINT uq_proposal_milestones_org_id UNIQUE (organization_id, id), CONSTRAINT uq_proposal_milestones_kind UNIQUE (organization_id, proposal_id, kind), FOREIGN KEY (organization_id, proposal_id) REFERENCES proposals (organization_id, id) ); CREATE INDEX idx_proposal_milestones_org_prop ON proposal_milestones (organization_id, proposal_id, due_at); ALTER TABLE proposal_milestones ENABLE ROW LEVEL SECURITY; ALTER TABLE proposal_milestones FORCE ROW LEVEL SECURITY; CREATE POLICY proposal_milestones_isolation ON proposal_milestones 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 proposal_milestones TO missionmatch_app; -- ============================================================================ -- 6. proposal_risks -- ============================================================================ CREATE TABLE proposal_risks ( organization_id uuid NOT NULL REFERENCES organizations(id), id uuid NOT NULL DEFAULT gen_random_uuid(), proposal_id uuid NOT NULL, title text NOT NULL DEFAULT '', likelihood text NOT NULL DEFAULT 'medium' CHECK (likelihood IN ('low','medium','high')), impact text NOT NULL DEFAULT 'medium' CHECK (impact IN ('low','medium','high')), owner_user_id uuid, mitigation text NOT NULL DEFAULT '', trigger_condition text NOT NULL DEFAULT '', status text NOT NULL DEFAULT 'open' CHECK (status IN ('open','mitigating','accepted','closed')), related_scope_item_id uuid, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (id), CONSTRAINT uq_proposal_risks_org_id UNIQUE (organization_id, id), FOREIGN KEY (organization_id, proposal_id) REFERENCES proposals (organization_id, id), FOREIGN KEY (organization_id, owner_user_id) REFERENCES users (organization_id, id) ON DELETE SET NULL (owner_user_id), FOREIGN KEY (organization_id, related_scope_item_id) REFERENCES opportunity_scope_items (organization_id, id) ON DELETE SET NULL (related_scope_item_id) ); CREATE INDEX idx_proposal_risks_org_prop ON proposal_risks (organization_id, proposal_id, status); ALTER TABLE proposal_risks ENABLE ROW LEVEL SECURITY; ALTER TABLE proposal_risks FORCE ROW LEVEL SECURITY; CREATE POLICY proposal_risks_isolation ON proposal_risks 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 proposal_risks TO missionmatch_app; -- ============================================================================ -- 7. proposal_team_members -- ============================================================================ CREATE TABLE proposal_team_members ( organization_id uuid NOT NULL REFERENCES organizations(id), id uuid NOT NULL DEFAULT gen_random_uuid(), proposal_id uuid NOT NULL, user_id uuid NOT NULL, team_role text NOT NULL DEFAULT 'contributor' CHECK (team_role IN ('capture_manager','proposal_manager','writer','reviewer', 'pricer','contributor')), status text NOT NULL DEFAULT 'active' CHECK (status IN ('active','removed')), created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (id), CONSTRAINT uq_proposal_team_org_id UNIQUE (organization_id, id), CONSTRAINT uq_proposal_team_member UNIQUE (organization_id, proposal_id, user_id, team_role), FOREIGN KEY (organization_id, proposal_id) REFERENCES proposals (organization_id, id), FOREIGN KEY (organization_id, user_id) REFERENCES users (organization_id, id) ); CREATE INDEX idx_proposal_team_org_prop ON proposal_team_members (organization_id, proposal_id, status); ALTER TABLE proposal_team_members ENABLE ROW LEVEL SECURITY; ALTER TABLE proposal_team_members FORCE ROW LEVEL SECURITY; CREATE POLICY proposal_team_isolation ON proposal_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); GRANT SELECT, INSERT, UPDATE ON proposal_team_members TO missionmatch_app; -- ============================================================================ -- 8. proposal_staffing_links, AND THE PRICING FROM THE 30 JULY AMENDMENT -- ============================================================================ -- This is the table the eight way race runs against. approved_count is a DENORMALISED COUNTER maintained by -- the approve and withdraw transactions, and it exists because the readiness engine needs coverage without -- counting slate members on every calculation. -- -- A DENORMALISED COUNTER IS A CORRECTNESS RISK AND IS TREATED AS ONE. It carries a CHECK that it can never go -- negative and never exceed required_quantity, so a lost update or a double decrement fails LOUDLY at the -- database rather than quietly producing a coverage number that flatters the proposal. Gate 3 requires the -- count to survive an eight way race; this constraint is what turns a race defect into an error instead of a -- wrong answer. -- -- PRICING, per the amendment. pay_rate and bill_rate are the ends; burden_pct is what makes the middle real. -- MARGIN IS NOT STORED. It is computed by a pure function, because a stored margin drifts from its inputs and -- the gate requires the same inputs to produce a bit identical result. burden_pct being NULL is the state that -- clause A of the gate is about: it means the line is UNBURDENED and no margin may be reported for it at all. CREATE TABLE proposal_staffing_links ( organization_id uuid NOT NULL REFERENCES organizations(id), id uuid NOT NULL DEFAULT gen_random_uuid(), proposal_id uuid NOT NULL, requirement_id uuid NOT NULL, slate_id uuid, required_quantity integer NOT NULL DEFAULT 1, approved_count integer NOT NULL DEFAULT 0, -- Pricing. numeric, never float, mirroring the engine's decimal discipline into the schema. labor_category text NOT NULL DEFAULT '', pay_rate numeric(12,4), burden_pct numeric(9,6), bill_rate numeric(12,4), wage_floor_id uuid, rate_source text NOT NULL DEFAULT '', rate_version text NOT NULL DEFAULT '', priced_by_user_id uuid, priced_at timestamptz, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (id), CONSTRAINT uq_proposal_staffing_org_id UNIQUE (organization_id, id), CONSTRAINT uq_proposal_staffing_link UNIQUE (organization_id, proposal_id, requirement_id), FOREIGN KEY (organization_id, proposal_id) REFERENCES proposals (organization_id, id), FOREIGN KEY (organization_id, requirement_id) REFERENCES requirements (organization_id, id), FOREIGN KEY (organization_id, priced_by_user_id) REFERENCES users (organization_id, id) ON DELETE SET NULL (priced_by_user_id), CONSTRAINT ck_staffing_required_positive CHECK (required_quantity >= 0), -- THE COUNTER INVARIANT, and the whole reason the race is provable rather than arguable. CONSTRAINT ck_staffing_approved_range CHECK (approved_count >= 0 AND approved_count <= required_quantity), CONSTRAINT ck_staffing_rates_nonneg CHECK ( (pay_rate IS NULL OR pay_rate >= 0) AND (bill_rate IS NULL OR bill_rate >= 0) AND (burden_pct IS NULL OR burden_pct >= 0) ), -- A PRICED LINE NAMES WHO PRICED IT AND WHEN. A bill rate with no attribution cannot be defended to a -- customer or an auditor. CONSTRAINT ck_staffing_priced_attribution CHECK ( bill_rate IS NULL OR (priced_by_user_id IS NOT NULL AND priced_at IS NOT NULL) ) ); CREATE INDEX idx_proposal_staffing_org_prop ON proposal_staffing_links (organization_id, proposal_id); CREATE INDEX idx_proposal_staffing_org_req ON proposal_staffing_links (organization_id, requirement_id); ALTER TABLE proposal_staffing_links ENABLE ROW LEVEL SECURITY; ALTER TABLE proposal_staffing_links FORCE ROW LEVEL SECURITY; CREATE POLICY proposal_staffing_isolation ON proposal_staffing_links 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 proposal_staffing_links TO missionmatch_app; -- ============================================================================ -- 9. proposal_attachments -- ============================================================================ CREATE TABLE proposal_attachments ( organization_id uuid NOT NULL REFERENCES organizations(id), id uuid NOT NULL DEFAULT gen_random_uuid(), proposal_id uuid NOT NULL, attachment_kind text NOT NULL DEFAULT 'other' CHECK (attachment_kind IN ('past_performance','resume','letter_of_commitment', 'pricing_workbook','representations','technical_volume','other')), required boolean NOT NULL DEFAULT false, file_name text NOT NULL DEFAULT '', object_key text NOT NULL DEFAULT '', content_sha256 text NOT NULL DEFAULT '', byte_size bigint NOT NULL DEFAULT 0, status text NOT NULL DEFAULT 'missing' CHECK (status IN ('missing','uploaded','approved','superseded')), uploaded_by_user_id uuid, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (id), CONSTRAINT uq_proposal_attachments_org_id UNIQUE (organization_id, id), FOREIGN KEY (organization_id, proposal_id) REFERENCES proposals (organization_id, id), FOREIGN KEY (organization_id, uploaded_by_user_id) REFERENCES users (organization_id, id) ON DELETE SET NULL (uploaded_by_user_id), -- An uploaded attachment has bytes behind it. A status of uploaded with no object key is a lie the -- readiness engine would believe. CONSTRAINT ck_proposal_attachments_bytes CHECK ( status IN ('missing') OR (object_key <> '' AND content_sha256 <> '') ) ); CREATE INDEX idx_proposal_attachments_org_prop ON proposal_attachments (organization_id, proposal_id, status, required); ALTER TABLE proposal_attachments ENABLE ROW LEVEL SECURITY; ALTER TABLE proposal_attachments FORCE ROW LEVEL SECURITY; CREATE POLICY proposal_attachments_isolation ON proposal_attachments 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 proposal_attachments TO missionmatch_app; -- ============================================================================ -- 10. proposal_readiness_snapshots. APPEND ONLY, no UPDATE grant. -- ============================================================================ -- One decimal column per component, per Section 7, rather than a jsonb blob, because a component score is a -- number the product reasons about and a blob is a number nobody can index or constrain. Weights live in the -- engine, not here: a snapshot records what was computed, never how. CREATE TABLE proposal_readiness_snapshots ( organization_id uuid NOT NULL REFERENCES organizations(id), id uuid NOT NULL DEFAULT gen_random_uuid(), proposal_id uuid NOT NULL, overall numeric(6,4) NOT NULL, staffing_coverage numeric(6,4) NOT NULL, compliance_matrix numeric(6,4) NOT NULL, required_sections numeric(6,4) NOT NULL, review_and_approval numeric(6,4) NOT NULL, schedule_health numeric(6,4) NOT NULL, risk_posture numeric(6,4) NOT NULL, blockers jsonb NOT NULL DEFAULT '[]'::jsonb, drivers jsonb NOT NULL DEFAULT '[]'::jsonb, algorithm_version text NOT NULL, stale boolean NOT NULL DEFAULT false, calculated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (id), CONSTRAINT uq_proposal_readiness_org_id UNIQUE (organization_id, id), FOREIGN KEY (organization_id, proposal_id) REFERENCES proposals (organization_id, id), CONSTRAINT ck_readiness_bounds CHECK ( overall BETWEEN 0 AND 1 AND staffing_coverage BETWEEN 0 AND 1 AND compliance_matrix BETWEEN 0 AND 1 AND required_sections BETWEEN 0 AND 1 AND review_and_approval BETWEEN 0 AND 1 AND schedule_health BETWEEN 0 AND 1 AND risk_posture BETWEEN 0 AND 1 ), CONSTRAINT ck_readiness_algo_named CHECK (algorithm_version <> '') ); CREATE INDEX idx_proposal_readiness_org_prop ON proposal_readiness_snapshots (organization_id, proposal_id, calculated_at DESC); ALTER TABLE proposal_readiness_snapshots ENABLE ROW LEVEL SECURITY; ALTER TABLE proposal_readiness_snapshots FORCE ROW LEVEL SECURITY; CREATE POLICY proposal_readiness_isolation ON proposal_readiness_snapshots 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); -- UPDATE is granted for exactly one column's worth of behaviour, the staleness flag a withdrawal sets. A -- snapshot's NUMBERS are never rewritten; only the flag saying they no longer describe the present. GRANT SELECT, INSERT ON proposal_readiness_snapshots TO missionmatch_app; GRANT UPDATE (stale) ON proposal_readiness_snapshots TO missionmatch_app; -- ============================================================================ -- 11. wage_determination_floors. THE RECORDED FLOOR, from the 30 July amendment. -- ============================================================================ -- ENTERED BY A HUMAN, NOT EXTRACTED. Finding one of the four that held Phase 2 open is that the SCA wage -- determination's text layer carries no usable character map, so its rate grid cannot be read from the -- document and OCR is deferred as candidate P2. A floor a named person recorded, with the determination number -- and revision beside it, is defensible. A floor guessed from an unreadable PDF would not be, and it would be -- worse than no floor at all because the submission blocker would be enforcing a fiction. CREATE TABLE wage_determination_floors ( organization_id uuid NOT NULL REFERENCES organizations(id), id uuid NOT NULL DEFAULT gen_random_uuid(), wd_number text NOT NULL, wd_revision text NOT NULL DEFAULT '', labor_category text NOT NULL, locality text NOT NULL DEFAULT '', hourly_floor numeric(12,4) NOT NULL, fringe_hourly numeric(12,4) NOT NULL DEFAULT 0, effective_from date NOT NULL, source_note text NOT NULL DEFAULT '', recorded_by_user_id uuid NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (id), CONSTRAINT uq_wage_floors_org_id UNIQUE (organization_id, id), CONSTRAINT uq_wage_floors_key UNIQUE (organization_id, wd_number, wd_revision, labor_category, locality, effective_from), FOREIGN KEY (organization_id, recorded_by_user_id) REFERENCES users (organization_id, id), CONSTRAINT ck_wage_floors_positive CHECK (hourly_floor > 0 AND fringe_hourly >= 0), CONSTRAINT ck_wage_floors_named CHECK (wd_number <> '' AND labor_category <> '') ); CREATE INDEX idx_wage_floors_org_lcat ON wage_determination_floors (organization_id, labor_category, effective_from DESC); ALTER TABLE wage_determination_floors ENABLE ROW LEVEL SECURITY; ALTER TABLE wage_determination_floors FORCE ROW LEVEL SECURITY; CREATE POLICY wage_floors_isolation ON wage_determination_floors 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); -- APPEND ONLY. A floor is a historical fact tied to a determination revision. Correcting one means recording -- the next revision, not editing the last, because a bill rate approved against an old floor was approved -- against that floor and the audit has to still show it. GRANT SELECT, INSERT ON wage_determination_floors TO missionmatch_app; ALTER TABLE proposal_staffing_links ADD CONSTRAINT fk_staffing_wage_floor FOREIGN KEY (organization_id, wage_floor_id) REFERENCES wage_determination_floors (organization_id, id) ON DELETE SET NULL (wage_floor_id); -- ============================================================================ -- 12. THE SLATE APPROVAL LIFECYCLE, amendment 2.3, Phase 3 item 7. -- ============================================================================ -- slate_members already carries decision, decided_by, decided_at and decision_note, which is the RECRUITER's -- call on a candidate. The lifecycle below is a DIFFERENT fact: whether that member is approved onto a -- proposal. Conflating the two would lose the distinction between "we think this person fits" and "we have -- committed this person to a bid", and only the second one moves approved_count. ALTER TABLE slate_members ADD COLUMN status text NOT NULL DEFAULT 'proposed' CHECK (status IN ('proposed','approved','withdrawn')), ADD COLUMN approved_by_user_id uuid, ADD COLUMN approved_at timestamptz, ADD COLUMN withdrawn_by_user_id uuid, ADD COLUMN withdrawn_at timestamptz, ADD COLUMN withdrawal_reason text NOT NULL DEFAULT ''; -- THE REASON IS REQUIRED ON EVERY WITHDRAWAL AND IT IS ENFORCED HERE, not only at the API. Gate 3 says a -- withdrawal without a reason is rejected by the API rather than just the form; this constraint means it is -- also rejected when the API is wrong. ALTER TABLE slate_members ADD CONSTRAINT ck_slate_members_withdrawal CHECK ( status <> 'withdrawn' OR (withdrawal_reason <> '' AND withdrawn_by_user_id IS NOT NULL AND withdrawn_at IS NOT NULL) ), ADD CONSTRAINT ck_slate_members_approval CHECK ( status <> 'approved' OR (approved_by_user_id IS NOT NULL AND approved_at IS NOT NULL) ); CREATE INDEX idx_slate_members_org_status ON slate_members (organization_id, slate_id, status); GRANT UPDATE (status, approved_by_user_id, approved_at, withdrawn_by_user_id, withdrawn_at, withdrawal_reason) ON slate_members TO missionmatch_app; -- ============================================================================ -- 13. STAGING ROLE PARITY, made reusable so tonight's Gate A failure cannot recur. -- ============================================================================ -- Migration 0085 repaired the staging app role by hand after Gate A found it missing seven column grants and -- absent from six policies. THE UNDERLYING CAUSE WAS NEVER FIXED: 51 migrations grant to missionmatch_app -- literally and none mentions the staging role, so every NEW grant repeats the gap. This migration adds -- grants, so without this it would repeat it right now. -- -- The function is created here and called at the end. Every future migration that adds a grant should call it -- as its last statement, which is one line and is cheaper than another Gate A failure. -- +goose StatementBegin CREATE OR REPLACE FUNCTION mm_mirror_staging_role_privileges() RETURNS void AS $$ DECLARE staging_app CONSTANT text := 'missionmatch_staging_app'; prod_app CONSTANT text := 'missionmatch_app'; g record; BEGIN -- Roles are CLUSTER wide while privileges are per database, so without this guard the block would grant the -- staging role privileges on PRODUCTION tables. Inert today because the staging roles cannot connect to the -- production database, and defence in depth is worth more than relying on that. IF current_database() <> 'missionmatch_staging' THEN RETURN; END IF; IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = staging_app) THEN RETURN; END IF; FOR g IN SELECT a.table_name, a.privilege_type FROM information_schema.role_table_grants a WHERE a.grantee = prod_app AND a.table_schema = 'public' AND NOT EXISTS (SELECT 1 FROM information_schema.role_table_grants b WHERE b.grantee = staging_app AND b.table_schema='public' AND b.table_name = a.table_name AND b.privilege_type = a.privilege_type) LOOP EXECUTE format('GRANT %s ON public.%I TO %I', g.privilege_type, g.table_name, staging_app); END LOOP; FOR g IN SELECT a.table_name, a.column_name, a.privilege_type FROM information_schema.column_privileges a WHERE a.grantee = prod_app AND a.table_schema = 'public' AND NOT EXISTS (SELECT 1 FROM information_schema.column_privileges b WHERE b.grantee = staging_app AND b.table_schema='public' AND b.table_name = a.table_name AND b.column_name = a.column_name AND b.privilege_type = a.privilege_type) LOOP EXECUTE format('GRANT %s (%I) ON public.%I TO %I', g.privilege_type, g.column_name, g.table_name, staging_app); END LOOP; -- CONTAINMENT RE ASSERTED rather than trusted. Nothing above touches a database privilege; this proves it. IF has_database_privilege(staging_app, 'missionmatch', 'CONNECT') THEN RAISE EXCEPTION 'staging parity: % gained CONNECT on the production database', staging_app; END IF; END $$ LANGUAGE plpgsql; -- +goose StatementEnd SELECT mm_mirror_staging_role_privileges(); -- +goose Down DROP TABLE IF EXISTS proposal_readiness_snapshots; DROP TABLE IF EXISTS proposal_attachments; ALTER TABLE proposal_staffing_links DROP CONSTRAINT IF EXISTS fk_staffing_wage_floor; DROP TABLE IF EXISTS wage_determination_floors; DROP TABLE IF EXISTS proposal_staffing_links; DROP TABLE IF EXISTS proposal_team_members; DROP TABLE IF EXISTS proposal_risks; DROP TABLE IF EXISTS proposal_milestones; DROP TABLE IF EXISTS proposal_compliance_items; DROP TABLE IF EXISTS proposal_section_versions; DROP TABLE IF EXISTS proposal_sections; DROP TABLE IF EXISTS proposals; ALTER TABLE slate_members DROP CONSTRAINT IF EXISTS ck_slate_members_withdrawal, DROP CONSTRAINT IF EXISTS ck_slate_members_approval; DROP INDEX IF EXISTS idx_slate_members_org_status; ALTER TABLE slate_members DROP COLUMN IF EXISTS status, DROP COLUMN IF EXISTS approved_by_user_id, DROP COLUMN IF EXISTS approved_at, DROP COLUMN IF EXISTS withdrawn_by_user_id, DROP COLUMN IF EXISTS withdrawn_at, DROP COLUMN IF EXISTS withdrawal_reason; DROP FUNCTION IF EXISTS mm_mirror_staging_role_privileges();

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