Mechanism 01 — Insert trigger
A proposal is born pending, whatever it asked for.
Every proposed term is written through slip_proposals. A BEFORE INSERT trigger rewrites the row on its way in: status is forced to proposed, and the two columns that record a decision are blanked.
This runs for every caller, including the service role the assistants use. An assistant can construct an insert asking for a term to arrive already approved, with a decider and a timestamp attached. What lands in the table is a pending row with those fields empty.
The trigger is BEFORE INSERT rather than a check constraint deliberately: a constraint would reject the write and surface an error the caller might handle, retry, or route around. This accepts the write and corrects it.
create or replace function slip_proposals_force_pending()
returns trigger
language plpgsql
set search_path = pg_temp
as $$
begin
new.status := 'proposed';
new.decided_by := null;
new.decided_at := null;
return new;
end;
$$;
create trigger slip_proposals_force_pending_trg
before insert on slip_proposals
for each row
execute function slip_proposals_force_pending();