-- t-paliad-161: Inline Paliadin chat modal + agent-suggested write path. -- -- Design: docs/design-paliadin-inline-2026-05-08.md §7.1 + §4.2. -- -- Two coordinated additions: -- -- 1. paliad.approval_requests gets two new columns marking which requests -- were drafted by Paliadin on a user's behalf: -- - requester_kind text — 'user' (direct create) | 'agent' (Paliadin -- suggestion awaiting the user's review) -- - agent_turn_id uuid — links back to the paliadin_turns row that -- produced the suggestion. ON DELETE SET NULL -- so audit rows survive turn-row purges. -- The xor-check pins (kind='agent' ↔ agent_turn_id IS NOT NULL) so we -- can't lose provenance on agent rows or accidentally tag user rows -- with a turn id. -- -- 2. paliad.paliadin_turns.context jsonb — the structured page-context -- payload (route_name + primary_entity_type + primary_entity_id + -- user_selection_text + view hints) the inline widget submits with -- every turn. Old page_origin column stays as the cosmetic URL field. -- -- Idempotent — every ALTER uses IF NOT EXISTS and the constraints/index -- are guarded by DO blocks. Re-applying after a partial failure leaves -- the same end state as a fresh run. -- ============================================================================ -- 1. paliad.approval_requests.requester_kind + agent_turn_id -- ============================================================================ ALTER TABLE paliad.approval_requests ADD COLUMN IF NOT EXISTS requester_kind text NOT NULL DEFAULT 'user'; DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'approval_requests_requester_kind_check' AND conrelid = 'paliad.approval_requests'::regclass ) THEN ALTER TABLE paliad.approval_requests ADD CONSTRAINT approval_requests_requester_kind_check CHECK (requester_kind IN ('user', 'agent')); END IF; END$$; ALTER TABLE paliad.approval_requests ADD COLUMN IF NOT EXISTS agent_turn_id uuid; DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'approval_requests_agent_turn_fk' AND conrelid = 'paliad.approval_requests'::regclass ) THEN ALTER TABLE paliad.approval_requests ADD CONSTRAINT approval_requests_agent_turn_fk FOREIGN KEY (agent_turn_id) REFERENCES paliad.paliadin_turns(turn_id) ON DELETE SET NULL; END IF; END$$; DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'approval_requests_agent_xor' AND conrelid = 'paliad.approval_requests'::regclass ) THEN ALTER TABLE paliad.approval_requests ADD CONSTRAINT approval_requests_agent_xor CHECK ( (requester_kind = 'agent' AND agent_turn_id IS NOT NULL) OR (requester_kind = 'user' AND agent_turn_id IS NULL) ); END IF; END$$; CREATE INDEX IF NOT EXISTS approval_requests_agent_turn_idx ON paliad.approval_requests (agent_turn_id) WHERE agent_turn_id IS NOT NULL; COMMENT ON COLUMN paliad.approval_requests.requester_kind IS 'Who originated the request: ''user'' (direct user create) or ' '''agent'' (Paliadin drafted it from a chat turn, awaiting user ' 'approval). Default ''user'' so existing audit rows backfill cleanly.'; COMMENT ON COLUMN paliad.approval_requests.agent_turn_id IS 'When requester_kind=''agent'', the paliadin_turns row the suggestion ' 'came from. NULL otherwise. ON DELETE SET NULL so the audit record ' 'survives if the turn row is later purged.'; -- ============================================================================ -- 2. paliad.paliadin_turns.context — structured page payload -- ============================================================================ ALTER TABLE paliad.paliadin_turns ADD COLUMN IF NOT EXISTS context jsonb; COMMENT ON COLUMN paliad.paliadin_turns.context IS 'Structured page-context payload from the inline widget: route_name + ' 'primary_entity_type + primary_entity_id + user_selection_text + ' 'view_mode + filter_summary. NULL for legacy turns (PoC standalone ' 'page predates the structured payload — page_origin is the only field ' 'they carry). Design: docs/design-paliadin-inline-2026-05-08.md §4.';