How Macro's Bidirectional @linking System Functions Across Diverse Entity Types

Macro implements a generic @link (entity‑mention) system using a central EntityMention struct that records both source and target for every relationship, enabling bidirectional queries and notifications across messages, documents, projects, email threads, calls, and more.

The macro-inc/macro codebase powers a unified collaboration platform where any object can reference any other object. The bidirectional @linking system serves as the backbone for cross-entity navigation, notifications, and relationship discovery. This article examines the architecture, data model, and API flow that make this polymorphic linking possible.

The Core Data Model: EntityMention

At the heart of the system lies the EntityMention struct, defined in crates/channels/src/domain/models.rs (lines 1385–1400). This struct captures four essential identifiers that make any link traversable from either direction:

  • source_entity_type + source_entity_id — the object creating the reference
  • entity_type + entity_id — the object being referenced
  • created_by — the user who created the link
  • created_at — timestamp for ordering and auditing
// Conceptual representation based on EntityMention definition
pub struct EntityMention {
    pub id: Uuid,
    pub source_entity_type: String,  // e.g., "message", "document"
    pub source_entity_id: Uuid,      // UUID of the source
    pub entity_type: String,         // e.g., "Document", "Chat", "user"
    pub entity_id: Uuid,             // UUID of the target
    pub created_by: Uuid,
    pub created_at: DateTime<Utc>,
}

The CreateEntityMentionOptions struct (lines 1404–1415) mirrors these fields for API-level operations, while CreateEntityMentionRequest (lines 1420–1429) serves as the HTTP payload for POST /channels/mentions.

When a user creates an @link, the system validates the request and persists the relationship via insert_message_mentions in crates/channels/src/outbound/pg_channels_repo.rs (lines 62–70).

// Simplified flow showing the key operations
pub async fn insert_message_mentions(
    &self,
    message_id: Uuid,
    mentions: &[CreateEntityMentionOptions],
    created_by: Uuid,
) -> Result<Vec<Uuid>, Error> {  // Returns user IDs to notify
    // 1. Deduplicate existing mentions for this message
    // 2. Insert new rows into comms_entity_mentions
    // 3. If entity_type == "user", collect for notification
}

This function demonstrates polymorphic handling: it accepts any target type in the entity_type field, yet applies special logic when the target is a user (triggering push notifications).

The system enables two fundamental query patterns through the same table structure.

Forward Lookup: "What does this source mention?"

When fetching channel messages, get_latest_channel_messages_batch (lines 2120–2130) aggregates all mentions as "entity_type:entity_id" strings:

-- From pg_channels_repo.rs message retrieval query
SELECT 
    m.id,
    m.content,
    ARRAY(
        SELECT em.entity_type || ':' || em.entity_id
        FROM comms_entity_mentions em
        WHERE em.source_entity_id = m.id
    ) AS mentions
FROM comms_messages m
WHERE m.channel_id = $1
ORDER BY m.created_at DESC
LIMIT $2;

The frontend parses these strings to render clickable @links with appropriate icons and routing.

Reverse Lookup: "What sources mention this target?"

By filtering on entity_type + entity_id, the system finds all incoming references. The thread participant logic uses this implicitly: get_thread_data and get_thread_messages join comms_entity_mentions to identify users @mentioned in any message, ensuring they receive thread notifications regardless of direct channel membership.

Deletion and Consistency

Link lifecycle management relies on delete_entity_mentions_by_source (lines 33–40), which removes all rows matching a source entity ID. This generic deletion works across all source types:

pub async fn delete_entity_mentions_by_source(
    &self,
    source_entity_ids: &[Uuid],
) -> Result<u64, Error> {
    // DELETE FROM comms_entity_mentions 
    // WHERE source_entity_id = ANY($1)
}

This design ensures referential integrity without cascading constraints — when a message is deleted, its outgoing @links disappear automatically.

Supported Entity Types and Extensibility

The ReferencedShareItemType enum defines built-in target types in models.rs:

Type Description
Document Stored files and content
Chat Channels and direct messages
Project Workspace containers
EmailThread Email conversations
Call Voice/video call records

Because comms_entity_mentions stores types as plain String values, new entity categories require no schema migration. The service layer need only recognize and validate the new type string.

API Flow Summary

  1. Client constructs a CreateEntityMentionRequest with source and target identifiers
  2. Router accepts POST /channels/mentions and validates the payload
  3. Repository calls insert_message_mentions (or generic equivalent) to write rows
  4. Database stores bidirectional relationship in comms_entity_mentions
  5. Notification service receives user IDs when @user mentions occur
  6. Query handlers join the mentions table for rendering and reverse lookups
  7. Cleanup triggers delete_entity_mentions_by_source on entity deletion

Key Source Files

File Responsibility
crates/channels/src/domain/models.rs EntityMention, request/response types, ReferencedShareItemType
crates/channels/src/outbound/pg_channels_repo.rs SQL operations: insert_message_mentions, delete_entity_mentions_by_source, message queries with mention arrays
crates/channels/src/domain/mention_events.rs Event metadata generation for notifications
crates/channels/src/inbound/axum_router.rs HTTP route handlers for mention endpoints

Summary

  • Bidirectional @linking in Macro relies on a single table (comms_entity_mentions) with four identifier columns enabling traversal in either direction
  • EntityMention struct (lines 1385–1400) provides the canonical representation, decoupled from any specific source or target type
  • Polymorphic insertion via insert_message_mentions handles type-specific side effects (user notifications) while preserving generic storage
  • Deletion by source with delete_entity_mentions_by_source maintains graph consistency without foreign key cascades
  • String-based typing allows runtime extensibility — new entity types require code changes, not schema migrations

Frequently Asked Questions

How does Macro handle notification routing for @mentions?

The insertion function insert_message_mentions checks whether entity_type == "user" for each mention. When true, it resolves the referenced user's notification preferences and channel participation status, returning their UUIDs to the caller for enqueueing push/email notifications.

Yes. The data model imposes no restrictions on source-target type combinations. A Call can reference a Document, an EmailThread can reference a Project, and so on. The frontend determines how to render each combination based on the entity_type string.

The current implementation does not automatically cascade deletions when a target is removed. The mentions array in query results may contain stale references (dangling links), which the frontend typically handles by showing "unavailable" placeholders. Deletion cascades only from source entities via delete_entity_mentions_by_source.

How scalable is the comms_entity_mentions table for high-volume workspaces?

The table structure supports indexing strategies for both query directions: (source_entity_type, source_entity_id) for forward lookups and (entity_type, entity_id) for reverse lookups. The batch message query uses ARRAY aggregation to minimize round-trips, and the deduplication logic in insert_message_mentions prevents redundant rows for frequently edited messages.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →