How Macro Creates Tasks from Emails with Bidirectional Linking

Macro creates tasks from emails by parsing inbound messages for task cues, persisting them as documents with subtype "task", and establishing symmetric Link records in the database that enable querying from either the email or task direction.

The macro-inc/macro codebase implements a robust pipeline that transforms incoming emails into actionable tasks while maintaining persistent bidirectional relationships between the source message and the derived task. This architecture ensures that users can navigate seamlessly from an email to its associated task and vice versa through a symmetric linking mechanism implemented in Rust.

Email Ingestion and Task Detection

When an inbound email arrives, the email service handles the initial parsing and persists the message as a standard email entity within the system. The services/email_service/src/api/swagger.rs file defines the API contract that registers the endpoints triggering this processing, while packages/sdk/specs/email.json specifies the underlying data structure for email handling.

Simultaneously, the search-processing service analyzes the email content for task creation signals. This includes explicit commands such as "/task", explicit task-creation intents, or automatically detected action items within the message body.

Building the Task Document

Upon detecting a task cue, the system constructs a specialized document in services/search_processing_service/src/process/document/raw_document.rs. The service creates a Document struct flagged with sub_type: Some("task".to_string()), which distinguishes it from standard documents and enables task-specific UI rendering.

let task_doc = Document {
    title: extracted_title,
    sub_type: Some("task".to_string()),
    // …other fields…
};
let task_id = document_db.insert(task_doc).await?;

This document is inserted into MacroDB as a regular document entry, but the task subtype flag ensures it appears in task-specific views and workflows.

To maintain the relationship between the originating email and the newly created task, the email link manager creates a symmetric Link record in services/email_service/src/pubsub/link_manager/process.rs. This record stores both the source entity type (email) and target entity type (task) along with their respective identifiers.

The link table architecture is inherently symmetric, meaning a single row enables traversal in both directions. Queries can resolve email-to-task relationships or task-to-email relationships using the same underlying record.

let link = Link {
    source_entity_type: EntityType::Email,
    source_entity_id: email_id,
    target_entity_type: EntityType::Task,
    target_entity_id: task_id,
};
link_manager.create(link).await?;

Because the link structure is symmetric, the UI can efficiently retrieve related entities from either direction. When viewing an email, the system queries for associated tasks; when viewing a task, it retrieves the originating email.

let related = link_manager
    .find_by_source(EntityType::Email, email_id)
    .await?; // returns the task ID

Removing Email-Task Associations

The link manager also handles the deletion of associations when tasks are unlinked from their source emails. The error handling logic at line 366 of services/email_service/src/pubsub/link_manager/process.rs demonstrates the background task responsible for removing these relationships.

link_manager
    .delete(link_id)
    .await
    .context("Failed to delete link in background task")?;

Summary

  • Email ingestion triggers the pipeline through the email service, which parses and stores messages according to the API contract defined in packages/sdk/specs/email.json.
  • Task detection occurs in the search-processing service, which scans for "/task" commands and action items before creating documents with subtype "task" in raw_document.rs.
  • Bidirectional linking is implemented through symmetric Link records in the email link manager, enabling queries from either email-to-task or task-to-email directions.
  • Link removal is handled as a background task in services/email_service/src/pubsub/link_manager/process.rs, ensuring clean disassociation when needed.

Frequently Asked Questions

How does Macro detect when to create a task from an email?

The search-processing service examines incoming email content for specific cues such as "/task" commands, explicit task-creation intents, or automatically detected action items. When found, it instantiates a Document with sub_type: "task" and persists it to MacroDB.

What database structure enables bidirectional linking between emails and tasks?

The system uses a symmetric Link table that stores source_entity_type, source_entity_id, target_entity_type, and target_entity_id. Because the architecture treats the relationship as symmetric, queries can traverse from email to task or task to email using the same underlying record without requiring duplicate entries.

Yes, the email link manager provides deletion capabilities through background tasks defined in services/email_service/src/pubsub/link_manager/process.rs. The error handling at line 366 specifically manages the cleanup of link records when associations are removed.

Which services are involved in the email-to-task pipeline?

Three primary services collaborate: the email service handles ingestion and API endpoint registration, the search-processing service performs content analysis and task document creation, and the email link manager maintains the bidirectional associations between entities.

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 →