BMAD-METHOD/core/messenger/receive-message.xml

105 lines
4.3 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<task id="receive-message" name="Receive Inter-Agent Message" standalone="false">
<description>Receive and process messages from the messenger queue</description>
<config>
<source>{project-root}/_bmad/core/messenger/messenger-config.yaml</source>
<queue_file>{project-root}/_bmad-output/messenger/message-queue.yaml</queue_file>
<archive_dir>{project-root}/_bmad-output/messenger/archive</archive_dir>
</config>
<parameters>
<param name="agent" required="true" description="Agent checking for messages"/>
<param name="type_filter" required="false" description="Filter by message type"/>
<param name="mark_read" required="false" default="true" description="Mark messages as read"/>
</parameters>
<execution>
<step n="1" goal="Load message queue">
<action>Load queue from {queue_file}</action>
<action if="queue not exists or empty">
Return structured output:
messages: []
count: 0
status_message: "No messages in queue"
</action>
</step>
<step n="2" goal="Filter messages for agent">
<action>Filter messages where:
- to == {agent} OR
- to_agents contains {agent} OR
- to_agents == "all"
</action>
<action>Filter by status == "pending"</action>
<action if="type_filter specified">Filter by type == {type_filter}</action>
<action>Sort by priority (critical first), then by created (oldest first)</action>
</step>
<step n="3" goal="Process and archive messages">
<action if="no matching messages">
Return structured output:
messages: []
count: 0
status_message: "No pending messages for {agent}"
</action>
<action>For each message:
- Display message summary
- If mark_read == true:
- Update status to "read"
- Append message to archive file at {archive_dir}/{agent}-archive.yaml
</action>
<note>Archive preserves full message history per agent for audit/replay</note>
</step>
<step n="4" goal="Update queue with locking">
<action if="mark_read == true">
<substep n="4.1">Acquire exclusive lock on {queue_file}.lock</substep>
<substep n="4.2">Re-read {queue_file} to get latest state (another agent may have written)</substep>
<substep n="4.3">Merge status changes for processed message IDs into latest state</substep>
<substep n="4.4">Write updated queue to {queue_file}.tmp (atomic staging)</substep>
<substep n="4.5">Rename {queue_file}.tmp to {queue_file} (atomic replace)</substep>
<substep n="4.6">Release exclusive lock on {queue_file}.lock</substep>
</action>
<note>Lock ensures concurrent agents do not corrupt queue; atomic rename prevents partial writes</note>
</step>
<step n="5" goal="Return messages">
<action>Return structured output:
messages: [{message_objects}]
count: {number_of_messages}
status_message: "Messages for {agent}: {count}"
</action>
<display_format>
Messages for {agent}: {count}
{for each message}
---
[{priority}] {type} from {from}
ID: {message_id}
Received: {created}
{payload_summary}
---
{end for}
</display_format>
<note>Callers always receive {messages, count, status_message} regardless of path taken</note>
</step>
</execution>
<concurrency>
<note>Multiple agents may call receive-message concurrently. Implementations MUST:</note>
<requirement>Acquire exclusive lock before any write to {queue_file}</requirement>
<requirement>Use atomic write (write to temp file, then rename) to prevent corruption</requirement>
<requirement>Re-read queue after acquiring lock to avoid lost updates</requirement>
<requirement>Release lock promptly after write completes</requirement>
<requirement>Archive to {archive_dir} uses per-agent files to minimize lock contention</requirement>
</concurrency>
<output>
<field name="messages" description="Array of message objects (empty array if none)"/>
<field name="count" description="Number of messages (0 if none)"/>
<field name="status_message" required="false" description="Human-readable status for early returns or empty results"/>
</output>
</task>