Initial v1 code layout

The destination of the wayfinder map: directory and module structure, the
domain model as real schema files, permission boundaries expressed as code,
and typed seams where the undecided parts land.

Satisfies the four structural requirements earlier resolutions handed to a
layout that did not exist:

  - a single database entry point (#11) — the pool is not exported
  - a scheduler entry point with actor context and no request (#13)
  - an entitlement assertion helper (#20, #34) — its call sites are the
    paid-feature list
  - an enumerated list of privileged RLS bypasses (#33) — three of them

Seams carry real types and throw with the ticket that owns them, so the
skeleton wires up and fails only where a decision is genuinely missing.

Verified: tsc --noEmit clean; drizzle-kit generate produces RLS on 18 tables
and 6 policies calling the release-level functions.

Resolves #35

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Christian
2026-08-03 17:20:47 -04:00
commit da74362d00
46 changed files with 9927 additions and 0 deletions

View File

@@ -0,0 +1,96 @@
-- Custom migration, hand-authored. Drizzle cannot diff a SQL function.
--
-- Fixed by #14 decision 1: ONE function collapses the four inputs #12 named
-- into a release level, and ONE lookup answers whether a class is open at it.
-- Nothing else in the system encodes lifecycle.
--
-- ── WHY plpgsql AND WHY FIRST ────────────────────────────────────────────
-- The RLS policies are emitted by Drizzle in the same migration as the tables
-- they guard, so the functions those policies call must already exist. But the
-- functions read `solicitation`, `participation` and `release_matrix`, which
-- that same later migration creates.
--
-- `LANGUAGE sql` validates the body at CREATE time and would fail here.
-- plpgsql defers validation to first call, which is exactly the property
-- needed to break the cycle. Signatures use `text` rather than the
-- `release_level` enum for the same reason — the enum does not exist yet.
-- ─────────────────────────────────────────────────────────────────────────
-- The four inputs: audience membership, stage, participation state, acknowledgement.
CREATE FUNCTION release_level_for_solicitation(p_solicitation uuid)
RETURNS text
LANGUAGE plpgsql STABLE AS $$
DECLARE
v_level text;
BEGIN
SELECT CASE
WHEN p.id IS NULL THEN 'none'
WHEN s.stage = 'awarded' THEN 'awarded'
WHEN s.stage = 'finalists' THEN 'finalist'
WHEN s.stage = 'evaluating' THEN 'evaluating'
-- The deadline is the moment the whole field opens at once (#14 decision 2).
-- Before it, no response content is readable by anyone but its author, which
-- is what makes bid shopping structurally impossible rather than discouraged.
WHEN s.responses_due_at IS NOT NULL AND now() >= s.responses_due_at THEN 'open'
WHEN p.state = 'submitted' THEN 'submitted'
WHEN p.acknowledged_at IS NOT NULL OR NOT s.acknowledgement_required
THEN 'acknowledged'
ELSE 'invited'
END
INTO v_level
FROM solicitation s
LEFT JOIN participation p
ON p.solicitation_id = s.id
AND p.vendor_org_id = nullif(current_setting('app.org_id', true), '')::uuid
WHERE s.id = p_solicitation;
RETURN coalesce(v_level, 'none');
END;
$$;
-- The issuer reaches their own event at the level its stage implies, without
-- being a participant in it. Everyone else gets 'none' — including a dual-role
-- org's issuing side looking at its own vendor side (#11 decision 2).
CREATE FUNCTION release_level_for_response(p_response uuid)
RETURNS text
LANGUAGE plpgsql STABLE AS $$
DECLARE
v_level text;
BEGIN
SELECT CASE
WHEN current_setting('app.capability', true) = 'issuer'
AND s.issuer_org_id = nullif(current_setting('app.org_id', true), '')::uuid
THEN release_level_for_solicitation(s.id)
WHEN current_setting('app.capability', true) = 'vendor'
AND r.vendor_org_id = nullif(current_setting('app.org_id', true), '')::uuid
THEN release_level_for_solicitation(s.id)
ELSE 'none'
END
INTO v_level
FROM response r
JOIN solicitation s ON s.id = r.solicitation_id
WHERE r.id = p_response;
RETURN coalesce(v_level, 'none');
END;
$$;
-- Reads the CURRENT matrix version. A past release is reconstructed by reading
-- an older version, which is why the matrix is versioned rather than updated.
CREATE FUNCTION class_released(p_level text, p_class text)
RETURNS boolean
LANGUAGE plpgsql STABLE AS $$
DECLARE
v_released boolean;
BEGIN
SELECT released = 'true'
INTO v_released
FROM release_matrix
WHERE class = p_class::disclosure_class
AND level = p_level::release_level
ORDER BY version DESC
LIMIT 1;
RETURN coalesce(v_released, false);
END;
$$;