diff --git a/crates/manager-core/migrations/0064_shared_topic_queue_kinds.sql b/crates/manager-core/migrations/0064_shared_topic_queue_kinds.sql new file mode 100644 index 0000000..2275426 --- /dev/null +++ b/crates/manager-core/migrations/0064_shared_topic_queue_kinds.sql @@ -0,0 +1,17 @@ +-- §11.6 (cont., v1.2 Hierarchies): admit 'topic' and 'queue' as shared- +-- collection kinds. +-- +-- D2 (shared topics) + D3 (shared queues) extend the group shared-collection +-- machinery from data stores (kv/docs/files) to the two event/messaging kinds. +-- A shared TOPIC is storeless — it is a group-scoped publish NAMESPACE watched +-- by `shared = true` group pubsub triggers (no per-kind storage table). A shared +-- QUEUE gets its group-keyed message store in 0065 (D3). This migration only +-- widens the marker allow-list; both kinds route through the same owner-generic +-- reconcile + `resolve_owning_group` path as the data kinds. + +-- Widen the marker kind allow-list (auto-named `group_collections_kind_check`, +-- an inline column CHECK), matching 0054/0055. +ALTER TABLE group_collections + DROP CONSTRAINT group_collections_kind_check, + ADD CONSTRAINT group_collections_kind_check + CHECK (kind IN ('kv', 'docs', 'files', 'topic', 'queue')); diff --git a/crates/manager-core/src/apply_service.rs b/crates/manager-core/src/apply_service.rs index c06c38d..d3cd790 100644 --- a/crates/manager-core/src/apply_service.rs +++ b/crates/manager-core/src/apply_service.rs @@ -119,7 +119,7 @@ fn default_collection_kind() -> String { } /// The shared-collection kinds the apply engine accepts. -const COLLECTION_KINDS: &[&str] = &["kv", "docs", "files"]; +const COLLECTION_KINDS: &[&str] = &["kv", "docs", "files", "topic", "queue"]; #[derive(Debug, Clone, Deserialize)] pub struct BundleScript { diff --git a/crates/manager-core/tests/expected_schema.txt b/crates/manager-core/tests/expected_schema.txt index 0e1efbf..44ba662 100644 --- a/crates/manager-core/tests/expected_schema.txt +++ b/crates/manager-core/tests/expected_schema.txt @@ -769,7 +769,7 @@ constraints on files_trigger_details: [PRIMARY KEY] files_trigger_details_pkey: PRIMARY KEY (trigger_id) constraints on group_collections: - [CHECK] group_collections_kind_check: CHECK ((kind = ANY (ARRAY['kv'::text, 'docs'::text, 'files'::text]))) + [CHECK] group_collections_kind_check: CHECK ((kind = ANY (ARRAY['kv'::text, 'docs'::text, 'files'::text, 'topic'::text, 'queue'::text]))) [CHECK] group_collections_owner_exactly_one: CHECK (((group_id IS NULL) <> (app_id IS NULL))) [FOREIGN KEY] group_collections_app_id_fkey: FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE CASCADE [FOREIGN KEY] group_collections_group_id_fkey: FOREIGN KEY (group_id) REFERENCES groups(id) ON DELETE CASCADE @@ -948,3 +948,4 @@ constraints on vars: 0061: shared triggers 0062: materialized triggers 0063: materialized unique + 0064: shared topic queue kinds diff --git a/crates/picloud-cli/src/manifest.rs b/crates/picloud-cli/src/manifest.rs index 5a25106..f9ae577 100644 --- a/crates/picloud-cli/src/manifest.rs +++ b/crates/picloud-cli/src/manifest.rs @@ -270,6 +270,12 @@ pub enum CollectionKind { Kv, Docs, Files, + /// §11.6 D2: a storeless group-scoped publish namespace, watched by + /// `shared = true` group pubsub triggers. + Topic, + /// §11.6 D3: a group-keyed durable queue with competing per-descendant + /// consumers. + Queue, } impl CollectionKind { @@ -279,6 +285,8 @@ impl CollectionKind { Self::Kv => "kv", Self::Docs => "docs", Self::Files => "files", + Self::Topic => "topic", + Self::Queue => "queue", } } }