Per the owner's spec: each social connection gets publishGovernance in its fields — unset = manual publish (today), auto = dispatch on approval, admin1/admin_majority/admin_consensus = a publish-phase chatgov poll (subject draft_publish, its own open-poll slot, no collision with review gates). Dispatch holds until the publish poll passes (E3 pre-dispatch check); auto dispatches on approval. Includes migration 033, the PATCH /connections/:id/publish-governance route (chatgov.manage), and 19+31 new tests. Implementation by the branding-panel process (claude/opus), lint and integration fixes here.
40 lines
2.2 KiB
SQL
40 lines
2.2 KiB
SQL
-- Per-connection publish governance (publish-phase gate).
|
|
--
|
|
-- connections.fields.publishGovernance selects what happens once a draft is
|
|
-- approved: unset = manual publish (today), 'auto' = dispatch on approval,
|
|
-- 'admin1' | 'admin_majority' | 'admin_consensus' = a SECOND chatgov poll
|
|
-- (subject_kind 'draft_publish', kind yes_no) must pass before the publish
|
|
-- is scheduled. The setting itself lives in the established fields jsonb;
|
|
-- this migration only teaches `polls` about the publish-phase subject.
|
|
--
|
|
-- publish_connection_id the destination the publish poll releases to
|
|
-- publish_request {publishAt, requiredPermission} captured at hold
|
|
-- time; the requester is polls.created_by and stays
|
|
-- the scheduling principal (re-checked at claim).
|
|
|
|
ALTER TABLE polls DROP CONSTRAINT polls_subject_kind_check;
|
|
ALTER TABLE polls ADD CONSTRAINT polls_subject_kind_check CHECK
|
|
(subject_kind IN ('draft', 'chat_post', 'none', 'draft_publish'));
|
|
|
|
ALTER TABLE polls
|
|
ADD COLUMN publish_connection_id uuid,
|
|
ADD COLUMN publish_request jsonb CHECK (publish_request IS NULL OR jsonb_typeof(publish_request) = 'object');
|
|
|
|
ALTER TABLE polls ADD CONSTRAINT polls_publish_connection_fk
|
|
FOREIGN KEY (publish_connection_id, workspace_id) REFERENCES connections (id, workspace_id)
|
|
ON DELETE NO ACTION ON UPDATE RESTRICT;
|
|
|
|
ALTER TABLE polls ADD CONSTRAINT polls_publish_subject_check CHECK (
|
|
(subject_kind = 'draft_publish') = (publish_connection_id IS NOT NULL)
|
|
AND (subject_kind <> 'draft_publish' OR (draft_id IS NOT NULL AND publish_request IS NOT NULL))
|
|
);
|
|
|
|
-- One open REVIEW gate per draft stays index-enforced; publish gates get
|
|
-- their own slot per (draft, destination) so they never collide with it.
|
|
DROP INDEX polls_open_one_per_draft;
|
|
CREATE UNIQUE INDEX polls_open_one_per_draft ON polls (workspace_id, draft_id)
|
|
WHERE state = 'open' AND draft_id IS NOT NULL AND subject_kind <> 'draft_publish';
|
|
CREATE UNIQUE INDEX polls_open_one_per_publish ON polls (workspace_id, draft_id, publish_connection_id)
|
|
WHERE state = 'open' AND subject_kind = 'draft_publish';
|
|
CREATE INDEX polls_ws_publish ON polls (workspace_id, draft_id, publish_connection_id, created_at DESC)
|
|
WHERE subject_kind = 'draft_publish';
|