feat(broker+api): every mesh ships with a default #general topic
The web chat surface needed a guaranteed landing room — a topic that exists for every mesh from creation onward so the dashboard always has somewhere to drop the user. #general is the convention; ephemeral DMs remain ephemeral (mesh.message_queue) so agentic privacy is unchanged. Three hooks plus a backfill: - packages/api/src/modules/mesh/mutations.ts — createMyMesh now calls ensureGeneralTopic() right after the mesh insert. New helper is idempotent via the unique (mesh_id, name) index. - apps/broker/src/index.ts — handleMeshCreate (CLI claudemesh new) inserts #general + subscribes the owner member as 'lead' in the same handler. - apps/broker/src/crypto.ts — invite-claim flow auto-subscribes the newly minted member to #general as 'member', defensively ensuring the topic exists if predates this change. - packages/db/migrations/0024_general_topic_backfill.sql — one-shot backfill: creates #general for every active mesh that doesn't have one, subscribes every active member, and marks the mesh owner as 'lead' based on owner_user_id == member.user_id. Idempotent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
34
packages/db/migrations/0024_general_topic_backfill.sql
Normal file
34
packages/db/migrations/0024_general_topic_backfill.sql
Normal file
@@ -0,0 +1,34 @@
|
||||
-- 0024_general_topic_backfill.sql
|
||||
--
|
||||
-- Every mesh now ships with a default #general topic auto-created on mesh
|
||||
-- creation. This migration backfills the convention for meshes that
|
||||
-- predate that hook:
|
||||
-- 1. Insert a #general row for every mesh that doesn't already have one.
|
||||
-- 2. Subscribe every active (non-revoked) member to #general.
|
||||
--
|
||||
-- Idempotent — safe to re-run. The unique indices on (mesh_id, name) and
|
||||
-- (topic_id, member_id) make the inserts no-ops on the second pass.
|
||||
|
||||
INSERT INTO mesh.topic (mesh_id, name, description, visibility)
|
||||
SELECT
|
||||
m.id,
|
||||
'general',
|
||||
'Default mesh-wide channel. Every member can read and post.',
|
||||
'public'
|
||||
FROM mesh.mesh m
|
||||
WHERE m.archived_at IS NULL
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM mesh.topic t
|
||||
WHERE t.mesh_id = m.id AND t.name = 'general'
|
||||
);
|
||||
|
||||
INSERT INTO mesh.topic_member (topic_id, member_id, role)
|
||||
SELECT
|
||||
t.id,
|
||||
mm.id,
|
||||
CASE WHEN m.owner_user_id = mm.user_id THEN 'lead' ELSE 'member' END
|
||||
FROM mesh.topic t
|
||||
JOIN mesh.mesh m ON m.id = t.mesh_id
|
||||
JOIN mesh.member mm ON mm.mesh_id = t.mesh_id AND mm.revoked_at IS NULL
|
||||
WHERE t.name = 'general'
|
||||
ON CONFLICT (topic_id, member_id) DO NOTHING;
|
||||
Reference in New Issue
Block a user