feat(modules): group-docs schema + kind-generalized registry (§11.6 docs C1)

Data layer for extending shared group collections from KV to docs. KV behavior
unchanged (callers pass kind="kv").

- 0054_group_docs.sql: widen the group_collections kind CHECK to ('kv','docs')
  and add the group-keyed group_docs store (clone of docs/0013, keyed by
  group_id, CASCADE on group delete) + its (group_id,collection) and data GIN
  indexes.
- group_collection_repo: thread a `kind` parameter through list_for_owner,
  resolve_owning_group, insert/delete_collection_tx; add list_all_for_owner ->
  (name,kind) for the kind-aware diff (D4). Relocate the injectable
  GroupCollectionResolver trait + Postgres impl here (now shared by kv+docs;
  resolve takes kind) — was in group_kv_service.
- group_docs_repo: a near-clone of docs_repo keyed by group_id; find reuses the
  generalized build_find_query.
- docs_repo: generalize build_find_query(table, owner_col, owner_id, …) — both
  are compile-time literals (no injection); app docs passes ("docs","app_id"),
  group docs ("group_docs","group_id"). row_to_doc/build_find_query are now
  pub(crate) for reuse.

Schema snapshot re-blessed (55 migrations).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-30 07:33:39 +02:00
parent af525e71bd
commit 5d1d4b3ff6
8 changed files with 479 additions and 87 deletions

View File

@@ -231,6 +231,14 @@ table: group_collections
created_at: timestamp with time zone NOT NULL default=now()
updated_at: timestamp with time zone NOT NULL default=now()
table: group_docs
group_id: uuid NOT NULL
collection: text NOT NULL
id: uuid NOT NULL
data: jsonb NOT NULL
created_at: timestamp with time zone NOT NULL default=now()
updated_at: timestamp with time zone NOT NULL default=now()
table: group_kv_entries
group_id: uuid NOT NULL
collection: text NOT NULL
@@ -509,6 +517,11 @@ indexes on group_collections:
group_collections_group_uidx: public.group_collections USING btree (group_id, lower(name), kind) WHERE (group_id IS NOT NULL)
group_collections_pkey: public.group_collections USING btree (id)
indexes on group_docs:
group_docs_pkey: public.group_docs USING btree (group_id, collection, id)
idx_group_docs_data_gin: public.group_docs USING gin (data jsonb_path_ops)
idx_group_docs_group_collection: public.group_docs USING btree (group_id, collection)
indexes on group_kv_entries:
group_kv_entries_pkey: public.group_kv_entries USING btree (group_id, collection, key)
idx_group_kv_entries_group_collection: public.group_kv_entries USING btree (group_id, collection)
@@ -714,12 +727,16 @@ 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 = 'kv'::text))
[CHECK] group_collections_kind_check: CHECK ((kind = ANY (ARRAY['kv'::text, 'docs'::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
[PRIMARY KEY] group_collections_pkey: PRIMARY KEY (id)
constraints on group_docs:
[FOREIGN KEY] group_docs_group_id_fkey: FOREIGN KEY (group_id) REFERENCES groups(id) ON DELETE CASCADE
[PRIMARY KEY] group_docs_pkey: PRIMARY KEY (group_id, collection, id)
constraints on group_kv_entries:
[FOREIGN KEY] group_kv_entries_group_id_fkey: FOREIGN KEY (group_id) REFERENCES groups(id) ON DELETE CASCADE
[PRIMARY KEY] group_kv_entries_pkey: PRIMARY KEY (group_id, collection, key)
@@ -863,3 +880,4 @@ constraints on vars:
0051: extension points
0052: group collections
0053: group kv entries
0054: group docs