* feat(orchestrator): carry sender_task metadata through user message paths
Add WithSenderTask builder to UserMessageMeta and a metadata-aware
QueueMessageWithMetadata variant on the message queue. The drain path
in event_handlers_agent merges queued metadata into the recorded user
message so attribution survives the queue → DB transition.
* feat(mcp): attribute message_task_kandev calls to the sending agent
The MCP server inside agentctl now injects sender_task_id and
sender_session_id from its own server context. The backend handler
validates the sender (rejecting missing IDs, self-messages, and unknown
tasks), looks up the sender title, wraps the prompt in a <kandev-system>
attribution block, and propagates structured metadata across all three
delivery paths (queue, created-session, record+prompt). The wrapped
content lets the receiving agent identify the peer both live and on ACP
session resume; the existing sysprompt strip pipeline hides the wrapper
from the UI.
* feat(web): render sender-task badge on cross-task agent messages
User messages whose metadata carries sender_task_id now render a
clickable purple badge above the bubble, linking to the source task.
The badge live-resolves the sender's title from the kanban store and
falls back to a greyed snapshot when the task isn't loaded
(cross-workspace, deleted, etc.). Adds a non-subscribing useTaskById
selector so badges don't fan out WS subscriptions per message.
* test(e2e): cover cross-task agent message attribution
Eight scenarios driven through the mock-agent's MCP script directive:
queue path, prompt path, self-message rejection, unknown-target error,
badge link target, live title re-resolution after rename, kandev-system
wrapper preservation in raw_content, and prompt bodies containing their
own kandev-system tags. Adds an updateTaskTitle helper to ApiClient.
* fix: address CodeRabbit + Greptile P1 findings on PR #819
- requeueMessage now forwards queuedMsg.Metadata via QueueMessageWithMetadata
so sender attribution survives transient delivery failures and retries.
- CREATED-session branch in handleMessageTask records the user message AFTER
StartCreatedSession succeeds, so a launch failure no longer leaves an
orphaned attributed chat row.
- SenderTaskBadge: collapse identical displayTitle/fullTitle locals.
- E2E: extract waitForTargetIdle helper; replace the 1.5s raw sleep in the
collision-check test with the same poll the rest of the suite uses.
* fix: address Claude review suggestions on PR #819
- wrapAgentMessage strips </kandev-system> from the embedded sender title
before formatting. Without this, a title containing the closing tag
would short-circuit the non-greedy strip regex and leak the attribution
tail into the visible UI bubble. Metadata snapshot keeps the original
title so the badge still displays it.
- Add a regression test covering the malicious-title case.
- Document the deliberate cross-workspace messaging policy on the sender
lookup so future readers don't add a workspace-scope check (Q13 of the
feature plan locked this as intentional).